/* NO BAND YET — THE BACKLINE
   ============================================================================
   Van Neistat / Tom Sachs "Workshop Web", relocated from the garage to the
   backline: the load-in door, the gaffer-taped stage, the studio track sheet,
   the flight case stencilled with a band's name, the laminate around your neck,
   and the kick-drum head the name is actually painted on.

   Palette, geometry and type are the LOCKED SPINE, verbatim, from
   ../cars/docs/design-spine.md. Every colour in this file resolves to a token
   declared in section 00 — there is no raw hex below that block.

   Loads AFTER Pico CSS (classless base) and after the Google Fonts <link>.
   Mobile-first: the design target is a 360px phone.

   00  TOKENS               05  BOARD / NAME CARD
   01  PICO OVERRIDES       06  INSPECTION STAMPS
   02  BASE + TYPE          07  LAMINATE / CREW
   03  SITE PLATE           08  NOTES (GAFFER TAPE)
   04  SECTION HEADS + RACK     09  TALKBACK
                            10  FILE FORM + SWITCHES
                            11  CHROME
                            12  MOTION + A11Y + WIDE
                            13  NOSTR LOGIN
   ========================================================================= */

[x-cloak],
[hidden] {
    display: none !important;
}

/* ===========================================================================
   00 — :root TOKENS — "BLACKFACE AMP"
   The geometry and type below are the locked spine from ../cars/docs/design-spine.md,
   verbatim. The PALETTE deliberately diverges: cars' birch/plywood/traffic-cone
   read as the same app, and this one is an amp, not a workbench. Black tolex,
   cream faceplate script, amber tube-glow for the one thing you should touch.

   Names are SEMANTIC on purpose. A token called --gaffer-black holding a cream
   value is a lie the next reader has to decode, so the roles are named instead
   of the materials.
   ========================================================================= */
:root {
    /* materials — 80% of every screen */
    --band: #0E0E0D;
    --page: #161615;
    --surface: #22221F;
    --ink: #EDE6D2;
    /* Ink for text sitting ON a light accent fill (amber/red/sand). The two
       poles inverted when this theme went dark, so --ink is now the LIGHT one
       and anything painted on an accent needs the dark one instead. */
    --on-accent: #161615;
    --muted: #A9A49A;

    /* Fill for neutral controls. It has HUE on purpose — deep oxblood, the
       vintage amp tolex/grille colour. A button filled with --surface is the
       same colour as the card behind it, so only its border showed and it read
       as absent; another neutral grey would read as disabled. Cream ink on this
       measures ~12:1. */
    --control: #3A2023;
    --control-hover: #4E2C30;

    /* safety accents — 5% of every screen, earned by an action or a hazard */
    --action: #FFB000;
    --stop: #EF5540;   /* lightened from #E23A2E: at 12px neither dark nor cream ink cleared AA on the deeper red (4.21 / 3.47) */
    --archive: #3E5D75;
    --caution: #D9C27A;
    --tape: #7FA8C9;

    /* geometry */
    --border: 2px solid var(--ink);
    --radius: 0;
    --unit: 8px;
    --shadow-block: 4px 4px 0 var(--ink);

    /* type */
    --font-display: 'Oswald', 'Archivo Narrow', system-ui, sans-serif;
    --font-body: 'IBM Plex Mono', 'Courier Prime', ui-monospace, monospace;
    --font-note: 'Permanent Marker', cursive; /* MAX 2 uses per screen */

    --snap: 120ms linear;
}

/* ===========================================================================
   01 — PICO v2 OVERRIDES
   Pico ships first and is classless, so every one of its variables is flipped
   onto the spine before a single component rule runs.

   DECLARED ON `body`, NOT JUST `:root`, DELIBERATELY. Pico v2 declares its own
   variables with selectors more specific than a bare `:root`, so a `:root`
   override loses the cascade and is silently ignored. Custom properties
   inherit, so declaring them on `body` wins for every descendant by PROXIMITY
   — the nearest declaring ancestor — no matter how specific Pico's selector
   is. Without this, `article.name-card` keeps Pico's pure #FFF card fill.
   Do not "simplify" this back to `:root` alone.
   ========================================================================= */
:root,
body {
    color-scheme: dark;

    /* surfaces & ink */
    --pico-background-color: var(--page);
    --pico-color: var(--ink);
    --pico-h1-color: var(--ink);
    --pico-h2-color: var(--ink);
    --pico-h3-color: var(--ink);
    --pico-h4-color: var(--ink);
    --pico-h5-color: var(--ink);
    --pico-h6-color: var(--ink);
    --pico-muted-color: var(--muted);
    --pico-card-background-color: var(--surface);
    --pico-card-sectioning-background-color: var(--surface);
    --pico-form-element-background-color: var(--surface);
    --pico-form-element-color: var(--ink);
    --pico-form-element-border-color: var(--ink);
    --pico-form-element-placeholder-color: var(--muted);
    --pico-muted-border-color: var(--ink);
    --pico-mark-background-color: var(--caution);
    --pico-mark-color: var(--ink);

    /* action = orange; secondary = archival blue; stop = red */
    --pico-primary: var(--action);
    --pico-primary-hover: var(--action);
    --pico-primary-background: var(--action);
    --pico-primary-hover-background: var(--action);
    --pico-primary-border: var(--ink);
    --pico-primary-inverse: var(--on-accent);
    --pico-primary-focus: var(--archive);
    --pico-primary-underline: var(--action);
    --pico-secondary: var(--archive);
    --pico-secondary-background: var(--archive);
    --pico-secondary-border: var(--ink);
    --pico-secondary-inverse: var(--ink);
    --pico-del-color: var(--stop);
    --pico-ins-color: var(--ink);

    /* geometry — kill every radius, thicken every rule, hard shadows only */
    --pico-border-radius: 0;
    --pico-border-width: 2px;
    --pico-border-color: var(--ink);
    --pico-outline-width: 2px;
    --pico-box-shadow: var(--shadow-block);
    --pico-card-box-shadow: var(--shadow-block);

    /* type */
    --pico-font-family: var(--font-body);
    --pico-font-family-sans-serif: var(--font-body);
    --pico-font-family-monospace: var(--font-body);
    --pico-font-size: 100%;
    --pico-line-height: 1.5;
    --pico-spacing: calc(var(--unit) * 2);
    --pico-typography-spacing-vertical: calc(var(--unit) * 2);
    --pico-form-element-spacing-vertical: var(--unit);
    --pico-form-element-spacing-horizontal: var(--unit);
}

