/* Structure and typography follow the didit console (Inter, ink #0A0A0A on canvas
   #FEFEFE, hairline borders, small radii). The accent is WhatsVibe's own teal,
   sampled from the logo, because didit's blue fought with the mark. */

:root {
    --canvas: 254 254 254;
    --ink: 10 10 10;
    --surface: 249 249 249;
    --line: 237 237 237;
    --muted: 93 93 93;

    --brand: 7 130 113;        /* #078271, the deep teal in the wordmark */
    --brand-deep: 0 98 86;     /* #006256, hover */
    --brand-soft: 230 244 241;

    --danger-fg: 182 58 46;
    --danger-bg: 251 233 229;
    --success-fg: 10 125 47;

    --bg: var(--canvas);
    --fg: var(--ink);
    --card: var(--canvas);
    --border: var(--line);
    --muted-fg: var(--muted);
    --field: var(--canvas);

    --radius-xs: .375rem;
    --radius: .5rem;
    --radius-sm: .75rem;
    --radius-md: 1rem;
    --radius-full: 999px;

    --maxw: 1080px;
}

:root.dark {
    --bg: var(--ink);
    --fg: var(--canvas);
    --card: 11 11 15;
    --border: 255 255 255 / .14;
    --muted-fg: 170 170 180;
    --surface: 20 20 26;
    --field: 16 16 21;
    --brand-soft: 11 42 38;
    --danger-bg: 46 22 20;
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
    margin: 0;
    background: rgb(var(--bg));
    color: rgb(var(--fg));
    font-family: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
    font-size: 15px;
    line-height: 1.55;
    -webkit-font-smoothing: antialiased;
    display: flex;
    flex-direction: column;
}

h1, h2, h3 { margin: 0; font-weight: 500; letter-spacing: -.02em; }
h1 { font-size: 2.25rem; line-height: 1.1; letter-spacing: -.025em; }
h2 { font-size: 1.625rem; line-height: 1.15; }
h3 { font-size: 1.125rem; }
p { margin: 0; }
a { color: rgb(var(--brand)); text-decoration: none; }
a:hover { text-decoration: underline; }

.muted { color: rgb(var(--muted-fg)); }

:focus-visible {
    outline: 2px solid rgb(var(--brand));
    outline-offset: 2px;
    border-radius: var(--radius-xs);
}

/* ------------------------------------------------------------------ topbar */

.topbar {
    position: sticky;
    top: 0;
    z-index: 50;
    background: rgb(var(--bg));
    flex: none;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    height: 60px;
    padding: 0 1.5rem;
    border-bottom: 1px solid rgb(var(--border));
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: .55rem;
    color: rgb(var(--fg));
    font-weight: 500;
    letter-spacing: -.015em;
}
.brand:hover { text-decoration: none; }

.brand-logo {
    display: block;
    height: 26px;
    width: auto;
}
/* "Whats" is a deep teal that goes muddy on a near-black background. */
:root.dark .brand-logo { filter: brightness(1.5) saturate(1.05); }

.topbar-left { display: flex; align-items: center; gap: .75rem; }
.topbar-actions { display: flex; align-items: center; gap: .5rem; }
.topbar-actions form { margin: 0; }

.icon-button {
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: transparent;
    color: rgb(var(--muted-fg));
    cursor: pointer;
}
.icon-button:hover { color: rgb(var(--fg)); }
.icon-button .icon-moon { display: none; }
:root.dark .icon-button .icon-sun { display: none; }
:root.dark .icon-button .icon-moon { display: block; }

/* ----------------------------------------------------------------- buttons */

.button {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: .5rem;
    padding: .6rem 1.1rem;
    border: 1px solid transparent;
    border-radius: var(--radius);
    background: rgb(var(--brand));
    color: #fff;
    font: inherit;
    font-weight: 500;
    cursor: pointer;
    transition: background .12s ease, border-color .12s ease;
}
.button:hover { background: rgb(var(--brand-deep)); text-decoration: none; }
.button:disabled,
.button[aria-disabled="true"] {
    opacity: .58;
    cursor: not-allowed;
}

.button.is-loading {
    position: relative;
    pointer-events: none;
    cursor: wait;
    color: transparent;
}
/* Hide the label and any icon, but keep them holding the width so nothing jumps. */
.button.is-loading > * { visibility: hidden; }

.button.is-loading::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 16px;
    height: 16px;
    margin: -8px 0 0 -8px;
    border-radius: 50%;
    border: 2px solid rgb(255 255 255 / .35);
    border-top-color: #fff;
    animation: spin .6s linear infinite;
}
/* Outline buttons sit on the page, so their spinner takes the brand colour. */
.button-quiet.is-loading::after,
.button-google.is-loading::after {
    border-color: rgb(var(--muted-fg) / .3);
    border-top-color: rgb(var(--brand));
}

@keyframes spin { to { transform: rotate(360deg); } }

.button-quiet {
    background: transparent;
    border-color: rgb(var(--border));
    color: rgb(var(--fg));
}
.button-quiet:hover { background: rgb(var(--surface)); border-color: rgb(var(--muted-fg) / .5); }

.button-block { width: 100%; }

.button-google {
    background: transparent;
    border-color: rgb(var(--border));
    color: rgb(var(--fg));
}
.button-google:hover { background: rgb(var(--surface)); }
.button-google[aria-disabled="true"] { opacity: .55; pointer-events: none; }
.google-g { flex: none; }
/* Google's mark must keep its own colours, so it never picks up currentColor. */
:root.dark .google-g { filter: none; }

/* -------------------------------------------------------------------- home */

.home {
    flex: 1;
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 4rem;
    align-items: center;
    width: 100%;
    max-width: var(--maxw);
    margin: 0 auto;
    padding: 4rem 1.5rem;
}

.home-lede { margin-top: 1rem; max-width: 46ch; font-size: 1.0625rem; }

.home-points {
    margin: 1.75rem 0 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: .6rem;
    max-width: 46ch;
}
.home-points li {
    display: grid;
    grid-template-columns: 18px 1fr;
    gap: .6rem;
    color: rgb(var(--muted-fg));
}
.home-points li::before {
    content: "";
    margin-top: .5rem;
    width: 18px;
    height: 1px;
    background: rgb(var(--border));
}

.home-actions { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: 2rem; }

/* The signature: a real exchange, not a description of one. */
.proof {
    padding: 1.25rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-md);
    background: rgb(var(--card));
}

.proof-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-bottom: .9rem;
    margin-bottom: 1.1rem;
    border-bottom: 1px solid rgb(var(--border));
    font-size: .8rem;
    color: rgb(var(--muted-fg));
}
.proof-live { display: inline-flex; align-items: center; gap: .4rem; }
.proof-live::before {
    content: "";
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: rgb(var(--success-fg));
}

.thread { display: grid; gap: .55rem; }

.bubble {
    max-width: 82%;
    padding: .6rem .85rem;
    border-radius: var(--radius-sm);
    border: 1px solid rgb(var(--border));
    background: rgb(var(--surface));
    animation: rise .5s cubic-bezier(.2, .7, .3, 1) backwards;
}
.bubble.out {
    justify-self: end;
    background: rgb(var(--brand));
    border-color: rgb(var(--brand));
    color: #fff;
}
.bubble:nth-child(1) { animation-delay: .05s; }
.bubble:nth-child(2) { animation-delay: .5s; }
.bubble:nth-child(3) { animation-delay: 1s; }
.bubble:nth-child(4) { animation-delay: 1.5s; }

@keyframes rise {
    from { opacity: 0; transform: translateY(7px); }
    to   { opacity: 1; transform: none; }
}

.proof-foot {
    margin-top: 1.1rem;
    padding-top: .9rem;
    border-top: 1px solid rgb(var(--border));
    font-size: .8rem;
    color: rgb(var(--muted-fg));
}

@media (prefers-reduced-motion: reduce) {
    .bubble { animation: none; }
}

/* -------------------------------------------------------------------- auth */

.auth {
    flex: 1;
    display: grid;
    place-items: center;
    padding: 3rem 1.5rem;
}

.auth-card { width: 100%; max-width: 400px; }

.auth-head { margin-bottom: 1.75rem; }
.auth-head h1 { font-size: 1.75rem; }
.auth-head p { margin-top: .4rem; color: rgb(var(--muted-fg)); }

.field { margin-bottom: 1rem; }
.field label {
    display: block;
    margin-bottom: .4rem;
    font-size: .8125rem;
    font-weight: 500;
}
.field input {
    width: 100%;
    padding: .65rem .8rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font: inherit;
}
.field input::placeholder { color: rgb(var(--muted-fg) / .7); }

/* Every visible radio in the app: a big ring whose dot grows in, so a
   switch of choice is seen, not squinted at. Controls that hide their radio
   behind a styled card (plan lengths, color swatches) opt back out below. */
input[type="radio"] {
    appearance: none;
    -webkit-appearance: none;
    flex: none;
    width: 22px;
    height: 22px;
    margin: 0;
    display: inline-grid;
    place-items: center;
    border: 2px solid rgb(var(--muted-fg) / .5);
    border-radius: 50%;
    background: transparent;
    cursor: pointer;
    transition: border-color .18s ease, box-shadow .18s ease;
}
input[type="radio"]::before {
    content: "";
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: rgb(var(--brand));
    transform: scale(0);
    transition: transform .18s ease;
}
input[type="radio"]:checked { border-color: rgb(var(--brand)); }
input[type="radio"]:checked::before { transform: scale(1); }
input[type="radio"]:hover { border-color: rgb(var(--brand)); }
input[type="radio"]:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgb(var(--brand) / .25);
}
.field input:focus {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}
.field input[aria-invalid="true"] { border-color: rgb(var(--danger-fg)); }

.field-hint { margin-top: .35rem; font-size: .75rem; color: rgb(var(--muted-fg)); }
.field-error { margin-top: .35rem; font-size: .75rem; color: rgb(var(--danger-fg)); }

.form-error {
    margin-bottom: 1rem;
    padding: .6rem .8rem;
    border-radius: var(--radius);
    background: rgb(var(--danger-bg));
    color: rgb(var(--danger-fg));
    font-size: .875rem;
}

.divider {
    display: flex;
    align-items: center;
    gap: .75rem;
    margin: 1.25rem 0;
    color: rgb(var(--muted-fg));
    font-size: .75rem;
}
.divider::before, .divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: rgb(var(--border));
}

.auth-alt { margin-top: 1.5rem; text-align: center; font-size: .875rem; color: rgb(var(--muted-fg)); }



/* ------------------------------------------------------------------ mobile */

@media (max-width: 860px) {
    .home {
        grid-template-columns: 1fr;
        gap: 2.5rem;
        padding: 2.5rem 1.25rem;
    }
    h1 { font-size: 1.875rem; }
    .topbar { padding: .75rem 1.25rem; }
    .sitefoot { padding: 2.5rem 1.25rem; }
    .foot-icon { height: 64px; }
    .foot-line { font-size: 1.25rem; }
}

/* ------------------------------------------------------------------ footer */

.sitefoot {
    flex: none;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .85rem;
    padding: 3.25rem 1.5rem;
    border-top: 1px solid rgb(var(--border));
    background: rgb(var(--surface));
    text-align: center;
}

.foot-icon {
    display: block;
    height: 84px;
    width: auto;
}

.foot-line {
    font-size: 1.5rem;
    font-weight: 600;
    letter-spacing: -.02em;
    line-height: 1.2;
    /* The wordmark's own gradient, teal into green, read across the line. */
    background: linear-gradient(100deg, rgb(var(--brand-deep)), #01AFAA 55%, #6DBE45);
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
}

.foot-note {
    font-size: .8125rem;
    color: rgb(var(--muted-fg));
}

/* ------------------------------------------------------------ verify code */

.auth-head-center { text-align: center; }

.auth-icon {
    display: block;
    height: 68px;
    width: auto;
    margin: 0 auto .9rem;
}

.code {
    display: grid;
    grid-template-columns: repeat(6, 1fr);
    gap: .5rem;
    margin-bottom: 1.5rem;
}

.code-digit {
    width: 100%;
    height: 60px;
    padding: 0;
    border: 1px solid rgb(var(--border));
    border-radius: 12px;
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font-family: inherit;
    font-size: 1.5rem;
    font-weight: 600;
    text-align: center;
    transition: border-color .15s ease, box-shadow .15s ease, background .15s ease;
}
.code-digit:focus {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .16);
}
/* Each digit lands with a small pop and turns brand green. */
.code-digit.is-filled {
    border-color: rgb(var(--brand));
    background: rgb(var(--brand-soft));
    color: rgb(var(--brand-deep));
    animation: digit-pop .22s cubic-bezier(.2, .9, .3, 1.25);
}
:root.dark .code-digit.is-filled { color: #4ee0c4; }

@keyframes digit-pop {
    0%   { transform: scale(.86); }
    60%  { transform: scale(1.06); }
    100% { transform: scale(1); }
}

.code.is-wrong { animation: shake .4s cubic-bezier(.36, .07, .19, .97); }
@keyframes shake {
    10%, 90% { transform: translateX(-2px); }
    20%, 80% { transform: translateX(4px); }
    30%, 50%, 70% { transform: translateX(-7px); }
    40%, 60% { transform: translateX(7px); }
}

.linkish {
    padding: 0;
    border: 0;
    background: none;
    color: rgb(var(--brand));
    font: inherit;
    cursor: pointer;
    text-decoration: underline;
}
.linkish:disabled { opacity: .5; cursor: default; }

@media (prefers-reduced-motion: reduce) {
    .code-digit.is-filled, .code.is-wrong { animation: none; }
}

/* -------------------------------------------------------------- dashboard */

/* Signed-in pages behave like an app: the topbar and menu hold still and only
   the workspace scrolls. Without this the sidebar stretches to the height of a
   long page and its bottom items drift into the middle of the screen. */
body.app { height: 100vh; height: 100dvh; overflow: hidden; }
body.app .topbar { flex: none; }

.shell {
    flex: 1;
    display: grid;
    grid-template-columns: 232px 1fr;
    min-width: 0;
    min-height: 0;   /* lets the children scroll instead of growing the page */
    overflow: hidden;
    isolation: isolate;
}

.sidebar {
    /* The shell already starts below the topbar and owns the remaining viewport
       height. Keeping the sidebar in that grid prevents workspace content from
       sliding or painting underneath it. */
    position: relative;
    z-index: 2;
    height: 100%;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    padding: 1.25rem .75rem;
    border-right: 1px solid rgb(var(--border));
    background: rgb(var(--surface));
}
/* Pushes whatever follows it down to the bottom of the menu. */
.sidebar-nav { flex: 1; }

.sidebar-bottom {
    margin-top: auto;
    display: flex;
    flex-direction: column;
    gap: .25rem;
    padding-top: .75rem;
    border-top: 1px solid rgb(var(--border));
}

.nav-item {
    display: flex;
    align-items: center;
    gap: .65rem;
    padding: .6rem .75rem;
    border-radius: var(--radius);
    color: rgb(var(--muted-fg));
    font-weight: 500;
    text-decoration: none;
}
/* Hover is a pointer affordance. A touch leaves it stuck on whatever was
   tapped last, which reads as a second selected item, so real pointers only. */
@media (hover: hover) {
    .nav-item:hover { background: rgb(var(--card)); color: rgb(var(--fg)); text-decoration: none; }
}
.nav-item.is-current { background: rgb(var(--brand-soft)); color: rgb(var(--brand)); }

/* A footnote, not a menu item. Quiet enough to ignore, there when looked for. */
.sidebar-version {
    margin-top: .9rem;
    padding: 0 .75rem;
    font-size: .6875rem;
    letter-spacing: .04em;
    color: rgb(var(--muted-fg) / .65);
}
.sidebar-version span { color: rgb(var(--muted-fg) / .85); }

.theme-picker { margin-top: .15rem; }

.nav-theme {
    width: 100%;
    border: 0;
    border-radius: var(--radius);
    padding: .6rem .75rem;
    background: none;
    font: inherit;
    font-weight: 500;
    cursor: pointer;
    text-align: left;
}
.nav-theme:hover { background: rgb(var(--card)); color: rgb(var(--fg)); }
.nav-theme-label { flex: 1; }
.nav-theme .icon-moon { display: none; }
:root.dark .nav-theme .icon-sun { display: none; }
:root.dark .nav-theme .icon-moon { display: block; }

.theme-chevron { flex: none; transition: transform .28s cubic-bezier(.2, .8, .3, 1); }
.theme-picker.is-open .theme-chevron { transform: rotate(180deg); }

/* 0fr to 1fr animates to the panel's natural height with no magic numbers. */
/* The folded-away bottom group. One quiet row, and the whole menu unfolds
   under it with the same easing the theme picker already uses. */
.more-trigger {
    width: 100%;
    border: 0;
    border-radius: var(--radius);
    background: none;
    font: inherit;
    font-weight: 500;
    text-align: left;
    cursor: pointer;
}
.more-trigger:hover { background: rgb(var(--card)); color: rgb(var(--fg)); }
.more-chevron {
    margin-left: auto;
    transform: rotate(180deg);
    transition: transform .28s cubic-bezier(.2, .8, .3, 1);
}
.more-trigger[aria-expanded="true"] .more-chevron { transform: none; }

.more-menu {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows .28s cubic-bezier(.2, .8, .3, 1);
}
.more-menu.is-open { grid-template-rows: 1fr; }
.more-menu-inner {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    min-height: 0;
    overflow: hidden;
    opacity: 0;
    transform: translateY(-4px);
    transition: opacity .2s ease, transform .28s cubic-bezier(.2, .8, .3, 1);
}
.more-menu.is-open .more-menu-inner { opacity: 1; transform: none; }

.theme-options {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows .28s cubic-bezier(.2, .8, .3, 1);
}
.theme-picker.is-open .theme-options { grid-template-rows: 1fr; }

.theme-options-inner {
    overflow: hidden;
    opacity: 0;
    transform: translateY(6px);
    transition: opacity .2s ease, transform .28s cubic-bezier(.2, .8, .3, 1);
}
.theme-picker.is-open .theme-options-inner { opacity: 1; transform: none; }

.theme-hint {
    padding: .35rem .75rem .5rem;
    font-size: .7rem;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: rgb(var(--muted-fg));
}

.theme-option {
    display: flex;
    align-items: center;
    gap: .65rem;
    width: 100%;
    padding: .55rem .75rem;
    border: 0;
    border-radius: var(--radius);
    background: none;
    color: rgb(var(--muted-fg));
    font: inherit;
    font-weight: 500;
    cursor: pointer;
    text-align: left;
}
.theme-option:hover { background: rgb(var(--card)); color: rgb(var(--fg)); }
.theme-option.is-current { color: rgb(var(--brand)); }

.theme-tick { margin-left: auto; opacity: 0; flex: none; }
.theme-option.is-current .theme-tick { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
    .theme-options, .theme-options-inner, .theme-chevron,
    .more-menu, .more-menu-inner, .more-chevron { transition: none; }
}

.workspace {
    /* Fills the grid column. A max-width here would also drag the scrollbar
       inward, since this element is the one that scrolls. */
    padding: 2.5rem 2.5rem 3rem;
    width: 100%;
    min-width: 0;
    min-height: 0;
    position: relative;
    z-index: 1;
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
}

.workspace-head { margin-bottom: 1.75rem; }
.workspace-head h1 { font-size: 1.75rem; }
.workspace-head p { margin-top: .25rem; }

.panel {
    /* Flat: a rule and space separate sections, not a box around them. */
    padding: 2rem 0;
    border: 0;
    border-top: 1px solid rgb(var(--border));
    border-radius: 0;
    background: none;
}
.panel:first-of-type { border-top: 0; padding-top: .5rem; }

/* Settings Page & Components */
.settings-grid {
    display: grid;
    gap: 1.5rem;
}

.panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: 1.25rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid rgb(var(--border));
}

