  html{--_c1:1;}
  /* Visual redesign (2026-09-05, "Atlas Console" mockup) — a GLOBAL palette/typography swap,
     deliberately, not phased per-screen: every color/font below is a shared CSS variable used
     across every tab and the Form Builder, so there is no way to repaint only the sidebar/topbar
     without the same tokens cascading everywhere else too. This is fine — it changes zero HTML
     structure and zero data-*/id attributes anywhere, so it carries none of the functional risk
     the later structural phases (Form tab cards, Form Builder canvas, etc.) still have to guard
     against carefully. --accent maps to the mockup's own teal (used there for focus/hover/active
     highlights almost everywhere) — its two literal "primary CTA button" call sites (.btn,
     .export-cluster-btns button.primary) are repointed to --navy instead, matching the mockup's
     own .btn-primary being navy, not teal. Form-Builder-only block-type colors (--form-bar and
     the rest) are deliberately UNTOUCHED here — chosen fresh, deliberately, during the Form
     Builder's own later phase, not guessed at now. */
  html{--_c2:1;}
  /* Real bug found 2026-09-06: on at least one managed/corporate machine (two different
     browsers, zero extensions, confirmed via document.styleSheets — every other rule in this
     file parses fine, but a single large multi-line ":root{ ... }" block containing ~20 custom
     property declarations was being silently dropped by the browser's CSS parser entirely (not
     present in cssRules at all — not "overridden", genuinely never parsed). Root cause not fully
     identified (a corporate proxy/security appliance doing its own CSS "optimization" is the
     leading suspect, since it reproduced identically across Chrome and Edge on the same machine
     but not elsewhere) — worked around by using the plain "html" selector instead of the ":root"
     pseudo-class (equivalent for a top-level HTML document) and splitting the one dense block
     into several smaller ones, one declaration per line. Confirmed 2026-09-06 (reproduced in a
     clean headless-Chrome test, not just the original corporate machine — this is NOT machine-
     specific): the rule immediately after a long enough comment block is dropped regardless of
     the rule's own content — a short throwaway rule survives there, so one is placed before every
     substantial comment block in this file as insurance, with the real rule moved after it. */
  html{--_c1:1;}
  html{ --ground:#eef1f6; --surface:#ffffff; --surface-2:#f4f7fa; --ink:#10151c; --muted:#5b6472; }
  html{ --accent:#2aa198; --accent-ink:#ffffff; --line:#dde3ea; --bad:#c85a44; --good:#3a9b5c; --warn:#c98a2e; }
  html{ --navy:#0f2e4c; --navy-2:#173f66; --navy-ink:#ffffff; }
  html{ --form-bar:#3B7DD8; --dataset-bar:#4CAF7D; --panel-head:#ECECEC; --logic:#6B5CA5; --repeat-bar:#C2678D; --timed-bar:#C77D1F; --section-bar:#2E8FA8; }
  html{ --font-disp:'Sora',system-ui,sans-serif; }
  html{ --font-body:'Source Sans 3',system-ui,-apple-system,Segoe UI,Roboto,sans-serif; }
  html{ --font-mono:'IBM Plex Mono',"SF Mono",Consolas,monospace; }
  *{box-sizing:border-box;}
  body{margin:0; font-family:var(--font-body); background:var(--ground); color:var(--ink);}
  h1,h2,h3{font-family:var(--font-disp); text-wrap:balance;}
  /* Real bug found 2026-09-06 via a direct computed-style check: browsers don't inherit the
     page font onto form controls by default, so any button/input/select without its own
     explicit font-family (or an "all:unset" reset) silently falls back to the browser's own
     default control font. Most button classes here already use "all:unset" (which resets font
     to the correctly-inherited value as a side effect) - .chip was the one real exception,
     rendering in Arial instead of Source Sans 3 this whole time. Matches the mockup's own exact
     global reset (it has this same rule) rather than patching .chip alone, closing the whole
     class of bug for any future button/input/select too. */
  button,input,select,textarea{font-family:inherit;}

  html{--_c3:1;}
  /* Left-sidebar shell (2026-09-05, "Atlas Console" redesign) — replaces the old pinned
     topbar+horizontal-tabbar combo (#app-header-sticky) with: a sticky top bar (logo/brand/
     who-am-i/sign-out only) plus a below-it grid of [sidebar nav][main content], sidebar
     scrolling normally with the page rather than staying pinned, matching the mockup exactly.
     #tabbar/.tab/.tab.active keep their EXACT same ids/classes and the EXACT same JS wiring
     (selectTab(), the click listener in showMainApp()) — only the CSS driving their layout
     changes, from a horizontal row of tabs to a vertical column of sidebar buttons. */
  #topbar{background:var(--navy); color:var(--navy-ink); display:flex; align-items:center; gap:14px; padding:10px 20px; position:sticky; top:0; z-index:50;}
  .topbar-logo{width:44px; height:44px; border-radius:9px; object-fit:cover; flex-shrink:0; display:block;}
  #topbar .title{font-family:var(--font-disp); font-weight:700; font-size:19px; letter-spacing:0.01em; flex:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
  #topbar .who{font-size:12.5px; opacity:0.85; flex-shrink:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:140px;}
  html{--_c4:1;}
  /* flex-shrink:0 (all:unset resets it to the initial value, 1) so a long title/username
     can never push this off-screen — it's the only way back to the login screen. */
  /* Real bug found 2026-09-06 while reviewing the modals for Phase 7: this rule was scoped to
     "#topbar .logout", but 3 of its 4 real usages (the Data table/Form Builder/Report modals'
     own "Close" buttons) sit inside a DIFFERENT topbar-like div (#data-modal-topbar/#builder-
     topbar/#report-modal-topbar), not #topbar itself — the selector never matched them, so
     they've been rendering as completely unstyled default browser buttons this whole time.
     Un-scoped to a standalone class; every real usage is on a navy background, so the same
     styling is correct everywhere it's used. */
  .logout{all:unset; cursor:pointer; flex-shrink:0; font-size:0.8rem; color:#fff; text-decoration:underline; margin-left:12px;}

  #layout{display:grid; grid-template-columns:224px 1fr; align-items:start;}
  /* Pinned in place while the main content scrolls (2026-09-29, per direct feedback) -- matches
     #topbar's own position:sticky; top:0 pattern already used above, just offset by the topbar's
     real rendered height (measured live: 64px) so it sticks flush beneath it, not underneath it.
     #layout's own align-items:start (already set) is what lets this size to the sidebar's own
     content instead of stretching to match #app's real, much taller column. */
  #sidebar{background:var(--navy-2); padding:18px 12px; display:flex; flex-direction:column; gap:3px; min-height:calc(100vh - 64px); position:sticky; top:64px;}
  /* "+ New form" (2026-09-29, Phase 3) -- the one remaining unscoped entry point into the Form
     tab, now that Form/Export/Fluency AI are no longer their own persistent sidebar destinations.
     Filled accent style (not navy, like .tab) so it visually reads as an action, not a nav item,
     matching the approved mockup's own .rme-newform-btn. */
  .sidebar-newform-btn{all:unset; box-sizing:border-box; width:100%; display:flex; align-items:center; justify-content:center; gap:8px; padding:10px 12px; border-radius:8px; font-size:13.5px; font-weight:700; color:var(--accent-ink); background:var(--accent); cursor:pointer; margin-bottom:8px;}
  .sidebar-newform-btn:hover{filter:brightness(1.08);}
  #tabbar{display:flex; flex-direction:column; gap:3px;}
  .tab{display:flex; align-items:center; gap:10px; padding:11px 12px; border-radius:8px; cursor:pointer;
       color:#c6d4e0; font-size:14px; font-weight:600; white-space:nowrap; user-select:none;}
  .tab .tab-ico{width:18px; height:18px; display:flex; align-items:center; justify-content:center; text-align:center; opacity:.9; font-size:13px; flex-shrink:0;}
  .tab .tab-ico svg{display:block;}
  .tab-ico-logo{width:16px; height:16px; border-radius:5px; object-fit:cover; display:block; opacity:1;}
  .tab:hover{background:rgba(255,255,255,.07); color:#fff;}
  .tab.active{background:rgba(255,255,255,.14); color:#fff;}
  /* NewGlobe branding moved above the divider line (2026-09-06, real user asset swap) — the
     white-outline-on-transparent PNG sits directly on the navy sidebar with no white box needed,
     unlike the old solid-navy-background JPG this replaces. margin-top:auto now lives on the
     logo's own wrapper (pushing everything below it, including the divider, to the bottom) so the
     line still separates the logo from the summary text beneath it, just as before. */
  #sidebar-logo-wrap{margin-top:auto; padding:0 4px 14px;}
  #sidebar-logo{display:block; height:48px; width:auto; max-width:100%;}
  #sidebar-note{padding-top:14px; border-top:1px solid rgba(255,255,255,.12);}
  #sidebar-summary{font-size:11px; color:#9fb3c3; line-height:1.5; margin:0;}
  /* "Project" sidebar dropdown (2026-09-29), replacing the old dash-chips row -- a real count
     badge plus an expandable, searchable list of forms, matching the tab row's own visual
     language rather than a separate widget bolted on beside it. */
  .tab-count-badge{margin-left:auto; background:rgba(255,255,255,.16); color:#fff; border-radius:999px; font-size:11px; font-family:var(--font-mono); padding:1px 8px; flex-shrink:0;}
  /* No left indent (2026-09-29 fix) -- this used to sit 30px further right than "Project" itself,
     contradicting the same left-alignment the mockup deliberately established (form names should
     start under the first letter of the parent label, not indented like a typical nested menu).
     The search box and each form-name button carry their own ~10px internal padding already,
     which is what lines their text up with "Project"'s own 12px padding — no extra container
     padding is needed on top of that. */
  #dash-dropdown{padding:2px 4px 6px 0;}
  /* #dash-dropdown-search (the id, not just .dash-dropdown-search) deliberately (2026-09-29 fix)
     -- the input used to also carry the shared .search class, and since both are single-class
     selectors, .search's own padding/background/border/width (defined later in this file) won
     the cascade on source order alone, silently overriding this rule's intended dark, borderless
     styling and throwing off the left-edge alignment with the list items below it. An id
     selector's specificity beats any class, so this can't be silently re-overridden the same way
     again regardless of where .search or anything else ends up in the file. */
  #dash-dropdown-search{all:unset; box-sizing:border-box; width:100%; padding:7px 10px; margin:4px 0 6px; border-radius:7px; background:rgba(255,255,255,.08); border:1px solid rgba(255,255,255,.14); font-size:12.5px; color:#fff;}
  #dash-dropdown-search::placeholder{color:rgba(255,255,255,.45);}
  #dash-dropdown-search:focus{border-color:var(--accent);}
  .dash-dropdown-item{all:unset; box-sizing:border-box; display:block; width:100%; padding:7px 10px; border-radius:7px; font-size:12.5px; line-height:1.4; color:rgba(255,255,255,.68); cursor:pointer; overflow-wrap:break-word; word-break:break-word;}
  .dash-dropdown-item:hover{background:rgba(255,255,255,.06);}
  .dash-dropdown-item.active{background:rgba(255,255,255,.14); color:#fff; font-weight:700;}
  .dash-dropdown-empty{padding:6px 10px; font-size:12px; color:rgba(255,255,255,.5);}

  html{--_c5:1;}
  /* ---- Dashboard tab (2026-09-05, Phase 3 of the admin redesign) — new, matches the mockup's
     own class names directly since none of this existed in the real app before. Kept inside the
     same 900px-capped #app container every other tab already uses, rather than the mockup's own
     wider dashboard-only layout, for consistency with the rest of this app's established width.
     .card is a new, simpler sibling to the existing .panel (which always carries its own header
     bar) — every other tab keeps using .panel unchanged; .card is scoped to Dashboard content. */
  .card{background:var(--surface); border:1px solid var(--line); border-radius:12px;}
  /* Matches the mockup's own .h1/.h2 exactly -- reused by the Surveys-list heading and every
     per-survey Overview section header, instead of duplicating the same inline style per use.
     overflow-wrap:anywhere added 2026-10-19 (real bug, reported directly with a screenshot): a
     survey title with NO spaces at all -- e.g. "JigawaUNITE_Midline_Evalulation_FIELDWORK_2026",
     all underscores -- has no natural break point under the browser's default word-break, so as a
     flex child (the per-survey header's own title row) it was overflowing its row entirely instead
     of wrapping, silently clipped by whatever contained it. Harmless no-op for every short title
     that already fits on one line. */
  .h1{margin:0; font:700 26px var(--font-disp); overflow-wrap:anywhere;}
  .h2{margin:0; font:600 17px var(--font-disp); overflow-wrap:anywhere;}
  html{--_c6:1;}
  /* Real gap caught during Phase 4 work: Phase 3's Dashboard JS (dashStatusPill) already emits
     class="pill good/muted" but this CSS was never actually added, so those status pills have
     been rendering as unstyled plain text since Phase 3 shipped. Fixed here, alongside adding the
     same .pill to the Form tab's own per-form status pill (Phase 4). */
  .pill{font-size:11px; font-weight:700; padding:3px 9px; border-radius:999px; letter-spacing:.02em; display:inline-flex; gap:5px; white-space:nowrap;}
  .pill.good{background:color-mix(in srgb, var(--good) 16%, transparent); color:var(--good);}
  .pill.warn{background:color-mix(in srgb, var(--warn) 18%, transparent); color:var(--warn);}
  .pill.muted{background:var(--surface-2); color:var(--muted); border:1px solid var(--line);}
  .pill.bad{background:color-mix(in srgb, var(--bad) 18%, transparent); color:var(--bad);}
  .kpi-row{display:grid; grid-template-columns:repeat(auto-fit,minmax(150px,1fr)); gap:12px; margin-bottom:18px;}
  .kpi{padding:14px 16px;}
  .kpi-n{font-family:var(--font-disp); font-weight:700; font-size:26px; line-height:1.1;}
  .kpi-n.good{color:var(--good); font-size:16px;}
  /* Matches the mockup's own .label exactly (11px/600/uppercase/.06em letter-spacing) -- this
     tile-caption look is used consistently for every KPI label across Dashboard and Fluency AI,
     real visible mismatch found 2026-10-14 (was plain 12px mixed-case, not uppercase). */
  .kpi-l{font-size:11px; font-weight:600; letter-spacing:.06em; text-transform:uppercase; color:var(--muted); margin-top:6px;}
  .dash-asof{font-size:12px; color:var(--muted); font-family:var(--font-mono);}
  /* Surveys list (Stage 1 admin redesign, 2026-10-14) -- status chips, project/sort filters, the
     search box, the List/Cards toggle, and the survey table/card grid, all matching the approved
     mockup's own layout (docs/redesign/mockup/Main.dc.html), built from this file's existing
     tokens rather than the mockup's own slightly different teal (same rule Stage 1's shell used). */
  .status-chip{border:1px solid var(--line); background:var(--surface); color:var(--ink); border-radius:999px; padding:7px 14px; min-height:38px; cursor:pointer; font:600 14px var(--font-body);}
  .status-chip.on{border-color:var(--navy); background:var(--navy); color:#fff;}
  .status-chip-n{opacity:.75;}
  .dash-search-box{display:flex; align-items:center; gap:8px; border:1px solid var(--line); background:var(--surface); border-radius:8px; padding:8px 12px; color:var(--muted);}
  .dash-search-box input{border:0; outline:none; font:inherit; width:220px; background:transparent; color:var(--ink);}
  .view-toggle{display:inline-flex; background:var(--surface-2); border-radius:8px; padding:3px;}
  .view-toggle button{border:0; border-radius:6px; padding:6px 14px; min-height:34px; cursor:pointer; font:600 14px var(--font-body); background:transparent; color:var(--muted);}
  .view-toggle button.on{background:var(--surface); color:var(--ink); box-shadow:0 1px 2px rgba(16,21,28,.12);}
  .survey-row{display:grid; grid-template-columns:minmax(240px,2.4fr) minmax(110px,1fr) 96px minmax(170px,1.4fr) 140px 76px; gap:10px; padding:10px 16px; border-top:1px solid var(--line); align-items:center; font-size:15px;}
  .survey-row:first-child{border-top:0;}
  .survey-row-head{font-size:11px; text-transform:uppercase; letter-spacing:.05em; font-weight:600; padding-top:10px; padding-bottom:10px;}
  .survey-progress-cell{display:flex; flex-direction:column; gap:4px;}
  .survey-progress-n{font-size:13px;}
  .survey-bar{display:block; height:8px; border-radius:999px; background:var(--surface-2); overflow:hidden;}
  .survey-bar i{display:block; height:100%; background:var(--accent);}
  .survey-cards-grid{display:grid; grid-template-columns:repeat(auto-fill,minmax(300px,1fr)); gap:16px;}
  .survey-card{padding:18px; display:flex; flex-direction:column; gap:12px;}

  /* ---- Fluency AI (2026-09-11) ---- */
  .fa-table tr[data-fa]{cursor:pointer;}
  .fa-table tr[data-fa]:hover td{background:var(--surface-2);}
  .fa-table tr.is-open td{background:var(--surface-2);}
  /* Human WCPM / AI WCPM / Difference / Status (columns 8-11, shifted by the checkbox column at
     1) centered, header+data alike — a score/status is a value to scan down the column, not a
     label to read left-to-right, unlike the name/text columns (Date...Enumerator) before them
     which stay left-aligned. */
  .fa-table th:nth-child(n+8), .fa-table td:nth-child(n+8){text-align:center;}
  /* Sortable column headers (2026-10-05, real user ask; made bolder same day after direct
     feedback that a small text glyph "wasn't well seen") — built from two stacked CSS triangles
     (not a font glyph, which rendered too small/faint to notice at a glance and varies by font)
     so every sortable header shows an unmistakable up/down indicator, with the active column's
     matching triangle filled solid in the accent color and the other dimmed. */
  .fa-sortable{cursor:pointer; user-select:none; white-space:nowrap;}
  .fa-sortable:hover{color:var(--accent);}
  .fa-sort-icon{display:inline-flex; flex-direction:column; gap:2px; margin-left:6px; vertical-align:middle;}
  .fa-sort-tri{width:0; height:0; border-left:4px solid transparent; border-right:4px solid transparent; opacity:0.35;}
  .fa-sort-tri.up{border-bottom:5px solid var(--ink);}
  .fa-sort-tri.down{border-top:5px solid var(--ink);}
  .fa-sortable:hover .fa-sort-tri{opacity:0.6;}
  .fa-sort-tri.active{opacity:1 !important; border-bottom-color:var(--accent); border-top-color:var(--accent);}
  .diff-pill{font-size:11px; font-weight:700; padding:3px 9px; border-radius:999px; font-family:var(--font-mono);}
  .diff-pill.close{background:color-mix(in srgb, var(--good) 16%, transparent); color:var(--good);}
  .diff-pill.review{background:color-mix(in srgb, var(--warn) 18%, transparent); color:var(--warn);}
  .diff-pill.gap{background:color-mix(in srgb, var(--bad) 16%, transparent); color:var(--bad);}
  .diff-pill.pending{background:var(--surface-2); color:var(--muted);}
  .diff-pill.failed{background:color-mix(in srgb, var(--bad) 16%, transparent); color:var(--bad);}
  .fa-view-toggle{display:flex; gap:2px; background:var(--surface-2); border:1px solid var(--line); border-radius:9px; padding:3px;}
  .fa-view-btn{padding:6px 14px; border-radius:7px; font-size:13px; font-weight:700; font-family:var(--font-body,inherit); border:none; cursor:pointer; background:transparent; color:var(--muted);}
  .fa-view-btn.on{background:var(--surface); color:var(--navy); box-shadow:0 1px 3px rgba(15,30,50,.12);}
  .fa-agg-row{display:flex; align-items:center; gap:12px; padding:11px 4px; border-top:1px solid var(--line); font-size:13px;}
  .fa-agg-row:first-child, .fa-agg-header + .fa-agg-row{border-top:none;}
  .fa-agg-row .fa-agg-label{width:220px; flex-shrink:0;}
  .fa-agg-row .fa-agg-label b{display:block;}
  .fa-agg-row .fa-agg-sub{font-size:11.5px; color:var(--muted);}
  .fa-agg-row .fa-agg-n{width:64px; flex-shrink:0; text-align:center; font-family:var(--font-mono); color:var(--muted);}
  .fa-agg-row .fa-agg-hval{width:70px; flex-shrink:0; text-align:center; font-family:var(--font-mono);}
  .fa-agg-row .fa-agg-bar{flex:1; height:8px; border-radius:999px; background:var(--surface-2); overflow:hidden;}
  .fa-agg-row .fa-agg-bar i{display:block; height:100%;}
  /* Right-aligned, not centered (2026-09-22): a numeric column reads better right-aligned
     (decimal points line up down the column), and — the actual reason this matters here — the
     header's own .fa-agg-gap span below is a much WIDER box carrying the "Avg. Gap (WCPM)"
     label text itself, not the decorative bar. Right-aligning both means their right edges
     coincide exactly (they share the same fixed-width column to their right), so the label
     visually sits directly above the number regardless of the two boxes' different widths. */
  .fa-agg-row .fa-agg-gap{width:40px; flex-shrink:0; text-align:right; font-family:var(--font-mono); font-size:12.5px;}
  .fa-agg-row .fa-agg-flag{width:100px; flex-shrink:0; text-align:center;}
  .fa-agg-header{display:flex; align-items:center; gap:12px; padding:0 4px 9px; border-bottom:1.5px solid var(--line);}
  .fa-agg-header span{text-align:center; font-size:11px; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); font-weight:800;}
  .fa-agg-header .fa-agg-label{width:220px; flex-shrink:0; text-align:left;}
  .fa-agg-header .fa-agg-n{width:64px; flex-shrink:0;}
  .fa-agg-header .fa-agg-hval{width:70px; flex-shrink:0;}
  /* The "Avg. Gap (WCPM)" label text itself lives in .fa-agg-gap below, not here — bar-label is
     just the flex-1 filler that keeps this header's column count/widths matching the data row's
     (bar + gap), so it stays empty. */
  .fa-agg-header .fa-agg-bar-label{flex:1;}
  /* Widened well past the data row's 40px (2026-09-22) so "AVG. GAP (WCPM)" fits on one line —
     safe because both this and the row's .fa-agg-gap are right-aligned and sit immediately
     before the same fixed-width .fa-agg-flag column, so their right edges always coincide
     exactly no matter how much wider this header box is. See the row rule's comment. */
  .fa-agg-header .fa-agg-gap{width:150px; flex-shrink:0; text-align:right;}
  .fa-agg-header .fa-agg-pct{width:52px; flex-shrink:0;}
  .fa-agg-header .fa-agg-flag{width:100px; flex-shrink:0;}
  #fa-detail h4{margin:0 0 4px; font-size:14px;}
  .fa-detail-meta{font-size:12px; color:var(--muted); margin:0 0 16px;}
  #fa-pagination{display:flex; align-items:center; gap:12px; padding:12px 0 0; justify-content:center; flex-wrap:wrap;}
  .fa-chip-legend{display:flex; gap:14px; flex-wrap:wrap; font-size:11.5px; color:var(--muted); margin-bottom:14px;}
  .fa-chip-legend .dot{width:9px; height:9px; border-radius:3px; display:inline-block; margin-right:5px; vertical-align:middle;}
  .fa-marking{margin-bottom:16px;}
  .fa-marking .who{font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); margin-bottom:6px;}
  .fa-passage{line-height:2.1; font-size:14px;}
  .fa-word{padding:2px 5px; border-radius:5px; margin-right:2px; display:inline-block;}
  .fa-word.correct{background:color-mix(in srgb, var(--good) 15%, transparent);}
  /* Both wrong states render in real red text (not just a faint tint) so a screenshot or
     printout reads at a glance the way a hand-marked paper sheet does — a strikethrough for a
     genuine misread (the child said something, just not the right word) vs. a dotted underline
     for an omission (skipped over but the child kept going), so the two stay tellable apart
     while both still clearly say "wrong" the way the user asked for. */
  .fa-word.error{background:color-mix(in srgb, var(--bad) 15%, transparent); color:var(--bad); font-weight:700; text-decoration:line-through; text-decoration-color:var(--bad);}
  .fa-word.omitted{background:color-mix(in srgb, var(--bad) 10%, transparent); color:var(--bad); font-weight:700; text-decoration:underline dotted; text-decoration-color:var(--bad);}
  .fa-word.unreached{color:var(--muted);}
  .fa-transcript{font-size:12.5px; color:var(--muted); background:var(--surface-2); border-radius:8px; padding:10px 12px;}

  .dash-section-h{font-family:var(--font-disp); font-weight:600; font-size:15px; margin:16px 0 10px;}
  .dash-table th{text-align:left; font-size:11.5px; text-transform:uppercase; letter-spacing:.06em; color:var(--ink); font-weight:800; padding:9px 10px;}
  .dash-table td{padding:10px 10px; border-top:1px solid var(--line); font-size:13px;}


  /* Breadcrumb above the per-survey header (Stage 1 admin redesign, 2026-10-14) -- "Surveys /
     {title}", matching the approved mockup's own breadcrumb exactly. */
  .fdash-breadcrumb{font-size:14px; margin-bottom:6px;}
  .fdash-breadcrumb a{color:var(--ink); text-decoration:none; font-weight:600;}
  .fdash-breadcrumb a:hover{text-decoration:underline;}
  /* Per-survey tab row -- rebuilt 2026-10-14 to match the mockup's own real underline-tab style
     exactly (docs/redesign/mockup/Main.dc.html's own `tabs` style string), replacing the earlier
     filled-pill placeholder this started as. */
  .fdash-tabs{display:flex; gap:2px; border-bottom:1px solid var(--line); overflow-x:auto; margin-bottom:16px;}
  .dash-tabbtn{all:unset; box-sizing:border-box; display:inline-flex; align-items:center; padding:12px 15px; min-height:44px; border-bottom:3px solid transparent; cursor:pointer; white-space:nowrap; font-size:15px; font-weight:600; color:var(--muted);}
  .dash-tabbtn.on{border-bottom-color:var(--accent); color:var(--navy);}
  /* "Showing X only" banner (2026-09-29, Phase 3) -- shown atop Form/Export/Fluency AI whenever
     that tab is scoped to one form (which, for Export/Fluency AI, is now the only way to reach
     them at all). Reuses the mockup's own accent-tinted treatment. */
  .scope-banner{display:flex; align-items:center; gap:8px; background:color-mix(in srgb, var(--accent) 12%, transparent); border:1px solid color-mix(in srgb, var(--accent) 35%, transparent); border-radius:8px; padding:9px 14px; font-size:13px; margin-bottom:16px;}
  .scope-banner b{color:var(--navy);}
  .scope-clear-btn{all:unset; margin-left:auto; font-size:12px; font-weight:700; color:var(--accent); cursor:pointer; text-decoration:underline;}

  .sub-note{font-size:12px; color:var(--muted); margin:0 0 10px;}
  .sub-row{display:flex; align-items:center; gap:14px; padding:11px 4px; border-top:1px solid var(--line); font-size:13px;}
  .sub-row:first-child{border-top:none;}
  .sub-row .sub-school{font-weight:700; width:220px; flex-shrink:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
  .sub-row .sub-bar{flex:1; height:8px; border-radius:999px; background:var(--surface-2); overflow:hidden;}
  .sub-row .sub-bar i{display:block; height:100%; background:var(--accent);}
  .sub-row .sub-n{width:28px; text-align:right; font-weight:700; font-variant-numeric:tabular-nums;}
  .sub-empty{padding:16px 4px; color:var(--muted); font-style:italic; font-size:13px;}
  /* 2026-10-05: breakdown cards now list every school/enumerator (not just the top 8) — scrollable,
     capped to roughly 8 rows tall by default so the common case looks unchanged. */
  .dash-rows-scroll{max-height:320px; overflow-y:auto;}
  .dash-rows-scroll::-webkit-scrollbar{width:8px;}
  .dash-rows-scroll::-webkit-scrollbar-thumb{background:var(--line); border-radius:999px;}

  /* "Submissions by school" actual-vs-expected bars (2026-10-05) — used instead of .sub-row only
     when the form has a real preload dataset to compute an expected total from (see
     findExpectedAssessmentConfig, src/index.js); every other form keeps the plain .sub-row bars. */
  .exp-row{display:flex; align-items:center; gap:8px; padding:11px 4px; border-top:1px solid var(--line); font-size:13px; overflow:hidden;}
  .exp-row:first-child{border-top:none;}
  .exp-row .exp-school{font-weight:700; width:40%; flex-shrink:1; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
  .exp-row .exp-bar{flex:1; min-width:16px; height:8px; border-radius:999px; background:var(--surface-2); overflow:hidden;}
  .exp-row .exp-bar i{display:block; height:100%; background:var(--accent); border-radius:999px;}
  .exp-row .exp-bar i.met{background:var(--good);}
  .exp-row .exp-n{width:62px; text-align:right; font-variant-numeric:tabular-nums; color:var(--muted); font-size:11.5px; flex-shrink:0; white-space:nowrap;}
  .exp-row .exp-n b{color:var(--ink); font-weight:700;}
  .exp-row .exp-n.met{color:var(--good); font-weight:700;}
  .exp-row .exp-n.met b{color:var(--good);}
  .exp-check{display:inline-block; margin-left:3px;}
  .exp-legend{display:flex; gap:18px; font-size:12px; color:var(--muted); margin:8px 4px 0; padding-top:8px; border-top:1px solid var(--line);}
  .exp-legend span{display:inline-flex; align-items:center; gap:6px;}
  .exp-legend i{width:10px; height:10px; border-radius:50%; display:inline-block;}

  html{--_c7a:1;}
  html{--_c7:1;}
  /* Real bug found 2026-09-06 from a direct screenshot comparison: the mockup's own ".main"
     content area has NO width cap at all (Dashboard uses the full available width), and only
     non-Dashboard panels get a 980px cap that's plain block-left-aligned, never centered with
     margin:auto. #app previously capped AND centered every tab at 900px — fixed to match the
     mockup exactly: no cap here, a separate rule below caps only Form/Export/Manage access. */
  /* min-width:0 added 2026-10-20 (Data tab conversion) -- #app is a CSS Grid item (#layout's own
     `1fr` track), and grid items default to min-width:auto same as flex items -- a real bug found
     live: the Data tab's own wide table (see #data-table-scroll's own comment) had enough intrinsic
     width to blow out the WHOLE `1fr` track past the viewport, not just its own flex chain, pushing
     everything in #app (not only Data) wider than the window. Nothing else in #app has ever needed
     this before because nothing else renders content anywhere near this wide. */
  #app{padding:20px 16px 60px; min-width:0;}
  #tab-design, #tab-export, #tab-connect{max-width:980px;}
  /* Stage 1 admin redesign's own mockup (docs/redesign/mockup/Main.dc.html) caps its whole main
     content column at 1280px (line 90) -- the Surveys list/Overview were stretching edge-to-edge
     with no cap at all before this, a real visible mismatch ("not a full page for the card") found
     2026-10-14. This supersedes the OLDER, unrelated Atlas Console mockup's own "no cap" decision
     referenced in the comment above (a different mockup, from before this redesign). */
  #tab-dashboard{max-width:1280px;}
  /* Cases never got this same cap when Dashboard did (2026-10-14) -- a real gap found directly via
     screenshot comparison, 2026-10-23: the mockup's "Case lists"/Assign cases/Quota/Back-check
     cards all live under that SAME single 1280px-capped main content wrapper (Main.dc.html:90), so
     Cases stretching edge-to-edge (no cap at all) was a real, visible mismatch, not a rendering
     quirk. Same value, same reasoning as #tab-dashboard right above. */
  #tab-cases{max-width:1280px;}
  html{--_c8:1;}
  /* .panel itself clips (overflow:hidden, for the rounded-corner header bar) — this inner
     scroll container lets a wide table degrade to horizontal scroll on a narrow viewport
     instead of clipping silently. The Export tab's earlier full-bleed/near-full-viewport
     override was removed once the row collapsed to two buttons (Download CSV + a "More" menu)
     instead of six — that redesign is what actually needed the extra width, not the tab itself,
     so #tab-export now shares the same centered 900px width as Design/Connect again. */
  #export-list{overflow-x:auto;}
  #export-list table{width:100%; border-collapse:collapse;}
  #export-list th, #export-list td{padding:10px 8px;}
  #export-list th.col-sub, #export-list td.col-sub{text-align:center;}
  html{--_c9:1;}
  /* Export tab, grouped-clusters layout (2026-09-02) — the six per-form actions genuinely fall
     into three purposes (export the raw data / review it / report on it), so they're grouped
     visibly by that instead of one primary button + everything else hidden behind "More". */
  .export-row{background:var(--surface); border:1px solid var(--line); border-radius:10px; padding:14px 16px; margin-bottom:10px;}
  .export-row-top{display:flex; justify-content:space-between; align-items:baseline; gap:10px; margin-bottom:12px;}
  .export-row-title{font-weight:700; font-size:14px;}
  .export-row-count{font-size:12px; color:var(--muted); flex-shrink:0;}
  .export-clusters{display:flex; gap:22px; flex-wrap:wrap;}
  .export-cluster-label{font-size:10.5px; font-weight:700; text-transform:uppercase; letter-spacing:0.07em; color:var(--muted); margin-bottom:6px;}
  .export-cluster-btns{display:flex; gap:6px; flex-wrap:wrap;}
  .export-cluster-btns button{
    all:unset; cursor:pointer; font-size:12px; font-weight:600; padding:6px 10px; border-radius:8px;
    border:1px solid var(--line); background:var(--surface); color:var(--ink);
  }
  .export-cluster-btns button:hover{border-color:var(--accent);}
  .export-cluster-btns button.primary{background:var(--navy); color:var(--navy-ink); border-color:var(--navy);}
  .export-cluster-btns button:disabled{opacity:0.4; cursor:not-allowed;}
  .export-cluster-btns button:disabled:hover{border-color:var(--line);}
  .panel{background:var(--surface); border:1px solid var(--line); border-radius:6px; margin-top:16px; overflow:hidden;}
  .panel-head{display:flex; align-items:center; justify-content:space-between; padding:12px 16px; border-bottom:3px solid var(--good); font-weight:700;}
  .panel-toolbar{display:flex; gap:18px; padding:10px 16px; background:var(--panel-head); border-bottom:1px solid var(--line);}
  .toolbar-btn{all:unset; cursor:pointer; display:flex; flex-direction:column; align-items:center; gap:2px; font-size:0.72rem; color:var(--muted); min-width:44px; text-align:center;}
  html{--_c10:1;}
  /* Explicit block+center on both lines (2026-09-02), not just flex align-items — the archive
     toggle's label ("Show archived (0)") is much wider than "Refresh"/"New form", and relying on
     flex's own item-sizing alone left its icon reading as off-center above the longer text. */
  .toolbar-btn .icon, .toolbar-btn > span:last-child{display:block; width:100%; text-align:center; white-space:nowrap;}
  .toolbar-btn .icon{font-size:1.1rem;}
  .toolbar-btn:hover{color:var(--ink);}

  /* Real toggle switch (mockup-fidelity fix, 2026-10-23) -- Org Settings' AI-features card used
     plain checkboxes + a separate "Save" button; the mockup uses real on/off switches that save
     immediately (Main.dc.html's own .switch class, same 44x26px pill + 20px knob spec, reused
     verbatim). On/off colour and knob position are set per-instance via inline style in
     connect.js's own render, same as the mockup's own cl.style-style inline computation. */
  .switch{width:44px; height:26px; border-radius:99px; border:0; cursor:pointer; position:relative; flex:none;}
  .switch > span{position:absolute; top:3px; width:20px; height:20px; border-radius:50%; background:#ffffff; transition:left 0.15s;}

  html{--_c11:1;}
  /* Manage access tab restyled 2026-09-06 (Phase 6) — .panel/.panel-head wrapper (still used by
     nothing else now) replaced with the mockup's own plain card-stack pattern; every id/data-*/
     click handler underneath (btn-save-app-access, btn-create-enum-user, etc.) is unchanged. */
  .connect-grid{display:flex; flex-direction:column; gap:16px; max-width:720px;}
  /* Org Settings' own card grid (mockup-fidelity fix, 2026-10-23) -- matches the mockup's real
     Settings layout exactly (Main.dc.html:715: display:grid; grid-template-columns:repeat(auto-fit,
     minmax(320px, 1fr))). Deliberately a NEW class, not a .connect-grid edit -- that class is also
     used by Team's own single "Shared device login" card, which has no reason to become a grid. */
  .org-settings-grid{display:grid; grid-template-columns:repeat(auto-fit, minmax(320px, 1fr)); gap:16px;}
  .connect-card{padding:18px 20px;}
  .connect-card h3{margin:0 0 4px; font-size:14.5px; font-family:var(--font-disp); display:flex; align-items:center; gap:7px;}
  .connect-card .sub{margin:0 0 14px; font-size:12px;}
  .dot-flag{width:6px; height:6px; border-radius:50%; background:var(--bad); display:inline-block;}

  .upload-card{padding:16px; border-bottom:1px solid var(--line); background:var(--surface-2);}
  .new-form-opt{
    display:flex; align-items:center; gap:12px; border:1.5px solid var(--line); border-radius:8px;
    padding:12px 14px; margin-bottom:8px; cursor:pointer;
  }
  .new-form-opt:hover{border-color:var(--accent); background:color-mix(in srgb, var(--accent) 6%, var(--surface));}
  .new-form-opt .nficon{font-size:1.3rem; flex-shrink:0;}
  .new-form-opt .nflabel{font-weight:700; font-size:0.9rem;}
  .new-form-opt .nfdesc{color:var(--muted); font-size:0.78rem; margin-top:2px;}
  .upload-card label{font-size:0.85rem;}
  .upload-card .muted{margin:2px 0 8px;}

  /* New-survey modal's "How do you want to start?" grid (mockup-fidelity closure, 2026-10-21) --
     a real single-select grid of 5 cards (matching the approved mockup's own layout exactly),
     replacing the old new-form-opt row list's immediate-action behavior (clicking used to jump
     straight into Upload/Build/Template; this grid only marks a selection, "Create survey" acts). */
  .new-survey-start-card{
    border:1.5px solid var(--line); border-radius:8px; padding:12px 14px; cursor:pointer; text-align:left;
    background:var(--surface); font-family:inherit;
  }
  .new-survey-start-card:hover{border-color:var(--accent);}
  .new-survey-start-card.on{border-color:var(--accent); background:color-mix(in srgb, var(--accent) 8%, var(--surface));}
  .new-survey-start-card .nflabel{font-weight:700; font-size:0.88rem;}
  .new-survey-start-card .nfdesc{color:var(--muted); font-size:0.78rem; margin-top:3px;}

  html{--_c12:1;}
  /* Form tab restyled 2026-09-06 (Phase 4 of the admin redesign) — .form-row becomes a real card
     (the mockup's own .fcard look) instead of a bordered row; every id/data-* attribute the JS
     queries by (drag-handle, form-detail, row-actions' own icon-btn children, dtabs/dtab/
     dtab-panel) is completely unchanged — only how each already-existing element is painted. The
     colored left accent bar (row-bar, distinguishing a form row from a dataset sub-row) isn't in
     the mockup at all, but is a real existing feature worth keeping — kept as a thin card-edge
     accent instead of a separate flex column, matching how the mockup itself already uses a
     border-left accent elsewhere (.fdash-head) for the same kind of emphasis. */
  .form-row{background:var(--surface); border:1px solid var(--line); border-radius:10px; margin-bottom:12px; display:flex; overflow:hidden;}
  .form-row:last-child{margin-bottom:0;}
  html{--_c13:1;}
  /* Archive (2026-09-02) — muted, not hidden-looking: still fully usable (Unarchive/Export/etc
     all work normally), just visually set apart from the active forms sitting above it. */
  .form-row.archived-row, .export-row.archived-row{opacity:0.65;}
  .archived-badge{
    display:inline-block; font-size:0.68rem; font-weight:700; text-transform:uppercase; letter-spacing:0.04em;
    color:var(--muted); background:var(--surface-2); border:1px solid var(--line); border-radius:4px;
    padding:1px 6px; margin-left:6px; vertical-align:middle;
  }
  /* Matched to the mockup's own .drag exactly (2026-09-06): a muted grip glyph, not an icon-btn. */
  .drag-handle{cursor:grab; color:var(--muted); font-size:13px; line-height:1.5; padding-top:1px; user-select:none; flex-shrink:0;}
  .drag-handle:active{cursor:grabbing;}
  .form-row.dragging{opacity:0.4;}
  .form-row.drag-over-top{box-shadow:inset 0 2px 0 0 var(--accent);}
  .form-row.drag-over-bottom{box-shadow:inset 0 -2px 0 0 var(--accent);}
  .row-bar{width:4px; flex-shrink:0;}
  .row-bar.form{background:var(--form-bar);}
  .row-bar.dataset{background:var(--dataset-bar);}
  .row-body{flex:1; padding:16px 18px;}
  .row-top{display:flex; align-items:flex-start; justify-content:space-between; gap:12px; flex-wrap:wrap;}
  .row-title{font-weight:700; font-size:14px;}
  /* Matched to the mockup's own .fcard-meta exactly (2026-09-06): only the Form ID gets the
     monospace treatment (via .mono), plain flex+gap spacing instead of literal middot separators,
     and "N submissions"/"uploaded DATE" with no "Submissions:"/"Uploaded:" label prefixes. */
  .row-meta{color:var(--muted); font-size:0.75rem; margin-top:4px; display:flex; flex-wrap:wrap; gap:4px 14px;}
  .row-meta .mono{font-family:var(--font-mono);}
  .expand-btn{all:unset; cursor:pointer; font-size:0.75rem; font-weight:700; color:var(--navy); display:flex; align-items:center; gap:4px;}
  .expand-btn .car{display:inline-block; transition:transform .15s ease;}
  .expand-btn.open .car{transform:rotate(180deg);}
  .row-actions{display:flex; gap:2px; flex-wrap:wrap; padding:10px 0 14px; border-bottom:1px solid var(--line); margin-bottom:14px;}
  html{--_c14:1;}
  /* Collapsed cards, expand to configure (2026-09-02) — the detail section (actions/datasets/
     config tabs) only appears once a card is expanded; a top border marks it as a distinct
     section dropping down from the always-visible collapsed summary above it. */
  .form-detail{border-top:1px solid var(--line); margin-top:12px; padding-top:2px;}
  .icon-btn{all:unset; cursor:pointer; display:flex; flex-direction:column; align-items:center; gap:4px;}
  .icon-btn{font-size:10.5px; font-weight:700; color:var(--muted); padding:8px 10px; border-radius:8px;}
  .icon-btn{min-width:72px; text-align:center; border:1px solid transparent;}
  .icon-btn:hover{background:var(--surface-2); border-color:var(--line); color:var(--ink);}
  .icon-btn .icon{font-size:1rem;}
  /* Real custom icon set (2026-09-06), replacing the plain emoji glyphs for the 9 toolbar
     actions the user provided icons for (Open/Copy link/Download XLSForm/Questionnaire PDF/
     QA Report/Update/Edit/Archive/Delete) — Unarchive/Preview draft/Deploy/Discard draft keep
     their existing emoji glyphs since no replacement was provided for those. */
  .icon-img{width:18px; height:18px; border-radius:50%; display:block;}
  .sub-rows{margin-top:8px; border:1px solid var(--line); border-radius:8px; overflow:hidden;}
  html{--_c15:1;}
  /* Plain checkbox+label (2026-09-06, matching the mockup's own "Show archived" control exactly)
     — the underlying id/click-wiring/dynamic archived-count label text are all unchanged, only
     the control itself changed from an icon-button to a real checkbox. */
  .check-label{display:flex; align-items:center; gap:6px; font-size:12.5px; color:var(--muted); cursor:pointer; white-space:nowrap;}
  .check-label input[type=checkbox]{width:15px; height:15px; accent-color:var(--navy); cursor:pointer; margin:0;}

  /* Fixed-width, small-text search box matching the mockup's own .search exactly (2026-09-06) —
     previously relied on the generic input[type=text] rule (full-width, no font-size override). */
  .search{border:1px solid var(--line); background:var(--surface); border-radius:8px; padding:9px 12px; font-size:13px; width:220px;}
  .search::placeholder{color:var(--muted);}

  /* .btn's base display was never set after "all:unset" strips it to its initial value (inline),
     not the inline-flex the mockup's own .btn relies on for vertical+horizontal text centering --
     real bug found 2026-10-14 ("Open" button text not centered), affecting every .btn in this app. */
  .btn{all:unset; cursor:pointer; padding:9px 14px; border-radius:8px; background:var(--navy); color:var(--navy-ink); font-weight:600; font-size:13px;
       display:inline-flex; align-items:center; justify-content:center; gap:6px; box-sizing:border-box; white-space:nowrap;}
  .btn.secondary{background:var(--surface); border:1px solid var(--line); color:var(--ink);}
  /* Matches the mockup's own .btn-danger exactly -- a subtler outlined/red-TEXT treatment (base
     .btn's white-ish surface + red text/border), not a solid filled-red button. Only used by the
     Overview draft banner's "Discard draft" so far -- safe to define this way, not a repaint of an
     existing solid-danger convention used elsewhere. */
  .btn.danger{background:var(--surface); border:1px solid color-mix(in srgb, var(--bad) 45%, transparent); color:var(--bad);}
  .btn.danger:hover{background:color-mix(in srgb, var(--bad) 8%, var(--surface));}
  /* Survey-row "Open" button (2026-10-14) -- the mockup's own filled btn-teal, not the white/
     outlined .btn.secondary used for every other row/toolbar action in this console. */
  .btn.teal{background:var(--accent); color:var(--accent-ink);}
  .btn.teal:hover{filter:brightness(1.08);}
  html{--_c16:1;}
  /* Real gap caught 2026-09-06 alongside the Export/Dashboard fidelity pass: the Dashboard's own
     "View"/"View dashboard" buttons have used class="btn btn-sm" since Phase 3, but .btn-sm was
     never actually defined — these buttons have been rendering full-size the whole time. */
  /* Matches the mockup's own .btn-sm exactly (2026-10-14 fix: was missing a real min-height,
     rendering a few px shorter than the mockup's own 34px small-button size everywhere). */
  .btn.btn-sm{padding:5px 10px; font-size:13px; min-height:34px;}
  .row{display:flex; gap:8px; flex-wrap:wrap; align-items:center; margin-top:8px;}
  .muted{color:var(--muted); font-size:0.85rem;}
  .error{color:var(--bad);}
  .warn-item{font-size:0.85rem; color:var(--muted); margin-top:6px;}
  input[type=text], input[type=password]{padding:9px 11px; font-size:13px; border:1px solid var(--line); border-radius:7px; width:100%; background:var(--surface);}
  select{padding:9px 11px; font-size:13px; border:1px solid var(--line); border-radius:7px; background:var(--surface); color:var(--ink);}
  details summary{cursor:pointer; font-size:0.82rem; color:var(--muted); padding:6px 0;}
  html{--_c17:1;}
  /* Overrides the generic muted/small "details summary" rule above for a dataset block's own
     outer summary — collapsed by default (2026-09-01) so a form with several preload datasets
     doesn't push its Roster/Audio/etc tabs far down the page; this one needs to read like a
     real row title (bold, normal size), not the small "Fix column mapping" toggle nested inside it. */
  summary.dataset-summary{font-size:0.9rem; font-weight:700; color:var(--ink);}
  summary.dataset-summary .muted{font-weight:400; font-size:0.82rem;}
  .dtabs{display:flex; gap:6px; margin-top:14px; margin-bottom:12px; flex-wrap:wrap;}
  .dtab{all:unset; cursor:pointer; padding:6px 10px; font-size:11.5px; font-weight:700; color:var(--muted);
    white-space:nowrap; background:var(--surface-2); border:1px solid var(--line); border-radius:999px;}
  .dtab.on{background:var(--navy); border-color:var(--navy); color:#fff;}
  .dtab-count{color:inherit; font-weight:400; opacity:0.8; font-family:var(--font-mono); font-size:0.68rem; margin-left:3px;}
  .dtab-panel{padding:12px 14px; font-size:12.5px; background:var(--surface-2); border-radius:8px; margin-top:2px;}
  .dtab-panel[hidden]{display:none;}
  .add-row{padding:10px 16px; color:var(--accent); font-size:0.85rem; cursor:pointer; border-top:1px solid var(--line);}
  .add-row:hover{background:var(--ground);}
  table{width:100%; border-collapse:collapse; margin-top:10px;}
  td, th{padding:8px; border-bottom:1px solid var(--line); text-align:left; font-size:0.9rem;}

  html{--_c18:1;}
  /* Login screen restyled 2026-09-05 (Phase 2 of the admin redesign) — colors/fonts only, same
     real branded photo-background approach as before (this screen isn't in the mockup at all).
     .panel's own translucent background is now navy-tinted (rgb of --navy) instead of the old
     near-black-green, and the Sign In button/links inherit the new shared navy/teal palette
     instead of a one-off hardcoded blue (#2F8FE0) that Phase 1's shared .btn restyle had already
     made inconsistent with the rest of the app. */
  /* Matched to app/index.html's own login screen (2026-09-07) — found via a direct side-by-side
     screenshot comparison that this screen was never actually carried into the Atlas Console
     redesign the way the rest of admin was: still the plain NewGlobe logo/white inputs/navy-on-
     navy button from before, while the app's login got the full Lens-branded treatment. Same
     two real bugs fixed here as were found on the app's login the same day, since this used the
     identical pattern: (1) a radial-gradient vignette centered off-center left a weak, barely-
     tinted spot at the edges where login_bg.jpg's own warm/skin-tone content could show through
     on a real tall viewport — replaced with a flat, uniform scrim (two identical gradient stops)
     so there's no fade-out to have a weak spot in the first place; (2) background-attachment:
     fixed dropped for the same real-mobile-browser unreliability reason. */
  #login-screen{position:fixed; inset:0; overflow-y:auto;
    background:linear-gradient(rgba(15,46,76,0.82), rgba(15,46,76,0.82)), url(./assets/login_bg.jpg) center/cover no-repeat;}
  #login-screen .login-wrap{min-height:100%; display:flex; flex-direction:column; align-items:center; justify-content:center; padding:40px 16px;}
  #login-screen .panel{background:rgba(15,46,76,0.82); backdrop-filter:blur(6px); -webkit-backdrop-filter:blur(6px);
    border:1px solid rgba(255,255,255,0.12); border-radius:14px; padding:28px; width:100%; max-width:360px;
    box-shadow:0 12px 40px rgba(0,0,0,0.35);}
  /* Lens badge/wordmark/tagline — identical pattern to app/index.html's .lens-badge, reusing the
     admin topbar's own existing Lens_logo_cropped.jpg (already square, already navy-backed) so
     no new asset is needed. */
  #login-screen .lens-badge{
    width:72px; height:72px; border-radius:20px; margin:0 auto;
    background:rgba(255,255,255,0.06); border:1px solid rgba(255,255,255,0.14);
    display:flex; align-items:center; justify-content:center; overflow:hidden;
  }
  #login-screen .lens-badge img{width:100%; height:100%; object-fit:cover; display:block;}
  #login-screen .lens-wordmark{font-family:var(--font-disp); font-weight:700; font-size:22px; color:#fff; text-align:center; margin-top:10px;}
  #login-screen .lens-tagline{font-size:13px; color:rgba(255,255,255,0.6); text-align:center; margin-top:2px;}
  #login-screen label{
    font-size:12px; font-weight:700; text-transform:uppercase; letter-spacing:0.05em;
    display:block; margin-top:14px; color:rgba(255,255,255,0.75);
  }
  #login-screen input{margin-top:6px; background:rgba(0,0,0,0.18); border-color:rgba(255,255,255,0.16); color:#fff;}
  #login-screen input::placeholder{color:rgba(255,255,255,0.4);}
  /* Override, not inherit, the shared admin .btn's navy — navy-on-navy against this same panel
     background is exactly the low-contrast "button blends into the card" bug the screenshot
     comparison found; teal is what the app's own Sign In button already uses on this identical
     glass panel, confirmed there to have good contrast. */
  #login-screen .btn{width:100%; box-sizing:border-box; justify-content:center; align-items:center;
    margin-top:22px; display:flex; padding:10px 16px; font-size:0.92rem; border-radius:6px; line-height:1.4;
    background:var(--accent); color:var(--accent-ink);}
  #login-screen .login-links{display:flex; justify-content:flex-end; margin-top:8px;}
  #login-screen .login-links a{color:color-mix(in srgb, var(--accent) 55%, white); font-size:0.82rem; text-decoration:none;}
  #login-screen .login-links a:hover{text-decoration:underline;}

  html{--_c19:1;}
  /* --- Data tab (Phase 3 admin redesign, 2026-10-20) --- */
  /* Converted from a position:fixed;inset:0 full-page overlay (#data-modal) into a real inline
     tab, same conversion/height-budget arithmetic #tab-build's own Phase A already established --
     see that rule's own comment for why 124px. #data-shared-header needed its own extra padding
     override before this (the old #data-modal wasn't a descendant of #app, so it never inherited
     #app's own 20px/16px padding) -- now a plain, unstyled div like every other shared-header, same
     as #builder-shared-header/#export-shared-header/etc. */
  /* min-width:0 on both flex items below -- a real bug found live, not assumed safe: #data-table
     can be far wider than the viewport (a real submission export often has 50-100+ columns,
     min-width:100% below), and #data-table-scroll's own overflow:auto could only contain that
     width when #data-modal was position:fixed;inset:0 (a fixed box has no "intrinsic content
     width" to grow to). As a normal flex child now, min-width defaults to auto, which let the wide
     table push #data-modal-body, then #tab-data itself, wider than the window -- visibly shoving
     the topbar's own Columns/Show labels/Back-to-Surveys controls off the right edge of the screen
     instead of staying put while the table scrolled under them. */
  #tab-data{display:flex; flex-direction:column; height:calc(100vh - 124px); min-height:0;}
  .data-toggle{display:flex; align-items:center; gap:6px; font-size:13px; cursor:pointer;}
  #data-modal-body{flex:1; display:flex; flex-direction:column; min-height:0; min-width:0; padding:14px 20px;}
  #data-table-scroll{flex:1; overflow:auto; min-width:0; border:1px solid var(--line); border-radius:6px;}
  #data-table{border-collapse:collapse; width:max-content; min-width:100%; font-size:13px;}
  #data-table th, #data-table td{border-bottom:1px solid var(--line); border-right:1px solid var(--line); padding:8px 10px; text-align:left; white-space:nowrap; max-width:280px; overflow:hidden; text-overflow:ellipsis;}
  #data-table thead th{background:var(--panel-head); position:sticky; top:0; cursor:pointer; user-select:none; z-index:1;}
  #data-table thead th:hover{background:var(--line);}
  #data-table thead tr.filter-row th{cursor:default; background:var(--surface); padding:4px 6px;}
  #data-table thead tr.filter-row th:hover{background:var(--surface);}
  #data-table thead tr.filter-row input, #data-table thead tr.filter-row select{width:100%; font-size:12px; padding:4px; margin:0;}
  #data-table tbody tr:hover{background:var(--ground);}
  .data-eye-btn{all:unset; cursor:pointer; padding:2px 6px; font-size:1rem;}
  .data-sort-arrow{color:var(--accent); margin-left:4px;}
  #data-pagination{display:flex; align-items:center; gap:12px; padding:12px 0 0; justify-content:center; flex-wrap:wrap;}
  html{--_c20:1;}
  /* Supervisor validation status (2026-09-01) — a pill-styled <select> right in the row, no
     separate save step. Color mirrors the QA report's own navy/red/orange/green convention:
     rejected=bad, flagged=accent(amber), approved=good, pending=muted/neutral. */
  .status-select{border:none; border-radius:12px; padding:4px 10px; font-size:12px; font-weight:700; cursor:pointer; appearance:none; -webkit-appearance:none;}
  .status-select.status-pending{background:var(--panel-head); color:var(--muted);}
  .status-select.status-approved{background:var(--good); color:#fff;}
  .status-select.status-flagged{background:var(--warn); color:#fff;}
  .status-select.status-rejected{background:var(--bad); color:#fff;}
  html{--_c21:1;}
  /* Audio browser clip rows (2026-09-02) — fixed grid columns so "Open in Drive" lands in the
     same place on every row regardless of the field name's length (was flex + min-width,
     which let a long name like "note_oral_reading3_text" push everything after it rightward
     while short ones like "letters" stayed put). */
  .audio-clip-row{display:grid; grid-template-columns:180px 320px auto; align-items:center; gap:8px; margin-top:6px;}
  .audio-clip-field{font-size:13px; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;}
  /* Kept at 60px visible (2026-09-28, reverted from a taller box that traded the problem for
     wasted space) — Drive's own embedded player squeezed straight into a 60px iframe renders its
     play button/volume control off-centre, cut off against a timestamp row above it. Fix: render
     the real iframe at a size Drive has real room to lay out in (960x180 — same 16:3 shape as
     this 320x60 slot, just 3x bigger), then uniformly scale the whole thing down by exactly 1/3
     to fit. Scaling (not cropping) is what keeps this correct regardless of exactly where Drive
     places its own controls — whatever their relative position in the bigger render, that's
     where they land after the shrink too. The 1/3 factor is tied to the grid column's 320px width
     above; if that ever changes, the width/height/scale here need to change together with it.
     Not independently visually verified here — Drive's preview requires a Google sign-in this
     headless environment doesn't have, so this couldn't be screenshot-checked before shipping. */
  .audio-clip-player-wrap{width:100%; height:60px; overflow:hidden; position:relative; background:#000; border-radius:4px;}
  .audio-clip-player{position:absolute; top:0; left:0; width:960px; height:180px; border:none; transform:scale(0.33333); transform-origin:top left;}
  .audio-clip-link{font-size:12.5px; justify-self:start;}

  #data-columns-panel{position:fixed; inset:0; background:rgba(0,0,0,0.35); z-index:1100; display:flex; align-items:center; justify-content:center;}
  .data-columns-panel-inner{background:var(--surface); border-radius:8px; width:360px; max-width:90vw; max-height:80vh; display:flex; flex-direction:column; padding:16px;}
  .data-columns-panel-head{display:flex; justify-content:space-between; align-items:center; font-weight:700; margin-bottom:10px;}
  #data-columns-search{margin-bottom:8px;}
  #data-columns-list{overflow-y:auto; flex:1;}
  .data-column-row{display:flex; align-items:center; gap:8px; padding:5px 0; font-size:13px;}
  .data-column-row .muted{font-size:12px;}
  /* "Select all" row (2026-10-06, direct feedback: too many columns to check one by one) --
     visually set apart from the plain per-column rows below it with a bottom border, same
     convention as this project's other "divider before a summary/control row" patterns. */
  .data-column-row.select-all{font-weight:700; border-bottom:1px solid var(--line); padding-bottom:8px; margin-bottom:4px;}

  #data-record-modal{position:fixed; inset:0; background:rgba(0,0,0,0.35); z-index:1100; display:flex; align-items:center; justify-content:center; padding:20px;}
  .data-record-inner{background:var(--surface); border-radius:8px; width:min(600px,100%); max-height:85vh; display:flex; flex-direction:column;}
  .data-record-head{display:flex; justify-content:space-between; align-items:center; font-weight:700; padding:14px 18px; border-bottom:1px solid var(--line);}
  #data-record-body{overflow-y:auto; padding:14px 18px;}
  /* Fluency AI reading detail modal (2026-09-29) -- same overlay/card/close-button pattern as
     #data-record-modal above, just wider (760px, not 600px) since a real reading's word-by-word
     passage comparison needs more breathing room than a plain field/value record does. */
  #fa-detail-modal{position:fixed; inset:0; background:rgba(0,0,0,0.35); z-index:1100; display:flex; align-items:center; justify-content:center; padding:20px;}
  .fa-detail-inner{background:var(--surface); border-radius:8px; width:min(760px,100%); max-height:85vh; display:flex; flex-direction:column;}
  .fa-detail-head{display:flex; justify-content:space-between; align-items:center; font-weight:700; font-size:15px; padding:14px 18px; border-bottom:1px solid var(--line);}
  #fa-detail{overflow-y:auto; padding:18px 20px;}
  /* Real bug caught before shipping: this modal reused .fa-detail-inner/.fa-detail-head for its
     INNER box but initially had no rule of its own for the OUTER wrapper -- without this, setting
     style.display="flex" just made it a normal flex container sitting wherever it falls in the
     document, never an actual full-screen centered overlay. Mirrors #fa-detail-modal exactly. */
  #backcheck-detail-modal{position:fixed; inset:0; background:rgba(0,0,0,0.35); z-index:1100; display:flex; align-items:center; justify-content:center; padding:20px;}
  #backcheck-detail-body{overflow-y:auto; padding:18px 20px;}
  .data-record-item{padding:8px 0; border-bottom:1px solid var(--line);}
  .data-record-head{font-size:15px;}
  .data-record-item .rlabel{font-size:11px; color:var(--muted); text-transform:uppercase; letter-spacing:0.04em;}
  .data-record-item .rvalue{margin-top:2px; word-break:break-word;}

  /* Translations modal (Phase B, 2026-10-14) -- same overlay pattern as #data-record-modal above,
     reusing .data-record-head/#data-record-body-style padding directly; just its own id/width
     (980px, the mockup's own real value -- a side-by-side English/translation table needs more
     room than a plain field/value record). */
  #translations-modal{position:fixed; inset:0; background:rgba(0,0,0,0.35); z-index:1100; display:flex; align-items:center; justify-content:center; padding:20px;}
  .translations-inner{background:var(--surface); border-radius:8px; width:min(980px,100%); max-height:88vh; display:flex; flex-direction:column;}
  #translations-modal-body{overflow-y:auto; padding:14px 18px; display:flex; flex-direction:column; gap:12px;}
  #translations-modal-body input[type="text"], #translations-modal-body select{
    padding:8px 9px; border:1px solid var(--line); border-radius:6px; background:var(--surface);
    color:var(--ink); font-size:13px; font-family:inherit; box-sizing:border-box;
  }
  .trans-lang-chip{
    all:unset; cursor:pointer; padding:5px 12px; border-radius:99px; font-size:13px; font-weight:600;
    background:var(--panel-head); color:var(--ink); border:1px solid var(--line);
  }
  .trans-lang-chip:hover{border-color:var(--accent);}
  .trans-lang-chip.on{background:var(--accent); color:var(--accent-ink); border-color:var(--accent);}
  .trans-lang-chip .trans-lang-progress{opacity:0.78; font-weight:400; margin-left:3px;}
  .trans-addlang-row{display:flex; gap:8px; flex-wrap:wrap; align-items:center; background:var(--panel-head); border-radius:10px; padding:10px;}
  .trans-table{border:1px solid var(--line); border-radius:10px; overflow:auto;}
  .trans-row{display:grid; grid-template-columns:130px minmax(200px,1fr) minmax(200px,1fr); gap:10px; padding:10px 12px; min-width:620px; align-items:start; border-top:1px solid var(--line);}
  .trans-row:first-child{border-top:none;}
  .trans-row-head{
    font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:0.04em; color:var(--muted);
    background:var(--panel-head); position:sticky; top:0;
  }
  .trans-row.missing{background:color-mix(in srgb, var(--warn) 6%, var(--surface));}
  .trans-kind{color:var(--muted); font-size:13px;}
  .trans-en{font-size:15px;}
  .trans-row input[type="text"]{width:100%;}

  html{--_c22:1;}
  /* --- Form builder v1 (2026-09-03) — canvas + inspector, same interaction as this tab's own
     collapsed-card "select a question, configure it on the right" pattern, same visual language
     as the published Form Builder Concept artifact this was designed against.
     Converted from a position:fixed modal to a real inline tab (Stage 3 admin redesign,
     2026-10-14, matching the approved mockup -- its own Build screen has no overlay at all). The
     old modal's independent-scroll canvas/inspector columns only worked because the whole chain
     was pinned to exactly 100vh (position:fixed;inset:0) -- #tab-build now gets its OWN height
     budget instead (the same calc(100vh - 64px) arithmetic #sidebar already uses for the same
     reason, with a little extra margin for #app's own top/bottom padding), so #builder-workspace's
     flex:1;min-height:0 below still has a real, bounded height to divide between its columns
     exactly as before -- this is the one non-cosmetic change a fixed-overlay-to-inline-tab
     conversion needs; everything else here is a pure background/border cleanup. */
  /* No !important here (removed 2026-10-19 -- see legacy.js's selectTab()/TAB_SHOWN_DISPLAY for the
     full story). It was added 2026-10-14 to beat selectTab()'s inline style.display="block" (this
     tab needs flex, not block, for its 3-column outline/canvas/inspector layout) -- but !important
     beats ANY non-important inline style, including the "none" the same loop sets when hiding this
     tab, so #tab-build never actually hid again once opened once; it just stayed rendered, full
     height, underneath whatever tab was switched to (real bug, reported directly with a screenshot
     of Dashboard content and a leftover Build tab both visible at once). selectTab() now assigns
     "flex" directly as this tab's own shown-value instead, so plain display:flex here (no
     !important) is correctly beaten by its own "none" whenever this tab is hidden. */
  #tab-build{display:flex; flex-direction:column; height:calc(100vh - 124px); min-height:0;}
  #builder-topbar{background:var(--navy); color:#fff; display:flex; align-items:center; gap:14px; padding:12px 20px; flex-wrap:wrap; border-radius:10px 10px 0 0; flex-shrink:0;}
  #builder-title-input{
    all:unset; flex:1; min-width:200px; font-size:17px; font-weight:700; color:#fff;
    border-bottom:1.5px dashed rgba(255,255,255,0.35); padding-bottom:2px;
  }
  #builder-title-input::placeholder{color:rgba(255,255,255,0.5); font-weight:400;}
  #builder-id-line{font-size:11px; color:rgba(255,255,255,0.5); font-family:var(--font-mono);}
  .builder-actions{display:flex; align-items:center; gap:14px;}
  #builder-save{padding:8px 16px;}
  html{--_c23:1;}
  /* Draft/Deploy (2026-09-04) — an edit to an already-published form now stages a draft rather
     than going live immediately (see CLAUDE.md); this bar is the one place that pending state is
     surfaced inside the builder itself, distinct in color from every other bar here on purpose so
     it reads as "attention needed," not just informational. */
  #builder-draft-bar{display:flex; align-items:center; justify-content:space-between; gap:10px; padding:8px 20px; background:color-mix(in srgb, var(--timed-bar) 16%, var(--surface)); border-bottom:1px solid var(--timed-bar); flex-wrap:wrap; flex-shrink:0;}
  #builder-draft-status{font-size:13px; font-weight:700; color:var(--timed-bar);}
  .builder-draft-actions{display:flex; align-items:center; gap:8px;}
  #builder-datasets-bar{display:flex; align-items:center; gap:10px; padding:8px 20px; background:var(--panel-head); border-bottom:1px solid var(--line); flex-wrap:wrap; flex-shrink:0;}
  #builder-datasets-list{display:flex; gap:6px; flex-wrap:wrap;}
  .bi-dataset-chip{
    font-size:12px; font-weight:700; background:var(--surface); border:1px solid var(--line);
    border-radius:20px; padding:3px 10px; color:var(--ink);
  }
  html{--_cAi:1;}
  /* Lens AI Assistant (2026-10-01) -- a persistent floating launcher + overlay panel, visible on
     every screen (not a sidebar tab/page -- see the markup just before #main-app's closing tag).
     z-index:1150 is deliberately above #builder-modal's 1000 AND every other full-screen overlay in
     this file that also sits at 1100 (#data-columns-panel/#data-record-modal/#fa-detail-modal, all
     later in the DOM -- real bug found 2026-10-01 via review: a tied z-index with later DOM order
     meant those three modals silently painted OVER this panel whenever both happened to be open,
     exactly contradicting "floats above everything, reachable from every screen"), so it floats
     correctly no matter what else is open behind it -- "accessible anywhere" means anywhere.
     Insurance rule above per this file's own documented long-comment-drop bug. */
  /* A bare icon with no label gives a first-time user no way to know what it does (real feedback,
     2026-10-01) -- the launcher is a labeled pill, not a silent icon-only bubble. */
  #ai-launcher-btn{
    all:unset; display:flex; align-items:center; gap:8px; position:fixed; bottom:20px; right:20px;
    z-index:1150; padding:8px 16px 8px 8px; border-radius:30px; cursor:grab; background:var(--navy);
    box-shadow:0 6px 18px rgba(15,30,45,.3); transition:transform .12s;
  }
  #ai-launcher-btn:active{cursor:grabbing;}
  #ai-launcher-btn:hover{transform:scale(1.04);}
  #ai-launcher-btn img{width:36px; height:36px; border-radius:50%; object-fit:cover; display:block; flex-shrink:0;}
  #ai-launcher-btn span{color:#fff; font-weight:700; font-size:13.5px; font-family:var(--font-disp); white-space:nowrap;}
  .ai-heading-logo{width:28px; height:28px; border-radius:8px; object-fit:cover; display:block;}
  #ai-assistant-panel{
    display:flex; flex-direction:column; background:var(--surface); overflow:hidden;
    border:1px solid var(--line); border-radius:12px; box-shadow:0 2px 10px rgba(15,30,45,.08);
  }
  /* Anchored to the SAME bottom-right corner as the launcher (not top-right) so the panel visibly
     opens upward from the bubble it was summoned from, rather than appearing disconnected at the
     top of the screen. Sized to match what this panel looked like as a full inline page before
     2026-10-01 (max-width:900px there too) -- a real, roomy panel, not a small chat widget. Grows
     with its content up to the cap rather than a fixed height, so a short "How do I" answer
     doesn't leave a tall box mostly empty. */
  #ai-assistant-panel.ai-floating{
    position:fixed; bottom:20px; right:20px; z-index:1150; width:860px; max-width:calc(100vw - 40px);
    max-height:82vh; box-shadow:0 12px 32px rgba(15,30,45,.28);
  }
  /* Above everything else in this file, including the AI assistant panel -- a confirmation the
     user is actively answering must never be silently buried behind another overlay. */
  .admin-confirm-modal{
    position:fixed; inset:0; z-index:1200; display:flex; align-items:center; justify-content:center;
    background:rgba(16,21,28,0.55); padding:20px;
  }
  .admin-confirm-box{
    background:var(--surface); border:1.5px solid var(--line); border-radius:14px; padding:20px;
    max-width:380px; width:100%; text-align:center; box-shadow:0 8px 24px rgba(16,21,28,0.25);
  }
  .admin-confirm-box p{margin:0 0 18px; font-size:14px; line-height:1.45; color:var(--ink); text-align:left;}
  .admin-confirm-actions{display:flex; gap:10px; justify-content:flex-end;}
  .admin-prompt-input{
    all:unset; box-sizing:border-box; width:100%; padding:9px 12px; margin:-6px 0 18px;
    border:1.5px solid var(--line); border-radius:8px; font-size:14px; color:var(--ink);
  }
  .admin-prompt-input:focus{border-color:var(--accent);}
  .ai-assistant-head{display:flex; align-items:center; justify-content:space-between; padding:10px 10px 10px 14px; border-bottom:1px solid var(--line); flex-shrink:0; cursor:move;}
  .ai-assistant-head #ai-assistant-close{cursor:pointer;}
  .ai-assistant-tabs{display:flex; gap:6px; flex-wrap:wrap;}
  .ai-tab{all:unset; cursor:pointer; font-size:12px; font-weight:700; padding:6px 10px; border-radius:7px; color:var(--muted); background:var(--ground); transition:background .12s,color .12s;}
  .ai-tab:hover{color:var(--accent); background:color-mix(in srgb, var(--accent) 12%, var(--ground));}
  .ai-tab.on{color:#fff; background:var(--navy);}
  .ai-tab.on:hover{color:#fff; background:var(--navy);}
  #ai-assistant-close{all:unset; cursor:pointer; font-size:18px; line-height:1; color:var(--muted); padding:2px 6px; border-radius:6px; transition:background .12s;}
  #ai-assistant-close:hover{background:var(--ground); color:var(--ink);}
  .ai-panel-body{padding:12px 14px 14px; overflow-y:auto; flex:1; min-height:0;}
  .ai-row{display:flex; gap:8px;}
  .ai-row input{flex:1; min-width:0; font:inherit; font-size:13.5px; padding:9px 11px; border-radius:8px; border:1px solid var(--line); background:var(--surface); color:var(--ink); transition:border-color .12s;}
  .ai-row input:focus{outline:none; border-color:var(--accent);}
  .ai-chip-row{display:flex; flex-wrap:wrap; gap:6px; margin-bottom:10px;}
  .ai-chip{all:unset; cursor:pointer; font-size:11.5px; padding:5px 9px; border-radius:999px; background:var(--ground); border:1px solid var(--line); color:var(--muted); transition:border-color .12s,color .12s,background .12s;}
  .ai-chip:hover{border-color:var(--accent); color:var(--accent); background:color-mix(in srgb, var(--accent) 8%, var(--ground));}
  .ai-chat-log{display:flex; flex-direction:column; gap:8px; max-height:320px; overflow-y:auto; margin-bottom:10px;}
  .ai-msg{font-size:13px; line-height:1.45; border-radius:10px; padding:8px 10px; max-width:90%;}
  /* Teal (the accent token), not navy, for the enumerator/admin's own question bubble -- matches
     the approved prototype and the user's own direct correction. */
  .ai-msg.user{align-self:flex-end; background:var(--accent); color:var(--accent-ink);}
  .ai-msg.assistant{align-self:flex-start; background:var(--ground); border:1px solid var(--line); color:var(--ink);}
  .ai-msg.assistant.thinking{color:var(--muted); font-style:italic;}
  .ai-msg.error{align-self:flex-start; background:color-mix(in srgb, var(--bad) 12%, var(--surface)); border:1px solid var(--bad); color:var(--bad);}
  .ai-query-toggle{all:unset; cursor:pointer; display:inline-block; margin-top:6px; font-family:var(--font-mono); font-size:10.5px; color:var(--accent); text-decoration:underline;}
  .ai-query-box{display:none; margin-top:6px; background:var(--surface); border:1px solid var(--line); border-radius:7px; padding:7px 9px; font-family:var(--font-mono); font-size:11px; color:var(--muted); white-space:pre-wrap;}
  .ai-query-box.show{display:block;}
  .ai-result-table{margin-top:6px; width:100%; border-collapse:collapse; font-size:11px;}
  .ai-result-table th,.ai-result-table td{text-align:left; padding:3px 6px; border-bottom:1px solid var(--line);}
  .ai-draft-note{font-size:12px; color:var(--warn); background:color-mix(in srgb, var(--warn) 12%, var(--surface)); border:1px solid var(--warn); border-radius:7px; padding:7px 9px; margin-bottom:10px;}
  .ai-expr-help-btn{all:unset; cursor:pointer; font-size:11.5px; font-weight:700; color:var(--accent); padding:3px 0 0; display:inline-block;}
  .ai-expr-help-btn:hover{text-decoration:underline;}
  .ai-expr-help-btn:disabled{color:var(--muted); cursor:default;}
  #ai-howto-answer{margin-top:10px; font-size:13.5px; line-height:1.5; color:var(--ink);}
  #ai-howto-answer.thinking{color:var(--muted); font-style:italic;}
  #ai-howto-answer.error{color:var(--bad);}
  #ai-draft-status{margin-top:8px; font-size:13px; color:var(--muted); font-style:italic;}
  #ai-draft-status.error{color:var(--bad); font-style:normal;}
  .ai-draft-cols{display:flex; gap:16px; margin-top:10px; flex-wrap:wrap;}
  .ai-draft-col{flex:1; min-width:260px;}
  .ai-draft-col h4{margin:0 0 6px; font-size:11.5px; text-transform:uppercase; letter-spacing:.04em; color:var(--muted); font-family:var(--font-mono);}
  .ai-block-card{background:var(--surface); border:1px solid var(--line); border-radius:10px; padding:12px 14px; border-left:4px solid var(--accent);}
  .ai-block-type{font-family:var(--font-mono); font-size:10px; text-transform:uppercase; letter-spacing:.05em; color:var(--accent); font-weight:700;}
  .ai-block-label{font-weight:700; font-size:14.5px; margin-top:4px; color:var(--ink);}
  .ai-block-choices{margin-top:8px; display:flex; flex-direction:column; gap:4px;}
  .ai-block-choice{font-size:12.5px; padding:5px 9px; border:1px solid var(--line); border-radius:7px; background:var(--ground); color:var(--ink);}
  .ai-block-meta-row{font-family:var(--font-mono); font-size:11px; color:var(--muted); background:var(--ground); border-radius:6px; padding:5px 8px; margin-top:6px;}
  .ai-block-meta-row b{color:var(--ink);}
  .ai-block-warning{margin-top:8px; font-size:12px; color:var(--bad); background:color-mix(in srgb, var(--bad) 10%, var(--surface)); border:1px solid var(--bad); border-radius:7px; padding:6px 9px;}
  .ai-xls-table{width:100%; border-collapse:collapse; font-size:11.5px; font-family:var(--font-mono);}
  .ai-xls-table th,.ai-xls-table td{text-align:left; padding:5px 7px; border:1px solid var(--line); white-space:nowrap;}
  .ai-xls-table th{background:var(--ground); color:var(--muted); font-weight:600;}
  .ai-xls-wrap{overflow-x:auto; border-radius:8px;}
  .ai-xls-caption{font-size:11.5px; color:var(--muted); margin:6px 0 10px;}
  #ai-draft-add-btn{margin-top:10px;}
  #builder-workspace{flex:1; display:flex; min-height:0; border:1px solid var(--line); border-top:0; border-radius:0 0 10px 10px; overflow:hidden;}
  /* Outline column (Stage 3 admin redesign Phase B, 2026-10-14) -- real single-section filtering,
     matching the mockup's own 3-column Build layout. A presentation-layer filter only: clicking an
     entry sets which block (if any) #builder-canvas renders members of -- no mutation function
     changes at all, same real data either way. */
  #builder-outline{flex:0 0 220px; padding:8px 0; background:var(--surface); overflow-y:auto; border-right:1px solid var(--line);}
  .bo-item{all:unset; box-sizing:border-box; display:flex; width:100%; justify-content:space-between; align-items:flex-start; gap:8px; padding:9px 16px; cursor:pointer; font-size:13.5px;}
  .bo-item:hover{background:var(--ground);}
  .bo-item.on{background:color-mix(in srgb, var(--accent) 12%, var(--surface)); border-left:3px solid var(--accent); padding-left:13px;}
  .bo-item-name{font-weight:600; color:var(--ink);}
  .bo-item-tag{display:block; font-size:12px; font-weight:400; color:var(--muted); margin-top:2px;}
  .bo-item-count{font-size:12.5px; color:var(--muted); flex-shrink:0;}
  .bo-section-header{display:flex; justify-content:space-between; align-items:center; gap:10px; flex-wrap:wrap; padding:12px 14px; background:var(--surface); border-bottom:1px solid var(--line); margin:-16px -16px 12px;}
  .bo-section-title{all:unset; cursor:pointer; font:600 19px var(--font-disp); color:var(--ink);}
  .bo-section-title:hover{color:var(--accent); text-decoration:underline;}
  #builder-canvas{flex:1.4; padding:16px; background:var(--ground); overflow-y:auto;}
  #builder-inspector{flex:1; padding:16px 18px; background:var(--surface); overflow-y:auto; border-left:1px solid var(--line);}
  .bq-card{
    background:var(--surface); border:1.5px solid var(--line); border-radius:8px; padding:11px 12px;
    display:flex; align-items:flex-start; gap:10px; margin-bottom:8px; cursor:pointer;
  }
  .bq-card:hover{border-color:var(--accent);}
  /* Real leftover from the old amber-era palette found in Phase 8a (2026-09-06): this was still a
     hardcoded rgba(185,121,31,...) amber glow, the one spot in the whole builder that never
     picked up the navy/teal redesign — fixed to match every other "selected" glow here
     (.bq-card.repeat-head.sel/.section-head.sel below already use color-mix(var(--accent)/etc)). */
  .bq-card.sel{border-color:var(--accent); box-shadow:0 0 0 2px color-mix(in srgb, var(--accent) 18%, transparent);}
  .bq-card .bqdrag{color:var(--muted); font-size:13px; padding-top:2px; cursor:grab; flex-shrink:0;}
  .bq-card .bqdrag:active{cursor:grabbing;}
  html{--_c24:1;}
  /* Move question blocks up/down (2026-09-04) — same visual language as the Design tab's own
     form-list drag-and-drop (.form-row.dragging/.drag-over-top/-bottom), scoped to .bq-card here
     instead. */
  .bq-card.dragging{opacity:0.4;}
  .bq-card.drag-over-top{box-shadow:inset 0 2px 0 0 var(--accent);}
  .bq-card.drag-over-bottom{box-shadow:inset 0 -2px 0 0 var(--accent);}
  .bq-card .bqbody{flex:1; min-width:0;}
  .bq-type-pill{
    font-size:10px; font-weight:700; text-transform:uppercase; letter-spacing:0.04em;
    padding:2px 7px; border-radius:4px; display:inline-block; margin-bottom:4px;
  }
  .bq-type-text{background:color-mix(in srgb, var(--form-bar) 18%, var(--surface)); color:var(--form-bar);}
  .bq-type-select{background:color-mix(in srgb, var(--dataset-bar) 20%, var(--surface)); color:var(--dataset-bar);}
  .bq-type-logic{background:color-mix(in srgb, var(--logic) 20%, var(--surface)); color:var(--logic);}
  .bq-type-timed{background:color-mix(in srgb, var(--timed-bar) 20%, var(--surface)); color:var(--timed-bar);}
  .bq-label{font-weight:600; font-size:14px;}
  .bq-flags{display:flex; gap:5px; margin-top:6px; flex-wrap:wrap;}
  .bq-flag{
    font-size:11px; color:var(--muted); background:var(--panel-head); border:1px solid var(--line);
    border-radius:4px; padding:1px 6px;
  }
  .bq-flag.logic{color:var(--logic); border-color:color-mix(in srgb, var(--logic) 40%, var(--line));}
  .bq-del{all:unset; cursor:pointer; color:var(--muted); padding:2px 6px; flex-shrink:0;}
  .bq-del:hover{color:var(--bad);}
  .bq-dup{all:unset; cursor:pointer; color:var(--muted); padding:2px 6px; flex-shrink:0; font-size:14px;}
  .bq-dup:hover{color:var(--accent);}
  #builder-add-row{
    border:1.5px dashed var(--line); border-radius:8px; padding:12px; text-align:center;
    color:var(--accent); font-weight:700; font-size:13px; cursor:pointer; margin-top:4px;
  }
  #builder-add-row:hover{background:color-mix(in srgb, var(--accent) 8%, var(--surface));}
  #builder-add-picker{
    display:none; margin-top:8px; background:var(--surface); border:1px solid var(--line); border-radius:8px; padding:10px;
  }
  #builder-add-picker.open{display:block;}
  .bq-picker-grid{display:grid; grid-template-columns:repeat(2, 1fr); gap:6px;}
  .bq-type-btn{
    all:unset; cursor:pointer; text-align:left; padding:8px 9px; border-radius:6px; font-size:13px;
    border:1px solid var(--line);
  }
  .bq-type-btn:hover{border-color:var(--accent); background:color-mix(in srgb, var(--accent) 6%, var(--surface));}
  .bq-changetype-btn{
    all:unset; cursor:pointer; font-size:11.5px; font-weight:600; color:var(--accent);
    padding:2px 6px; border:1px solid color-mix(in srgb, var(--accent) 40%, var(--line)); border-radius:4px;
  }
  .bq-changetype-btn:hover{background:color-mix(in srgb, var(--accent) 8%, var(--surface));}
  .bq-picker-grid-wrap{
    margin:8px 0 14px; background:var(--panel-head); border:1px solid var(--line); border-radius:8px; padding:10px;
  }
  html{--_c25:1;}
  /* Repeat groups and Sections (2026-09-04, arbitrary nesting depth) — both are "block" markers
     (repeat_begin/repeat_end, section_begin/section_end) at real positions in the flat
     builderState.questions array; any question physically between a pair is "inside" it, exactly
     mirroring a real XLSForm survey sheet — no nested-DOM tree needed even at depth > 1, since
     depth is just how many block markers are still open at that position in a single left-to-
     right pass (see renderBuilderCanvas). Indentation and rail color are set inline per element
     (margin-left scales with depth; color follows whichever block type is innermost at that
     point) rather than through a fixed single-level CSS class — .bq-nested here only supplies the
     shared structural rule (border width/radius/padding), never the color or amount. */
  .bq-type-repeat{background:color-mix(in srgb, var(--repeat-bar) 20%, var(--surface)); color:var(--repeat-bar);}
  .bq-type-section{background:color-mix(in srgb, var(--section-bar) 20%, var(--surface)); color:var(--section-bar);}
  .bq-card.repeat-head{border-color:var(--repeat-bar);}
  .bq-card.repeat-head.sel{box-shadow:0 0 0 2px color-mix(in srgb, var(--repeat-bar) 30%, transparent);}
  .bq-card.section-head{border-color:var(--section-bar);}
  .bq-card.section-head.sel{box-shadow:0 0 0 2px color-mix(in srgb, var(--section-bar) 30%, transparent);}
  .bq-nested{border-left-width:3px; border-left-style:solid; border-top-left-radius:2px; border-bottom-left-radius:2px;}
  .bq-repeat-foot{font-size:11px; color:var(--muted); padding:0 0 6px 13px; border-left:3px solid var(--repeat-bar);}
  .bq-repeat-add-row{padding-left:13px; border-left:3px solid var(--repeat-bar);}
  .bq-repeat-add-btn{all:unset; cursor:pointer; font-weight:700; font-size:12px; padding:4px 0; display:block;}
  .bq-repeat-add-picker{display:none; margin-top:6px; background:var(--surface); border:1px solid var(--line); border-radius:8px; padding:10px;}
  .bq-repeat-add-picker.open{display:block;}
  /* Inspector's 3 sub-tabs (Stage 3 admin redesign Phase C, 2026-10-14) -- Question/Phone preview/
     Survey, same underline-tab visual language as the per-survey workspace's own 7-tab row. */
  .bi-tabs{display:flex; gap:2px; border-bottom:1px solid var(--line); margin:-16px -18px 16px; padding:0 18px;}
  .bi-tabbtn{all:unset; box-sizing:border-box; display:inline-flex; align-items:center; padding:10px 12px; border-bottom:3px solid transparent; cursor:pointer; white-space:nowrap; font-size:13.5px; font-weight:600; color:var(--muted);}
  .bi-tabbtn.on{border-bottom-color:var(--accent); color:var(--navy);}
  /* max-width trimmed 300->260px (2026-10-20, same bug as the compact-header fix above) -- the
     frame's height is tied to its width via aspect-ratio, so this alone buys back ~70px of real
     height in the already-tight Build-tab inspector column, on top of the header fix. */
  .bi-phone-frame{width:100%; max-width:260px; aspect-ratio:9/18; margin:0 auto; border:8px solid var(--navy); border-radius:28px; overflow:hidden; background:#fff; box-shadow:0 4px 16px rgba(16,21,28,.18);}
  .bi-phone-frame iframe{width:100%; height:100%; border:0; display:block;}
  .bi-empty{color:var(--muted); font-size:13px; padding:30px 4px; text-align:center;}
  .bi-head{font-weight:700; font-size:15px; margin-bottom:2px;}
  .bi-sub{color:var(--muted); font-size:12px; margin-bottom:16px;}
  .bi-group{margin-bottom:16px;}
  .bi-label{font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:0.04em; color:var(--muted); margin-bottom:5px; display:block;}
  .bi-hint{font-size:12px; color:var(--muted); margin-top:4px; line-height:1.4;}
  #builder-inspector input[type="text"], #builder-inspector select, #builder-inspector textarea{
    width:100%; padding:8px 9px; border:1px solid var(--line); border-radius:6px; background:var(--surface);
    color:var(--ink); font-size:13px; font-family:inherit; box-sizing:border-box;
  }
  .bi-choice-row{display:flex; align-items:center; gap:6px; margin-bottom:6px;}
  .bi-choice-row input{flex:1;}
  .bi-choice-del{all:unset; cursor:pointer; color:var(--muted); padding:2px 6px;}
  .bi-choice-del:hover{color:var(--bad);}
  .bi-add-link{all:unset; cursor:pointer; color:var(--accent); font-size:13px; font-weight:700; padding-top:2px; display:inline-block;}
  .bi-choice-chip{
    all:unset; cursor:pointer; flex-shrink:0; width:26px; height:26px; border-radius:6px;
    display:flex; align-items:center; justify-content:center; font-size:13px; color:var(--muted);
    border:1px solid var(--line);
  }
  .bi-choice-chip:hover{border-color:var(--accent);}
  .bi-choice-chip.on{background:color-mix(in srgb, var(--logic) 18%, var(--surface)); border-color:var(--logic); color:var(--logic);}
  .bi-logic-box{background:color-mix(in srgb, var(--logic) 7%, var(--surface)); border:1px solid color-mix(in srgb, var(--logic) 30%, var(--line)); border-radius:8px; padding:12px;}
  .bi-ifthen-row{display:flex; align-items:center; gap:8px; margin-bottom:6px;}
  .bi-ifthen-tag{
    font-size:11px; font-weight:700; text-transform:uppercase; letter-spacing:0.04em; flex-shrink:0;
    width:44px; text-align:center; padding:3px 0; border-radius:4px; background:var(--panel-head); color:var(--muted);
  }
  .bi-toggle-row{display:flex; align-items:center; justify-content:space-between; padding:8px 0; border-top:1px solid var(--line);}
  .bi-toggle-row:first-child{border-top:none;}
  .bi-switch{position:relative; width:34px; height:19px; flex-shrink:0;}
  .bi-switch input{opacity:0; width:0; height:0;}
  .bi-slider{position:absolute; inset:0; background:var(--line); border-radius:20px; cursor:pointer;}
  .bi-slider:before{content:""; position:absolute; width:14px; height:14px; left:2.5px; top:2.5px; background:#fff; border-radius:50%; transition:0.15s;}
  .bi-switch input:checked + .bi-slider{background:var(--good);}
  .bi-switch input:checked + .bi-slider:before{transform:translateX(15px);}

  html{--_c26:1;}
  /* --- Report (Export tab's "Report" button — bar charts + tables aggregate view) --- */
  #report-modal{position:fixed; inset:0; background:var(--surface); z-index:1000; display:flex; flex-direction:column;}
  #report-modal-topbar{background:var(--navy); color:#fff; display:flex; align-items:center; justify-content:space-between; gap:12px; padding:12px 20px; flex-wrap:wrap;}
  #report-modal-title{font-weight:700; font-size:17px;}
  #report-modal-body{flex:1; overflow-y:auto; padding:20px; max-width:820px; margin:0 auto; width:100%;}
  .report-disclaimer{color:var(--muted); font-size:13px; font-style:italic; margin-bottom:20px;}
  .report-section{border-top:1px solid var(--line); padding:18px 0;}
  .report-section:first-child{border-top:none;}
  .report-section h3{margin:0 0 4px; font-size:15px;}
  .report-meta{color:var(--muted); font-size:13px; margin-bottom:14px;}
  /* Plain-language "what does this mean" summary (2026-10-05) -- a deterministic template over
     the real computed numbers above (see computeInsight, src/report.js), set apart from the
     plain-gray TYPE/count line so it reads as a takeaway, not more metadata. */
  .report-insight{background:var(--ground); border-left:3px solid var(--accent); border-radius:0 6px 6px 0; padding:8px 12px; font-size:13.5px; margin-bottom:14px;}
  .report-bar-row{display:flex; align-items:center; gap:10px; margin-bottom:6px; font-size:13px;}
  .report-bar-label{width:160px; flex-shrink:0; text-align:right;}
  .report-bar-track{flex:1; background:var(--ground); border-radius:4px; overflow:hidden; height:16px;}
  .report-bar-fill{background:var(--good); height:100%; border-radius:4px 0 0 4px;}
  .report-bar-count{width:36px; flex-shrink:0; font-weight:700;}
  .report-table{width:100%; margin-top:14px; font-size:13px;}
  .report-table th{text-align:left; color:var(--muted); font-weight:700; padding-bottom:6px; border-bottom:1px solid var(--line);}
  .report-table td{padding:6px 0; border-bottom:1px solid var(--line);}
  .report-table td.num{text-align:right; width:90px;}