/* ===========================================================================
   02 — BASE + TYPE
   Display = Oswald ALL CAPS (flight-case stencil). Body, labels and data =
   IBM Plex Mono (the field manual). Permanent Marker is reserved for margin
   annotations and is deliberately not applied by any rule in this file — a
   screen may still spend it twice.

   On --muted: it clears WCAG AA only at large sizes, so secondary text
   here stays black and earns its rank from size, weight and tracking instead.
   Gray is spent on fills, disabled states and redundant glyphs.
   ========================================================================= */
*,
*::before,
*::after {
    box-sizing: border-box;
    border-radius: var(--radius);
}

body {
    min-height: 100vh;
    margin: 0;
    background: var(--page);
    color: var(--ink);
    font-family: var(--font-body);
    font-size: 15px;
    line-height: 1.5;
    -webkit-text-size-adjust: 100%;
}

main {
    display: block;
    width: 100%;
    max-width: 720px;
    margin-inline: auto;
    padding: calc(var(--unit) * 2);
    padding-bottom: calc(var(--unit) * 6 + env(safe-area-inset-bottom));
}

/* every top-level screen section is a knolled stack with even rhythm */
main > section {
    display: flex;
    flex-direction: column;
    gap: calc(var(--unit) * 2);
    margin-bottom: calc(var(--unit) * 3);
}

h1,
h2,
h3,
h4,
h5,
h6 {
    margin: 0;
    font-family: var(--font-display);
    font-weight: 700;
    line-height: 1.05;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    color: var(--ink);
}

h1 { font-size: 1.75rem; }
h2 { font-size: 1.375rem; }
h3 { font-size: 1.125rem; }
h4,
h5,
h6 { font-size: 1rem; }

p { margin: 0 0 var(--unit); }
p:last-child { margin-bottom: 0; }

a {
    color: var(--action);
    font-weight: 600;
    text-decoration: underline;
    text-decoration-thickness: 3px;
    text-underline-offset: 3px;
}
a:visited {
    color: var(--archive);
    text-decoration-color: var(--archive);
}
@media (hover: hover) {
    a:hover {
        background: var(--caution);
        color: var(--on-accent);
        text-decoration-color: var(--on-accent);
    }
}

mark {
    background: var(--caution);
    color: var(--on-accent);
    padding: 0 0.15em;
}

code,
kbd,
samp {
    font-family: var(--font-body);
    background: var(--page);
    padding: 0 4px;
}

hr {
    height: 0;
    margin: calc(var(--unit) * 2) 0;
    border: 0;
    border-top: var(--border);
}

/* Pico still owns bare buttons; square them and put them in mono. */
button,
[type="button"],
[type="submit"],
[type="reset"] {
    border-radius: var(--radius);
    font-family: var(--font-body);
}

:focus-visible {
    outline: 3px solid var(--archive);
    outline-offset: 2px;
}

/* the shared label voice: uppercase mono, letterspaced, small */
.tagline,
/* .line-check is styled but NOT rendered yet: there is no sync layer, so a
   status chip could only ever say the same thing. It stays for when sync lands
   and must then speak ONLY for states worth interrupting someone about. */
.line-check,
.card-date,
.crew-tag,
.crew-serial,
.note__meta,
.char-count,
.field__label,
.board__end,
.talkback__count {
    font-family: var(--font-body);
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

/* ===========================================================================
   03 — SITE PLATE / KICK-DRUM HEAD
   The first thing anyone sees. A band's name genuinely lives on a kick-drum
   head, so the wordmark is painted on one: a paper head inside a black hoop,
   tension lugs stamped top and bottom, bolted to a plywood riser.
   ========================================================================= */
.site-plate {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--unit);
    background: var(--band);
    border-bottom: var(--border);
    padding: calc(var(--unit) * 2);
    padding-top: calc(var(--unit) * 2 + env(safe-area-inset-top));
}

.drum-head {
    position: relative;
    flex: 1 0 100%;
    background: var(--surface);
    border: var(--border);
    padding: calc(var(--unit) * 4) calc(var(--unit) * 2) calc(var(--unit) * 3);
}

/* the hoop — a second hard rule set inside the shell */
.drum-head::before {
    content: "";
    position: absolute;
    inset: 5px;
    border: var(--border);
    pointer-events: none;
}

/* tension lugs — hard-stop square studs along the top and bottom of the hoop */
.drum-head::after {
    content: "";
    position: absolute;
    inset: 13px 16px;
    pointer-events: none;
    background-image:
        repeating-linear-gradient(to right, var(--ink) 0 6px, transparent 6px 28px),
        repeating-linear-gradient(to right, var(--ink) 0 6px, transparent 6px 28px);
    background-size: 100% 6px, 100% 6px;
    background-position: left top, left bottom;
    background-repeat: no-repeat;
}

.wordmark {
    display: block;
    margin: 0;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: clamp(2.25rem, 13vw, 4rem);
    line-height: 0.92;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    text-wrap: balance;
    color: var(--ink);
}

.tagline {
    display: block;
    margin-top: calc(var(--unit) * 1.5);
    padding-top: var(--unit);
    border-top: var(--border);
    font-size: 10px;
    font-weight: 500;
    letter-spacing: 0.14em;
    line-height: 1.4;
    color: var(--ink);
}