.panel-title-group {
    display: flex;
    align-items: center;
    gap: .75rem;
}

.panel-icon-badge {
    display: grid;
    place-items: center;
    width: 38px;
    height: 38px;
    border-radius: var(--radius);
    background: rgb(var(--brand-soft));
    color: rgb(var(--brand));
    flex: none;
}

.panel-title-group h2 {
    font-size: 1.125rem;
    font-weight: 600;
}

.badge {
    display: inline-flex;
    align-items: center;
    gap: .35rem;
    padding: .25rem .6rem;
    border-radius: var(--radius-full);
    font-size: .725rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: .04em;
}
.badge-success { background: rgb(var(--success-fg) / .15); color: rgb(var(--success-fg)); }
.badge-warning { background: rgb(217 119 6 / .15); color: rgb(217 119 6); }
:root.dark .badge-warning { color: #f59e0b; }
.badge-muted { background: rgb(var(--muted-fg) / .15); color: rgb(var(--muted-fg)); }

/* The admin inside a customer's account: an amber strip under the topbar on
   every page, saying whose account this is, with the way back out. */
.admin-view-bar {
    flex: none;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .9rem;
    padding: .45rem 1.5rem;
    background: rgb(217 119 6 / .14);
    border-bottom: 1px solid rgb(217 119 6 / .35);
    font-size: .875rem;
}
.admin-view-bar p { margin: 0; }
.admin-view-bar form { margin: 0; flex: none; }
.admin-view-back {
    padding: .25rem .85rem;
    border: 1px solid rgb(217 119 6 / .6);
    border-radius: 999px;
    background: transparent;
    color: inherit;
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}
.admin-view-back:hover,
.admin-view-back:focus-visible { background: rgb(217 119 6 / .2); outline: none; }
@media (max-width: 719.98px) {
    .admin-view-bar { justify-content: space-between; padding: .45rem 1rem; }
}
.feature-count form { margin: 0; }
.feature-count form .button { white-space: nowrap; }

.setting-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1rem 0;
}
.setting-row + .setting-row {
    border-top: 1px solid rgb(var(--border));
}

.setting-info { flex: 1; }
.setting-info h3 { font-size: .95rem; font-weight: 500; }
.setting-info p { font-size: .85rem; color: rgb(var(--muted-fg)); margin-top: .15rem; }

.switch {
    position: relative;
    display: inline-block;
    width: 44px;
    height: 24px;
    flex: none;
}
.switch input { opacity: 0; width: 0; height: 0; }
.slider {
    position: absolute;
    cursor: pointer;
    inset: 0;
    background-color: rgb(var(--border));
    transition: .2s ease;
    border-radius: 24px;
}
.slider:before {
    position: absolute;
    content: "";
    height: 18px;
    width: 18px;
    left: 3px;
    bottom: 3px;
    background-color: white;
    transition: .2s ease;
    border-radius: 50%;
}
input:checked + .slider { background-color: rgb(var(--brand)); }
input:checked + .slider:before { transform: translateX(20px); }

.field-grid-2 {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 1rem;
}
@media (max-width: 600px) {
    .field-grid-2 { grid-template-columns: 1fr; }
}

.panel-empty { padding: 4rem 0; text-align: center; }
.panel-empty h2 { font-size: 1.125rem; margin-bottom: .35rem; }
.panel-empty p { max-width: 44ch; margin: 0 auto; }
.panel-empty .button { margin-top: 1.25rem; }
.panel-empty .field-hint { margin-top: .6rem; }

.panel-icon { display: block; height: 64px; width: auto; margin: 0 auto 1rem; }

@media (max-width: 760px) {
    .shell { grid-template-columns: 1fr; }
    .workspace { padding: 1.75rem 1.25rem; }

    /* The menu becomes a drawer the hamburger slides in.

       It starts BELOW the topbar, not at the top of the screen. Sitting at
       top:0 put the first menu item underneath the fixed topbar, where it was
       invisible, and it also hid the close button behind the drawer. */
    .sidebar {
        position: fixed;
        top: 60px;
        left: 0;
        bottom: 0;
        height: auto;
        z-index: 45;
        width: 264px;
        max-width: 82vw;
        border-right: 1px solid rgb(var(--border));
        box-shadow: 0 0 40px rgb(0 0 0 / .18);
        transform: translateX(-100%);
        transition: transform .24s cubic-bezier(.2, .8, .3, 1);
    }
    .shell.menu-open .sidebar { transform: none; }

    /* The dim starts below the topbar too, so the close button stays reachable. */
    .shell.menu-open::before {
        content: "";
        position: fixed;
        top: 60px;
        right: 0;
        bottom: 0;
        left: 0;
        z-index: 44;
        background: rgb(0 0 0 / .38);
        animation: alert-fade .2s ease both;
    }
}

@media (min-width: 761px) {
    /* The sidebar is permanently visible on a wide screen, so a button to open
       it is redundant. It stays for the mobile drawer only. */
    .menu-toggle { display: none; }

    .shell {
        transition: grid-template-columns .22s cubic-bezier(.2, .8, .3, 1);
    }
    .shell.menu-collapsed {
        grid-template-columns: 60px 1fr;
    }
    .shell.menu-collapsed .sidebar {
        width: 60px;
        padding: 1.25rem .4rem;
        align-items: center;
    }
    .shell.menu-collapsed .nav-item span,
    .shell.menu-collapsed .nav-badge,
    .shell.menu-collapsed .nav-theme-label,
    .shell.menu-collapsed .theme-chevron,
    .shell.menu-collapsed .theme-options,
    .shell.menu-collapsed .more-chevron,
    .shell.menu-collapsed .sidebar-version {
        display: none;
    }
    .shell.menu-collapsed .nav-item {
        justify-content: center;
        padding: .6rem 0;
        width: 100%;
    }
}

@media (prefers-reduced-motion: reduce) {
    .sidebar { transition: none; }
}

.auth-back { margin-top: .5rem; }
.auth-back a { color: rgb(var(--muted-fg)); }
.auth-back a:hover { color: rgb(var(--fg)); }

/* ----------------------------------------------------------- account menu */

.sr-only {
    position: absolute;
    width: 1px; height: 1px;
    padding: 0; margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
}

.account { position: relative; }

.avatar {
    display: grid;
    place-items: center;
    width: 36px;
    height: 36px;
    padding: 0;
    overflow: hidden;
    border: 1px solid rgb(var(--border));
    border-radius: 50%;
    background: rgb(var(--brand-soft));
    color: rgb(var(--brand));
    font: inherit;
    font-size: .95rem;
    font-weight: 600;
    cursor: pointer;
    transition: border-color .12s ease;
}
.avatar:hover { border-color: rgb(var(--brand)); }
.avatar img { width: 100%; height: 100%; object-fit: cover; display: block; }
.account.is-open .avatar { border-color: rgb(var(--brand)); }

/* A Pro account wears a green ring that shines past the edge, so the plan
   reads from the topbar without opening a single page. */
.avatar.is-pro,
.avatar.is-pro:hover,
.account.is-open .avatar.is-pro {
    border-color: transparent;
    box-shadow: 0 0 0 2px rgb(var(--success-fg)),
                0 0 12px 3px rgb(var(--success-fg) / .45);
}

.account-menu {
    position: absolute;
    top: calc(100% + .5rem);
    right: 0;
    z-index: 50;
    width: 232px;
    padding: .35rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-sm);
    background: rgb(var(--card));
    box-shadow: 0 12px 32px rgb(0 0 0 / .16);
    /* Hidden by visibility rather than display, so it can animate. */
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px) scale(.98);
    transform-origin: top right;
    transition: opacity .16s ease, transform .18s cubic-bezier(.2, .8, .3, 1),
                visibility .18s;
}
.account.is-open .account-menu {
    opacity: 1;
    visibility: visible;
    transform: none;
}
.account-menu form { margin: 0; }

.account-who {
    padding: .6rem .65rem .65rem;
    margin-bottom: .3rem;
    border-bottom: 1px solid rgb(var(--border));
}
.account-name { font-weight: 600; }
.account-email {
    margin-top: .1rem;
    font-size: .78rem;
    color: rgb(var(--muted-fg));
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.account-item {
    display: flex;
    align-items: center;
    gap: .6rem;
    width: 100%;
    padding: .55rem .65rem;
    border: 0;
    border-radius: var(--radius-xs);
    background: none;
    color: rgb(var(--fg));
    font: inherit;
    font-weight: 500;
    text-align: left;
    text-decoration: none;
    cursor: pointer;
}
.account-item:hover { background: rgb(var(--surface)); text-decoration: none; }
.account-signout { color: rgb(var(--danger-fg)); }

@media (prefers-reduced-motion: reduce) {
    .account-menu { transition: none; }
}

.facts { display: grid; gap: 1.1rem; }
.facts > div { display: grid; gap: .2rem; }
.facts dt {
    font-size: .75rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: rgb(var(--muted-fg));
}
.facts dd { margin: 0; font-weight: 500; }

/* --------------------------------------------------------- password reveal */

.password-wrap { position: relative; }
/* Room for the eye so long passwords never run underneath it. */
.password-wrap input { padding-right: 2.9rem; }

.password-eye {
    position: absolute;
    top: 50%;
    right: .3rem;
    transform: translateY(-50%);
    display: grid;
    place-items: center;
    width: 34px;
    height: 34px;
    padding: 0;
    border: 0;
    border-radius: var(--radius-xs);
    background: none;
    color: rgb(var(--muted-fg));
    cursor: pointer;
}
.password-eye:hover { color: rgb(var(--fg)); }

/* -------------------------------------------------------------- scrollbars */

/* Firefox. Colours come from the theme tokens so it follows light and dark. */
* {
    scrollbar-width: thin;
    scrollbar-color: rgb(var(--muted-fg) / .38) transparent;
}

/* Chrome, Edge, Safari. Overlaying the track keeps the layout width stable. */
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-corner { background: transparent; }

::-webkit-scrollbar-thumb {
    background-color: rgb(var(--muted-fg) / .3);
    border-radius: 999px;
    /* A transparent border painted over the background makes the thumb read as
       a slim pill with breathing room, rather than a bar jammed to the edge. */
    border: 3px solid transparent;
    background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background-color: rgb(var(--muted-fg) / .55); }
::-webkit-scrollbar-thumb:active { background-color: rgb(var(--brand)); }

/* The scrollbar only appears while there is something to scroll, and fades in
   on hover over the region that owns it. */
.workspace::-webkit-scrollbar-thumb,
.sidebar::-webkit-scrollbar-thumb { background-color: rgb(var(--muted-fg) / .22); }
.workspace:hover::-webkit-scrollbar-thumb,
.sidebar:hover::-webkit-scrollbar-thumb { background-color: rgb(var(--muted-fg) / .42); }

/* ----------------------------------------------------------- settings page */

/* Two cards side by side that fold to one column on a narrow screen. */
.split {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
    gap: 2.5rem;
    align-items: start;
}
/* A hairline between the two halves, only when they sit side by side. */
@media (min-width: 720px) {
    .split > * + * {
        padding-left: 2.5rem;
        border-left: 1px solid rgb(var(--border));
    }
    .split-tight > * + * { padding-left: 0; border-left: 0; }
}
.split-tight { gap: 1rem; }




.panel-head { margin-bottom: 1.25rem; }
.panel-head h2 { font-size: 1.0625rem; }
.panel-head p { margin-top: .2rem; font-size: .875rem; }
.panel-head-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
}

