/* ============================================================================
 * assets/app.css — Custom styling not covered by Tailwind utility classes.
 * Tailwind theme extensions live in index.html (the tailwind.config block).
 * ========================================================================== */

body {
    font-family: 'Inter', system-ui, -apple-system, sans-serif;
    background-color: #FAFAFA;
    color: #09090B;
}

/* ---- WHAT USED TO BE HERE: the two mode-driven variable blocks (Phase 4.12).
   `--fs-ui-*` (5 sizes) and `--pad-*` / `--radius-badge` / `--border-pill`
   each carried TWO values — an Editing one on `:root` and a Presenting one
   under `body[data-mode="presentation"]` — and the Presenting/Editing split
   that resolved them is gone (DESIGN §12.1, ADR-031).

   With one register left there is nothing for a variable to resolve, so both
   blocks are deleted and every call site below carries the surviving Editing
   value as a literal. That is the denser register, which is the correct one:
   every surface still on the v2 layer is PM-only now, and no client ever sees
   one. A single-valued variable would only be a token that looked like a
   switch. `text-ui-*` in HGA_TAILWIND_CONFIG (src/index.html) took the same
   treatment for the same reason. */

/* ---- Semantic role trios (DESIGN §3.1) -----------------------------------
   fill = background · accent = border/dot/icon · text = any text. The roles
   are NOT interchangeable: four of the five accents fail AA as text on white.
   Mirrored from the Tailwind config so raw-CSS components below can reach a
   role without a utility class. */
:root {
    /* ===================================================================
       THE v2 PALETTE STOOD HERE AND IS DELETED (2026-09-06).
       -------------------------------------------------------------------
       27 tokens: the five semantic roles with three values each
       (focus / approved / caution / critical / evidence), three text inks,
       three rules, three surfaces, the neutral status dot and the charcoal
       chrome band. They were the whole colour system before Basel, and
       ADR-031 kept them alive DELIBERATELY so both layers could run while
       surfaces converted one screen at a time.

       Every surface converted at Phase 4.20 (ADR-041), and the audit of
       2026-09-06 traced all 221 remaining declarations to dead code or to a
       rule that could not match. Nothing live took a legacy colour, so this
       came out as a deletion rather than a migration.

       WHAT REPLACED THEM: the --basel-* block below. Four STANDINGS keyed to
       "whose move is it" (waiting / working / done / queued), not five roles
       keyed to "what kind of thing is this" — which is why there was never a
       rename that could turn one into the other, and why the two layers had
       to coexist instead of being swapped.

       DESIGN §1-§11 still describe this layer as the record of what it was.
       Their NUMBERING is load-bearing: 58 comments in src/ and tools/ cite
       "DESIGN §n" in that range. Do not renumber them.
       =================================================================== */
    --charcoal-muted: #A39A8C;
}

/* ============================================================================
   BASEL LAYER (DESIGN.md v3) — mirrored from HGA_TAILWIND_CONFIG
   ----------------------------------------------------------------------------
   Same job the v2 mirror above does: raw-CSS components need to reach a token
   without a utility class. The Tailwind config in index.html stays the source
   of truth — these are a copy, and §7's change protocol means the config is
   edited FIRST and this block follows. Nothing here is a new value.

   ADDITIVE by design. Not one v2 declaration above is touched: both layers run
   until the last v2 surface is converted (ADR-031). `body` still carries Inter
   and the v2 canvas — the Basel ground is applied by the shell in Phase 4, not
   by retinting the body out from under every existing screen.
   ========================================================================== */
:root {
    /* --- Grounds. Never more than two of the three in a single view. ------- */
    --basel-paper:        #F7F6F1;
    --basel-sheet:        #FFFFFF;
    --basel-sheet-sunken: #EFEDE6;
    --basel-hair:         #E4E1D7;
    --basel-hair-strong:  #CBC7B9;

    /* --- Ink. Two TEXT levels. `faint` is non-text only — at 2.88:1 on paper
       it fails AA for text and the 3.0 non-text floor alike, so it may carry a
       disabled glyph or a decorative mark and nothing a client has to read.
       Eyebrows and column labels take --basel-ink-muted. ------------------- */
    --basel-ink:        #1B1A17;
    --basel-ink-muted:  #6B675D;
    --basel-ink-faint:  #97927F;
    /* Same colour as --basel-ink, as R,G,B components — the only way an
       alpha-blended use of it (the dialog scrim) can read the token instead
       of a hand-copied rgba() literal. Keep the two in sync by hand; a CSS
       custom property cannot derive one from the other. */
    --basel-ink-rgb:    27, 26, 23;

    /* --- Standings, two roles each (see the config for the full rule).
       -mark = shape fill, NEVER text. -ink = text on any Basel ground, and the
       fill of a solid block carrying --basel-paper text. ------------------- */
    --basel-waiting-mark: #D23B2E;  --basel-waiting-ink: #AC2D22;
    /* --basel-waiting-mark as R,G,B components, for the same reason
       --basel-ink-rgb exists: the "why" strip's tint fills read it at partial
       alpha instead of hand-copying the colour. Keep in sync by hand. */
    --basel-waiting-mark-rgb: 210, 59, 46;
    --basel-working-mark: #2749B8;  --basel-working-ink: #20399A;
    --basel-done-mark:    #23794C;  --basel-done-ink:    #23794C;
    --basel-queued-mark:  #8A867B;  --basel-queued-ink:  #6B675D;

    /* --- Rules carry the hierarchy that v2 carried with boxes, radius and
       shadow. Those three are forbidden in Basel; these are the replacement. */
    --basel-rule-section: 2px;   /* section boundary, in --basel-ink */
    --basel-rule-row:     1px;   /* row hairline, in --basel-hair */
    --basel-rule-active:  3px;   /* active nav item, in --basel-waiting-mark */
    --basel-radius:       0;     /* every corner. There is no exception. */

    /* The drawn <select>/button caret (Basel loads no icon font): two triangle
       gradients meeting at a point. Four call sites hand-copied this same
       value with their own background-position; one token, used four times,
       keeps them from drifting apart by accident. */
    --basel-select-caret: linear-gradient(45deg, transparent 50%, var(--basel-ink-muted) 50%),
                           linear-gradient(135deg, var(--basel-ink-muted) 50%, transparent 50%);

    /* --- Component metrics. Basel keeps the v2 4→48 spacing scale (§5); these
       are the handful of component paddings the scale does not name, taken
       from the canvas rather than re-derived. FLAT — no density switch, because
       the Presenting/Editing mode split is gone (Phase 4.12). ------------- */
    --basel-pad-row-y:     10px;  --basel-pad-row-x:     18px;   /* record row */
    --basel-pad-group-y:    9px;  --basel-pad-group-x:   18px;   /* collapsible group header */
    /* THE NESTING STEP, and it is deliberately NOT --basel-pad-group-x any more
       (ADR-058). It was, and one step of 18px is narrower than the gap between
       two words in the header it is supposed to be indenting — level 2 and
       level 3 read as siblings. 32px is one clear step against a 14px mark, and
       the whole cost lands on the description column: every track to its right
       is fixed, so a nested row's Standing and Need by do not move a pixel. */
    --basel-pad-nest-x:    32px;
    /* WHERE A LOG ROW'S CONTENT STARTS. --basel-pad-row-x put the covered
       checkbox directly under the group header's CHEVRON, so the tick that
       belongs to a record sat in the column of the control that folds its
       section. This is that padding plus the chevron (10px) and its gap (12px),
       which lands the checkbox under the group NAME instead — the tick lines up
       with the words it is about. The log's column header takes the same number
       (.basel-thead--log) or the labels stop naming their columns. */
    --basel-pad-rowlead-x: 40px;
    /* WHERE AN OPEN ROW'S HEADLINE STARTS (ADR-059): the ID column. The row
       lead (40) + the covered checkbox (16) + its gap (12) + the mark (14) +
       its gap (16). A kicker's headline lines up with the kicker's first word,
       not with the gutter its shapes sit in. */
    --basel-pad-headline-x: 98px;
    --basel-pad-screen-y:  36px;  --basel-pad-screen-x:  48px;   /* screen padding */
    /* THE PANEL IS 240 AND THE NAV ROW HAS NO GLYPH TRACK — one decision, not
       two (ADR-077). At 210 a tab name got 102px and four of the eight shipped
       names need more, so they wrapped; at 240 with the 10px glyph track still
       in place the longest clears by 2.6px, which is inside the margin a font
       update moves. Spending the reserved track is what makes this number safe.
       Put the track back and this has to go to 256. */
    --basel-nav-w:        240px;                                 /* side panel */
    /* Identity band (§12.10). A METRIC, not a measurement: #basel-side is
       sticky and 100vh, so it has to subtract this from both its offset and
       its height, and neither can be done against a band whose height varies.
       That is the whole reason .basel-band-name carries a fixed min-height —
       the 28 -> 19 step would otherwise take ~10px off the band on resize,
       mid-session, and this number with it. */
    --basel-band-h:         79px;                                /* identity band */

    /* Fixed toolbar-select caps (ADR-026). Same three numbers, same reason: a
       <select> with no width renders as wide as its longest <option>, so ONE
       70-character building name is what wraps a filter toolbar — not the
       control count. The closed control truncates; the native open list is
       unaffected by the trigger's width. */
    --basel-ctl-bldg:     150px;
    --basel-ctl-disc:     136px;   /* 120 cut "All Disciplines" and "All Departments" at rest (ADR-158) */
    --basel-ctl-phase:    130px;
}

/* Startup problem banner. Styled HERE rather than by the Tailwind classes on
   the element, because the loudest thing it has to report is "Tailwind never
   arrived" — and with the utility classes inert it was laid out in normal flow
   at the bottom of a very long page, i.e. reporting the failure somewhere
   nobody would ever scroll to. Local CSS always loads, so this always holds.
   Colours are the `critical` role from DESIGN.md §2 (fill/accent/text). */
#boot-error {
    display: none;              /* shown by showBootProblems() in js/core/main.js */
    position: fixed;
    bottom: 16px;
    right: 16px;
    z-index: 100;
    max-width: 24rem;
    padding: 16px;
    border: 1px solid #EF4444;
    border-radius: 8px;
    background-color: #FEF2F2;
    color: #B91C1C;
    font-size: 12px;
    line-height: 1.5;
    box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1);
}
#boot-error strong { display: block; margin-bottom: 4px; font-weight: 700; }
/* Each failure is its own line in the innerText showBootProblems() writes. */
#boot-error-text { white-space: pre-line; }

/* Tailwind's own `.hidden`, restated locally. The app hides tabs, dialogs and
   admin controls with this class, and Tailwind arrives over the network — if
   it never does (js/core/cdn.js reports it and the app keeps running), without
   this rule every tab and every hidden panel renders at once and the
   #boot-error banner explaining why is lost in the pile. Identical
   declaration, so it changes nothing when Tailwind is present. */
.hidden { display: none; }

/* WHAT USED TO BE HERE: .ai-dropzone-overlay / .ai-dropzone-active, the v2 AI
   Parser's amber dashed drop overlay. The parser is the Communications screen's
   draft row now (Phase 4.17, ADR-039) and its drop target is the draft textarea
   itself, which takes .basel-drop-active — a 2px ink border rather than an amber
   fill, because §12.2 has three grounds and no accent colour. */

/* Ask AI: brief flash on the row/card a citation or search result jumps to,
   so landing on it after a tab switch is obvious. */
/* The row a citation just landed on. Named for the JUMP and not for the
   deleted Ask AI screen it arrived with (ADR-102) — every caller is a link
   chip, a chronology, a Timeline row or a filed session. */
.basel-jump-flash { animation: basel-jump-flash-kf 2s ease-out; }
@keyframes basel-jump-flash-kf {
    0% { background-color: #FDE68A; } /* amber-200 */
    100% { background-color: transparent; }
}

/* Print Agenda: scope the print job to just the agenda content, nothing else on the page. */
@media print {
    body * { visibility: hidden; }
    #agenda-print-area, #agenda-print-area * { visibility: visible; }
    #agenda-print-area {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
    }
}

#hga-toast-container {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 80;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    align-items: flex-end;
    pointer-events: none;
}

/* BASEL (Phase 4.13). The toast is a solid ink block carrying paper text —
   the one direction DESIGN §12.3 measured as safe in both directions, and the
   same pairing .basel-btn--primary already uses. Square, hairline, no shadow.
   The class NAMES stay `hga-` deliberately: this markup is injected at runtime
   (ADR-007) and every smoke assertion addresses it by these names and ids. It
   is the values that are Basel, not the naming, and the names die with the
   prefix (ADR-031). */
.hga-toast {
    pointer-events: auto;
    display: flex;
    align-items: flex-start;
    gap: 10px;
    max-width: min(22rem, calc(100vw - 2rem));
    background: var(--basel-ink);
    border: var(--basel-rule-row) solid var(--basel-ink);
    border-radius: var(--basel-radius);
    padding: 10px 12px;
    font-size: 13px;
    line-height: 1.45;
    color: var(--basel-paper);
}

/* A toast that carries an action stacks the button under its message rather
   than beside it, so a two-line message keeps its full width. Toasts without
   one look and measure exactly as they did — the wrapper is a plain block. */
.hga-toast-body { display: block; min-width: 0; }
.hga-toast-action {
    display: inline-block;
    margin-top: 7px;
    padding: 3px 10px;
    background: var(--basel-paper);
    border: var(--basel-rule-row) solid var(--basel-paper);
    border-radius: var(--basel-radius);
    color: var(--basel-ink);
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
}
.hga-toast-action:hover { background: var(--basel-sheet-sunken); border-color: var(--basel-sheet-sunken); }
textarea.hga-card-edit {
    resize: vertical;
    line-height: 1.6;
}

/* ============================================================================
   THE CONFIRMATION DIALOG (ADR-007) — BASEL (Phase 4.13)
   ----------------------------------------------------------------------------
   WHAT USED TO BE HERE: .hga-modal-shell / -head / -title / -body / -foot and
   .hga-field-label — the v2 dialog anatomy, 12px radius, canvas-band footer.
   Every dialog in the app is Basel now (js/core/modals.js), so those six rules
   had no element left to style and are deleted rather than left to rot. The
   Basel anatomy that replaced them is in the Basel block below, with the rest
   of the layer.

   THIS ONE STAYS HERE, AND ITS CLASS NAMES STAY `hga-`. showConfirm()
   (js/core/toast.js) injects it at runtime and the failure it most needs to
   survive is Tailwind itself not being available (ADR-007, ADR-006), so it is
   plain CSS and not utility classes — which is unchanged. What changed is
   every VALUE: paper and ink, square corners, hairlines, the standing inks.
   The names are what ~20 smoke assertions address it by; they die with the
   `basel-` prefix (ADR-031), not before. ---------------------------------- */
.hga-confirm-box {
    background: var(--basel-sheet);
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    /* No shadow (DESIGN §12.1) — the scrim behind it carries the separation. */
    max-width: 30rem;
    width: 100%;
    display: flex;
    flex-direction: column;
    position: relative;
    z-index: 60;
}
/* Head, body, actions — the same three parts every Basel dialog has, closed by
   the same 2px ink rule (DESIGN §12.5). */
.hga-confirm-head {
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 16px 24px 12px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
}
/* The eyebrow that replaced the amber circle-and-glyph. Case, weight and
   tracking do what a coloured disc did — and a destructive one takes the
   waiting ink, which is the one place in this app red is allowed to mean
   "your move" (DESIGN §12.3). */
.hga-confirm-kind {
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--basel-ink-muted);
}
.hga-confirm-kind.is-destructive { color: var(--basel-waiting-ink); }
.hga-confirm-title {
    font-size: 19px;
    font-weight: 700;
    letter-spacing: -0.015em;
    line-height: 1.2;
    color: var(--basel-ink);
    margin: 0;
}
.hga-confirm-message {
    font-size: 13px;
    line-height: 1.55;
    color: var(--basel-ink-muted);
    white-space: pre-wrap;
    padding: 18px 24px;
    margin: 0;
}
.hga-confirm-actions {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    padding: 12px 24px;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
    background-color: var(--basel-sheet-sunken);
}
.hga-confirm-btn {
    padding: 6px 14px;
    border-radius: var(--basel-radius);
    font-size: 13px;
    font-weight: 700;
    line-height: 1.2;
    cursor: pointer;
}
.hga-confirm-btn-cancel {
    background: var(--basel-sheet);
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    color: var(--basel-ink-muted);
}
.hga-confirm-btn-cancel:hover { border-color: var(--basel-ink); color: var(--basel-ink); }
.hga-confirm-btn-primary {
    background: var(--basel-ink);
    border: var(--basel-rule-row) solid var(--basel-ink);
    color: var(--basel-paper);
}
.hga-confirm-btn-primary:hover { background: var(--basel-ink-muted); border-color: var(--basel-ink-muted); }
/* The one solid red block in the app, and it says what red always says here:
   the next move is yours. `basel-paper` on `basel-waiting-ink` is the
   direction DESIGN §12.3 measured, not the `mark` hex, which fails as a
   ground under paper text. */
.hga-confirm-btn-destructive {
    background: var(--basel-waiting-ink);
    border: var(--basel-rule-row) solid var(--basel-waiting-ink);
    color: var(--basel-paper);
}
/* Hover DARKENS the border rather than lightening the ground: paper on
   `basel-waiting-mark` measures 4.41:1 and would drop the label below AA
   for as long as the cursor sat on it. Nothing here is hover-only anyway
   (DESIGN §1) — the button reads as destructive at rest. */
.hga-confirm-btn-destructive:hover { border-color: var(--basel-ink); }
.hga-confirm-btn:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }

/* ============================================================================
   BASEL SHELL (DESIGN §12, ADR-031) — Phase 4.1
   ----------------------------------------------------------------------------
   Everything below styles the app shell and NOTHING ELSE. It is 100% Basel:
   every value comes from the --basel-* mirror above, radius is 0, there are no
   shadows, and hierarchy is carried by 2px/1px/3px rules and by type.

   THE BOUNDARY, IN BOTH DIRECTIONS (DESIGN §0):
     - There is no .v2-well left to descend into, and no v2 surface to reach
       out to: Settings converted in Phase 4.20 and took the last one with it
       (DESIGN §12.20, ADR-041). The boundary held for nineteen phases and is
       now simply the whole app.
     - The v2 TOKENS survive one more commit — see the note above .basel-shell
       — so the rule that matters until then is the plain one: never write a
       v2 token, utility or component class onto a Basel surface.

   THERE IS NO MODE (Phase 4.12). §12.1's removal of the Presenting/Editing
   split has landed: `body[data-mode]`, `body.is-presenting` and the
   `--fs-ui-*` / `--pad-*` density pairs are all deleted, and so is
   #basel-mode-toggle, the one piece of shell chrome that ever read the mode.
   Basel's scale is flat (§12.4) and there is nothing left for a rule here to
   key off.
   ========================================================================== */

.basel-shell {
    display: grid;
    grid-template-columns: var(--basel-nav-w) minmax(0, 1fr);
    /* Row 1 is the identity band, spanning both columns; row 2 is the panel
       and the log area. Phase 4.7.

       ROW 1 IS SIZED BY THE TOKEN, NOT BY `auto`. The band spans both rows so
       that it has somewhere to stick (see #basel-band), and a spanning item
       makes an `auto` row's height a distribution question rather than a
       stated one. Naming the height settles it. */
    grid-template-rows: var(--basel-band-h) minmax(0, 1fr);
    align-items: start;
    min-height: 100vh;
    background-color: var(--basel-paper);
    /* body still carries the v2 ink (#09090B) for every unconverted surface;
       the shell states its own rather than inheriting one. */
    color: var(--basel-ink);
}

/* ============ THE MENU FOLDS (ADR-131) =====================================
   ONE control, at the identity band's right end, folds the side panel away.
   The first column goes to 0 and #basel-side is not rendered, so the panel is
   out of the LAYOUT rather than painted under the log — a panel merely hidden
   would still hold 240px of the content column open.

   THIS IS NOT THE DELETED PRESENTING MODE (ADR-037). Nothing becomes
   unavailable, no control is withheld from a client, and no screen changes
   what it draws: the NAVIGATION folds, exactly as the "Internal · PM" group
   already folds (ADR-043). A shut <details> keeps its ids in the DOM too,
   which is what lets renderBaselShell() keep writing to the panel's marks —
   the folder dot included — while the fold is shut.

   THE CONTROL CANNOT LIVE IN THE PANEL, for the obvious reason: a control
   inside the thing it folds cannot unfold it. It lives in the band, which is
   why the band is the one part of the shell this ADR touches at all — and it
   touches nothing on the band's LEFT, where §12.10's 14px edge and the two
   lines that start on it are load-bearing.
   ========================================================================= */
.basel-shell[data-menu="hidden"] { grid-template-columns: 0 minmax(0, 1fr); }
.basel-shell[data-menu="hidden"] > #basel-side { display: none; }

/* --- Standing marks. Shape fills only, never text (§12.3, §12.8 #5). ------ */
.basel-mark-waiting { fill: var(--basel-waiting-mark); }
.basel-mark-working { fill: var(--basel-working-mark); }
.basel-mark-done    { fill: var(--basel-done-mark); }
.basel-mark-queued  { fill: var(--basel-queued-mark); }

/* ========================== IDENTITY BAND (§12.10) ========================
   Phase 4.7. Spans both grid columns and is closed by the 2px ink rule —
   the same device that closes a record headline. It sits on
   basel-sheet-sunken, the side panel's own ground, so the two read as one
   chrome and the count of grounds in view stays at two (§12.8 #1).

   Left padding is 14px, NOT --basel-pad-screen-x: that is the side panel's
   text edge (.basel-side-block's padding-x, the "Internal · PM only"
   eyebrow, the folder label), and both lines of the band start on it. Right
   padding IS --basel-pad-screen-x, so the countdown's right edge lines up
   with the log screen's.
   ========================================================================= */

#basel-band {
    grid-column: 1 / -1;
    /* SPANS BOTH ROWS, SITS IN THE FIRST. A sticky grid item can only travel
       inside its own grid area, so a band confined to the 79px row 1 would
       scroll away and leave a band-height of bare paper above the pinned side
       panel — the panel is sticky at top:var(--basel-band-h) precisely because
       the band is expected to still be there. Spanning row 2 as well gives it
       the whole shell to stick in; align-self:start plus the fixed height keep
       it in row 1 at rest, so it never covers the panel or the log. */
    grid-row: 1 / -1;
    align-self: start;
    position: sticky;
    top: 0;
    z-index: 21;   /* over the log; the side panel's Manage menu is at 20 and
                      opens at the panel's other end, so the two never meet */
    display: flex;
    align-items: center;
    gap: 24px;
    min-width: 0;
    /* The token IS the height, not a note about it. 12 + 30 (the name's floor)
       + 5 + 17 (the sub-line) + 13 + the 2px rule = 79, and stating it here
       means the two calc()s on #basel-side can never drift from what the band
       actually measures — including if a fallback face changes a line box. */
    height: var(--basel-band-h);
    padding: 12px var(--basel-pad-screen-x) 13px 14px;
    background-color: var(--basel-sheet-sunken);
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
}

/* GROWS TO FILL THE SLOT, and that is not cosmetic. Left at its default
   flex:0 1 auto the block sizes to its own content, so the name's clientWidth
   is whatever the text happens to be — it never widens when the viewport does,
   the ResizeObserver never sees a change, and a name stepped down at 1280px
   stays stepped at 1920px where it would fit comfortably. */
.basel-band-identity { flex: 1 1 auto; min-width: 0; }

/* THE FIXED HEIGHT IS THE POINT, not a rounding of the 28px line box.
   fitBaselBandName() swaps text-basel-title for text-basel-head when the name
   will not fit, and that step fires on resize as well as on load — so without
   a floor the band would lose ~10px mid-session and --basel-band-h, which the
   sticky panel above depends on, could not be a constant at all.

   The id qualifier is load-bearing too: tailwind.css loads AFTER this file
   (js/core/cdn.js), so a bare .basel-band-name would lose line-height to
   .text-basel-title's 1.15 — and 28 x 1.15 = 32.2px is taller than the floor,
   which would put the height back in play. */
#basel-band .basel-band-name {
    min-height: 30px;
    line-height: 1.05;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
    color: var(--basel-ink);
}

.basel-band-sub    { display: flex; align-items: center; gap: 12px; margin-top: 5px; }
.basel-band-num    { color: var(--basel-ink-muted); }
.basel-band-div    { flex: 0 0 auto; width: 1px; height: 13px; background-color: var(--basel-hair-strong); }
/* The phase name is what gives way if the sub-line is ever short of room —
   never the number and never Home (ADR-043). */
.basel-band-phase  { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--basel-ink-muted); }

/* HOME, IN THE BAND (ADR-043, DESIGN §12.22). The first word on the sub-line,
   before the commission number: you are looking at one project, and the line
   that says which one is where the way back to all of them belongs. Written by
   renderBaselBand(); its active state by renderBaselHomeNav(). Same treatment
   as the Manage summary — working-ink, 700 — and NO red rule: the band is not
   the nav, and §12.3's red is spoken for. A word, never a row: the band's
   height is the token the sticky panel is calculated from (§12.10). */
.basel-band-home {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 0;
    border: 0;
    border-radius: var(--basel-radius);
    background: none;
    font: inherit;
    font-weight: 700;
    color: var(--basel-working-ink);
    white-space: nowrap;
    cursor: pointer;
}
.basel-band-home:hover        { color: var(--basel-ink); }
.basel-band-home[data-active] { color: var(--basel-ink); cursor: default; }
.basel-band-home:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }

/* HIDE MENU / SHOW MENU — the band's one control (ADR-131). 30px tall, so it
   sits inside the band's 79px without touching either rule; a 1px hairline box
   at the quiet end of the scale, because folding the nav is not a project
   verb. Folded, it inverts to solid ink: "Show menu" is then the ONLY way back
   to the navigation, and the one control that can undo a fold has to read as
   live rather than as the same quiet box that folded it. The size and weight
   come from .text-basel-note on the element (13/700 with the rule below), never
   from a number here — §12.6. */
.basel-menu-toggle {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    height: 30px;
    padding: 0 10px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: transparent;
    color: var(--basel-ink-muted);
    font: inherit;
    font-weight: 700;
    line-height: 1;
    white-space: nowrap;
    cursor: pointer;
}
.basel-menu-toggle:hover { color: var(--basel-ink); border-color: var(--basel-ink); }
.basel-menu-toggle:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }
.basel-menu-toggle[data-folded] {
    background-color: var(--basel-ink);
    border-color: var(--basel-ink);
    color: var(--basel-paper);
}
.basel-menu-toggle[data-folded]:hover { color: var(--basel-paper); }

/* ============================ SIDE PANEL ================================== */

#basel-side {
    /* Placed explicitly. The band above spans both rows in both columns, so
       auto-placement would find no free cell in row 2 and open an implicit
       row 3 for the panel and the log. */
    grid-column: 1;
    grid-row: 2;
    position: sticky;
    /* --basel-band-h TWICE, and both are required. The offset alone leaves the
       panel a band-height too tall, which pushes the folder connection below
       the fold — the one control §12 says must never become something you have
       to hunt for. The height alone leaves it overlapping the band. */
    top: var(--basel-band-h);
    display: flex;
    flex-direction: column;
    height: calc(100vh - var(--basel-band-h));
    /* NOT overflow:hidden. The panel is 210px and the Manage menu is wider
       than that by design (it carries full sentences, not icons), so clipping
       here silently cut the menu in half. The scrolling is done by
       .basel-side-scroll below, which is the only part that needs it. */
    overflow: visible;
    z-index: 20;   /* the Manage menu opens over the v2 well */
    background-color: var(--basel-sheet-sunken);
    border-right: var(--basel-rule-row) solid var(--basel-hair-strong);
}

/* Only the record scrolls. The Internal · PM group and the folder connection
   are pinned to the bottom edge below this (ADR-043) — "nothing is reaching
   the workbook" has to stay visible rather than become discoverable, and the
   PM's own screens fold rather than scroll. The phase countdown used to be
   pinned here too, before it went up into the band (§12.10). */
.basel-side-scroll {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
}
#basel-internal, #basel-folder { flex: 0 0 auto; }

.basel-side-block {
    padding: 12px 14px;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
/* The rule between the nav and the pinned blocks belongs to the scroll
   container, not to the last item inside it, or it scrolls away with them. */
.basel-side-scroll > :last-child { border-bottom: 0; }
/* Two pinned blocks below the scroll region (ADR-043): the Internal · PM group
   and, under it, the folder connection. The rule that separates pinned from
   scrolling belongs to the first; the second draws its own top rule, and the
   first draws no bottom rule so the two never double up. */
#basel-internal { border-top: var(--basel-rule-row) solid var(--basel-hair); border-bottom: 0; }
/* #basel-folder's own top rule is declared with the rest of its block, "5.
   Folder connection + Manage" below — one definition, not two. */

/* Nav and the internal list own their own item padding — the block would
   double it and pull the 3px active rule off the panel edge. */
.basel-side-section,
#basel-internal { padding-left: 0; padding-right: 0; }

/* --- The two section eyebrows (Phase 4.12, then ADR-043) -------------------
   The panel names its groups: the shared project record, and the PM's own
   things. That naming IS the replacement for the Presenting / Editing toggle
   (§12.1) — the separation used to be a state the PM switched into and is now
   a statement the panel makes with nothing hidden to support it, so both
   labels have to read as one set. There were three until ADR-043: the file
   lost its eyebrow when it joined the Internal · PM group as the part of it
   that never folds.

   Same token, same case, same ink as the "Internal · PM" summary, and the same
   14px text edge as the band and the folder label. It is a heading, so it
   takes the block's own padding rather than the nav item's 11px + 3px rule
   inset. */
.basel-section-label {
    display: block;
    padding: 0 14px 8px;
    color: var(--basel-ink-muted);
}

/* --- 1. Identity — moved into the band above (§12.10, Phase 4.7) ---------- */

/* --- 2. Log nav ----------------------------------------------------------- */
.basel-nav-item {
    display: grid;
    /* Three tracks since ADR-077: number, name, count. The leading 10px glyph
       track is deleted, and --basel-nav-w is calculated without it. */
    grid-template-columns: 22px minmax(0, 1fr) auto;
    gap: 8px;
    align-items: center;
    width: 100%;
    padding: 8px 14px 8px 11px;      /* 11 + the 3px rule = the panel's 14px */
    border: 0;
    border-left: var(--basel-rule-active) solid transparent;
    border-radius: var(--basel-radius);
    background: none;
    text-align: left;
    color: var(--basel-ink-muted);
    cursor: pointer;
}
.basel-nav-item:hover { color: var(--basel-ink); }
.basel-nav-item[data-active] {
    border-left-color: var(--basel-waiting-mark);
    /* Paper, not sheet. The canvas lifts the active row onto white, but that
       would put paper + sunken + sheet in one view and §12.8 #1 caps it at
       two of the three. Paper against sunken is a real step and keeps the
       count honest. */
    background-color: var(--basel-paper);
    color: var(--basel-ink);
}
/* WHAT USED TO BE HERE: .basel-nav-glyph, a 10px track held empty since Phase
   4.7 for a per-log dominant-standing shape that was never specified — eight
   phases, no feature. ADR-077 spent it on the tab name instead: it is the 18px
   (the track plus its gap) that lets the panel stop at 240 rather than 256, and
   §12.3 had turned against the idea anyway — a shape in the nav is a fifth
   place a standing gets drawn, and the panel is not where anybody's move is
   decided. If it is ever specified it costs a relayout; that is the price. */
.basel-nav-num   { color: var(--basel-ink-muted); }
.basel-nav-item[data-active] .basel-nav-num { color: var(--basel-waiting-ink); }
.basel-nav-label { min-width: 0; font-weight: 600; }
.basel-nav-item[data-active] .basel-nav-label { font-weight: 700; }
.basel-nav-count { color: var(--basel-ink-muted); }

/* --- 3. Internal · PM — pinned, one fold (ADR-043) ------------------------ */
.basel-internal-summary {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 4px 14px 8px;
    cursor: pointer;
    list-style: none;
    color: var(--basel-ink-muted);
}
.basel-internal-summary::-webkit-details-marker { display: none; }
.basel-chevron { flex: 0 0 auto; transform: rotate(-90deg); transition: transform 120ms linear; }
#basel-internal[open] .basel-chevron { transform: rotate(0deg); }
.basel-internal-label { color: var(--basel-ink-muted); }
/* How many items are put away. Shown only while folded: open, the list is its
   own count. Mono, muted — the same voice as a nav count. */
.basel-internal-count { margin-left: auto; color: var(--basel-ink-muted); }
#basel-internal[open] .basel-internal-count { display: none; }

.basel-util {
    display: block;
    width: 100%;
    padding: 6px 14px;
    border: 0;
    border-radius: var(--basel-radius);
    background: none;
    text-align: left;
    color: var(--basel-ink-muted);
    cursor: pointer;
}
.basel-util:hover { color: var(--basel-ink); }
.basel-util[data-active] { color: var(--basel-ink); font-weight: 700; }

/* WHAT USED TO BE HERE: .basel-mode / .basel-mode-btn, the Presenting /
   Editing pair pinned above the folder block (Phase 4.12). It was the one
   piece of shell chrome whose appearance depended on state.viewMode, it was
   always migration scaffolding, and ADR-031's stated deletion trigger has
   fired. Nothing in this file reads a mode any more. */

/* --- 4. Phase countdown — the band's right group (§12.10) -----------------
   Stacked in the side panel, it read eyebrow / 36px figure / progress track.
   In the band it is one horizontal group pushed right by margin-left:auto,
   and the figure drops from text-basel-metric to text-basel-head: at 36px it
   out-shouted the project name, which is the thing the band exists to lead
   with. The phase NAME moved to the sub-line beside the commission number. */
#basel-phase {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-left: auto;
    flex: 0 0 auto;
}
.basel-phase-track   { flex: 0 0 auto; width: 118px; height: 6px; background-color: var(--basel-hair); }
.basel-phase-fill    { display: block; height: 6px; background-color: var(--basel-ink); }
.basel-phase-figure  { color: var(--basel-ink); }
.basel-phase-caption { color: var(--basel-ink-muted); white-space: nowrap; }

/* --- 5. Folder connection + Manage ---------------------------------------- */
#basel-folder            { border-top: var(--basel-rule-row) solid var(--basel-hair); border-bottom: 0; padding-bottom: 16px; }
/* THE CONNECTION IS A DOT AND A LABEL; THE PATH IS ONE HOVER AWAY (ADR-043).
   The state row is a button now. Hover shows the popover, a click pins it,
   Escape or a click anywhere else lets it go. With no path (browser only) the
   button is disabled and reads exactly as the plain row did. */
.basel-folder-conn       { position: relative; }
.basel-folder-state {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 0;
    border: 0;
    border-radius: var(--basel-radius);
    background: none;
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
}
.basel-folder-state[disabled] { cursor: default; }
.basel-folder-state:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }
.basel-folder-label      { font-weight: 700; color: var(--basel-ink); }
/* Opens UPWARD over the row, on paper behind a hairline like the Manage menu,
   and is allowed to run out over the main column — a path is wider than a
   210px panel and truncating it would defeat the hover. z-index 30 matches
   the Manage menu: both hang off the same pinned block. */
.basel-folder-pop {
    position: absolute;
    bottom: 100%;
    left: -4px;
    z-index: 30;
    margin-bottom: 6px;
    padding: 6px 10px;
    background-color: var(--basel-paper);
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    white-space: nowrap;
}
.basel-folder-path       { color: var(--basel-ink-muted); }
/* ONE CHILD NOW — Manage. The "Saved 10:42" span it used to sit opposite was
   deleted with the timestamp (ADR-076); space-between with a single item is
   flex-start, so the rule is left as it is rather than churned. */
.basel-folder-foot       { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 8px; }

/* THE FAILED-SAVE BLOCK. The only alarm in the shell, and it spends no new
   colour to be one: the 3px --basel-waiting-mark rule is the same pairing the
   active nav item uses (§12.3's rationed red), and the heading takes
   --basel-waiting-ink, which is the AA-safe text sibling of that same red.
   Radius 0 and a sheet ground, like every other Basel surface. */
.basel-folder-alert {
    margin-top: 8px;
    padding: 8px 10px;
    border-left: var(--basel-rule-active) solid var(--basel-waiting-mark);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet);
}
.basel-folder-alert-head { display: block; font-weight: 700; color: var(--basel-waiting-ink); }
.basel-folder-alert-body { display: block; margin-top: 4px; color: var(--basel-ink-muted); }
.basel-folder-alert .basel-act { margin-top: 8px; }

.basel-manage            { position: relative; }
.basel-manage-summary    { font-weight: 700; color: var(--basel-working-ink); cursor: pointer; list-style: none; }
.basel-manage-summary::-webkit-details-marker { display: none; }
/* Opens upward and to the RIGHT, out over the main column. Right-aligning it
   to the summary would run it off the left edge of a 210px panel. */
.basel-manage-menu {
    position: absolute;
    bottom: 100%;
    left: 0;
    z-index: 30;
    width: 232px;
    margin-bottom: 6px;
    padding: 4px 0;
    background-color: var(--basel-paper);
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
}
.basel-manage-item {
    display: block;
    width: 100%;
    padding: 6px 12px;
    border: 0;
    border-radius: var(--basel-radius);
    background: none;
    text-align: left;
    color: var(--basel-ink-muted);
    cursor: pointer;
    white-space: nowrap;
}
.basel-manage-item:hover { color: var(--basel-ink); background-color: var(--basel-sheet-sunken); }
.basel-manage-rule { height: var(--basel-rule-row); margin: 4px 0; background-color: var(--basel-hair); }

/* ---- The submenu (ADR-116) -----------------------------------------------
   A nested <details>, so it opens on a CLICK and stays open. NEVER on hover:
   this app is driven live over a screen share and the person watching cannot
   hover (DESIGN §1), so a cascading hover menu is unreachable for half the
   room. One level deep only — a submenu inside a submenu opens off the edge
   of a 240px panel and cannot be aimed at over a compressed video stream.

   It opens to the RIGHT, out over the main column, for the same reason the
   menu itself opens upward and to the right: there is nothing to the left of
   a 240px panel. */
.basel-manage-fold { position: relative; }
.basel-manage-fold > summary { list-style: none; cursor: pointer; }
.basel-manage-fold > summary::-webkit-details-marker { display: none; }
.basel-manage-more {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}
/* The chevron turns to point DOWN when the fold is open, which is the same
   grammar every other chevron in this app uses (§12.31). */
.basel-manage-chev { color: var(--basel-ink-faint); }
.basel-manage-fold[open] > summary { color: var(--basel-ink); font-weight: 700; }
.basel-manage-sub {
    position: absolute;
    top: 0;
    left: 100%;
    z-index: 31;
    width: 214px;
    margin-left: 4px;
    padding: 4px 0;
    background-color: var(--basel-paper);
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
}
/* A SHORT WINDOW (ADR-158): baselFitManageMenu() caps the menu to the room
   under the band and it scrolls; the Copies submenu then opens inline, since a
   flyout inside a scrolling box would be cut off. */
.basel-manage--short .basel-manage-menu { overflow-y: auto; }
.basel-manage--short .basel-manage-sub {
    position: static;
    width: auto;
    margin: 0 0 4px 12px;
    padding: 0;
    border: 0;
    border-left: var(--basel-rule-row) solid var(--basel-hair-strong);
}
/* A named group, not an unlabelled rule (Phase 4.12). Twelve items separated
   by two rules said only "these three sets are different"; the PM still read
   all twelve to find one. Same eyebrow token, case and ink as the panel's own
   section labels, so the menu reads as part of the panel it hangs off. */
.basel-manage-group {
    display: block;
    padding: 6px 12px 2px;
    color: var(--basel-ink-muted);
}

/* Off-screen rather than display:none: a display:none file input still opens
   its picker on .click(), but keeping it laid out removes the question. */
.basel-visually-gone { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }

/* ============================ MAIN COLUMN ================================= */

#basel-main {
    grid-column: 2;
    grid-row: 2;
    display: flex;
    flex-direction: column;
    min-width: 0;
    /* The band's height comes off here too, or every screen carries a
       permanent band-height of scroll it has no content for. */
    min-height: calc(100vh - var(--basel-band-h));
}

/* --- THE SEAM IS GONE, AND THAT IS THE RECORD OF IT (Phase 4.20).
   A `.v2-well` rule stood here from Phase 4.1 to Phase 4.19. It restated the
   v2 canvas ground, the v2 ink and the v2 font stack on each unconverted
   panel, so neither the Basel paper ground nor Archivo could inherit across
   the line — §0's failure case ("a Basel row inside a v2 card") had no shared
   parent to happen in. One well per unconverted panel, deleted one at a time,
   `<main>` carrying the v2 container geometry beside them.

   Settings was the last panel and it converted (DESIGN §12.20, ADR-041), so
   the class, this rule and `<main id="v2-main">` went together exactly as the
   old comment here said they would. There is no v2 SURFACE left in this app.

   THE v2 TOKEN LAYER IS STILL HERE AND STILL LIVE — §1–§11 above, and the
   `--canvas`/`--ink-*` custom properties they name. Nothing renders through
   it any more; deleting it and dropping the `basel-` prefix is one mechanical
   pass and it is the migration's last step (ADR-031). Do not start it here. */

/* --- Footer legend: exactly four standings, always visible (§12.3). --------
   Sticky, because "always visible" is the point: the legend is what teaches
   the shape grammar, and a client scrolling a long log is exactly when they
   need to look it up. The 2px ink rule is the section boundary above it.

   `margin-top: auto` IS WHAT MAKES "ALWAYS VISIBLE" TRUE ON A SHORT LOG.
   Sticky only pins an element the page can scroll past; a five-row log does
   not scroll, so the legend sat wherever the content ended — halfway up the
   window, with paper below it and the 2px rule reading as a divider in the
   middle of nothing. #basel-main is already a flex column at min-height:100vh,
   so an auto top margin on the last item absorbs the free space and pushes the
   legend to the window bottom; when there IS no free space the margin resolves
   to 0 and `sticky` does the work exactly as before. One property, both cases,
   and it does not care which of the converted screens is on screen — which
   `flex: 1` on a content area would have, since <main> and each Basel screen
   are separate siblings and only one of them is visible at a time. */
#basel-legend {
    position: sticky;
    bottom: 0;
    z-index: 10;
    margin-top: auto;
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 24px;
    padding: var(--basel-pad-group-y) var(--basel-pad-group-x);
    border-top: var(--basel-rule-section) solid var(--basel-ink);
    background-color: var(--basel-paper);
}
.basel-legend-title { color: var(--basel-ink-muted); }
.basel-legend-item  { display: inline-flex; align-items: center; gap: 8px; }
/* The words take the -ink variants — the only ones that clear AA as text.
   The shapes above take -mark. They are not interchangeable (§12.3). */
.basel-legend-waiting { color: var(--basel-waiting-ink); }
.basel-legend-working { color: var(--basel-working-ink); }
.basel-legend-done    { color: var(--basel-done-ink); }
.basel-legend-queued  { color: var(--basel-queued-ink); }

/* ============================================================================
   BASEL LOG SCREEN (DESIGN §12, ADR-031) — Phase 4.2 (Issues), 4.3 (Actions)
   ----------------------------------------------------------------------------
   Everything below styles EVERY converted log screen and nothing else — there
   is one stylesheet for the Basel log, not one per log, which is the CSS half
   of §12.9's "a config entry and a field list". It is 100%
   Basel: every value comes from the --basel-* mirror above, radius is 0, there
   are no shadows, and hierarchy is carried by 2px/1px rules and by type.

   THE BOUNDARY, IN BOTH DIRECTIONS (DESIGN §0), unchanged from the shell:
     - There is no .v2-well left in either direction (Phase 4.20, ADR-041).
       The rule that outlives it: never a v2 token, utility or component class
       on a Basel surface.

   MODE-INDEPENDENT LIKE THE SHELL, and since Phase 4.12 there is no mode to
   be independent of: `body[data-mode]`, `--fs-ui-*` and `--pad-*` are deleted
   and Basel's scale is flat (§12.4). The rule that outlived them is the one
   worth keeping — a control this screen does not offer is NOT BUILT, never
   hidden with a class, because a control hidden by CSS is still on the page
   for a stray click to find. tools/smoke-test.js asserts it.

   THE ROW GRID IS DECLARED TWICE, ON PURPOSE — once on .basel-thead-cells and
   once on .basel-row-btn, from the same custom property. A <table> would have
   aligned them for free and cost the collapse/expand behaviour ADR-022 is
   built on; one shared variable is the cheapest honest substitute.
   ========================================================================== */

.basel-screen {
    /* Every screen sits directly on the paper ground of the Basel main
       column, full-bleed, with the screen padding §12.5 names. This used to
       read "NOT in a .v2-well", which was the whole visible difference between
       a converted screen and an unconverted one; there are no unconverted ones
       left (Phase 4.20). */
    padding: 0 var(--basel-pad-screen-x) var(--basel-pad-screen-y);
    min-width: 0;
}

/* ---- Screen head (BaselAnatomy #5) --------------------------------------- */

.basel-screen-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 8px 16px;
    padding-top: var(--basel-pad-screen-y);
}
.basel-screen-title {
    margin: 0;
    display: flex;
    align-items: baseline;
    gap: 10px;
    color: var(--basel-ink);
}
/* The section number is the one place red is spent outside a standing and the
   3px active-nav rule — it is the same number the side panel's nav shows, and
   it is what ties the screen to the log you clicked. */
.basel-screen-num { color: var(--basel-waiting-ink); }

.basel-screen-sub {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 14px;
    color: var(--basel-ink-muted);
}
/* Middots between the figures, drawn rather than typed so the strings stay
   exactly what renderDrawingSummary() writes. */
.basel-screen-sub > * + *::before { content: "· "; color: var(--basel-ink-faint); }

.basel-screen-actions {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 10px;
}

/* ---- Buttons ------------------------------------------------------------- */

.basel-btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 8px 15px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet);
    color: var(--basel-ink-muted);
    font-weight: 700;
    white-space: nowrap;
    cursor: pointer;
}
.basel-btn:hover { border-color: var(--basel-ink); color: var(--basel-ink); }
/* One solid ink block per screen, and it is the primary verb. `basel-paper` on
   `basel-ink` is the direction §12.3 measured as safe. */
.basel-btn--primary {
    background-color: var(--basel-ink);
    border-color: var(--basel-ink);
    color: var(--basel-paper);
}
.basel-btn--primary:hover { background-color: var(--basel-ink-muted); border-color: var(--basel-ink-muted); color: var(--basel-paper); }
/* The destructive primary (Phase 4.13). showConfirm()'s destructive variant is
   the one place it is worn — the Remap & Delete Phase dialog that also wore it
   went with the editable phase list (ADR-044). It is `basel-paper` on
   `basel-waiting-INK`, never on the `mark` hex, which measures 4.41:1 under
   paper text (DESIGN §12.3). Hover darkens the border rather than lightening
   the ground, for the same reason. */
.basel-btn--destructive {
    background-color: var(--basel-waiting-ink);
    border-color: var(--basel-waiting-ink);
    color: var(--basel-paper);
}
.basel-btn--destructive:hover { border-color: var(--basel-ink); color: var(--basel-paper); }

/* A quieter button for chrome inside a record — same shape, smaller. */
.basel-act {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 11px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet);
    color: var(--basel-ink-muted);
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
}
.basel-act:hover { border-color: var(--basel-ink); color: var(--basel-ink); }
.basel-act[data-active] { border-color: var(--basel-ink); color: var(--basel-ink); font-weight: 700; }

.basel-icon-btn {
    border: 0;
    background: none;
    padding: 2px 4px;
    color: var(--basel-ink-muted);
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    cursor: pointer;
}
.basel-icon-btn:hover { color: var(--basel-ink); }
.basel-icon-btn[data-active] { color: var(--basel-ink); }

/* A labelled checkbox rather than a two-state word button: "CLIENT" beside
   every update read as a badge on the entry rather than as the control that
   changes it, and Basel has no lock glyph to fall back on (no icon font). An
   unticked box labelled "Internal" says both what the entry is and what the
   control does. It is a PM control on a PM's screen (ADR-036: an internal
   entry is shown and marked; Export Client Copy is what removes it). */
.basel-update-internal {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    color: var(--basel-ink-muted);
    cursor: pointer;
    white-space: nowrap;
}
.basel-update-internal:hover { color: var(--basel-ink); }

/* ---- Toolbar (BaselAnatomy #6, #7, #8) ----------------------------------- */

/* TWO ROWS, DECLARED — not one row left to wrap.
   Basel.dc.html draws a single row of eight controls. This log has thirteen,
   and every one of them is load-bearing: ADR-026 caps Building / Discipline /
   Phase because they exist, ADR-027 made Status a multi-select, and grouping,
   sorting, Covered and Walkthrough all moved here when the tables went. Thirteen
   controls measure ~1590px against the 1294px a 1600px viewport leaves after the
   210px side panel and the screen gutters, so "one row" was never available at
   any width this app is driven at — the only question was whether the wrap
   landed somewhere meaningful. Left to itself it produced three ragged rows with
   the Reset orphaned on the last one.

   So the split is stated, and it is the split the DOM already had: what you are
   LOOKING THROUGH (group, sort, covered, walkthrough) on the first row, what you
   are FILTERING BY on the second, with Reset closing it. The second row is the
   [data-toolbar] element, which is the same behavioural contract core/sort.js
   and core/dropdowns.js have always read. */
.basel-toolbar {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 18px 0 14px;
}
.basel-toolbar-set {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    min-width: 0;
}

/* ---- THE TAB STRIP (ADR-131) ----------------------------------------------
   A SECOND SEGMENTED VOCABULARY, standing BESIDE .basel-seg rather than
   replacing it. .basel-seg is a small boxed switch INSIDE a screen — the
   Schedule's three views, Settings' seven sub-tabs — drawn at the toolbar's
   own quiet register. This is the SCREEN'S OWN SPLIT: two halves of one tab,
   at the record headline's size and weight, closed by the 2px ink rule that
   closes every section boundary (§12.5). The two are different questions and
   they get different controls.

   SETTINGS MAY ADOPT THIS LATER and is deliberately untouched now: seven
   sub-tabs is a different shape from two, and moving them is a second decision
   about a second screen (ADR-116).

   The type comes from .text-basel-head on the button (19/700/−.015em) and the
   quiet half from .text-basel-id on the count — written out in full, never
   assembled (§12.6). --basel-ink-faint is never text: the count and the
   meeting's label take --basel-ink-muted (§12.3). */
.basel-tabs {
    display: flex;
    align-items: flex-end;
    gap: 28px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
}
.basel-tab {
    display: inline-flex;
    align-items: baseline;
    gap: 9px;
    padding: 0 0 10px;
    /* Pulls the tab's own 3px rule down over the strip's 2px, so the active
       tab's mark IS the section boundary rather than a line above it. */
    margin-bottom: calc(var(--basel-rule-section) * -1);
    border: 0;
    border-bottom: 3px solid transparent;
    border-radius: 0;
    background: none;
    font: inherit;
    font-weight: 700;
    color: var(--basel-ink-muted);
    white-space: nowrap;
    cursor: pointer;
}
.basel-tab:hover { color: var(--basel-ink); }
.basel-tab[data-active] {
    color: var(--basel-ink);
    border-bottom-color: var(--basel-ink);
    cursor: default;
}
.basel-tab[data-active] .basel-tab-n { color: var(--basel-ink-muted); }
.basel-tab:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }
.basel-tab-n { color: var(--basel-ink-muted); font-weight: 700; }

/* ---- The caption line (row 2) ---------------------------------------------
   WHAT YOU ARE LOOKING THROUGH, SAID RATHER THAN BOXED. Group, Sort, Covered
   and Stale only used to be .basel-ctl controls — the same 34px hairline box,
   the same 13px, the same weight as the eight filters above them — so the
   split the toolbar has always DECLARED was invisible: two identical rows of
   boxes read as one set that happened to wrap. Measured before the change:
   row 1 was 535px of content in a 1294px row, a short ragged line above a full
   one, with nothing to say which row did what.

   So this row keeps the words and drops the boxes. The field name is muted,
   the VALUE is ink at 700, and the row sits directly on the table it
   describes. Nothing here is below 13px (§12.4) and no control changed what it
   does — both selects keep their ids, their handlers and the data-attributes
   that keep them off the Reset badge.

   `display: contents` on the slot is load-bearing: the three rendered controls
   are written into it as innerHTML, and without it they would be one flex item
   between two others and the gaps would not match. */
.basel-viewline {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 6px 24px;
    min-width: 0;
    color: var(--basel-ink-muted);
    font-size: 13px;
    line-height: 1.55;
}
.basel-viewline-slot { display: contents; }
/* Agenda Prep's caption line sat 3px above THE AGENDA; the logs' sits ~14px
   above their column heads (ADR-158). */
#agenda-prep-viewline { margin-bottom: 14px; }
.basel-viewitem {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    white-space: nowrap;
    cursor: pointer;
}
.basel-viewitem:hover { color: var(--basel-ink); }
/* The value the control currently states. It has to outweigh the field name,
   because the label is the confirmation channel — the same rule .basel-ctl b
   follows one row up. `font: inherit` is what stops a native <select> from
   dropping to the UA font: this element carries no type utility class, so its
   size comes from .basel-viewline and its weight from right here. */
.basel-viewsel {
    appearance: none;
    -webkit-appearance: none;
    padding: 0 14px 0 0;
    border: 0;
    border-radius: var(--basel-radius);
    background-color: transparent;
    color: var(--basel-ink);
    font: inherit;
    font-weight: 700;
    cursor: pointer;
    /* A native <select> is as wide as its WIDEST OPTION, so "Grouped by Folder"
       reserved the width of "No grouping" and left the caret floating a dozen
       pixels off the value it belongs to — the caption line reads as a sentence
       and a gap in the middle of it is the ragged look this row exists to fix.
       field-sizing shrinks the box to the chosen option. It is a progressive
       enhancement: where it is unsupported the control falls back to exactly
       the widest-option width it has always had, which is loose but correct. */
    field-sizing: content;
    /* The caret is drawn, not a font glyph: Basel loads no icon font. Same two
       gradients .basel-ctl--select uses, pulled to this control's right edge. */
    background-image: var(--basel-select-caret);
    background-position: calc(100% - 4px) center, calc(100% - 0px) center;
    background-size: 4px 4px, 4px 4px;
    background-repeat: no-repeat;
}
.basel-viewsel:hover { color: var(--basel-ink); }
.basel-viewsel:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }

/* THE GROUP CONTROL IS A BUTTON NOW (ADR-058), because the question stopped
   having one answer: it states the current order in words and opens the
   grouping dialog. Same register as .basel-viewsel beside it — the field name
   muted, the value in ink at 700 — and the same drawn caret, because it is the
   same gesture from the reader's side: this word is a control, press it to
   change what the list is doing. Basel loads no icon font, so the caret is two
   gradients here exactly as it is there. */
.basel-viewbtn {
    padding: 0 14px 0 0;
    border: 0;
    border-radius: var(--basel-radius);
    background-color: transparent;
    color: var(--basel-ink);
    font: inherit;
    font-weight: 700;
    text-align: left;
    cursor: pointer;
    background-image: var(--basel-select-caret);
    background-position: calc(100% - 4px) center, calc(100% - 0px) center;
    background-size: 4px 4px, 4px 4px;
    background-repeat: no-repeat;
}
.basel-viewbtn:hover { text-decoration: underline; text-underline-offset: 3px; }
.basel-viewbtn:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }

.basel-ctl {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 34px;
    padding: 0 12px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet);
    color: var(--basel-ink-muted);
    font-weight: 600;
    white-space: nowrap;
    cursor: pointer;
}
.basel-ctl:hover { border-color: var(--basel-ink); }
/* A control that states its current value states it in ink — the label is the
   confirmation channel, so the value has to outweigh the field name. */
.basel-ctl b { color: var(--basel-ink); font-weight: 700; }
.basel-ctl--select {
    appearance: none;
    -webkit-appearance: none;
    padding-right: 26px;
    /* The caret is drawn, not a font glyph: Basel loads no icon font. */
    background-image: var(--basel-select-caret);
    background-position: calc(100% - 15px) 15px, calc(100% - 11px) 15px;
    background-size: 4px 4px, 4px 4px;
    background-repeat: no-repeat;
    overflow: hidden;
    text-overflow: ellipsis;
}
.basel-ctl--search { width: 216px; cursor: text; font-weight: 400; }
.basel-ctl--search::placeholder { color: var(--basel-ink-muted); }
/* ADR-026's three caps. See --basel-ctl-* above for why these exist. */
.basel-ctl--bldg  { max-width: var(--basel-ctl-bldg); }
.basel-ctl--disc  { max-width: var(--basel-ctl-disc); }
.basel-ctl--phase { max-width: var(--basel-ctl-phase); }
.basel-ctl--check { gap: 7px; font-weight: 600; }
.basel-ctl:focus-visible, .basel-input:focus-visible, .basel-select:focus-visible,
.basel-textarea:focus-visible, .basel-row-btn:focus-visible, .basel-group:focus-visible {
    outline: 2px solid var(--basel-ink);
    outline-offset: -2px;
}

/* The reset is a verb, not a control: no box until it has something to say. */
.basel-reset {
    margin-left: auto;
    height: 34px;
    padding: 0 10px;
    border: var(--basel-rule-row) solid transparent;
    border-radius: var(--basel-radius);
    background: none;
    color: var(--basel-ink-muted);
    font-weight: 700;
    white-space: nowrap;
    cursor: pointer;
}
.basel-reset:hover { color: var(--basel-ink); }
.basel-reset.hga-reset-active {
    /* The class name is core/sort.js's, set generically on every log's reset
       button; the appearance is entirely Basel. Renamed with the v2 layer. */
    color: var(--basel-waiting-ink);
    border-color: var(--basel-hair-strong);
}

/* Status multi-select popover (ADR-027) — a <details>, never <select multiple>. */
.basel-multi { position: relative; }
.basel-multi-btn { list-style: none; }
.basel-multi-btn::-webkit-details-marker { display: none; }
.basel-multi-panel {
    position: absolute;
    /* UNDER THE BAND (21), over the footer legend (10) — ADR-158. An open list
       used to paint over the project's name as the page scrolled. JS places it
       `fixed` against its summary (baselPlacePopoverPanel()), so no box that
       scrolls can clip it. */
    z-index: 15;
    top: calc(100% + 4px);
    left: 0;
    min-width: 220px;
    display: flex;
    flex-direction: column;
    padding: 6px;
    background-color: var(--basel-sheet);
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
}
.basel-multi-item {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 5px 7px;
    color: var(--basel-ink-muted);
    white-space: nowrap;
    cursor: pointer;
}
.basel-multi-item:hover { background-color: var(--basel-sheet-sunken); color: var(--basel-ink); }

/* One checkbox appearance for the whole layer. accent-color, not a hand-built
   box: the native control is keyboard- and screen-reader-correct already. */
.basel-check {
    flex-shrink: 0;
    width: 14px;
    height: 14px;
    margin: 0;
    accent-color: var(--basel-ink);
    cursor: pointer;
}

/* ---- Column header + rows (BaselAnatomy #10) ----------------------------- */

/* mark · ID · description · standing · need-by · chevron. */
:root { --basel-row-cols: 14px 76px minmax(0, 1fr) 210px 62px 16px; }

.basel-thead,
.basel-row-head {
    display: grid;
    grid-template-columns: 16px minmax(0, 1fr);
    gap: 12px;
    align-items: center;
}
.basel-thead {
    padding: 0 var(--basel-pad-row-x) 9px;
    /* 2px ink: the section boundary (§12.5). */
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
}
/* THE LOG'S column header, and only the log's. `.basel-thead` is also the
   Schedule's, Home's and the Site Matrix's head — none of which has a covered
   checkbox under it — so the lead offset that lines a log's tick up with its
   group names is scoped to a class the six log heads carry and nothing else. */
.basel-thead--log { padding-left: var(--basel-pad-rowlead-x); }
.basel-thead-cells,
.basel-row-btn {
    display: grid;
    grid-template-columns: var(--basel-row-cols);
    gap: 16px;
    align-items: center;
    min-width: 0;
}
.basel-thead-cells > span { color: var(--basel-ink-muted); }

.basel-rows { display: block; }

.basel-row {
    background-color: var(--basel-sheet);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-row-head {
    padding: var(--basel-pad-row-y) var(--basel-pad-row-x)
             var(--basel-pad-row-y) var(--basel-pad-rowlead-x);
}
.basel-row-btn {
    border: 0;
    background: none;
    padding: 0;
    text-align: left;
    color: inherit;
    cursor: pointer;
    font: inherit;
}
.basel-row-mark { display: inline-flex; align-items: center; }
.basel-row-id { color: var(--basel-ink); }
.basel-row-desc {
    min-width: 0;
    color: var(--basel-ink);
    font-weight: 600;
    /* One line, ellipsised. A collapsed row that wraps stops being scannable
       at twenty records, which is the whole reason rows collapse (ADR-022). */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* THE FOLDER PATH ON THE ROW (ADR-058). Built only when the grouping dialog has
   taken the path off the sections, so the one-line row above is untouched in
   every other state. The cell becomes two stacked lines, each ellipsised on its
   own — the description keeps its 15px/600 and the path is the 11px uppercase
   eyebrow in muted ink, which is the one register below 13px §12.4 permits. */
.basel-row-desc--path {
    display: flex;
    flex-direction: column;
    gap: 1px;
    white-space: normal;
}
.basel-row-desc--path > span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.basel-row-path { color: var(--basel-ink-muted); }

.basel-row-standing { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-row-date { color: var(--basel-ink-muted); white-space: nowrap; }
.basel-row-chev { display: inline-flex; align-items: center; color: var(--basel-ink-muted); }
.basel-row--open > .basel-row-head .basel-row-chev svg { transform: rotate(180deg); }

/* Internal, and covered-in-this-meeting. Both are 3px inset rules rather than
   a fill, so they read on a compressed screen share without spending a colour
   the standing vocabulary needs. Internal is ink (a fact about the record);
   covered is the done mark (a fact about the meeting). */
.basel-row--internal { box-shadow: inset 3px 0 0 var(--basel-ink); }
.basel-row--covered  { box-shadow: inset 3px 0 0 var(--basel-done-mark); background-color: var(--basel-sheet-sunken); }
.basel-row--internal.basel-row--covered { box-shadow: inset 3px 0 0 var(--basel-done-mark), inset 6px 0 0 var(--basel-ink); }

/* An internal UPDATE inside a thread takes the same 3px ink rule its record's
   row takes, so "internal" is one mark in both places rather than two
   vocabularies. Both are ink, never red — §12.3 rations red to "your move",
   and internal is a fact about the record, not an answer to that question. */
.basel-update--internal { box-shadow: inset 3px 0 0 var(--basel-ink); padding-left: 9px; }

.basel-flag {
    display: inline-block;
    margin-right: 8px;
    padding: 1px 5px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    color: var(--basel-ink-muted);
    vertical-align: 1px;
}

/* ---- Standings (DESIGN §12.3) -------------------------------------------- */

/* The words. -ink variants only: these clear 4.5:1 as text on every Basel
   ground, and the -mark variants (used by .basel-mark-* on the SHAPES) do not.
   They are not interchangeable. */
.basel-standing { font-weight: 700; }
.basel-standing-waiting { color: var(--basel-waiting-ink); }
.basel-standing-working { color: var(--basel-working-ink); }
.basel-standing-done    { color: var(--basel-done-ink); }
.basel-standing-queued  { color: var(--basel-queued-ink); }
.basel-standing-tally   { font-weight: 700; opacity: 1; }

/* ---- Group sections (BaselAnatomy #9) ------------------------------------ */

.basel-group {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 12px;
    padding: var(--basel-pad-group-y) var(--basel-pad-group-x);
    border: 0;
    border-top: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet-sunken);
    color: var(--basel-ink);
    text-align: left;
    cursor: pointer;
    font: inherit;
}
/* DEPTH, DRAWN AT LAST (DESIGN §12.9). buildLogGroupTree() has always nested
   correctly and renderNode() has always emitted nested .basel-group-section —
   but the class carried no rule at all, so `Electrical Conduit | Bathroom`
   rendered as two flush headers and read as two sibling groups of one record
   each, with the parent's subtree count making the duplicate "1" look like a
   bug rather than a total.

   THE INDENT MOVES THE ROWS TOO, AND THAT IS SAFE. The row grid is declared
   twice from --basel-row-cols, once on .basel-thead-cells and once on
   .basel-row-btn, and that shared declaration is what replaces a <table> — so
   the obvious worry is that indenting a row slides it out from under the
   column header. It does not: `description` is the only flexible track and
   every column to its right is fixed, so a narrower content box is absorbed
   entirely by that one column and Standing, Need by and the chevron do not
   move a pixel. The ID and the mark do move, which is the point of an indent.
   The whole cost is description width: 18px a level, out of roughly 770.

   THE STEP IS 32px NOW, NOT 18 (ADR-058). It was --basel-pad-row-x, borrowed
   because it was already on the screen — and 18px is narrower than the gap
   between two words inside the header it indents, so level 2 and level 3 read
   as siblings sitting slightly apart rather than as one inside the other. That
   only got worse with multi-key grouping, which can nest five deep. 32px reads
   as one clear step against a 14px mark, and the cost is unchanged in kind:
   description is the only flexible track, so a nested row loses width there and
   Standing, Need by and the chevron do not move.

   The nested header also drops from the sunken ground to paper and loses its
   top rule, so depth is carried by indent AND register — which is what keeps
   the third and fourth levels legible when the indent alone has run out. */
.basel-group-section .basel-group-section { padding-left: var(--basel-pad-nest-x); }
.basel-group-section .basel-group-section > .basel-group {
    background-color: var(--basel-paper);
    border-top: 0;
}

.basel-group--muted .basel-group-name { color: var(--basel-ink-muted); }
.basel-group-chev { display: inline-flex; align-items: center; color: var(--basel-ink-muted); }
.basel-group[aria-expanded="false"] .basel-group-chev svg { transform: rotate(-90deg); }
.basel-group-name  { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-group-count { color: var(--basel-ink-muted); }
/* The worst standing under this header, with a count. A collapsed group that
   hides a red triangle has to say so, or collapsing becomes a way to lose the
   only thing on screen that mattered. */
.basel-group-worst {
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: 7px;
    font-weight: 700;
}

/* ---- The expanded record (BaselAnatomy #10; its parts are #15 headline,
       #16 meta line, #17 field grid, #18 links, #19 folded run) ------------- */

.basel-record {
    padding: 4px var(--basel-pad-row-x) 18px 46px;   /* indented past the mark + check gutter */
    display: flex;
    flex-direction: column;
    gap: 16px;
}
.basel-record-desc { display: flex; flex-direction: column; gap: 9px; }

/* THE HEADLINE SITS BETWEEN THE ROW HEAD AND THE RECORD (ADR-059), aligned to
   the ID column: the row above it keeps its small facts and gives up the
   sentence, so the sentence is read once. The open head tightens to 4px under
   its line — the headline is its second line, not a new block. */
.basel-row-headline { padding: 0 var(--basel-pad-row-x) 0 var(--basel-pad-headline-x); }
.basel-row--open .basel-row-head { padding-bottom: 4px; }
.basel-row-desc--open { min-height: 1px; }

/* The facts line under the headline, with the polish verb on its end — the one
   line there that is not a field (§12.9 rule 5), so a verb on it is not a verb
   in the grid. */
.basel-record-facts { display: flex; align-items: baseline; gap: 4px 12px; flex-wrap: wrap; }
.basel-record-facts .basel-record-meta { flex: 1 1 auto; }

/* THE HEADLINE. Still a textarea — still the field's declared editDom, still
   commit-on-change — but in the record's own register rather than the field
   grid's: 19/700 with no box, closed by the 2px ink rule that IS the section
   boundary between what the record is and what it carries (§12.5).

   The rule is on the control, not on a wrapper, so it doubles as the edit
   affordance: there is always a visible edge under the text, which is what
   stops a borderless headline reading as undiscoverable on a screen share. It
   darkens rather than appears on focus. */
.basel-desc-head {
    width: 100%;
    min-width: 0;
    padding: 0 0 9px;
    border: 0;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
    border-radius: var(--basel-radius);
    background-color: transparent;
    color: var(--basel-ink);
    font-family: inherit;
    font-size: 19px;
    line-height: 1.3;
    font-weight: 700;
    letter-spacing: -0.015em;
    resize: vertical;
}
.basel-desc-head::placeholder { color: var(--basel-ink-muted); font-weight: 400; letter-spacing: 0; }
.basel-desc-head:focus-visible { outline: 0; background-color: var(--basel-sheet-sunken); }

/* The derived facts, which are not fields (see baselRecordMetaHtml). Middots
   drawn rather than typed, same as the screen head's figure strip. */
.basel-record-meta {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 2px 14px;
    color: var(--basel-ink-muted);
}
.basel-record-meta > * + *::before { content: "· "; color: var(--basel-ink-faint); }

/* FOUR, EXPLICITLY, not auto-fit. `repeat(auto-fit, minmax(190px, 1fr))` put
   seven fields across a 1294px record and truncated half of them — a building
   name in a 150px cell is a field the PM cannot read, which is the same defect
   the tables were removed for (ADR-022). Four is what Basel.dc.html draws and
   what a 15px value needs; it steps down rather than compressing.

   No top rule any more: the description's 2px ink IS this section's boundary,
   and a hairline 20px under it was the second of two rules doing one job. */
.basel-fields {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 16px 24px;
}
@media (max-width: 1180px) { .basel-fields { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (max-width: 900px)  { .basel-fields { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
/* A 2px left rule instead of a box: the field IS its label plus its value, and
   a border on four sides would be the box-inside-a-box §12.1 removes. */
.basel-field { border-left: var(--basel-rule-section) solid var(--basel-hair-strong); padding-left: 12px; min-width: 0; }
.basel-field-k { color: var(--basel-ink-muted); margin-bottom: 5px; }
.basel-field-v { min-width: 0; color: var(--basel-ink); }
.basel-field-static { display: inline-flex; align-items: baseline; gap: 8px; flex-wrap: wrap; }
.basel-field-note { color: var(--basel-ink-muted); }
.basel-field-check { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; }

/* ---- Inputs -------------------------------------------------------------- */

.basel-input,
.basel-select,
.basel-textarea {
    width: 100%;
    min-width: 0;
    padding: 5px 8px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet);
    color: var(--basel-ink);
    font-family: inherit;
}
.basel-input::placeholder, .basel-textarea::placeholder { color: var(--basel-ink-muted); }
/* A TEXT BOX WITH A LIST BEHIND IT (a name, a group path, a source reference)
   draws the browser's own heavy black triangle the moment it has the focus —
   the one browser-drawn mark ADR-158's pass left behind, and Neil saw it on
   "Requested by". It takes the app's caret instead, at the size every other
   control draws it. The box stays a text box (type to narrow, click the caret
   for the whole list); a date keeps its calendar button, which is why this is
   scoped to [list]. The LIST ITSELF is still drawn by the browser and cannot
   be styled — replacing it is the people-picker job on BACKLOG. Every list-backed
   input in the app is covered, boxed (.basel-input) or quiet (.basel-set-quiet). */
input[list]::-webkit-calendar-picker-indicator {
    width: 8px;
    height: 4px;
    padding: 0;
    margin: 0 2px 0 8px;
    background-image: var(--basel-select-caret);
    background-position: 0 0, 4px 0;
    background-size: 4px 4px, 4px 4px;
    background-repeat: no-repeat;
    background-color: transparent;
    color: transparent;
    opacity: 1;
    cursor: pointer;
}
/* EVERY <select> WEARS THE DRAWN CARET (ADR-158, extending ADR-144's call for
   Mapping). The browser's own heavy black arrow was on every dropdown in a
   record, a dialog and a Settings list; the toolbar, the caption line and
   Mapping drew Basel's. One caret, the toolbar's own token. */
.basel-select {
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    padding-right: 26px;
    text-overflow: ellipsis;
    background-image: var(--basel-select-caret);
    background-position: calc(100% - 15px) 50%, calc(100% - 11px) 50%;
    background-size: 4px 4px, 4px 4px;
    background-repeat: no-repeat;
}
/* The one weight variation a control gets: the field a record is keyed on —
   Building, Status, Priority, Owner — in an Add dialog. A class, never an
   inline style. TWO classes, because a type-size utility (text-basel-body)
   also sets a weight and loads after this sheet: at one class it silently won
   and every key field drew regular (ADR-158). */
.basel-select.basel-select--strong { font-weight: 700; }
/* ONE HEIGHT FOR A ROW OF FIELDS (ADR-158). In a record's field grid and an
   Add dialog the dropdowns were 30px, the text boxes 35px, the mono sheet box
   29px and the date 31px, side by side. The toolbar's 34px for all of them. */
.basel-field-v > .basel-input,
.basel-field-v > .basel-select { height: 34px; }
.basel-textarea { resize: vertical; }

/* ---- Links (ADR-028) ----------------------------------------------------- */

.basel-link-section,
.basel-thread {
    border-top: var(--basel-rule-row) solid var(--basel-hair);
    padding-top: 14px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.basel-section-title { color: var(--basel-ink-muted); }
.basel-subhead { color: var(--basel-ink-muted); }

.basel-link-row { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.basel-link-chips { display: flex; flex-wrap: wrap; gap: 6px; }
.basel-link-controls { display: flex; flex-wrap: wrap; gap: 18px; }
.basel-link-control { display: flex; flex-direction: column; gap: 5px; min-width: 240px; }
.basel-link-add { display: flex; align-items: stretch; gap: 6px; }
.basel-link-add .basel-input { min-width: 220px; }
.basel-link-log { color: var(--basel-ink-muted); }

.basel-linked-block { display: flex; flex-direction: column; }
/* TOP-ALIGNED, NOT CENTRED (ADR-136). Centring was safe while every one of
   these rows was exactly one line high. It stopped being safe when the
   description grew a second line: a 10px mark centred against a four-line
   record floats halfway down it, pointing at nothing. The three nudges below
   put the mark and the two labels back on the FIRST line of text rather than
   on the middle of the block — each is half the difference between that
   child's line box and the note ramp's 20.15px (13 × 1.55), and each is
   written out rather than derived, because there is no calc() that can see
   another element's line-height. */
.basel-linked {
    display: grid;
    grid-template-columns: 10px 62px 84px minmax(0, 1fr) auto;
    /* COLUMN GAP ONLY. This was `gap: 10px` — written when the row was one
       grid row, where the two axes cannot be told apart. The sentence
       (ADR-136) gave the row a second grid row and that shorthand started
       paying out vertically as well: 10px of gap on top of the sentence's own
       3px, so a name sat 13px off its own sentence and read as unrelated to
       it. Measured at 98px a row against 88 with the gap named properly. */
    column-gap: 10px;
    row-gap: 0;
    align-items: start;
    width: 100%;
    padding: 7px 0;
    border: 0;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
    background: none;
    text-align: left;
    cursor: pointer;
    font: inherit;
}
.basel-linked:hover { background-color: var(--basel-sheet-sunken); }
.basel-linked-id   { color: var(--basel-ink); }
.basel-linked-log  { color: var(--basel-ink-muted); }
/* The nudges. 10px mark -> 5px; 13/1.3 id (16.9px) -> 2px; 11/1.3 eyebrow
   (14.3px) -> 3px. The status takes the note ramp itself and needs none. */
.basel-linked > svg { margin-top: 5px; }
.basel-linked > .basel-linked-id  { padding-top: 2px; }
.basel-linked > .basel-linked-log { padding-top: 3px; }
.basel-linked-desc { min-width: 0; color: var(--basel-ink-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* THE NAME (ADR-136). Full ink at the row's own weight — both values already
   live on this row, on the record id, so the name costs no new token. It keeps
   the base cell's one line and its ellipsis: a name past the column is a name
   somebody mis-wrote, and it is at most 60 characters by the split's own rule. */
.basel-linked-desc--summary { color: var(--basel-ink); font-weight: 700; }
/* NO NAME — the whole description stays on the row and wraps. Every one of the
   base cell's three truncation declarations has to be undone by hand; leaving
   any one of them in place is a silent single-line row. */
.basel-linked-desc--wrap {
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    max-width: 78ch;
    text-wrap: pretty;
}
/* THE SENTENCE, on its own line under the name. `-3 / -1` rather than a column
   number: this row is a five-track grid, a four-track one without its log
   column, and a different four on the Schedule's rail — but the description is
   the second-to-last track in all three, so counting from the END is the one
   expression that is right everywhere. 78ch is a reading measure; the ~976px
   the track offers at 1440 is not one. Never clamped and never ellipsised. */
.basel-linked-sentence {
    grid-column: -3 / -1;
    /* THE SECOND ROW, STATED. Without it this cell is auto-placed, and grid
       auto-placement walks a cursor that never goes backwards: the sentence
       claimed `-3 / -1`, the cursor came to rest past it, and the NEXT child —
       the standing, "In Progress · 21d" — could no longer fit beside it. It
       was pushed to a THIRD row and into the 10px mark track, where a 95px
       phrase wraps to four lines and takes the row from 78px to 135. Naming
       the row makes the sentence definitely-placed, so every other child
       auto-flows along the first row as it always did. */
    grid-row: 2;
    min-width: 0;
    max-width: 78ch;
    padding-top: 3px;
    color: var(--basel-ink-muted);
    text-wrap: pretty;
}
.basel-linked-status { font-weight: 700; white-space: nowrap; }

/* ---- Chips (links, attachments) ------------------------------------------ */

.basel-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 3px 7px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet);
    max-width: 100%;
}
.basel-chip-main {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    min-width: 0;
    border: 0;
    background: none;
    padding: 0;
    color: var(--basel-ink-muted);
    cursor: pointer;
    font: inherit;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.basel-chip-main:hover { color: var(--basel-ink); }
.basel-chip-size { color: var(--basel-ink-muted); }
.basel-chip-x {
    border: 0;
    background: none;
    padding: 0 2px;
    color: var(--basel-ink-muted);
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
}
.basel-chip-x:hover { color: var(--basel-waiting-ink); }
.basel-chip-row { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }

/* ---- The update thread (BaselAnatomy #11) -------------------------------- */

.basel-thread-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.basel-thread-title { display: inline-flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.basel-thread-count { color: var(--basel-ink-muted); }

/* The segmented thread filter. Five words at the eyebrow size — the toolbar's
   own vocabulary one scale down, so "narrow what I am looking at" reads the
   same at record scale as it does at screen scale. It is never stored and
   never per-record: closing the row drops it (baselThreadFilters), because a
   filter that survived a reopen would hide entries on a shared screen with
   nothing on the page saying so. */
.basel-seg { display: inline-flex; align-items: center; }
.basel-seg-btn {
    padding: 4px 10px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-right: 0;
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet);
    color: var(--basel-ink-muted);
    cursor: pointer;
}
.basel-seg-btn:last-child { border-right: var(--basel-rule-row) solid var(--basel-hair-strong); }
.basel-seg-btn:hover { color: var(--basel-ink); }
.basel-seg-btn[data-active] {
    background-color: var(--basel-ink);
    border-color: var(--basel-ink);
    color: var(--basel-paper);
}
.basel-seg-btn:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }

/* ---- Folded machine runs (DESIGN §12.9) ----------------------------------
   A run of two or more consecutive machine entries collapses to one hairline
   that still names how many, on what date, and which fields moved — so the
   fold tells the truth shut, which is why this is a fold and not a filter
   default. A run of one is never folded: a disclosure wrapping a single item
   is furniture, not a summary. */
.basel-fold { border-top: var(--basel-rule-row) solid var(--basel-hair); background-color: var(--basel-paper); }
.basel-fold-summary {
    display: flex;
    align-items: center;
    gap: 9px;
    padding: 7px 0;
    color: var(--basel-ink-muted);
    cursor: pointer;
    list-style: none;
}
.basel-fold-summary::-webkit-details-marker { display: none; }
.basel-fold-summary:hover { color: var(--basel-ink); }
.basel-fold-summary:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }
.basel-fold-chev { display: inline-flex; align-items: center; transform: rotate(-90deg); transition: transform 120ms linear; }
.basel-fold[open] .basel-fold-chev { transform: rotate(0deg); }
.basel-fold-fields { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-fold-body { padding-left: 18px; }
.basel-fold-body .basel-update:first-child { border-top: 0; }

/* ONE LINE (ADR-059): Who · the box · Post, on the sheet, no sunken well. The
   two-row well spent 120px before a word was typed; this spends 60, and the
   entries start where the eye lands. */
.basel-composer {
    display: flex;
    flex-direction: column;
    gap: 6px;
}
.basel-composer-line { display: flex; align-items: flex-start; gap: 8px; }
.basel-composer-text {
    flex: 1 1 auto;
    width: auto;
    height: 34px;
    min-height: 34px;
    padding: 6px 10px;
    resize: none;
    overflow: hidden;
}
.basel-composer-quiet { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 10px; color: var(--basel-ink-muted); }
.basel-composer-hint { white-space: nowrap; }
.basel-composer-hint::before { content: "· "; color: var(--basel-ink-faint); }
/* The typed box is the popover's last row — one control, both answers. */
.basel-who-foot { display: flex; flex-direction: column; gap: 6px; padding: 8px 10px; background-color: var(--basel-paper); }
.basel-who-foot .basel-input--who { flex: none; width: 100%; }
/* The correction row's picker fills its 158px cell and ellipsises its label. */
.basel-who-host--edit, .basel-who-host--edit .basel-multi { display: block; width: 100%; min-width: 0; }
.basel-who-host--edit .basel-multi-btn { width: 100%; justify-content: space-between; padding: 0 10px; }
.basel-who-host--edit .basel-multi-btn > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- The WHO picker (DESIGN §12.9) ---------------------------------------
   WAS a row of one chip per person above the textarea. Seven names spent ~940
   of the composer's 1206px on a control that is usually one click and grew
   with the People list — but the deciding defect was that the composer had TWO
   "who" controls with the textarea between them: the chips, and "Not listed —
   type a name" two rows below. They asked the same question in two places.

   Now one <details> on the action row, beside the free-text box, re-using
   .basel-multi — the popover ADR-027 already built for the status filter, so
   this is a new arrangement of shipped parts rather than a new component. The
   panel groups by COMPANY, because "is that one of ours or one of theirs?" is
   what the room actually asks; the company headers carry no shape, since the
   four shapes mean standings and spending one on a company would un-teach the
   legend exactly as a per-log nav glyph would (§12.3). */
.basel-who-host { display: inline-flex; align-items: center; gap: 8px; }
/* 400px, not the 220px .basel-multi-panel default, and a 150px role column
   rather than 130. The role is the whole point of the join, and the longest one
   the shipped roster carries — "Disney Facilities Director" — measures 143px,
   so 130 ellipsised it to "Disney Facilities Dir…", which answers nothing. A
   genuinely long custom role still ellipsises; the row title carries the full
   name · role · email · phone either way. */
.basel-who-panel { min-width: 400px; max-height: 340px; overflow-y: auto; padding: 0; }
.basel-who-co {
    position: sticky;
    top: 0;
    padding: 7px 10px 5px;
    background-color: var(--basel-sheet-sunken);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    color: var(--basel-ink);
}
.basel-who-co--loose { color: var(--basel-ink-muted); }
.basel-who-row {
    display: grid;
    grid-template-columns: 14px minmax(0, 1fr) 150px;
    gap: 10px;
    align-items: center;
    padding: 6px 10px;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    color: var(--basel-ink-muted);
    cursor: pointer;
}
.basel-who-row:hover { background-color: var(--basel-sheet-sunken); }
.basel-who-row--empty { grid-template-columns: minmax(0, 1fr); cursor: default; }
.basel-who-name { color: var(--basel-ink); font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-who-role { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* The free-text box no longer stretches: it sits beside the picker as the
   second half of one question, not as a full-width field of its own. */
.basel-input--who { flex: 0 1 230px; width: auto; }
.basel-person {
    padding: 3px 9px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet);
    color: var(--basel-ink-muted);
    font-weight: 600;
    cursor: pointer;
}
.basel-person:hover { border-color: var(--basel-ink); color: var(--basel-ink); }
/* Selected is fill + weight, so it survives compression and never depends on
   hover (DESIGN §9.3). Paper on ink is the measured-safe direction (§12.3). */
.basel-person[data-active] {
    background-color: var(--basel-ink);
    border-color: var(--basel-ink);
    color: var(--basel-paper);
    font-weight: 700;
}
.basel-person--add { border-style: dashed; }

.basel-polish-host:empty { display: none; }
.basel-polish {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 10px 12px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    background-color: var(--basel-sheet);
}
.basel-polish-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; }
.basel-polish-col { border-left: var(--basel-rule-section) solid var(--basel-hair-strong); padding-left: 12px; }
.basel-polish-col--suggested { border-left-color: var(--basel-ink); }
.basel-polish-text { margin: 4px 0 0; color: var(--basel-ink-muted); }
.basel-polish-actions { display: flex; flex-wrap: wrap; gap: 8px; }

/* 840px is the reading measure the v2 thread settled on and it survives the
   conversion unchanged: the cap is about line length for PROSE, which is a
   content rule (DESIGN §8), not a token. The composer above deliberately does
   NOT take it — a compose box visibly short of the record's own width was the
   defect that put the cap on the entry list alone in the first place. */
.basel-thread-list { display: flex; flex-direction: column; max-width: 840px; }
/* 88px, because the stored date is a full YYYY-MM-DD and 62px broke it across
   two lines in the mono. The date is a figure, not prose; it does not wrap. */
.basel-update {
    display: grid;
    grid-template-columns: 88px 158px minmax(0, 1fr) auto;
    gap: 12px;
    align-items: start;
    padding: 9px 0;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-update-date { color: var(--basel-ink-muted); padding-top: 2px; white-space: nowrap; }
.basel-update-who  { color: var(--basel-ink); font-weight: 700; min-width: 0; }
/* The company under the name, from the same roster join the picker uses. "Who
   is this and who do they work for" is a READING question — it is asked months
   later, looking at an entry — and a bare name never answered it. A name with
   no roster entry renders nothing at all rather than an apology. */
.basel-update-co { display: block; margin-top: 1px; color: var(--basel-ink-muted); font-weight: 400; }
.basel-update-body { min-width: 0; color: var(--basel-ink-muted); }
.basel-update-text { display: block; white-space: pre-wrap; }
.basel-update-tools { display: inline-flex; align-items: center; gap: 8px; }
/* System-authored entries collapse to one muted line: scanning for what a
   human typed must not be slowed down by machine noise (ADR-019). */
.basel-update--auto .basel-update-who,
.basel-update--auto .basel-update-body { color: var(--basel-ink-muted); font-weight: 400; font-style: italic; }
.basel-update-edited { margin-left: 8px; color: var(--basel-ink-muted); }
.basel-update-attach { display: inline-block; margin-top: 6px; color: var(--basel-ink-muted); cursor: pointer; }
.basel-update-attach:hover { color: var(--basel-ink); }
.basel-update-editor { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
.basel-update-editor-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.basel-update-editor-hint { margin-left: auto; color: var(--basel-ink-muted); white-space: nowrap; }
/* THE CORRECTION ROW (ADR-059): the same grid, three cells turned live. The
   date is a mono field under a hairline — the 88px column holds YYYY-MM-DD
   and nothing else, so a native date control with its own chrome would not. */
.basel-update--editing { align-items: start; }
.basel-update-date-edit {
    width: 100%;
    min-width: 0;
    padding: 8px 0 7px;
    border: 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: transparent;
    color: var(--basel-ink);
}
.basel-update-date-edit:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }

/* ---- Record actions (BaselAnatomy #12) ----------------------------------- */

.basel-record-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 10px;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
    padding-top: 12px;
}
/* "Ask today" leads the row and is the second red on the screen, because it is
   the only thing on the row addressed to the MEETING rather than to the
   record: it is what the PM says out loud when this item comes up. */
.basel-ask-label { color: var(--basel-waiting-ink); white-space: nowrap; }
.basel-ask-input {
    flex: 1 1 260px;
    min-width: 0;
    padding: 5px 8px;
    border: 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: transparent;
    color: var(--basel-ink);
    font-family: inherit;
    font-weight: 600;
}
.basel-ask-input::placeholder { color: var(--basel-ink-muted); font-weight: 400; }
.basel-ask-input:focus-visible { outline: 0; border-bottom-color: var(--basel-ink); }

/* ---- The verbatim block (DESIGN §12.17) ----------------------------------
   THE THIRD KIND OF THING A RECORD CAN CARRY. The field grid holds values you
   can type; the meta line holds facts the app derives; this holds SUBSTANCE —
   on Communications, the archived text of what actually came in. It is neither
   of the other two and could not be either: a `.basel-field` is a 2px rule, an
   uppercase key and a control, an anatomy that promises you can type there, and
   this text is read-only by design; the meta line is one row of middot-separated
   derived facts and this is four hundred words of somebody else's prose.

   IT IS A SUNKEN INSET, NOT A CARD. Sheet-sunken with a 2px ink rule down its
   left edge — the same 2px `--basel-rule-section` a field uses, at the same
   inset — so it reads as a quotation from outside the app rather than as
   something the app is offering to edit. No border on four sides: that is the
   box-inside-a-box §12.1 removes.

   THE TEXT SCROLLS RATHER THAN CLAMPING. The v2 card truncated at 260
   characters behind a "Show full text" toggle, which is a collapse inside a
   collapse (§12.9 rule 4) — the record is already the collapse level. A max
   height and an overflow keeps a forty-minute transcript from pushing the field
   grid off the screen while leaving every word reachable in place. */
.basel-verbatim {
    border-left: var(--basel-rule-section) solid var(--basel-ink);
    padding: 12px 16px;
    background-color: var(--basel-sheet-sunken);
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.basel-verbatim-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 8px 14px;
}
.basel-verbatim-note { color: var(--basel-ink-muted); margin: 0; }
.basel-verbatim-text {
    margin: 0;
    max-height: 280px;
    overflow-y: auto;
    white-space: pre-wrap;
    color: var(--basel-ink);
}
/* The DRAFT state's editor. Paper-white against the sunken block so the one
   thing on this screen you can still type into looks like it. */
.basel-verbatim-input { background-color: var(--basel-sheet); resize: vertical; }
/* Drag-over. A 2px ink border, not an accent fill: §12.2 has three grounds and
   no decorative colour, and §12.3 rations the one red to "your move". */
.basel-drop-active { border: var(--basel-rule-section) dashed var(--basel-ink); }

/* ---- Extraction, and the candidates it produces --------------------------
   Inside the verbatim block, because extraction is an operation ON the text
   above it and reads where that text is. Not a fourth declared slot: what a log
   puts inside its own block is that log's business, the same latitude `metaFn`
   has. */
.basel-extract {
    display: flex;
    flex-direction: column;
    gap: 10px;
    border-top: var(--basel-rule-row) solid var(--basel-hair-strong);
    padding-top: 10px;
}
.basel-extract-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.basel-cands { display: flex; flex-direction: column; gap: 12px; }

/* ONE SUGGESTED ITEM. A hairline-separated block on the ground it is already
   standing on — NEVER a bordered card. Two reasons, and both are §12.1:

   A four-sided border inside the verbatim block is a box inside a box, which is
   the thing Basel removes outright. And a raised surface would make a proposal
   read as a record that already exists, which is the one thing a candidate is
   not until the PM clicks Add. The hairline and the ink type block carry the
   separation instead. */
.basel-cand {
    display: flex;
    flex-direction: column;
    gap: 10px;
    padding: 12px 0 4px;
    border-top: var(--basel-rule-row) solid var(--basel-hair-strong);
}
.basel-cand-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; }
/* The log this would land in, as a solid ink block carrying paper text — the
   `ink`-as-fill role §12.3 names, and the only mass in the component. */
.basel-cand-type {
    padding: 2px 7px;
    background-color: var(--basel-ink);
    color: var(--basel-paper);
}
.basel-cand-scope { color: var(--basel-ink-muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-cand-actions { display: flex; align-items: center; justify-content: flex-end; gap: 10px; }

/* THE ITEM AN ANSWER LANDS ON (ADR-100). Every other candidate is a form of
   the record it would create; an answer changes one that already exists, so
   this row IS the card's subject — the standing it carries today, its id, and
   its own words read live off the record rather than copied at extraction
   time. The PM is confirming a match, not trusting an id.

   It takes the LOG ROW's own three-track shape (mark · id · words), because
   that is what this app has always meant by "a record, named" — and the mark
   is drawn through baselStandingMarkHtml() like every other one (§12.3). */
.basel-cand-answers {
    display: grid;
    grid-template-columns: 18px auto minmax(0, 1fr);
    gap: 10px;
    align-items: baseline;
    padding-bottom: 2px;
}
.basel-cand-answers .basel-row-mark { align-self: center; }
.basel-cand-asked { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--basel-ink); }

/* ---- Empty states -------------------------------------------------------- */

.basel-empty {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 8px;
    padding: 32px var(--basel-pad-row-x);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    background-color: var(--basel-sheet);
}
.basel-empty-title { color: var(--basel-ink); }
.basel-empty-sub   { color: var(--basel-ink-muted); }
.basel-empty-line  { margin: 0; padding: 9px 0; color: var(--basel-ink-muted); }

/* ==========================================================================
   BASEL SCHEDULE SCREEN (DESIGN §12.23, ADR-044; §12.11 and ADR-032 for the
   first pass) — Phase 4.8, second pass 2026-09-01
   --------------------------------------------------------------------------
   One screen with one graphic at the head. Everything above it in this file is
   reused rather than re-invented: .basel-screen, .basel-screen-head,
   .basel-toolbar, .basel-thead, .basel-group, .basel-linked, .basel-chip,
   .basel-empty. What is new here is the HERO (the active phase in big type,
   the ruler, the countdown, the next milestone, the causes), the STRIP (every
   phase as one numbered step), the LOG ROW (this phase's records by log — the
   readiness bar's anatomy turned to face a phase) and the two COLUMN SETS the
   milestone rows and the Mapping rows still need.

   WHAT WAS DELETED, NOT HIDDEN: the master phase list's grid and its tokens
   (.basel-sched-phase*, .basel-sched-add, --basel-sched-phase-cols), the
   active-phase card (.basel-sched-now*), the Milestones view's wrapper rule
   and its static group header (.basel-group--static), the ladder and its
   three tokens (.basel-ladder*). MS Project is the only source of a phase, a
   date, a duration, a percentage or a "done"; there is no control for any of
   them, so there is nothing to hide.

   THE TWO GRIDS ARE EACH DECLARED ONCE AND WORN TWICE — by the column header
   and by its rows — exactly as --basel-row-cols is on a log. That shared
   declaration is what replaces a <table>, and it is why the header and the rows
   cannot drift apart.

   THE MIN-WIDTH IS DELIBERATE. A milestone row carries eight columns of MS
   Project fact and there is no honest way to drop one: a start date without a
   finish date, or a promised date without the change against it, is worse than
   a horizontal scrollbar. So each grid states the width it needs and its own
   wrapper scrolls; the page body never does (DESIGN §1).

   NEVER RED FOR A DATE OR A FILE. A slipped date is not a standing (§12.12
   rule 1) and neither is a stale file: the head's age line goes to full ink at
   700 past a week and never to --basel-waiting-*. The only red on this screen
   is a standing mark on a record.
   ========================================================================== */

/* THE TWO COLUMN SETS, and the width each one needs before a name column
   stops being a name column. Measured in the live DOM at the two viewports this
   app is driven at: the Basel main column is ~1215px of content at 1600 and
   ~895px at 1280, and every figure below is the smaller of the two that still
   leaves the flexible name track above 150px. Below its min-width the SECTION
   scrolls, never the page (DESIGN §1). */
:root {
    /* name · start · finish · duration · % · promised · change · cause */
    /* gutter (the milestone diamond) · name · kind · start · finish · duration ·
       done · promised · change · cause. The gutter and the narrower cause are
       ADR-054: the diamond replaced an inline pill that cost the NAME ~90px, and
       the cause cell is a figure now instead of wrapping chips over a select, so
       140px buys nothing. KIND is its own column (ADR-055, second pass) rather
       than a flag on the name — appended, it read as part of the title and
       pushed every name a different distance right — and it sits AFTER the name,
       so the name keeps the left edge the eye looks along. 122px holds "Drawing
       development"; a longer word ellipsises and keeps it on its title. */
    --basel-sched-ms-cols: 18px minmax(0, 1fr) 122px 84px 84px 74px 54px 84px 58px 92px;
    --basel-sched-ms-min-w: 1050px;
    /* track as · shows as · strip · shown · your name · MSP name · lvl · used by.
       "Shows as" and "Strip" are ADR-055: the first is the client-facing word
       the app only ever prints, the second splits the old single Shown tick
       into the two questions it was answering at once. This screen is the PM's
       and already scrolls; a wider grid here costs nothing a client sees. */
    /* Your name · When · Track as · Shows as · Strip · Shown · Used by (ADR-144).
       Name first and flexible; the six to its right are fixed, so a nested row
       loses width from its name and never moves a control. 704px of fixed
       tracks and gaps leaves the name ~360px on a 1,104px screen, where the old
       eight columns needed 1,176px and scrolled sideways. */
    --basel-sched-map-cols: minmax(0, 1fr) 188px 116px 132px 44px 48px 104px;
    --basel-sched-map-min-w: 900px;
    /* The hero's name, three steps (§12.23): 64px when the shown name is
       18 characters or fewer, 48px to 34, 40px past that — never a third line
       and never smaller. The strip's step reserves two lines of its 12px
       uppercase name so a long name and a short one share a baseline. */
    --basel-sched-hero-name-64: 64px;
    --basel-sched-hero-name-48: 48px;
    --basel-sched-hero-name-40: 40px;
    --basel-sched-hero-figure:  64px;
    --basel-sched-step-name-h:  30px;
    /* The gap between the split graphic's segments and between its columns
       (ADR-142). ONE number, because scheduleSplitHtml() subtracts it in calc
       to place today's rule: a gap that changed here and not there would put
       the rule back off its date, which is the defect this token exists for. */
    --basel-sched-bar-gap:      3px;
}

/* ---- Sections ------------------------------------------------------------ */

.basel-sched-sec { padding-top: 28px; overflow-x: auto; }
.basel-sched-sec-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 8px 14px;
    padding-bottom: 8px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
}
.basel-sched-sec-title { color: var(--basel-ink); }
.basel-sched-sec-note  { color: var(--basel-ink-muted); }
.basel-sched-toolbar-note { color: var(--basel-ink-muted); }

/* ---- The screen head's age line ------------------------------------------ */

/* "imported N days ago · older than the weekly meeting": full ink at 700 past
   a week. Ink, never red — a stale file is nobody's move (§12.23). */
.basel-sched-age--stale { color: var(--basel-ink); font-weight: 700; }
.basel-sched-map-count { color: var(--basel-ink-muted); }

/* ---- The hero (§12.23) --------------------------------------------------- */

.basel-sched-hero {
    display: grid;
    grid-template-columns: minmax(0, 1.5fr) 1px minmax(0, 1fr);
    gap: 0 40px;
    align-items: start;
    margin-top: 18px;
    padding-top: 28px;
    border-top: var(--basel-rule-section) solid var(--basel-ink);
}
@media (max-width: 900px) {
    .basel-sched-hero { grid-template-columns: minmax(0, 1fr); }
    .basel-sched-hero-div { display: none; }
}
.basel-sched-hero-main {
    display: flex;
    flex-direction: column;
    gap: 6px;
    min-width: 0;
}
/* The eyebrow's middle word is the phase's standing word in that standing's
   ink; the rest is the muted eyebrow register. */
.basel-sched-hero-eyebrow { color: var(--basel-ink-muted); }
.basel-sched-hero-eyebrow .basel-standing { font: inherit; letter-spacing: inherit; }

/* THE NAME. Archivo 800, −.035em, balanced, clamped to two lines. Three sizes
   by the shown name's length — a class per size, never an inline font-size —
   and a name that still does not fit is what Mapping's "Your name" is for. */
.basel-sched-hero-name {
    font-family: Archivo, 'Helvetica Neue', Helvetica, Arial, sans-serif;
    font-weight: 800;
    letter-spacing: -0.035em;
    line-height: 1.02;
    color: var(--basel-ink);
    text-wrap: balance;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.basel-sched-hero-name--64 { font-size: var(--basel-sched-hero-name-64); line-height: 1; }
.basel-sched-hero-name--48 { font-size: var(--basel-sched-hero-name-48); }
.basel-sched-hero-name--40 { font-size: var(--basel-sched-hero-name-40); }

/* The dates line: mono at 400 in the muted ink; the promised clause's signed
   figure keeps .basel-sched-late's full ink at 700. */
.basel-sched-hero-dates { margin-top: 6px; color: var(--basel-ink-muted); font-weight: 400; }
.basel-sched-hero-nodates { margin-top: 6px; color: var(--basel-ink-muted); }
/* The ruler at the width of the column: its own row padding is pulled back
   out so the track's ends sit on the column's edges. */
.basel-sched-hero-ruler { margin: 22px calc(-1 * var(--basel-pad-row-x)) 0; }
.basel-sched-hero-ruler .basel-ruler { padding-top: 0; padding-bottom: 0; }

.basel-sched-hero-div { display: block; align-self: stretch; background-color: var(--basel-hair); }

.basel-sched-hero-side {
    display: flex;
    flex-direction: column;
    gap: 22px;
    min-width: 0;
}
.basel-sched-hero-block { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.basel-sched-hero-block + .basel-sched-hero-block {
    padding-top: 18px;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-sched-hero-block--causes { gap: 6px; }
.basel-sched-hero-k { color: var(--basel-ink-muted); }
/* The working-day figure: the mono at 64px/1, in ink. */
.basel-sched-hero-figure {
    font-size: var(--basel-sched-hero-figure);
    line-height: 1;
    letter-spacing: -0.03em;
    font-weight: 700;
    color: var(--basel-ink);
}
.basel-sched-hero-v { color: var(--basel-ink-muted); }
.basel-sched-hero-v .font-basel-mono { font-weight: 400; }
.basel-sched-hero-next { color: var(--basel-ink); }
/* The cause rows are the ruler's own rows, minus the drop-line: there is no
   slip block above them here to hang from. */
.basel-sched-hero-block--causes .basel-sched-hero-k + .basel-ruler-cause .basel-linked { border-top: 0; }

/* ---- The strip (§12.23) -------------------------------------------------- */

/* One equal step per master phase. Flex with a zero basis rather than a
   repeat() count: the number of phases is the file's, and this way there is no
   literal to keep in step with it. */
.basel-sched-strip {
    display: flex;
    gap: 3px;
    margin-top: 28px;
}
/* ---- The split graphic (ADR-141, amending ADR-092) ----------------------- */

/* ADR-092 made a STEP proportional — its share of the programme as an inline
   flex basis, floored at 6%. That is deleted. A step's words need 153px and a
   floored step offered 58px, so a short phase clipped on every screen there is;
   the name was sliced mid-glyph because a single word overflows a two-line clamp
   sideways instead of triggering its ellipsis. A box sized as a share of the
   SCREEN cannot hold text sized in PIXELS — so the two jobs are split. The bar
   above is duration and today and carries no words; the row below is the equal
   strip's own step at equal width, where nothing has ever clipped. */
.basel-sched-split { display: block; }

/* The bar's box OWNS the air its Today label sits in — padding, not the strip's
   margin, which would collapse out and take the label's coordinate space with it. */
.basel-sched-split-bar { position: relative; padding-top: 28px; }
/* THE GAPS COME OUT BEFORE THE SHARES DO (ADR-142). A segment's share is its
   flex-GROW over a zero basis, set inline, so the shares divide the width LEFT
   AFTER the gaps. 2.76.0 set the share as a fixed BASIS and the gaps sat on top:
   the bar ran (n - 1) x 3px past the row beneath it, 18px on seven phases. */
.basel-sched-bar { display: flex; gap: var(--basel-sched-bar-gap); height: 30px; }
/* NO min-width and no floor: a segment is exactly its share, so a two-week phase
   draws as a two-week phase. It carries nothing that can clip. */
.basel-sched-bar-seg {
    display: flex;
    align-items: center;
    justify-content: center;
    min-width: 0;
    overflow: hidden;
}
/* Ground by standing — the same three grounds the step takes, so the bar and the
   row under it read as one graphic. No red on either (§12.3). */
.basel-sched-bar-seg--done    { background-color: var(--basel-hair-strong); color: var(--basel-ink); }
.basel-sched-bar-seg--working { background-color: var(--basel-ink); color: var(--basel-paper); }
.basel-sched-bar-seg--queued  { background-color: var(--basel-sheet-sunken); color: var(--basel-ink-muted); }
.basel-sched-bar-n { font-weight: 400; }

/* The programme's two ends. A measured line says what it runs between. */
.basel-sched-split-axis {
    display: flex;
    justify-content: space-between;
    padding-top: 7px;
    color: var(--basel-ink-muted);
}

.basel-sched-splitcols { display: flex; gap: var(--basel-sched-bar-gap); margin-top: 14px; }
.basel-sched-splitcols .basel-sched-step { position: relative; }
/* THE PHASE WE ARE IN, marked on its own column. The Today rule cannot run down
   here: this row is measured in PHASES, not in time, so a rule at today's
   percentage lands in one phase's column while the project is in another, and it
   lands somewhere different and equally wrong every month. A mark on the working
   column cannot be off by one. It sits in the gap ABOVE the column, on paper —
   never inside the black step, where this red against full ink fails contrast.
   Second use of red on one graphic, and it is the same red saying the same word
   (ADR-092's carve-out from §12.3): NOW, not "your move". */
.basel-sched-splitcols--today .basel-sched-step--working::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: -8px;
    height: 2px;
    background-color: var(--basel-waiting-mark);
}

.basel-sched-today {
    position: absolute;
    top: 20px;
    height: 50px;
    width: 2px;
    background-color: var(--basel-waiting-mark);
}
/* The label sits ABOVE the strip, centred on its rule, and is the one place in
   the app where this red says "now" rather than "waiting on you" — which is why
   it is a word beside a rule and never a fill behind a step (§12.3). */
.basel-sched-today-l {
    position: absolute;
    top: 0;
    transform: translateX(-50%);
    white-space: nowrap;
    color: var(--basel-waiting-ink);
}
.basel-sched-step {
    flex: 1 1 0;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: 4px;
    padding: 8px 10px;
    border: 0;
    border-radius: var(--basel-radius);
    text-align: left;
    cursor: pointer;
    font: inherit;
}
.basel-sched-step:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }
/* Ground by standing. Done on the strong hairline in ink; the active phase in
   full ink with paper text; the rest on the sunken sheet, muted. No red. */
.basel-sched-step--done    { background-color: var(--basel-hair-strong); color: var(--basel-ink); }
.basel-sched-step--working { background-color: var(--basel-ink); color: var(--basel-paper); }
.basel-sched-step--queued  { background-color: var(--basel-sheet-sunken); color: var(--basel-ink-muted); }
.basel-sched-step-n { font-weight: 400; color: var(--basel-ink-muted); }
.basel-sched-step--working .basel-sched-step-n { color: var(--basel-paper); }
.basel-sched-step-name {
    font-size: 12px;
    line-height: 1.25;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    min-height: var(--basel-sched-step-name-h);
}
/* The window, in the mono at 400. The board drew it at 11px; §12.4's floor is
   13px for anything that is not an uppercase 700 eyebrow, and the conformance
   probe enforces it, so it takes text-basel-id's size and drops the weight. */
.basel-sched-step-dates {
    margin-top: auto;
    font-weight: 400;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* ---- The log row (§12.23) ------------------------------------------------ */

/* 200px lead and five equal cells, a hairline above and below — the readiness
   bar's grid (.basel-readiness-row), turned to face one phase. Five is a
   literal for the same reason it is there: a sixth log is one row in
   READINESS_LOGS and this one number. */
.basel-sched-logs {
    display: grid;
    grid-template-columns: 200px repeat(6, minmax(0, 1fr));
    margin-top: 28px;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    overflow-x: auto;
}
.basel-sched-logs-lead {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 2px;
    padding: 12px 0;
    min-width: 0;
}
.basel-sched-logs-lead .text-basel-note { color: var(--basel-ink-muted); }
.basel-sched-logs-cell {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 4px;
    min-width: 0;
    padding: 12px 16px;
    border: 0;
    border-left: var(--basel-rule-row) solid var(--basel-hair);
    border-radius: var(--basel-radius);
    background: none;
    color: var(--basel-ink-muted);
    text-align: left;
    font: inherit;
    cursor: pointer;
}
.basel-sched-logs-cell:hover { background-color: var(--basel-sheet-sunken); }
.basel-sched-logs-cell:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }
.basel-sched-logs-cell--nil { cursor: default; }
.basel-sched-logs-cell--nil:hover { background: none; }
/* The log's name is the PM's own from Tab 08 and a column must be NAMED
   (§12.8), so the eyebrow wraps rather than ellipsising — the readiness bar's
   own rule for its head (§12.13 finding 4). */
.basel-sched-logs-k { color: var(--basel-ink-muted); min-width: 0; }
.basel-sched-logs-fig { display: flex; align-items: center; gap: 8px; min-width: 0; }
/* The count: the mono in that standing's ink, at THE SAME SIZE the readiness
   bar's count takes on the five log tabs. It was 19px -- a one-off number that
   is not on §12.4's scale -- so the same fact, "N records at this standing",
   was drawn half again as large here as on every screen the PM compares it
   with. `text-basel-id`'s 13px/700 is what the readiness cell uses. */
.basel-sched-logs-count { font-size: 13px; line-height: 1.3; font-weight: 700; }
.basel-sched-logs-n { color: var(--basel-ink-muted); white-space: nowrap; }
/* "We looked, there is nothing." Decorative, and the one thing
   --basel-ink-faint is allowed to be (§12.2). Not a zero and not a standing. */
.basel-sched-logs-nil { color: var(--basel-ink-faint); }

/* ---- Shared row parts ---------------------------------------------------- */

.basel-sched-cell {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--basel-ink-muted);
}
/* Slippage is a NUMBER, not a standing. Late is full ink at 700 and early is
   the muted ink — the sign and the weight carry it, and the red triangle keeps
   meaning the one thing it means everywhere else in this app (§12.3). */
.basel-sched-late  { color: var(--basel-ink); font-weight: 700; }
.basel-sched-early { color: var(--basel-ink-muted); }

/* ---- The per-phase breakdown --------------------------------------------- */

/* THE PHASE ROW'S COLUMNS (§12.23 third pass, ADR-046). One grid, declared
   once and worn by the column header and every row: name · Start · the bar ·
   Finish · Weeks · Records. Below the min-width the breakdown scrolls
   sideways, as the milestone grid inside it already does (§12.11 finding 3). */
/* ADR-145: Start and Finish are gone — the dates ride on the bars — and the
   timeline takes two shares of the width to the name's one, so it is ~560px
   with the menu open and ~720px with it folded, against the 320px cap it had. */
:root {
    --basel-sched-prow-cols: minmax(200px, 1fr) minmax(360px, 2fr) 64px 110px;
    --basel-sched-prow-min-w: 900px;
}
#schedule-phase-breakdown { overflow-x: auto; }
.basel-sched-prow-head {
    grid-template-columns: var(--basel-sched-prow-cols);
    gap: 0 16px;
    align-items: end;
    min-width: var(--basel-sched-prow-min-w);
    padding: 0 var(--basel-pad-group-x) 6px;
}
.basel-group--phase {
    display: grid;
    grid-template-columns: var(--basel-sched-prow-cols);
    gap: 0 16px;
    min-width: var(--basel-sched-prow-min-w);
}
/* The active phase's row: the 3px --basel-rule-active inset on the sheet
   ground — what the side panel means by red, and what §12.11 gave the Active
   row of the list this replaced. It marks WHICH row, never a standing. */
.basel-group--phase.basel-group--active {
    box-shadow: inset var(--basel-rule-active) 0 0 var(--basel-waiting-mark);
    background-color: var(--basel-sheet);
}
.basel-sched-prow-name { display: flex; align-items: center; gap: 12px; min-width: 0; }
.basel-sched-prow-name .basel-group-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-sched-prow-weeks { color: var(--basel-ink-muted); font-weight: 400; white-space: nowrap; }
.basel-sched-prow-recs { display: flex; align-items: center; justify-content: flex-end; gap: 8px; min-width: 0; }
.basel-sched-prow-recs .basel-group-worst { margin-left: 0; }
/* THE TIMELINE (ADR-145). One cell per row on one shared axis. The bar says
   where the phase is: FINISHED is a solid bar in the muted ink (3.33:1 on a
   finished row's grey, 4.81:1 on the sunken row); UNDER WAY is a 1px ink box
   filling with ink up to today; STILL TO COME is an open box in the muted ink.
   It replaced a 2px line of --basel-hair-strong that was 1.44:1 on its row and
   1.00:1 — invisible — on a finished one, whose ground is that same grey.
   A bar is never narrower than 6px, so a four-day phase is still a box.
   Quarter lines and today run the full height of the row (the row's own
   vertical padding plus its 1px rule, so consecutive rows join into one line).
   Position is percent, never pixels (§12.12 rule 2); nothing here is red. */
.basel-sched-tl { position: relative; display: block; height: 18px; min-width: 0; }
.basel-sched-tl-grid,
.basel-sched-tl-today {
    position: absolute;
    top: calc(-1 * var(--basel-pad-group-y) - var(--basel-rule-row));
    bottom: calc(-1 * var(--basel-pad-group-y) - var(--basel-rule-row));
    width: var(--basel-rule-row);
    background-color: var(--basel-hair);
}
.basel-sched-tl-grid--year { background-color: var(--basel-hair-strong); }
.basel-sched-tl-today { background-color: var(--basel-ink); }
.basel-sched-tl-bar {
    position: absolute;
    top: 4px;
    height: 10px;
    min-width: 6px;
    box-sizing: border-box;
    overflow: hidden;
}
.basel-sched-tl-bar--done   { background-color: var(--basel-ink-muted); }
.basel-sched-tl-bar--active { background-color: var(--basel-sheet); border: var(--basel-rule-row) solid var(--basel-ink); }
.basel-sched-tl-bar--next   { background-color: var(--basel-sheet); border: var(--basel-rule-row) solid var(--basel-ink-muted); }
.basel-sched-tl-fill { position: absolute; left: 0; top: 0; bottom: 0; background-color: var(--basel-ink); }
/* A heading's span: a bracket, because a heading is MS Project's summary over
   several phases and not a phase — it has no state of its own to fill. */
.basel-sched-tl-span {
    position: absolute;
    top: 5px;
    height: 7px;
    border: var(--basel-rule-row) solid var(--basel-ink-muted);
    border-bottom: 0;
    box-sizing: border-box;
}
/* A phase that runs off the zoomed axis: a small arrow at the edge it runs off. */
.basel-sched-tl-arrow { position: absolute; top: 3px; width: 0; height: 0; border-top: 6px solid transparent; border-bottom: 6px solid transparent; }
.basel-sched-tl-arrow--l { left: 0; border-right: 7px solid var(--basel-ink-muted); }
.basel-sched-tl-arrow--r { right: 0; border-left: 7px solid var(--basel-ink-muted); }
/* THE DATES ON THE BAR. After the bar, before it, or on top of it — chosen by
   which third of the column the bar ends in, never by measuring the label. */
.basel-sched-tl-lab {
    position: absolute;
    top: 0;
    padding: 0 8px;
    line-height: 18px;
    color: var(--basel-ink);
    white-space: nowrap;
    background-color: var(--basel-sheet-sunken);
}
.basel-sched-tl-lab--before { transform: translateX(-100%); }
.basel-group--active .basel-sched-tl-lab { background-color: var(--basel-sheet); }
.basel-group-section--done > .basel-group--phase .basel-sched-tl-lab { background-color: var(--basel-hair-strong); }
.basel-sched-heading .basel-sched-tl-lab { background-color: var(--basel-paper); color: var(--basel-ink-muted); font-weight: 400; }
.basel-sched-tl-lab--in { margin-left: 4px; }
/* Off the zoomed axis: 12px clear of the arrow at that edge. */
.basel-sched-tl-lab--after.basel-sched-tl-lab--edge,
.basel-sched-tl-lab--in.basel-sched-tl-lab--edge { margin-left: 12px; }
.basel-sched-tl-lab--before.basel-sched-tl-lab--edge { margin-left: -12px; }
.basel-sched-tl-none { position: absolute; left: 0; top: 0; line-height: 18px; color: var(--basel-ink-muted); }
/* The column header: the axis's two ends and the zoom on one line, the month
   names under it on the same percentages as the lines they name. */
.basel-sched-tl-head { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.basel-sched-tl-head-top { display: flex; align-items: center; justify-content: space-between; gap: 12px; }
.basel-sched-tl-zoom .basel-seg-btn { padding: 2px 8px; }
.basel-sched-tl-months { position: relative; display: block; height: 15px; }
/* The size, tracking and weight are .text-basel-eyebrow's, on the element —
   the one register under 13px §12.4 permits. */
.basel-sched-tl-month {
    position: absolute;
    top: 0;
    padding-left: 3px;
    line-height: 15px;
    color: var(--basel-ink-muted);
    white-space: nowrap;
}
.basel-sched-tl-month--year { color: var(--basel-ink); }

/* THE HEADINGS (ADR-145): MS Project's summary rows over the phases. The phase
   row's grid on the paper ground, with no top rule except at the outermost
   level, so they read as the file's structure and not as more phases. */
.basel-sched-heading {
    display: grid;
    grid-template-columns: var(--basel-sched-prow-cols);
    gap: 0 16px;
    min-width: var(--basel-sched-prow-min-w);
    background-color: var(--basel-paper);
    border-top: 0;
}
.basel-sched-heading .basel-group-name { color: var(--basel-ink-muted); }
.basel-sched-heading--top { border-top: var(--basel-rule-row) solid var(--basel-ink); }
.basel-sched-heading-n { color: var(--basel-ink-muted); white-space: nowrap; }
/* DEPTH, as every grouped surface draws it: the shared nesting step, on the
   name only, so the timeline and every column to its right stay on one axis.
   Three levels, then it stops (ADR-058's bound). */
.basel-sched-depth-1 { padding-left: var(--basel-pad-nest-x); }
.basel-sched-depth-2 { padding-left: calc(2 * var(--basel-pad-nest-x)); }
.basel-sched-depth-3 { padding-left: calc(3 * var(--basel-pad-nest-x)); }
.basel-sched-phase-body { padding: 4px 0 18px; background-color: var(--basel-sheet); }
.basel-sched-sub { padding: 10px var(--basel-pad-row-x) 4px; }
.basel-sched-sub--records { padding-bottom: 0; }
.basel-subhead--sched { display: flex; align-items: baseline; gap: 12px; padding-bottom: 6px; }
.basel-subhead--sched .font-basel-mono { color: var(--basel-ink-muted); }
.basel-sched-sub-actions { padding: 6px var(--basel-pad-row-x) 0; }
.basel-sched-log-n { color: var(--basel-ink-muted); }

/* THE LOG CHIPS (§12.23, second pass). One line, five chips, one underlined in
   ink: number · label · count · worst. The line is the index — every log's
   count and worst standing is on it at once — so the rows beneath belong to
   exactly one log and nothing is behind a chevron. The chosen chip's rule is
   2px of ink on the line's own hairline, the same pair a section boundary
   draws over a row rule. */
.basel-sched-chips {
    display: flex;
    flex-wrap: nowrap;            /* ONE line. A long tab name shortens; the line never breaks. */
    gap: 4px;
    margin: 0 0 0 var(--basel-pad-row-x);   /* the subhead's own left edge, and the full width to the right */
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-sched-chip {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    flex: 0 1 auto;
    /* min-content, never 0: at 0 a chip shrank below its number + 8ch name +
       count + worst mark and those ran 16px into the next chip (ADR-158). */
    min-width: auto;
    padding: 8px 6px;
    border: 0;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    border-radius: var(--basel-radius);
    background: none;
    color: var(--basel-ink-muted);
    font: inherit;
    cursor: pointer;
}
/* A name may shorten, but never below eight characters: "ISSUES" and "ACTION
   ITEMS" hold while "DECISIONS & APPROVALS" gives way — the long label is
   the one with room to lose. */
.basel-sched-chip-name { min-width: 8ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-sched-chip > * { flex: 0 0 auto; }
.basel-sched-chip > .basel-sched-chip-name { flex: 0 1 auto; }
.basel-sched-chip:hover { color: var(--basel-ink); }
.basel-sched-chip:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }
.basel-sched-chip[data-active] { border-bottom-color: var(--basel-ink); color: var(--basel-ink); }
.basel-sched-chip[data-active] .basel-sched-log-n,
.basel-sched-chip[data-active] .basel-sched-chip-name { color: var(--basel-ink); }
.basel-sched-chip-count { color: var(--basel-ink-muted); }
.basel-sched-chip-worst { display: inline-flex; align-items: center; gap: 5px; }

/* The rows of the chosen log, on a rail: a 1px strong hairline hangs down the
   left of the list, one indent in from the chips, and the rows sit one indent
   further. Depth by indent and by line, no second header. */
.basel-sched-rowlist {
    position: relative;
    padding: 0 var(--basel-pad-row-x) 0 calc(var(--basel-pad-row-x) * 3 + 12px);
    background-color: var(--basel-sheet);
}
.basel-sched-rowlist::before {
    content: "";
    position: absolute;
    left: calc(var(--basel-pad-row-x) * 2 + 5px);
    top: 0;
    bottom: 0;
    width: 1px;
    background-color: var(--basel-hair-strong);
}
.basel-sched-rowlist-empty { padding-left: calc(var(--basel-pad-row-x) * 3 + 12px); }
.basel-sched-showing { padding: 8px var(--basel-pad-row-x) 4px calc(var(--basel-pad-row-x) * 3 + 12px); color: var(--basel-ink-muted); }
.basel-sched-showing strong { color: var(--basel-ink); font-weight: 700; }

/* THE ROW: the linked row minus its log column (the chip says it), plus the
   three dots. The dots are a sibling of the row button, never a child. */
.basel-sched-rec { display: flex; align-items: center; gap: 12px; }
.basel-sched-rec .basel-linked {
    flex: 1 1 auto;
    min-width: 0;
    grid-template-columns: 10px 76px minmax(0, 1fr) auto;
    gap: 12px;
    padding: 8px 0;
}
.basel-sched-rec-acts { position: relative; flex: 0 0 auto; }

/* THE THREE DOTS AND THE ROW MENU (ADR-045). The dots are the faint ink —
   the non-text ink, which is what it is for — and go to full ink on hover,
   focus and while their menu is open. The menu is the Manage menu's anatomy:
   paper, one strong hairline, radius 0, items at 6/12. It opens under the
   dots, right-aligned, over the rows beneath it. */
.basel-kebab {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: 0;
    border-radius: var(--basel-radius);
    background: none;
    color: var(--basel-ink-faint);
    cursor: pointer;
}
.basel-kebab:hover, .basel-kebab:focus-visible, .basel-kebab[aria-expanded="true"] { color: var(--basel-ink); }
.basel-kebab:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 0; }
/* THE POSITION HERE IS A FALLBACK ONLY (ADR-089). baselPlaceRowMenus()
   (core/basel-shell.js) switches every open menu to `position: fixed` against
   its own button's rect, because absolute placement resolves inside whichever
   box the screen puts its list in — and three of those boxes scroll, which is
   what grew a scrollbar on the roster and cut the master lists' menus in half.
   Fixed is resolved against the viewport, so no ancestor can clip it and no
   ancestor counts it as content. Do not add a transform, filter or `contain`
   to any ancestor of a row menu: that would make it the containing block for
   `fixed` and put the defect back. */
.basel-menu {
    position: absolute;
    right: 0;
    top: calc(100% + 4px);
    z-index: 15;   /* under the band, over the legend — ADR-158 */
    width: 232px;
    padding: 4px 0;
    background-color: var(--basel-paper);
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
}
.basel-menu-head { display: flex; align-items: center; gap: 8px; padding: 6px 12px 4px; color: var(--basel-ink-muted); }
.basel-menu-head .font-basel-mono { color: var(--basel-ink); }
.basel-menu-item {
    display: block;
    width: 100%;
    padding: 6px 12px;
    border: 0;
    border-radius: var(--basel-radius);
    background: none;
    color: var(--basel-ink);
    text-align: left;
    font: inherit;
    cursor: pointer;
    white-space: nowrap;
}
.basel-menu-item:hover, .basel-menu-item:focus-visible { background-color: var(--basel-sheet-sunken); outline: 0; }
.basel-menu-rule { height: var(--basel-rule-row); margin: 4px 0; background-color: var(--basel-hair); }
/* A menu that can list dozens of rows ("+ Link a Delay…") scrolls inside
   itself rather than running off a screen it is fixed to. */
.basel-menu--scroll { max-height: 320px; overflow-y: auto; }

/* AN ACTION MENU (ADR-158): a button that opens the row menu's anatomy. It
   replaced five <select>s that ran their command on an arrow key. The button
   wears the toolbar select's clothes; its menu hangs from its left edge. */
.basel-actmenu { position: relative; display: inline-block; max-width: 100%; }
.basel-actmenu-btn { text-align: left; max-width: 100%; }
.basel-actmenu-btn[aria-expanded="true"] { border-color: var(--basel-ink); color: var(--basel-ink); }
.basel-actmenu--promote .basel-actmenu-btn { min-width: 150px; }
/* In a grid cell (Mapping's Set all) the menu's host is the cell. */
.basel-actmenu--cell { display: block; min-width: 0; }

/* ---- Milestone rows ------------------------------------------------------ */

.basel-sched-ms {
    display: grid;
    grid-template-columns: var(--basel-sched-ms-cols);
    gap: 12px;
    align-items: center;
    min-width: var(--basel-sched-ms-min-w);
}
.basel-sched-ms:not(.basel-thead) {
    padding: var(--basel-pad-row-y) var(--basel-pad-row-x);
    background-color: var(--basel-sheet);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
/* The header inside a phase body takes THE ROW'S OWN horizontal padding, so a
   column heading sits over its column. It used to zero both, on the reasoning
   that .basel-sched-sub had already paid the row's 18px -- but the row pays it
   AGAIN (.basel-sched-ms:not(.basel-thead) below), so the header's content box
   was 36px wider than every row's. The flexible name column absorbed the
   difference and pushed all seven fixed columns 18px out of line with their
   headings. Measured: header cells at 884/980/1076..., rows at 866/962/1058...
   The 2px ink rule is unaffected either way -- a border-bottom spans the
   element box, which padding does not shrink. */
.basel-sched-sub .basel-sched-ms.basel-thead {
    padding-left: var(--basel-pad-row-x);
    padding-right: var(--basel-pad-row-x);
}
.basel-sched-ms-name {
    min-width: 0;
    display: flex;
    align-items: center;
    gap: 8px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    color: var(--basel-ink);
}
/* "Past · not in the file": finish is before today and the file does not say
   done. A note for the scheduler, not a standing — no mark, no colour, and
   the faint ink because it is the one thing on the row a client need not
   read (§12.23). */
.basel-sched-past { color: var(--basel-ink-muted); white-space: nowrap; }
/* THE NAME KEEPS THE LEFT EDGE (ADR-145): it gives way before the notes after
   it do, so a long name ellipsises and "Past · not in the file" stays readable. */
.basel-sched-ms-t { flex: 0 1 auto; min-width: 6em; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-sched-ms-name > .basel-sched-past,
.basel-sched-ms-name > .basel-flag { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
/* A ROW INSIDE A SERIES: one nesting step in, on the name only, on paper, so
   the eight meetings read as the series line's own list and not as more rows
   of the phase. */
.basel-sched-ms.basel-sched-ms--nested { background-color: var(--basel-paper); }
.basel-sched-ms.basel-sched-ms--nested.basel-sched-ms--done { background-color: var(--basel-hair-strong); }
.basel-sched-ms--nested .basel-sched-ms-name { padding-left: var(--basel-pad-nest-x); }
/* THE SERIES LINE: the gutter holds its fold, the name cell two lines — the
   summary row's name, and what is under it. Both are buttons that fold it. */
.basel-sched-series-chev {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    border: 0;
    background: none;
    color: var(--basel-ink-muted);
    cursor: pointer;
}
.basel-sched-series-chev[aria-expanded="false"] .basel-group-chev svg { transform: rotate(-90deg); }
.basel-sched-series-chev:focus-visible,
.basel-sched-series-name:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }
.basel-sched-series-name {
    flex-direction: column;
    align-items: flex-start;
    gap: 1px;
    padding: 0;
    border: 0;
    background: none;
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.basel-sched-series-t { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-sched-ms .basel-sched-series-t { font-weight: 600; }
/* The sub-line WRAPS rather than cut: its last words are "N past, not updated
   in the file", and a fold that clipped them would hide the one thing it owes
   the reader (ADR-056). */
.basel-sched-series-sub { max-width: 100%; color: var(--basel-ink-muted); white-space: normal; }
.basel-sched-series-sub strong { color: var(--basel-ink); font-weight: 600; white-space: nowrap; }
.basel-sched-sub-note { color: var(--basel-ink-muted); }
/* Chips and the picker share one cell and must not force the row taller. */
/* THE CAUSE CELL (ADR-054). A figure and a word, on ONE line, never wrapping:
   it held a chip per linked Delay above a select, so a row with two causes was
   three lines tall. `nowrap` is load-bearing — the whole point is that this
   cell can no longer change a row's height. */
.basel-sched-cause {
    display: inline-flex;
    align-items: baseline;
    gap: 6px;
    min-width: 0;
    padding: 0;
    border: 0;
    background: none;
    color: var(--basel-ink-muted);
    font: inherit;
    white-space: nowrap;
    cursor: pointer;
}
.basel-sched-cause-n { color: var(--basel-ink); }
.basel-sched-cause-w { text-decoration: underline; text-underline-offset: 3px; text-decoration-color: var(--basel-hair-strong); }
.basel-sched-cause:hover { color: var(--basel-ink); }
.basel-sched-cause:hover .basel-sched-cause-w { text-decoration-color: var(--basel-ink); }
.basel-sched-cause:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }

/* One linked Delay inside the dialog: id · what it says · Unlink. */
.basel-sched-cause-row {
    display: grid;
    grid-template-columns: 88px minmax(0, 1fr) auto;
    gap: 12px;
    align-items: center;
    padding: 9px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-sched-cause-desc { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-sched-cause-add { padding-top: 14px; }
.basel-sched-cause-add .basel-select { width: 100%; }

/* THE MILESTONE DIAMOND (ADR-054) and the empty gutter a task takes instead,
   which is what keeps every row's first column the same width. A rotated
   square, not a border trick: it has to stay square at any zoom. It is
   deliberately NOT one of §12.3's four standing shapes and never shares a
   cell with one — the gutter is the separation. */
/* The kind cell. Empty on a row the PM has not labelled, and the flag inside
   ellipsises rather than wrapping — nothing in this row may change its height. */
.basel-sched-kind { display: flex; align-items: center; min-width: 0; overflow: hidden; }
.basel-sched-kind .basel-flag {
    max-width: 100%;
    margin-right: 0;
    /* .basel-flag is an inline-block sitting 1px off its baseline, which on a
       text line adds 2px to the row. In a flex cell it is a flex item and the
       baseline nudge does nothing, so a labelled row is exactly as tall as an
       unlabelled one. Measured: 47px vs 45px before this. */
    vertical-align: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* FINISHED IS A GROUND (ADR-056). --basel-hair-strong is exactly what the strip
   above these rows already gives a finished step, so the rows say "behind us"
   the way the screen already says it rather than in a second vocabulary. The
   TEXT keeps its ink: nothing a client reads gets harder to read, which is what
   ruled out greying the row. A finished phase group takes the same ground on
   its header, through phaseStandingOf() — never a fifth reading of the dates. */
/* Both classes on purpose: .basel-sched-ms:not(.basel-thead) already paints the
   row --basel-sheet, and that selector is two classes deep, so a single-class
   rule here loses the cascade and the row stays white. Measured before this. */
.basel-sched-ms.basel-sched-ms--done { background-color: var(--basel-hair-strong); }
/* The Kind box keeps its outline on a finished row (ADR-145): its hairline is
   --basel-hair-strong, which is exactly the finished ground, so the box used to
   vanish and leave a word floating. */
.basel-sched-ms--done .basel-sched-kind .basel-flag { border-color: var(--basel-ink-muted); }
.basel-group-section--done > .basel-group--phase { background-color: var(--basel-hair-strong); }

.basel-sched-gutter { display: block; width: 100%; height: 1px; }
.basel-sched-diamond {
    display: block;
    width: 9px;
    height: 9px;
    margin: 0 auto;
    background-color: var(--basel-ink);
    transform: rotate(45deg);
}

/* ---- Mapping ------------------------------------------------------------- */

#schedule-mapping-view { overflow-x: auto; }
.basel-sched-map {
    display: grid;
    grid-template-columns: var(--basel-sched-map-cols);
    gap: 12px;
    align-items: center;
    min-width: var(--basel-sched-map-min-w);
}
.basel-sched-map:not(.basel-thead) {
    padding: var(--basel-pad-row-y) var(--basel-pad-row-x);
    background-color: var(--basel-sheet);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
/* ---- Mapping: one section per phase (ADR-144, amending ADR-061) -----------
   THE NAME CELL LEADS: a fold (or an empty gutter), the row's mark, and the
   name as TEXT — a text box with no box until you point at it — over one quiet
   line: the MS Project name when you renamed the row, how many rows a summary
   holds, or what a grouping row groups. Only the name column indents, so every
   control stays under the heading that names it at every depth; ADR-061 moved
   the WHOLE row 32px a level and put the Shown tick 96px right of its heading
   by the time it reached Feasibility's rows. */
.basel-sched-map-name { display: flex; align-items: center; gap: 10px; min-width: 0; padding-left: var(--basel-pad-nest-x); }
.basel-sched-map--grp .basel-sched-map-name { padding-left: 0; }
.basel-sched-map--nested .basel-sched-map-name { padding-left: calc(2 * var(--basel-pad-nest-x)); }
.basel-sched-map.basel-sched-map--nested { background-color: var(--basel-paper); }
.basel-sched-map-gutter { flex: 0 0 14px; width: 14px; }
.basel-sched-map-fold {
    flex: 0 0 14px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 14px;
    padding: 0;
    border: 0;
    background: none;
    color: var(--basel-ink-muted);
    cursor: pointer;
}
.basel-sched-map-fold[aria-expanded="false"] .basel-group-chev svg { transform: rotate(-90deg); }
.basel-sched-map-fold:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }
.basel-sched-map-names { display: flex; flex-direction: column; min-width: 0; flex: 1 1 auto; }
.basel-sched-map-input {
    width: 100%;
    min-width: 0;
    margin-left: -7px;
    padding: 2px 6px;
    border: var(--basel-rule-row) solid transparent;
    border-radius: var(--basel-radius);
    background-color: transparent;
    color: var(--basel-ink);
    font-family: inherit;
    font-weight: 600;
    text-overflow: ellipsis;
}
.basel-sched-map-input:hover { border-color: var(--basel-hair-strong); background-color: var(--basel-sheet); }
.basel-sched-map-input:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; background-color: var(--basel-sheet); }
/* Two classes deep on purpose: tailwind.css loads AFTER this file, so a lone
   class loses its weight to .text-basel-body's 400 (the known app-wide trap). */
.basel-sched-map .basel-sched-map-input { font-weight: 600; }
.basel-sched-map .basel-sched-map-input--off { color: var(--basel-ink-muted); font-weight: 400; }
.basel-sched-map .basel-sched-map-input--head { font-weight: 700; }
.basel-sched-map .basel-sched-map-plain { font-weight: 600; }
.basel-sched-map-subline { display: flex; align-items: center; gap: 8px; min-width: 0; }
.basel-sched-map-subline .basel-flag { margin-right: 0; }
.basel-sched-map-sub--msp { flex: 0 0 auto; max-width: 55%; }
.basel-sched-map-sub { min-width: 0; font-size: 13px; line-height: 1.4; color: var(--basel-ink-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.basel-sched-map-title { color: var(--basel-ink-muted); }
.basel-sched-map-step { flex: 0 0 auto; color: var(--basel-waiting-ink); }
.basel-sched-map-when { color: var(--basel-ink-muted); font-weight: 400; white-space: nowrap; }
/* THE ROW'S MARK, the Schedule's own shapes: a diamond is a milestone, a
   square a phase, a bar a task, a hairline a row not tracked. A row that is
   not on the Schedule draws its mark in the faint ink — a shape, never text. */
.basel-sched-map-mark { flex: 0 0 14px; width: 14px; height: 14px; display: inline-flex; align-items: center; justify-content: center; }
.basel-sched-map-mark::after { content: ""; display: block; background-color: var(--basel-ink); }
.basel-sched-map-mark--milestone::after { width: 9px; height: 9px; transform: rotate(45deg); }
.basel-sched-map-mark--phase::after { width: 10px; height: 10px; }
.basel-sched-map-mark--task::after { width: 12px; height: 4px; background-color: var(--basel-ink-muted); }
.basel-sched-map-mark--ignore::after { width: 10px; height: 1px; background-color: var(--basel-ink-faint); }
.basel-sched-map-mark--off::after { background-color: var(--basel-ink-faint); }
/* THE DROPDOWNS ARE BASEL'S (ADR-144). They were the browser's own widget with
   its grey rounded arrow; the caret is drawn exactly as the toolbar's
   .basel-ctl--select draws it, because Basel loads no icon font. The ticks
   stay .basel-check: "one checkbox appearance for the whole layer". */
.basel-sched-map .basel-select,
.basel-sched-setall {
    appearance: none;
    -webkit-appearance: none;
    padding-right: 24px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    background-image: var(--basel-select-caret);
    background-position: calc(100% - 13px) 50%, calc(100% - 9px) 50%;
    background-size: 4px 4px, 4px 4px;
    background-repeat: no-repeat;
}
/* THE SECTION HEADER IS THE PHASE'S OWN ROW, drawn once, on the sunken ground
   and the same grid; the section's rows sit under it on the sheet. */
.basel-sched-map--grp {
    background-color: var(--basel-sheet-sunken);
    border-top: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    padding: var(--basel-pad-group-y) var(--basel-pad-group-x);
}
.basel-sched-map.basel-sched-map--grp { background-color: var(--basel-sheet-sunken); }
.basel-sched-map--above .basel-sched-map-names { gap: 2px; }
/* The Set all line: a quiet row inside an open section, on paper. */
.basel-sched-map.basel-sched-map--set { padding-top: 6px; padding-bottom: 6px; background-color: var(--basel-paper); }
.basel-sched-map-setlabel { padding-left: var(--basel-pad-nest-x); color: var(--basel-ink-muted); white-space: nowrap; }
.basel-sched-map-sec { display: block; }
.basel-sched-map-empty { padding: 12px var(--basel-pad-row-x); margin: 0; background-color: var(--basel-sheet); }
/* The toolbar's Phase popover: step · name · rows, one line each. */
.basel-sched-map-phase .basel-multi-panel { min-width: 380px; max-height: 60vh; overflow-y: auto; }
.basel-sched-map-phase .basel-multi-item { display: grid; grid-template-columns: 24px minmax(0, 1fr) auto; gap: 8px; align-items: baseline; text-align: left; border: 0; background: none; font: inherit; }
.basel-sched-map-phase .basel-multi-item[data-active] { color: var(--basel-ink); font-weight: 700; }
.basel-sched-map-phase-t { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-sched-toolbar-note--line { flex-basis: 100%; }

/* THE SET-ALL CONTROLS. A dashed hairline and no value: these are a verb
   sitting in a column, not a setting with a current state, and a solid box
   would read as though the group already had one. */
.basel-sched-setall {
    width: 100%;
    min-width: 0;
    padding: 4px 7px;
    border: var(--basel-rule-row) dashed var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: transparent;
    color: var(--basel-ink-muted);
    font-family: inherit;
    cursor: pointer;
}
.basel-sched-setall:hover { border-color: var(--basel-ink); color: var(--basel-ink); }
.basel-sched-setall.basel-actmenu-btn { display: block; text-align: left; }

/* THE GROUP TICK, three states. A button rather than a checkbox: the third
   state is `indeterminate`, a DOM property no attribute can set, and a control
   whose rendered state depends on a script having run draws wrong for one frame
   on every re-render. */
.basel-sched-gcheck {
    width: 14px;
    height: 14px;
    padding: 0;
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet);
    cursor: pointer;
}
.basel-sched-gcheck[data-state="on"]    { background-color: var(--basel-ink); border-color: var(--basel-ink); color: var(--basel-paper); }
.basel-sched-gcheck[data-state="mixed"] { border-color: var(--basel-ink); }
.basel-sched-gcheck[data-state="mixed"]::after {
    content: "";
    width: 8px;
    height: 2px;
    background-color: var(--basel-ink);
}
.basel-sched-gcheck:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }

/* THE MILESTONE MARKS ON A STEP (ADR-061). A diamond and a short name inside
   the step whose window contains the date — the same shape §12.25 gives a
   milestone on the schedule, at the size the step can carry. It sits between
   the name and the window, and it inherits the step's ink, so a mark on the
   working step is paper on ink like everything else in that step. */
.basel-sched-step-marks { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; }
.basel-sched-step-mark {
    display: flex;
    align-items: center;
    gap: 4px;
    min-width: 0;
    font-size: 11px;
    line-height: 1.2;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}
.basel-sched-step-mark > span {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.basel-sched-step-mark svg { flex-shrink: 0; }


/* ==========================================================================
   THE SLIP RULER — DESIGN §12.12
   --------------------------------------------------------------------------
   ONE MEASURED LINE, DRAWN TWICE: at the head of the hero for the active phase
   (without its cause list — the causes go to the hero's right column) and
   inside every expanded phase in the breakdown (with it). Both are built by
   scheduleRulerHtml() in js/modules/schedule-view.js §4. The LADDER — every
   phase on one shared axis above the toolbar — was the masthead variant until
   the second pass (§12.23) and is deleted with its three tokens: the hero and
   the strip are the head now.

   EVERYTHING IS A HAIRLINE EXCEPT THE SLIP. The track is 4px of --basel-hair,
   the elapsed fill 4px of --basel-ink, every tick 1px — and the slip block is
   20px of SOLID ink carrying its own figure in --basel-paper. That is the one
   filled mass on the line and it is the whole point of the component: the eye
   finds the slip before it reads a date.

   SLIP IS NEVER RED (§12.3, and the same decision §12.11 finding 1 made for
   the Change column). A date that moved answers nobody's "whose move is it?",
   so it takes no standing and no triangle. Late is full ink at 700, early is
   the muted ink at 400 — the identical register .basel-sched-late /
   .basel-sched-early already carry. The only red this component can produce is
   a standing mark on a cause record, where it is that Delay's own standing.

   GEOMETRY IS PERCENT, NOT PIXELS. Every left/width on these elements comes
   from the axis span as an inline percentage, so the ruler is fluid and needs
   no measured width. The one pixel floor below (--basel-ruler-slip-min-w) is
   the only place a pixel is stated, and it is there because a percentage
   cannot express "must not vanish".
   ========================================================================== */

:root {
    /* A one-day slip on a year-long axis rounds to 0.27% and would disappear.
       The floor is on the WIDTH only, never on the left edge, so the block
       still starts on the promised date. */
    --basel-ruler-slip-min-w: 6px;
    --basel-ruler-line-h:     26px;
    /* How far tier 2 hangs below tier 1 (ADR-141). One number: the label's drop,
       the mark's extra length and the body's reserved margin all derive from it.
       It is the LABEL'S OWN HEIGHT plus air, measured: 11px eyebrow (14.29) + 2px
       gap + 13px mono (16.9) = 33.19px. A drop of 32 left the two tiers touching
       by a pixel wherever they were horizontally coincident — which is exactly the
       case this component exists to survive. Never set it below the label. */
    --basel-ruler-tier-drop:  40px;
}

/* ---- The phase ruler ------------------------------------------------------ */

.basel-ruler { padding: 14px var(--basel-pad-row-x) 4px; }

/* The labels hang out of the bottom of the line into this margin; the line
   itself is only the track's own height, so nothing below it has to know how
   tall a label stack happens to be. */
/* The reserved margin holds BOTH tiers (ADR-141), on every ruler whether or not
   this phase happens to be crowded. A height that varied with the dates would
   make the lists below jump as the weeks pass, which is worse than 32px. */
.basel-ruler-body {
    position: relative;
    height: var(--basel-ruler-line-h);
    margin-bottom: calc(46px + var(--basel-ruler-tier-drop));
}

.basel-ruler-track {
    position: absolute;
    left: 0;
    right: 0;
    top: 11px;
    height: 4px;
    background-color: var(--basel-hair);
}
.basel-ruler-fill {
    position: absolute;
    left: 0;
    top: 11px;
    height: 4px;
    background-color: var(--basel-ink);
}
/* A shown deliverable's finish, below the track. Ticks only — the list under
   the ruler already names every one of them, and a second set of labels here
   would be the same sentence twice. */
.basel-ruler-ms {
    position: absolute;
    top: 17px;
    width: 1px;
    height: 7px;
    background-color: var(--basel-ink-muted);
}

.basel-ruler-tick {
    position: absolute;
    top: 0;
    width: 0;
}
.basel-ruler-tick-mark {
    position: absolute;
    left: 0;
    top: 0;
    width: 1px;
    height: var(--basel-ruler-line-h);
    background-color: var(--basel-ink-muted);
}
.basel-ruler-tick-lab {
    position: absolute;
    left: 0;
    top: 30px;
    display: flex;
    flex-direction: column;
    gap: 2px;
    white-space: nowrap;
    transform: translateX(-50%);
}
/* Centred everywhere except the two ends, where half the label would hang off
   the ruler. Tier 2 also takes these two to push the moving pair apart. */
.basel-ruler-tick--head .basel-ruler-tick-lab { transform: translateX(0); }
.basel-ruler-tick--tail .basel-ruler-tick-lab { transform: translateX(-100%); }

/* TIER 2 — the moving dates (ADR-141). Today and Promised hang one drop below
   Start and Now, and the hairline grows by the same amount so it reaches the
   label it belongs to. Which tier a tick takes is decided by which DATE it is,
   in scheduleRulerTickHtml(); nothing here measures anything. */
.basel-ruler-tick--t2 .basel-ruler-tick-mark {
    height: calc(var(--basel-ruler-line-h) + var(--basel-ruler-tier-drop));
}
.basel-ruler-tick--t2 .basel-ruler-tick-lab {
    top: calc(30px + var(--basel-ruler-tier-drop));
}
.basel-ruler-tick-k { color: var(--basel-ink-muted); }
.basel-ruler-tick-v { color: var(--basel-ink); }

/* THE SLIP BLOCK. The only filled mass on the line. --basel-ink as a solid
   fill under --basel-paper text is the one direction §12.3 allows for an
   `ink`, and both directions of that pair were measured for AA. */
.basel-ruler-slip {
    position: absolute;
    top: 3px;
    height: 20px;
    min-width: var(--basel-ruler-slip-min-w);
    display: flex;
    align-items: center;
    justify-content: center;
    overflow: hidden;
}
.basel-ruler-slip--late  { background-color: var(--basel-ink); }
/* Finishing early is not an alarm and must not be dressed as one: the muted
   fill and weight 400 are .basel-sched-early's register, as mass instead of
   as text. */
.basel-ruler-slip--early { background-color: var(--basel-ink-muted); }
.basel-ruler-slip-fig {
    padding: 0 6px;
    color: var(--basel-paper);
    white-space: nowrap;
}
.basel-ruler-slip--early .basel-ruler-slip-fig { font-weight: 400; }

/* ---- The cause list (the ruler with a populated "why") -------------------- */

.basel-ruler-causes {
    position: relative;
    padding-top: 22px;
}
/* Hangs at the slip block's own centre percentage, so it reads as coming off
   the block. It starts BELOW the date labels rather than at the block's lower
   edge: a rule drawn through "PROMISED / 2026-09-04" crosses the one thing on
   the line the client is being asked to read. */
.basel-ruler-drop {
    position: absolute;
    top: 0;
    width: 1px;
    height: 22px;
    background-color: var(--basel-hair-strong);
}
.basel-ruler-cause-head {
    color: var(--basel-ink-muted);
    padding-bottom: 4px;
}
.basel-ruler-cause { display: flex; align-items: center; gap: 10px; }
.basel-ruler-cause .basel-linked { flex: 1 1 auto; min-width: 0; }


/* ==========================================================================
   BASEL READINESS + AGENDA (DESIGN §12.13, ADR-031) — Phase 4.10
   --------------------------------------------------------------------------
   Two components, and they ship together because the first had nowhere else
   to live (the full grid it once stood beside, `.basel-matrix`, went in
   2.30.0 — item 00 of the Agenda IS the bar now, ADR-081):

     .basel-readiness  the same grid above each of the six log lists AND item
                     00 of the Agenda, shut to one row: the worst standing per
                     log across every building. Open, the building rows drop
                     in. The ambient handle.
     .basel-meet /
     .basel-agenda-* the meeting itself: identity, the roll call folded to a
                     line, the items with the cursor on one of them (§12.33),
                     and the archive.

   They replace #persistent-matrix-panel, a collapsed grid of seven v2 cards
   mounted above every log screen and defaulted shut because it cost its own
   height on all five of them. BaselReadinessHome.dc.html rejected that
   placement outright and recommended these two instead.

   100% BASEL: every value comes from the --basel-* mirror, radius is 0, there
   are no shadows, and hierarchy is carried by 2px/1px rules and by type.
   Nothing here keys off a mode: there is none (Phase 4.12), the scale is flat
   (§12.4), and a control this screen does not offer is NOT BUILT.
   ========================================================================== */

/* ---- The readiness bar ----------------------------------------------------
   THE MATRIX, SHUT TO ONE ROW: a 210px name column, the six log columns, one
   cell summariser. Shut
   and open are the SAME DRAWING with a different number of rows, which is the
   whole reason this shape survived where a collapsible panel did not — nothing
   resizes, nothing re-flows, the columns do not move (DESIGN §12.13, ADR-035).

   WHAT WAS HERE: .basel-pulse, one chip per building on a nowrap row whose
   items were `flex: 1 0 132px` with `min-width: 132px`. They could grow and
   could not shrink, so past eight buildings the surplus went behind
   `overflow-x: auto` rather than compressing. Measured in the live DOM: at a
   1600px window all eight fitted with all eight NAMES ellipsised; at 1440px
   two buildings sat off the end; at 1280px, three. The comment above that rule
   claimed `flex: 1 1 0` and said a pulse you have to scroll is not a pulse —
   the code did not do what the comment said, and a building off the end took
   its red triangle with it. THIS GRID CANNOT DO THAT: the log count is fixed
   at five, so the columns share what there is instead of holding a floor.

   Measured in the live DOM at eight buildings, against 50px for the strip:
   77px shut and 323px open at a 1600px window, 92px shut at 1440px where the
   longer log names wrap their column label to two lines. The 27px it costs
   shut buys the log axis the strip never had, and the wrap is deliberate: a
   column must be named in full (§12.8) and these names are the PM's own. */

.basel-readiness {
    padding: 10px 0;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    overflow-x: auto;
}
/* Six is a literal because CSS repeat() cannot take a custom property as its
   count. A seventh log is one row in READINESS_LOGS and this one number. */
.basel-readiness-row {
    display: grid;
    grid-template-columns: 210px repeat(6, minmax(96px, 1fr));
    gap: 0 12px;
    /* STRETCH, NOT CENTRE, AND IT IS LOAD-BEARING. A populated cell is a button
       whose line box is the 13px mono figure; an empty one is a span carrying a
       middot at the note size. Centred, two different heights give two different
       tops and the totals row stops reading as one line. Stretched, every cell
       is the row's height and each centres its own content. Invisible while
       every log had records, which is why it surfaced only when Tab 09 added a
       sixth column that is empty on a new project (tools/smoke-test.js measures
       it as shutCellRows). The head labels opt back out below. */
    align-items: stretch;
    min-width: 798px;
}
.basel-readiness-row + .basel-readiness-row {
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
/* The head is the eyebrow row AND the control. A 1px hairline under it, not the
   2px ink rule .basel-thead takes: this bar sits above the toolbar and the 2px
   rule is the log's own section boundary further down (§12.5). Two of them in
   one screen would say the list starts twice. */
.basel-readiness-head { padding-bottom: 6px; }
.basel-readiness-head .basel-readiness-toggle { align-self: center; }
.basel-readiness-col { justify-self: center; align-self: center; color: var(--basel-ink-muted); }

/* The toggle. It is the grid's name, so it is drawn as the eyebrow it replaces
   and not as a button: no border, no ground, no box — a place to click, which
   is Tier 0 of §12.21's three weights. The chevron takes `currentColor` so it
   follows the ink on hover rather than needing a second rule. */
.basel-readiness-toggle {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    justify-self: start;
    padding: 0;
    border: 0;
    border-radius: var(--basel-radius);
    background: none;
    color: var(--basel-ink-muted);
    text-align: left;
    cursor: pointer;
}
.basel-readiness-toggle:hover { color: var(--basel-ink); }
.basel-readiness-toggle:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }

/* The row lead: "Worst, all 8 buildings" shut, a building name open. */
.basel-readiness-lead {
    min-width: 0;
    padding: 7px 0;
    color: var(--basel-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* A building with nothing logged anywhere. Muted, not a fifth standing (§12.3). */
.basel-readiness-quiet { color: var(--basel-ink-muted); }

.basel-readiness-cell {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    padding: 7px 0;
    border: 0;
    border-radius: var(--basel-radius);
    background: none;
    color: var(--basel-ink-muted);
    font: inherit;
    cursor: pointer;
}
.basel-readiness-cell:hover { background-color: var(--basel-sheet-sunken); }
.basel-readiness-cell:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }
/* "We looked, there is nothing." Decorative, aria-hidden, and the one thing
   --basel-ink-faint is allowed to be (§12.2). Not a zero and not a standing.

   `align-self: stretch` IS LOAD-BEARING. The row centres its items, so a cell
   is only as tall as its own content — and this one holds a middot where its
   neighbours hold a 12px mark and a figure. Centred, a shorter cell sits a
   pixel or two lower than the rest and the totals row stops sharing a top.
   Stretching makes it the row's height and its own flex centring does the rest.
   Invisible until a log with no records sits beside logs that have some, which
   is why it went unnoticed until Tab 09 (schema 21) added a sixth column that
   is empty on a new project. tools/smoke-test.js measures it as shutCellRows. */
.basel-readiness-cell--nil {
    justify-self: center;
    padding: 7px 0;
    color: var(--basel-ink-faint);
    cursor: default;
}
/* ---- Agenda items — ONE ROW GRID FOR EVERY KIND OF ITEM (ADR-088, §12.40)
   The item is the LOG ROW's anatomy (§12.9): fixed tracks, 44px folded, the
   chevron in the LAST column at the same right edge the Issues row puts it.
   Number 44 · title+count 1fr · outcome-or-tallies 1fr · minutes 56 · tick 24
   · chevron 24, gap 20; Prep appends a dots column (--prep). The gap and the
   44px number track are the ones the item has carried since §12.13; the tick
   and chevron are 24 — the kebab's own width, so a control column is one
   width on both screens; the minutes track is the widest figure it holds
   ("120 min") in the mono at 13px. A row starts the first grid row at 44px
   (the log row's --basel-pad-row-y twice round its 24px line) and the body of
   an open item is a second row spanning every track after the number, which
   is the 64px the body always started at. The row's right padding is the log
   row's, --basel-pad-row-x, and it is what puts the chevron's right edge
   exactly where the Issues row's is; nothing on the left, because the number
   track leads the row on both screens. The 3px cursor rule (--current) and
   the finished ground (--covered) are the only per-state differences. */

/* THE TWO SCREENS SHARE THE ANATOMY, NOT ONE TRACK STRING (ADR-132).
   ADR-088 said every row of every kind on the Agenda AND on Prep resolves to
   one literal `grid-template-columns`. That could not hold: Prep carries two
   controls the Agenda does not (a drag grip and an On toggle) and the Agenda
   gives Standing the log row's own 210px. What is shared is the ORDER of the
   tracks — lead track, title, standing, min, marks, chevron LAST at the row's
   right edge — and the one cross-screen measurement that still means
   something: the chevron's right edge is the Issues row chevron's. */
:root {
    /* The number track, named because the section heading of ADR-091 hangs in
       it. It is 76px on the Agenda with 36px of it spent as the indent INSIDE
       the number cell (ADR-132): depth is padding in a row's own cells and
       never on the grid container, because padding on the container is
       absorbed by the flexible track and slides Min out of column on the
       indented rows alone. */
    --basel-agenda-num-w: 76px;
    --basel-agenda-num-pad: 36px;        /* the indent, inside the cell */
    --basel-agenda-cline-pad: 68px;      /* a client line, one step further in */
    --basel-agenda-band-num-w: 36px;     /* a section heading stays at the margin */
    /* 210px is the LOG ROW's Standing width, --basel-row-cols' own number
       reused rather than a second one invented. At 1440 the title column
       measures 614px. */
    --basel-agenda-cols: var(--basel-agenda-num-w) minmax(0, 1fr) 210px 48px 24px 24px;
    /* PREP'S OWN TRACKS (ADR-133). The same ANATOMY and not the same string:
       the grip rides INSIDE the 76px number track, which is what buys the
       title back the 38px a separate grip column used to cost, and Standing is
       150 here rather than the Agenda's 210 because Prep gives two columns to
       controls the Agenda does not have. 150 still holds the words — the 10px
       mark, "3 waiting", "of 9" — measured at 113px with 37 to spare, so it is
       never shrunk to a mark and a figure. At 1440 with the rail open the
       title column is 346px. The DOTS COLUMN IS GONE: a topic's verbs are in
       the topic's own opened body now, where there is room for them. */
    --basel-agenda-cols-prep: var(--basel-agenda-num-w) minmax(0, 1fr) 150px 44px 44px 24px;
    /* THE SOURCE RAIL FOLDS (ADR-133), and folded the row simply grows: the
       same six tracks, Standing at the log row's own 210 and the title at 594.
       Same screen, same rows, wider columns. */
    --basel-agenda-cols-prep-wide: var(--basel-agenda-num-w) minmax(0, 1fr) 210px 48px 44px 24px;
}
.basel-agenda-item,
.basel-agenda-thead {
    display: grid;
    grid-template-columns: var(--basel-agenda-cols);
    grid-auto-rows: auto;
    gap: 0 18px;
    /* START, NOT CENTRE (ADR-132). A title wraps to two lines and carries what
       came of it under it, so a centred number, minutes, tick and chevron
       would drift down the row as it grew. Every mark sits on the title's
       FIRST line; the cells that are shorter than that line nudge themselves
       onto it below. */
    align-items: start;
    min-height: 44px;
    padding: 9px var(--basel-pad-row-x) 9px 0;
    min-width: 0;
}
.basel-agenda-item { border-bottom: var(--basel-rule-row) solid var(--basel-hair); }
/* PREP TAKES THE AGENDA'S ANATOMY (ADR-133): the same start alignment, the
   same 44px floor and the same 9px of air, on its own six tracks. It used to
   centre its cells against a fixed 44px row, which a wrapping title cannot
   live in. */
.basel-agenda-item--prep,
.basel-agenda-thead--prep {
    grid-template-columns: var(--basel-agenda-cols-prep);
    gap: 0 14px;
}
.basel-prep-split[data-prep-rail="hidden"] .basel-agenda-item--prep,
.basel-prep-split[data-prep-rail="hidden"] .basel-agenda-thead--prep {
    grid-template-columns: var(--basel-agenda-cols-prep-wide);
    gap: 0 18px;
}
/* THE GRIP IS THE NUMBER TRACK'S LEAD ON PREP, so the number cell takes no
   indent of its own: 24px of grip and 7 of gap is the step the Agenda spends
   as padding, and 76 cannot hold both. The TITLE keeps the Agenda's 36, so a
   title starts at the same x on both screens. */
.basel-agenda-item--prep > .basel-prep-num,
.basel-agenda-thead--prep > .basel-agenda-num-h { padding-left: 0; }

/* The column header: `.basel-thead--log`'s anatomy — the 11px uppercase
   labels in the muted ink and the 2px ink rule under it (§12.5). Not a
   `.basel-thead`, whose own grid is the log's; the tracks here are the row's,
   so a head sits over its column. */
.basel-agenda-thead {
    padding-bottom: 9px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
    grid-template-rows: auto;
    min-height: 0;
    align-items: baseline;
}
.basel-agenda-thead > span { color: var(--basel-ink-muted); white-space: nowrap; }
/* MIN IS RIGHT-ALIGNED OVER ITS FIGURES (ADR-132): the cell under it is, and
   a header that is not sits over the wrong end of its own column. */
.basel-agenda-thead > .basel-agenda-min-h { text-align: right; }
/* ON THIS AGENDA over the 24px tick column: measured at 121px as an 11px
   uppercase eyebrow at .16em (§12.18), so it is centred on the column and
   overflows both ways, over the MIN head's empty right half and the blank
   chevron head — never over a word. */
.basel-agenda-thead-on { justify-self: center; overflow: visible; }

/* THE CELLS. The number track's figure, the title cell (title · the log's
   chip · the count, one line, ellipsised), the third column (what came of
   it, or the tallies), the minutes, the tick and the chevron. */
.basel-agenda-num  { color: var(--basel-ink-muted); }
.basel-agenda-num--off { color: var(--basel-ink-faint); }
.basel-agenda-title-cell {
    display: flex;
    align-items: baseline;
    gap: 12px;
    min-width: 0;
    overflow: hidden;
}
.basel-agenda-title {
    color: var(--basel-ink);
    min-width: 0;
    flex: 0 1 auto;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.basel-agenda-title-cell > .basel-set-quiet.basel-agenda-title,
.basel-agenda-titleline > .basel-set-quiet.basel-agenda-title { flex: 1 1 auto; width: auto; height: 26px; }
/* A QUIET TITLE FIELD WRAPS, AND GROWS (ADR-134). Prep's topic titles and
   their agenda's lines are quiet fields (ADR-050), and a one-line field threw
   the end of a long title off the right edge of a 346px column — the same
   defect §12.47 fixed for the static titles, arriving a second time on the
   half of them that happen to be editable. It is ONE control still: the same
   cell, the same commit, the same register — no box until focus, a hairline
   on hover, the ink and the sheet on focus. It simply wraps.

   At rest it is capped at the SAME TWO LINES `--wrap` clamps to, so a row is
   the same height whether its title is a word or a field. Focused, the cap
   comes off and the field takes the height its text needs — `agendaPrepSizeTitle()`
   sets it, because a textarea has no content height in CSS. `overflow: hidden`
   in both states: a title on this screen never scrolls, in either axis.

   No `resize` grip: it would be a second affordance on a control whose height
   is the app's to decide, and it is the one corner of a row the drag would
   have to argue with. */
.basel-agenda-title-cell > .basel-set-quiet.basel-agenda-title--field,
.basel-agenda-titleline > .basel-set-quiet.basel-agenda-title--field {
    height: auto;
    min-height: 26px;
}
.basel-agenda-title--field {
    display: block;
    width: 100%;
    resize: none;
    overflow: hidden;
    overflow-wrap: break-word;
    white-space: pre-wrap;
    text-overflow: clip;
}
.basel-agenda-count { color: var(--basel-ink-muted); flex: 0 0 auto; white-space: nowrap; }
/* "off this meeting" / "nothing open" in the count place: words, not a figure. */
.basel-agenda-count--off { font-family: inherit; }
.basel-agenda-min-cell { min-width: 0; white-space: nowrap; text-align: right; }

/* ---- THE AGENDA'S OWN CELLS (ADR-132) -------------------------------------
   THE INDENT IS PADDING INSIDE THE CELL, never on the grid container: a
   container padded to indent one row loses that width out of the ONE flexible
   track, so Min, the tick and the chevron slide left on the indented rows and
   the column header stops sitting over its column. Padding inside the cell
   costs the same width and costs it in the same place on every row.

   SINCE ADR-133 NONE OF THIS IS SCOPED AWAY FROM PREP: the two screens share
   the row's anatomy, so the indent, the first-line alignment and the body's
   own x are one set of rules read by both. */
.basel-agenda-item > .basel-agenda-num,
.basel-agenda-thead > .basel-agenda-num-h {
    padding-left: var(--basel-agenda-num-pad);
}
.basel-agenda-title-cell--stack {
    display: block;
    overflow: visible;
    padding-left: var(--basel-agenda-num-pad);
}
/* THEIR AGENDA'S LINES STEP AGAINST THE BLOCK (ADR-132) — the TITLE steps,
   never the number: a line's number sits in the number track at the same
   36px as every other number on the screen, or the decimal it carries stops
   reading as one of the meeting's own. */
.basel-agenda-item--cline > .basel-agenda-title-cell--stack { padding-left: var(--basel-agenda-cline-pad); }
.basel-agenda-item--cline .basel-agenda-title--wrap { font-weight: 400; }

/* THE COUNT IS A SIBLING OF THE TITLE, NOT A CHILD OF IT. Inside the clamped
   box a long title pushes the count past the clamp and it silently
   disappears — a figure that is right until the title is long is worse than
   no figure. `flex-start`, never `baseline`: an `overflow: hidden` box
   synthesises its baseline from its BOTTOM edge, so a two-line title would
   drop the count beside its second line. */
.basel-agenda-titleline {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    min-width: 0;
}
.basel-agenda-titleline > .basel-agenda-count,
.basel-agenda-titleline > .basel-agenda-logno { flex: 0 0 auto; padding-top: 3px; }
/* THE TITLE WRAPS TO TWO LINES (ADR-132). One line and an ellipsis threw away
   the end of every long item title at 1440 — and an item title is a sentence
   the room is about to discuss, so its end is the half that matters. */
/* THE RAMP IS SHARED WITH THE QUIET TITLE FIELD (ADR-134) and the CLAMP is
   not: a static title is a clamped `-webkit-box`, an editable one is a
   textarea that wraps and grows, and the two must read as the same words at
   rest — which they only do if the type is stated once. */
/* Two classes on the field's own selector, and that is load-bearing:
   `.basel-set-quiet` carries `font: inherit`, a SHORTHAND that resets
   font-size, line-height and weight, and it is declared further down this
   file. A one-class ramp lost to it silently and the editable title came out
   at the inherited 16/1.5 beside a static one at 15/1.4. */
.basel-agenda-title--wrap,
.basel-set-quiet.basel-agenda-title--field,
.basel-agenda-title--field {
    text-wrap: pretty;
    white-space: normal;
    font-size: 15px;
    line-height: 1.4;
    font-weight: 700;
    letter-spacing: normal;
}
.basel-agenda-title--wrap {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-overflow: clip;
}
/* WHAT CAME OF IT IS ITS OWN LINE under the title, not a column: the third
   track is the STANDING now, and a sentence squeezed into 210px was an
   ellipsis pretending to be a summary. */
.basel-agenda-said2 {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    text-wrap: pretty;
    margin-top: 3px;
    color: var(--basel-ink-muted);
    font-size: 13px;
    line-height: 1.55;
}
/* THE STANDING COLUMN: the worst standing under this item and how many are at
   it — "▲ 3 waiting  of 9". The four-way breakdown is not repeated here; it
   is in the open item, where there is room to read it. */
.basel-agenda-standing {
    display: flex;
    align-items: center;
    gap: 7px;
    min-width: 0;
    overflow: hidden;
    white-space: nowrap;
    font-weight: 700;
}
.basel-agenda-standing-n { font-weight: 700; }
.basel-agenda-standing-of { color: var(--basel-ink-muted); font-weight: 400; }
/* `.basel-agenda-dots` went with Prep's dots column (ADR-133); the tick and
   the chevron are the two control cells now, on both screens. */
.basel-agenda-tickcell, .basel-agenda-chev {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 0;
}
/* The chevron glyph sits where the Issues row's sits: that track is 16px
   and this one is 24, so the cell leads with the 8px difference and the
   glyph's x is the same on both screens (§12.40). At rest it points down;
   on the current item it turns, exactly as `.basel-row--open` turns it. */
.basel-agenda-chev { justify-content: flex-start; padding-left: 8px; color: var(--basel-ink-muted); }
.basel-agenda-chev[data-open="true"] svg { transform: rotate(180deg); }
.basel-agenda-chev > .basel-prep-chev { width: 16px; height: 20px; }
/* ON THE TITLE'S FIRST LINE (ADR-132). The row is `align-items: start`, so
   each of these shorter cells is nudged down onto the 21px line the title
   opens with instead of sitting at the top of the row above it. */
.basel-agenda-item > .basel-agenda-min-cell { padding-top: 3px; }
.basel-agenda-item > .basel-agenda-tickcell { padding-top: 2px; }
.basel-agenda-item > .basel-agenda-chev { padding-top: 8px; }

/* The body of an open item: the second grid row, spanning every track after
   the number, with the item's air under it. */
.basel-agenda-body { grid-column: 2 / -1; min-width: 0; padding-bottom: 14px; }
/* The parts start at the TITLE's own x, which is 36px into the second track
   since ADR-132 indented the cell rather than the container. */
.basel-agenda-item > .basel-agenda-body { padding-left: var(--basel-agenda-num-pad); }
.basel-agenda-item--cline > .basel-agenda-body { padding-left: var(--basel-agenda-cline-pad); }

/* ============================================================================
   THE OPEN ITEM'S BODY IS PARTS — ADR-093, DESIGN §12.42
   ----------------------------------------------------------------------------
   The body was a stack of blocks with nothing between them: the words the PM
   wrote, the tallies, the record rows, what was said and the composer ran
   together as one column of text at four sizes. Read on a projector it was
   noise, which is the defect this block exists to fix.

   The separator is the ROW HAIRLINE and it is drawn HERE, on the body's direct
   children, rather than by each part — so a kind of item that grows a new part
   is separated without knowing this rule exists. Nothing is boxed and nothing
   is indented: the parts already start at the title's own x, and a rule plus
   air is the whole of the hierarchy (§12.5). */
.basel-agenda-body > * + * {
    margin-top: 14px;
    padding-top: 14px;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
/* The composer is several elements — a lands line, the field, the nav — so it
   is wrapped at the call site to take ONE rule above it rather than one
   between each of its own pieces. */
.basel-agenda-part { min-width: 0; }
/* A part's heading: the app's eyebrow, the count in the mono after it. */
.basel-agenda-partk {
    display: flex;
    align-items: baseline;
    gap: 10px;
    margin-bottom: 8px;
}
.basel-agenda-partn { color: var(--basel-ink-muted); }
/* A part's heading sits directly on its rows, so the first row must not draw a
   second line under the heading's air. */
.basel-agenda-partk + .basel-agenda-rows { margin-top: 0; }

/* WHAT WAS SAID — one line per post, the record thread's own anatomy (§12.28):
   the words carry the line, the name and the clock follow in the mono at the
   right. Two tracks, so a long post wraps against its own column and never
   under the name. */
.basel-agenda-post {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 4px 16px;
    align-items: baseline;
    padding: 7px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-agenda-post:last-child { border-bottom: 0; padding-bottom: 0; }
/* THE META CELL HOLDS THE NAME, THE CLOCK AND THE VERB (ADR-094, T2). Always
   drawn, never on hover: a hover verb does not exist on a touch screen and
   cannot be found during a meeting. Baseline-aligned with the words, so the
   row is still one line. */
.basel-agenda-post-m { display: flex; align-items: baseline; gap: 12px; color: var(--basel-ink-muted); white-space: nowrap; }
.basel-post-edit { flex: 0 0 auto; }
/* THE DELETE GLYPH (ADR-126). A drawn cross, because Basel loads no icon font
   and because the one verb that takes something away should not read as a
   fourth word in a row of words. Muted at rest and the waiting ink on hover —
   the app's one warning colour, spent where something is about to be lost.
   A 24px box round an 11px mark: the row is 13px type, and a control smaller
   than its own line is a control you miss with a finger. */
.basel-post-del {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    margin: -6px -6px -6px 0;
    padding: 0;
    color: var(--basel-ink-faint);
}
.basel-post-del:hover,
.basel-post-del:focus-visible { color: var(--basel-waiting-ink); }
.basel-post-del svg { display: block; }
/* The "edited" mark rides with the words, not with the name: what was
   corrected is the sentence. */
.basel-post-edited { margin-left: 10px; color: var(--basel-ink-muted); }
/* ============================================================================
   ONE LIST, NOT TWO — ADR-122, DESIGN §12.42
   ----------------------------------------------------------------------------
   The item drew its records in one part and what was said in another, and
   nothing joined them: the same three issues were on the screen twice, and a
   note reading "Pending PO" belonged to neither of them. A post now carries
   the record it was said about, so the record row leads its own posts.

   The nest is --basel-pad-nest-x, the app's own nesting step, and it is the
   only indent inside an item body. The posts in a group draw NO hairline
   between them: .basel-linked's own border-top is already the line above each
   record, so a second rule inside the group would be one line too many. */
.basel-agenda-rt-in { padding-left: var(--basel-pad-nest-x); padding-bottom: 5px; }
.basel-agenda-rt-in > .basel-agenda-post { border-bottom: 0; padding: 5px 0; }
.basel-agenda-rt-in > .basel-agenda-post:last-child { padding-bottom: 0; }
/* The line that tells the PM, mid-meeting, what has not been reached. It takes
   the faintest ink the app has, because it is the absence of a fact. */
/* A quiet PHRASE, so muted and never faint (ADR-132). */
.basel-agenda-rt-q { margin: 0; padding: 5px 0 7px var(--basel-pad-nest-x); color: var(--basel-ink-muted); }
/* "On the meeting" — a group heading, not a record row, so it takes the
   record row's own rule above it and the app's eyebrow rather than a mark. */
.basel-agenda-rt-k {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding: 9px 0 3px;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
/* Pushes the thread's own count to the right of the part heading, so one
   heading says how many records there are and how much was said. */
.basel-agenda-partk-gap { flex: 1 1 auto; }

/* A post being corrected gives up the two-track row and takes the editor's
   own block — the posts above and below do not move. */
.basel-agenda-post--editing { display: block; padding: 4px 0 10px; }
.basel-post-editor-cells {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 12px;
    margin-bottom: 8px;
}
.basel-post-editor-cells .basel-set-quiet { flex: 0 1 220px; height: 34px; }
/* The correction row takes the composer's own who-picker (ADR-128), so it
   takes the composer's own height with it: 34px, like every other control the
   PM meets on this screen. */
.basel-post-editor-cells > .basel-who-host > .basel-who > .basel-multi-btn { height: 34px; }
/* The third quiet cell: which record this was said about (ADR-123). It sits
   between the name and the clock — the two facts about a post that were
   already correctable — and the row wraps (`.basel-post-editor-cells`, above)
   rather than squeezing the name. */
.basel-post-editor-cells > .basel-ctl--select { flex: 0 1 300px; height: 34px; min-width: 0; }
.basel-post-editor-note { margin: 0 0 8px; color: var(--basel-ink-muted); }
/* The clock is shown and NOT editable (ADR-094): a record's date is a business
   fact that can be typed wrong; a post's clock is the app's own stamp of when
   it was said, and a thread whose times could be retyped is a thread whose
   order could be rewritten afterwards. */
.basel-post-editor-when { color: var(--basel-ink-muted); }
.basel-agenda-post-t { color: var(--basel-ink); min-width: 0; }
/* Their block's body on Prep spans the WHOLE row, so a line inside it is the
   width of every other row and lands on the same tracks (ADR-088). It takes
   NO indent of its own (ADR-133): a body indented 36px would make the lines
   inside it a different width from every other row on the screen, which is the
   one thing this class exists to prevent. */
.basel-agenda-item > .basel-agenda-body--full {
    grid-column: 1 / -1;
    padding-left: 0;
    margin-right: calc(-1 * var(--basel-pad-row-x));
}
.basel-agenda-item--fixed > .basel-agenda-body,
.basel-reorder-row > .basel-agenda-body { padding-bottom: 14px; }

.basel-agenda-note  { margin-top: 5px; color: var(--basel-ink-muted); }
.basel-agenda-tallies {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 18px;
    margin-top: 10px;
}
.basel-agenda-tally { display: inline-flex; align-items: center; gap: 7px; font-weight: 700; white-space: nowrap; }
.basel-agenda-rows  { margin-top: 10px; }
/* The .basel-linked row WITHOUT its log column — four tracks, not five. On an
   agenda item the log is the item's own title three lines above, so the third
   track says nothing; it also wrapped "ACTION ITEM" onto two lines, because an
   11px uppercase label at .16em measures 96px in the live DOM against the 84px
   that class declares. The closed-items appendix keeps the column: that list
   genuinely mixes logs. */
.basel-linked--nolog { grid-template-columns: 10px 62px minmax(0, 1fr) auto; }
/* The record id inside an interim-update line: a link, at the same weight the
   thread's own dates carry. */
.basel-agenda-ref {
    padding: 0;
    margin-right: 8px;
    border: 0;
    background: none;
    color: var(--basel-ink);
    font: inherit;
    font-weight: 700;
    cursor: pointer;
}
.basel-agenda-ref:hover { color: var(--basel-working-ink); }

/* ---- The meeting block and the archive ---------------------------------- */

.basel-meet { border-top: var(--basel-rule-section) solid var(--basel-ink); }
/* THE DATES LINE OF THE HERO (ADR-088): the date, the series name, the phase
   chip, the internal mark, the hours, then the head's controls at its right.
   The identity band above it — the eyebrow and the label at the hero's size
   step — is `.basel-sched-hero`'s own; see §12.23 and the block below. */
.basel-meet-head {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    padding: 4px 0 0;
    min-width: 0;
}
.basel-meet-name { color: var(--basel-ink); min-width: 0; }
/* The label — OAC · 2026-09-03 · 1:30 pm — leads the line (ADR-083, ADR-146); the hours are a quiet number
   field with its unit, live or locked, because the time is recorded after.
   `.basel-ctl--hours` went with Prep's toolbar (ADR-133): the hours are a
   caption-line item there now, and the Agenda's head is this rule's one caller. */
.basel-meet-label { color: var(--basel-ink); }
.basel-meet-hours { display: inline-flex; align-items: baseline; gap: 4px; color: var(--basel-ink-muted); }
.basel-meet-head .basel-meet-hours-in { width: 56px; flex: 0 0 56px; text-align: right; }
/* The same field on Prep's caption line (ADR-133). */
.basel-viewitem .basel-meet-hours-in { width: 52px; text-align: right; }
.basel-meet-when { color: var(--basel-ink-muted); }
.basel-meet-head .basel-input  { flex: 1 1 200px; width: auto; }
.basel-meet-head .basel-select { flex: 0 1 200px; width: auto; }
.basel-meet-body { padding-bottom: 4px; }

/* ============================================================================
   THE COMPOSER STANDS STILL — ADR-125, DESIGN §12.44
   ----------------------------------------------------------------------------
   Sticky, not fixed: it takes real layout space, so it clears the side panel
   without knowing the panel's width, never covers the last note on the item,
   and settles at the end of the meeting instead of hanging over the archive.

   ONE HEIGHT FOR EVERY CONTROL ON THE LINE. The bar is the only place in the
   app where a picker, a who-cell, a field and the primary verb sit on one row,
   and a button whose height comes from its padding does not land on a 34px
   control by accident. So the row states 34px once and everything on it takes
   it — the field's min-height too, or a textarea reports 34 and paints 36. */
.basel-meet-bar {
    position: sticky;
    /* ABOVE THE LEGEND, NOT UNDER IT (ADR-127). `#basel-legend` is sticky at
       bottom 0 with z-index 10, so a bar at bottom 0 has its last row painted
       over — Back, Next and Polish wording went under the standing legend. The
       legend measures its own height onto the root (fitBaselLegendHeight),
       because it WRAPS: one line at 1440, two on a narrow laptop, so a number
       here would be right at one width and wrong at the other. */
    bottom: var(--basel-legend-h, 0px);
    z-index: 4;
    /* TWO ROWS, NOT FOUR. Drawn as three stacked blocks the bar measured 166px
       of a 1,000px screen — a composer taking a sixth of the meeting to say
       one sentence. The two things the bar says about TARGETING — which item
       it posts to, and which record the note lands on — belong on one line,
       so the grid puts them there and gives the composer the row beneath.
       The builder is untouched: it still emits a lands line and a composer,
       and the bar places them. */
    display: grid;
    grid-template-columns: minmax(0, auto) minmax(0, 1fr);
    align-items: center;
    gap: 9px 20px;
    padding: 11px var(--basel-pad-row-x) 12px 0;
    border-top: var(--basel-rule-section) solid var(--basel-ink);
    background-color: var(--basel-sheet-sunken);
}
.basel-meet-bar > .basel-composer { grid-column: 1 / -1; }
/* The bar spans the screen's own padding, so its ground reaches both edges
   while its content stays on the items' left margin. */
.basel-meet-bar::before {
    content: "";
    position: absolute;
    top: calc(-1 * var(--basel-rule-section));
    bottom: 0;
    left: calc(-1 * var(--basel-pad-screen-x));
    right: calc(-1 * var(--basel-pad-screen-x));
    z-index: -1;
    border-top: var(--basel-rule-section) solid var(--basel-ink);
    background-color: var(--basel-sheet-sunken);
}
/* What it will post to — the one thing a composer owes you once it no longer
   sits under the item it writes to. */
.basel-meet-bar-k {
    display: flex;
    align-items: baseline;
    gap: 10px;
    min-width: 0;
}
.basel-meet-bar-n { color: var(--basel-ink-muted); flex: 0 0 auto; }
.basel-meet-bar-t {
    color: var(--basel-ink);
    font-weight: 700;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* The lands-on line rides the caption's row, at its right end. */
.basel-meet-bar > .basel-agenda-lands { margin-top: 0; justify-self: end; }
/* THE ROW LINES UP. Centred, one height, and the field's min-height set with
   it so an auto-growing textarea starts level with the controls beside it. */
.basel-meet-bar .basel-composer-line { align-items: center; gap: 10px; }
.basel-meet-bar .basel-composer-line > .basel-ctl,
.basel-meet-bar .basel-composer-line > .basel-who-host > .basel-who > .basel-multi-btn,
.basel-meet-bar .basel-composer-line > .basel-btn {
    height: 34px;
    padding-top: 0;
    padding-bottom: 0;
    align-items: center;
}
.basel-meet-bar .basel-composer-text { height: 34px; min-height: 34px; }
/* THE QUIET ROW LINES UP WITH THE ROW ABOVE IT. Back and Next take the same
   34px, and the pair ends where Post ends — the bar is a block of controls and
   a right edge that is 4px out reads as a mistake at a metre and a half. */
.basel-meet-bar .basel-composer-quiet { margin-top: 7px; }
.basel-meet-bar .basel-agenda-nav { gap: 10px; }
.basel-meet-bar .basel-agenda-nav > .basel-btn {
    height: 34px;
    padding-top: 0;
    padding-bottom: 0;
    align-items: center;
}
.basel-meet-bar .basel-composer { gap: 0; }
/* THE PICKER OPENS UPWARD IN THE BAR (ADR-128). A popover anchored under its
   button is right everywhere else in the app and wrong in the one control that
   lives at the bottom of the screen: it opened downward, off the viewport, and
   the standing legend covered what was left. The bar is ALWAYS at the foot, so
   this needs no measuring — up is the only direction there is. */
.basel-meet-bar .basel-multi-panel { top: auto; bottom: calc(100% + 4px); }
/* Never taller than the room above it, whatever the roster's length. */
.basel-meet-bar .basel-who-panel { max-height: min(340px, 50vh); }
.basel-meet-sec  { padding: 14px 0 4px; }
/* The dots on the meeting head (ADR-045, ADR-087): the one menu anatomy,
   opening under the dots at the head's right end. */
.basel-meet-menu { position: relative; display: inline-flex; align-items: center; align-self: center; }
.basel-meet-menu .basel-menu { right: 0; left: auto; width: 232px; }
/* What was typed before the meeting had a position (ADR-087): a record,
   drawn as stored, read-only, in a section that says what it is. Prose at
   the reading measure every other markdown block takes. */
.basel-meet-legacy { border-top: var(--basel-rule-row) solid var(--basel-hair); }
.basel-prose { max-width: 84ch; color: var(--basel-ink); }
.basel-prose p:last-child { margin-bottom: 0; }
/* A kind of series that is missing, said under Prep's picker (ADR-087). */
.basel-prep-series-note { display: inline-flex; align-items: baseline; gap: 4px; flex-wrap: wrap; color: var(--basel-ink-muted); }
/* WHAT USED TO BE HERE (ADR-131): `.basel-archive` and `.basel-agenda-arch-row`
   — the fold at the foot of the live meeting and the row it drew per archived
   meeting. The meetings are a REGISTER on their own tab now, in the log's own
   anatomy (`.basel-arch-*`, below the progress track), so nothing is left for
   these to style. The one lesson they carried is carried there: a button
   cannot hold a button, so the minutes verb sits BESIDE the row's fold. */

/* ---- Roll call ------------------------------------------------------------
   Rows, not a table: the same decision ADR-022 made for the logs, and for the
   same reason — a <table> buys column alignment and costs everything else. */

/* THREE COLUMNS SINCE ADR-132: tick, name, role. Company and email are ROSTER
   facts — they belong to the person, not to the meeting — and repeating them
   on every meeting's roll call put two columns of unchanging text where the
   one question the room asks ("who is here") is answered. They are edited and
   read in Settings. */
.basel-roll { display: block; }
.basel-roll-row {
    display: grid;
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr);
    gap: 12px;
    align-items: center;
    padding: 7px 0;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-roll-row--tick { grid-template-columns: 16px minmax(0, 1.1fr) minmax(0, 1fr); }
.basel-roll-row--head { border-top: 0; border-bottom: var(--basel-rule-section) solid var(--basel-ink); }
.basel-roll-row--head > span { color: var(--basel-ink-muted); }
.basel-roll-name { color: var(--basel-ink); font-weight: 700; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-roll-cell { color: var(--basel-ink-muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-roll-cell a { color: var(--basel-working-ink); }

/* ---- Rendered markdown ---------------------------------------------------
   renderMarkdown() (core/utils.js) emits <strong>, <em>, <div>, <br> and two
   Tailwind list classes. It carries no colour and no size of its own, so it
   inherits whatever it is dropped into — which is why this container states
   both rather than the markup doing it. */

.basel-md { margin-top: 8px; color: var(--basel-ink); max-width: 840px; }
.basel-md strong { font-weight: 700; }
.basel-md ul, .basel-md ol { margin: 4px 0; }

/* ==========================================================================
   AGENDA PREP — the last meeting screen (DESIGN §12.18, ADR-040) — Phase 4.18
   --------------------------------------------------------------------------
   IT ADDS FOUR RULES AND NOTHING ELSE, which is the sixth time a converted
   screen has been able to say that. A prep section IS an `.basel-agenda-item`
   — the same 44px mono number, the same body, the same hairline between
   sections — because a prep section and an agenda item are the same object
   seen an hour apart. Its records are `.basel-linked` rows drawn by
   agendaRecapRowHtml() (js/modules/agenda.js), the identical row the Agenda
   itself draws, so a record cannot read one way here and another way there.

   THE FIVE:
     .basel-prep-num    the 44px number track, stacked: the section's number
                        and, under it, the drag handle. It is PREP-ONLY. The
                        track itself, `.basel-agenda-num` and the whole of
                        `.basel-agenda-item` belong to the Agenda — a prep
                        section IS an agenda item (§12.18) — so the handle goes
                        inside a wrapper of this screen's own rather than into
                        that grid, which the projector screen also draws.
     .basel-prep-why    the one-line reason under a flagged record ("Blocking ·
                        no update in 21 d"). It is prose about the record, not a
                        standing, so it takes the muted ink and no shape — the
                        same call §12.13 makes about a building-level word.
     .basel-prep-delta  a delta entry: the New / Changed / Closed eyebrow, then
                        the ordinary record row under it.
     .basel-prep-fields the prep-notes block's own inputs, laid out as the
                        record field grid is (`.basel-fields`) but holding
                        settings rather than a record's fields.
   ========================================================================== */

/* A hidden section keeps its full contrast and says so in words. Dimming it
   would put a fact about the MEETING into the same channel §12.14 uses for a
   fact about a RECORD, and half-contrast prose fails AA on the ground it sits
   on either way.

   AND THERE ARE NO VERBS ON THIS ROW ANY MORE (ADR-078, *Draw the state, not
   the verb*). It carried two — **Hide from agenda** in a hairline box and
   **Collapse** as a bare word — correctly graded by §12.21's three weights and
   still wrong, because §12.21 only ever asked how LOUD a control should be and
   never whether it should be a word at all. Both of these set a two-state fact
   about the row: this section is covered by this meeting, or it is not; it is
   open, or it is shut. Thirteen sections meant thirteen four-word sentences in
   boxes down a screen whose actual content is the titles.

   So the state is a TICK (`.basel-prep-on`, the app's one `.basel-check`) named
   once by `.basel-prep-colhead`, and the fold is the CHEVRON every other
   collapsible thing in this app already draws. `.basel-prep-acts-div` and
   `.basel-prep-acts` are both gone — the second outlived the email digest that
   was its last caller (ADR-099) and is deleted here (ADR-133) rather than left
   as a rule the next reader has to prove is dead.

   THE TICK IS QUIET AND ITS CONSEQUENCE IS NOT, which is the one objection to
   this and the reason the tooltip carries the whole sentence: untick and the
   client stops receiving that section. The `OFF THIS AGENDA` .basel-flag beside
   the title and the "N of M sections" figure in the screen head still say the
   state in words — the tick replaced the BUTTON, never the mark.

   The two column widths are stated so the head lines up over the ticks. They
   are the only fixed widths on this row and they exist for that one reason. */
/* NO `margin-left: auto` (ADR-133). It was right when this was the last child
   of a flex head; as a GRID CELL it pushed the tick to the right-hand edge of
   its own 44px track, so the ON header — centred on the track, as a header
   must be — sat 18px left of the ticks it names. A column header that stops
   lining up with its column is worse than no header, which is the defect §12.18
   recorded the first time and this is the second. */
.basel-prep-state {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 14px;
}
/* THE TICK IS THE COLUMN, AND THE COLUMN IS NAMED "ON" (ADR-088, ADR-133).
   The word was "On this agenda" — 121px of 11px uppercase eyebrow at .16em,
   centred over a 24px track and overflowing both ways over its neighbours'
   empty halves. Prep has a Standing column and a Min column of its own now, so
   there is no empty half to overflow into: the header is short enough to sit
   in its own 44px track, and the reason the tick reads at all is that it
   finally HAS one. */
.basel-prep-on {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
}
/* Not `.basel-act`: it is not a verb and takes no box. The chevron's own
   rotation is here rather than on the svg, the same shape every other fold in
   this app uses (`.basel-group-chev`, `.basel-fold-chev`). */
.basel-prep-chev {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 20px;
    height: 20px;
    padding: 0;
    border: 0;
    border-radius: var(--basel-radius);
    background: none;
    color: var(--basel-ink-muted);
    cursor: pointer;
}
.basel-prep-chev:hover, .basel-prep-chev:focus-visible { color: var(--basel-ink); }
.basel-prep-chev:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 0; }
.basel-prep-chev[aria-expanded="false"] svg { transform: rotate(-90deg); }

/* `.basel-prep-colhead` stays on the column header itself
   (`.basel-agenda-thead--prep`, ADR-088) so the one-head rule still reads. */

/* A ROW WITH NO GRIP HOLDS THE GRIP'S WIDTH (ADR-133), so every number on the
   screen sits at one x: a fixed section and their agenda's lines cannot be
   dragged, and a number that slid 31px left on those rows alone would read as
   an indent nobody meant. */
.basel-grip-hole { flex: 0 0 auto; width: 24px; }

/* THE TOPIC'S VERBS, at the head of its own opened body (ADR-133). A row of
   words with the body's own rule under them, never a box: they are Tier 2
   (§12.21), because each changes what the client receives. */
.basel-prep-verbs { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 18px; margin-bottom: 12px; }

/* The roll call opens under the foot row, where its verb is. */
.basel-prep-rollbody { padding-top: 12px; }

/* THE HANDLE SITS BESIDE THE NUMBER, NOT UNDER IT (ADR-085). Stacked, the
   track was 38px tall and every folded item paid for it — thirteen rows and a
   column that could not read as a list. Side by side they fit the 44px track
   the Agenda already holds, and the grip leads because that is the edge the
   pointer goes for. */
.basel-prep-num {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 7px;
}

/* A FOLDED ITEM IS ONE LINE, AND ONE LINE MEANS IT DOES NOT WRAP (ADR-085,
   ADR-088). It is the one row grid now: every cell but the title is a fixed
   track, the title cell ellipsises, and the row is 44px on both screens. */
.basel-prep-body { margin-top: 10px; }
.basel-prep-more  { margin-top: 12px; }

.basel-prep-why {
    margin: 3px 0 0 72px;
    color: var(--basel-ink-muted);
}

.basel-prep-flagged + .basel-prep-flagged { margin-top: 8px; }

.basel-prep-delta { display: block; }
.basel-prep-delta + .basel-prep-delta { margin-top: 8px; }
.basel-prep-delta-head {
    display: flex;
    align-items: baseline;
    gap: 10px;
    flex-wrap: wrap;
}
.basel-prep-delta-note { color: var(--basel-ink-muted); }

/* Same anatomy as `.basel-fields` one screen over: a 2px left rule, an 11px
   uppercase key, the control. These hold agendaSettings rather than a record's
   own fields, which is why they are not `.basel-field` — nothing here commits
   through commitLogCardField(). */
.basel-prep-fields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
    gap: 18px;
    margin-top: 14px;
}
.basel-prep-field { border-left: var(--basel-rule-section) solid var(--basel-hair-strong); padding-left: 12px; min-width: 0; }
.basel-prep-field-k { color: var(--basel-ink-muted); margin-bottom: 5px; }
.basel-prep-field .basel-input,
.basel-prep-field .basel-select { width: 100%; }

/* THE VERB IS AT THE END OF THE WORK (ADR-133). It was a paragraph explaining
   a button that lived at the top of the screen; the paragraph is deleted and
   the button is here, beside the two facts a PM checks last — who is expected
   and when the next one is — with the roll one verb away. Publish stays in the
   screen head as well: every converted screen puts its one solid-ink action
   there, and this is the same action, not a second one. */
.basel-prep-foot {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 10px 18px;
    padding: 14px 0 4px;
    border-top: var(--basel-rule-section) solid var(--basel-ink);
    color: var(--basel-ink-muted);
    margin: 0;
}
.basel-prep-foot .basel-btn--primary { margin-left: auto; }
.basel-prep-foot-roll { color: var(--basel-ink); font-weight: 700; }

/* ==========================================================================
   THE LETTER FRAME — a document this app hands to another program (DESIGN §0)
   --------------------------------------------------------------------------
   WHAT USED TO BE HERE: the Email Digest’s two-column chrome — .basel-mail-cols,
   -col, -section, -count, -ai-acts, -subject and -subject-row. Deleted with
   that screen (ADR-099) rather than left behind: a rule with no markup is a
   rule the next reader has to prove is dead.

   THESE TWO SURVIVE, because they were never the digest’s — they are the frame
   any screen puts a MAIL LETTER in, and the Outstanding screen draws two of
   them (the live preview while drafting, and every archived letter in the sent
   log). What sits INSIDE .basel-mail-preview is inline-styled HTML built for
   Outlook and Gmail, which have never heard of this app’s tokens; it is neither
   Basel nor v2, the same standing the Excel export has. The frame is a Basel
   component and its contents are exempt — and the smoke suite’s foreign-content
   exclusion keys off THIS CLASS, so a new screen inherits the exemption by
   using the frame (ADR-097).
   ========================================================================== */

.basel-mail-pane {
    overflow-y: auto;
    padding: 18px;
    background-color: var(--basel-sheet-sunken);
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    min-height: 300px;
}
.basel-mail-preview {
    border: var(--basel-rule-row) solid var(--basel-hair);
    background-color: var(--basel-sheet);
    padding: 16px;
    min-height: 264px;
}

/* ==========================================================================
   HOME — the third system (DESIGN §12.16, ADR-038) — Phase 4.16
   --------------------------------------------------------------------------
   Every project the PM is running, above the project record and the PM's own
   tools rather than inside either. It replaces the Portfolio and By Owner
   tabs, which were two fragments of one screen.

   IT REUSES THE LOG ANATOMY AND ADDS ALMOST NOTHING. A project row is a
   `.basel-row` with its own column set; an outstanding item under it is a
   `.basel-linked`, the same leaf row a link section and the Agenda already
   draw; the standings come from `.basel-mark-*` and `.basel-standing-*`. What
   is genuinely new here is one column set, one two-column lower register and
   the day-grouped week list — which is §12.11 finding 3's rule (a screen that
   is not a log declares its own column set and states its min-width) applied
   for the fourth time.

   ONE COLLAPSE PER RECORD (§12.9 rule 4). There is no rule below for a
   collapsed thing inside `.basel-home-pbody`, because there is not one.
   ========================================================================== */

/* TWO GRIDS, BOTH DECLARED, AND THE ROW AND ITS COLUMN HEAD USE BOTH (ADR-051).

   The outer grid is the row HEAD: the expander, then Open, then the chevron.
   The inner grid is the expander's own five cells: name · phase · deliverable ·
   standing · saved. They are two grids rather than one because Open is a
   BUTTON and the expander is a button — a button inside a button is invalid
   HTML and does not receive clicks — so the two have to be siblings, and
   siblings can only share a column set by nesting one grid inside a cell of
   the other.

   Standing is 200px, near the width the log rows give it, because it carries
   the same kind of sentence ("4 waiting on somebody else"). Only the name
   flexes, which is rule 7 one screen out. */
:root {
    /* THE CHEVRON TRACK IS GONE (ADR-117): nothing on this screen expands, so
       the row is the verb and there is nothing left for a chevron to do.
       the row · its one verb · the row menu */
    --basel-home-phead-cols: minmax(0, 1fr) 156px 28px;
    /* name · file + where · open items · last read. Deliverable and the
       standing sentence went with the expansion — they are triage, and this
       list is for switching; the current project above still carries both. */
    --basel-home-prow-cols:  minmax(0, 1.15fr) minmax(0, 1.5fr) 62px 96px;
}
/* The open-items tally: one figure, right-aligned under its column label, in
   the mono like every other count in this app (§12.8 #3). */
.basel-home-popencount { text-align: right; color: var(--basel-ink); }
.basel-home-popencount-v { display: inline-flex; align-items: center; gap: 5px; }
/* A heading that used to be a fold. Same rule, same ink, no pointer. */
.basel-home-fold--static { cursor: default; }
/* The location note, under the folder name: the one fact the browser cannot
   supply, typed once when the project was added. */
.basel-home-pwhere { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.basel-home-pwhere > :first-child { font-weight: 400; color: var(--basel-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-home-pwhere > :last-child  { color: var(--basel-ink-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A sortable column head: a plain button in the eyebrow's clothes, the arrow
   on the one in force. Worst-first is the resting order; this is the session's
   departure from it (ADR-060). */
.basel-home-sorth {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    border: 0;
    padding: 0;
    background: none;
    color: var(--basel-ink-muted);
    text-align: left;
    cursor: pointer;
    font: inherit;
}
.basel-home-sorth:hover, .basel-home-sorth[data-active] { color: var(--basel-ink); }
.basel-home-sorth:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }
.basel-home-pmenu { position: relative; display: inline-flex; justify-content: center; }
.basel-home-pmenu .basel-menu { right: 0; left: auto; }

.basel-home-section { margin-top: 24px; }

/* The head's exclusion line: its own row under the figures, full width, so it
   reads as a sentence rather than as a fifth figure with a middot in front. */
.basel-home-note {
    flex: 1 0 100%;
    margin: 4px 0 0;
    color: var(--basel-ink-muted);
    max-width: 92ch;
}

/* ---- The two register folds (ADR-060) ------------------------------------
   The same chevron the side panel's Internal · PM summary and every group
   header use, on a full-width button that carries the 2px section rule. On
   register 2 this button IS the gate: shut and armed, a click asks the
   confirm; open, it folds. "Hide these" is gone. */
.basel-home-fold {
    display: flex;
    align-items: baseline;
    gap: 10px;
    width: 100%;
    padding: 0 var(--basel-pad-row-x) 10px;
    border: 0;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
    border-radius: var(--basel-radius);
    background: none;
    color: var(--basel-ink-muted);
    text-align: left;
    cursor: pointer;
    font: inherit;
}
.basel-home-fold:hover .basel-home-fold-label { color: var(--basel-ink); }
.basel-home-fold:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }
.basel-home-fold .basel-group-chev { align-self: center; }
.basel-home-fold[aria-expanded="false"] .basel-group-chev svg { transform: rotate(-90deg); }
.basel-home-fold-acts { margin-left: auto; display: flex; flex-wrap: wrap; gap: 8px; align-self: center; }
/* The head's own row when it carries actions the fold must not swallow: the
   fold button and the actions are SIBLINGS in a flex row so a click on Add a
   project never toggles the register. */
.basel-home-foldrow { display: flex; align-items: flex-end; gap: 12px; border-bottom: var(--basel-rule-section) solid var(--basel-ink); margin-top: 28px; }
.basel-home-foldrow > .basel-home-fold { border-bottom: 0; flex: 1 1 auto; width: auto; }
.basel-home-foldrow > .basel-home-fold-acts { padding: 0 var(--basel-pad-row-x) 8px 0; }

/* ---- The Schedule's head, inside a Home block (ADR-060) -------------------
   The hero, the strip and the chips are the Schedule's own classes and rules;
   what follows only fits them into a sheet with row padding. The strip's
   28px top margin is the Schedule's page rhythm and reads as a gap here. */
.basel-home-hero { padding-top: 0; margin-top: 0; border-top: 0; }
.basel-home-open .basel-sched-strip { margin-top: 20px; }
.basel-home-open .basel-sched-chips,
.basel-home-pbody .basel-sched-chips { margin: 18px 0 0; }
.basel-home-open .basel-sched-rowlist,
.basel-home-pbody .basel-sched-rowlist { padding-left: calc(var(--basel-pad-row-x) * 2 + 12px); }
.basel-home-open .basel-sched-rowlist::before,
.basel-home-pbody .basel-sched-rowlist::before { left: calc(var(--basel-pad-row-x) + 5px); }
.basel-home-open .basel-sched-showing,
.basel-home-pbody .basel-sched-showing { padding-left: calc(var(--basel-pad-row-x) * 2 + 12px); }
.basel-home-chips-lead { display: flex; align-items: baseline; gap: 10px; margin-top: 22px; color: var(--basel-ink-muted); }
/* The strip's steps are BUTTONS on the Schedule (they open a phase group) and
   plain divs here — a Home strip has no group under it to open. Same look. */
.basel-home-open .basel-sched-step,
.basel-home-pbody .basel-sched-step { cursor: default; }
/* A row's hero is the head at 40px: the name shares the line with the figure. */
.basel-home-rowhero { display: flex; align-items: baseline; flex-wrap: wrap; gap: 6px 18px; padding-top: 14px; }
.basel-home-rowhero-fig { margin-left: auto; display: inline-flex; align-items: baseline; gap: 10px; }
.basel-home-rowhero-fig .basel-sched-hero-figure { font-size: var(--basel-sched-hero-name-40); }

/* ---- Register 1: the project you have open (ADR-051) --------------------- */

/* A SHEET, NOT A CARD. Same ground and same hairline foot as a row, so it
   reads as the same material one size up rather than as a box §12.1 removed.
   The only thing that separates it from register 2 is the section rule the
   register head carries, which is §12.5's boundary, not a container. */
.basel-home-open {
    background-color: var(--basel-sheet);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    padding: 16px var(--basel-pad-row-x) 18px;
}
/* Where the workbook is and when it was last written — the least urgent facts
   on the block, so they sit under it rather than in the head. */
.basel-home-open-foot {
    padding-top: 12px;
    margin-top: 4px;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}

/* ---- Register 2: every other project ------------------------------------- */

/* ---- The source strip (ADR-052) ------------------------------------------
   WHICH FOLDER THIS LIST COMES FROM, ON THE SUNKEN GROUND. Sunken rather than
   sheet because it is not a row and must not read as the first one: it is the
   sentence the rows below it are read under. It sits between the register head
   and the toolbar — the object before the verbs that act on it, which is the
   whole point of it existing (a verb with no object beside it is a guess). */
.basel-home-source {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px 12px;
    padding: 10px var(--basel-pad-row-x) 11px;
    background-color: var(--basel-sheet-sunken);
}
.basel-home-source > :first-child { color: var(--basel-ink-muted); }
.basel-home-source-name { color: var(--basel-ink); }

/* ---- Register 2's toolbar (ADR-052) --------------------------------------
   NOT §12.9'S TOOLBAR AND IT MUST NEVER BECOME ONE. It carries no
   `[data-toolbar]`, so updateFilterBadge() (core/sort.js) — which counts the
   six logs' controls — never sees it, and no Reset badge exists here to light.
   What states the narrowing is the count line at the end of the row.

   It sits on the sheet with a hairline foot, like the column head under it,
   because it belongs to the rows rather than to the strip above. */
.basel-home-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px 10px;
    padding: 9px var(--basel-pad-row-x);
    background-color: var(--basel-sheet);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
/* The find field is the one control on this screen sized to its content rather
   than to a column, so it states its own width instead of taking `100%`. Since
   ADR-120 it is the ONLY control in this row: the kind segments are gone and
   every attached project is listed. */
.basel-home-find { width: 220px; flex: 0 1 220px; }
/* "Showing 4 of 6" — empty and therefore invisible until something narrows. */
.basel-home-toolbar > #home-showing { margin-left: auto; }

/* The row head's outer grid, and the expander's inner one. */
.basel-home-phead {
    display: grid;
    grid-template-columns: var(--basel-home-phead-cols);
    gap: 12px;
    align-items: center;
    min-width: 0;
    padding: var(--basel-pad-row-y) var(--basel-pad-row-x);
}
.basel-home-prow-main {
    display: grid;
    grid-template-columns: var(--basel-home-prow-cols);
    gap: 16px;
    align-items: center;
    min-width: 0;
}
/* The column head declares BOTH grids, which is the whole reason Open lands in
   a column rather than wherever the row's text happened to end. It sits under
   the register head, so it carries the hairline and not the 2px rule — the
   section boundary is already spent one element above it. */
.basel-thead.basel-home-phead {
    padding: 9px var(--basel-pad-row-x) 9px;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}

/* OPEN IS QUIET ON PURPOSE. It appears on every row, and a verb repeated eight
   times down a column at any weight above this one stops being a control and
   becomes a texture. It is `.basel-act`'s shape at eyebrow size. */
.basel-home-openbtn { padding: 4px 9px; justify-content: center; width: 100%; }

.basel-home-project { background-color: var(--basel-sheet); }

/* A closed project, and a folder that could not be read. Both are rows you are
   not being asked to act on, so both drop to the muted ink — and NEITHER is
   red: §12.3 rations red to "your move", and a finished project and a folder
   with no workbook in it are equally nobody's move. */
.basel-home-project--quiet .basel-home-pname-main { color: var(--basel-ink-muted); }

.basel-home-pname { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.basel-home-pname-main {
    color: var(--basel-ink);
    font-weight: 700;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.basel-home-pname-sub {
    color: var(--basel-ink-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.basel-home-psaved { color: var(--basel-ink-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The one thing --basel-ink-faint may never be is text (§12.2), so a quiet
   phrase takes the muted ink like every other quiet phrase in the app. */
.basel-home-quiet { color: var(--basel-ink-muted); }

/* ---- The expanded project ------------------------------------------------ */

.basel-home-pbody {
    padding: 0 var(--basel-pad-row-x) 18px calc(var(--basel-pad-row-x) + 18px);
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-home-facts {
    display: flex;
    flex-wrap: wrap;
    gap: 6px 32px;
    padding: 14px 0 4px;
}
.basel-home-fact { display: inline-flex; align-items: baseline; gap: 8px; min-width: 0; }
.basel-home-fact > :first-child { color: var(--basel-ink-muted); }

.basel-home-tallies { display: flex; flex-wrap: wrap; gap: 6px 24px; padding: 6px 0 12px; }
.basel-home-tally { display: inline-flex; align-items: center; gap: 7px; }

.basel-home-pactions { padding-top: 14px; }
.basel-home-why { margin: 8px 0 0; color: var(--basel-ink-muted); max-width: 76ch; }

/* ---- The gate ------------------------------------------------------------
   Other projects are not drawn until the PM asks. Since ADR-051 it is REGISTER
   2'S BODY rather than a block after the list: the register head above it
   has already spent the 2px section rule and named the register, so this is
   what stands where the rows would be. It is not red, not amber and not a
   warning panel: it states what the click costs, in the words §12.3 leaves
   available to something that is not a standing. */
.basel-home-shield {
    padding: 18px var(--basel-pad-row-x);
    background-color: var(--basel-sheet-sunken);
}
.basel-home-shield p { margin: 0 0 14px; color: var(--basel-ink); max-width: 76ch; }

/* The sentences that can stand where register 2's rows would be — no project
   matches the search, no kind is selected — take the rows' own left edge. A
   paragraph flush to the screen margin under a column head indented 18px reads
   as belonging to something else. */
#home-projects > .basel-home-why,
#home-other-rows > .basel-home-why { margin: 0; padding: 16px var(--basel-pad-row-x) 4px; }

/* The chevron is its own button now (ADR-051), so it needs its own hover; and
   the row's open state rotates it from the row rather than from a head element
   this screen no longer has. */
.basel-home-phead .basel-row-chev:hover { color: var(--basel-ink); }
.basel-home-project.basel-row--open .basel-row-chev svg { transform: rotate(180deg); }

/* ---- The lower register: who has what, and this week --------------------- */

.basel-home-lower {
    display: grid;
    /* The board's proportions. The week column is fixed because its content is
       a day label and a short sentence and never wants the extra width; the
       people column takes it. Below the stated floor the two stack rather than
       squeezing a 380px column into 200. */
    grid-template-columns: minmax(0, 1fr) 380px;
    gap: 40px;
    margin-top: 36px;
    padding-top: 20px;
    border-top: var(--basel-rule-section) solid var(--basel-ink);
}
@media (max-width: 1180px) {
    .basel-home-lower { grid-template-columns: minmax(0, 1fr); gap: 28px; }
}
.basel-home-col { min-width: 0; }
.basel-home-col > .basel-subhead { display: block; margin-bottom: 10px; }

.basel-home-person { padding-bottom: 16px; }
.basel-home-person + .basel-home-person { padding-top: 16px; border-top: var(--basel-rule-row) solid var(--basel-hair); }
.basel-home-person-head { display: flex; align-items: baseline; flex-wrap: wrap; gap: 4px 10px; }
.basel-home-person-name { color: var(--basel-ink); }
.basel-home-person-count { margin-left: auto; color: var(--basel-ink-muted); white-space: nowrap; }

/* ---- This week -----------------------------------------------------------
   A DAY HEADER ONLY WHERE A DAY CARRIES MORE THAN ONE ENTRY. A single-entry
   day prints its own label in the row's first column, so the header would say
   the same word twice one line apart; a week where every day is a header reads
   as a calendar rather than as a list of what is coming. The empty first cell
   under a header keeps every row on one grid whichever shape it took. */
.basel-home-week-row {
    display: grid;
    grid-template-columns: 54px minmax(0, 1fr);
    gap: 12px;
    align-items: baseline;
    padding: 10px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-home-week-row--under { border-bottom: var(--basel-rule-row) solid var(--basel-hair); }
.basel-home-week-head {
    display: flex;
    align-items: baseline;
    gap: 10px;
    padding: 12px 0 6px;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair-strong);
}
.basel-home-week-day { color: var(--basel-ink-muted); }
.basel-home-week-text { min-width: 0; color: var(--basel-ink-muted); }
/* A dated ITEM goes somewhere; a meeting or a deliverable is a date, not a
   record, and is deliberately not a control. */
.basel-home-week-link {
    display: block;
    border: 0;
    background: none;
    padding: 0;
    text-align: left;
    font: inherit;
    color: inherit;
    cursor: pointer;
}
.basel-home-week-link:hover { color: var(--basel-ink); }

/* WHAT USED TO BE HERE: .basel-side-home, the panel block that held Home as
   its first item. Home is in the identity band since ADR-043 (.basel-band-home,
   above), so the block and its rule are gone. */

/* ============================================================================
   BASEL — DIALOGS AND WALKTHROUGH MODE (DESIGN §12, Phase 4.13)
   ----------------------------------------------------------------------------
   FOURTEEN DIALOGS AND ONE WALKTHROUGH, AND THE POINT IS THAT THEY ARE ONE
   THING. Every dialog in js/core/modals.js is built by one function —
   M.shell() — from the three parts below: a HEAD closed by the 2px ink rule, a
   BODY that scrolls, and an ACTIONS band on the sunken ground. There is no
   per-dialog shell markup left anywhere in the app, and a fifteenth dialog is
   a call to that function.

   The walkthrough is the same shell with a wider cap and a navigation strip in
   its head, because it IS a dialog — what made it look like a separate species
   was forty lines of hand-written v2 chrome, not a different anatomy.

   WHAT IS DELIBERATELY REUSED RATHER THAN RE-INVENTED: .basel-field (the 2px
   left rule and the 11px key), .basel-input / .basel-select / .basel-textarea,
   .basel-act, .basel-btn / .basel-btn--primary, .basel-chip, .basel-linked,
   .basel-empty, and the whole standing vocabulary. A dialog is a screen in a
   box; it is not a second design.

   NO ICON FONT. Every dialog carried a Font Awesome glyph in its title and
   another in every hint paragraph. Basel's hierarchy is type and rules
   (§12.5), and an amber circle-and-glyph over a paper ground is the exact
   mismatch this phase exists to remove. The eyebrow does the job the icon was
   doing — saying what kind of thing this is — in the vocabulary the rest of
   the app already teaches. ------------------------------------------------- */

/* ---- The scrim -----------------------------------------------------------
   NOT `bg-black/60`: §12.2 forbids pure black. This is `basel-ink` at 55%,
   the same ink the identity band and every section rule are drawn in. The
   transparency is on the SCRIM, never on content — §1's "no opacity below
   1.0" is about the legibility of what you are reading, and the scrim is what
   you are reading THROUGH. */
.basel-scrim {
    position: fixed;
    inset: 0;
    z-index: 50;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background-color: rgba(var(--basel-ink-rgb), 0.55);
    /* STATED, NOT INHERITED, and the conformance probe is what found it: the
       dialog layer hangs off <body>, which carries the v2 ink, so every dialog
       root computed to #09090B — a v2 hex sitting on a Basel surface, invisible
       because nothing directly under it inherited far enough to show it. A
       Basel surface names its own ink for the same reason a Basel screen names
       its own ground. */
    color: var(--basel-ink);
}
/* Higher specificity than Tailwind's `.hidden` on purpose: openModal() and
   closeModal() toggle that class, and this rule has to win however the two
   stylesheets end up ordered. */
.basel-scrim.hidden { display: none; }

/* ---- The shell ----------------------------------------------------------- */
.basel-dialog {
    display: flex;
    flex-direction: column;
    width: 100%;
    max-height: 90vh;
    background-color: var(--basel-sheet);
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
}
/* Five caps, so no dialog states an arbitrary width at its call site. */
.basel-dialog--sm   { max-width: 460px; }
.basel-dialog--md   { max-width: 620px; }
.basel-dialog--lg   { max-width: 780px; }
.basel-dialog--xl   { max-width: 1000px; }
.basel-dialog--wide { max-width: min(96vw, 1600px); max-height: 88vh; }

.basel-dialog-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 14px;
    padding: 16px var(--basel-pad-row-x) 12px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
}
/* The eyebrow leads on its own line, the way a screen head's number does. */
.basel-dialog-eyebrow { flex: 0 0 100%; color: var(--basel-ink-muted); }
.basel-dialog-title   { min-width: 0; margin: 0; color: var(--basel-ink); }
.basel-dialog-count   { color: var(--basel-ink-muted); }
.basel-dialog-gap     { flex: 1 1 auto; }
.basel-dialog-nav     { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }

.basel-dialog-body {
    flex: 1 1 auto;
    min-height: 0;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 18px;
    padding: 20px var(--basel-pad-row-x) 22px;
}
.basel-dialog-foot {
    flex: 0 0 auto;
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 8px;
    padding: 12px var(--basel-pad-row-x);
    border-top: var(--basel-rule-row) solid var(--basel-hair);
    background-color: var(--basel-sheet-sunken);
}

/* A note the PM actually reads, never an aside in a smaller size — 13px is
   the floor and there is nothing under it (§12.4). */
.basel-dialog-note { margin: 0; color: var(--basel-ink-muted); }
.basel-dialog-note strong { color: var(--basel-ink); font-weight: 700; }
.basel-dialog-note--ruled { padding-top: 12px; border-top: var(--basel-rule-row) solid var(--basel-hair); }
/* A hairline break inside a dialog body. The grouping dialog uses it to part
   the three key rows from the two settings that bound what they produce. */
.basel-dialog-hair { height: var(--basel-rule-row); background-color: var(--basel-hair); }
.basel-dialog-note--split { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; }

/* Two fields side by side — the dialogs' one layout primitive, the same pair
   the record's field grid uses, at dialog measure. */
.basel-dialog-pair { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 14px; }
@media (max-width: 620px) { .basel-dialog-pair { grid-template-columns: minmax(0, 1fr); } }

/* A strip across the top of a body: a label, a count, sometimes one control. */
.basel-dialog-strip {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    padding-bottom: 12px;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}

/* ---- A flag the PM sets while creating a record --------------------------
   Internal and Blocking are ONE anatomy with two sets of words, which is this
   phase's whole argument in miniature: they were an amber box and a red box,
   and the difference between them was never the colour. Basel has neither
   (§12.2, §12.3's rationing of red), so both read as a 2px rule, an uppercase
   key and a sentence — which is what a .basel-field is, and this is one. */
.basel-dialog-opt {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    padding: 10px 12px;
    border-left: var(--basel-rule-section) solid var(--basel-hair-strong);
    background-color: var(--basel-sheet-sunken);
    cursor: pointer;
}
.basel-dialog-opt-k    { display: block; color: var(--basel-ink); }
.basel-dialog-opt-note { display: block; margin-top: 3px; color: var(--basel-ink-muted); }

/* ---- File pickers --------------------------------------------------------
   The browser's own button is the one solid ink block on the control, same
   direction .basel-btn--primary takes. 13px stated on both halves: a file
   input is one of the places a browser defaults below the floor. */
.basel-file {
    width: 100%;
    padding: 6px 8px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet);
    color: var(--basel-ink-muted);
    font-size: 13px;
    line-height: 1.4;
}
.basel-file::file-selector-button {
    margin-right: 10px;
    padding: 4px 10px;
    border: 0;
    border-radius: var(--basel-radius);
    background-color: var(--basel-ink);
    color: var(--basel-paper);
    font-family: inherit;
    font-size: 13px;
    font-weight: 700;
    cursor: pointer;
}

/* ---- A list inside a dialog ----------------------------------------------
   Recently Deleted, the pre-share check, the template picker and the keyword
   fallback are the same thing four times: a section eyebrow with a mono
   count, then rows separated by hairlines. One anatomy, four callers. */
/* The container a dialog's list renderer writes into. It exists so a dialog
   holding TWO sections — the pre-share check holds records and updates — puts
   the same 18px between them the dialog body puts between its own children,
   rather than butting two 2px head rules together. */
.basel-dlg-stack { display: flex; flex-direction: column; gap: 18px; }
/* Grouped list rows (the template picker, ADR-060): no gap between rows, a
   2px-ruled eyebrow over each group. */
.basel-dlg-stack--tight { gap: 0; }
.basel-dlg-group { margin: 18px 0 6px; padding-bottom: 6px; border-bottom: var(--basel-rule-section) solid var(--basel-ink); color: var(--basel-ink-muted); }
.basel-dlg-group:first-child { margin-top: 0; }
.basel-dlg-empty { padding: 8px 12px 2px; }
/* Two doors (Add a project): equal sheets, the verb at the foot of each. */
.basel-dlg-doors { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px; margin-top: 16px; }
@media (max-width: 640px) { .basel-dlg-doors { grid-template-columns: minmax(0, 1fr); } }
.basel-dlg-door { display: flex; flex-direction: column; gap: 8px; padding: 16px 18px; background-color: var(--basel-sheet); border: var(--basel-rule-row) solid var(--basel-hair-strong); border-radius: var(--basel-radius); }
.basel-dlg-door-t { color: var(--basel-ink); font-weight: 600; }
.basel-dlg-door-d { margin: 0; color: var(--basel-ink-muted); }
.basel-dlg-door-act { margin-top: auto; padding-top: 6px; }
.basel-linkbtn { border: 0; padding: 0; background: none; color: var(--basel-ink); text-decoration: underline; cursor: pointer; font: inherit; }
.basel-linkbtn:hover { color: var(--basel-working-ink); }
/* Key · value facts inside a dialog (Where does this live, Save as template). */
.basel-dlg-facts { display: grid; grid-template-columns: 130px minmax(0, 1fr); gap: 10px 16px; align-items: baseline; margin: 14px 0; }
.basel-dlg-facts > .text-basel-eyebrow { color: var(--basel-ink-muted); }
.basel-dlg-check { display: inline-flex; align-items: center; gap: 8px; color: var(--basel-ink); cursor: pointer; }
.basel-dlg-sec { display: flex; flex-direction: column; }
.basel-dlg-sec-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding-bottom: 7px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
    color: var(--basel-ink-muted);
}
.basel-dlg-row {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    width: 100%;
    text-align: left;
    padding: 10px 0;
    border: 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    border-radius: var(--basel-radius);
    background-color: transparent;
    color: var(--basel-ink);
}
button.basel-dlg-row { cursor: pointer; }
button.basel-dlg-row:hover { background-color: var(--basel-sheet-sunken); }
button.basel-dlg-row:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }
.basel-dlg-row-main { flex: 1 1 auto; min-width: 0; }
.basel-dlg-row-line { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.basel-dlg-row-id   { color: var(--basel-ink); font-weight: 700; }
.basel-dlg-row-log  { color: var(--basel-ink-muted); }
.basel-dlg-row-when { color: var(--basel-ink-muted); }
.basel-dlg-row-desc { display: block; margin-top: 3px; color: var(--basel-ink-muted); }
.basel-dlg-row-acts { display: inline-flex; align-items: center; gap: 6px; flex: 0 0 auto; }

/* ---- The two-panel column manager ---------------------------------------
   Available on the left, active on the right. It had eight class names and
   not one stylesheet rule anywhere in the app — it has been rendering
   unstyled since it shipped — so this is the first anatomy it has ever had. */
.basel-cols { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 18px; }
@media (max-width: 720px) { .basel-cols { grid-template-columns: minmax(0, 1fr); } }
.basel-col-panel { display: flex; flex-direction: column; min-width: 0; }
.basel-col-panel-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 8px;
    padding-bottom: 7px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
    color: var(--basel-ink-muted);
}
/* 420px, not 340px: a 44px handle raises the row height, and at the old
   max-height two of the rows in a full list fell below the fold of a panel that
   is scrolled with the same pointer the drag uses. */
.basel-col-panel-body { max-height: 420px; overflow-y: auto; }
.basel-col-row {
    display: flex;
    align-items: center;
    gap: 8px;
    min-height: 44px;
    padding: 7px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
/* THE HANDLE IS LAID OUT INLINE HERE, not stacked. A column row is one short
   line, so a 44px column of rules-over-word would be taller than everything
   beside it; an Agenda Prep section runs from 60px to 400px and stacks the
   handle under the number instead. Same component, same target, two rows. */
/* (.basel-col-row's labelled-grip override is gone with the label — ADR-047.) */
.basel-col-name { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--basel-ink); }
.basel-col-empty { padding: 18px 0; color: var(--basel-ink-muted); }
.basel-col-row .basel-ctl--select { flex: 0 0 auto; }

/* ---- The site-readiness rows -------------------------------------------- */
.basel-site-row {
    display: grid;
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr) minmax(0, 1.3fr) auto;
    gap: 12px;
    align-items: center;
    padding: 9px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
@media (max-width: 820px) { .basel-site-row { grid-template-columns: minmax(0, 1fr); } }
.basel-site-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--basel-ink); font-weight: 700; }
.basel-site-acts { display: inline-flex; align-items: center; gap: 8px; justify-self: end; }
/* Auto vs Manual is a FACT about the row, not a standing — so it reads the way
   the internal mark does (§12.14): an uppercase eyebrow behind a hairline,
   quiet, and never one of the four shapes. */
.basel-site-flag {
    padding: 2px 7px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    color: var(--basel-ink-muted);
    white-space: nowrap;
}
.basel-site-flag[data-manual] { border-color: var(--basel-ink); color: var(--basel-ink); }

/* ---- Walkthrough Mode (ADR-016) -----------------------------------------
   The same shell with a wider cap. What is INSIDE it is built by the same
   functions the log row builds an expanded record from, which is what makes
   the two incapable of disagreeing about a record's standing. */
.basel-wt { display: flex; flex-direction: column; gap: 18px; }
.basel-wt-idline { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.basel-wt-mark   { display: inline-flex; align-items: center; }
.basel-wt-id     { color: var(--basel-ink); }
.basel-wt-gap    { flex: 1 1 auto; }
/* The headline, at the same 19/700 over the same 2px ink rule the expanded
   record's own headline takes (§12.9). It is a <p> and not a textarea: the
   walkthrough SHOWS a record and the log EDITS it, and a control that looked
   typeable and was not would be the .basel-field lie one screen over. */
.basel-wt-desc {
    margin: 0;
    padding-bottom: 10px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
    color: var(--basel-ink);
}
.basel-wt-sec  { border-top: var(--basel-rule-row) solid var(--basel-hair); padding-top: 14px; }
.basel-wt-foot {
    flex: 0 0 auto;
    padding: 9px var(--basel-pad-row-x);
    border-top: var(--basel-rule-row) solid var(--basel-hair);
    background-color: var(--basel-sheet-sunken);
    color: var(--basel-ink-muted);
    text-align: center;
}
/* A disabled navigation control. NOT opacity (§1) — a lighter VALUE, and
   `basel-ink-faint` is precisely what §12.2 keeps that hex for: a disabled
   glyph, nothing a client has to read. */
.basel-act[disabled] { color: var(--basel-ink-faint); border-color: var(--basel-hair); cursor: default; }
.basel-act[disabled]:hover { color: var(--basel-ink-faint); border-color: var(--basel-hair); }

/* ==========================================================================
   BASEL SETTINGS SCREEN (DESIGN §12.20, ADR-041) — Phase 4.20
   --------------------------------------------------------------------------
   The last panel to convert, and the one that emptied the v2 well list. It is
   a FORM, which is the one shape §12.9's log anatomy has nothing to say about:
   no records, no standings, no groups, nothing to expand. What it does have is
   ~40 controls across three sub-tabs, so everything below is about keeping a
   dense form legible without a single box.

   THREE RULES CARRIED THE WHOLE CONVERSION:
     - A section boundary is a 2px ink rule (§12.5), never a card. The v2 panel
       nested three deep — a white card holding a bordered well holding a
       bordered list item — which is exactly the box-inside-a-box §12.1 removes.
     - A list ROW is a 1px hairline and a grid, the same anatomy
       .basel-roll-row and .basel-sched-ms already use. There is one row
       grammar in this app; this screen does not get a second.
     - A count, an ID, a date and a version number are mono (§12.8 #3). Every
       "N Items" badge on the old panel was a rounded zinc pill; it is a mono
       figure now, which is what the rest of the app already does.

   NO STANDING SHAPES ARE DRAWN HERE, and that is decided rather than missing.
   A master-list entry, a holiday and a roster member are not anybody's move —
   both halves of baselStandingOf() are silent, exactly as they are on
   Communications (§12.17). File Health's three states were tone-coloured
   banners (emerald / amber / red); they are an uncoloured .basel-flag eyebrow
   and prose now, the identical call §12.18 finding 4 made about the countdown.
   §12.3 rations red to "whose move is it", and a file written by a newer app
   version does not answer that question.
   ========================================================================== */

/* ---- Sub-tab bodies ------------------------------------------------------ */

.settings-subtab-content { display: block; }
.settings-subtab-content.hidden { display: none; }

/* ---- Sections ------------------------------------------------------------ */

.basel-set-sec { padding-top: 26px; min-width: 0; }
.basel-set-sec-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px 16px;
    padding-bottom: 10px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
}
.basel-set-sec-title { margin: 0; color: var(--basel-ink); }
.basel-set-sec-note  { margin: 0; color: var(--basel-ink-muted); flex: 1 1 240px; }
.basel-set-note      { margin: 10px 0 0; color: var(--basel-ink-muted); max-width: 860px; }
.basel-set-note strong { font-weight: 700; color: var(--basel-ink); }

/* A SECOND LEVEL OF HEADING INSIDE ONE SECTION (ADR-160). Saved setups has two
   groups that are not two sections — they are two PLACES the same parcel can
   live, and a 2px ink rule between them would read as two subjects. So: the
   eyebrow the rest of this screen already uses for a column label, a sentence
   beside it, and a hairline. Anything needing its own 2px rule is a section. */
.basel-set-subhead {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 14px;
    margin-top: 28px;
    padding-bottom: 8px;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair-strong);
}
.basel-set-subhead-title { margin: 0; color: var(--basel-ink); }
.basel-set-subhead-note  { margin: 0; color: var(--basel-ink-muted); flex: 1 1 240px; }

/* ---- The field grid. Same anatomy as .basel-prep-fields — a 2px left rule,
   an 11px uppercase key, the control — and for the same reason: nothing here
   commits through commitLogCardField(), so it is not a .basel-field. -------- */

.basel-set-fields {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
    gap: 18px;
    margin-top: 16px;
}
.basel-set-field { border-left: var(--basel-rule-section) solid var(--basel-hair-strong); padding-left: 12px; min-width: 0; }
.basel-set-field-k { display: block; color: var(--basel-ink-muted); margin-bottom: 5px; }
.basel-set-field-note { margin: 5px 0 0; color: var(--basel-ink-muted); }
.basel-set-field .basel-input,
.basel-set-field .basel-select { width: 100%; }

/* ADR-109: a field validation message, drawn by showFieldNote() (core/toast.js)
   immediately under the input it corrects. Ink, never red (§12.3 rations red
   to standings) — a blank required field is not a standing.
   ITS OWN NAME (ADR-149): this rule used to be a second `.basel-field-note`,
   and coming later in the file it overrode the muted hint of that name above
   (the one every log field's help line uses) and painted all of them in ink. */
.basel-field-problem { display: block; margin: 5px 0 0; color: var(--basel-ink); }

/* ---- The four master lists, side by side. Each column is opened by a 2px ink
   rule rather than closed by a border box: depth reads by rule and register. */

.basel-set-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
    gap: 26px;
    margin-top: 18px;
}
.basel-set-col { min-width: 0; }
/* "Used by ..." under a master list's heading (ADR-159): the same muted note
   the section heads carry, so the line reads as the list's own caption. */
.basel-set-col-note { margin: 6px 0 0; color: var(--basel-ink-muted); }
/* One status word per row: the app's own word, then the PM's box. */
.basel-set-list--words .basel-set-item { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 12px; align-items: center; }
/* The app's own word wraps rather than ellipsises: it is the thing the PM is
   renaming, so it has to be readable in full. */
.basel-set-list--words .basel-set-cell { overflow: visible; white-space: normal; }
.basel-set-col-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    padding-bottom: 8px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
}
.basel-set-col-title { margin: 0; color: var(--basel-ink-muted); }
.basel-set-count     { color: var(--basel-ink-muted); white-space: nowrap; }

/* WHAT USED TO BE HERE: `.basel-set-add`, the boxed add row under a list, and
   `.basel-set-addrow`, the roster's six-control variant of it. Both are
   DELETED with the eight forms they styled (ADR-090). A row is added in a
   DRAFT ROW of the list itself now — the same columns and the same quiet
   fields — so there is no second grammar left to style. */

/* ---- List rows ----------------------------------------------------------- */

.basel-set-list { margin: 0; padding: 0; list-style: none; max-height: 330px; overflow-y: auto; }
/* EVERY SETTINGS LIST DRAGS (2.5.0, DESIGN §12.21). The handle is the first
   cell — Tier 0, its own place, never inside the verb group — so the row is
   grip · name · verbs. A row with a handle carries .basel-reorder-row and the
   lifted / shifted styles the two older drag lists already wear. */
/* THE LIST ROW (ADR-047): handle · content · attribute · menu. 34px, a
   hairline above, nothing boxed on the row. The handle is the first cell and
   the three dots the last; the verbs live under the dots. A row whose
   entries carry an attribute (a company's ours / theirs, a holiday's date)
   gets one fixed cell for it before the menu. Holidays sort by date and do
   not drag, so their first cell is the date. */
.basel-set-item {
    display: grid;
    grid-template-columns: 24px minmax(0, 1fr) 24px;
    gap: 10px;
    align-items: center;
    min-height: 34px;
    padding: 0 0 0 4px;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-set-item--party  { grid-template-columns: 24px minmax(0, 1fr) 96px 24px; }
/* A meeting series (ADR-083): grip · name · prefix · next · hours · client · dots. */
/* The fourth track is the USUAL TIME (ADR-146), 104px because a time field
   reads "01:30 PM" and its clock; it was 60px for a two-digit next number. */
.basel-set-item--series { grid-template-columns: 24px minmax(0, 1fr) 72px 104px 60px 108px 44px 24px; }
.basel-set-row.basel-set-item--series { min-height: 30px; padding: 0 0 0 4px; gap: 10px; }
.basel-set-tick { display: inline-flex; align-items: center; justify-content: center; }
.basel-set-item--date   { grid-template-columns: 118px minmax(0, 1fr) 24px; padding-left: 8px; }
/* A VALUE IS A QUIET FIELD (ADR-050, list-row editing option A). The cell is
   the input: at rest it reads as the text it holds; hover draws a hairline
   under it; focus draws the ink under it and the sheet behind it. Leaving it
   saves. No box, no button, no hint — nothing on the row changes shape. */
.basel-set-quiet {
    display: block;
    width: 100%;
    min-width: 0;
    height: 26px;
    margin: 0;
    padding: 0 4px;
    border: 0;
    border-bottom: 1px solid transparent;
    background: none;
    font: inherit;
    color: var(--basel-ink);
    text-overflow: ellipsis;
}
.basel-set-quiet:hover { border-bottom-color: var(--basel-hair-strong); }
.basel-set-quiet:focus { outline: 0; border-bottom-color: var(--basel-ink); background: var(--basel-sheet); }
.basel-set-quiet::placeholder { color: var(--basel-ink-faint); }
.basel-set-quiet--strong { font-weight: 600; }
/* The quiet list's dropdowns wear the drawn caret too, always — not only on
   hover — so a cell that opens a list can be told from one that is typed in
   (ADR-158, option B1). Speaks-for's summary is a list as well and says so. */
.basel-set-quiet--select,
.basel-set-multi > summary.basel-set-quiet {
    padding-left: 0;
    padding-right: 18px;
    cursor: pointer;
    appearance: none;
    -webkit-appearance: none;
    background-image: var(--basel-select-caret);
    background-position: calc(100% - 11px) 50%, calc(100% - 7px) 50%;
    background-size: 4px 4px, 4px 4px;
    background-repeat: no-repeat;
}
.basel-set-multi > summary.basel-set-quiet { padding-left: 4px; }
/* A name with its role mark beside it (a company): the field takes the room, the mark keeps its own. */
.basel-set-quiet-wrap { display: flex; align-items: center; min-width: 0; }
.basel-set-quiet-wrap .basel-set-quiet { flex: 1 1 auto; width: auto; }
.basel-set-quiet-wrap .basel-set-item-mark { flex: 0 0 auto; }
/* "this firm" / "client" beside a company's name (ADR-048): an eyebrow in the
   working ink, the same pairing the Manage summary wears. */
.basel-set-item-mark { margin-left: 8px; color: var(--basel-working-ink); }
.basel-set-item .basel-sched-rec-acts { position: relative; justify-self: end; }
/* INSIDE A MASTER-LIST COLUMN THE ROW STACKS, AND THAT WAS MEASURED RATHER
   THAN PREFERRED. Four columns at a 1600px viewport leave each list 300px, and
   the four verbs take 182px of it — which left the NAME 93px on a list whose
   longest seeded entry is 49 characters, so every second building ellipsised
   to "Frank G. Well…". Stacked, the name takes the full 285px and the verbs
   sit under it. The full-width lists (responsible parties, holidays) keep the
   one-line grid: they have the room and measure fine. */
/* (The stacked two-line row inside a master-list column is gone with the verbs
   that forced it — ADR-047. The name has the whole width but 58px now.) */
.basel-set-item-name {
    color: var(--basel-ink);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.basel-set-item-when { color: var(--basel-ink-muted); white-space: nowrap; }
.basel-set-acts { display: inline-flex; align-items: center; gap: 5px; justify-self: end; }
.basel-set-empty { padding: 9px 0; color: var(--basel-ink-muted); border-top: var(--basel-rule-row) solid var(--basel-hair); }

/* ---- Wide rows: the nav-tab table, the team roster, the file-health tally.
   One grammar, three column templates — declared per table on the wrapper so
   the head and its rows can never drift apart. */

.basel-set-table { display: block; margin-top: 4px; min-width: 0; overflow-x: auto; }
.basel-set-row {
    display: grid;
    gap: 12px;
    align-items: center;
    padding: 7px 0;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-set-row--head { border-top: 0; border-bottom: var(--basel-rule-section) solid var(--basel-ink); }
.basel-set-row--head > span { color: var(--basel-ink-muted); }
.basel-set-table--nav    .basel-set-row { grid-template-columns: 24px 36px minmax(0, 1.4fr) minmax(0, 1fr) 150px; min-height: 34px; padding: 0 0 0 4px; }
.basel-set-num { color: var(--basel-waiting-ink); }
.basel-set-num--none { color: var(--basel-ink-faint); font-weight: 400; }
/* The quiet add line (ADR-047): a place to start typing, not a button. */
.basel-set-reveal { display: block; padding: 8px 0 8px 38px; border: 0; background: none; color: var(--basel-ink-muted); text-align: left; font: inherit; cursor: pointer; }
.basel-set-reveal:hover, .basel-set-reveal:focus-visible { color: var(--basel-ink); outline: 0; }
.basel-set-reveal[hidden] { display: none; }
.basel-set-table--roster .basel-set-row { grid-template-columns: minmax(0, 1.1fr) minmax(0, 1fr) minmax(0, 1fr) minmax(0, 1.2fr) 118px 116px 24px; min-height: 34px; padding: 0; }
/* A SAVED SETUP (ADR-160): what it is on the left, what you can do with it on
   the right. Two lines in the first cell rather than two columns, because the
   line under the name is a SENTENCE of variable length (the summary the picker
   draws too) and a column would cut it — the same reason ADR-136 gives for the
   record row. No max-height and no scroll: a PM has a handful of these, and a
   list that scrolls inside a page that scrolls is the thing §12.20 already
   regrets on the master lists. */
.basel-set-table--setups .basel-set-row { grid-template-columns: minmax(0, 1fr) auto; align-items: start; padding: 12px 0; }
.basel-set-setup-name { color: var(--basel-ink); }
.basel-set-setup-desc { margin: 2px 0 0; color: var(--basel-ink-muted); }
.basel-set-setup-acts { display: inline-flex; align-items: center; gap: 6px; justify-self: end; }
/* THE DRAFT ROW (ADR-090). A row of the list, marked as not yet real: the
   sheet behind it and a 2px ink rule at its left edge — the same weight a
   section boundary takes, drawn as an inset shadow so it costs the grid no
   column and the cells stay under the headings they belong to. Only the FIRST
   cell takes the extra lead; nudging every cell would shift the whole row off
   the columns above it.

   TWO SELECTORS BECAUSE THERE ARE TWO ROW SHAPES on this screen and both take
   a draft: `.basel-set-row` is the wide grid (the roster), `.basel-set-item`
   the list row (the four master lists, Companies, Holidays, Meetings). One
   declaration block, so they cannot drift apart. */
.basel-set-row--draft,
.basel-set-item--draft { background-color: var(--basel-sheet); box-shadow: inset 2px 0 0 var(--basel-ink); }
.basel-set-row--draft > :first-child .basel-set-quiet,
.basel-set-item--draft > :nth-child(2) .basel-set-quiet { padding-left: 8px; }
/* A cell that holds no value yet and no control either — the roster draft's
   Speaks-for, which cannot answer for a person who does not exist. */
.basel-set-quiet--none { color: var(--basel-ink-faint); }
/* WHO SPEAKS FOR A DISCIPLINE (ADR-084) is a `.basel-multi` popover wearing
   the quiet field's own summary, because every row on this list is one row
   high and a native multi-select listbox is not (ADR-027, ADR-050). */
.basel-set-multi { min-width: 0; }
.basel-set-multi > summary.basel-set-quiet { cursor: pointer; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-set-multi .basel-multi-panel { right: 0; left: auto; max-height: 240px; overflow-y: auto; }
.basel-set-table--tally  .basel-set-row { grid-template-columns: minmax(0, 1fr) 96px 156px 156px; }
.basel-set-cell {
    color: var(--basel-ink-muted);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.basel-set-cell--strong { color: var(--basel-ink); font-weight: 600; font-size: 13px; }
.basel-set-cell--num { text-align: right; color: var(--basel-ink); }
.basel-set-cell--quiet { color: var(--basel-ink-muted); }

/* ---- File Health (ADR-021 — it reports, it never writes) ----------------- */

.basel-set-health { display: flex; flex-direction: column; gap: 14px; margin-top: 16px; }
.basel-set-health-state { border-left: var(--basel-rule-section) solid var(--basel-ink); padding-left: 12px; }
.basel-set-health-detail { margin: 6px 0 0; color: var(--basel-ink-muted); max-width: 860px; }
.basel-set-health-figs { display: flex; flex-wrap: wrap; gap: 6px 24px; color: var(--basel-ink-muted); }
.basel-set-health-figs strong { color: var(--basel-ink); font-weight: 700; }

/* THE "WHAT TO DO" STEPS (ADR-076). Deliberately NOT .basel-set-item: that is
   the settings LIST row — a 24/1fr/24 grid, 34px tall, vertically centred, for
   a name and its verbs. These are sentences of prose, and the middle cell of a
   row built for a name would centre a three-line sentence against a step
   number and clip nothing but read wrong. Hairlines and a mono figure, which is
   the same vocabulary, at the size prose actually needs. */
.basel-set-steps { margin: 8px 0 12px; padding: 0; list-style: none; }
.basel-set-step {
    display: grid;
    grid-template-columns: 24px minmax(0, 1fr);
    gap: 10px;
    align-items: start;
    padding: 9px 0;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
    max-width: 860px;
}
.basel-set-step-n { color: var(--basel-ink-muted); }

/* ADR-109: the session's own log of failures nobody clicked toward — never a
   toast, only ever read here. A hairline per line, the same vocabulary as the
   steps above; nothing here is coloured. */
.basel-set-problems-list { margin: 8px 0 0; padding: 0; list-style: none; max-width: 860px; }
.basel-set-problems-list li { display: flex; gap: 10px; padding: 6px 0; border-top: var(--basel-rule-row) solid var(--basel-hair); }

/* ---- Actions bar under a section (templates, MS Project, the key) -------- */

.basel-set-acts-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 14px; }
/* A file input has no Basel appearance worth fighting for, so the control is a
   <label> shaped exactly like the button beside it with the input inside it,
   visually gone. */
.basel-set-file { position: relative; }
.basel-set-file input[type="file"] { position: absolute; width: 1px; height: 1px; opacity: 0; pointer-events: none; }

/* The key row: the field, its reveal, the model and the two verbs. The reveal
   is a WORD, not an eye glyph — Basel loads no icon font. */
.basel-set-key { display: flex; flex-wrap: wrap; align-items: stretch; gap: 6px; margin-top: 16px; }
.basel-set-key-field { display: flex; align-items: stretch; gap: 6px; flex: 1 1 320px; min-width: 0; }
.basel-set-key-field .basel-input { flex: 1 1 auto; }
.basel-set-sync {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 10px;
    margin-top: 16px;
    padding-top: 14px;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-set-sync-state { color: var(--basel-ink-muted); min-width: 0; }

/* ---- Your AI instructions (ADR-153) ---------------------------------------
   Thirteen boxes: the standing one, one per feature, and under each feature's
   Advanced fold the app's own instruction in full. Every box is a
   `.basel-set-field` — the 2px left rule and the 11px key — because that is
   what a labelled value on this screen already looks like; no second field
   anatomy. The fold is a <details> that opens on a CLICK (ADR-116's rule for
   anything that opens on this app's screens), its chevron the `.basel-fold-chev`
   every other fold turns. The warning above extraction's text is INK, never
   red: §12.3 rations red to standings, and a format note is not anybody's move. */
.basel-ai-field { margin-top: 16px; max-width: 860px; }
.basel-ai-fields { margin-top: 6px; }
.basel-ai-box { display: block; min-height: 44px; }
.basel-ai-base { min-height: 220px; margin-top: 8px; }
.basel-ai-adv { margin-top: 6px; }
.basel-ai-adv-sum {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 0;
    color: var(--basel-ink-muted);
    cursor: pointer;
    list-style: none;
}
.basel-ai-adv-sum::-webkit-details-marker { display: none; }
.basel-ai-adv-sum:hover { color: var(--basel-ink); }
.basel-ai-adv-sum:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }
.basel-ai-adv[open] > .basel-ai-adv-sum { color: var(--basel-ink); }
.basel-ai-adv[open] .basel-fold-chev { transform: rotate(0deg); }
.basel-ai-adv-sum .basel-set-item-mark { margin-left: 0; }
.basel-ai-warn {
    margin: 6px 0 0;
    padding-left: 10px;
    border-left: var(--basel-rule-section) solid var(--basel-ink);
    color: var(--basel-ink);
    max-width: 860px;
}
.basel-ai-warn strong { font-weight: 700; }

/* ---- The MS Project import review -----------------------------------------
   THESE DRESS THE SCHEDULE SCREEN, NOT THIS ONE (ADR-095). The review moved
   out of Tab 08 and became the Schedule's third state, beside Mapping, and the
   class names moved with it: they were `basel-set-*` while they lived here,
   which after the move would have been a name claiming the wrong screen. They
   are left in this file rather than chased into the Schedule block because the
   `.basel-set-mline` above is still shared with three Settings sections and
   splitting the rule across two regions helps nobody.

   The two tables are the one place either screen keeps a real <table>, because
   the column widths are the PM's own (getColumnPrefs / startColumnResize,
   core/columns.js) and a grid cannot carry a per-column drag handle. */

.basel-msp-scroll {
    max-height: 60vh;
    overflow: auto;
    border: var(--basel-rule-row) solid var(--basel-hair);
    background-color: var(--basel-sheet);
    margin-top: 6px;
}
.basel-msp-table { width: 100%; border-collapse: collapse; table-layout: fixed; text-align: left; }
.basel-msp-thead { position: sticky; top: 0; z-index: 1; background-color: var(--basel-sheet-sunken); }
/* A COLUMN HEAD WRAPS RATHER THAN RUNNING INTO ITS NEIGHBOUR. These cells take
   a per-column px width the PM has dragged, and they were `nowrap` with nothing
   clipping them: at every width measured, "Track" (48px of text in a 47px cell)
   and "Baseline Finish" (123px in 125px, less 16px of padding) spilled into the
   head beside them and read as TRACKLABEL and BASELINE FINISHFINISH. It is
   older than the move to this screen — it was worse in the narrower Settings
   column, where those cells were 36px and 96px — and only became worth fixing
   when the review stopped being a thing nobody looked at (ADR-095).
   Wrapping alone is the fix: "Baseline Finish" takes two lines, and the two
   columns whose HEAD was wider than any value in them were widened instead
   (core/columns.js). A word is never broken mid-way — `overflow: hidden` is the
   last-resort clip, and it cannot reach the resize handle, which is absolutely
   positioned at right:0 INSIDE this box. Bottom alignment keeps a one-line head
   on the same baseline as a two-line one. */
.basel-msp-th {
    position: relative;
    padding: 7px 8px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
    color: var(--basel-ink-muted);
    vertical-align: bottom;
    overflow: hidden;
}
.basel-msp-th--c { text-align: center; }
.basel-msp-row { border-top: var(--basel-rule-row) solid var(--basel-hair); }
.basel-msp-row--new { background-color: var(--basel-sheet-sunken); }
.basel-msp-row--missing { color: var(--basel-ink-muted); }
.basel-msp-cell { padding: 3px 8px; color: var(--basel-ink-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-msp-cell--c { text-align: center; }
.basel-msp-cell .basel-input { padding: 2px 6px; }
.basel-msp-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 8px; margin-top: 16px; }
.basel-msp-head-title { color: var(--basel-ink-muted); }
.basel-msp-filter { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; padding: 12px 0; border-bottom: var(--basel-rule-row) solid var(--basel-hair); }
.basel-msp-filter .basel-input { flex: 1 1 220px; }
.basel-msp-check { display: inline-flex; align-items: center; gap: 6px; color: var(--basel-ink-muted); white-space: nowrap; }
.basel-msp-foot { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 8px; margin-top: 14px; padding-top: 12px; border-top: var(--basel-rule-row) solid var(--basel-hair); }
.basel-set-mline { margin: 8px 0 0; color: var(--basel-ink-muted); }
/* The review's own line and action cluster. Same drawing as the two
   Settings rules they were forked from, under a name that is true on the
   screen they are actually on. */
.basel-msp-line { margin: 8px 0 0; color: var(--basel-ink-muted); }
.basel-msp-acts { display: inline-flex; align-items: center; gap: 5px; }

/* ---- The foot. Reset is the most destructive action in the app and it does
   not sit beside Save: the screen head takes the one primary verb, and the
   thing that replaces every master list gets its own rule, its own sentence
   and the destructive ink. It still asks first, with the list of what it
   replaces (confirmResetSettingsToDefault, modules/settings.js — ADR-007). */

.basel-set-foot {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    margin-top: 34px;
    padding-top: 14px;
    border-top: var(--basel-rule-section) solid var(--basel-ink);
}
.basel-set-foot-note { margin: 0; flex: 1 1 380px; color: var(--basel-ink-muted); }

/* ==========================================================================
   REORDERING (DESIGN §12.21) — one mechanism, two surfaces
   --------------------------------------------------------------------------
   Every list a PM can put in their own order goes through core/reorder.js:
   the ↑ / ↓ pair, the live announcement, the Undo toast and the focus restore.
   Two of those lists — the Agenda Prep sections and the log column manager —
   also take a pointer drag, which is the .basel-grip half below.
   ========================================================================== */

/* The live region. It is a direct child of the shell's two-column grid and
   must not open an implicit row, which is exactly what `position: absolute`
   buys: an absolutely-positioned grid item is out of flow. Not
   `display: none` and not `visibility: hidden` — either one takes it out of
   the accessibility tree and it would announce nothing. */
.basel-sr {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    border: 0;
    overflow: hidden;
    white-space: nowrap;
    clip-path: inset(50%);
}

/* The keyboard has to be able to SEE where it is, on the one control whose
   whole job is to be pressed several times in a row. Same 2px inset ink rule
   as the nine other Basel focus rules — a control that moves its own box on
   focus would shuffle the list under the pointer. */
.basel-act:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }

/* A reorder button at the end of its list. Identical to `.basel-act[disabled]`
   above in every declaration and different in the one thing that matters: an
   aria-disabled button is still focusable, so the top row's ↑ stays in the tab
   order and the focus restore has something to land on. The `[disabled]` rules
   stay — the MS Project review and other screens still use them. */
.basel-act[aria-disabled="true"] { color: var(--basel-ink-faint); border-color: var(--basel-hair); cursor: default; }
.basel-act[aria-disabled="true"]:hover { color: var(--basel-ink-faint); border-color: var(--basel-hair); }

/* ---- The drag handle ------------------------------------------------------
   Three 1px rules in a 12px square: the one mark in this system that means
   "this can be picked up", drawn the way every other affordance here is drawn
   — a rule, not a glyph, because Basel loads no icon font.

   `touch-action: none` IS DECLARED HERE AND NEVER SET IN JS. Without it the
   browser claims the gesture for scrolling before the first pointermove
   arrives, and on a touch screen the handle silently does nothing — which is
   the failure mode the HTML5 implementation had for its whole life. */

/* THE HANDLE IS SIX DOTS (ADR-047). One 24px cell, the same on every list in
   the app: muted at rest, ink on hover, focus and while its row is lifted.
   No word, no box, no border colour — a mark drawn in the colour of edges
   read as an edge, which is why the last handle carried the word DRAG. */
.basel-grip {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    height: 24px;
    padding: 0;
    border: 0;
    border-radius: var(--basel-radius);
    background-color: transparent;
    color: var(--basel-ink-muted);
    cursor: grab;
    touch-action: none;
}
.basel-grip-face { display: block; fill: currentColor; }
.basel-grip:hover, .basel-grip:focus-visible, .basel-reorder-row[data-lifted] .basel-grip { color: var(--basel-ink); }
.basel-grip:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }

/* ---- THREE WEIGHTS, AND A PLACE IS NOT A CONTROL (DESIGN §12.21) ----------
   Every control is drawn at the weight of its CONSEQUENCE. Tier 2 is the
   ordinary `.basel-act`: a hairline box and full ink, for anything that changes
   what the client receives, what the app stores or what another screen reads.
   Tier 1 is the rule below: a bare word, muted, for a control that changes only
   what this PM is looking at and un-does itself by being pressed again. Tier 0
   is not a control at all — the grip — and it is the block after that.

   Today Tier 1 is Collapse / Expand and nothing else in the app. It keeps the
   `.basel-act` box model (padding, type, focus ring) and gives up only the
   border and the background, so a row of mixed weights still sits on one
   baseline. */
.basel-act--quiet {
    border-color: transparent;
    background: none;
    padding-left: 2px;
    padding-right: 2px;
}
.basel-act--quiet:hover { border-color: transparent; color: var(--basel-ink); text-decoration: underline; }
/* The focus ring goes OUTSIDE, not inset: there is no border to sit inside, so
   an inset ring would print through the word itself. */
.basel-act--quiet:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }

/* WHAT WAS HERE: `.basel-prep-acts-div`, one hairline between the two weights
   on the Agenda Prep section row. Both weights are gone (ADR-078) — the row
   carries a tick and a chevron and no verb at all — so the divider that said
   "this group is two things" has nothing left to divide. */

/* ---- TIER 0: the grip --------------------------------------
   SIX DOTS, 2x3, IN A 24px BOX, AND NO WORD. It was three hairlines drawn in
   --basel-hair-strong, which is the colour of every divider and every button
   border in this app: a mark drawn in the colour of edges reads as an edge, and
   the PM who owns this app did not know the row could be dragged. The dots are
   the muted ink, going to full ink on hover, focus and while the row is lifted.

   THE WORD DRAG WENT WITH THE HAIRLINES (ADR-047), and this comment said
   otherwise for six phases — as did the comment above baselReorderGripHtml()
   and DESIGN §12.21, which also claimed a 44px target this rule has never
   declared, and a BACKLOG item turned that into a false WCAG 2.5.5 compliance
   claim. Four stale descriptions of one handle is how the wrong handle ends up
   in the next thing anybody draws, which is exactly what happened in the
   design session behind ADR-078. The rule below is the only authority: read it,
   not the prose. Colour is the hover/focus channel rather than a box, because a
   box would make it a button again and Tier 0 is a place.

   (.basel-grip--labelled and .basel-grip-word are gone with the word. Every
   list draws the same six dots, out of one function.) */

/* ---- A list that can be reordered by pointer ------------------------------
   Only two lists carry this: the Agenda Prep sections and the log column
   manager (DESIGN §12.21). `position: relative` is what the drop rule is
   positioned against. */

.basel-reorder-list { position: relative; }

/* The lifted row. An INSET rule, never a drop shadow: §12.1 removes shadows
   and the smoke guard rejects any that is not inset — the same reason the
   internal mark is drawn this way (§12.14). */
.basel-reorder-row[data-lifted] {
    box-shadow: inset 2px 0 0 0 var(--basel-ink);
    cursor: grabbing;
}
.basel-reorder-row[data-lifted] .basel-grip { cursor: grabbing; }

/* The rows the lifted one would pass. They slide 3px in the direction they
   would travel — WHICH rows move, not how far; the drop rule says how far. */
.basel-reorder-row[data-shift] { transition: transform 90ms linear; }

/* Where it would land: one 2px ink rule in the gap between two rows, which is
   the same weight §12.5 gives a section boundary. A gap, never a highlighted
   row — a highlighted row cannot say which SIDE of itself the drop is on, and
   that ambiguity is the defect the old implementation shipped. */
.basel-drop-rule {
    position: absolute;
    left: 0;
    right: 0;
    top: 0;
    height: 0;
    border-top: var(--basel-rule-section) solid var(--basel-ink);
    pointer-events: none;
}

/* ==========================================================================
   TAB 09 — BUILDING DEPARTMENT (schema 21)
   --------------------------------------------------------------------------
   Everything ABOVE the sign-off list: the open building, the basics block,
   four figures, and the register of every other building. The ruler and the
   Ready-to-Issue ladder are NOT here — they are .basel-ruler and
   .basel-sched-strip, reused exactly as the Schedule draws them, because a
   permit clock and a phase clock are the same measurement pointed at
   different dates.

   NOTHING IN THIS BLOCK IS A NEW COLOUR OR A NEW SHAPE. The standings come
   from .basel-mark-* through baselStandingMarkHtml(); the grounds are the
   paper, the sheet and the sunken sheet; the one radius is 0.
   ========================================================================== */

/* The open building — an ink band, the way Home draws the open project. */
.basel-bd-open {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto auto auto auto;
    align-items: baseline;
    gap: 0 18px;
    padding: 12px var(--basel-pad-row-x);
    margin-top: 20px;
    background-color: var(--basel-ink);
    color: var(--basel-paper);
}
.basel-bd-open-name { font-weight: 700; letter-spacing: -0.01em; min-width: 0; }
.basel-bd-open-acts { display: flex; align-items: center; gap: 8px; }
.basel-bd-open .basel-btn { border-color: var(--basel-paper); color: var(--basel-paper); background: transparent; }
.basel-bd-open .basel-btn:hover { background-color: var(--basel-paper); color: var(--basel-ink); }

/* The basics. Reference, not work — the sunken sheet says so. */
.basel-bd-basics {
    display: grid;
    grid-template-columns: repeat(4, minmax(0, 1fr));
    gap: 14px 24px;
    padding: 16px var(--basel-pad-row-x);
    background-color: var(--basel-sheet-sunken);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-bd-basics > div { min-width: 0; }
.basel-bd-k { color: var(--basel-ink-muted); margin-bottom: 3px; }
.basel-bd-v { overflow: hidden; text-overflow: ellipsis; }
.basel-bd-nil { color: var(--basel-ink-muted); }
.basel-bd-link { color: var(--basel-ink); text-decoration: underline; }
.basel-bd-link:hover { color: var(--basel-ink-muted); }

/* The four figures. */
.basel-bd-figs { display: flex; flex-wrap: wrap; gap: 40px; padding: 20px var(--basel-pad-row-x) 4px; }
.basel-bd-fig { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.basel-bd-fign { font-size: 44px; line-height: 1; font-weight: 700; letter-spacing: -0.03em; }
.basel-bd-fign small { font-size: 18px; font-weight: 400; letter-spacing: 0; margin-left: 4px; }
.basel-bd-figk { color: var(--basel-ink-muted); }
/* A forecast is not a measurement — the muted ink and weight 400 are the same
   register .basel-sched-early carries, for the same reason. */
.basel-bd-fig--q .basel-bd-fign { color: var(--basel-ink-muted); font-weight: 400; }

.basel-bd-strip { padding: 0 var(--basel-pad-row-x); }

/* The register of every other building. */
.basel-bd-others { padding: 26px var(--basel-pad-row-x) 0; }
.basel-bd-others-k { color: var(--basel-ink-muted); padding-bottom: 8px; }
.basel-bd-reg {
    display: grid;
    grid-template-columns: 18px minmax(0, 1fr) 168px 132px minmax(0, 1fr) 96px 56px;
    align-items: center;
    gap: 0 12px;
    width: 100%;
    padding: 10px 0;
    border: 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet);
    text-align: left;
    font: inherit;
    cursor: pointer;
}
.basel-bd-reg:hover { background-color: var(--basel-sheet-sunken); }
.basel-bd-reg:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }
.basel-bd-reg--head {
    background-color: transparent;
    cursor: default;
    padding-top: 0;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
}
.basel-bd-reg--head:hover { background-color: transparent; }
.basel-bd-reg--head > span { color: var(--basel-ink-muted); }
.basel-bd-reg-mark { display: flex; align-items: center; }
.basel-bd-reg-name { font-weight: 700; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.basel-bd-reg-days { text-align: right; }

/* Nothing filed yet. */
.basel-bd-empty {
    display: flex; flex-direction: column; align-items: flex-start; gap: 10px;
    padding: 30px var(--basel-pad-row-x);
    margin-top: 20px;
    background-color: var(--basel-sheet);
    border-top: var(--basel-rule-section) solid var(--basel-ink);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-bd-empty-h { font-weight: 700; }
.basel-bd-empty-p { color: var(--basel-ink-muted); max-width: 76ch; }

/* One clearance line inside the details dialog. */
.basel-bd-clear-head { color: var(--basel-ink-muted); padding: 18px 0 6px; border-top: var(--basel-rule-section) solid var(--basel-ink); margin-top: 18px; }
.basel-bd-clear {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 148px 148px 32px;
    align-items: center;
    gap: 0 10px;
    padding: 6px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-bd-danger { padding-top: 18px; margin-top: 18px; border-top: var(--basel-rule-row) solid var(--basel-hair); }

@media (max-width: 1100px) {
    .basel-bd-basics { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .basel-bd-figs { gap: 26px; }
    .basel-bd-reg { grid-template-columns: 18px minmax(0, 1fr) 132px 110px 96px 56px; }
    .basel-bd-reg > span:nth-child(5) { display: none; }
}

/* ---- "Why this exists" (ADR-075, DESIGN §12.30) ---------------------------
   Replaces the Links block. One grid shared by every chronology row — date |
   rule gutter | id | log | description | words — and every cell that could grow
   is CLIPPED, never wrapped, so a row can never grow into the row beneath it.
   Measured: a wrapping row was the defect that killed the first drawing of this.
   -------------------------------------------------------------------------- */

.basel-why-section {
    border-top: var(--basel-rule-row) solid var(--basel-hair);
    padding-top: 14px;
    display: flex;
    flex-direction: column;
    gap: 10px;
}

/* the head is the disclosure, and its count keeps signal in the shut state */
.basel-why-head {
    display: flex;
    align-items: baseline;
    gap: 10px;
    width: 100%;
    padding: 0 0 8px;
    border: 0;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
    background: none;
    text-align: left;
    cursor: pointer;
    font: inherit;
}
/* THE APP'S ONE CHEVRON (ADR-079). This was two typographic characters —
   &#9662; and &#9656;, solid filled triangles sized by font-size — which is a
   THIRD drawing of open/shut in an app that already had two, and the only one
   whose size the type-floor guard had an opinion about (it caught this at 11px,
   because 11 is for an uppercase 700 eyebrow and nothing else, §12.4). It is
   baselChevronHtml() now: 10px, stroke 3, stroked rather than filled, and the
   font-size question does not arise because it is no longer text. */
.basel-why-chev { color: var(--basel-ink-muted); display: inline-flex; align-items: center; }
.basel-why-chev--shut svg { transform: rotate(-90deg); }
.basel-why-head .basel-section-title { color: var(--basel-ink); }
.basel-why-count { margin-left: auto; color: var(--basel-ink-muted); }

/* the answer — drawn in BOTH states, because it is what the section is for */
.basel-why-answer {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 20px;
    align-items: start;
    padding: 15px 17px;
    background-color: var(--basel-sheet-sunken);
    border-left: 3px solid var(--basel-ink);
}
.basel-why-answer-p { margin: 0; max-width: 82ch; }
.basel-why-answer-acts { display: flex; flex-direction: column; gap: 7px; }
.basel-why-section.is-polishing .basel-why-answer-p { color: var(--basel-ink-muted); }

/* the strip — one ruler, a fixed 52px whatever the span */
.basel-why-strip { position: relative; height: 52px; margin: 4px 0 2px; }
.basel-why-rule { position: absolute; left: 0; right: 0; top: 14px; height: 1px; background-color: var(--basel-hair-strong); }
.basel-why-rule-late { position: absolute; top: 14px; height: 1px; background-color: var(--basel-waiting-mark); }
.basel-why-band { position: absolute; top: 8px; height: 13px; background-color: rgba(var(--basel-waiting-mark-rgb), 0.12); }
.basel-why-tick { position: absolute; top: 10px; width: 9px; height: 9px; margin-left: -4px; }
.basel-why-tick--waiting { background-color: var(--basel-waiting-mark); }
.basel-why-tick--working { background-color: var(--basel-working-mark); }
.basel-why-tick--done    { background-color: var(--basel-done-mark); }
.basel-why-tick--queued  { background-color: var(--basel-queued-mark); }
.basel-why-tick--none    { background-color: var(--basel-hair-strong); }
.basel-why-tick--open    { border: 1px solid var(--basel-hair-strong); background-color: var(--basel-sheet); }
.basel-why-tick--me      { top: 8px; width: 13px; height: 13px; margin-left: -6px; background-color: var(--basel-ink); }
.basel-why-slab { position: absolute; top: 30px; color: var(--basel-ink-muted); white-space: nowrap; }
.basel-why-slab--me { color: var(--basel-ink); max-width: 120px; overflow: hidden; text-overflow: ellipsis; }
.basel-why-slab--late { color: var(--basel-waiting-ink); }

.basel-why-k {
    display: flex;
    align-items: baseline;
    gap: 10px;
    color: var(--basel-ink-muted);
    padding: 12px 0 7px;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-why-k-n { color: var(--basel-ink-muted); text-transform: none; letter-spacing: 0; font-weight: 400; }

/* THE ROW GRID. 124px on the log column because "Communication" measures 115. */
.basel-why-row {
    display: grid;
    grid-template-columns: 76px 26px 62px 124px minmax(0, 1fr) 116px 22px;
    gap: 10px;
    align-items: center;
    height: 34px;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-why-row[onclick] { cursor: pointer; }
.basel-why-row[onclick]:hover { background-color: var(--basel-sheet-sunken); }
.basel-why-dt { color: var(--basel-ink-muted); text-align: right; white-space: nowrap; }
.basel-why-g { position: relative; height: 100%; }
.basel-why-m { position: absolute; left: 8px; top: 12px; width: 9px; height: 9px; }
.basel-why-m--waiting { background-color: var(--basel-waiting-mark); }
.basel-why-m--working { background-color: var(--basel-working-mark); }
.basel-why-m--done    { background-color: var(--basel-done-mark); }
.basel-why-m--queued  { background-color: var(--basel-queued-mark); }
.basel-why-t {
    position: absolute; left: 8px; top: 12px; width: 9px; height: 9px;
    border: 1px solid var(--basel-hair-strong); background-color: var(--basel-sheet);
}
.basel-why-id  { color: var(--basel-ink); white-space: nowrap; }
.basel-why-log { color: var(--basel-ink-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.basel-why-d   { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.basel-why-f   { color: var(--basel-ink-muted); font-weight: 700; white-space: nowrap; text-align: right; overflow: hidden; text-overflow: ellipsis; }
.basel-why-row--me {
    background-color: var(--basel-ink);
    color: var(--basel-paper);
    margin: 0 -18px;
    padding: 0 18px;
    border-bottom: 0;
}
.basel-why-row--me .basel-why-dt,
.basel-why-row--me .basel-why-log,
.basel-why-row--me .basel-why-f { color: var(--basel-paper); opacity: 0.72; }
.basel-why-row--me .basel-why-id { color: var(--basel-paper); }
.basel-why-row--last { border-bottom: 0; }

/* the one interval that is still running */
.basel-why-live {
    margin: 0 -18px;
    padding: 13px 18px;
    background-color: rgba(var(--basel-waiting-mark-rgb), 0.06);
    border-top: var(--basel-rule-row) solid var(--basel-waiting-mark);
    border-bottom: var(--basel-rule-row) solid var(--basel-waiting-mark);
}
.basel-why-live-in { display: grid; grid-template-columns: 76px 26px minmax(0, 1fr); gap: 10px; align-items: center; }
.basel-why-x-cell { display: flex; justify-content: flex-end; }
.basel-why-live-n { font-size: 19px; font-weight: 700; color: var(--basel-waiting-ink); text-align: right; white-space: nowrap; }
.basel-why-live-g { position: relative; height: 100%; }
.basel-why-live-r { position: absolute; left: 12px; top: -14px; bottom: -14px; width: 1px; background-color: var(--basel-waiting-mark); }
.basel-why-live-p { margin: 0; color: var(--basel-ink); }

.basel-why-cost { display: flex; flex-wrap: wrap; gap: 6px; padding-top: 10px; }
.basel-why-chip-words { color: var(--basel-ink-muted); font-weight: 700; white-space: nowrap; }

/* the gaps — questions with a one-tap answer, never warnings */
.basel-why-gaps { margin-top: 14px; border-top: var(--basel-rule-section) solid var(--basel-waiting-mark); }
.basel-why-gaps-k { color: var(--basel-waiting-ink); padding: 9px 0 7px; }
.basel-why-gap {
    display: grid;
    grid-template-columns: minmax(0, 1fr) auto;
    gap: 14px;
    align-items: center;
    padding: 8px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-why-gap-p { color: var(--basel-ink); }

/* the controls, demoted — same picker, same ids, in both states */
.basel-why-foot {
    display: flex;
    align-items: stretch;
    gap: 10px;
    flex-wrap: wrap;
    padding-top: 12px;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-why-foot-c { display: flex; flex-direction: column; gap: 5px; min-width: 240px; }
.basel-why-foot-c:first-child { flex: 1 1 340px; }

/* ==========================================================================
   THE AGENDA YOU WRITE (DESIGN §12.32, ADR-080)
   --------------------------------------------------------------------------
   One list, three kinds of item. A TOPIC and a LOG SECTION are the same
   component — `.basel-agenda-item`, unchanged — because that is the claim the
   whole merge rests on: they are one object seen from two ends. Nothing below
   redefines that grid. What is here is only what a topic has and a log section
   does not: the sentence you mean to say, the line naming where its records
   came from, and the block that holds a client's own agenda.
   ========================================================================== */

/* The sentence. Body size and full ink, not the muted 13px `.basel-agenda-note`
   beside it: this is the thing you are going to SAY, and the note is metadata
   about a section. Same distinction §12.9 draws between a record's words and
   its chrome. */
.basel-agenda-say { margin-top: 7px; max-width: 84ch; }
.basel-agenda-say p:last-child { margin-bottom: 0; }

/* Where the records came from. A hairline under it, because it is a caption for
   the rows below rather than a thing in its own right. */
.basel-agenda-src {
    display: flex;
    align-items: baseline;
    gap: 9px;
    flex-wrap: wrap;
    margin-top: 12px;
    padding-bottom: 7px;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-agenda-src-k { color: var(--basel-ink-muted); }
.basel-agenda-src-v { color: var(--basel-ink); font-weight: 700; }
/* CORRECTED BY ADR-132. This is a PHRASE — "named by you", "by rule,
   refreshes itself" — and it carries a fact the PM has to be able to read at
   the back of a room, so it takes --basel-ink-muted. --basel-ink-faint is
   never text: it may mark an ABSENT number or a decorative middot, and that
   is the whole of its licence. */
.basel-agenda-src-n { margin-left: auto; color: var(--basel-ink-muted); }

/* What a log section says about the records that went onto a topic. A sunken
   ground and full ink: it is an account of what is missing from the list above
   it, and lifting is only safe because this is never quiet enough to skip. */
.basel-agenda-lifted {
    margin-top: 9px;
    padding: 8px 13px;
    background-color: var(--basel-sheet-sunken);
    color: var(--basel-ink-muted);
}
.basel-agenda-lifted strong { color: var(--basel-ink); font-weight: 700; }

/* ---- Their agenda, and Round the room: a GROUP (ADR-088) ---------------- */

/* The block's header is `.basel-group` — the fold chevron on the LEFT as every
   group header has it, the meeting number, the name, the count — and its lines
   are ordinary rows in the one grid. The 2px rule and the -18px margin the
   block carried from §12.32 are gone: a block indented two pixels was a row
   two pixels narrower, and one grid means one width. ONE caption still, never
   a badge per line (§12.18 finding 2). */
.basel-agenda-client { margin: 0; }
.basel-agenda-group { border-radius: 0; }
.basel-agenda-client-k { color: var(--basel-ink); }
.basel-agenda-client-n { color: var(--basel-ink-muted); }
.basel-agenda-client-p { color: var(--basel-ink-muted); margin: 0 0 4px; }
/* Round the room (ADR-083) is the same block — one caption, one number, the
   five logs inside it on the same 2px rule. */
.basel-agenda-round .basel-agenda-client-k { color: var(--basel-ink); }
/* FACING (ADR-084). The buildings with nothing open are ONE LINE at the foot
   of the block, not eight empty sections — a fact about the project, so it
   carries no shape and no colour, only the muted ink every note takes. */
.basel-agenda-foldnote {
    margin: 0;
    padding: 10px 0 2px;
    color: var(--basel-ink-muted);
}
/* "No update" rides the composer's own caption line beside Back and Next; it
   is a quiet act, never a second primary button on the projector. */
.basel-agenda-noupd { margin-right: 10px; }

/* ON THIS SCREEN, FINISHED IS THE TICK ALONE (ADR-132).
   ADR-056's ground is right where it was written — the SCHEDULE, where a
   finished task carries no other mark and the row has to say "behind us" on
   its own. It is wrong here. A meeting list already states covered in the tick
   column, on every covered row, in the column the header names; the ground
   said it a second time, and said it as a slab under a wrapping title that a
   client reads as "this one is greyed out". The part of ADR-056 that does NOT
   move is the other half: the text keeps FULL ink either way. Never faint ink
   (2.88:1, forbidden for text) and never green — green is a STANDING, and
   whether you got to somebody's agenda item is not one (§12.3).

   `.basel-agenda-item--covered` is kept as the hook that says the row is
   covered; it draws nothing. */
/* The client line's own tick, in the tick column (ADR-080): the tick IS the
   fact, and the word beside it went (ADR-088). */
.basel-agenda-covered {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin: 0;
    cursor: pointer;
}

/* ==========================================================================
   THE MEETING HAS A POSITION (DESIGN §12.33, ADR-081)
   --------------------------------------------------------------------------
   Three drawings of ONE component. `.basel-agenda-item` is unchanged; what
   changes with the cursor is whether its body is folded to a line
   (`--covered`, `--later`) or drawn in full on a 3px ink rule (`--current`).
   Covered takes the finished ground §12.27 already gives a finished thing;
   nothing here is a standing colour, because a meeting's progress is nobody's
   move (§12.3).
   ========================================================================== */

/* A line: the one grid, folded to its first row — 44px, the head cells only.
   On a live meeting the line is the control that moves the cursor. */
.basel-agenda-item--line[role="button"] { cursor: pointer; }
.basel-agenda-item--line[role="button"]:hover .basel-agenda-title { text-decoration: underline; text-underline-offset: 3px; }
.basel-agenda-item--line:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }

/* The filled tick a covered line carries: a solid ink square with the check
   in paper. Square, because radius is 0 everywhere in this layer (§12.8 #6). */
.basel-agenda-tick {
    display: inline-flex;
    width: 18px;
    height: 18px;
    align-items: center;
    justify-content: center;
    background-color: var(--basel-ink);
    color: var(--basel-paper);
}

/* The current item: a 3px ink rule down its left edge, drawn outside the
   grid so the number track does not move between states. */
.basel-agenda-item--current { position: relative; }
.basel-agenda-item--current::before {
    content: "";
    position: absolute;
    left: -14px;
    top: 0;
    bottom: 0;
    width: 3px;
    background-color: var(--basel-ink);
}
/* Mouse focus draws no ring (the item is not a button and a click should not
   look like one), but keyboard focus still needs to be seen — same ring every
   other :focus-visible rule in this file draws (e.g. .basel-agenda-chip,
   .basel-viewsel), so Tab-ing through the agenda never goes invisible. */
.basel-agenda-item--current:focus { outline: none; }
.basel-agenda-item--current:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }

/* Where what was said lands, then the record's own composer (§12.28). */
.basel-agenda-lands { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 10px; color: var(--basel-ink-muted); }
.basel-agenda-lands .basel-ctl { text-transform: none; letter-spacing: normal; }
.basel-agenda-item--current .basel-composer { margin-top: 8px; }
/* `.basel-agenda-said` is DELETED with the single-line outcome it dressed
   (ADR-093). It was `display: flex; flex-wrap: wrap` over a <p> of three inline
   spans, and the thread that replaced that <p> reused its class name — so the
   heading and every post became flex children of one row and the thread
   rendered as a sentence, with each post's bottom border drawn as an underline
   through it. A rule that outlives its markup is worse than no rule: the class
   name is now `.basel-agenda-thread`, and the block below is its only styling. */
.basel-agenda-thread { min-width: 0; }

/* Back and Next — the only controls on the item list. Under the current item
   they ride the composer's caption line, at its right end. Before the meeting
   starts and after it ends the one control is on the MEETING HEAD (ADR-087) —
   Start the meeting, then Back — on the hero's dates line since 2.37.0. */
.basel-agenda-nav { display: inline-flex; align-items: center; gap: 10px; margin-left: auto; }
.basel-agenda-nav .basel-act, .basel-agenda-nav .basel-btn { display: inline-flex; align-items: center; gap: 6px; }
.basel-meet-head > .basel-btn, .basel-meet-head > .basel-act[data-agenda-back] { display: inline-flex; align-items: center; gap: 6px; align-self: center; }

/* THE HERO (ADR-088): the Schedule's `.basel-sched-hero` reused whole — the
   eyebrow, the big fact at its own size step, the dates line, and where the
   Schedule draws its ruler, the progress track. The one difference is the
   air above it: the meeting block's 2px rule is the section boundary the hero
   already draws, so it draws it once. */
.basel-meet > .basel-meet-hero { margin-top: 0; padding-top: 22px; border-top: 0; }
/* The Schedule's main column is 1.5fr because its side carries three blocks;
   the meeting's side carries ONE figure, and its dates line carries the
   controls. Measured at 1440: at 1.5fr the main column is 605px and the line
   — date, series, phase, hours, Start the meeting, the roll fold, the dots —
   is 728, so it wrapped; at 2.5fr it is 748. */
.basel-meet-hero { grid-template-columns: minmax(0, 2.5fr) 1px minmax(0, 1fr); }
.basel-meet-hero .basel-sched-hero-name { min-width: 0; }
/* The head's controls, one group at the line's right: Start the meeting or
   Back, the minutes verb, the roll fold, the dots (ADR-087, ADR-088). */
.basel-meet-acts { margin-left: auto; display: inline-flex; align-items: center; gap: 12px; flex-wrap: nowrap; }
.basel-meet-acts > .basel-btn, .basel-meet-acts > .basel-act[data-agenda-back] { display: inline-flex; align-items: center; gap: 6px; }
/* THE PROGRESS TRACK, in the ruler's place. Words first; the track is a 4px
   decoration with a hairline tick where each numbered item begins and no
   standing colour. The ruler's own negative margin is not wanted here: the
   track sits on the column's own edges. */
.basel-meet-hero .basel-agenda-prog { margin: 18px 0 0; }
/* TWO ROWS SINCE ADR-131, so the base column direction stands: the boxed
   filter row, then the quiet caption line, exactly as a log draws them
   (§12.9). It was one row of one control until the Previous-meetings register
   gave the screen a list to narrow. */
.basel-toolbar--agenda { gap: 10px; padding: 12px 0 10px; }
.basel-agenda-prog-w { display: flex; gap: 18px; flex-wrap: wrap; margin-top: 7px; color: var(--basel-ink-muted); }
.basel-agenda-prog-n { color: var(--basel-ink); font-weight: 700; }
.basel-agenda-prog-t { position: relative; height: 4px; background-color: var(--basel-hair); }
.basel-agenda-prog-f { position: absolute; left: 0; top: 0; bottom: 0; background-color: var(--basel-ink); }
.basel-agenda-prog-k { position: absolute; top: -2px; bottom: -2px; width: 1px; background-color: var(--basel-hair-strong); }

/* ==== THE PREVIOUS-MEETINGS REGISTER (ADR-131) =============================
   Every meeting that has been held, in THE LOG'S OWN ANATOMY: an 11px
   uppercase muted header closed by the 2px ink rule, .basel-group headers by
   series, one row per meeting, the chevron LAST at the row's right edge. It
   replaces the fold at the foot of the live meeting, where a year of meetings
   was one collapsed line under the thing the PM was actually running.

   THE ROW'S LEAD IS --basel-pad-rowlead-x, the log's own, so a meeting's
   label sits directly under its group's name — the same alignment a log row's
   tick has with its group header.

   THE FOUR FIGURE COLUMNS ARE RIGHT-ALIGNED, headers included: present,
   covered, asks and hours are quantities, and a column of quantities is read
   down its right edge.

   THE GRID IS DECLARED TWICE, exactly as the log's is (--basel-row-cols on
   .basel-thead-cells and .basel-row-btn): the header spans all ten tracks; the
   row is three (the open button, the minutes verb, the chevron) because A
   BUTTON CANNOT HOLD A BUTTON, and the button re-declares the first eight. The
   two agree because the fixed tracks and the 16px gap are identical and the
   flexible one absorbs the same remainder. */
/* THE MEETING, ITS NAME, ITS SERIES (ADR-147). The first track is the whole
   label — "OAC · 2026-08-03 · 12:45 pm", 211px in the mono, the longest clock
   text there is, so 216px — and there is no separate date track any more
   (ADR-146 split the label into a prefix and a date; one cell is clearer).
   The NAME a PM gave the meeting and the SERIES share what is left, and each
   keeps 96px: below that they are an ellipsis and nothing else, which is what
   the series column was at 1280 before this (50px, "Client ..."). So the
   register has a floor — .basel-arch-in — and scrolls inside .basel-arch
   under it, which is what that box's overflow-x was always for. */
:root {
    --basel-arch-cols:     216px minmax(96px, 1.2fr) minmax(96px, 1fr) 124px 64px 78px 56px 44px 88px 24px;
    --basel-arch-btn-cols: 216px minmax(96px, 1.2fr) minmax(96px, 1fr) 124px 64px 78px 56px 44px;
}
.basel-arch-in { min-width: 1072px; }
.basel-arch-head {
    display: grid;
    grid-template-columns: var(--basel-arch-cols);
    gap: 16px;
    align-items: center;
    min-width: 0;
    padding: 0 18px 9px var(--basel-pad-rowlead-x);
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
}
.basel-arch-head > span { color: var(--basel-ink-muted); }
.basel-arch-fig { text-align: right; }

/* The group header is the log's, standing still: nothing folds here, so it is
   a heading rather than a button (ADR-117's finding, applied to a register
   that is already one screenful). */
.basel-arch-group { cursor: default; }
.basel-arch-group-meta { margin-left: auto; color: var(--basel-ink-muted); }

.basel-arch-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 88px 24px;
    gap: 16px;
    align-items: center;
    min-width: 0;
    padding: 0 18px 0 var(--basel-pad-rowlead-x);
    background-color: var(--basel-sheet);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-arch-btn {
    display: grid;
    grid-template-columns: var(--basel-arch-btn-cols);
    gap: 16px;
    align-items: center;
    min-width: 0;
    padding: var(--basel-pad-row-y) 0;
    border: 0;
    border-radius: var(--basel-radius);
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.basel-arch-btn:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }
.basel-arch-name { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
/* THE PHASE IS A WORD HERE, NOT A CHIP. A `.basel-flag` is the phase's shape
   everywhere else on this screen, and at 124px "Schematic Design" came back
   cut through the middle of its own box with no ellipsis to say so — a chip
   cannot shrink, it can only be clipped. A quiet phrase in --basel-ink-muted
   ellipsises honestly, which is the whole reason the column is 124px and not
   the chip's natural width. */
.basel-arch-cell { min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; color: var(--basel-ink-muted); }
.basel-arch-chev { display: inline-flex; align-items: center; justify-content: flex-end; color: var(--basel-ink-muted); }
.basel-arch-row[data-expanded="false"] .basel-arch-chev svg { transform: rotate(-90deg); }
.basel-arch-body {
    padding: 0 18px 18px var(--basel-pad-rowlead-x);
    background-color: var(--basel-sheet);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
/* Wide on purpose: ten columns do not compress, so the register scrolls inside
   its own box rather than making the page scroll sideways. */
.basel-arch { overflow-x: auto; }

/* ==========================================================================
   THE ROLL CALL IS THE CHIPS (ADR-132)
   --------------------------------------------------------------------------
   WHAT WAS HERE: `.basel-agenda-rollbtn`, a "7 of 9 present" word-button on
   the meeting head that folded a four-column table open. The figure was true
   and useless — the room wants to know WHO, and the only way to find out was
   to open a table, on a projector, in front of the people in it.

   The chips ARE the roll call: one per member, clicking one toggles them.
   They sit at FULL WIDTH BELOW THE HERO, not in the hero's left column, where
   nine of them wrapped to two lines and pushed the progress track down.

   34px tall, which is the height every other control in this app uses. Present
   takes an ink border with the initials block filled ink-on-paper; absent
   takes a DASHED border and muted ink — the app's own "not built" outline
   (§12.5), which is the right shape for somebody who was expected and is not
   there. The surname alone: nine full names do not fit on one line, and the
   surname is what the room says out loud.
   ========================================================================== */
.basel-agenda-roll {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    padding: 12px 0 2px;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    padding-bottom: 12px;
}
.basel-agenda-chip {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    height: 34px;
    padding: 0 12px 0 0;
    border: var(--basel-rule-row) solid var(--basel-ink);
    border-radius: var(--basel-radius);
    background: none;
    color: var(--basel-ink);
    font: inherit;
    font-weight: 700;
    cursor: pointer;
}
.basel-agenda-chip[disabled] { cursor: default; }
.basel-agenda-chip-i {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 24px;
    align-self: stretch;
    background-color: var(--basel-ink);
    color: var(--basel-paper);
}
.basel-agenda-chip--out {
    border-style: dashed;
    border-color: var(--basel-hair-strong);
    color: var(--basel-ink-muted);
    font-weight: 400;
}
.basel-agenda-chip--out .basel-agenda-chip-i { background: none; color: var(--basel-ink-muted); }
.basel-agenda-chip:not([disabled]):hover { border-color: var(--basel-ink); color: var(--basel-ink); }
.basel-agenda-chip:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }

/* The foot: the next-meeting date, and nothing else since ADR-131 — the
   archive's fold that shared this row is a tab of its own now. */
.basel-agenda-foot {
    display: flex;
    align-items: center;
    gap: 16px;
    margin-top: 6px;
    padding-top: 6px;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-agenda-foot-next { display: flex; align-items: center; gap: 12px; }
.basel-agenda-foot-next .basel-input { width: auto; }

/* ITEM 00 IS A GROUP, AND ITS TOTALS ROW NEVER LEAVES (ADR-132).
   It used to be a `.basel-agenda-item` with `00` in the number track, a
   title/subtitle/verb block that existed only while it was open, and a bar
   whose open state REPLACED the totals row with the building rows. The
   replacement was the defect: opening it took away the one line the client
   had been reading. Opening ADDS the buildings underneath the totals now.

   The heading is the `.basel-group` anatomy every other fold on this screen
   uses — chevron left, the number, the name, the count — with the verb BESIDE
   the button, because a button cannot hold a button (ADR-131). */
.basel-agenda-mx-head { display: flex; align-items: stretch; gap: 8px; }
.basel-agenda-mx-head > .basel-group { flex: 1 1 auto; width: auto; }
.basel-agenda-mx-head > .basel-act { flex: 0 0 auto; align-self: center; }
.basel-agenda-mx .basel-readiness { border-top: 0; }
.basel-readiness-lead--focus { display: flex; flex-direction: column; gap: 1px; white-space: normal; }
.basel-readiness-lead-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-readiness-focus { color: var(--basel-ink-muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- The prep side ------------------------------------------------------ */

/* The topic's own sentence, being written rather than read. */
.basel-prep-say { margin-bottom: 10px; }
.basel-prep-say-k { color: var(--basel-ink-muted); margin-bottom: 6px; }
.basel-prep-say .basel-textarea { width: 100%; }

/* A client line on the prep screen. It is NOT a `.basel-reorder-row`: the block
   is one entry in sectionOrder, so the drag moves the whole thing and cannot
   reach inside it. That is the data refusing to reorder their agenda, rather
   than a rule somebody has to remember. It is a row in the one grid (ADR-088)
   with its textarea in the body row under it. */
.basel-agenda-item--cline .basel-textarea { width: 100%; margin-top: 6px; }

/* ==========================================================================
   THE TEMPLATE (DESIGN §12.34, ADR-082)
   --------------------------------------------------------------------------
   Two standing topics, the asks and the capture. Nothing below is a new
   component on the projector: an ask is a hairline row in the item's own
   body, a capture is `.basel-linked`, the log's number is a chip, the minutes
   are a mono figure. No colour anywhere here answers "whose move is it".
   ========================================================================== */

/* The log's own number, moved INTO the line as a chip after the title (the
   number track carries the meeting's 1..N now). Hairline box, mono, muted:
   it is the tab, not a standing. */
.basel-agenda-logno {
    display: inline-block;
    padding: 0 5px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    color: var(--basel-ink-muted);
    line-height: 18px;
}
/* "10 min" — a plan, not a clock. Muted mono at the line's right end. */
.basel-agenda-min { color: var(--basel-ink-muted); white-space: nowrap; }
.basel-agenda-tally-k { color: var(--basel-ink-muted); margin-right: 4px; }

/* The client block's meeting number rides the block head's left edge. */
.basel-agenda-client-num { color: var(--basel-ink-muted); }

/* Where we are: the Schedule's strip inside an item. The strip's own rules
   apply; the wrapper only keeps it off the item's hairline. */
.basel-agenda-where-strip { margin-top: 6px; }
/* Item 01's ruler sits under its strip with the strip's own air between them
   (ADR-092). The ruler already carries 46px below its line for the hanging
   date labels, so nothing is added under it. */
.basel-agenda-where-ruler { margin-top: 26px; }
.basel-agenda-where-ruler .basel-ruler { padding-left: 0; padding-right: 0; }

/* ============================================================================
   THE SECTIONS OF A MEETING — ADR-091, DESIGN §12.41
   ----------------------------------------------------------------------------
   A section heading is NOT a row and is deliberately not on
   `.basel-agenda-item`'s grid: it has no tick, no chevron and no minutes,
   because none of the three is true of a heading. What it shares with the row
   is the one thing that makes the items read as belonging to it — the 44px
   number track. The number hangs in that track and the name starts at 64px,
   which is exactly where every item title below it starts, so the heading sits
   left of its items and the items are indented against it without one value in
   the row grid moving.

   The name is at the screen-title step (28/800) against the item's 19/700 —
   two steps, which is what carries the rank at the back of a room where a rule
   on its own does not. The 2px ink rule above it is §12.5's section boundary,
   the same rule the log screens draw.
   ========================================================================== */
/* THE HEADING STAYS AT THE MARGIN (ADR-132). Its number track is 36px, which
   is exactly the indent every item's number carries INSIDE its own 76px cell —
   so the heading sits one step left of its items and the items read as
   belonging to it, with nothing in the row grid moved. The two size steps
   ADR-091 set were 28 against 19; the item title dropped to 15, so the
   heading drops to 19/700 and the two steps are kept at a size a wrapping
   list can afford. */
.basel-agenda-band {
    display: flex;
    align-items: baseline;
    gap: 18px;
    padding: 26px var(--basel-pad-row-x) 9px 0;
    border-top: var(--basel-rule-section) solid var(--basel-ink);
    min-width: 0;
}
/* The first heading on the list sits under the column header's own 2px rule
   and must not draw a second one on top of it. */
.basel-agenda-thead + .basel-agenda-band { border-top: 0; padding-top: 14px; }
.basel-agenda-band-n {
    flex: 0 0 var(--basel-agenda-band-num-w);
    width: var(--basel-agenda-band-num-w);
    font-size: 19px;
    line-height: 1.3;
    letter-spacing: -0.015em;
    font-weight: 700;
    color: var(--basel-ink-muted);
}
.basel-agenda-band-h {
    min-width: 0;
    font-size: 19px;
    line-height: 1.3;
    letter-spacing: -0.015em;
    font-weight: 700;
    color: var(--basel-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.basel-agenda-band-s { margin-left: auto; color: var(--basel-ink-muted); white-space: nowrap; }

/* ON PREP a heading is a reorder ROW (ADR-091) — the drag maps a gap index
   onto `sectionOrder`, so every entry in that array has exactly one row here.
   It takes the row's grid back, its grip rides in the number track beside the
   number as every prep row's does, and the name is a quiet field (ADR-050). */
/* THE TYPE STEP IS THE AGENDA'S NOW (ADR-133). It was pinned at 22/800 while
   ADR-132 dropped the Agenda's heading to 19/700 — a pin that existed only to
   stop the change leaking onto a screen that was not ready for it. Prep is
   ready, so the two screens say a section heading at one size again and the
   overrides below are deleted rather than restated. */
.basel-agenda-band--prep {
    display: grid;
    grid-template-columns: var(--basel-agenda-cols-prep);
    grid-template-rows: minmax(44px, auto);
    gap: 0 14px;
    align-items: center;
    padding: 18px var(--basel-pad-row-x) 6px 0;
}
.basel-prep-split[data-prep-rail="hidden"] .basel-agenda-band--prep {
    grid-template-columns: var(--basel-agenda-cols-prep-wide);
    gap: 0 18px;
}
.basel-agenda-band--prep .basel-agenda-band-n { flex: none; width: auto; }
.basel-agenda-band--prep .basel-agenda-band-h {
    height: 34px;
    width: auto;
    flex: 1 1 auto;
}

/* ---- The asks ------------------------------------------------------------ */

/* One ask: the kind as an eyebrow (a WORD, never a shape — §12.3), the
   question with who needs it under it, and the answer or the two carry
   fields. Hairline between lines, none above the first. */
.basel-asks { margin-top: 8px; }
.basel-ask {
    display: grid;
    grid-template-columns: 72px minmax(0, 1.4fr) minmax(0, 1fr);
    gap: 12px;
    align-items: start;
    padding: 8px 0;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-ask:first-child { border-top: 0; }
.basel-ask-k { color: var(--basel-ink-muted); padding-top: 3px; }
.basel-ask-q { display: flex; flex-direction: column; gap: 2px; min-width: 0; color: var(--basel-ink); }
.basel-ask-q-t[role="link"] { cursor: pointer; }
.basel-ask-q-t[role="link"]:hover { text-decoration: underline; text-underline-offset: 3px; }
.basel-ask-q-s { color: var(--basel-ink-muted); }
.basel-ask-a { min-width: 0; color: var(--basel-ink); }
/* "Not answered" is a phrase, not an absent number, so muted (ADR-132). */
.basel-ask-a--empty { color: var(--basel-ink-muted); font-style: italic; }
.basel-ask-a--done { font-weight: 700; }
.basel-ask-by { color: var(--basel-ink-muted); font-weight: 400; margin-left: 6px; }
/* An answered line takes the finished ground the covered item takes (ADR-056). */
.basel-ask--done { background-color: var(--basel-hair-strong); margin: 0 -8px; padding-left: 8px; padding-right: 8px; }
.basel-ask--done + .basel-ask--done { border-top-color: var(--basel-paper); }
/* The two carry fields: who, then to when, side by side, quiet (ADR-050). */
.basel-ask-carry { display: flex; gap: 8px; align-items: center; }
.basel-ask-carry .basel-set-quiet { height: 24px; }
.basel-ask-carry-by { flex: 1 1 120px; }
.basel-ask-carry-to { flex: 0 0 132px; width: auto; }

/* The next-meeting date on the review. */
.basel-agenda-next { display: flex; align-items: center; gap: 12px; margin-top: 12px; }
.basel-agenda-next .basel-input { width: auto; }

/* ---- The capture ---------------------------------------------------------- */

/* Three quiet ticks on the caption line: Issue · Decision · Note. Radios,
   because exactly one is true, and Note is the resting answer. */
.basel-agenda-capkind { display: inline-flex; align-items: center; gap: 6px; text-transform: none; letter-spacing: normal; cursor: pointer; }
.basel-agenda-capkind .basel-check { width: 14px; height: 14px; }
.basel-agenda-caps { margin-top: 10px; }
.basel-cap-who { color: var(--basel-ink); font-weight: 700; }
.basel-cap--note { cursor: default; }
.basel-cap--note:hover { background: none; }

/* ---- The prep side of the template ------------------------------------- */

/* Minutes: a quiet number in the 56px minutes track (ADR-088) — the figure
   right-aligned, its unit only once there is a figure (ADR-087). */
.basel-prep-min-wrap { display: inline-flex; align-items: baseline; justify-content: flex-end; gap: 3px; min-width: 0; color: var(--basel-ink-muted); }
.basel-prep-min { width: 30px; height: 22px; padding: 0 2px; text-align: right; -moz-appearance: textfield; }
.basel-prep-min::-webkit-outer-spin-button, .basel-prep-min::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* An ask line being written: kind · words · who · remove. */
.basel-prep-asks { margin-top: 4px; }
.basel-prep-ask {
    display: grid;
    grid-template-columns: 104px minmax(0, 1fr) 180px 28px;
    gap: 10px;
    align-items: center;
    padding: 4px 0;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-prep-ask-k { color: var(--basel-ink-muted); }
.basel-prep-ask-t { min-width: 0; color: var(--basel-ink); }
.basel-prep-ask-t--rec { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding: 0 4px; }
.basel-prep-ask-by { color: var(--basel-ink-muted); }
.basel-prep-ask--sugg .basel-prep-ask-by { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.basel-prep-ask-sugg { margin-top: 14px; }
.basel-prep-ask-sugg .basel-prep-say-k { margin-bottom: 2px; }

/* Who presents, what is shown. */
.basel-prep-showing { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 16px; margin-bottom: 12px; }
.basel-prep-presenter { display: flex; align-items: center; gap: 6px; }
/* Round the room, faced, an hour early (ADR-084): a hairline grid of heading ·
   who answers · count. A count is mono; nothing here is anybody's move, so
   nothing here draws a standing. */
.basel-prep-faced { display: block; margin-top: 6px; }
.basel-prep-faced-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr) 40px;
    gap: 10px;
    align-items: baseline;
    padding: 5px 0;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-prep-faced-t { color: var(--basel-ink); font-weight: 600; }
.basel-prep-faced-m { color: var(--basel-ink-muted); }
.basel-prep-faced-n { color: var(--basel-ink-muted); text-align: right; }
.basel-prep-presenter .basel-set-quiet { flex: 1 1 auto; width: auto; }
@media (max-width: 900px) { .basel-prep-showing { grid-template-columns: minmax(0, 1fr); } }

/* The dots, in the control group after the chevron (ADR-078 clause two). A
   topic has a second action; a log section still has none, so this is built on
   a topic row and nowhere else. */
/* WHAT WAS HERE: `.basel-prep-menu`, the topic row's dots. Deleted with the
   column (ADR-133) rather than left behind — a rule with no markup is a rule
   the next reader has to prove is dead. */

/* The two verbs that ADD an item, at the TOP of the list they add to since
   2.34.0 (ADR-085) — the list is short now and its head is where a PM starts. */
.basel-prep-add {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    padding: 12px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-prep-add-n { color: var(--basel-ink-muted); }

/* ==========================================================================
   THE PREP SPLIT (DESIGN §12.37, ADR-085) — Phase 5, 2.34.0
   --------------------------------------------------------------------------
   The agenda on the left, what you read to write it on the right. 1.5fr to
   1fr since 2.37.0 (ADR-088): the board drew 1.15, sized for a 124px control
   group; the row is the one grid now — 292px of fixed tracks and gaps — and
   at 1.15 the title cell measured 275px at 1440 against 325 for "Decisions &
   Approvals · 03 · nothing open", so titles ellipsised again. At 1.5 it is
   344px; the right column keeps the 420px its folded headers and prose need.

   ONE COLUMN BELOW 1280px, and the breakpoint is stated rather than fluid
   because the left column stops being the agenda the moment its rows wrap —
   the whole claim of the split is that what you see here is what the room sees
   an hour later, in the same shape (§12.18).

   The gutter is a hairline and 32px of air, not a box: the two columns are one
   screen, and drawing a border round either would make them two panels.
   ========================================================================== */
/* THE RAIL IS 300px AND IT FOLDS (ADR-133). A ratio made the two columns
   argue about every pixel the row wanted; the sources need a stated width to
   hold their folded headers and their prose, and the agenda takes what is
   left. Folded, the second track is not declared and the rail is not
   rendered — the rows keep their anatomy and their columns simply grow. */
.basel-prep-split {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 300px;
    gap: 32px;
    align-items: start;
}
.basel-prep-split[data-prep-rail="hidden"] { grid-template-columns: minmax(0, 1fr); gap: 0; }
.basel-prep-col { min-width: 0; }
.basel-prep-col--sources {
    border-left: var(--basel-rule-row) solid var(--basel-hair);
    padding-left: 32px;
}
@media (max-width: 1279px) {
    .basel-prep-split { grid-template-columns: minmax(0, 1fr); gap: 8px; }
    .basel-prep-col--sources { border-left: 0; padding-left: 0; }
}

/* Each column names itself once, on the 2px ink rule a screen head takes. */
.basel-prep-colk {
    display: flex;
    align-items: baseline;
    gap: 12px;
    padding-bottom: 7px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
}
/* MUTED, NOT FAINT (ADR-133). `--basel-ink-faint` is never text — its whole
   licence is an absent number or a decorative middot (§12.47) — and both of
   these are a sentence somebody has to read. */
.basel-prep-colk-n { color: var(--basel-ink-muted); }

/* HIDE SOURCES / SHOW SOURCES (ADR-133) — the band's own fold vocabulary
   (ADR-131), reused rather than a second one invented: a glyph, a word, a
   hairline box, inverted to solid ink once folded because the control that can
   undo a fold has to read as live. It rides in the RAIL's head while the rail
   is open and in the agenda column's head once it is not — a control inside
   the thing it folds cannot unfold it. */
.basel-prep-colk .basel-menu-toggle { margin-left: auto; }

/* WHAT IS NEW, NOT WHAT IS THERE (ADR-133). A count of 18 is not news: each
   fold says how much of it arrived since the last published meeting, in the
   waiting standing's own ink, or "nothing new" quietly. It is the header's
   last word, so the eye reads name · count · news across every fold. */
.basel-prep-src-new { margin-left: auto; font-weight: 700; white-space: nowrap; }
.basel-prep-src-new--none { color: var(--basel-ink-muted); font-weight: 400; }

/* A SOURCE IS A FOLD AND NOTHING ELSE. No number track, no tick, no grip —
   it is not an item of the agenda, so all three would be claims about it that
   are not true. `.basel-group` is the fold this app already draws everywhere. */
.basel-prep-src + .basel-prep-src { margin-top: 2px; }
.basel-prep-src > .basel-group { border-radius: 0; flex-wrap: wrap; row-gap: 2px; }
.basel-prep-src .basel-prep-body { padding: 0 var(--basel-pad-group-x) 14px; }

/* A SOURCE HEADER IS TWO LINES IN A 300px RAIL (ADR-133), and the fold is
   named on the first of them. Its count is a sentence — "last 7 days · 0",
   "Feasibility · target 2026-09-25" — so at 267px of content the name and the
   count fought and BOTH ellipsised. The name takes the first line whole, the
   count and what is new share the second, and the name never clips
   (`.basel-prep-src > .basel-group`, above). */
.basel-prep-src-sub {
    flex: 1 1 100%;
    display: flex;
    align-items: baseline;
    gap: 12px;
    min-width: 0;
    padding-left: 28px;                /* under the name, clear of the chevron */
}
.basel-prep-src-sub > .basel-group-count { min-width: 0; white-space: normal; }

/* The one verb a source row carries, UNDER the row it belongs to (ADR-133):
   beside it, in a rail this narrow, the verb took the width the record's own
   words needed and every id in the list came back ellipsised. */
.basel-prep-src-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
    gap: 2px;
    align-items: start;
}
.basel-prep-src-rec { min-width: 0; }
.basel-prep-src-verb { white-space: nowrap; justify-self: start; }

/* The meeting's own facts, under the agenda: who is expected, and the three
   fields that say which meeting this is. Closed off by the same 2px ink rule a
   section head takes, because it is about the meeting and not on it. */
.basel-prep-meet {
    margin-top: 18px;
    padding-top: 4px;
    border-top: var(--basel-rule-section) solid var(--basel-ink);
}

/* On a topic: the second door (ADR-085). Three tracks — the radio, the topic's
   own title, and the word that says it is already there. */
.basel-prep-onatopic-rec {
    display: grid;
    grid-template-columns: 62px 84px minmax(0, 1fr);
    gap: 11px;
    align-items: baseline;
    padding: 9px 0;
    border-top: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    margin-bottom: 14px;
}
.basel-onatopic-row {
    display: grid;
    grid-template-columns: 14px minmax(0, 1fr) 92px;
    gap: 11px;
    align-items: baseline;
    padding: 7px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    cursor: pointer;
}
.basel-onatopic-row .basel-topicpick-d { color: var(--basel-ink); }

/* ---- Records on a topic: the picker ------------------------------------- */

.basel-topicpick {
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    gap: 0;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
}
.basel-topicpick-col { padding: 14px 16px; min-width: 0; }
.basel-topicpick-col + .basel-topicpick-col {
    border-left: var(--basel-rule-row) solid var(--basel-hair);
    background-color: var(--basel-sheet-sunken);
}
.basel-topicpick-k { color: var(--basel-ink-muted); margin-bottom: 9px; }
.basel-topicpick-col .basel-input { width: 100%; margin-bottom: 8px; }
/* A stated height and its own scroll, so a project with 200 records cannot make
   this dialog taller than the window — §12.11 finding 3's rule, one component
   down: the section scrolls and the page body never does (§1). */
.basel-topicpick-list { max-height: 320px; overflow-y: auto; }
.basel-topicpick-row {
    display: grid;
    grid-template-columns: 14px 62px 84px minmax(0, 1fr);
    gap: 11px;
    align-items: baseline;
    padding: 7px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    cursor: pointer;
}
.basel-topicpick-d {
    color: var(--basel-ink-muted);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.basel-topicpick-open { display: inline-flex; align-items: center; gap: 9px; cursor: pointer; }

/* The pasted-agenda preview (ADR-080). The parser is deliberately stupid — it
   splits on line breaks and nothing else — so this is where a pasted email's
   greeting and sign-off are taken off, BY THE PM. A line that looks like
   wrapping arrives unticked with its reason beside it: a suggestion, never a
   deletion they have to notice. Number and words stay editable. */
.basel-clientprev-k { color: var(--basel-ink-muted); margin: 16px 0 6px; }
.basel-clientprev-row {
    display: grid;
    grid-template-columns: 14px 44px minmax(0, 1fr) 150px;
    gap: 11px;
    align-items: center;
    padding: 5px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    cursor: pointer;
}
.basel-clientprev-w { color: var(--basel-ink-muted); text-align: right; }

/* ============================================================================
   WORK NOT YET IN THE FILE (ADR-106, modules/merge-review.js)
   ----------------------------------------------------------------------------
   THE MERGE REVIEW IS A LOG, so almost everything it needs already exists:
   .basel-group, .basel-row, .basel-row-head, .basel-thead--log, .basel-check,
   .basel-btn and .basel-act are used exactly as the six log screens use them.
   What is below is the four-column cell grid that sits inside the row (the
   log's own --basel-row-cols answers a different question — mark, id,
   description, standing, need-by — and none of those five is what a change is)
   plus the two rows that are not record rows.

   NO COLOUR IS DECLARED HERE, AND THAT IS THE POINT (DESIGN §12.3). "Added",
   "changed" and "removed" are not standings: they do not answer "whose move is
   it", so they may not borrow a standing's shape or its hex. The operator is a
   mono glyph in ink and the kind is a muted eyebrow. The one red in this whole
   feature is the waiting-mark triangle on Home, which IS the PM's move.
   ========================================================================== */

/* what · the change · kind, after the operator. Description is the only
   flexible track, exactly as in a log row, so a long headline never pushes the
   kind column off its right edge. */
:root { --basel-merge-cols: 14px minmax(0, 1fr) 300px 84px; }

.basel-merge-cells {
    display: grid;
    grid-template-columns: var(--basel-merge-cols);
    gap: 16px;
    align-items: center;
    min-width: 0;
}
/* A conflict row spends the detail column on its two answers, so that track
   grows and the headline gives way — the question is the row's whole content. */
.basel-merge-cells--pick { grid-template-columns: 14px minmax(0, 1fr) 400px 84px; }

.basel-merge-op {
    font-size: 15px;
    font-weight: 700;
    color: var(--basel-ink);
}
.basel-merge-what {
    min-width: 0;
    font-size: 15px;
    line-height: 1.5;
    font-weight: 600;
    color: var(--basel-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.basel-merge-what--muted { color: var(--basel-ink-muted); font-weight: 600; }
.basel-merge-detail {
    min-width: 0;
    color: var(--basel-ink-muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.basel-merge-kind { color: var(--basel-ink-muted); text-align: right; }
/* `faint` is NON-TEXT ONLY (§12.4) — a locked row's word takes muted like every
   other, and what marks it apart is the paper ground under the row. */
.basel-merge-kind--locked { color: var(--basel-ink-muted); }

/* A row nothing can be done to sits on the page ground rather than the sheet:
   the sheet is where the records you can act on live. */
.basel-row--locked { background-color: var(--basel-paper); }

.basel-merge-picks { display: inline-flex; gap: 8px; min-width: 0; }
.basel-merge-picks .basel-act { min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* The group header's right-hand figure. Same slot .basel-group-worst uses on a
   log — where a collapsed section states the worst standing under it — saying
   the equivalent thing for this screen: how much of what is folded away is
   coming across. */
.basel-merge-tally { margin-left: auto; color: var(--basel-ink-muted); }
.basel-merge-note  { margin-left: auto; color: var(--basel-ink-muted); }

/* THE QUIET CAPTION LINE, not a filter toolbar (§12.9). It carries no
   [data-toolbar] and no Reset badge: nothing on it narrows the list. */
.basel-merge-caption {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 4px 14px;
    margin: 20px 0 14px;
    color: var(--basel-ink-muted);
}
.basel-merge-link {
    border: 0;
    background: none;
    padding: 0;
    font: inherit;
    color: var(--basel-ink-muted);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
}
.basel-merge-link:hover { color: var(--basel-ink); }
.basel-merge-link:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }

/* --- The whole-copy screen: two copies, no shared history ----------------- */
.basel-merge-two {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    margin-top: 24px;
    border-top: var(--basel-rule-section) solid var(--basel-ink);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-merge-side { padding: 16px var(--basel-pad-row-x); min-width: 0; }
.basel-merge-side--theirs { background-color: var(--basel-paper); border-right: var(--basel-rule-row) solid var(--basel-hair); }
.basel-merge-side--mine   { background-color: var(--basel-sheet); }
.basel-merge-side .text-basel-eyebrow { color: var(--basel-ink-muted); }
.basel-merge-side--mine .text-basel-eyebrow { color: var(--basel-ink); }
.basel-merge-side-when { margin-top: 4px; color: var(--basel-ink-muted); }
.basel-merge-side-figures { display: flex; flex-wrap: wrap; gap: 4px 18px; margin-top: 12px; }

.basel-merge-wholes { display: flex; flex-direction: column; margin-top: 18px; border-top: var(--basel-rule-row) solid var(--basel-hair); }
.basel-merge-whole {
    display: flex;
    align-items: baseline;
    gap: 16px;
    padding: 12px var(--basel-pad-row-x);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    cursor: pointer;
}
.basel-merge-whole.is-on { background-color: var(--basel-sheet); }
.basel-merge-whole-label { flex: 0 0 260px; font-size: 15px; font-weight: 600; color: var(--basel-ink); }
.basel-merge-whole-note  { min-width: 0; color: var(--basel-ink-muted); }

/* The panel's entry. It is a .basel-util like the rest of the Internal · PM
   group; the count beside it is the only place this feature spends red, and it
   is spent on the one fact that is genuinely the PM's move. */
/* .basel-util is `display: block`, so the count needs a flex row of its own
   rather than a bare margin-left:auto. Same anatomy the nav rows above it use:
   the label takes the room, the mono figure sits at the right edge. */
/* THE CHANGE COLUMN GIVES WAY BEFORE THE HEADLINE DOES. At 300px fixed, a
   1024px window left the description 156px and a 900px one left it 32px — the
   row still lined up perfectly and said nothing. What a row is ABOUT has to
   survive a narrow window; what it changed FROM can ellipsise, and below that
   the two stack, which is the treatment a log row's folder path already takes
   (.basel-row-desc--path). */
@media (max-width: 1200px) {
    :root { --basel-merge-cols: 14px minmax(0, 1fr) 210px 84px; }
    .basel-merge-cells--pick { grid-template-columns: 14px minmax(0, 1fr) 300px 84px; }
}
@media (max-width: 1000px) {
    .basel-merge-cells { grid-template-columns: 14px minmax(0, 1fr) 84px; }
    .basel-merge-cells--pick { grid-template-columns: 14px minmax(0, 1fr) 84px; }
    /* The headline keeps its column; the detail moves under it and the two
       share one cell, each still ellipsised on its own line. */
    .basel-merge-what { grid-column: 2; }
    .basel-merge-detail { grid-column: 2; grid-row: 2; }
    .basel-merge-picks { grid-column: 2; grid-row: 2; flex-wrap: wrap; }
    .basel-merge-kind { grid-column: 3; grid-row: 1; }
    .basel-merge-op { grid-row: 1; }
    /* THE HEADER HAS TO STACK WITH THE BODY. Its four plain spans flow into
       the three-column grid, so the fourth — Kind — wrapped onto a second row
       and the header ended up naming a column the rows no longer had. The
       detail's label goes instead: below this width the detail sits under the
       headline, in the column the header already names. */
    .basel-thead--log .basel-merge-cells > span:nth-child(3) { display: none; }
}

.basel-util--merge { display: flex; align-items: center; gap: 8px; }
.basel-merge-nav-count { margin-left: auto; color: var(--basel-waiting-ink); }

/* The "not in the file" line on Home's open project (ADR-106). A row, not a
   box: rules carry hierarchy in Basel and this is one more fact under the
   project's own facts — with the one waiting-mark this feature spends. */
.basel-home-notinfile {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 6px 14px;
    margin-top: 10px;
    padding-top: 10px;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-home-notinfile > svg { align-self: center; flex: 0 0 auto; }
.basel-home-notinfile-say { min-width: 0; color: var(--basel-ink); }
.basel-home-notinfile-n { color: var(--basel-waiting-ink); }
.basel-home-notinfile-acts { margin-left: auto; display: flex; align-items: center; gap: 8px; }

/* The assumed-ancestor banner (ADR-106). Neither copy carries a write stamp,
   so the list is built on an inference and says so before the PM reads a
   single row. A 2px ink rule and prose — not a coloured box: §12.3 has no
   colour for "be careful", and this is not a standing. */
.basel-merge-assumed {
    margin: 20px 0 0;
    padding: 12px 0 0;
    border-top: var(--basel-rule-section) solid var(--basel-ink);
    max-width: 88ch;
    color: var(--basel-ink-muted);
}
.basel-merge-assumed strong { color: var(--basel-ink); font-weight: 700; }
.basel-merge-assumed em { font-style: italic; }
/* The word as it appears in the Kind column, so the sentence points at
   something the eye can find. */
.basel-merge-kindword {
    font-size: 11px;
    line-height: 1.3;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--basel-ink);
}

/* ==========================================================================
   OUTSTANDING — the register, the letter, the sent log (DESIGN §12.44, ADR-097)
   --------------------------------------------------------------------------
   Three states of one screen, so one section rather than three. Nothing here
   invents a token: the grounds are paper/sheet/sunken, the rules are the two
   §12.5 already defines, the radius is 0 like everything else, and the only
   colour spent is --basel-waiting-ink on a date that has passed — which is
   the one thing on this screen that means "somebody's move is late", the
   sense §12.3 rations red to.

   THE TWO REGISTERS' COLUMNS ARE DECLARED TWICE ON PURPOSE — once on the head
   and once on the row — because .basel-thead's own 16px/1fr default would
   otherwise win, and a head that does not line up with its rows is the defect
   this screen would be least able to hide.
   ========================================================================== */

.basel-thead--out,
.basel-out-row {
    display: grid;
    grid-template-columns: 18px minmax(0, 1fr) 150px 84px 76px 104px 132px;
    gap: 14px;
    align-items: center;
}
.basel-thead--sent,
.basel-sent-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 170px 90px 118px 96px;
    gap: 14px;
    align-items: center;
}

.basel-out-group { background-color: var(--basel-sheet); }
.basel-out-row,
.basel-sent-row {
    padding: var(--basel-pad-row-y) var(--basel-pad-row-x);
    cursor: pointer;
}
.basel-out-row:hover,
.basel-sent-row:hover { background-color: var(--basel-paper); }
.basel-out-row:focus-visible,
.basel-sent-row:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }

.basel-out-name,
.basel-sent-subject { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 700; }
.basel-out-co { color: var(--basel-ink-muted); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A promised date that has passed, and nothing else (ADR-075's rule, kept). */
.basel-out-late { color: var(--basel-waiting-ink); font-weight: 700; }
.basel-out-verb { justify-self: end; }

/* ---- One person's items, under their row -------------------------------- */

.basel-out-items {
    border-top: var(--basel-rule-row) solid var(--basel-hair);
    background-color: var(--basel-paper);
    padding: 0 var(--basel-pad-row-x) 8px var(--basel-pad-nest-x);
}
.basel-out-item {
    display: grid;
    grid-template-columns: 18px minmax(0, 1fr) auto;
    gap: 12px;
    align-items: start;
    padding: 12px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-out-item:last-child { border-bottom: 0; }
.basel-out-item-body { min-width: 0; display: flex; flex-direction: column; gap: 3px; }
.basel-out-id { color: var(--basel-ink-muted); }
.basel-out-facts { display: flex; flex-wrap: wrap; gap: 4px 14px; color: var(--basel-ink-muted); }
.basel-out-facts > * + *::before { content: "\00b7 "; color: var(--basel-ink-faint); }
.basel-out-answer { margin-top: 8px; }
.basel-out-item-acts { display: flex; align-items: center; gap: 8px; }

/* ---- The two act variants this screen needed ---------------------------- */

.basel-act--primary {
    background-color: var(--basel-ink);
    border-color: var(--basel-ink);
    color: var(--basel-paper);
}
.basel-act--primary:hover { background-color: var(--basel-ink-muted); border-color: var(--basel-ink-muted); }
.basel-act--primary[disabled] { opacity: 0.5; cursor: default; }
/* Not a button, and shaped so it cannot be mistaken for one: "Assign first"
   is a sentence about why there is no verb here, not a verb. */
.basel-act--mute {
    border-color: var(--basel-hair);
    color: var(--basel-ink-muted);
    cursor: default;
}

.basel-out-back {
    border: 0;
    background: none;
    padding: 0 10px 0 0;
    color: var(--basel-working-ink);
    font-weight: 700;
    cursor: pointer;
}
.basel-out-back:hover { color: var(--basel-ink); }

/* ---- The drafting screen ------------------------------------------------ */

.basel-out-draft {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 470px;
    gap: 40px;
    align-items: start;
    margin-top: 8px;
}
@media (max-width: 1280px) {
    .basel-out-draft { grid-template-columns: minmax(0, 1fr); }
}
.basel-out-letter,
.basel-out-chat { min-width: 0; display: flex; flex-direction: column; gap: 22px; }
.basel-out-chat { gap: 0; }
.basel-out-field { display: flex; flex-direction: column; gap: 8px; }
.basel-out-addr { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.basel-ctl--to { border-color: var(--basel-ink); color: var(--basel-ink); gap: 8px; cursor: default; }
.basel-out-addr-mail { color: var(--basel-ink-muted); font-weight: 400; }

.basel-out-picks { display: flex; flex-direction: column; }
.basel-out-pick {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 9px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-out-pick:last-child { border-bottom: 0; }
.basel-out-pick-desc { min-width: 0; flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---- The conversation --------------------------------------------------- */

.basel-out-chat-head {
    display: flex;
    align-items: baseline;
    gap: 12px;
    padding-bottom: 9px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
}
.basel-out-chat-count { margin-left: auto; color: var(--basel-ink-muted); }

/* What the service was handed, said out loud rather than left to be guessed.
   Sunken, because it is the app talking about itself and not part of the
   thread. */
.basel-out-given {
    background-color: var(--basel-sheet-sunken);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    padding: 12px 14px;
    color: var(--basel-ink-muted);
}
.basel-out-given .basel-subhead { display: block; margin-bottom: 6px; }
.basel-out-given b { color: var(--basel-ink); }

/* A turn is a THREAD ENTRY, not a bubble: this app already has one vocabulary
   for "somebody said a thing at a time" and it is a hairline, a name in ink
   and a mono clock (§12.9). A chat bubble would be a second one. */
.basel-out-turn { padding: 13px 0; border-bottom: var(--basel-rule-row) solid var(--basel-hair); }
.basel-out-turn-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 4px; }
.basel-out-turn-head .font-basel-mono { color: var(--basel-ink-muted); }
.basel-out-turn-text { white-space: pre-wrap; }
.basel-out-turn-acts { display: flex; gap: 8px; margin-top: 10px; }
.basel-out-turn-wait { color: var(--basel-ink-muted); font-style: italic; }

.basel-out-composer { border-top: var(--basel-rule-section) solid var(--basel-ink); padding-top: 14px; margin-top: 4px; }
.basel-out-composer-row { display: flex; gap: 8px; align-items: stretch; }
.basel-out-composer-row .basel-textarea { flex: 1 1 auto; }
.basel-out-quick { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 10px; }

/* ---- The sent log ------------------------------------------------------- */

.basel-out-sent-body {
    border-top: var(--basel-rule-row) solid var(--basel-hair);
    background-color: var(--basel-paper);
    padding: 16px var(--basel-pad-row-x) 20px var(--basel-pad-nest-x);
}
.basel-out-sent-cols {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 400px;
    gap: 36px;
    align-items: start;
}
@media (max-width: 1280px) {
    .basel-out-sent-cols { grid-template-columns: minmax(0, 1fr); }
}
.basel-out-sent-side { display: flex; flex-direction: column; gap: 20px; min-width: 0; }
.basel-out-wrote {
    display: flex;
    gap: 10px;
    padding: 9px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    color: var(--basel-ink-muted);
}
.basel-out-wrote:last-child { border-bottom: 0; }

/* HANDED OVER, NOT SENT (ADR-078). The app watched a button being pressed; it
   cannot see an Outbox. The block is a sheet with a hairline, never a standing
   shape — "did that leave" is not anybody's move. */
.basel-out-delivery {
    background-color: var(--basel-sheet);
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    padding: 14px;
    color: var(--basel-ink-muted);
}
.basel-out-delivery .basel-flag { display: inline-block; margin-bottom: 8px; }
.basel-out-delivery p { margin: 0 0 12px; }
.basel-out-delivery-acts { display: flex; flex-wrap: wrap; gap: 8px; }

/* ======================= THE TIMELINE (§12.45, ADR-101) ====================
   The reading surface. Every value here comes from the --basel-* block, radius
   is 0, and the screen declares its own column set the way §12.11 finding 3
   requires of a screen that is not a log — a timeline row answers "when, what,
   whose", which is not the question --basel-row-cols answers.

   THE HERO AND THE STRIP ARE NOT REDRAWN. They are .basel-sched-* markup built
   by homeStripHtml() and the Schedule's own hero classes, so the slip and the
   events that caused it are literally the same graphic the Schedule draws. Only
   the right-hand facts column and the strip's own spacing are new here.
   ========================================================================= */

/* ---- The three states ---------------------------------------------------- */

.basel-tl-states {
    display: flex;
    gap: 0;
    margin-top: 18px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
}
.basel-tl-state {
    padding: 10px 18px;
    border: 0;
    border-radius: var(--basel-radius);
    background: none;
    color: var(--basel-ink-muted);
    font: inherit;
    font-size: 13px;
    line-height: 1.3;
    font-weight: 700;
    cursor: pointer;
}
.basel-tl-state:hover { color: var(--basel-ink); }
/* The state you are in is a FILL, not a rule: three states side by side need
   the current one to read at a glance across a room on a screen share, and a
   3px underline is the nav's own device, already spoken for. */
.basel-tl-state--on { background-color: var(--basel-ink); color: var(--basel-paper); }
.basel-tl-state:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }

/* ---- The hero's right-hand column ---------------------------------------- */

.basel-tl-facts { display: flex; flex-direction: column; }
.basel-tl-fact {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 16px;
    padding: 7px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-tl-fact:last-child { border-bottom: 0; }
.basel-tl-fact > span:first-child { color: var(--basel-ink-muted); }
.basel-tl-strip { margin-top: 22px; }
.basel-tl-strip .basel-sched-strip { margin-top: 0; }

/* ---- The stream ---------------------------------------------------------- */

/* THE STREAM'S COLUMNS, DECLARED ONCE (ADR-103). The header, the jump button's
   own cells and the Follow gutter all read this, so they cannot drift out of
   line. The row is a grid of two parts — the jump, which carries its own copy
   of these six columns, and the verb beside it — because a button inside a
   button is invalid and does not take a click. */
:root { --basel-tl-cols: 78px 14px 78px minmax(0, 1fr) 168px 96px; }

.basel-thead--tl {
    display: grid;
    grid-template-columns: var(--basel-tl-cols) var(--basel-tl-follow-w);
    gap: 16px;
    align-items: center;
    padding-left: var(--basel-pad-rowlead-x);
}
.basel-thead--tl > span { color: var(--basel-ink-muted); }

/* The verb's gutter. Wide enough for the word and its 1px box, and NEVER
   collapsed away on a row that has no verb: a column that appears and vanishes
   moves every cell beside it as you read down the list. */
:root { --basel-tl-follow-w: 76px; }

/* The month band. A 2px ink rule under it — the same device that closes a
   record headline and a log's column head, because a month is a section here. */
.basel-tl-month {
    display: flex;
    align-items: baseline;
    gap: 14px;
    padding: 20px var(--basel-pad-row-x) 9px var(--basel-pad-rowlead-x);
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
    background-color: var(--basel-paper);
}
.basel-tl-month > span:last-child { color: var(--basel-ink-muted); }

.basel-tl-row {
    display: grid;
    grid-template-columns: minmax(0, 1fr) var(--basel-tl-follow-w);
    gap: 16px;
    align-items: center;
    padding: var(--basel-pad-row-y) var(--basel-pad-row-x)
             var(--basel-pad-row-y) var(--basel-pad-rowlead-x);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    background-color: var(--basel-sheet);
}
.basel-tl-row:hover { background-color: var(--basel-sheet-sunken); }
.basel-tl-row-open {
    display: grid;
    grid-template-columns: var(--basel-tl-cols);
    gap: 16px;
    align-items: center;
    min-width: 0;
    width: 100%;
    padding: 0;
    border: 0;
    border-radius: var(--basel-radius);
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.basel-tl-row-open--flat { cursor: default; }
.basel-tl-row-open:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }
/* The verb is drawn at rest, on every record row, and is never revealed by a
   pointer (ADR-094's rule): this screen is driven on a share, and hover does
   not exist on touch. An empty span holds the column open where no record can
   be followed, so nothing beside it shifts as the list is read. */
.basel-tl-follow { justify-self: end; }
button.basel-tl-follow { font-weight: 600; }
.basel-tl-mark { display: inline-flex; align-items: center; }
.basel-tl-date, .basel-tl-who, .basel-tl-kind { color: var(--basel-ink-muted); }
.basel-tl-ref { color: var(--basel-ink); }
/* One line, ellipsised — a stream that wraps stops being scannable at forty
   events, which is the whole reason it is a stream and not a set of cards. */
.basel-tl-desc {
    min-width: 0;
    color: var(--basel-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* A record's OWN line carries the weight; an update against it does not. That
   is the only hierarchy in the stream and it is WEIGHT rather than a shape,
   because "is this the record or a note on it" is not anybody's move (§12.3). */
.basel-tl-desc--lead { font-weight: 600; }

/* ---- The conversation ---------------------------------------------------- */

.basel-tl-chat {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 1px 360px;
    gap: 0 40px;
    align-items: start;
    margin-top: 26px;
}
@media (max-width: 1100px) {
    .basel-tl-chat { grid-template-columns: minmax(0, 1fr); }
    .basel-tl-chat-div { display: none; }
}
.basel-tl-chat-main { min-width: 0; }
.basel-tl-chat-div { display: block; align-self: stretch; background-color: var(--basel-hair); }
.basel-tl-eyebrow { color: var(--basel-ink-muted); padding-bottom: 6px; }

.basel-tl-title { margin-bottom: 16px; }
.basel-tl-title .basel-ctl {
    display: block;
    width: 100%;
    height: auto;
    padding: 10px 14px;
    text-transform: none;
    letter-spacing: normal;
    color: var(--basel-ink);
}

.basel-tl-turns { display: flex; flex-direction: column; gap: 12px; }
.basel-tl-turn {
    padding: 14px 18px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet);
}
/* EVIDENCE AND RECOLLECTION NEVER SHARE A GROUND. The record's own answer sits
   on the sheet; what the PM added sits on the sunken one under a dated mark.
   Two years from now that ground is the only thing that says which half of a
   narrative the project can actually back — so it is a GROUND and not a colour,
   because §12.3 rations every colour this app has to whose move something is. */
.basel-tl-turn--memory { background-color: var(--basel-sheet-sunken); }
/* A LINE HAS A LENGTH (ADR-104). An answer ran the full width of its column —
   past 120 characters a line on a wide screen — which is a wall before a single
   word of it is read. 68ch is the measure the rest of this app's prose already
   sits at (the Why paragraph, the chase letter), and it is why those read and
   this did not. */
.basel-tl-answer { max-width: 68ch; }
.basel-tl-turn-foot { color: var(--basel-ink-muted); margin-top: 8px; }
.basel-tl-turn-actions { display: flex; gap: 8px; margin-top: 14px; }

.basel-tl-openings { margin-top: 4px; }
.basel-tl-opening {
    display: block;
    width: 100%;
    padding: 11px 0;
    border: 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    border-radius: var(--basel-radius);
    background: none;
    color: var(--basel-ink);
    font: inherit;
    font-size: 15px;
    line-height: 1.5;
    text-align: left;
    cursor: pointer;
}
.basel-tl-opening:hover { color: var(--basel-ink-muted); }
.basel-tl-opening:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }

.basel-tl-composer {
    margin-top: 20px;
    border: var(--basel-rule-row) solid var(--basel-ink);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet);
}
.basel-tl-composer textarea {
    display: block;
    width: 100%;
    height: auto;
    border: 0;
    padding: 14px 18px;
    background: none;
    color: var(--basel-ink);
    text-transform: none;
    letter-spacing: normal;
    font-weight: 400;
    resize: vertical;
}
.basel-tl-composer textarea:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }
.basel-tl-composer-foot {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 18px;
    border-top: var(--basel-rule-row) solid var(--basel-hair);
    background-color: var(--basel-sheet-sunken);
}
.basel-tl-composer-service { margin-left: auto; color: var(--basel-ink-muted); }

/* ---- The scope rail ------------------------------------------------------ */

.basel-tl-rail { min-width: 0; }
.basel-tl-rail-head {
    color: var(--basel-ink-muted);
    padding-bottom: 12px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
}
.basel-tl-rail-head + .basel-tl-fact { padding-top: 9px; }
/* Only a SECOND heading takes air above it. The rail's two sections are
   "what it reads" and "what you added", and without this they run together —
   which is the one separation on this screen that has to be unmissable. */
.basel-tl-rail > .basel-tl-rail-head:not(:first-child) { margin-top: 30px; }
.basel-tl-rail-what {
    color: var(--basel-ink);
    padding: 7px 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-tl-rail-row { padding: 10px 0; border-bottom: var(--basel-rule-row) solid var(--basel-hair); }
.basel-tl-rail-date { color: var(--basel-ink-muted); }
.basel-tl-rail-empty { color: var(--basel-ink-muted); margin: 10px 0 0; }
.basel-tl-rail-note {
    margin-top: 26px;
    padding: 14px 16px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet-sunken);
}

/* ---- The filed register -------------------------------------------------- */

.basel-thead--tlf,
.basel-tl-frow-head {
    display: grid;
    grid-template-columns: 92px minmax(0, 1fr) 200px 90px 72px 24px;
    gap: 16px;
    align-items: center;
}
.basel-thead--tlf { padding-left: var(--basel-pad-rowlead-x); }
.basel-thead--tlf > span { color: var(--basel-ink-muted); }

.basel-tl-frow {
    background-color: var(--basel-sheet);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-tl-frow-head {
    width: 100%;
    padding: var(--basel-pad-row-y) var(--basel-pad-row-x)
             var(--basel-pad-row-y) var(--basel-pad-rowlead-x);
    border: 0;
    border-radius: var(--basel-radius);
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.basel-tl-frow-head:hover { background-color: var(--basel-sheet-sunken); }
.basel-tl-frow-head:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }
.basel-tl-frow-title {
    min-width: 0;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.basel-tl-frow-chev { color: var(--basel-ink-faint); text-align: right; }
.basel-tl-frow-body {
    padding: 4px var(--basel-pad-row-x) 22px var(--basel-pad-rowlead-x);
    border-top: var(--basel-rule-row) solid var(--basel-hair);
}
.basel-tl-conclusion { color: var(--basel-ink); }
.basel-tl-chips { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.basel-tl-added {
    margin-top: 22px;
    padding: 14px 18px;
    max-width: 900px;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet-sunken);
}
.basel-tl-added-line { padding: 4px 0; color: var(--basel-ink); }
.basel-tl-added-line span { color: var(--basel-ink-muted); }
.basel-tl-frow-actions { display: flex; gap: 10px; margin-top: 20px; }

/* ---- The scope IS the head (ADR-103) ------------------------------------- */

/* The head keeps .basel-screen-head's own grid; these are the parts that only
   exist while one record is the subject. */
.basel-tl-subject-lead {
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: 15px;
    white-space: nowrap;
    color: var(--basel-ink-muted);
}
.basel-screen-back {
    border: 0;
    background: none;
    padding: 0;
    color: var(--basel-ink);
    font: inherit;
    letter-spacing: inherit;
    text-transform: inherit;
    /* One line, always. It is two words at 11px with .16em tracking, and a head
       that wraps its own way out puts the separator rule through the middle of
       it. */
    white-space: nowrap;
    cursor: pointer;
}
.basel-screen-back:hover { color: var(--basel-ink-muted); }
.basel-screen-back:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: 2px; }
.basel-tl-subject-sep {
    width: var(--basel-rule-row);
    align-self: stretch;
    background-color: var(--basel-hair-strong);
}
.basel-tl-subject-id {
    display: flex;
    align-items: center;
    gap: 12px;
    margin-top: 6px;
    color: var(--basel-ink-muted);
}
.basel-tl-subject-id > span:first-child { color: var(--basel-ink); }
.basel-tl-subject-mark { display: inline-flex; align-items: center; }
/* THE RECORD'S OWN SENTENCE, AS THE SCREEN'S NAME. One step under the
   Schedule's 40px hero name and above .text-basel-head, because it is the
   subject of this screen but not the subject of the project. Two lines, then
   it ellipsises — a headline that wraps to four pushes the whole stream off
   the fold. */
.basel-tl-subject-name {
    margin: 8px 0 0;
    font-size: 26px;
    line-height: 1.18;
    letter-spacing: -0.025em;
    font-weight: 800;
    color: var(--basel-ink);
    text-wrap: balance;
    max-width: 44ch;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.basel-tl-date-in {
    border: 0;
    background: none;
    padding: 0;
    color: var(--basel-ink);
    font: inherit;
    font-weight: 700;
    cursor: pointer;
}

/* ---- The record picker (ADR-105) -----------------------------------------
   The app draws this list, not the browser. A native <datalist> cannot be
   grouped, cannot be styled, ignores <optgroup> and truncates its own options
   in system chrome — so twenty records arrived as one flat wall in a control
   that looked nothing like this app. The vocabulary below is `.basel-who-*`'s,
   because this is the same component ADR-027 already built: a grouped list of
   things to pick, under a field you type into. */

.basel-tl-picker { margin-top: 22px; }
.basel-tl-picker .basel-subhead { display: block; color: var(--basel-ink-muted); padding-bottom: 8px; }
.basel-tl-pick-find { display: flex; align-items: stretch; gap: 6px; }
.basel-tl-pick-find .basel-input { max-width: 560px; }

.basel-tl-picklist {
    margin-top: 10px;
    max-width: 760px;
    /* Scrolls rather than caps: a record left off a list with nothing on screen
       to say so is the failure this app spends most of its guards preventing. */
    max-height: 360px;
    overflow-y: auto;
    border: var(--basel-rule-row) solid var(--basel-hair-strong);
    border-radius: var(--basel-radius);
    background-color: var(--basel-sheet);
}
/* STICKY, so the log you are reading is still named after ten rows of it. */
.basel-tl-pick-group {
    position: sticky;
    top: 0;
    z-index: 1;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    padding: 7px 12px 5px;
    background-color: var(--basel-sheet-sunken);
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    color: var(--basel-ink);
}
.basel-tl-pick-group > span:last-child { color: var(--basel-ink-muted); }
.basel-tl-pick-row {
    display: grid;
    grid-template-columns: 14px 78px minmax(0, 1fr);
    gap: 12px;
    align-items: center;
    width: 100%;
    padding: 7px 12px;
    border: 0;
    border-bottom: var(--basel-rule-row) solid var(--basel-hair);
    border-radius: var(--basel-radius);
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}
.basel-tl-pick-row:hover { background-color: var(--basel-sheet-sunken); }
.basel-tl-pick-row:focus-visible { outline: 2px solid var(--basel-ink); outline-offset: -2px; }
.basel-tl-pick-mark { display: inline-flex; align-items: center; }
.basel-tl-pick-id { color: var(--basel-ink); }
.basel-tl-pick-desc {
    min-width: 0;
    color: var(--basel-ink);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.basel-tl-pick-empty { color: var(--basel-ink-muted); margin: 0; padding: 14px 12px; }
.basel-tl-caption { color: var(--basel-ink-muted); margin: 22px 0 0; }

/* ==========================================================================
   A FILED MEETING IS READ, AND CORRECTED — ADR-137, DESIGN §12.49
   --------------------------------------------------------------------------
   Three additions and no repaint. An item OPEN on a filed meeting is the same
   component as an item open mid-meeting; the only difference is that it does
   not carry the 3px "you are here" rule, because a meeting that has been filed
   has no here. It therefore needs no rule of its own — `--open` exists so that
   `--current`'s ::before does not match it.
   ========================================================================== */
.basel-agenda-item--open { position: relative; }

/* WHEN THE NOTES WERE LAST TOUCHED, on the meeting head's dates line. The
   quiet-phrase idiom §12.28 gives every fact that is not a control: muted
   ink, the app's own note size, the date in the mono beside it. */
.basel-meet-touched {
    display: inline-flex;
    align-items: baseline;
    gap: 5px;
    color: var(--basel-ink-muted);
    white-space: nowrap;
}

/* THE LATE NOTE (ADR-137) — the correction editor's anatomy at a smaller size,
   never the composer bar's. The picker and the textarea share one row at
   `start`, because the picker is a 34px control and the textarea grows: on a
   centred row the button drifts down the box as the note gets longer. */
.basel-agenda-late-cells {
    display: flex;
    align-items: flex-start;
    gap: 12px;
    margin-bottom: 8px;
}
.basel-agenda-late-cells > .basel-who-host { flex: 0 0 auto; }
.basel-agenda-late-cells > .basel-who-host > .basel-who > .basel-multi-btn { height: 34px; }
.basel-agenda-late-cells > .basel-textarea { flex: 1 1 auto; min-width: 0; }

/* ==========================================================================
   FILING MOVES THE MEETING — ADR-138, DESIGN §12.52
   --------------------------------------------------------------------------
   Two additions. THE EMPTY STATE of This meeting gains a row of verbs — the
   `.basel-empty` anatomy above it is unchanged, and the verbs sit under the
   sentence at the same left edge as everything else in the block.
   ========================================================================== */
.basel-empty-acts {
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    margin-top: 8px;
}

/* THE HEAD OF AN OPENED REGISTER ROW. A filed meeting no longer sits on This
   meeting, so its hero is never drawn; the three controls that lived on it —
   the dots, the hours field and the "notes changed" mark — are on one quiet
   line here instead, at the row's own size and never a hero's. The 2px ink
   rule under it is §12.5's section boundary, the same rule the meeting head
   carried. */
.basel-arch-bodyhead {
    display: flex;
    align-items: center;
    gap: 14px;
    flex-wrap: wrap;
    padding: 12px 0 9px;
    border-bottom: var(--basel-rule-section) solid var(--basel-ink);
}
.basel-arch-bodyhead-k { color: var(--basel-ink-muted); }
.basel-arch-bodyhead .basel-meet-menu { margin-left: auto; }

/* THE PRE-2.30 PROSE IS CORRECTABLE (ADR-138). Its heading carries the verb,
   which is the note thread's own idiom (§12.43): always there, quietest
   repetition available, past the words rather than on hover. */
.basel-meet-legacy-k {
    display: flex;
    align-items: baseline;
    gap: 12px;
    margin-top: 10px;
    color: var(--basel-ink-muted);
}
.basel-meet-legacy-k > .basel-post-edit { margin-left: 0; }