/* LINE CHECK — the patch-bay sync indicator (flourish) */
.line-check {
    display: inline-flex;
    align-items: center;
    gap: var(--unit);
    background: var(--surface);
    border: var(--border);
    padding: 5px var(--unit);
    font-size: 10px;
    font-weight: 500;
    line-height: 1;
    color: var(--ink);
    white-space: nowrap;
}

/* the jack socket: a hard square lamp in a black ring, never a soft LED */
.line-check__dot {
    flex: 0 0 auto;
    width: 10px;
    height: 10px;
    background: var(--muted);
    box-shadow: 0 0 0 2px var(--ink);
}
.line-check[data-state="ok"] .line-check__dot { background: var(--archive); }
.line-check[data-state="queued"] .line-check__dot { background: var(--caution); }
.line-check[data-state="off"] .line-check__dot { background: var(--muted); }
.line-check[data-state="error"] .line-check__dot { background: var(--stop); }

/* ===========================================================================
   04 — SECTION HEADS, TAB RACK
   ========================================================================= */


/* SECTION HEAD — a numbered ruler, ticked along its baseline */
.section-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: var(--unit);
    padding-bottom: var(--unit);
    border-bottom: var(--border);
    background-image: repeating-linear-gradient(
        to right, var(--ink) 0 1px, transparent 1px 8px);
    background-size: 100% 4px;
    background-repeat: no-repeat;
    background-position: bottom;
}
/* The Sachs "number everything" motif is deliberately NOT applied to section
   heads or field labels. A serial on a work order carries real information; a
   "02" bolted to SUBMIT A BAND NAME and an "01 ·" in front of NAME carry none,
   and a first-time visitor reads them as steps in a sequence that does not
   exist. See PRD 09.6 — the aesthetic lives in the visuals, not the labels.
   FIG. NNNN on a card survives because it is a real per-name serial. */
.section-head > :last-child:not(:first-child) { margin-left: auto; }

/* BOARD FILTER — a checkbox, squared off like everything else. Pico rounds its
   checkboxes and tints them; both are overridden. */
.filter {
    display: inline-flex;
    align-items: center;
    gap: var(--unit);
    margin: 0;
    font-family: var(--font-body);
    font-size: 11px;
    font-weight: 500;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    cursor: pointer;
}
.filter input[type="checkbox"] {
    appearance: none;
    -webkit-appearance: none;
    flex: 0 0 auto;
    width: 18px;
    height: 18px;
    margin: 0;
    padding: 0;
    background: var(--surface);
    border: var(--border);
    border-radius: 0;
    cursor: pointer;
}
/* checked = an inked plate, same language as an inked vote stamp */
/* RED when on, deliberately: the filter being on means you are NOT seeing the
   whole board, which is a warning state, not an action. */
.filter input[type="checkbox"]:checked {
    background: var(--stop);
    box-shadow: inset 0 0 0 3px var(--surface);
}
.filter input[type="checkbox"]:focus-visible {
    outline: 2px solid var(--stop);
    outline-offset: 2px;
}

/* TAB RACK — the app's ONLY navigation, so it must read as navigation.
   Three things do that work, none of them decorative:
     1. It sits directly under the wordmark and STICKS to the top, so it is
        always the thing above the content it governs.
     2. The selected tab drops its bottom border and shares the panel's fill,
        so it physically merges into the sheet below — a folder tab. "You are
        here" is structure, not just a colour.
     3. The UNSELECTED tab is the one that has to look pressable, so it gets
        the recessed plywood fill and an underlined label. A flat pair of
        equal boxes reads as a toggle; this reads as somewhere to go. */
.tab-rack {
    position: sticky;
    top: 0;
    z-index: 20;
    display: flex;
    border: var(--border);
    border-bottom: 0;                 /* the selected tab owns this edge */
    background: var(--band);
    box-shadow: 0 2px 0 var(--ink);
}

.tab {
    flex: 1 1 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: auto;
    min-height: 48px;
    margin: 0;
    padding: var(--unit) calc(var(--unit) * 1.5);
    border: 0;
    border-right: var(--border);
    border-bottom: var(--border);     /* recessed: NOT part of the sheet */
    background: var(--band);
    color: var(--ink);
    font-family: var(--font-display);
    font-size: 14px;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    white-space: nowrap;
    cursor: pointer;
    transition: background var(--snap);
}
.tab:last-child { border-right: 0; }

/* the tab you are NOT on: underlined, so it reads as a destination */
.tab[aria-selected="false"] {
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 3px;
}

/* the tab you ARE on: orange block, bottom edge removed, merged into the panel */
.tab[aria-selected="true"] {
    background: var(--action);
    color: var(--on-accent);
    border-bottom: 0;
    padding-bottom: calc(var(--unit) + 2px);   /* reclaim the dropped border */
    text-decoration: none;
    cursor: default;
}

@media (hover: hover) {
    .tab[aria-selected="false"]:hover { background: var(--page); }
}
.tab[aria-selected="false"]:active { background: var(--page); }

/* The panels the rack governs share the selected tab's top edge. */
.board,
.talkback {
    border-top: var(--border);
}

/* ===========================================================================
   05 — THE BOARD (studio track sheet) + NAME CARDS
   One sheet, framed, ruled between takes. Nothing floats: a card is a row on
   the sheet, numbered by its riveted FIG. plate.
   ========================================================================= */
.board {
    background: var(--surface);
    border: var(--border);
    box-shadow: var(--shadow-block);
}

.name-card {
    position: relative;
    display: block;
    padding: calc(var(--unit) * 3) calc(var(--unit) * 2) calc(var(--unit) * 2);
    border-bottom: var(--border);
}
.name-card:last-child { border-bottom: 0; }
.name-card:focus-within { box-shadow: inset 3px 0 0 var(--archive); }


.card-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: flex-end;
    gap: var(--unit);
    /* the meta row rides the plate's own 24px band, so the date lines up with
       FIG. instead of stranding a dead strip under it */
    margin-top: calc(var(--unit) * -3);
    min-height: calc(var(--unit) * 3);
    margin-bottom: var(--unit);
}

.card-date {
    font-size: 10px;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
}