.pill-on { background: rgb(var(--brand-soft)); color: rgb(var(--brand)); border-color: transparent; }
.pill-off { background: rgb(var(--surface)); color: rgb(var(--muted-fg)); }

.notice {
    padding: .7rem .85rem;
    margin-bottom: 1rem;
    border: 1px solid rgb(var(--border));
    border-left: 3px solid rgb(var(--brand));
    border-radius: var(--radius);
    background: rgb(var(--surface));
    font-size: .875rem;
}

.row-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; margin-top: 1rem; }

.button-danger { background: rgb(var(--danger-fg)); border-color: rgb(var(--danger-fg)); color: #fff; }
.button-danger:hover { background: rgb(var(--danger-fg)); filter: brightness(.92); }

/* ------------------------------------------------------------------ sheets */

/* Hidden overlays must never cover the page or capture a click. The explicit
   rule also protects this behaviour from component display declarations. */
.alert-backdrop[hidden],
.sheet-backdrop[hidden] {
    display: none !important;
    pointer-events: none !important;
}

/* Shared alert used by async forms and confirmation prompts. This markup is
   rendered after the app shell, so it must be a fixed layer; without these
   rules it lands below the locked viewport and appears as an unresponsive page. */
.alert-backdrop {
    position: fixed;
    inset: 0;
    z-index: 220;
    display: grid;
    place-items: center;
    padding: 1.25rem;
    background: rgb(0 0 0 / .42);
    -webkit-backdrop-filter: blur(3px);
            backdrop-filter: blur(3px);
    animation: alert-fade .18s ease both;
}

.alert-panel {
    width: min(100%, 360px);
    overflow: hidden;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-md);
    background: rgb(var(--card));
    box-shadow: 0 20px 55px rgb(0 0 0 / .3);
    animation: alert-pop .22s cubic-bezier(.2, .9, .3, 1.06) both;
}
.alert-panel:focus { outline: none; }
.alert-panel.is-closing { animation: alert-pop-out .14s ease both; }

.alert-body { padding: 1.25rem 1.25rem 1.1rem; text-align: center; }
.alert-title { font-size: 1rem; font-weight: 600; }
.alert-message {
    margin-top: .4rem;
    color: rgb(var(--muted-fg));
    font-size: .875rem;
}
.alert-message[hidden] { display: none !important; }

.alert-action {
    width: 100%;
    min-height: 46px;
    padding: .7rem 1rem;
    border: 0;
    border-top: 1px solid rgb(var(--border));
    background: transparent;
    color: rgb(var(--brand));
    font: inherit;
    font-weight: 600;
    cursor: pointer;
}
.alert-action:hover { background: rgb(var(--surface)); }

@keyframes alert-fade {
    from { opacity: 0; }
    to { opacity: 1; }
}
@keyframes alert-pop {
    from { opacity: 0; transform: translateY(8px) scale(.97); }
    to { opacity: 1; transform: none; }
}
@keyframes alert-pop-out {
    from { opacity: 1; transform: none; }
    to { opacity: 0; transform: translateY(5px) scale(.98); }
}

.sheet-backdrop {
    position: fixed;
    inset: 0;
    z-index: 210;
    display: grid;
    place-items: center;
    padding: 1.5rem;
    overflow-y: auto;
    background: rgb(0 0 0 / .38);
    -webkit-backdrop-filter: blur(3px);
            backdrop-filter: blur(3px);
    animation: alert-fade .18s ease both;
}

.sheet {
    width: 100%;
    max-width: 420px;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-md);
    background: rgb(var(--card));
    box-shadow: 0 20px 50px rgb(0 0 0 / .3);
    animation: alert-pop .22s cubic-bezier(.2, .9, .3, 1.06) both;
}
.sheet:focus { outline: none; }

.sheet-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 1.1rem 1.25rem;
    border-bottom: 1px solid rgb(var(--border));
}
.sheet-head h2 { font-size: 1.0625rem; }
.sheet-body { padding: 1.25rem; }

.steps {
    margin: 0 0 1.25rem;
    padding-left: 1.15rem;
    display: grid;
    gap: .4rem;
    font-size: .875rem;
    color: rgb(var(--muted-fg));
}

.qr-holder {
    display: grid;
    place-items: center;
    /* A generous quiet zone is not padding for looks: scanners need clear
       margin around the code to lock onto it. */
    padding: 1.35rem;
    margin-bottom: 1rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-md);
    /* The QR stays dark on white in both themes, or cameras can't read it. */
    background: #fff;
}
.qr-holder img {
    display: block;
    width: 216px;
    height: 216px;
    /* Never let a half-loaded rotation show as a smeared code. */
    image-rendering: pixelated;
}

/* Two diagonal corner marks, the way a viewfinder frames a subject. They say
   "aim here" without spending a line of copy on it. Four would read as a stock
   scanner overlay; two read as a decision. */
.qr-holder::before,
.qr-holder::after {
    content: '';
    position: absolute;
    width: 20px;
    height: 20px;
    border: 2px solid rgb(var(--brand));
    pointer-events: none;
}
.qr-holder::before {
    top: .55rem;
    left: .55rem;
    border-right: 0;
    border-bottom: 0;
    border-top-left-radius: 7px;
}
.qr-holder::after {
    right: .55rem;
    bottom: .55rem;
    border-left: 0;
    border-top: 0;
    border-bottom-right-radius: 7px;
}

/* A rotation replaces the image in place. This fade is the only thing that
   tells you it happened, so it must never blank the frame on the way. */
.qr-holder img.is-fresh { animation: qr-arrive .34s ease-out; }

@keyframes qr-arrive {
    from { opacity: .25; }
    to   { opacity: 1; }
}

.centered { text-align: center; }

.totp-secret {
    margin: .3rem 0 1.25rem;
    text-align: center;
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: .95rem;
    letter-spacing: .06em;
    word-break: break-all;
}

.codes {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: .5rem;
    margin: 1.1rem 0 0;
    padding: 0;
    list-style: none;
}
.codes li {
    padding: .5rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-xs);
    background: rgb(var(--surface));
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: .9rem;
    text-align: center;
}

/* The prompt variant of the iOS dialog. */
.alert-input {
    width: calc(100% - 2rem);
    margin: .9rem 1rem 0;
    padding: .55rem .7rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-xs);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font: inherit;
    text-align: center;
}
.alert-input:focus { outline: none; border-color: rgb(var(--brand)); }

.alert-actions { display: grid; grid-template-columns: 1fr 1fr; }
.alert-action-split { border-top: 1px solid rgb(var(--border)); }
.alert-action-split + .alert-action-split { border-left: 1px solid rgb(var(--border)); }
:root.dark .alert-action-split { border-top-color: rgb(255 255 255 / .16); }
:root.dark .alert-action-split + .alert-action-split { border-left-color: rgb(255 255 255 / .16); }
.alert-action.is-danger { color: rgb(var(--danger-fg)); }

@media (max-width: 560px) {
    .codes { grid-template-columns: 1fr; }
}

.profile { display: flex; align-items: center; gap: 1rem; flex-wrap: wrap; }
.profile-photo {
    width: 60px;
    height: 60px;
    border-radius: 50%;
    object-fit: cover;
    flex: none;
}
.profile-initial {
    display: grid;
    place-items: center;
    background: rgb(var(--brand-soft));
    color: rgb(var(--brand));
    font-size: 1.5rem;
    font-weight: 600;
}
.profile-who { flex: 1; min-width: 0; }
.profile-name { font-size: 1.125rem; font-weight: 600; letter-spacing: -.01em; }

.tag-ok { background: rgb(var(--brand-soft)); color: rgb(var(--brand)); }
.tag-wait { background: rgb(var(--surface)); color: rgb(var(--muted-fg)); }
.facts dd .tag { margin-left: .4rem; }

/* A wide, landscape dialog on desktop: QR beside the instructions, so the
   sheet stays short instead of running down the screen. */
.sheet-wide { max-width: 680px; }

@media (min-width: 720px) {
    .sheet-cols {
        display: grid;
        /* The first column takes the QR holder's natural width: the code plus
           its scanner quiet zone. A fixed track squeezed the holder and left
           everything under it sitting crooked. */
        grid-template-columns: auto 1fr;
        gap: 1.75rem;
        align-items: start;
    }
    .sheet-cols .steps { margin-top: .15rem; }
    .sheet-cols .qr-holder { margin-bottom: .75rem; }
    .sheet-cols .totp-secret { margin-bottom: 0; font-size: .85rem; }
    .sheet-cols .code { margin-top: 1.25rem; }
}

.secret-row {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    margin-bottom: 1.25rem;
}
.secret-row .totp-secret { margin: 0; }

/* The tick replaces the copy glyph for a moment after a successful copy. */
#copy-secret .icon-copied { display: none; }
#copy-secret.is-copied .icon-copy { display: none; }
#copy-secret.is-copied .icon-copied { display: block; color: rgb(var(--brand)); }
#copy-secret.is-copied { border-color: rgb(var(--brand)); }

/* --------------------------------------------------- current value summary */

.current {
    display: flex;
    align-items: center;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 1rem;
}
.current[hidden] { display: none; }

.current-label {
    font-size: .75rem;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: rgb(var(--muted-fg));
}
.current-value {
    margin-top: .2rem;
    font-size: 1.0625rem;
    font-weight: 500;
    word-break: break-word;
}
.current-hidden { letter-spacing: .12em; }

/* Grows to the form's natural height, with no fixed pixel guess. */
.reveal {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows .28s cubic-bezier(.2, .8, .3, 1);
}
.reveal.is-open { grid-template-rows: 1fr; }
.reveal-inner { overflow: hidden; opacity: 0; transition: opacity .2s ease; }
.reveal.is-open .reveal-inner { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
    .reveal { transition: none; }
}

/* ----------------------------------------------------------------- devices */

.workspace-head-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    flex-wrap: wrap;
    gap: 1rem;
}

.devices { margin: 0; padding: 0; list-style: none; }

.device {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1.1rem 0;
    border-top: 1px solid rgb(var(--border));
}
.device:last-child { border-bottom: 1px solid rgb(var(--border)); }

.device-dot {
    flex: none;
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: rgb(var(--muted-fg) / .45);
}
.device-dot.is-on {
    background: rgb(var(--success-fg));
    /* A soft halo so "connected" reads at a glance. */
    box-shadow: 0 0 0 4px rgb(var(--success-fg) / .16);
}

.device-who { flex: 1; min-width: 0; }
.device-name { font-weight: 500; }
.device-meta { font-size: .8125rem; margin-top: .1rem; }
.device-actions { display: flex; align-items: center; gap: .75rem; flex: none; }

.empty-state { padding: 4rem 0; text-align: center; }
.empty-state h2 { font-size: 1.125rem; margin-bottom: .35rem; }
.empty-state p { max-width: 44ch; margin: 0 auto; }
.empty-state .button { margin-top: 1.25rem; }

.qr-holder { position: relative; }
.qr-waiting {
    position: absolute;
    inset: 0;
    z-index: 1;
    display: grid;
    place-items: center;
    background: #fff;
    border-radius: var(--radius-md);
    color: #6b7280;
    font-size: .85rem;
}
.qr-waiting[hidden] { display: none; }
/* Above the waiting cover, so the frame holds its shape while a code loads
   and the panel never collapses to a blank white square. */
.qr-holder::before,
.qr-holder::after { z-index: 2; }

.link-status { margin: 1rem 0 0; font-size: .875rem; color: rgb(var(--muted-fg)); }

/* The whole box is the button. The code is what someone is reaching for, so
   it is the target; a small icon on its own would be a poor one on a phone,
   which is where this screen is usually read. */
.pair-code {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    margin-top: 1rem;
    /* Matching room either side keeps the code optically centred with the
       icon sitting in the right-hand gap rather than pushing it along. */
    padding: .9rem 3.1rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-sm);
    background: rgb(var(--surface));
    color: rgb(var(--fg));
    font: inherit;
    cursor: pointer;
    transition: border-color .15s ease, background-color .15s ease;
}
.pair-code:hover {
    border-color: rgb(var(--brand) / .55);
    background: rgb(var(--brand-soft));
}
.pair-code[hidden] { display: none; }

.pair-code-value {
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: 1.5rem;
    font-weight: 700;
    /* The trailing space this adds after the last character is nudged back
       off, so the code sits centred rather than a hair to the left. */
    letter-spacing: .18em;
    margin-right: -.18em;
    text-align: center;
    word-break: break-all;
}

.pair-code-copy {
    position: absolute;
    top: 50%;
    right: 1rem;
    transform: translateY(-50%);
    display: grid;
    place-items: center;
    color: rgb(var(--muted-fg));
    transition: color .15s ease;
}
.pair-code:hover .pair-code-copy { color: rgb(var(--brand)); }

/* Both icons occupy the same cell, so swapping them cannot shift the box. */
.pair-code-icon { grid-area: 1 / 1; }
.pair-code-icon.is-done { opacity: 0; transform: scale(.6); }
.pair-code-icon.is-copy,
.pair-code-icon.is-done { transition: opacity .18s ease, transform .18s ease; }

.pair-code.is-copied {
    border-color: rgb(var(--brand));
    background: rgb(var(--brand-soft));
}
.pair-code.is-copied .pair-code-copy { color: rgb(var(--brand)); }
.pair-code.is-copied .pair-code-icon.is-copy { opacity: 0; transform: scale(.6); }
.pair-code.is-copied .pair-code-icon.is-done { opacity: 1; transform: scale(1); }

.pair-code-note {
    margin-top: .5rem;
    font-size: .8125rem;
    color: rgb(var(--muted-fg));
    text-align: center;
}
.pair-code-note[hidden] { display: none; }
.pair-code.is-copied + .pair-code-note { color: rgb(var(--brand)); }

@media (prefers-reduced-motion: reduce) {
    .pair-code,
    .pair-code-copy,
    .pair-code-icon { transition: none; }
}

@media (max-width: 620px) {
    .device { flex-wrap: wrap; }
    .device-actions { width: 100%; padding-left: 1.75rem; }
}

/* ------------------------------------------------- animated burger button */

.burger {
    position: relative;
    display: block;
    width: 18px;
    height: 14px;
}
.burger i {
    position: absolute;
    left: 0;
    display: block;
    width: 100%;
    height: 2px;
    border-radius: 2px;
    background: currentColor;
    transition: transform .28s cubic-bezier(.2, .8, .3, 1),
                opacity .18s ease,
                top .28s cubic-bezier(.2, .8, .3, 1);
}
.burger i:nth-child(1) { top: 0; }
.burger i:nth-child(2) { top: 6px; }
.burger i:nth-child(3) { top: 12px; }

/* Open: the outer bars meet in the middle and cross, the middle one fades. */
.menu-toggle[aria-expanded="true"] .burger i:nth-child(1) { top: 6px; transform: rotate(45deg); }
.menu-toggle[aria-expanded="true"] .burger i:nth-child(2) { opacity: 0; transform: scaleX(.4); }
.menu-toggle[aria-expanded="true"] .burger i:nth-child(3) { top: 6px; transform: rotate(-45deg); }

@media (prefers-reduced-motion: reduce) {
    .burger i { transition: none; }
}

/* --------------------------------------------------------- route progress */

/* Fills while the next page loads, so a tap feels answered immediately. */
.route-bar {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 200;
    height: 2px;
    width: 0;
    background: rgb(var(--brand));
    opacity: 0;
    pointer-events: none;
}
.route-bar.is-going {
    opacity: 1;
    width: 88%;
    /* Slows as it goes, so it never finishes before the page does. */
    transition: width 2.4s cubic-bezier(.05, .7, .1, 1), opacity .1s ease;
}
.route-bar.is-done {
    opacity: 0;
    width: 100%;
    transition: width .18s ease, opacity .3s ease .1s;
}

