/*
 * ============================================================================
 * app.css — CSS entry point for the authenticated / Control-adjacent surfaces
 * ============================================================================
 * WHAT: the single <link> a Control-side page loads (login, magic-link verify,
 *   code entry, password set, Live, Editor, tenant dashboard, onboarding,
 *   super-admin). It declares the @layer order (docs/blueprint/03-gui.md §1) and
 *   pulls in the shared token + component layers, then adds the Control-surface
 *   compositions in @layer patterns. Skærm/Overblik have their own entry
 *   (display.css / overblik.css) — same tokens + components, different patterns.
 *
 * ROLE: <link rel="stylesheet" href="/css/app.css"> on any render_bare/layout
 *   Control page. Plain CSS, no build step, same-origin only (CSP-safe: no CDN
 *   @import — see FONTS note below).
 *
 * FONTS: self-hosted via fonts.css (@font-face, woff2) — never a Google-Fonts CDN
 *   @import, which the Slice 1.5 CSP (style-src 'self') blocks. Same fonts.css is
 *   used by display.css, so every surface shares one typeface source (D4 offline-safe).
 * ============================================================================
 */
@layer reset, tokens, base, components, patterns, utilities;

@import url('/css/tokens.css') layer(tokens);
@import url('/css/fonts.css');
@import url('/css/components.css');

/* ─────────────────────────────────────────────────────────────────────────
 * patterns — Control-surface composition (§8). Surface-specific only; every
 * primitive it uses is a semantic token, every piece it shows is a §6 component.
 * ───────────────────────────────────────────────────────────────────────── */