.name-title {
    margin: 0;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: clamp(1.375rem, 7vw, 2rem);
    line-height: 1.02;
    letter-spacing: 0.02em;
    text-transform: uppercase;
    overflow-wrap: anywhere;
}

.pitch {
    margin: var(--unit) 0 0;
    padding-left: var(--unit);
    border-left: 2px solid var(--band);
    font-family: var(--font-body);
    font-size: 13px;
    line-height: 1.55;
    color: var(--ink);
}

.card-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--unit);
    margin-top: calc(var(--unit) * 2);
}

/* VOID — a foreman-hidden take, struck out but still on the sheet */
.name-card--void { background: var(--page); }
.name-card--void .name-title,
.name-card--void .pitch {
    text-decoration: line-through;
    text-decoration-thickness: 2px;
    color: var(--muted);
}

/* end of sheet */
.board__end {
    padding: calc(var(--unit) * 1.5) calc(var(--unit) * 2);
    border-top: var(--border);
    background: var(--page);
    font-size: 10px;
    font-weight: 500;
    line-height: 1.4;
    color: var(--ink);
}

/* ===========================================================================
   06 — INSPECTION STAMPS
   Rectangular QC stamps: square corners, 2px border, uppercase mono.
   LOCKED colour logic (PRD 09.4): an untouched card is calm black-on-paper.
   Safety paint appears only where YOU have acted — your HIT inks orange,
   your FLOP inks red. Ink on a fill is always --ink: black clears AA
   on both orange (5.7:1) and red (4.6:1), paper on red does not (3.4:1).
   ========================================================================= */
.stamp {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: auto;
    margin: 0;
    padding: 4px var(--unit);
    background: var(--surface);
    color: var(--ink);
    border: var(--border);
    font-family: var(--font-body);
    font-size: 11px;
    font-weight: 600;
    line-height: 1.2;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* the votes are switches you throw — full touch target, mechanical press */
button.stamp {
    min-height: 44px;
    padding: var(--unit) calc(var(--unit) * 1.5);
    font-size: 12px;
    cursor: pointer;
    transition: box-shadow var(--snap), transform var(--snap), background var(--snap);
}
@media (hover: hover) {
    button.stamp:hover { box-shadow: var(--shadow-block); transform: translate(-2px, -2px); }
}
button.stamp:active { box-shadow: none; transform: translate(2px, 2px); }
button.stamp:disabled {
    background: var(--page);
    color: var(--muted);
    border-color: var(--muted);
    box-shadow: none;
    transform: none;
    cursor: not-allowed;
}

/* inked = the vote you cast */
.stamp--hit.stamp--inked { background: var(--action); color: var(--on-accent); }
.stamp--flop.stamp--inked { background: var(--stop); color: var(--on-accent); }

/* VOID — the foreman pulled it. Muted, not painted: hiding is not an error. */
.stamp--void {
    background: var(--muted);
    color: var(--ink);
    text-decoration: line-through;
    text-decoration-thickness: 2px;
}

/* VERIFIED — a neutral plate. A proven key is a fact, not an alert. */
.stamp--verified {
    background: var(--surface);
    color: var(--ink);
    padding: 3px 6px;
    font-size: 10px;
    letter-spacing: 0.12em;
}

/* HEADLINER — the marquee. Bulbs around the sign; decorates, never reorders. */
.stamp--headliner {
    background: var(--caution);
    color: var(--on-accent);
    outline: 2px dashed var(--on-accent);
    outline-offset: 3px;
    margin: 3px;
}

/* ===========================================================================
   07 — LAMINATE (the pass around your neck)
   The identity plate: a punched lanyard slot, the claimed CREW TAG, and the
   CREW # serial the tag cannot lie about.
   ========================================================================= */
.laminate {
    position: relative;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--unit);
    margin-top: calc(var(--unit) * 1.5);
    padding: var(--unit) var(--unit) var(--unit) calc(var(--unit) * 4);
    background: var(--page);
    border: var(--border);
}

/* a real backstage laminate is punched with a SLOT, not a hole */
.laminate__hole {
    position: absolute;
    left: var(--unit);
    top: 50%;
    transform: translateY(-50%);
    width: 14px;
    height: 22px;
    background: var(--surface);
    border: var(--border);
}

.crew-tag {
    font-size: 11px;
    font-weight: 600;
    line-height: 1.3;
    color: var(--ink);
    overflow-wrap: anywhere;
}

.crew-serial {
    background: var(--ink);
    color: var(--surface);
    padding: 2px 6px;
    font-size: 10px;
    font-weight: 500;
    line-height: 1.3;
    font-variant-numeric: tabular-nums;
}

.laminate .stamp--verified { margin-left: auto; }

/* ===========================================================================
   08 — NOTES ON GAFFER TAPE
   .note__tape is an empty span: a pure substrate layer, torn across both ends.
   Exactly ONE strip per list sits off-axis — a deliberate single imperfection,
   never a randomized mess.
   ========================================================================= */
.notes {
    display: flex;
    flex-direction: column;
    gap: var(--unit);
    /* each note form is checklist line 01 of its own card, not a running
       total across every card the reader has opened */
    margin-top: calc(var(--unit) * 2);
    padding-top: calc(var(--unit) * 1.5);
    border-top: var(--border);
}

.note {
    position: relative;
    padding: var(--unit) calc(var(--unit) * 2);
}

.note__tape {
    position: absolute;
    inset: 0;
    z-index: 0;
    background: var(--band);
    /* torn ends: the long edges are factory-clean, the cuts are ragged */
    clip-path: polygon(
        9px 0, calc(100% - 6px) 0,
        100% 20%, calc(100% - 9px) 40%, 100% 60%, calc(100% - 7px) 80%, calc(100% - 10px) 100%,
        7px 100%, 0 76%, 8px 54%, 0 32%, 5px 14%
    );
}

/* the one hand-applied strip. :first-of-type, not :first-child — Alpine leaves
   its <template> parked as the list's first child and would eat the rotation. */