/* The item you tapped highlights at once, before the new page arrives. */
.nav-item.is-going {
    background: rgb(var(--brand-soft));
    color: rgb(var(--brand));
}

/* While that tap is being answered the page you are leaving is still on screen,
   so its menu item is still marked current. Highlighting both at once reads as
   two selected rows, and side by side they merge into one block. The highlight
   belongs to the item you tapped and to nothing else until the page arrives. */
.is-navigating .nav-item:not(.is-going) {
    background: transparent;
    color: rgb(var(--muted-fg));
}

/* ------------------------------------------------------- mobile top bar */

@media (max-width: 760px) {
    /* Burger left, logo dead centre, avatar right. */
    .topbar {
        display: grid;
        grid-template-columns: 1fr auto 1fr;
        padding: 0 .9rem;
    }
    .topbar-left { justify-self: start; }
    .brand { position: absolute; left: 50%; transform: translateX(-50%); }
    .topbar-actions { grid-column: 3; justify-self: end; }
    .brand-logo { height: 24px; }
}

/* -------------------------------------------------------------- kebab menu */

.kebab { position: relative; flex: none; }

.kebab-menu {
    position: absolute;
    top: calc(100% + .4rem);
    right: 0;
    z-index: 60;
    min-width: 210px;
    padding: .3rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-sm);
    background: rgb(var(--card));
    box-shadow: 0 12px 32px rgb(0 0 0 / .18);
    /* Hidden by visibility so it can animate rather than snap. */
    opacity: 0;
    visibility: hidden;
    transform: translateY(-6px) scale(.98);
    transform-origin: top right;
    transition: opacity .16s ease, transform .18s cubic-bezier(.2, .8, .3, 1), visibility .18s;
}
.kebab.is-open .kebab-menu { opacity: 1; visibility: visible; transform: none; }
.kebab.is-open > button { border-color: rgb(var(--brand)); color: rgb(var(--fg)); }

.kebab-item {
    display: flex;
    align-items: center;
    gap: .6rem;
    width: 100%;
    padding: .55rem .65rem;
    border: 0;
    border-radius: var(--radius-xs);
    background: none;
    color: rgb(var(--fg));
    font: inherit;
    font-weight: 500;
    text-align: left;
    cursor: pointer;
}
.kebab-item:hover { background: rgb(var(--surface)); }
.kebab-item svg { flex: none; color: rgb(var(--muted-fg)); }
.kebab-item.is-danger { color: rgb(var(--danger-fg)); }
.kebab-item.is-danger svg { color: rgb(var(--danger-fg)); }

@media (prefers-reduced-motion: reduce) {
    .kebab-menu { transition: none; }
}

/* ---------------------------------------------------- the connecting hold */

/* No panel edge here on purpose: the animation sits on a soft wash so the
   moment reads as the app working, not as another dialog to dismiss. */
.connecting-backdrop {
    background: rgb(var(--bg) / .82);
    -webkit-backdrop-filter: blur(10px);
            backdrop-filter: blur(10px);
}

.connecting {
    text-align: center;
    animation: connect-in .32s cubic-bezier(.2, .8, .3, 1) both;
}

.connecting-anim {
    width: 190px;
    height: 190px;
    margin: 0 auto .5rem;
}
.connecting-anim svg { display: block; }

.connecting-title {
    font-size: 1.25rem;
    font-weight: 600;
    letter-spacing: -.015em;
}
.connecting-note {
    margin-top: .35rem;
    font-size: .9375rem;
    color: rgb(var(--muted-fg));
}

/* Leaving: lift and fade, so the device list feels like it comes forward. */
.connecting-backdrop.is-leaving { animation: connect-out .3s ease both; }

@keyframes connect-in {
    from { opacity: 0; transform: translateY(10px) scale(.97); }
}
@keyframes connect-out {
    to { opacity: 0; transform: translateY(-8px) scale(1.02); }
}

@media (prefers-reduced-motion: reduce) {
    .connecting, .connecting-backdrop.is-leaving { animation: none; }
}

/* -------------------------------------------------- content loading state */

.workspace-loading {
    display: grid;
    place-items: center;
    gap: .9rem;
    padding: 5rem 0;
    /* Never captured by the page transition, so it cannot be frozen as a still. */
    view-transition-name: none;
    /* Held back a moment: on a fast connection the page usually wins the race,
       and a spinner that flashes for 80ms looks worse than none at all. */
    animation: loading-in .2s ease .12s both;
}

.spinner {
    display: block;
    width: 28px;
    height: 28px;
    border-radius: 50%;
    border: 2.5px solid rgb(var(--muted-fg) / .22);
    border-top-color: rgb(var(--brand));
    animation: spin .65s linear infinite;
}

@keyframes loading-in { from { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
    .workspace-loading { animation: none; }
    .spinner { animation-duration: 1.4s; }
}

/* ---------------------------------------------------- page-to-page handover */

/* Chrome cross-fades between documents instead of blanking and repainting.
   Kept very short on purpose: the outgoing page is captured as a still image,
   so anything slower freezes the loading spinner mid-spin and reads as a hang. */
@view-transition { navigation: auto; }

/* Chrome names these on the old and new page, so they morph instead of fading,
   which keeps the menu and topbar rock steady while the content swaps. */
.topbar { view-transition-name: topbar; }
.sidebar { view-transition-name: sidebar; }

::view-transition-old(root) { animation: page-out .08s linear both; }
::view-transition-new(root) { animation: page-in .13s ease-out both; }

/* Fade only. Any movement here reads as another delay before the content
   settles, which is the opposite of what a fast app feels like. */
@keyframes page-out { to   { opacity: 0; } }
@keyframes page-in  { from { opacity: 0; } }

/* The topbar and menu do not animate at all; they simply stay put. */
::view-transition-old(topbar),
::view-transition-new(topbar),
::view-transition-old(sidebar),
::view-transition-new(sidebar) { animation: none; }

@media (prefers-reduced-motion: reduce) {
    ::view-transition-old(root),
    ::view-transition-new(root) { animation: none; }
}

/* The burger is a plain glyph, not a boxed control. */
.menu-toggle {
    border: 0;
    background: none;
    width: 30px;
    margin-left: -.2rem;
    color: rgb(var(--fg));
}
.menu-toggle:hover { background: none; color: rgb(var(--brand)); }

/* The QR panel is white in both themes, so its spinner needs its own colours. */
.qr-waiting {
    display: grid;
    place-items: center;
    gap: .75rem;
    align-content: center;
}
.spinner-on-light {
    border-color: rgb(0 0 0 / .12);
    border-top-color: #078271;
}

/* ------------------------------------------------------------ qr liveness */

/* There is no countdown. Only the gateway knows when a code rotates, so the
   honest signal is that this one is current, not how long it has left. */
.qr-live {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .45rem;
    margin-top: .8rem;
    font-size: .75rem;
    letter-spacing: .01em;
    text-align: center;
    color: rgb(var(--muted-fg));
}
.qr-live[hidden] { display: none; }

.qr-live-dot {
    position: relative;
    flex: none;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: rgb(var(--brand));
}
/* One ring, breathing slowly. Enough to read as live, quiet enough to sit
   under a code someone is trying to hold a camera steady against. */
.qr-live-dot::after {
    content: '';
    position: absolute;
    inset: -3px;
    border-radius: 50%;
    border: 1px solid rgb(var(--brand));
    animation: qr-breathe 2.4s ease-out infinite;
}

@keyframes qr-breathe {
    0%   { opacity: .55; transform: scale(.6); }
    70%  { opacity: 0;   transform: scale(1.3); }
    100% { opacity: 0;   transform: scale(1.3); }
}

.qr-refresh-button {
    display: block;
    margin: .55rem auto 0;
    font-size: .8rem;
}
.qr-refresh-button[hidden] { display: none; }

/* ------------------------------------------------------ pairing code wait */

.pair-waiting {
    display: flex;
    align-items: center;
    gap: .7rem;
    margin-top: 1rem;
    padding: .85rem 1rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-sm);
    background: rgb(var(--surface));
    font-size: .875rem;
}
.pair-waiting[hidden] { display: none; }
.pair-waiting .spinner { width: 20px; height: 20px; border-width: 2px; flex: none; }

@media (prefers-reduced-motion: reduce) {
    .qr-holder img.is-fresh { animation: none; }
    .qr-live-dot::after { animation: none; opacity: .5; transform: scale(1); }
}

/* ----------------------------------------------------------- pick a device */

.picklist { margin: 0; padding: 0; list-style: none; }

.pick {
    position: relative;
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1.1rem .9rem;
    border-top: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    color: rgb(var(--fg));
    text-decoration: none;
    transition: background .16s ease;
}
.picklist li:last-child .pick { border-bottom: 1px solid rgb(var(--border)); }

.pick:hover,
.pick:focus-visible {
    background: rgb(var(--surface));
    text-decoration: none;
}
/* A teal edge on the left, drawn in rather than always present. */
.pick::before {
    content: "";
    position: absolute;
    left: 0;
    top: 50%;
    width: 3px;
    height: 0;
    border-radius: 0 3px 3px 0;
    background: rgb(var(--brand));
    transform: translateY(-50%);
    transition: height .18s ease;
}
.pick:hover::before,
.pick:focus-visible::before { height: 62%; }

.pick:hover .pick-name { color: rgb(var(--brand)); }
.pick:hover .pick-go { color: rgb(var(--brand)); transform: translateX(3px); }

/* Held while the next page loads, so the tap is clearly registered. */
.pick.is-loading { background: rgb(var(--surface)); pointer-events: none; }
.pick.is-loading .pick-go svg { display: none; }
.pick.is-loading .pick-go::after {
    content: "";
    display: block;
    width: 16px;
    height: 16px;
    border-radius: 50%;
    border: 2px solid rgb(var(--muted-fg) / .25);
    border-top-color: rgb(var(--brand));
    animation: spin .6s linear infinite;
}

.pick-who { flex: 1; min-width: 0; }
.pick-name { display: block; font-weight: 500; }
.pick-meta { display: block; font-size: .8125rem; margin-top: .1rem; }

.pick-go {
    flex: none;
    color: rgb(var(--muted-fg));
    transition: transform .16s ease, color .16s ease;
}

.offline-note { margin-top: 1.25rem; }

.crumb { margin-bottom: .4rem; font-size: .8125rem; }

/* ------------------------------------------------------------ status pill */

.device-status { margin-top: .45rem; }

.status-pill {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
    padding: .22rem .6rem .22rem .5rem;
    border-radius: var(--radius-full);
    font-size: .78rem;
    font-weight: 500;
    letter-spacing: .01em;
}

.status-pill .status-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: currentColor;
    flex: none;
}

.status-pill.is-on {
    background: rgb(var(--success-fg) / .12);
    color: rgb(var(--success-fg));
}
/* A slow pulse on the dot, so "live" reads without anything moving loudly. */
.status-pill.is-on .status-dot { animation: pulse-dot 2.2s ease-in-out infinite; }

.status-pill.is-off {
    background: rgb(var(--muted-fg) / .12);
    color: rgb(var(--muted-fg));
}