@layer patterns{

  /* Centered auth / onboarding card — shared by /login, /login/verify,
     /login/kode (magic-link code), /vaelg-kodeord. One column, vertically
     centered, card-contained. The 6-digit code page composes .pair/.pair-code
     (components.css) inside .auth-card; nothing here duplicates a component. */
  .auth{
    min-block-size:100dvh;display:grid;place-items:center;
    padding:var(--space-6);background:var(--bg);
  }
  .auth-card{
    inline-size:min(26rem,100%);background:var(--surface);
    border:1px solid var(--edge);border-radius:var(--radius-lg);
    padding:var(--space-7);display:flex;flex-direction:column;gap:var(--space-5);
  }
  .auth-card > header{display:flex;flex-direction:column;gap:var(--space-2)}
  .auth-card h1{font-size:var(--text-heading)}
  .auth-lede{color:var(--tx-mut);font-size:var(--text-body)}

  /* Auth forms use the canonical .field / .field-label / .input (components.css)
     and .notice-inline.is-danger for errors — no auth-local field/error styling. */
  .auth .btn-primary{inline-size:100%}
  .auth-note{color:var(--tx-mut);font-size:var(--text-caption);text-align:center}

  /* .raw-token (a copyable one-time bearer-token/pairing-code display) and
     .auth-card.in-shell (its placement variant, task #45 — generalised from
     `.admin > .auth-card` so a second consumer outside `.admin` would not have
     to copy the rule) were removed 2026-08-17 with box_created.php/
     box_paircode.php, their only two consumers — the ESP32/FOH box itself is
     gone. If a future feature needs either shape again, re-add it earning its
     own rule rather than reviving a comment describing a page that no longer
     exists (rule 7 — this exact drift is what the generalisation was meant to
     avoid, and an unused generalised rule is the same blind spot either way). */

  /* ───────────────────────────────────────────────────────────────────────
   * /tilknyt — screen-pairing keypad + two-step type choice (GUI-V3 §3 skærm 4,
   * defect #4). Composes inside .auth-card like every other pre-session page; the
   * pin boxes/keypad are the ONE new input pattern this page needs, everything
   * else (.notice, .btn-primary/.btn-ghost) is already canonical.
   * ─────────────────────────────────────────────────────────────────────── */
  /* Six digit "read-out" boxes. The REAL input is .pin-input, a transparent,
     focusable native <input> stretched over the boxes (position:absolute inside
     the relatively-positioned .pin-boxes) — so a physical keyboard works
     identically to the on-screen keypad; the boxes are a pure visual readout
     driven by tilknyt.js, never a second source of truth for the typed value. */
  .pin-boxes{position:relative;display:flex;gap:var(--space-2);justify-content:center;margin-block-end:var(--space-2)}
  .pin-box{inline-size:2.6rem;block-size:3.2rem;border:1px solid var(--edge-2);border-radius:var(--radius-sm);
    background:var(--surface-2);display:flex;align-items:center;justify-content:center;
    font-family:var(--font-mono);font-size:var(--text-heading);font-weight:700;color:var(--tx)}
  .pin-box.is-active{border-color:var(--accent);box-shadow:var(--focus-ring)}
  .pin-box.is-filled{border-color:var(--edge-3)}
  .pin-input{position:absolute;inset:0;opacity:0;border:0;padding:0;margin:0;cursor:pointer}
  .pin-input:disabled{cursor:not-allowed}

  /* Big numeric keypad — large touch targets for a tablet installer in the dark
     with gloves on (README §3 skærm 4). aria-hidden (the real input above is what
     assistive tech + a physical keyboard use); square buttons via aspect-ratio. */
  .keypad{display:grid;grid-template-columns:repeat(3,1fr);gap:var(--space-3)}
  .keypad button{aspect-ratio:1;font-family:var(--font-mono);font-size:var(--text-display);
    font-weight:700;background:var(--surface-2);border:1px solid var(--edge-2);
    border-radius:var(--radius-md);color:var(--tx);cursor:pointer}
  .keypad button:active{transform:scale(.96)}
  .keypad .kp-clear,.keypad .kp-back{font-family:var(--font-ui);font-size:var(--text-caption);
    font-weight:600;color:var(--tx-mut)}

  /* Wrong-PIN shake — the whole card shakes once; pairs with .notice.is-danger,
     never a colour change alone (a11y — motion + colour + text, not colour alone). */
  @keyframes pinShake{
    10%,90%{transform:translateX(-1px)} 20%,80%{transform:translateX(2px)}
    30%,50%,70%{transform:translateX(-4px)} 40%,60%{transform:translateX(4px)}
  }
  .is-shake{animation:pinShake .4s}
  @media (prefers-reduced-motion: reduce){ .is-shake{animation:none} }

  /* Step-2 type choice — big radio "cards", not a <select> (thumb-operated in the
     dark, README §3 skærm 4). :has() drives the checked-state styling; no JS
     needed for the selection look, only for the (optional) submit. */
  .choices{display:flex;flex-direction:column;gap:var(--space-3)}
  .choice-card{position:relative;display:flex;flex-direction:column;gap:var(--space-1);
    text-align:left;padding:var(--space-4) var(--space-5);border:1px solid var(--edge-2);
    border-radius:var(--radius-lg);background:var(--surface-2);color:var(--tx);cursor:pointer}
  .choice-card input{position:absolute;inline-size:1px;block-size:1px;opacity:0;margin:0}
  .choice-card:has(input:checked){border-color:var(--accent);background:var(--accent-tint)}
  .choice-card:has(input:focus-visible){box-shadow:var(--focus-ring)}
  .choice-card .cc-title{font-family:var(--font-display);font-weight:700;font-size:var(--text-body)}
  .choice-card .cc-sub{color:var(--tx-mut);font-size:var(--text-caption)}

  /* ───────────────────────────────────────────────────────────────────────
   * Live (/control/show/{id}) — the operator run screen (§8 "Live" row). The
   * ONLY surface with transport buttons (AGENTS.md §5's four-surfaces table;
   * cited as "CLAUDE.md" before the 2026-08-18 handover). It is
   * Overblik's read-only run view (hero clock + Nu/Næste/Derefter + rundown +
   * timeline, all §6 components) PLUS operator chrome: a topbar, the transport
   * row, and the nudge row. Every piece below is composed from components.css;
   * these patterns only lay them out and add the operator-only bits.
   *
   * SAFETY: the transport buttons ARE wired (real POST forms in live.php, a
   * real fetch() in live.js — see live.php's own header, "NO TEST-FIRED
   * TRANSPORT") — this stylesheet only lays them out. The transport-test rule
   * (AGENTS.md §5 "Also unconditional"; "CLAUDE.md #3" pre-2026-08-18) still
   * applies to how the team tests this file: never fire a transport call
   * against a running scene as a test.
   * ─────────────────────────────────────────────────────────────────────── */
  .op{max-inline-size:60rem;margin-inline:auto;padding:var(--space-5) var(--space-5) var(--space-9);
    display:flex;flex-direction:column;gap:var(--space-5)}
  /* Topbar uses the canonical .page-header (brand + .sub day + .actions). */

  /* Operator "now" card — the hero clock + type/drift/band, composed for the
     laptop-width operator view (container-scaled hero via .clock-wrap). */
  .op-clock{background:var(--surface);border:1px solid var(--edge);border-radius:var(--radius-lg);
    padding:var(--space-5);display:flex;flex-direction:column;gap:var(--space-2);text-align:center}
  .op-clock .lc-top{display:flex;justify-content:space-between;align-items:baseline;gap:var(--space-3)}
  .op-clock .lc-type{font-family:var(--font-display);font-weight:700;font-size:var(--text-body);
    letter-spacing:.06em;text-transform:uppercase;color:var(--accent)}
  .op-clock .lc-drift{font-weight:600;font-size:var(--text-caption);color:var(--tx-mut)}
  .op-clock .lc-band{font-family:var(--font-display);font-weight:700;font-size:var(--text-heading)}
  /* the hero clock number sits in a fixed-height container so cqb scaling has a box to fill */
  .op-clock .clock-wrap{block-size:clamp(4.5rem,18vw,7rem)}
  .op-clock .lc-sub{color:var(--tx-mut);font-size:var(--text-caption)}
  /* GUI-mockup threshold fase-bar in the operator hero clock (shrinks; colour set by live.js from the
   * shared flow — same green/yellow/red thresholds as /display + /overblik). Glow rides currentColor. */
  .op-clock .lc-bar{height:.55rem;border-radius:var(--radius-pill,99px);background:var(--surface-2);
    overflow:hidden;margin-block:var(--space-2) 0}
  .op-clock .lc-bar-fill{height:100%;border-radius:var(--radius-pill,99px);box-shadow:0 0 12px currentColor;
    transition:width .5s linear,background .5s}
  /* Wait-gap ('waitgap' shared state): the operator hero clock goes calm blue — waiting for a fixed time. */
  .op-clock.is-wait .lc-type,.op-clock.is-wait .hero-clock{color:#38bdf8}
  /* Anchor-pres: overtime eating a downstream FIXED time → red alarm on the hero clock (proportionate to
   * the operator screen, not whole-screen). Visual only; never moves the sacred time (rule 2). */
  @keyframes opAnchorGlow{
    0%,100%{box-shadow:inset 0 0 0 2px rgba(239,68,68,.6),0 0 18px 2px rgba(239,68,68,.25)}
    50%{box-shadow:inset 0 0 0 3px rgba(239,68,68,.95),0 0 30px 6px rgba(239,68,68,.45)}
  }
  .op-clock.anchor-pres{color-scheme:dark;background:#160a0c;border-color:var(--danger);animation:opAnchorGlow 2.4s ease-in-out infinite}
  .op-clock.anchor-pres .lc-type,.op-clock.anchor-pres .hero-clock{color:var(--danger)}
  /* Found task #17 (Live rebuild): the background above is a hardcoded dark alarm colour,
   * unconditional in EITHER theme (deliberate — a hard alarm state, not a themed surface). But
   * only .lc-type/.hero-clock were ever recoloured for it; .lc-band/.lc-sub/.lc-drift stayed on
   * --tx/--tx-mut, which resolve DARK in light theme — near-invisible against #160a0c. Verified by
   * actually rendering this state in a real browser (light theme), not assumed: the band name
   * ("which show"), the PÅ/AF/Dagen-slut line and the drift line were all unreadable in exactly the
   * state where the operator most needs to read them (overtime eating a sacred time). Fixed with a
   * light, theme-independent colour on all three, same reasoning as the background itself. */
  .op-clock.anchor-pres .lc-band,.op-clock.anchor-pres .lc-sub,.op-clock.anchor-pres .lc-drift{color:var(--gray-50)}
  /* Task #34 (architect finding, page-3 review): the enumeration above works today but breaks
   * again the moment a SEVENTH element joins .anchor-pres without also getting added to this
   * list -- a forced dark background alone does not tell light-dark()-based descendant tokens
   * to stop following the page's theme, so any new child left on a bare `var(--tx*)` would
   * repeat exactly the invisible-text bug task #17 found. `color-scheme:dark` (same fix
   * `.skaerm-preview` below already uses for the same reason) makes every light-dark() token a
   * NEW child might reach for resolve correctly by construction, with no enumeration to
   * maintain. Additive only: the three explicit overrides above are UNCHANGED and still win
   * (equal specificity, declared later) for the deliberate red/gray-50 alarm palette -- this
   * is a safety net under them, not a replacement for them. */
  @media (prefers-reduced-motion: reduce){
    .op-clock.anchor-pres{animation:none;box-shadow:inset 0 0 0 3px rgba(239,68,68,.7)}
  }

  /* Transport row — the operator's primary controls. btn-hold copy/colour are
     load-bearing (§6): amber "bandet er stadig på scenen", danger while holding. */
  .transport{display:grid;grid-template-columns:1fr auto 1fr;gap:var(--space-3);align-items:stretch}
  .transport .btn{font-size:var(--text-heading);padding-block:var(--space-4)}
  .transport .btn-hold{min-inline-size:9rem}
  @container (max-width: 34rem){ .transport{grid-template-columns:1fr} }

  /* Nudge row — small ±1/±5 minute corrections, secondary weight (ghost). */
  .nudge-row{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;justify-content:center}
  .nudge-label{font-family:var(--font-mono);font-size:var(--text-caption);text-transform:uppercase;
    letter-spacing:.14em;color:var(--tx-mut);margin-inline-end:var(--space-2)}
  .nudge-row .btn{min-inline-size:3.5rem}
  /* GUI-mockup: +1/+5 are the most-used nudges → biggest + green so the thumb finds them in the dark. */
  .nudge-row .btn.is-up{min-inline-size:4.5rem;font-size:var(--text-body);font-weight:700;
    background:var(--ok);border-color:var(--ok);color:#062012}

  /* ── Inter-scene clear-signal (shows.cleared_at, migration 029 — PLAN-2026-08-20-inter-scene-
     signal.md option 1). TWO halves on this surface, both in the accent palette (information,
     "I kan begynde" — never the danger palette, no motion):
     1) #klarsignal — the OWN scene's give/fortryd row, server-rendered in live.php under
        $can_transport. .btn-klar (the give-button) is accent-TINT, deliberately quieter than the
        solid .btn-primary in the transport row above it: giving the clear-signal is a once-a-day
        act, not a primary control. .klar-status is the standing "KLAR MELDT 22.41" receipt.
     2) .klar-strip — the SIBLING scenes' incoming clears ('TELTSCENEN: KLAR 22.41'), created by
        live.js after #driftBanner ONLY when at least one sibling is cleared (zero cleared = zero
        DOM — see live.js's renderKlarStrip comment). Same row classes as overblik.css's own
        .klar-row block, translated to this surface's semantic tokens. */
  .klarsignal{display:flex;align-items:center;justify-content:center;gap:var(--space-3);flex-wrap:wrap}
  .btn-klar{background:var(--accent-tint);color:var(--accent-strong);border-color:var(--accent);
    min-inline-size:12rem;font-weight:700}
  .klar-status{font-family:var(--font-mono);font-weight:700;font-size:var(--text-body);
    letter-spacing:.06em;color:var(--accent-strong);white-space:nowrap}
  .klar-strip{display:flex;flex-direction:column;gap:var(--space-2)}
  .klar-row{display:flex;align-items:baseline;gap:var(--space-3);background:var(--surface);
    border:1px solid var(--accent);border-left-width:4px;border-radius:var(--radius-md);
    padding:var(--space-3) var(--space-4)}
  .klar-scene{font-family:var(--font-display);font-weight:700;letter-spacing:.04em;
    text-transform:uppercase;color:var(--accent);min-inline-size:0;overflow:hidden;
    text-overflow:ellipsis;white-space:nowrap}
  .klar-msg{font-family:var(--font-mono);font-weight:700;color:var(--tx);
    font-variant-numeric:tabular-nums;white-space:nowrap}

  /* Tap-row action-sheet (GUI-mockup Control-A) — bottom sheet: goto (primary) + Editor link. */
  .sheet{position:fixed;inset:0;z-index:60;display:flex;align-items:flex-end;justify-content:center}
  .sheet-scrim{position:absolute;inset:0;background:rgba(0,0,0,.55)}
  .sheet-panel{position:relative;inline-size:min(30rem,100%);background:var(--surface);border:1px solid var(--edge);
    border-radius:var(--radius-lg) var(--radius-lg) 0 0;padding:var(--space-4) var(--space-5) var(--space-6);
    display:flex;flex-direction:column;gap:var(--space-3);animation:sheetUp .22s ease-out}
  .sheet-grip{inline-size:2.5rem;block-size:.3rem;border-radius:99px;background:var(--edge-3);align-self:center;margin-block-end:var(--space-1)}
  .sheet-title{font-family:var(--font-display);font-weight:700;font-size:var(--text-heading);text-align:center}
  .sheet .btn{inline-size:100%;justify-content:center}
  .sheet .sheet-error{margin:0}
  @keyframes sheetUp{from{transform:translateY(100%)}to{transform:translateY(0)}}
  @media (prefers-reduced-motion: reduce){ .sheet-panel{animation:none} }
  /* Live rundown rows are tappable (open the action-sheet) — scoped to the operator surface. */
  .op .rundown-list .rl-row{cursor:pointer}

  /* Operator contacts row — the cards themselves are the canonical .contact-card
     (components.css); here they only grow to fill the row. In-page "not wired yet"
     notices use the canonical .notice.is-warn / .notice.is-ok. */
  .op-contacts{display:flex;flex-wrap:wrap;gap:var(--space-3)}
  .op-contacts .contact-card{flex:1 1 12rem}

  /* ───────────────────────────────────────────────────────────────────────
   * Editor (/control/show/{id}/edit) — the planner's surface (§8 "Editor"
   * row). NEVER has transport buttons. Uses the editable rundown-row variant
   * (components .rl-row.editable) plus an actions column.
   *
   * SACRED-TIME LOCK (AGENTS.md §5 "Also unconditional"; "CLAUDE.md #2"
   * pre-2026-08-18): get-in / change over / on-stage / off-stage
   * are anchored times the DB triggers forbid moving ONCE THE DAY HAS GONE LIVE
   * (shows.started_at set) — a fixed time on a day that hasn't started yet is
   * merely anchored, not yet sacred (2026-08-06 fix; see edit.php's own header
   * for the bug this replaced). Two classes, two truths: `.is-fixed` (has an
   * anchor time, either state) is not draggable and shows the chip-fixed "Fast
   * HH:MM" chip; `.is-locked` (anchor time AND gone live) is additionally
   * VISIBLY LOCKED — lock icon, "Låst" badge, no Rediger/Slet at all. This
   * mirrors the DB enforcement on screen so a planner sees why a time will not
   * move once it truly can't, rather than being surprised by a rejected save —
   * and is NOT shown as locked one moment sooner than the trigger actually is.
   *
   * TYPE LABELS ARE LOAD-BEARING: derive_parallel_points() looks phase_types up by
   * label ('Load-out', 'Scene klar'). The type chip shows the label verbatim; the
   * type picker must never silently rename one.
   *
   * SCOPE THIS SLICE: view + CSS. Drag-reorder, add/edit/delete save, and the
   * act dialog are JS/backend follow-up (Slice 6). Dialog below is styled but its
   * open/save wiring is not attached yet.
   * ─────────────────────────────────────────────────────────────────────── */
  .ed{max-inline-size:60rem;margin-inline:auto;padding:var(--space-5) var(--space-5) var(--space-9);
    display:flex;flex-direction:column;gap:var(--space-4)}
  /* Topbar + add-actions use the canonical .page-header / .page-header .actions. */
  .ed-note{color:var(--tx-mut);font-size:var(--text-caption)}

  .ed-list{display:flex;flex-direction:column;gap:var(--space-1);list-style:none;margin:0;padding:0}
  /* Extends .rl-row.editable — adds the trailing actions column. */
  .ed-row{grid-template-columns:auto auto auto minmax(0,1fr) auto auto;gap:var(--space-3);align-items:center}
  .ed-row .rl-handle{cursor:grab;color:var(--tx-mut);display:inline-flex}
  .ed-row .rl-handle .icon{width:1.1em;height:1.1em}
  /* Task #76: was white-space:nowrap + ellipsis at ALL widths -- silently hid the one thing
     this row exists to show (which act/band it is). Verified against a real render (not
     reasoned about): each .ed-row is its OWN grid (.ed-list is flex, not a shared/subgrid
     table), so the other five columns size from their OWN content (icon/HH:MM/chip/duration/
     buttons), never from the name column's height -- letting a long name wrap only grows
     THAT row; time/chip/duration/actions stayed pixel-identical across every other row in
     the test render, both a ~95-char space-separated name and a ~95-char unbreakable token.
     overflow-wrap:anywhere (not just white-space:normal, which is all the <34rem block below
     already had) also fixes a second bug this surfaced: an unbreakable single-token name was
     STILL silently ellipsis-clipped on phone, because that block only stopped the nowrap, not
     the inherited overflow:hidden/text-overflow:ellipsis underneath it. */
  .ed-name{color:var(--tx);white-space:normal;overflow-wrap:anywhere}
  .ed-meta{display:flex;align-items:center;gap:var(--space-2);justify-content:flex-end;
    font-family:var(--font-mono);font-size:var(--text-caption);color:var(--tx-mut);white-space:nowrap}
  .ed-row-actions{display:flex;gap:var(--space-1)}
  .ed-row-actions .btn{padding:var(--space-2) var(--space-3);font-size:var(--text-caption)}
  /* Desktop: an invisible grouping wrapper -- .ed-meta/.ed-row-actions stay direct grid
     items in the six-column line exactly as before it existed (task #53 addendum, see
     the phone @container block below for why it exists at all). */
  .ed-foot{display:contents}

  /* Fixed-anchor rows (not draggable either way — see block header): sacred-locked
     (.is-locked, gone live) gets the strongest treatment; merely-fixed (.is-fixed,
     not yet started) gets the same "can't drag" cursor but stays full-opacity/
     editable, unlike .is-auto below. */
  .ed-row.is-locked .rl-handle, .ed-row.is-fixed .rl-handle{cursor:not-allowed;color:var(--info)}
  .ed-lock{display:inline-flex;align-items:center;gap:var(--space-1);color:var(--info);
    font-family:var(--font-display);font-size:var(--text-caption);font-weight:700;
    text-transform:uppercase;letter-spacing:.03em}
  .ed-lock .icon{width:1em;height:1em}

  /* Auto-derived rows (Scene klar / Load-out) — shown to match the screens, but
     computed: not draggable, no edit/delete. */
  .ed-row.is-auto{opacity:.7}
  .ed-row.is-auto .rl-handle{cursor:default;color:var(--tx-mut)}

  /* PHONE ROW REFLOW (task #53, 2026-08-02) -- P8 (docs/gui-rebuild-2026-07-31/53-MOBIL.md):
     narrow doesn't relax the six-column line, it removes the option of cramming all six at
     once. Verified against edit.js's own docblock + a full read of the file: it queries
     `.ed-row`'s classes/dataset (is-locked/is-auto/data-*) and `.js-edit`/`.js-del` -- it never
     selects `.rl-handle`/`.rl-time`/`.rl-chip`/`.ed-name`/`.ed-meta` at all, so reassigning them
     to a grid-area is layout-only and touches no id/class/data-attribute edit.js binds to.
     P0 (23-STATE-EMPHASIS-PRINCIPLES.md) is Editor's own grounding: a locked row's identity signal
     (chip-fixed "Fast HH:MM" in .ed-meta, "Låst" in .ed-row-actions) must keep reading as
     unmovable -- so nothing here is hidden behind a fold the way Ruling 1's day-row Rediger/
     Ridere were; every element just moves to its own line, still visible, still legible.
     Hierarchy, not squeeze: identity (handle+time+chip) stays together on line 1 exactly as
     read today; the name -- which act/band this row is -- gets a full-width line instead of an
     ellipsis fighting four neighbours; duration/lock-state + the row's own actions (or "Låst")
     share a third line. Rediger/Delete stay inline (not folded into a menu, unlike Ruling 1's
     day-row Live/Rediger/Ridere): unlike that day list, THIS page's whole job is per-row editing
     on a phone, so hiding its only two actions behind an extra tap on every row would fight the
     page's actual purpose rather than serve it.
     34rem (544px) reused from this file's own existing family (30/34/37.5/42/44/46/50rem, per
     53-MOBIL.md's addendum grep), not invented: comfortably covers 375-430px real phones with
     margin, without engaging on desktop widths where the six-column line already reads fine.
     VERIFIED against a real headless render (task #53 addendum, Aug 2): the first version of
     this block put `.ed-meta`/`.ed-row-actions` in separate grid areas sharing columns 1-3
     with row 1 -- so the actions cell was only ever as wide as row 1's OWN chip column, not
     the row's full width. On an actual 390px render that was narrower than "Rediger" + the
     delete button need, and the button was silently clipped past the container's right edge
     (ed-row-phone-390.png in this task's proof, before this fix). Fixed by giving line 3 a
     SINGLE grid area ("foot") spanning all 3 columns, filled by the `.ed-foot` flex wrapper
     (view + this file) -- meta and actions now share the row's full measured width via
     justify-content:space-between, the same "identity / name / meta+actions" three-line
     hierarchy the ruling asked for, just no longer bottlenecked by an unrelated column's
     content. Re-rendered after the fix (ed-row-phone-390-fixed.png) to confirm. */
  @container (max-width: 34rem){
    .ed-row{
      grid-template-columns:auto auto minmax(0,1fr);
      grid-template-areas:
        "handle time chip"
        "name   name name"
        "foot   foot foot";
      row-gap:var(--space-2);
    }
    .ed-row .rl-handle{grid-area:handle}
    .ed-row .rl-time{grid-area:time}
    .ed-row .rl-chip{grid-area:chip;justify-self:start}
    .ed-row .ed-name{grid-area:name;white-space:normal}
    .ed-row .ed-foot{grid-area:foot;display:flex;align-items:center;justify-content:space-between;gap:var(--space-2)}
    .ed-row .ed-meta{justify-content:flex-start}
    .ed-row .ed-row-actions{justify-content:flex-end}
  }

  .ed-empty{background:var(--surface);border:1px dashed var(--edge-2);border-radius:var(--radius-lg);
    padding:var(--space-8);text-align:center;display:flex;flex-direction:column;gap:var(--space-3);align-items:center}
  .ed-empty h2{font-size:var(--text-heading)}
  .ed-empty p{color:var(--tx-mut);margin:0}

  /* Phase add/edit dialog — styled; open/save/reorder JS is follow-up (Slice 6).
     Fields inside use the canonical .field / .field-label / .input (components.css). */
  .modal{border:1px solid var(--edge);border-radius:var(--radius-lg);background:var(--surface);
    color:var(--tx);padding:var(--space-6);inline-size:min(34rem,94vw)}
  .modal::backdrop{background:rgba(0,0,0,.55)}
  .modal h2{font-size:var(--text-heading);margin-block-end:var(--space-4)}
  .modal form{display:flex;flex-direction:column;gap:var(--space-4)}
  .chips{display:flex;flex-wrap:wrap;gap:var(--space-2)}
  /* .type-chip is a real <button> (keyboard-operable) — reset the UA font so it
     matches the surrounding UI. Selection state = .is-active + aria-pressed. */
  .type-chip{cursor:pointer;border:1px solid var(--edge-2);border-radius:var(--radius-pill);
    padding:var(--space-1) var(--space-3);font-family:inherit;font-size:var(--text-caption);
    line-height:1.4;color:var(--tx-2);background:transparent}
  .type-chip.is-active{border-color:var(--accent);color:var(--accent);background:var(--accent-tint)}
  /* Segmented control — real radios, visually hidden but FOCUSABLE (never `hidden`,
     which drops them from the tab order); the label shows checked + focus state. */
  .seg{display:inline-flex;border:1px solid var(--edge-2);border-radius:var(--radius-sm);overflow:hidden;inline-size:fit-content}
  .seg-opt{padding:var(--space-2) var(--space-4);cursor:pointer;font-size:var(--text-caption);color:var(--tx-2)}
  .seg-opt input{position:absolute;inline-size:1px;block-size:1px;opacity:0;margin:0;pointer-events:none}
  .seg-opt:has(input:checked){background:var(--accent-tint);color:var(--accent)}
  .seg-opt:has(input:focus-visible){box-shadow:var(--focus-ring)}
  .modal-actions{display:flex;justify-content:flex-end;gap:var(--space-2);margin-block-start:var(--space-5)}

  /* ───────────────────────────────────────────────────────────────────────
   * Tenant dashboard — Control-adjacent, NOT a fifth clock surface (§8 note).
   * The tenant's scenes shown as STATUS CARDS: each reads the per-scene status
   * from compute_full_rundown() output (the ONE clock) and prints a snapshot —
   * it renders NO NU/NÆSTE/countdown of its own, never ticks, never forks the
   * clock (AGENTS.md §3, the engine rule; "CLAUDE.md #6" pre-2026-08-18).
   * Navigation buttons only (to each scene's Live/Editor/screen link). Tenant
   * logo + accent branding; same components, no new visual language (§7).
   * [Corrected 2026-08-18: "Backend is follow-up — static sample rendered when
   * no data is passed" is history — src/views/dashboard.php renders REAL data
   * (scenes_for_scope() merged with scene_status_snapshot(), its own header:
   * "never sample").]
   * ─────────────────────────────────────────────────────────────────────── */
  .dash{max-inline-size:64rem;margin-inline:auto;padding:var(--space-5) var(--space-5) var(--space-9);
    display:flex;flex-direction:column;gap:var(--space-5)}
  /* Topbar uses the canonical .page-header; .dash-user is the right-side cluster. */
  /* flex-wrap:wrap added defensively alongside the task #51 admin-row fix, 2026-08-01: this
     cluster (Hjælp + optional org-switcher + user-menu) is the OTHER half of _nav.php's header
     row (.page-head already wraps it below .titles when the two don't fit side by side, but
     nothing let ITS OWN children wrap if a multi-org session's org-switcher button pushes the
     cluster itself past a narrow viewport). Same fix, same reasoning, checked while already here. */
  .dash-user{display:flex;align-items:center;flex-wrap:wrap;gap:var(--space-3);color:var(--tx-mut);font-size:var(--text-caption)}

  .dash-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(18rem,1fr));gap:var(--space-4)}
  .scene-card{background:var(--surface);border:1px solid var(--edge);border-radius:var(--radius-lg);
    padding:var(--space-5);display:flex;flex-direction:column;gap:var(--space-3)}
  /* R2.2, 22-MOCKUP-STATE-RULES.md (dc.html:839,841,843): the whole-card border turns accent ONLY
     for the running scene — "has a day today" and "no day" both keep the plain edge border, same
     "loud only for the state that matters" rule .org-row.is-behind/.drift-banner already enforce
     elsewhere. Added task #16 (dashboard rebuild), not present before tonight. */
  .scene-card.is-running{border-color:var(--accent)}
  /* align-items:flex-start (task #72, was :center): once h2 below wraps to 2+ lines, centering
     the chip against the whole block floated it in the middle of the name instead of sitting
     level with its first line -- flex-start reads correctly whether the name is one line or
     several. */
  .scene-card > header{display:flex;align-items:flex-start;justify-content:space-between;gap:var(--space-3)}
  /* task #72 (countin-audit, login->running walk): was white-space:nowrap + ellipsis with no
     title attribute -- a long, properly-descriptive scene name ("Store Scene — Nordre Plads")
     had no way to be read without opening the scene. The card already has vertical room a
     single-line header doesn't (unlike a page's own h1), so it wraps normally now instead of
     hiding text; overflow-wrap:anywhere is the same defensive fallback `.page-head h1` (task
     #51) and `.nav-menu-email` already use, for the one shape a normal word-wrap can't help
     (a single unbreakable token) -- the view's own `title` attribute covers that same case too. */
  .scene-card h2{font-size:var(--text-heading);overflow-wrap:anywhere}
  /* Status snapshot lines — a plain read of the clock output, never a live counter. */
  .scene-lines{display:flex;flex-direction:column;gap:var(--space-1);
    font-family:var(--font-mono);font-size:var(--text-caption);color:var(--tx-2)}
  .scene-lines .lbl{color:var(--tx-mut)}
  .scene-lines .none{color:var(--tx-mut)}
  .scene-actions{display:flex;gap:var(--space-2);flex-wrap:wrap;margin-block-start:var(--space-1)}
  .scene-actions .btn{padding:var(--space-2) var(--space-4);font-size:var(--text-caption)}
  /* Add-scene card (tenant-admin only) — a dashed sibling of the status cards holding the
   * create-scene form. Dashed border sets it apart from the real scene cards without a new component. */
  .scene-card.scene-add{border-style:dashed;border-color:var(--edge-2);background:transparent}
  .scene-card.scene-add .stack{display:flex;flex-direction:column;gap:var(--space-3)}
  .scene-card.scene-add .field{display:flex;flex-direction:column;gap:var(--space-1)}
  /* Genuine empty state — a member with no scene access yet (the ONLY reacher:
   * admins with zero scenes are redirected to /onboarding, index.php). No cards,
   * no dead links — just an honest "ask your admin". */
  .dash-empty{background:var(--surface);border:1px solid var(--edge);border-radius:var(--radius-lg);
    padding:var(--space-8) var(--space-5);text-align:center;display:flex;flex-direction:column;
    gap:var(--space-2);align-items:center}
  .dash-empty h2{font-size:var(--text-heading)}
  .dash-empty p{color:var(--tx-mut);font-size:var(--text-body);max-inline-size:32rem}

  /* ───────────────────────────────────────────────────────────────────────
   * Org onboarding / branding — Control-adjacent, first-run only, the ONE
   * screen that SETS tenant branding (§7/§8). Management page → canonical
   * .page-head. Left: the branding form (name + accent + logo). Right: a LIVE
   * miniature Skærm preview so a festival-admin sees their real accent + logo on
   * a stage screen BEFORE it goes on a real one. Accent recolour is the only
   * per-tenant change (§2 tenant tier); the preview injects --tenant-accent live.
   * The preview is a MOCKUP — it renders no real clock, forks nothing (a frozen
   * sample time), never touches compute_full_rundown().
   * ─────────────────────────────────────────────────────────────────────── */
  /* inline-size:100% is load-bearing: .onboard is ALSO the .cq container-type:inline-size
     element, so without a definite inline-size it can collapse to its content's min-width in
     a shrink-to-fit parent (flex/grid). On the "Klar" step the content is narrow (centered
     done-card), which then wrapped the stepper vertically + the heading one-word-per-line.
     100% (capped by max-inline-size) makes it always fill its container, on every step. */
  .onboard{inline-size:100%;max-inline-size:64rem;margin-inline:auto;
    padding:var(--space-6) var(--space-5) var(--space-9);
    display:flex;flex-direction:column;gap:var(--space-6)}
  .onboard-grid{display:grid;grid-template-columns:1fr 1fr;gap:var(--space-6);align-items:start}
  @container (max-width: 46rem){ .onboard-grid{grid-template-columns:1fr} }
  .onboard-form{display:flex;flex-direction:column;gap:var(--space-4);
    background:var(--surface);border:1px solid var(--edge);border-radius:var(--radius-lg);padding:var(--space-6)}
  .onboard-wizard{display:flex;justify-content:space-between;gap:var(--space-3);margin-block-start:var(--space-2)}
  /* Read-only field (e.g. the festival name, set at super-admin create — not editable in onboarding). */
  .readonly-field{background:var(--surface-2);border:1px solid var(--edge-2);border-radius:var(--radius-sm);
    padding:var(--space-3) var(--space-4);color:var(--tx);font-weight:600}
  /* Scener step — the list of scenes already added, above the "add scene" form. */
  .scene-added{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--space-2)}
  .scene-added li{display:flex;align-items:center;justify-content:space-between;gap:var(--space-3);
    background:var(--surface-2);border:1px solid var(--edge-2);border-radius:var(--radius-sm);
    padding:var(--space-2) var(--space-4)}
  .scene-added .s-name{font-weight:600;color:var(--tx)}
  .scene-added .s-tz{font-family:var(--font-mono);font-size:var(--text-caption);color:var(--tx-mut)}
  .onboard-sep{border:0;border-block-start:1px solid var(--edge);margin-block:var(--space-2)}
  /* Accent swatch input — a real <input type=color> plus the resolved hex label. */
  .accent-row{display:flex;align-items:center;gap:var(--space-3)}
  .accent-row input[type=color]{inline-size:3rem;block-size:2.25rem;padding:0;border:1px solid var(--edge-2);
    border-radius:var(--radius-sm);background:var(--surface-2);cursor:pointer}
  .accent-row .hex{font-family:var(--font-mono);font-size:var(--text-caption);color:var(--tx-mut)}

  /* Miniature Skærm preview — a real stage screen is dark-ONLY, so the preview
     forces dark (color-scheme + explicit tokens) regardless of the onboarding
     page theme, and applies the tenant accent to the anchored/is-now cue. */
  .skaerm-preview{color-scheme:dark;background:var(--gray-950);color:var(--gray-50);
    border:1px solid var(--edge);border-radius:var(--radius-lg);overflow:hidden;
    aspect-ratio:16/10;display:flex;flex-direction:column;padding:var(--space-4);position:relative}
  .skaerm-preview .sp-logo{height:1.6rem;width:auto;object-fit:contain}
  .skaerm-preview .sp-center{flex:1;display:flex;flex-direction:column;align-items:center;
    justify-content:center;text-align:center;gap:var(--space-1)}
  .skaerm-preview .sp-eyebrow{font-family:var(--font-display);font-weight:700;letter-spacing:.2em;
    text-transform:uppercase;font-size:var(--text-caption);color:var(--tenant-accent,var(--teal-400))}
  .skaerm-preview .sp-clock{font-family:var(--font-mono);font-weight:700;font-variant-numeric:tabular-nums;
    font-size:clamp(2.5rem,14cqi,4rem);line-height:.9}
  /* overflow-wrap:anywhere, same defensive fallback as `.scene-card h2` (task #72) and
     `.or-name` (task #38 follow-up): the band/scene name here is real tenant data, and one
     unbreakable token (checked: the only sibling carrying user text is .sp-band -- .sp-eyebrow
     and .sp-clock are fixed strings, .sp-logo is an <img>) forced the whole onboarding/scene-hub
     page horizontally scrollable at 375px before this. */
  .skaerm-preview .sp-band{font-family:var(--font-display);font-weight:700;font-size:var(--text-heading);
    overflow-wrap:anywhere}
  .skaerm-preview .sp-bar{height:.5rem;border-radius:var(--radius-pill);background:var(--gray-800);overflow:hidden}
  .skaerm-preview .sp-bar > i{display:block;height:100%;inline-size:62%;border-radius:var(--radius-pill);
    background:var(--tenant-accent,var(--teal-400))}
  .preview-caption{color:var(--tx-mut);font-size:var(--text-caption);text-align:center;margin-block-start:var(--space-2)}

  /* Onboarding "Klar" (done) step — a calm confirmation card + the checklist of
     what was set + the CTA into the dashboard. Uses --ok (success) only on the
     check affordances, never a new hue (§5.3). */
  .onboard-done{background:var(--surface);border:1px solid var(--edge);border-radius:var(--radius-lg);
    padding:var(--space-8);display:flex;flex-direction:column;align-items:center;gap:var(--space-5);text-align:center}
  .done-check{inline-size:3.5rem;block-size:3.5rem;border-radius:var(--radius-pill);
    background:var(--ok-tint);color:var(--ok);display:flex;align-items:center;justify-content:center}
  .done-check .icon{inline-size:1.75rem;block-size:1.75rem}
  .done-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:var(--space-2);text-align:start}
  .done-list li{display:flex;align-items:center;gap:var(--space-2);color:var(--tx-2)}
  .done-list .icon{inline-size:1.1em;block-size:1.1em;color:var(--ok);flex:none}

  /* Onboarding wizard stepper — built to countin-builder's wizard-shell markup
     contract (2026-07-19). The shell emits <nav class="stepper"> wrapping numbered
     .step spans (is-done / is-active / is-upcoming) separated by chevron .step-sep.
     Accent lives on the active step (§8: onboarding is where the tenant accent is
     chosen). Tokens only, one border weight, no shadow (§5). Lives here for now;
     promote to components.css if Import/Editor reuse a stepper. */
  .stepper{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;
    font-family:var(--font-mono);font-size:var(--text-caption)}
  .step{display:inline-flex;align-items:center;gap:var(--space-2);color:var(--tx-mut);white-space:nowrap}
  .step-n{display:inline-flex;align-items:center;justify-content:center;
    inline-size:1.5rem;block-size:1.5rem;border-radius:var(--radius-pill);
    border:1px solid currentColor;font-weight:700}
  .step.is-active{color:var(--accent);font-weight:700}
  .step.is-active .step-n{background:var(--accent-tint);border-color:var(--accent)}
  .step.is-done{color:var(--tx-2)}
  .step.is-done .step-n{border-color:var(--tx-2)}
  .step.is-upcoming{color:var(--tx-mut)}
  .step-sep{inline-size:1rem;block-size:1rem;flex:none;color:var(--tx-mut)}
  /* Hjælp + Log ud, pushed to the row's end — task #25: onboarding had no logout
     anywhere before this (the only exit was closing the tab). Does not touch
     .step/.step-n/.step-sep above. */
  .stepper-nav{display:flex;align-items:center;gap:var(--space-2);margin-inline-start:auto}

  /* ───────────────────────────────────────────────────────────────────────
   * Super-admin — Control-adjacent, Jonas only (§8). Platform view: the tenant
   * list (name + scene-count, NO show content — super-admin never sees a scene's
   * schedule) + the one "opret festival" action. bjerring.live-only branding, and
   * it NEVER sets [data-tenant] — super-admin is nobody's tenant, so no tenant
   * accent (§8). Management page → canonical .page-head. Backend (tenant list +
   * create-org) is follow-up; built to countin-api's confirmed contract
   * ($user{email} + $tenants[]{id,name,scene_count} + create-org form).
   * ─────────────────────────────────────────────────────────────────────── */
  .admin{max-inline-size:60rem;margin-inline:auto;padding:var(--space-6) var(--space-5) var(--space-9);
    display:flex;flex-direction:column;gap:var(--space-6)}
  .admin-cols{display:grid;grid-template-columns:2fr 1fr;gap:var(--space-6);align-items:start}
  @container (max-width: 46rem){ .admin-cols{grid-template-columns:1fr} }
  .admin-list{display:flex;flex-direction:column;gap:var(--space-2);list-style:none;margin:0;padding:0}
  /* MOBILE OVERFLOW FIX (task #51, 2026-08-01, owner's real phone): neither this row nor its
     own .t-meta cluster ever wrapped -- .t-meta additionally carried flex-shrink:0, so a row
     with a date/name on one side and a chip + 2-3 buttons on the other could never compress
     OR stack, forcing the row (and with nothing anywhere in the ancestor chain clipping
     overflow, the WHOLE DOCUMENT) wider than any real phone viewport. This is the shared base
     every admin-row consumer uses (box.php, superadmin.php, adgang.php's scene list, scene_hub
     .php's day list AND access rows) -- fixed once here, not per page. flex-wrap:wrap lets
     t-name drop above t-meta when they don't both fit on one line. */
  .admin-row{display:flex;align-items:center;justify-content:space-between;flex-wrap:wrap;
    gap:var(--space-3);background:var(--surface);border:1px solid var(--edge);
    border-radius:var(--radius-md);padding:var(--space-3) var(--space-4)}
  .admin-row .t-name{font-weight:600;color:var(--tx)}
  /* flex-wrap:wrap (was scene_hub.css's own page-scoped override, task #15 -- promoted here so
     EVERY admin-row consumer gets it, not just the one page that happened to need it first).
     flex-shrink:0 dropped: it was the second half of the same defect, refusing to compress even
     once wrapped onto its own row below t-name. */
  .admin-row .t-meta{display:flex;align-items:center;flex-wrap:wrap;gap:var(--space-3)}
  .admin-row .t-count{font-family:var(--font-mono);font-size:var(--text-caption);color:var(--tx-mut);white-space:nowrap}
  /* Genuine empty state — no festivals on the platform yet. Unreachable in normal
   * use (there is always ≥1 tenant) but no fake sample data / dead links, same as
   * the dashboard empty state. The real action (create) is the adjacent form. */
  .admin-empty{background:var(--surface);border:1px dashed var(--edge-2);border-radius:var(--radius-md);
    padding:var(--space-6) var(--space-4);text-align:center;color:var(--tx-mut);font-size:var(--text-body)}
  .admin-create{background:var(--surface);border:1px solid var(--edge);border-radius:var(--radius-lg);
    padding:var(--space-5);display:flex;flex-direction:column;gap:var(--space-4);position:sticky;top:var(--space-5)}
  .admin-create h2{font-size:var(--text-heading)}
  /* Per-org delete — a no-JS <details> disclosure so the destructive form (typed-name
   * confirm) stays folded away until the super-admin opens it. summary is styled as the
   * danger button; the marker is removed so it reads as a plain button, not a twisty. */
  .admin-row .t-del{position:relative}
  .admin-row .t-del > summary{list-style:none;cursor:pointer}
  .admin-row .t-del > summary::-webkit-details-marker{display:none}
  .admin-row .t-del[open] > summary{margin-block-end:var(--space-3)}
  .admin-row .t-del form{position:absolute;right:0;z-index:2;inline-size:16rem;
    background:var(--surface);border:1px solid var(--edge);border-radius:var(--radius-md);
    padding:var(--space-4);display:flex;flex-direction:column;gap:var(--space-2);
    box-shadow:0 8px 24px rgb(0 0 0 / .3)}

  /* ───────────────────────────────────────────────────────────────────────
   * Org-overblik (/control/overblik) — GUI-V3 §3 skærm 5, NEW surface, Jonas-
   * approved 2026-07-29. Production-lead's cross-scene view: a status strip +
   * one .org-row per scene. Used to also carry the backstage-board pairing
   * issuance panel (README §6.6) — removed 2026-08-17 with the board itself
   * ("backstage skal væk den er så dårligt lavet!"), along with the
   * .tavle-*/`.pair`-in-this-page wiring that panel needed; .pair/.pair-code
   * themselves are NOT removed, scene_hub.php still uses them for a scene's
   * own screen PIN. Reuses .page-head/.nnd-card/.chip*/.muted; the ONLY new
   * component is .org-row (three-column scene row) + its small .or-* children
   * + the status strip (page-scoped .card/.pair-row in scene_hub.css are NOT
   * reused here on purpose — they are prefixed under .scene-hub; this page
   * composes from the truly-global primitives instead).
   * ─────────────────────────────────────────────────────────────────────── */
  .overblik-org{max-inline-size:72rem;margin-inline:auto;padding:var(--space-6) var(--space-5) var(--space-9);
    display:flex;flex-direction:column;gap:var(--space-6)}

  .status-strip{display:flex;gap:var(--space-3);flex-wrap:wrap}
  .ss-item{display:flex;flex-direction:column;gap:var(--space-1);background:var(--surface);
    border:1px solid var(--edge);border-radius:var(--radius-md);padding:var(--space-3) var(--space-4);
    min-inline-size:8rem}
  /* --text-title (26px), not --text-heading (22px, was 84.6% of the mockup's
     stat-value size) — tokens.css, added 2026-07-31, team-lead decision. */
  .ss-n{font-family:var(--font-mono);font-weight:700;font-size:var(--text-title);color:var(--tx)}
  /* Mono/uppercase/letter-spaced caption — the SAME three properties as the already-
     shipped `.page-head .eyebrow` (components.css), not a new typographic voice. Task
     #20: was plain body text, a real drift from the mockup's caption style. */
  .ss-lbl{font-family:var(--font-mono);font-size:var(--text-caption);letter-spacing:.14em;
    text-transform:uppercase;color:var(--tx-mut)}
  /* Bagud is LOUD (bordered + red number); På plan is a QUIET acknowledgement (green
     number only, no border) — the same asymmetric loud/quiet pattern .org-row.is-behind
     already uses one level down (P1, bad news is loud). Task #20: the strip previously
     had zero colour coding at all. */
  .ss-item.is-bad{border-color:var(--danger)}
  .ss-n.is-bad{color:var(--danger)}
  .ss-n.is-good{color:var(--ok)}

  .org-rows{display:flex;flex-direction:column;gap:var(--space-3)}
  /* Row is a real link (click-through to the scene hub, README §3 skærm 5) — reset
     the anchor's own colour/underline so it reads as a plain row, not blue text. */
  .org-row{display:grid;grid-template-columns:1fr 1.4fr auto;gap:var(--space-4);align-items:center;
    padding:var(--space-4) var(--space-5);background:var(--surface);border:1px solid var(--edge);
    border-radius:var(--radius-lg);color:inherit;text-decoration:none}
  .org-row:hover{border-color:var(--edge-3)}
  @container (max-width: 42rem){ .org-row{grid-template-columns:1fr} }
  /* A scene that is BEHIND gets the WHOLE row red-bordered, not just a chip — same
     "louder when behind" hierarchy as .drift-banner.drift-behind (§4/§6, this is
     the SAME rule restated for a row instead of a banner, never a new threshold). */
  .org-row.is-behind{border-color:var(--danger);box-shadow:inset 0 0 0 2px var(--danger);background:var(--danger-tint)}
  .or-id{display:flex;align-items:center;gap:var(--space-2);flex-wrap:wrap;min-inline-size:0}
  .or-dot{inline-size:.85rem;block-size:.85rem;border-radius:50%;flex:none}
  /* --text-heading (22px), not the previous unset default (16px, inherited body size) —
     measured 24% under the mockup's 21px; heading lands within 5%. Approved fix,
     GUI rebuild measurement pass 2026-07-31. */
  /* overflow-wrap:anywhere, not white-space:nowrap + ellipsis (task #72 follow-up): unlike
     .or-current below (a band name sharing a tight line with the countdown clock, where
     ellipsis is the right call), the scene name sits alone on its own line in `.or-id` — the
     chip already wraps below it when the name is long (flex-wrap on `.or-id`), and the row
     itself is a grid whose height already varies per scene, so a wrapped 2nd line costs
     nothing a single row doesn't already tolerate. Confirmed truncating unreadably at BOTH
     375px and 1280px with no `title` before this fix — same bug, same fix as `.scene-card h2`. */
  .or-name{font-family:var(--font-display);font-weight:700;font-size:var(--text-heading);
    color:var(--tx);overflow-wrap:anywhere}
  .or-mid{display:flex;flex-direction:column;gap:var(--space-1);min-inline-size:0}
  .or-current{font-family:var(--font-display);font-weight:700;color:var(--tx);white-space:nowrap;
    overflow:hidden;text-overflow:ellipsis}
  /* The mockup's loudest per-row element — a large mono countdown, ticked client-side by
     overblik_org.js from data-end (a server timestamp already computed by
     scene_overblik_snapshot(), never a second clock). Task #20: this element and the
     live-tick behind it are the fix for the page's own missing premise
     (21-WHY-GUI-V3-WAS-REJECTED.md). font-size measured against the mockup's ~34px
     stat, same tabular-nums discipline live.js's own hero clock already uses so digits
     don't shift width as they tick. */
  .or-clock{font-family:var(--font-mono);font-weight:700;font-size:2.125rem;line-height:1;
    font-variant-numeric:tabular-nums;color:var(--tx)}
  /* R5.3 (22-MOCKUP-STATE-RULES.md, dc.html:727 clockColor) — the digit itself reddens at
     ≤60s remaining, independent of the row's BAGUD/PÅ PLAN state (R5.2): a scene can be
     perfectly on plan and still be sixty seconds from going on. Toggled by
     overblik_org.js on every tick, same threshold the phase-bar fill already uses. */
  .or-clock.is-urgent{color:var(--danger)}
  .or-bar-track{block-size:.5rem;border-radius:var(--radius-pill);background:var(--surface-2);overflow:hidden}
  .or-bar-fill{block-size:100%;border-radius:var(--radius-pill)}
  .or-remain{font-family:var(--font-mono);font-size:var(--text-caption);color:var(--tx-mut)}
}