.notes > .note:first-of-type .note__tape { transform: rotate(-1.2deg); }

.note > p {
    position: relative;
    z-index: 1;
    margin: 0;
    font-family: var(--font-body);
    font-size: 13px;
    line-height: 1.5;
    color: var(--ink);
    overflow-wrap: anywhere;
}

.note__meta {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px;
    margin-top: 6px;
    font-size: 10px;
    font-weight: 500;
    line-height: 1.3;
}

/* ===========================================================================
   09 — TALKBACK (the intercom line: every note, one channel)
   ========================================================================= */
.talkback {
    display: flex;
    flex-direction: column;
    background: var(--surface);
    border: var(--border);
    box-shadow: var(--shadow-block);
}

.talkback__item {
    position: relative;
    padding: calc(var(--unit) * 1.5) calc(var(--unit) * 2);
    border-bottom: var(--border);
}
.talkback__item:last-child { border-bottom: 0; }
.talkback__item p {
    margin: 0 0 4px;
    font-size: 13px;
    line-height: 1.5;
    overflow-wrap: anywhere;
}
.talkback__item p:last-child { margin-bottom: 0; }

/* the channel count — a stamped chip, legible on paper and on the orange tab */
.talkback__count {
    display: inline-block;
    min-width: 20px;
    padding: 1px 5px;
    background: var(--ink);
    color: var(--surface);
    font-size: 10px;
    font-weight: 600;
    line-height: 1.4;
    text-align: center;
    font-variant-numeric: tabular-nums;
}

/* ===========================================================================
   10 — FILE FORM (NASA checklist) + SWITCHES
   Every line numbered by a CSS counter, so the checklist renumbers itself.
   ========================================================================= */
.file-form {
    display: flex;
    flex-direction: column;
    gap: calc(var(--unit) * 2);
    padding: calc(var(--unit) * 2);
    background: var(--surface);
    border: var(--border);
    box-shadow: var(--shadow-block);
}

.field {
    display: block;
    /* Pico hangs `margin-bottom: var(--pico-spacing)` off a selector that
       outranks .field__input, which strands the char-count 16px below its own
       input. Zeroing the variable for the subtree beats it without a war. */
    --pico-spacing: 0;
}

.field__label {
    display: block;
    margin-bottom: 6px;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.3;
    color: var(--ink);
}

.field__input {
    display: block;
    width: 100%;
    margin: 0;
    padding: var(--unit);
    /* Darker than the card it sits on, so a field reads as a recessed well.
       Filling it with --surface made it the same colour as the card and only
       the border showed — the same mistake the buttons had. */
    background: var(--page);
    color: var(--ink);
    border: var(--border);
    font-family: var(--font-body);
    font-size: 15px;
    line-height: 1.4;
}
textarea.field__input {
    min-height: calc(var(--unit) * 12);
    resize: vertical;
}
.field__input::placeholder {
    color: var(--muted);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-size: 12px;
}
.field__input:focus {
    outline: 3px solid var(--archive);
    outline-offset: 0;
    border-color: var(--ink);
}

.char-count {
    display: block;
    margin-top: 4px;
    text-align: right;
    font-size: 10px;
    font-weight: 500;
    line-height: 1.2;
    font-variant-numeric: tabular-nums;
    color: var(--ink);
}
.char-count--over {
    color: var(--stop);
    font-weight: 600;
}

/* DUPE WARNING — hazard tone. A caution to read, not an error to fix. */
.dupe-warning {
    padding: calc(var(--unit) * 1.5);
    padding-left: calc(var(--unit) * 3.5);
    background-color: var(--caution);
    background-image: repeating-linear-gradient(
        45deg, var(--ink) 0 8px, var(--caution) 8px 16px);
    background-size: 16px 100%;
    background-repeat: no-repeat;
    background-position: left center;
    border: var(--border);
    color: var(--ink);
    font-size: 12px;
    line-height: 1.5;
}
.dupe-warning .name-title {
    margin-top: 6px;
    font-size: 1rem;
}

/* --- SWITCHES ------------------------------------------------------------
   Three tiers, so hierarchy survives: .btn is the neutral switch, .btn--primary
   is the ONE orange action on a screen, .btn--ghost is a quiet text switch.
   The press is the canonical feedback — it is all a touch user ever gets.
   ------------------------------------------------------------------------ */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    width: auto;
    min-height: 44px;
    margin: 0;
    padding: var(--unit) calc(var(--unit) * 2);
    background: var(--control);
    color: var(--ink);
    border: var(--border);
    font-family: var(--font-body);
    font-size: 12px;
    font-weight: 600;
    line-height: 1.2;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    text-decoration: none;
    white-space: nowrap;
    cursor: pointer;
    transition: box-shadow var(--snap), transform var(--snap), background var(--snap);
}
@media (hover: hover) {
    .btn:hover { background: var(--control-hover); color: var(--ink); }
}
.btn:active { transform: translate(2px, 2px); box-shadow: none; }
.btn:disabled,
.btn[aria-disabled="true"] {
    background: var(--page);
    color: var(--muted);
    border-color: var(--muted);
    box-shadow: none;
    transform: none;
    cursor: not-allowed;
}

.btn--primary {
    background: var(--action);
    color: var(--on-accent);
}
@media (hover: hover) {
    .btn--primary:hover {
        background: var(--action);
        color: var(--on-accent);
        box-shadow: var(--shadow-block);
        transform: translate(-2px, -2px);
    }
}

.btn--ghost {
    min-height: 40px;
    padding: var(--unit);
    background: none;
    border: 0;
    color: var(--ink);
    font-weight: 500;
    text-decoration: underline;
    text-decoration-thickness: 2px;
    text-underline-offset: 4px;
}
@media (hover: hover) {
    .btn--ghost:hover { background: var(--caution); color: var(--on-accent); }
}
.btn--ghost:disabled,
.btn--ghost[aria-disabled="true"] {
    background: none;
    border: 0;
}