/* Parked by the plan, not broken: the amber of "waiting", not an error. */
.status-pill.is-paused {
    background: rgb(217 119 6 / .14);
    color: rgb(217 119 6);
}
:root.dark .status-pill.is-paused { color: #f59e0b; }

@keyframes pulse-dot {
    0%, 100% { box-shadow: 0 0 0 0 rgb(var(--success-fg) / .45); }
    70%      { box-shadow: 0 0 0 5px rgb(var(--success-fg) / 0); }
}

:root.dark .status-pill.is-on { background: rgb(var(--success-fg) / .18); color: #4ade80; }

@media (prefers-reduced-motion: reduce) {
    .status-pill.is-on .status-dot { animation: none; }
}

/* ------------------------------------------------------------ bot options */

.options {
    margin: 0;
    padding: 0;
    list-style: none;
    /* On the list, not on the last child. Filtering hides children, and
       :last-child would put the closing rule under something invisible. */
    border-bottom: 1px solid rgb(var(--border));
}
.options.is-empty { border-bottom: 0; }

.option {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 2rem;
    padding: 1.4rem 0;
    border-top: 1px solid rgb(var(--border));
}
.option[hidden] { display: none; }

/* ----------------------------------------------------- filtering the list */

.filter {
    position: relative;
    display: flex;
    align-items: center;
    margin-bottom: .25rem;
    max-width: 26rem;
}
.filter svg {
    position: absolute;
    left: .75rem;
    color: rgb(var(--muted-fg));
    pointer-events: none;
}
.filter input {
    width: 100%;
    padding: .6rem .75rem .6rem 2.35rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font: inherit;
}
.filter input::placeholder { color: rgb(var(--muted-fg) / .8); }
.filter input:focus {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}
/* The browser's own clear button is inconsistent, so we draw our own. */
.filter input::-webkit-search-cancel-button { display: none; }

.filter-clear {
    position: absolute;
    right: .4rem;
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: none;
    color: rgb(var(--muted-fg));
    cursor: pointer;
}
@media (hover: hover) {
    .filter-clear:hover { background: rgb(var(--surface)); color: rgb(var(--fg)); }
}
.filter-clear[hidden] { display: none; }

.filter-empty { padding: 2rem 0; color: rgb(var(--muted-fg)); }
.filter-empty[hidden] { display: none; }
.filter-empty strong { color: rgb(var(--fg)); font-weight: 500; }

.option-text { flex: 1; min-width: 0; }
.option-name { font-weight: 500; font-size: 1.0625rem; }
.option-note { margin-top: .25rem; font-size: .875rem; max-width: 56ch; }

/* --------------------------------------------------------------- a switch */

.switch {
    position: relative;
    flex: none;
    width: 52px;
    height: 30px;
    margin-top: .15rem;
    padding: 0;
    border: 1px solid rgb(var(--border));
    border-radius: 999px;
    background: rgb(var(--surface));
    cursor: pointer;
    transition: background .18s ease, border-color .18s ease;
}
.switch[aria-checked="true"] {
    background: rgb(var(--brand));
    border-color: rgb(var(--brand));
}

.switch-knob {
    position: absolute;
    top: 3px;
    left: 3px;
    width: 22px;
    height: 22px;
    border-radius: 50%;
    background: #fff;
    box-shadow: 0 1px 3px rgb(0 0 0 / .25);
    transition: transform .2s cubic-bezier(.2, .8, .3, 1);
}
.switch[aria-checked="true"] .switch-knob { transform: translateX(22px); }
:root.dark .switch[aria-checked="false"] .switch-knob { background: rgb(var(--muted-fg)); }

/* No spinner here. The switch already moved, and a knob that turns into a
   spinner mid-flick reads as hesitation. It only blocks a second tap. */
.switch.is-saving { pointer-events: none; }

/* Saved: a quiet ring, instead of a dialog for every flick. */
.switch.is-saved { animation: switch-saved .9s ease; }
@keyframes switch-saved {
    0%   { box-shadow: 0 0 0 0 rgb(var(--brand) / .5); }
    70%  { box-shadow: 0 0 0 7px rgb(var(--brand) / 0); }
    100% { box-shadow: 0 0 0 0 rgb(var(--brand) / 0); }
}

/* An option's own setting, shown only while its switch is on. The command
   reads as a value you have, not a field to fill in, until you ask to edit. */
.option .current { padding-top: 1rem; }

/* Label above, then the value with its edit button sitting right beside it.
   Pushed to opposite ends of the row the button reads as unrelated. */
.current-stacked { display: block; }
.current-row {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-top: .3rem;
}
.current-row .current-value { margin-top: 0; }

.current-command {
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    color: rgb(var(--brand));
}

.icon-edit {
    flex: none;
    display: grid;
    place-items: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: transparent;
    color: rgb(var(--muted-fg));
    cursor: pointer;
    transition: color .15s ease, border-color .15s ease, background .15s ease;
}
@media (hover: hover) {
    .icon-edit:hover {
        color: rgb(var(--brand));
        border-color: rgb(var(--brand));
        background: rgb(var(--brand-soft));
    }
}
.icon-edit:focus-visible {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}

/* A short list of answers behind a trigger. Closed it reads as a setting you
   already have; open it is the whole list, with a tick on the saved one. */
.picker { padding-top: 1rem; max-width: 22rem; }
.choice-label {
    margin-bottom: .45rem;
    font-size: .8125rem;
    font-weight: 500;
}

.picker-trigger {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 100%;
    padding: .6rem .8rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font: inherit;
    cursor: pointer;
    text-align: left;
    transition: border-color .15s ease;
}
@media (hover: hover) {
    .picker-trigger:hover { border-color: rgb(var(--muted-fg) / .5); }
}
.picker-trigger:focus-visible {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}
.picker-current { flex: 1; min-width: 0; }

/* The trigger's own tick confirms the value showing there is the saved one. */
.picker-state { display: grid; place-items: center; width: 16px; height: 16px; flex: none; }
.picker-state .picker-tick { color: rgb(var(--brand)); opacity: 0; transition: opacity .2s ease; }
.picker.is-saved .picker-state .picker-tick { opacity: 1; }

.picker-chevron {
    flex: none;
    color: rgb(var(--muted-fg));
    transition: transform .28s cubic-bezier(.2, .8, .3, 1);
}
.picker.is-open .picker-chevron { transform: rotate(180deg); }

/* 0fr to 1fr animates to the list's natural height with no magic numbers. */
.picker-options {
    display: grid;
    grid-template-rows: 0fr;
    transition: grid-template-rows .28s cubic-bezier(.2, .8, .3, 1);
}
.picker.is-open .picker-options { grid-template-rows: 1fr; }

.picker-options-inner {
    overflow: hidden;
    opacity: 0;
    transform: translateY(6px);
    transition: opacity .2s ease, transform .28s cubic-bezier(.2, .8, .3, 1);
}
.picker.is-open .picker-options-inner { opacity: 1; transform: none; }

.picker-option {
    display: flex;
    align-items: center;
    gap: .65rem;
    width: 100%;
    margin-top: .25rem;
    padding: .55rem .8rem;
    border: 0;
    border-radius: var(--radius);
    background: none;
    color: rgb(var(--muted-fg));
    font: inherit;
    cursor: pointer;
    text-align: left;
}
.picker-option > span:first-child { flex: 1; min-width: 0; }
@media (hover: hover) {
    .picker-option:hover { background: rgb(var(--card)); color: rgb(var(--fg)); }
}
.picker-option:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}
.picker-option[aria-selected="true"] { color: rgb(var(--brand)); font-weight: 500; }

.picker-option .picker-tick { flex: none; opacity: 0; transition: opacity .2s ease; }
.picker-option[aria-selected="true"] .picker-tick { opacity: 1; }

/* While saving, a spinner stands where the tick will be. */
.picker-spinner { display: none; flex: none; width: 15px; height: 15px; }
.picker-option.is-saving .picker-spinner {
    display: block;
    border: 2px solid rgb(var(--muted-fg) / .3);
    border-top-color: rgb(var(--brand));
    border-radius: 50%;
    animation: spin .6s linear infinite;
}
.picker-option.is-saving .picker-tick { display: none; }
.picker-options.is-saving { pointer-events: none; }

.picker .field-hint { margin-top: .5rem; }

@media (prefers-reduced-motion: reduce) {
    .picker-options, .picker-options-inner, .picker-chevron { transition: none; }
}

/* The banned word list. A box you can see several lines of at once beats a
   single field for something people build up over time. */
.words-form { padding-top: .9rem; max-width: 26rem; }
.words-form label,
.reply-form label {
    display: block;
    margin-bottom: .4rem;
    font-size: .8125rem;
    font-weight: 500;
}

/* The message a declined call gets, sitting under the switch it belongs to
   rather than in a panel of its own. Indented so it reads as that switch's
   business, not another setting at the same level. */
.reply-form {
    padding: .1rem 0 .9rem 0;
    margin-left: .9rem;
    border-left: 2px solid rgb(var(--border));
    padding-left: .9rem;
}
.reply-form[hidden] { display: none; }
.reply-form .row-actions { margin-top: .5rem; }
.words-form textarea,
.reply-form textarea {
    width: 100%;
    padding: .6rem .75rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font: inherit;
    line-height: 1.5;
    resize: vertical;
}
.words-form textarea::placeholder,
.reply-form textarea::placeholder { color: rgb(var(--muted-fg) / .7); }
.words-form textarea:focus,
.reply-form textarea:focus {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}

/* Two pickers stacked inside one option need a little air between them. */
.option .picker + .picker { padding-top: 1.25rem; }

/* Switches for the settings inside an option. Narrower than the option's own
   switch, so it stays clear which one turns the whole feature on. */
.sub-options { padding-top: 1.25rem; max-width: 26rem; }
.sub-options-hint { margin-top: 0; margin-bottom: .35rem; }

.sub-option {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: .55rem 0;
    border-top: 1px solid rgb(var(--border));
}
.sub-option-name { font-size: .9375rem; }

.switch-small { width: 42px; height: 24px; margin-top: 0; }
.switch-small .switch-knob { width: 18px; height: 18px; top: 2px; left: 2px; }
.switch-small[aria-checked="true"] .switch-knob { transform: translateX(18px); }

.command-form { padding-top: .9rem; }
.command-form label {
    display: block;
    margin-bottom: .4rem;
    font-size: .8125rem;
    font-weight: 500;
}

.command-row { display: flex; gap: .5rem; align-items: stretch; }
.command-row input {
    width: 180px;
    padding: .55rem .75rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: .9375rem;
}
.command-row input:focus {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}
.command-row .button { flex: none; }

@media (max-width: 480px) {
    .command-row input { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
    .switch-knob { transition: none; }
    .switch.is-saved { animation: none; }
}

@media (max-width: 560px) {
    .option { gap: 1rem; }
}

/* ----------------------------------------------- the group lock schedule */

.sub-option-head { align-items: flex-start; border-top: 0; padding-top: 1.1rem; }
.sub-option-head .field-hint { margin-top: .2rem; max-width: 34ch; }

.schedule-form { padding-top: .9rem; }
.schedule-times {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: .6rem;
}
.schedule-times label {
    display: block;
    margin-bottom: .35rem;
    font-size: .8125rem;
    font-weight: 500;
}
.schedule-times input {
    padding: .55rem .7rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font: inherit;
}
.schedule-times input:focus {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}

.group-picker { padding-top: 1.25rem; max-width: 26rem; }
.group-picker-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
}
.group-picker-head .choice-label { margin-bottom: 0; }

.group-list {
    margin-top: .6rem;
    max-height: 17rem;
    overflow-y: auto;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
}
.group-list[hidden] { display: none; }

/* While WhatsApp is asked for the group list: a quiet ring and a word, so the
   wait reads as working rather than stuck. */
.group-loading {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .75rem;
    padding: 1.75rem 1rem;
}
.group-loading-spinner {
    width: 20px;
    height: 20px;
    flex: none;
    border-radius: var(--radius-full);
    border: 2.5px solid rgb(var(--brand) / .2);
    border-top-color: rgb(var(--brand));
    animation: group-loading-turn .8s linear infinite;
}
@keyframes group-loading-turn {
    to { transform: rotate(360deg); }
}
@media (prefers-reduced-motion: reduce) {
    .group-loading-spinner { animation: none; border-top-color: rgb(var(--brand) / .2); }
}

.group-row {
    display: flex;
    align-items: center;
    gap: .65rem;
    padding: .55rem .75rem;
    border-bottom: 1px solid rgb(var(--border));
    cursor: pointer;
    font-size: .9375rem;
}
.group-row:last-child { border-bottom: 0; }
@media (hover: hover) {
    .group-row:hover { background: rgb(var(--surface)); }
}
/* Big, smooth checkboxes, cousins of the app's radios: the tick sweeps in,
   so a chosen group is seen across the room. */
.group-row input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    flex: none;
    width: 22px;
    height: 22px;
    margin: 0;
    display: inline-grid;
    place-items: center;
    border: 2px solid rgb(var(--muted-fg) / .5);
    border-radius: 7px;
    background: transparent;
    cursor: pointer;
    transition: border-color .18s ease, background .18s ease,
                box-shadow .18s ease;
}
.group-row input[type="checkbox"]::before {
    content: "";
    width: 12px;
    height: 12px;
    background: #fff;
    clip-path: polygon(14% 44%, 0 65%, 40% 100%, 100% 18%, 82% 2%, 38% 66%);
    transform: scale(0);
    transition: transform .18s ease;
}
.group-row input[type="checkbox"]:checked {
    border-color: rgb(var(--brand));
    background: rgb(var(--brand));
}
.group-row input[type="checkbox"]:checked::before { transform: scale(1); }
.group-row:hover input[type="checkbox"] { border-color: rgb(var(--brand)); }
.group-row input[type="checkbox"]:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgb(var(--brand) / .25);
}

.group-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis;
              white-space: nowrap; }
.group-note { flex: none; font-size: .75rem; color: rgb(var(--muted-fg)); }
/* The mark of a group this number runs. */
.group-admin { flex: none; padding: .15rem .5rem; font-size: .625rem; }

/* A group we cannot moderate can be seen but never scheduled. */
.group-row.is-locked-out { cursor: default; opacity: .55; }
.group-row.is-locked-out .group-note { color: rgb(var(--danger-fg)); }

#save-groups { margin-top: .7rem; }
#save-groups[hidden] { display: none; }

/* ------------------------------------------------------ reporting a bug */

.report-form { max-width: 40rem; }
.report-form .choice { margin-bottom: 1.25rem; }

.choice-pill {
    padding: .5rem .9rem;
    border: 1px solid rgb(var(--border));
    border-radius: 999px;
    background: transparent;
    color: rgb(var(--muted-fg));
    font: inherit;
    font-size: .875rem;
    cursor: pointer;
    transition: color .15s ease, border-color .15s ease, background .15s ease;
}
@media (hover: hover) {
    .choice-pill:hover { color: rgb(var(--fg)); border-color: rgb(var(--muted-fg) / .5); }
}
.choice-pill[aria-checked="true"] {
    background: rgb(var(--brand-soft));
    border-color: rgb(var(--brand));
    color: rgb(var(--brand));
    font-weight: 500;
}
.choice-pill:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}

.report-form textarea {
    width: 100%;
    padding: .7rem .8rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font: inherit;
    line-height: 1.55;
    resize: vertical;
}
.report-form textarea::placeholder { color: rgb(var(--muted-fg) / .7); }
.report-form textarea:focus {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}
.report-count { font-size: .75rem; }

/* What they have already told us, so nobody sends the same thing twice. */
.report-past { margin-top: 2.5rem; padding-top: 1.5rem; border-top: 1px solid rgb(var(--border)); }
.report-past h2 { font-size: 1.0625rem; margin-bottom: .9rem; }

.report-list { margin: 0; padding: 0; list-style: none; max-width: 40rem; }
.report-item { padding: .9rem 0; border-bottom: 1px solid rgb(var(--border)); }
.report-item:last-child { border-bottom: 0; }

.report-item-head {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: .5rem;
    margin-bottom: .35rem;
}
.report-kind, .report-status {
    padding: .15rem .5rem;
    border-radius: 999px;
    font-size: .6875rem;
    font-weight: 500;
    letter-spacing: .03em;
}
.report-kind-bug { background: rgb(var(--danger-fg) / .14); color: rgb(var(--danger-fg)); }
.report-kind-idea { background: rgb(var(--brand-soft)); color: rgb(var(--brand)); }
.report-status { border: 1px solid rgb(var(--border)); color: rgb(var(--muted-fg)); }
.report-status-done { border-color: rgb(var(--brand)); color: rgb(var(--brand)); }
.report-when { font-size: .75rem; margin-left: auto; }
.report-body { font-size: .9375rem; word-break: break-word; }

/* ------------------------------------------------------- dashboard summary */

/* Flat sections divided by a hairline. No boxes: the numbers should read as
   one page, not a wall of separate panels. */
.summary { padding: 1.6rem 0; border-top: 1px solid rgb(var(--border)); }
.summary:first-of-type { border-top: 0; padding-top: .5rem; }
.summary .field-hint { margin-top: 1rem; max-width: 58ch; }

.summary-label {
    margin-bottom: 1rem;
    font-size: .75rem;
    text-transform: uppercase;
    letter-spacing: .07em;
    color: rgb(var(--muted-fg));
}

.figures {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
    gap: 1.5rem 2rem;
}
.figure-wide { grid-column: span 2; }

.figure-value {
    font-size: 1.75rem;
    font-weight: 500;
    line-height: 1.1;
    word-break: break-word;
}

/* The plan, bigger and bolder than any figure: one glance settles it. */
.plan-hero-name {
    font-size: 2.75rem;
    font-weight: 700;
    line-height: 1.05;
    letter-spacing: -.02em;
}
.plan-hero-name.is-paid { color: rgb(var(--brand)); }
.plan-hero .muted { margin-top: .35rem; }
.figure-name {
    margin-top: .25rem;
    font-size: .8125rem;
    color: rgb(var(--muted-fg));
}

.feature-list { margin: 0; padding: 0; list-style: none; max-width: 30rem; }
.feature-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding: .55rem 0;
    border-bottom: 1px solid rgb(var(--border));
}
.feature-row:last-child { border-bottom: 0; }
.feature-name { font-size: .9375rem; }
.feature-count { font-size: .8125rem; color: rgb(var(--muted-fg)); }

/* The animated Pro mark beside a paid menu item. */
.nav-lottie {
    flex: none;
    margin-left: auto;
    width: 22px;
    height: 22px;
}
.nav-lottie svg { display: block; }

/* A menu item that is announced but not ready. */
.nav-badge {
    margin-left: auto;
    padding: .1rem .4rem;
    border-radius: 999px;
    background: rgb(var(--muted-fg) / .16);
    color: rgb(var(--muted-fg));
    font-size: .625rem;
    font-weight: 500;
    letter-spacing: .04em;
}
.nav-item.is-current .nav-badge { background: rgb(var(--brand) / .18); color: rgb(var(--brand)); }

.soon-note { max-width: 52ch; }

/* Placeholder plan cards on the billing page. Nothing is purchasable yet. */
.plans {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr));
    gap: 1.25rem;
    max-width: 66rem;
}

.plan-card {
    padding: 1.5rem 1.75rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
}

/* The plan the account is on wears the brand green, so which card is
   "yours" reads before any word does. */
.plan-card.is-current {
    border-color: rgb(var(--brand));
    box-shadow: inset 0 0 0 1px rgb(var(--brand));
    background: rgb(var(--brand) / .05);
}

.plan-days-left {
    margin-top: .35rem;
    font-size: .8125rem;
    font-weight: 500;
    color: rgb(var(--brand));
}

.plan-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
}

.plan-name { font-size: 1rem; font-weight: 500; }

.plan-tag {
    padding: .1rem .45rem;
    border-radius: 999px;
    background: rgb(var(--muted-fg) / .16);
    color: rgb(var(--muted-fg));
    font-size: .625rem;
    font-weight: 500;
    letter-spacing: .04em;
    white-space: nowrap;
}
.plan-tag.is-current { background: rgb(var(--brand) / .18); color: rgb(var(--brand)); }