/* ===========================================================================
   11 — CHROME: ADD-TO-HOME-SCREEN DIALOG, EMPTY STATE, SETTINGS
   ========================================================================= */

/* A real <dialog> opened with showModal(), so it gets a genuine ::backdrop, a
   focus trap and Esc for free.

   Three things make it READ as a popup rather than as more page:
     1. It floats clear of the edges — `inset: 0` + `margin: auto` + a max-width,
        which centres a fixed element on both axes off its own intrinsic size.
        No transform, so nothing fights the UA's own centring.
     2. Its fill is DARKER than the card surface and it wears a hard cream
        offset shadow, so it reads as a plate lifted off the board. Matching the
        page fill was the bug: the panel dissolved into what was behind it.
     3. The scrim is dense enough to visibly kill the content behind it. */
.install-dialog {
    /* Pico v2 styles `dialog` as a flex CONTAINER to centre its own <article>.
       We position ourselves, so its flex would lay our h2/p/buttons out in a
       row. Force block back on. */
    display: block;
    position: fixed;
    inset: 0;
    /* Pico also sets `min-width: 100%; min-height: 100%` on `dialog` to make it
       a full-screen flex shell. A min floors a max, so max-width/height were
       silently ignored and the panel filled the viewport. Reset both. */
    min-width: 0;
    min-height: 0;
    margin: auto;
    width: calc(100% - var(--unit) * 5);
    max-width: 340px;
    height: fit-content;
    max-height: calc(100% - var(--unit) * 8);
    overflow-y: auto;
    padding: calc(var(--unit) * 2.5);
    background: var(--band);
    color: var(--ink);
    border: var(--border);
    border-radius: 0;
    box-shadow: var(--unit) var(--unit) 0 var(--ink);
}
.install-dialog::backdrop {
    /* flat scrim — the house has no gradients */
    background: rgba(0, 0, 0, 0.86);
}
.install-dialog > h2 {
    margin: 0 0 var(--unit);
    padding-bottom: var(--unit);
    border-bottom: var(--border);
    font-family: var(--font-display);
    font-size: 1.15rem;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
.install-dialog > p {
    margin: 0 0 calc(var(--unit) * 2);
    font-family: var(--font-body);
    font-size: 13px;
    line-height: 1.6;
}
.install-dialog ol {
    counter-reset: step;
    list-style: none;
    margin: 0 0 calc(var(--unit) * 2);
    padding: 0;
}
.install-dialog li {
    counter-increment: step;
    display: grid;
    grid-template-columns: calc(var(--unit) * 4) 1fr;
    align-items: start;
    gap: var(--unit);
    padding: var(--unit) 0;
    border-top: var(--border);
    font-size: 13px;
    line-height: 1.5;
}
.install-dialog li::before {
    content: counter(step, decimal-leading-zero);
    padding: 2px 0;
    background: var(--ink);
    color: var(--surface);
    font-size: 10px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-align: center;
}
/* One decision per row, ranked top to bottom: do it, don't do it now, never
   ask again. A side-by-side row made SKIP and the permanent opt-out look like
   equal-weight siblings, which they are not. */
.install-dialog .card-actions {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: var(--unit);
    margin-top: 0;
}
.install-dialog .card-actions .btn { width: 100%; }
/* the permanent opt-out is the quietest thing in the dialog */
.install-dialog .card-actions .btn--ghost {
    width: auto;
    align-self: center;
    font-size: 11px;
}

/* EMPTY STATE — an empty bin: taped off, labelled, awaiting stock */
.empty-state {
    padding: calc(var(--unit) * 3);
    background: var(--page);
    border: 2px dashed var(--ink);
    font-family: var(--font-body);
    font-size: 12px;
    line-height: 1.6;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--ink);
}
/* a plectrum, dropped in the empty bin */
.empty-state::before {
    content: "";
    display: block;
    width: 18px;
    height: 20px;
    margin-bottom: calc(var(--unit) * 1.5);
    background: var(--muted);
    clip-path: polygon(0 0, 100% 0, 78% 62%, 50% 100%, 22% 62%);
}
.empty-state p { margin: 0 0 var(--unit); }
.empty-state .btn { margin-top: var(--unit); }

/* SETTINGS — the road case: everything bolted down and labelled */
.settings {
    display: flex;
    flex-direction: column;
    gap: calc(var(--unit) * 2);
    padding: calc(var(--unit) * 2);
    background: var(--surface);
    border: var(--border);
}
.settings .field + .field {
    padding-top: calc(var(--unit) * 2);
    border-top: var(--border);
}
.settings .card-actions { margin-top: 0; }

/* ===========================================================================
   12 — MOTION, ACCESSIBILITY, WIDER VIEWPORTS
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        transition: none !important;
        animation: none !important;
        scroll-behavior: auto !important;
    }
    /* the press cue survives as the dropped shadow; the travel does not */
    .btn:active,
    button.stamp:active { transform: none; }
}

@media (prefers-contrast: more) {
    .pitch { border-left-color: var(--ink); }
    .empty-state::before { background: var(--ink); }
}

/* Larger phones and up: the sheet gets room, the drum head gets air. */
@media (min-width: 480px) {
    .site-plate { padding: calc(var(--unit) * 3); }
    .drum-head { padding: calc(var(--unit) * 5) calc(var(--unit) * 3) calc(var(--unit) * 4); }
    .name-card { padding: calc(var(--unit) * 4) calc(var(--unit) * 3) calc(var(--unit) * 3); }
    .board__end,
    .talkback__item { padding-inline: calc(var(--unit) * 3); }
}

@media (min-width: 720px) {
    main { padding-inline: calc(var(--unit) * 3); }
}

/* ===========================================================================
   13 — NOSTR LOGIN DIALOG + VENDORED nostr-universal SKIN
   Two halves.

   The first is our own <dialog>, built as a sibling of the install dialog in
   section 11: same plate, same scrim, same Pico resets, so the app has exactly
   one popup treatment rather than two that nearly match.

   The second is a skin for js/nostr-universal.js. That file is VENDORED — it
   injects its own <style id="nui-styles"> and reads var(--nui-*, <fallback>)
   for every colour, so the palette is handled by declaring those variables
   rather than by fighting its sheet. They are declared on .login-dialog and
   NOT on :root: custom properties inherit, so the whole card is covered, while
   a vendored name can never reach the app's own components. Everything the
   variables cannot express — radii, the soft drop shadow, the round spinner,
   the system font stack, white ink hardcoded onto the primary button — is
   overridden by descendant rule below.
   ========================================================================= */

/* Shell: see the note above .install-dialog for why display/min-width/
   min-height are reset — Pico's `dialog` mins floor any max we set here. */
.login-dialog {
    display: block;
    position: fixed;
    inset: 0;
    min-width: 0;
    min-height: 0;
    margin: auto;
    /* 16px of air each side at 390px, which leaves room for the 8px shadow */
    width: calc(100% - var(--unit) * 4);
    max-width: 420px;
    height: fit-content;
    max-height: calc(100% - var(--unit) * 6);
    overflow-y: auto;
    padding: calc(var(--unit) * 2);
    background: var(--band);
    color: var(--ink);
    border: var(--border);
    border-radius: 0;
    box-shadow: var(--unit) var(--unit) 0 var(--ink);

    /* nostr-universal palette map — every variable its injected sheet reads.
       --nui-success is the exception: the library only ever applies it from JS
       as an inline style/attribute, never from that sheet. It still resolves,
       because inline var() looks the value up on the element's own inherited
       custom properties, which come from here. */
    --nui-card: var(--band);
    --nui-card-hover: var(--control);
    --nui-bg: var(--page);
    --nui-text: var(--ink);
    --nui-muted: var(--muted);
    --nui-primary: var(--action);
    --nui-primary-hover: var(--action);
    --nui-border: var(--ink);
    --nui-error: var(--stop);
    --nui-success: var(--tape);
}
.login-dialog::backdrop {
    background: rgba(0, 0, 0, 0.86);
}
.login-dialog__blurb {
    margin: var(--unit) 0 calc(var(--unit) * 2);
    font-family: var(--font-body);
    font-size: 13px;
    line-height: 1.6;
    color: var(--muted);
}
/* CLOSE is not part of the login flow, so it sits below a rule rather than
   beside the signer options. */
.login-dialog .card-actions {
    justify-content: center;
    margin-top: calc(var(--unit) * 2);
    padding-top: calc(var(--unit) * 2);
    border-top: var(--border);
}

/* The library wraps everything in its own .nui-card. Our dialog already IS the
   card, so this one is flattened to a plain container — a card inside a card
   is two borders saying the same thing. */
.login-dialog .nui-card {
    padding: 0;
    background: none;
    border-radius: 0;
    box-shadow: none;
    font-family: var(--font-body);
    font-size: 13px;
    color: var(--ink);
}

/* The header is a LIVE view label — the library rewrites it per state ("Scan
   QR", "Approve Connection", "Local Dev Key"). It stays, demoted, because our
   own <h2> is the title of this dialog and a second centred 20px title under
   it competed with it. */
.login-dialog .nui-header {
    margin-bottom: calc(var(--unit) * 1.5);
    text-align: left;
}
/* In the opening state that label reads "Login with Nostr / Choose how to sign
   in", which is our own <h2> and blurb said twice. Drop it for that one view —
   matched through the sibling the library un-hides, so every other state keeps
   its heading. */
.login-dialog .nui-card:has(> .nui-view-options:not(.nui-hidden)) > .nui-header {
    display: none;
}
.login-dialog .nui-title {
    margin: 0;
    font-family: var(--font-display);
    font-size: 13px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--ink);
}
.login-dialog .nui-subtitle,
.login-dialog .nui-group-label,
.login-dialog .nui-recent-label,
.login-dialog .nui-label {
    letter-spacing: 0.06em;
    text-transform: uppercase;
}
/* the library's field label, matched to .field__label in section 10 */
.login-dialog .nui-label {
    margin-bottom: 6px;
    font-size: 11px;
    font-weight: 600;
    color: var(--ink);
}

/* Nothing in this theme has a rounded corner, and the library rounds every
   surface it draws. Squared in one pass. */
.login-dialog .nui-status,
.login-dialog .nui-spinner,
.login-dialog .nui-opt-btn,
.login-dialog .nui-btn,
.login-dialog .nui-btn-icon,
.login-dialog .nui-input,
.login-dialog .nui-input-plain,
.login-dialog .nui-scan-wrap,
.login-dialog .nui-qr-uri,
.login-dialog .nui-recent-item,
.login-dialog .nui-recent-remove,
.login-dialog .nui-app-header-signout {
    border-radius: 0;
}
/* The two remaining radii are written as INLINE styles by the library's
   markup, so a plain rule loses to them: the white plate behind the QR (its
   fill is load-bearing for scanning and stays) and the round recent-signer
   avatar, which is the first child of each row. */
.login-dialog .nui-qr-wrap > div,
.login-dialog .nui-recent-item > div:first-child {
    border-radius: 0 !important;
}

/* Controls: same two tiers as .btn / .btn--primary in section 10, so a button
   inside the login card is the same object as a button outside it. */
.login-dialog .nui-opt-btn,
.login-dialog .nui-btn,
.login-dialog .nui-btn-icon,
.login-dialog .nui-input,
.login-dialog .nui-input-plain,
.login-dialog .nui-recent-item,
.login-dialog .nui-status,
.login-dialog .nui-qr-uri {
    border: var(--border);
}
.login-dialog .nui-guest-section {
    border-top: var(--border);
}
.login-dialog .nui-opt-btn,
.login-dialog .nui-btn,
.login-dialog .nui-input,
.login-dialog .nui-input-plain,
.login-dialog .nui-guest-btn,
.login-dialog .nui-more-btn {
    font-family: var(--font-body);
}
.login-dialog .nui-options { gap: var(--unit); }
.login-dialog .nui-btn-row { gap: var(--unit); }
.login-dialog .nui-opt-btn {
    padding: calc(var(--unit) * 1.5) calc(var(--unit) * 2);
    font-size: 12px;
}
.login-dialog .nui-opt-label {
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}
/* the hint is prose, not a label — sentence case survives */
.login-dialog .nui-opt-hint {
    margin-top: 4px;
    letter-spacing: 0;
    text-transform: none;
}