.plan-price {
    margin-top: .75rem;
    font-size: 1.6rem;
    font-weight: 500;
    line-height: 1.1;
}
.plan-price .plan-per {
    font-size: .8125rem;
    font-weight: 400;
    color: rgb(var(--muted-fg));
}

.plan-blurb { margin-top: .6rem; font-size: .875rem; max-width: 34ch; }

.plan-list {
    margin: .9rem 0 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: .65rem;
}
.plan-item {
    display: grid;
    grid-template-columns: 16px 1fr;
    gap: .55rem;
    align-items: start;
    font-size: .875rem;
}
.plan-check {
    width: 16px;
    height: 16px;
    margin-top: .15rem;
    color: rgb(var(--success-fg));
}
.plan-item-sub {
    margin-top: .15rem;
    font-size: .78rem;
    line-height: 1.45;
    color: rgb(var(--muted-fg));
}

.plan-buy { margin-top: 1.1rem; display: grid; gap: .6rem; }
.plan-buy .button { justify-self: start; }

/* The checkout column: everything on the upgrade page sits centred in one
   readable measure, the card as wide as the column. */
.pay-centre { max-width: 36rem; margin-inline: auto; }

/* The checkout card on the upgrade page: one wide card, centred. */
.pay-card {
    width: 100%;
    max-width: 36rem;
    margin-inline: auto;
    padding: 1.75rem 2rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    display: grid;
    gap: 1.1rem;
}
/* display:grid on the class would otherwise beat the hidden attribute,
   and the waiting card must not show before a push has gone out. */
.pay-card[hidden] { display: none; }
.pay-card .button { justify-self: start; }

/* The length picker: four durations, one choice, priced on the face. */
.field .field-label {
    display: block;
    margin-bottom: .4rem;
    font-size: .8125rem;
    font-weight: 500;
}
.plan-lengths {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
    gap: .5rem;
}
.plan-length {
    display: grid;
    gap: .15rem;
    padding: .6rem .75rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    cursor: pointer;
}
/* The radio itself is for the keyboard and the reader; the card is the UI. */
.plan-length input {
    position: absolute;
    opacity: 0;
    pointer-events: none;
}
.plan-length:has(input:checked) {
    border-color: rgb(var(--brand));
    background: rgb(var(--brand-soft));
}
.plan-length:has(input:focus-visible) {
    outline: 2px solid rgb(var(--brand));
    outline-offset: 2px;
}
.plan-length-months { font-size: .875rem; font-weight: 500; }
.plan-length-price { font-size: .78rem; color: rgb(var(--muted-fg)); }
.pay-waiting-anim {
    width: 130px;
    height: 130px;
    margin-inline: auto;
}
#pay-waiting { text-align: center; }
.pay-waiting-title { font-weight: 500; font-size: 1.05rem; }

/* The plan tabs above the bot options, and the Pro mark on paid switches. */
.plan-tabs { margin-bottom: .25rem; }
.option-plan-tag {
    display: inline-block;
    margin-left: .4rem;
    vertical-align: 2px;
}

/* The receipts tab: each payment on its own row, its receipt one tap away. */
.receipts { max-width: 40rem; }
.receipts .plan-item-sub { display: block; }
.receipt-download {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    padding: .35rem .7rem;
    font-size: .8125rem;
}

/* Owner rows on the admin devices page: the whole row is the way in. */
.user-rows .feature-row { position: relative; }
.user-rows .row-link { color: inherit; text-decoration: none; }
.user-rows .row-link::after { content: ''; position: absolute; inset: 0; }
.user-rows .feature-row:hover .row-link { color: rgb(var(--brand)); }

/* No spinner arrows: the number is typed, not nudged. */
.no-spin {
    -moz-appearance: textfield;
    appearance: textfield;
}
.no-spin::-webkit-outer-spin-button,
.no-spin::-webkit-inner-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

@media (max-width: 760px) {
    .figures { grid-template-columns: repeat(auto-fill, minmax(8rem, 1fr)); gap: 1.25rem; }
    .figure-value { font-size: 1.5rem; }
}

/* ---------------------------------------------------------- AI automation */

/* One centred column for a single number's setup. That page is a form read
   top to bottom, so it stays narrow; the list page opposite it uses the full
   workspace like every other screen. */
/* One number's AI page runs the full workspace like every other page. The
   long-lined prose inside still stops at a readable measure via field-hint
   and copy max-widths, so width here costs no readability. */
.ai-centre {
    max-width: none;
    margin: 0;
    padding-bottom: 2rem;
}

/* Prose stops at a readable measure even though the section it sits in runs
   the whole width. */
/* The welcome: words on the left, the little chatbot alive on the right.
   A whisper of the brand teal behind it so the page opens warm, not flat. */
.ai-hero {
    display: grid;
    grid-template-columns: 1fr minmax(160px, 240px);
    align-items: center;
    gap: 1.5rem;
    margin-bottom: 1.75rem;
    padding: 1.75rem 2rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-md);
    background:
        radial-gradient(120% 160% at 100% 0%,
                        rgb(var(--brand) / .10), transparent 55%),
        rgb(var(--surface));
}
.ai-hero-title {
    font-size: 1.75rem;
    letter-spacing: -.02em;
    line-height: 1.15;
}
.ai-hero-copy {
    margin-top: .6rem;
    max-width: 52ch;
    font-size: .9375rem;
    line-height: 1.65;
    color: rgb(var(--muted-fg));
}
.ai-hero-anim {
    width: 100%;
    max-width: 240px;
    aspect-ratio: 1;
    justify-self: end;
}
.ai-hero-anim svg { display: block; }

/* The same welcome card without an animation beside it: words run the width. */
.ai-hero.is-words { grid-template-columns: 1fr; }
.ai-hero.is-words .ai-hero-copy { max-width: 62ch; }

@media (max-width: 720px) {
    .ai-hero { grid-template-columns: 1fr; padding: 1.5rem 1.25rem; }
    .ai-hero-anim { order: -1; justify-self: center; max-width: 180px; }
}

.ai-brief {
    max-width: 62ch;
    font-size: .9375rem;
    line-height: 1.6;
    color: rgb(var(--muted-fg));
}

/* Same track rhythm as the dashboard figures, only a little wider because
   these read as words rather than counts and should not wrap. Smaller value
   text for the same reason: a claim must not shout over the page title. */
.ai-figures {
    grid-template-columns: repeat(auto-fill, minmax(13rem, 1fr));
    margin-top: 1.5rem;
}
.ai-figures .figure-value { font-size: 1.125rem; font-weight: 600; }

.ai-picklist .pick { border-radius: var(--radius); }

.ai-down {
    margin-bottom: 1.5rem;
    padding: .8rem 1rem;
    border: 1px solid rgb(var(--danger-fg) / .35);
    border-radius: var(--radius);
    background: rgb(var(--danger-bg));
    color: rgb(var(--danger-fg));
    font-size: .875rem;
}

/* ------------------------------------------------- one number's AI setup */

.back-link {
    display: inline-flex;
    align-items: center;
    gap: .3rem;
    margin-bottom: 1.25rem;
    font-size: .8125rem;
    color: rgb(var(--muted-fg));
}
.back-link:hover { color: rgb(var(--brand)); }

.ai-setup-head { margin-bottom: 1.5rem; }
.ai-setup-head h1 { font-size: 1.5rem; letter-spacing: -.015em; }
.ai-setup-head p { margin-top: .15rem; font-size: .875rem; }

/* The switch is the thing people come here to move, so it gets its own card
   above everything else rather than sitting in a list of settings. */
/* ------------------------------------------------------------ the AI hub */

/* Two doors into AI automation, laid out as cards you can actually read
   before choosing, side by side where the width allows it. */
.hub-cards {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
    gap: 1rem;
}
.hub-card {
    display: flex;
    align-items: center;
    gap: 1.1rem;
    padding: 1.35rem 1.25rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-md);
    background: rgb(var(--surface));
    color: rgb(var(--fg));
    transition: border-color .16s ease;
}
.hub-card:hover,
.hub-card:focus-visible {
    border-color: rgb(var(--brand));
    text-decoration: none;
}
.hub-card:hover .hub-card-name { color: rgb(var(--brand)); }
.hub-card:hover .pick-go { color: rgb(var(--brand)); transform: translateX(3px); }
.hub-card-icon {
    flex: none;
    display: grid;
    place-items: center;
    width: 44px;
    height: 44px;
    border-radius: var(--radius);
    background: rgb(var(--brand-soft));
    color: rgb(var(--brand));
}
.hub-card-words { flex: 1; min-width: 0; }
.hub-card-name { display: block; font-weight: 500; }
.hub-card-desc { display: block; margin-top: .2rem; font-size: .8125rem; }

/* The chosen groups, worn as little pills rather than counted. */
.group-chips {
    display: flex;
    flex-wrap: wrap;
    gap: .45rem;
    /* Breathing room above the Save button, which sits right below. */
    margin: .3rem 0 1rem;
}
.group-chip {
    display: inline-flex;
    align-items: center;
    max-width: 100%;
    padding: .3rem .75rem;
    border-radius: var(--radius-full);
    background: rgb(var(--brand-soft));
    color: rgb(var(--brand));
    font-size: .8125rem;
    font-weight: 500;
    overflow-wrap: anywhere;
}

/* ------------------------------------------------------- the status studio */

/* The picture section hosts the upload cover, so it anchors it. */
.st-section { position: relative; min-height: 14rem; }

.st-head { display: flex; align-items: baseline; justify-content: space-between; }
.st-count { font-size: .8125rem; }
.st-add { display: inline-flex; align-items: center; gap: .5rem; margin: .75rem 0 1rem; }
.st-none { margin-top: .5rem; }

.st-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 1rem;
}
/* A card is a door: the picture, its state, and a tap to open the editor. */
.st-card {
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-md);
    background: rgb(var(--surface));
    overflow: hidden;
    cursor: pointer;
    transition: border-color .16s ease, transform .16s ease;
}
.st-card:hover,
.st-card:focus-visible {
    border-color: rgb(var(--brand));
    transform: translateY(-2px);
}
.st-card:focus-visible {
    outline: none;
    box-shadow: 0 0 0 3px rgb(var(--brand) / .25);
}
/* The tile plays the little animation until its picture has arrived. */
.st-thumb {
    position: relative;
    display: block;
    aspect-ratio: 4 / 3;
    background: rgb(var(--field));
}
.st-thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
    opacity: 0;
    transition: opacity .25s ease;
}
.st-thumb.is-loaded img { opacity: 1; }
.st-thumb-wait {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
}
.st-thumb-wait svg { width: 90px !important; height: 90px !important; }
.st-thumb.is-loaded .st-thumb-wait { display: none; }
.st-card .st-state { padding: .6rem .8rem .8rem; font-size: .8125rem; }
.st-fields { display: grid; gap: .7rem; margin-top: .35rem; }
.st-choice {
    display: flex;
    align-items: center;
    gap: .65rem;
    font-size: .9375rem;
    cursor: pointer;
}

/* The radios themselves are the app-wide kind, styled with .field input. */

/* The same field dress every other form here wears. */
.st-fields textarea,
.st-fields .st-clock,
.st-fields select {
    width: 100%;
    padding: .7rem .85rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font: inherit;
    /* The native clock popup and its little icon follow the app's theme
       instead of arriving stark white on a dark page. */
    color-scheme: light dark;
}
.st-fields textarea { line-height: 1.6; resize: vertical; }
.st-fields textarea::placeholder { color: rgb(var(--muted-fg) / .65); }
.st-fields textarea:focus,
.st-fields .st-clock:focus,
.st-fields select:focus {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}

.st-when-row { display: flex; gap: .6rem; flex-wrap: wrap; }
.st-when-row .st-day { flex: 1 1 9rem; width: auto; }
.st-when-row .st-clock { flex: 0 1 9rem; min-width: 7.5rem; }
.st-when-row select { flex: 0 1 11rem; width: auto; }
.st-when-row .st-day[hidden] { display: none; }
.st-actions { display: flex; align-items: center; gap: 1rem; margin-top: .2rem; }

/* The written composer holds a readable column rather than the full sweep. */
#st-written { max-width: 36rem; margin-top: 1rem; }
.st-swatches { margin: .15rem 0; }

.st-swatches { display: flex; gap: .5rem; flex-wrap: wrap; }
.st-swatch {
    position: relative;
    width: 30px;
    height: 30px;
    border-radius: 50%;
    cursor: pointer;
    border: 2px solid transparent;
}
.st-swatch input {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    border: 0;
    opacity: 0;
    cursor: pointer;
}
.st-swatch:has(input:checked) {
    border-color: rgb(var(--fg));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .35);
}

.st-written-list { margin: 1.25rem 0 0; padding: 0; list-style: none; }
.st-written-row {
    display: flex;
    align-items: center;
    gap: .9rem;
    padding: .8rem 0;
    border-top: 1px solid rgb(var(--border));
}
.st-chip {
    flex: none;
    width: 26px;
    height: 26px;
    border-radius: 8px;
}
.st-written-what { flex: 1; min-width: 0; }
.st-written-text { display: block; font-size: .9375rem; overflow-wrap: anywhere; }
.st-written-row .st-state { display: block; margin-top: .15rem; font-size: .8125rem; }

.ai-switch-card {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    margin-bottom: 1.75rem;
    padding: 1.1rem 1.25rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-md);
    background: rgb(var(--surface));
}
.ai-switch-name { font-size: 1rem; font-weight: 500; }
.ai-switch-card .field-hint { margin-top: .25rem; max-width: 42ch; }
.ai-switch-card .switch { flex: none; margin-top: 0; }

.ai-speed-field { margin-bottom: 1.75rem; }
.ai-speed-field label {
    display: block;
    margin-bottom: .4rem;
    font-size: 1rem;
    font-weight: 500;
}
.ai-speed-field select {
    width: 100%;
    padding: .7rem .85rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font: inherit;
}
.ai-speed-field select:focus {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}
.ai-speed-field select:disabled { opacity: .6; }
.ai-speed-field .field-hint { margin-top: .4rem; max-width: 52ch; }

.ai-persona-form label {
    display: block;
    font-size: 1rem;
    font-weight: 500;
}
.ai-persona-hint { margin: .25rem 0 .7rem; max-width: 52ch; }
.ai-persona-form textarea {
    width: 100%;
    padding: .85rem 1rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font: inherit;
    line-height: 1.6;
    resize: vertical;
}
.ai-persona-form textarea::placeholder { color: rgb(var(--muted-fg) / .65); }
.ai-persona-form textarea:focus {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}
.ai-persona-foot {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 1rem;
    margin: .75rem 0 .6rem;
}
.ai-persona-foot .field-hint { margin: 0; }

/* ------------------------------------------------ pictures the AI can send */

/* ------------------------------------------------------------ bulk message */

/* Two ways to send, sitting side by side on one rail. The chosen one is
   marked by the brand line under it rather than a filled box, so the tabs
   stay quieter than the buttons inside the panel. */
.bulk-tabs {
    display: flex;
    gap: .25rem;
    margin: 1.75rem 0 1.5rem;
    border-bottom: 1px solid rgb(var(--border));
}

.bulk-tab {
    position: relative;
    padding: .7rem 1rem;
    border: 0;
    border-radius: var(--radius) var(--radius) 0 0;
    background: none;
    color: rgb(var(--muted-fg));
    font: inherit;
    font-weight: 500;
    cursor: pointer;
}
.bulk-tab:hover { color: rgb(var(--fg)); background: rgb(var(--surface)); }
.bulk-tab[aria-selected="true"] { color: rgb(var(--brand)); }
/* Sits on the rail itself, so the chosen tab reads as joined to its panel. */
.bulk-tab[aria-selected="true"]::after {
    content: '';
    position: absolute;
    left: .5rem;
    right: .5rem;
    bottom: -1px;
    height: 2px;
    border-radius: 2px 2px 0 0;
    background: rgb(var(--brand));
}