/* Tap targets: the library ships 34-38px controls. 44 is the floor. */
.login-dialog .nui-btn,
.login-dialog .nui-guest-btn,
.login-dialog .nui-more-btn {
    min-height: 44px;
    padding: var(--unit) calc(var(--unit) * 2);
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
}
.login-dialog .nui-btn-icon {
    width: 44px;
    height: 44px;
    background: var(--control);
    color: var(--ink);
}
/* Primary is the one amber block per view. The library hardcodes white ink on
   it, which measures 1.8:1 — --on-accent takes the same fill to 9.9:1, and it
   is what .btn--primary already uses. */
.login-dialog .nui-btn-primary,
.login-dialog .nui-btn-primary:hover {
    background: var(--action);
    color: var(--on-accent);
}
.login-dialog .nui-btn-ghost {
    background: var(--control);
    color: var(--ink);
}
.login-dialog .nui-btn:active,
.login-dialog .nui-opt-btn:active,
.login-dialog .nui-btn-icon:active {
    transform: translate(2px, 2px);
}
@media (hover: hover) {
    .login-dialog .nui-btn-primary:hover {
        box-shadow: var(--shadow-block);
        transform: translate(-2px, -2px);
    }
    .login-dialog .nui-btn-ghost:hover,
    .login-dialog .nui-btn-icon:hover {
        background: var(--control-hover);
        color: var(--ink);
        border-color: var(--ink);
    }
    /* An option row fills with amber on hover; the library leaves the label,
       icon and hint at cream, which measures 1.5:1. Flip all three. */
    .login-dialog .nui-opt-btn:hover:not(:disabled) {
        background: var(--action);
        border-color: var(--ink);
    }
    .login-dialog .nui-opt-btn:hover:not(:disabled) .nui-opt-label,
    .login-dialog .nui-opt-btn:hover:not(:disabled) .nui-opt-icon,
    .login-dialog .nui-opt-btn:hover:not(:disabled) .nui-opt-hint {
        color: var(--on-accent);
    }
}

/* The library sets outline:none on its inputs for ALL focus, keyboard
   included. Put the app's focus ring back — see .field__input:focus. */
.login-dialog .nui-input:focus,
.login-dialog .nui-input-plain:focus {
    outline: 3px solid var(--archive);
    outline-offset: 0;
    border-color: var(--ink);
}
.login-dialog .nui-input,
.login-dialog .nui-input-plain {
    padding: var(--unit);
    font-size: 15px;
}
.login-dialog .nui-input::placeholder,
.login-dialog .nui-input-plain::placeholder {
    color: var(--muted);
    font-size: 12px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}
/* Two placeholders are literal machine strings the user is meant to reproduce
   — a bunker:// URL and an nsec. Shouting those is an instruction to type them
   wrong, so the house uppercase stops at prose. */
.login-dialog .nui-paste-input::placeholder,
.login-dialog .nui-localdev-input::placeholder {
    letter-spacing: 0;
    text-transform: none;
}

/* The QR view centres everything inside it. The code itself earns that; a
   wrapped 400-character connection string and a field label do not. */
.login-dialog .nui-qr-uri,
.login-dialog .nui-view-qr .nui-label {
    text-align: left;
}
.login-dialog .nui-qr-uri-row {
    align-items: flex-start;
}
.login-dialog .nui-qr-uri {
    padding: var(--unit);
    line-height: 1.5;
}

/* Status plates. The library's own fills are translucent indigo/red washes
   that belong to its default theme, so they are replaced outright. */
.login-dialog .nui-status {
    padding: calc(var(--unit) * 2);
}
.login-dialog .nui-status.nui-loading,
.login-dialog .nui-status.nui-approval {
    background: var(--surface);
}
.login-dialog .nui-status.nui-error {
    background: var(--stop);
}
.login-dialog .nui-status.nui-error,
.login-dialog .nui-status.nui-error .nui-status-step,
.login-dialog .nui-status.nui-error .nui-status-detail {
    color: var(--on-accent);
}
.login-dialog .nui-countdown-num {
    font-family: var(--font-body);
    color: var(--ink);
}
/* A square that steps round in eighths: machinery, not a soft iOS ring. */
.login-dialog .nui-spinner {
    border-color: var(--ink);
    border-top-color: var(--action);
    animation-timing-function: steps(8, end);
}

/* Recent-signer rows sit on the recessed fill, like every other field. */
.login-dialog .nui-recent-item {
    padding: var(--unit);
}
.login-dialog .nui-recent-name {
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
}
/* The remove button is revealed by :hover, so on a touch screen it is an
   invisible but still tappable destructive control sitting on top of a row
   whose job is "connect". Show it, size it, and fence it off. */
@media (hover: none) {
    .login-dialog .nui-recent-item { padding-right: calc(var(--unit) * 7); }
    .login-dialog .nui-recent-arrow { display: none; }
    .login-dialog .nui-recent-remove {
        top: 0;
        right: 0;
        bottom: 0;
        width: calc(var(--unit) * 5.5);
        height: auto;
        opacity: 1;
        border-left: var(--border);
        color: var(--stop);
    }
}

/* The app must pass allowLocalDev:true so nostr-universal will hold its own
   device key, and the library repays that by rendering a "Local Dev Key
   (nsec/hex) — development only" row in the signer list. It is a duplicate:
   SETTINGS already carries an IMPORT KEY control that is worded for the person
   actually using this app. Hidden by the action it dispatches, which is the
   only stable handle the library gives it. */
.login-dialog .nui-opt-btn[data-a="doLocalDev"] {
    display: none;
}