.bulk-panel[hidden] { display: none; }
.bulk-panel { max-width: 46rem; }

/* Which number sends: the house picker, sized for a header and floating its
   list over the page instead of pushing the tabs down. */
.device-picker {
    position: relative;
    padding-top: 0;
    width: min(20rem, 100%);
    flex: none;
}
.device-picker .picker-trigger { align-items: center; }
.device-picker .picker-current {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.device-picker .device-dot { flex: none; }
.device-picker .picker-options {
    position: absolute;
    z-index: 20;
    top: calc(100% + .35rem);
    left: 0;
    right: 0;
    border-radius: var(--radius);
    background: rgb(var(--card));
}
.device-picker.is-open .picker-options {
    border: 1px solid rgb(var(--border));
    box-shadow: 0 14px 34px rgb(0 0 0 / .22);
}
.device-picker .picker-options-inner { padding: .25rem; }
.device-picker .picker-option { margin-top: 0; }

.device-option-who {
    flex: 1;
    min-width: 0;
    display: grid;
    gap: .1rem;
    text-align: left;
}
.device-option-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.device-option-phone { font-size: .75rem; color: rgb(var(--muted-fg)); }

.bulk-form .field { margin-bottom: 1.25rem; }
.bulk-form label {
    display: block;
    margin-bottom: .4rem;
    font-size: 1rem;
    font-weight: 500;
}
.bulk-form input[type="tel"],
.bulk-form textarea {
    width: 100%;
    padding: .7rem .85rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font: inherit;
}
.bulk-form textarea { line-height: 1.6; resize: vertical; }
.bulk-form input[type="tel"] { letter-spacing: .02em; }
.bulk-form input:focus, .bulk-form textarea:focus {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}
.bulk-form input:disabled, .bulk-form textarea:disabled { opacity: .55; }
.bulk-form select:disabled { opacity: .55; }
.bulk-form .field-hint { margin-top: .4rem; }
.bulk-form select {
    width: 100%;
    padding: .7rem .85rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font: inherit;
}
.bulk-form select:focus {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}
.bulk-form #many-numbers {
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: .875rem;
    line-height: 1.7;
}

/* The label and the file link share one line above the list. */
.many-numbers-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
}
.many-numbers-head label { margin-bottom: .4rem; }
.many-numbers-head .linkish { font-size: .8125rem; }

/* Every group a number is in: a count, a reload, and the list itself. */
.manage-groups { max-width: 46rem; margin-top: 1.5rem; }
.manage-groups-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: .5rem;
}
.manage-groups-head .field-hint { margin: 0; }
/* A row that is read rather than ticked needs no room for a checkbox. */
.group-row.is-plain { cursor: default; }
.group-row.is-plain:hover { background: none; }

/* Pictures riding along with the message: the button, a word about how many,
   and a card each once they are up. */
.attach .attach-add {
    display: inline-flex;
    align-items: center;
    gap: .45rem;
}
.attach-note { margin-top: .45rem; }

.attach-cards {
    display: flex;
    flex-wrap: wrap;
    gap: .75rem;
    margin-top: .75rem;
}
.attach-cards:empty { display: none; }

.attach-card {
    position: relative;
    width: 104px;
    height: 104px;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-sm);
    background: rgb(var(--field));
    overflow: hidden;
}
.attach-card img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
}

/* Sits on the picture itself, dark enough to read over any photo. */
.attach-remove {
    position: absolute;
    top: .3rem;
    right: .3rem;
    display: grid;
    place-items: center;
    width: 22px;
    height: 22px;
    padding: 0;
    border: 0;
    border-radius: var(--radius-full);
    background: rgb(0 0 0 / .62);
    color: #fff;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}
.attach-remove:hover { background: rgb(0 0 0 / .8); }

/* While a list goes out: the animation, the count, and a way to stop. */
.bulk-working {
    display: grid;
    place-items: center;
    gap: .35rem;
    padding: 2rem 1rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-md);
    background: rgb(var(--surface));
    max-width: 46rem;
}
.bulk-working[hidden] { display: none; }
.bulk-working-anim { width: 120px; height: 120px; }
.bulk-working-count {
    font-size: 1.25rem;
    font-weight: 600;
    letter-spacing: -.01em;
}
.bulk-working .button { margin-top: .75rem; }

/* The AI's chosen groups: a label, the load link, and the shared list. */
.ai-groups-head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: .35rem;
}
.ai-groups-label { font-size: 1rem; font-weight: 500; }
#ai-groups-picker .group-list { margin: .5rem 0 .85rem; }
#ai-groups-picker { margin-bottom: 1.5rem; }

/* Away for: a number, its unit, and a save, reading as one sentence. */
.handover-wait-row {
    display: flex;
    align-items: center;
    gap: .6rem;
}
.handover-wait-row input[type="number"] {
    width: 6rem;
    padding: .7rem .85rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font: inherit;
}
.handover-wait-row select { flex: 0 1 10rem; width: auto; }
.handover-wait-row input:focus, .handover-wait-row select:focus {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}

/* The quiet door to the group and tagging settings. */
#ai-advanced .ai-group-card { margin-top: .25rem; }

/* The door to the library, sitting above the switches on the setup page. */
.ai-library-link {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: 1.25rem;
    padding: .55rem .9rem;
    text-decoration: none;
}
.ai-library-link:hover { text-decoration: none; }

.ai-media { display: block; margin-top: .5rem; }

.library-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1.5rem;
}
.library-head .button { display: inline-flex; align-items: center; gap: .45rem; }

.ai-media-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
    gap: 1rem;
    margin-top: .75rem;
}
.ai-media-item {
    display: flex;
    flex-direction: column;
    padding: 0;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-sm);
    background: rgb(var(--surface));
    overflow: hidden;
    cursor: pointer;
    transition: border-color .15s ease, transform .15s ease;
}
.ai-media-item:hover {
    border-color: rgb(var(--brand) / .5);
    transform: translateY(-2px);
}
.ai-media-item img {
    width: 100%;
    height: 140px;
    object-fit: cover;
    background: rgb(var(--field));
}
/* The whole prompt lives in the edit modal; the card shows only how it
   starts, ending in an ellipsis when there is more. */
.ai-media-caption {
    /* Space below is margin, not padding: a clamped box clips at its padding
       edge, so bottom padding let the top of a third line show through. */
    margin: 0 0 .7rem;
    padding: .6rem .75rem 0;
    font-size: .8125rem;
    line-height: 1.45;
    color: rgb(var(--muted-fg));
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.ai-media-empty { margin-top: 1.5rem; }

/* A card's picture, with what it holds written on top of it: how many
   photos a group sends, and whether the AI captions them. */
.ai-media-thumb { position: relative; }
.ai-media-badge,
.ai-media-tag {
    position: absolute;
    padding: .2rem .55rem;
    border-radius: 999px;
    font-size: .75rem;
    font-weight: 500;
    line-height: 1.4;
    pointer-events: none;
}
.ai-media-badge {
    top: .5rem;
    right: .5rem;
    background: rgb(0 0 0 / .62);
    color: #fff;
}
.ai-media-tag {
    left: .5rem;
    bottom: .5rem;
    background: rgb(var(--brand));
    color: #fff;
}

/* Where a photo is dropped in. Dashed on purpose: an opening, not a card. */
.drop-zone {
    position: relative;
    display: grid;
    place-items: center;
    width: 100%;
    aspect-ratio: 4 / 3;
    border: 2px dashed rgb(var(--border));
    border-radius: var(--radius-sm);
    background: rgb(var(--field));
    color: rgb(var(--muted-fg));
    cursor: pointer;
    overflow: hidden;
    transition: border-color .15s ease, background .15s ease;
}
.drop-zone:hover, .drop-zone.is-over {
    border-color: rgb(var(--brand));
    background: rgb(var(--brand-soft));
}
.drop-zone-words { display: grid; place-items: center; gap: .5rem; text-align: center; }
.drop-zone-words p { font-size: .8125rem; line-height: 1.5; }
.drop-zone-words[hidden] { display: none; }

/* The pictures chosen so far fill the zone as a grid, each with its own way
   back out, and a last tile for adding more while there is room. */
.drop-previews {
    position: absolute;
    inset: 0;
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    align-content: start;
    gap: .5rem;
    padding: .6rem;
    overflow-y: auto;
    cursor: default;
}
.drop-previews[hidden] { display: none; }
.drop-thumb {
    position: relative;
    aspect-ratio: 1;
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: rgb(var(--surface));
}
.drop-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.drop-thumb-remove {
    position: absolute;
    top: .25rem;
    right: .25rem;
    display: grid;
    place-items: center;
    width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    border: 0;
    border-radius: 50%;
    background: rgb(0 0 0 / .62);
    color: #fff;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}
.drop-thumb-remove:hover { background: rgb(0 0 0 / .8); }
.drop-more {
    display: grid;
    place-items: center;
    aspect-ratio: 1;
    border: 2px dashed rgb(var(--border));
    border-radius: var(--radius-sm);
    font-size: .75rem;
    text-align: center;
    cursor: pointer;
}
.drop-more:hover { border-color: rgb(var(--brand)); color: rgb(var(--brand)); }
.photo-count { margin-top: .5rem; }

/* The AI captions switch under the instructions, worded like the other
   switches on the AI pages. */
.captions-row {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    margin-top: 1rem;
}
.captions-name { margin: 0; font-weight: 500; }
.captions-row .field-hint { margin-top: .15rem; }

/* Every picture of an open group, small, under the one shown large. They
   wrap onto a second row rather than scroll, so Add is never out of sight. */
.edit-photo-strip {
    display: flex;
    flex-wrap: wrap;
    gap: .6rem .45rem;
    margin-top: .6rem;
}
.edit-photo-strip[hidden] { display: none; }
.edit-photo-strip img {
    flex: none;
    width: 3.5rem;
    height: 3.5rem;
    object-fit: cover;
    border: 2px solid transparent;
    border-radius: var(--radius-sm);
    cursor: pointer;
}
.edit-photo-strip img:hover,
.edit-photo-strip img:focus-visible {
    border-color: rgb(var(--brand));
    outline: none;
}

/* Each strip picture carries its own Remove under it. A marked picture
   fades and its button offers Undo; nothing leaves until Save. */
.strip-item {
    flex: none;
    display: grid;
    justify-items: center;
    align-content: start;
    gap: .3rem;
}
.strip-thumb {
    position: relative;
    display: block;
    width: 3.5rem;
    height: 3.5rem;
    border-radius: var(--radius-sm);
}
.strip-thumb img { display: block; }

/* A picture chosen to join the group, waiting for Save. */
.strip-new {
    position: absolute;
    top: .2rem;
    left: .2rem;
    padding: 0 .3rem;
    border-radius: 999px;
    background: rgb(var(--brand));
    color: #fff;
    font-size: .625rem;
    font-weight: 600;
    line-height: 1.5;
    pointer-events: none;
}

/* Add, at the end of the strip: the same size as a picture, dashed like
   the "Add more" tile of the add sheet, its word where Remove sits. */
.strip-add {
    flex: none;
    display: grid;
    justify-items: center;
    align-content: start;
    gap: .3rem;
    padding: 0;
    border: 0;
    background: transparent;
    color: rgb(var(--muted-fg));
    font: inherit;
    cursor: pointer;
}
.strip-add[hidden] { display: none; }
.strip-add-box {
    display: grid;
    place-items: center;
    width: 3.5rem;
    height: 3.5rem;
    border: 2px dashed rgb(var(--border));
    border-radius: var(--radius-sm);
    transition: border-color .15s ease, color .15s ease;
}
.strip-add-word {
    padding: .1rem .5rem;
    border: 1px solid transparent;
    font-size: .75rem;
    line-height: 1.5;
}
.strip-add:hover,
.strip-add:focus-visible { color: rgb(var(--brand)); outline: none; }
.strip-add:hover .strip-add-box,
.strip-add:focus-visible .strip-add-box { border-color: rgb(var(--brand)); }
.strip-remove {
    padding: .1rem .5rem;
    border: 1px solid rgb(var(--border));
    border-radius: 999px;
    background: transparent;
    color: rgb(var(--muted-fg));
    font: inherit;
    font-size: .75rem;
    line-height: 1.5;
    cursor: pointer;
}
.strip-remove:hover { border-color: #d64545; color: #d64545; }
.strip-item.is-removing img { opacity: .3; filter: grayscale(1); }
.strip-item.is-removing .strip-remove {
    border-color: rgb(var(--brand));
    color: rgb(var(--brand));
}

.sheet-cols label {
    display: block;
    margin-bottom: .4rem;
    font-size: .9375rem;
    font-weight: 500;
}
.sheet-cols textarea {
    width: 100%;
    min-height: 8.5rem;
    padding: .7rem .85rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font: inherit;
    line-height: 1.55;
    resize: vertical;
}
.sheet-cols textarea:focus {
    outline: none;
    border-color: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}

.edit-photo-frame {
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-sm);
    background: rgb(var(--field));
    overflow: hidden;
}
.edit-photo-view {
    display: block;
    width: 100%;
    aspect-ratio: 4 / 3;
    object-fit: contain;
}

/* A picture still on its way: its frame shimmers and a small ring turns in
   the middle until the picture fades in over them. The frame keeps its size
   from the picture's own box, so nothing jumps when it arrives. */
.pic-loading {
    position: relative;
    background-color: rgb(var(--field));
    background-image: linear-gradient(100deg, transparent 20%,
                                      rgb(var(--fg) / .07) 50%, transparent 80%);
    background-size: 200% 100%;
    background-repeat: no-repeat;
    animation: pic-shimmer 1.4s ease-in-out infinite;
}
.pic-loading img { opacity: 0; }
.pic-loading::after {
    content: "";
    position: absolute;
    top: 50%;
    left: 50%;
    width: 1.4rem;
    height: 1.4rem;
    margin: -.7rem 0 0 -.7rem;
    border: 2.5px solid rgb(var(--brand) / .22);
    border-top-color: rgb(var(--brand));
    border-radius: 50%;
    animation: spin .8s linear infinite;
    pointer-events: none;
}
.strip-thumb.pic-loading::after,
.drop-thumb.pic-loading::after {
    width: 1rem;
    height: 1rem;
    margin: -.5rem 0 0 -.5rem;
    border-width: 2px;
}
.ai-media-thumb img,
.edit-photo-view,
.strip-thumb img,
.drop-thumb img { transition: opacity .25s ease; }
@keyframes pic-shimmer {
    from { background-position: 150% 0; }
    to   { background-position: -50% 0; }
}
@media (prefers-reduced-motion: reduce) {
    .pic-loading { animation: none; }
    .pic-loading::after { animation-duration: 2.4s; }
    .ai-media-thumb img,
    .edit-photo-view,
    .strip-thumb img,
    .drop-thumb img { transition: none; }
}

.drop-zone-limit { font-size: .8125rem; color: rgb(var(--muted-fg)); }

/* The photo modals split evenly: the picture on one side, words on the
   other, which is what makes them read as landscape. */
@media (min-width: 720px) {
    .sheet-cols.photo-cols {
        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    }
}

.sheet-photo { max-width: 860px; position: relative; }

/* The words column mirrors the picture beside it: the label sits on top and
   the box grows to the same floor as the drop zone, so the two halves of the
   sheet read as one piece. On narrow screens the columns stack and the box
   keeps its own comfortable height instead. */
@media (min-width: 720px) {
    .photo-cols .sheet-col:last-child {
        display: flex;
        flex-direction: column;
    }
    .photo-cols .sheet-col:last-child textarea {
        flex: 1;
        min-height: 0;
    }
}

/* A footer running the full width of the sheet, its buttons in the bottom
   right corner, separated the same way the head is. */
.sheet-foot {
    display: flex;
    justify-content: flex-end;
    gap: .6rem;
    padding: 1rem 1.25rem;
    border-top: 1px solid rgb(var(--border));
}

/* On a phone the sheet is one column: breathing room between the halves, a
   shallower drop zone and box so everything fits without scrolling, and the
   two footer buttons share the row the way phone dialogs do. */
@media (max-width: 719.98px) {
    .photo-cols .sheet-col + .sheet-col { margin-top: 1.15rem; }
    .drop-zone { aspect-ratio: 3 / 2; }
    .sheet-cols textarea { min-height: 6rem; }
    .edit-photo-view { aspect-ratio: 3 / 2; }
    .sheet-foot { padding: .9rem 1rem; }
    .sheet-foot .button { flex: 1; }
}

/* While a photo uploads and the AI reads it, the sheet becomes one quiet
   scene: the animation, the count, and a line saying what is happening. */
.photo-working {
    position: absolute;
    inset: 0;
    z-index: 2;
    display: grid;
    place-items: center;
    align-content: center;
    gap: .25rem;
    background: rgb(var(--card) / .97);
    border-radius: inherit;
}
.photo-working[hidden] { display: none; }
.photo-working-anim { width: 130px; height: 130px; }
.photo-working-pct {
    font-size: 1.5rem;
    font-weight: 600;
    letter-spacing: -.02em;
}

/* A quiet button whose action removes something. */
.danger-quiet { color: rgb(var(--danger-fg)); }
.danger-quiet:hover {
    background: rgb(var(--danger-bg));
    border-color: rgb(var(--danger-fg) / .4);
}

.plain-list { margin: 0; padding-left: 1.1rem; max-width: 52ch; }
.plain-list li { margin-bottom: .4rem; font-size: .9375rem; color: rgb(var(--muted-fg)); }
.plain-list code {
    padding: .05rem .3rem;
    border-radius: 4px;
    background: rgb(var(--muted-fg) / .14);
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: .8125rem;
}
.linkish.is-danger { color: rgb(var(--danger-fg)); }

/* Numbers listed beside the AI they answer with. */
.ai-numbers { padding-top: .5rem; max-width: 30rem; }
.ai-numbers .sub-option:first-child { border-top: 0; }
.ai-numbers .field-hint { margin-top: .1rem; }
.switch:disabled { opacity: .45; cursor: not-allowed; }

/* ------------------------------------------------------------- API reference
   The developer page. Three columns on a wide screen: the section list, the
   reference, and the code beside the prose it belongs to. Below 1100px the
   code drops under its own section rather than shrinking into a gutter, and
   below 900px the section list goes with it. Documentation you have to
   scroll sideways is worse than none. */

.docs {
    display: grid;
    grid-template-columns: 12.5rem minmax(0, 1fr);
    gap: 2.5rem;
    align-items: start;
}

.docs-nav {
    position: sticky;
    top: 1rem;
    display: flex;
    flex-direction: column;
    gap: .1rem;
    padding-right: 1rem;
    border-right: 1px solid rgb(var(--border));
}
.docs-nav-head {
    margin: 1.1rem 0 .35rem;
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: rgb(var(--muted-fg));
}
.docs-nav-head:first-child { margin-top: 0; }
.docs-nav a {
    padding: .3rem .55rem;
    border-radius: var(--radius-xs);
    color: rgb(var(--fg));
    font-size: .875rem;
    line-height: 1.5;
}
.docs-nav a:hover { background: rgb(var(--surface)); text-decoration: none; }
/* The section you are reading. Marked, not merely hovered, so the page can
   always answer "where am I" without scrolling back to find out. */
.docs-nav a.is-current {
    background: rgb(var(--brand-soft));
    color: rgb(var(--brand));
    font-weight: 500;
}
:root.dark .docs-nav a.is-current { color: rgb(var(--canvas)); }

.docs-body { min-width: 0; }

.docs-section {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 2rem;
    padding: 2.25rem 0;
    border-top: 1px solid rgb(var(--border));
    /* Anchored jumps land under the sticky topbar without this. */
    scroll-margin-top: 1.5rem;
}
.docs-section:first-child { border-top: 0; padding-top: .25rem; }
/* Sections that are all prose, or all controls, use the full width. */
.docs-section.is-stacked { grid-template-columns: minmax(0, 1fr); }
.docs-section.is-stacked .docs-text { max-width: 46rem; }

.docs-text h2 {
    display: flex;
    align-items: center;
    gap: .55rem;
    margin-bottom: .5rem;
    font-size: 1.25rem;
}
.docs-text > p { max-width: 60ch; }
.docs-text > .muted { margin-bottom: 1rem; }

.docs-note {
    margin-top: 1rem;
    padding: .7rem .9rem;
    border-left: 2px solid rgb(var(--brand));
    border-radius: 0 var(--radius-xs) var(--radius-xs) 0;
    background: rgb(var(--brand-soft));
    font-size: .875rem;
}
:root.dark .docs-note { background: rgb(var(--surface)); }

.docs-label {
    margin: 1.25rem 0 .4rem;
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: rgb(var(--muted-fg));
}
.docs-aside > .docs-label:first-child { margin-top: 0; }

/* The method chip beside a path. */
.verb {
    padding: .12rem .45rem;
    border-radius: var(--radius-xs);
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .04em;
    color: #fff;
}
.verb-post { background: rgb(var(--brand)); }
.verb-get { background: rgb(var(--muted-fg)); }

/* --------------------------------------------------------------- the tables */

.docs-table {
    width: 100%;
    margin-top: .5rem;
    border-collapse: collapse;
    font-size: .875rem;
}
.docs-table th {
    padding: .45rem .6rem .45rem 0;
    border-bottom: 1px solid rgb(var(--border));
    text-align: left;
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: rgb(var(--muted-fg));
}
.docs-table td {
    padding: .55rem .6rem .55rem 0;
    border-bottom: 1px solid rgb(var(--border));
    vertical-align: top;
}
.docs-table td:first-child { white-space: nowrap; }
.docs-table code,
.docs-text p code,
.docs-note code {
    padding: .05rem .3rem;
    border-radius: 4px;
    background: rgb(var(--muted-fg) / .14);
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: .8125rem;
}
.req {
    margin-left: .3rem;
    font-size: .625rem;
    font-weight: 600;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: rgb(var(--danger-fg));
}

/* ----------------------------------------------------------- code and tabs */

.code-tabs {
    display: flex;
    gap: .2rem;
    margin-bottom: .5rem;
}
.code-tabs button {
    padding: .3rem .7rem;
    border: 1px solid transparent;
    border-radius: var(--radius-xs);
    background: none;
    color: rgb(var(--muted-fg));
    font: inherit;
    font-size: .8125rem;
    cursor: pointer;
}
.code-tabs button:hover { background: rgb(var(--surface)); }
.code-tabs button.is-current {
    border-color: rgb(var(--border));
    background: rgb(var(--surface));
    color: rgb(var(--fg));
    font-weight: 500;
}

.code-block {
    position: relative;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    /* Always dark, in both themes. Code reads as code, and the examples keep
       the same colours in a screenshot whoever took it. */
    background: #14141a;
    overflow: hidden;
}
.code-block + .code-block { margin-top: .5rem; }
.code-block pre {
    margin: 0;
    padding: .9rem 1rem;
    overflow-x: auto;
}
.code-block code {
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: .8125rem;
    line-height: 1.65;
    color: #e6e6ef;
    white-space: pre;
}
/* Responses and other things that are read rather than run. */
.code-block.is-quiet { background: rgb(var(--surface)); }
.code-block.is-quiet code { color: rgb(var(--fg)); }

.copy {
    position: absolute;
    top: .5rem;
    right: .5rem;
    padding: .25rem .55rem;
    border: 1px solid rgb(255 255 255 / .16);
    border-radius: var(--radius-xs);
    background: rgb(255 255 255 / .08);
    color: #e6e6ef;
    font: inherit;
    font-size: .75rem;
    cursor: pointer;
    opacity: 0;
    transition: opacity .12s ease;
}
.code-block:hover .copy,
.copy:focus-visible { opacity: 1; }
.copy:hover { background: rgb(255 255 255 / .16); }
.copy.is-done { opacity: 1; color: #7ee2b8; }
.code-block.is-quiet .copy {
    border-color: rgb(var(--border));
    background: rgb(var(--card));
    color: rgb(var(--muted-fg));
}
/* No hover on a touch screen, so the button has to be there already. */
@media (hover: none) {
    .copy { opacity: 1; }
}

/* ---------------------------------------------------------------- try it */

.try {
    margin-top: .75rem;
    padding: .75rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius);
    background: rgb(var(--surface));
}
.try-body {
    width: 100%;
    padding: .6rem .7rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-xs);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: .8125rem;
    line-height: 1.6;
    resize: vertical;
}
.try-body:focus { outline: 2px solid rgb(var(--brand)); outline-offset: -1px; }
.try-bar {
    display: flex;
    align-items: center;
    gap: .75rem;
    margin-top: .6rem;
}
.try-bar .button { padding: .4rem 1rem; font-size: .875rem; }
.try-status { font-size: .8125rem; font-variant-numeric: tabular-nums; }
.try-status.is-bad { color: rgb(var(--danger-fg)); }
.try-out {
    max-height: 18rem;
    margin: .6rem 0 0;
    padding: .7rem .8rem;
    border-radius: var(--radius-xs);
    background: #14141a;
    overflow: auto;
}
.try-out code {
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: .8125rem;
    line-height: 1.6;
    color: #e6e6ef;
    white-space: pre;
}

/* The key box. No button beside the field: ui.js fits an eye to every
   password input on the site, and a second control doing the same job is
   the kind of thing you stop seeing and start tripping over. */
.try-key { max-width: 30rem; }
.try-key-label {
    display: block;
    margin-bottom: .35rem;
    font-size: .8125rem;
    font-weight: 500;
}
.try-key input {
    width: 100%;
    /* Room on the right for that eye, so a long key never runs under it. */
    padding: .6rem 2.9rem .6rem .75rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-xs);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: .875rem;
    letter-spacing: .01em;
}
.try-key input:focus {
    border-color: rgb(var(--brand));
    outline: 2px solid rgb(var(--brand) / .35);
    outline-offset: 1px;
}
.try-key input::placeholder { color: rgb(var(--muted-fg) / .6); }

/* Everything below this section is either armed or dead. This is the page
   saying which, rather than leaving it to be discovered by pressing a
   button and being told off. */
.try-key-state {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-top: .55rem;
    font-size: .8125rem;
    color: rgb(var(--muted-fg));
}
.try-key-dot {
    flex: none;
    width: 6px;
    height: 6px;
    border-radius: var(--radius-full);
    background: rgb(var(--muted-fg) / .45);
    transition: background .15s ease, box-shadow .15s ease;
}
.try-key-state.is-ready { color: rgb(var(--brand)); }
.try-key-state.is-ready .try-key-dot {
    background: rgb(var(--brand));
    box-shadow: 0 0 0 3px rgb(var(--brand) / .18);
}
:root.dark .try-key-state.is-ready { color: rgb(126 226 184); }
:root.dark .try-key-state.is-ready .try-key-dot {
    background: rgb(126 226 184);
    box-shadow: 0 0 0 3px rgb(126 226 184 / .16);
}

/* Same shape as .docs-note, so it reads as one of the page's asides rather
   than an interruption. The red edge and icon carry the caution; a filled
   slab shouted louder than the field it is warning about and pulled the eye
   past the control you actually came here to use. */
.try-warn {
    display: flex;
    align-items: flex-start;
    gap: .55rem;
    max-width: 42rem;
    margin-top: 1.25rem;
    padding: .7rem .9rem;
    border-left: 2px solid rgb(var(--danger-fg));
    border-radius: 0 var(--radius-xs) var(--radius-xs) 0;
    background: rgb(var(--danger-bg) / .55);
    font-size: .875rem;
}
.try-warn svg { flex: none; margin-top: .12rem; color: rgb(var(--danger-fg)); }
:root.dark .try-warn { background: rgb(var(--surface)); }
:root.dark .try-warn svg { color: rgb(232 130 116); }

@media (prefers-reduced-motion: reduce) {
    .try-key-dot { transition: none; }
}

/* ------------------------------------------------------------ key manager */

.key-new { display: flex; gap: .5rem; max-width: 32rem; margin-bottom: 1rem; }
.key-new input {
    flex: 1;
    min-width: 0;
    padding: .55rem .7rem;
    border: 1px solid rgb(var(--border));
    border-radius: var(--radius-xs);
    background: rgb(var(--field));
    color: rgb(var(--fg));
    font: inherit;
    font-size: .9375rem;
}
.key-new input:focus { outline: 2px solid rgb(var(--brand)); outline-offset: -1px; }

.key-reveal {
    margin-bottom: 1rem;
    padding: .85rem;
    border: 1px solid rgb(var(--brand) / .45);
    border-radius: var(--radius);
    background: rgb(var(--brand-soft));
}
:root.dark .key-reveal { background: rgb(var(--surface)); }
.key-reveal-head {
    margin-bottom: .5rem;
    font-size: .875rem;
    font-weight: 500;
}

.key-list { display: flex; flex-direction: column; }
.key-row {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: .8rem 0;
    border-top: 1px solid rgb(var(--border));
}
.key-row:first-child { border-top: 0; }
.key-row-main { flex: 1; min-width: 0; }
.key-row-name { font-weight: 500; }
.key-row-meta { margin-top: .1rem; font-size: .8125rem; }
.key-row-meta code {
    padding: .05rem .3rem;
    border-radius: 4px;
    background: rgb(var(--muted-fg) / .14);
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: .75rem;
}
.key-row.is-revoked .key-row-main { opacity: .5; }
.key-tag {
    margin-left: .45rem;
    padding: .08rem .4rem;
    border-radius: var(--radius-full);
    background: rgb(var(--danger-bg));
    color: rgb(var(--danger-fg));
    font-size: .6875rem;
    font-weight: 600;
    letter-spacing: .02em;
}
.key-revoke { padding: .35rem .8rem; font-size: .8125rem; }
.key-revoke:hover {
    color: rgb(var(--danger-fg));
    border-color: rgb(var(--danger-fg) / .4);
}
.key-empty { font-size: .875rem; }

@media (max-width: 1100px) {
    /* The code goes under the prose it explains rather than into a gutter
       too narrow to read a line of Python in. */
    .docs-section { grid-template-columns: minmax(0, 1fr); gap: 1.25rem; }
}

@media (max-width: 900px) {
    .docs { grid-template-columns: minmax(0, 1fr); gap: 1.5rem; }
    .docs-nav {
        position: static;
        flex-flow: row wrap;
        gap: .35rem;
        padding: 0 0 1rem;
        border-right: 0;
        border-bottom: 1px solid rgb(var(--border));
    }
    /* The group headings are only useful as a column. */
    .docs-nav-head { display: none; }
    .docs-nav a {
        border: 1px solid rgb(var(--border));
        border-radius: var(--radius-full);
        font-size: .8125rem;
    }
}

@media (max-width: 560px) {
    .key-new { flex-direction: column; }
    .key-new .button { width: 100%; }
    .key-row { flex-wrap: wrap; gap: .5rem; }
}
