/* ===================================================================
 * tw-tokens.css — TriviaWorks unified design tokens (2026-04-29)
 *
 * Authoritative semantic-token layer for the editor unification arc
 * (memory/editor-unification-arc-2026-04-29.md). Coexists with
 * tw-design-tokens-v4.css; v4 tokens remain in place for surfaces
 * not yet retoken'd. Phase 5 retokens migrate them across.
 *
 * Token namespaces (no collisions with v4 --paper-* / --ink-v4 / etc.):
 *   --surface-*    canvas / raised / overlay / scrim
 *   --text-*       primary / secondary / tertiary / inverse / link
 *   --border-*     subtle / strong / focus
 *   --accent-*     brand
 *   --state-*      success / warning / error / info (text + bg pair)
 *   --pool-*       internal / external / archive (semantic, distinct
 *                  from v4 --pool-import/internal/external)
 *   --type-*       semantic type scale (Gotham)
 *   --space-*      4 / 8 / 12 / 16 / 24 / 32 / 48 / 64
 *   --radius-*     xs / sm / md / lg / xl / pill
 *   --elev-*       elevation shadows 0..4
 *   --motion-*     instant / quick / standard / deliberate
 *   --ease-*       standard / enter / exit / emphasized
 *   --row-h, --pad-x, --pad-y, --gap, --target-min  (density-driven)
 *   --z-*          z-index scale
 *
 * Theming:
 *   :root                 light (default)
 *   [data-theme="dark"]   dark (explicit)
 *   prefers-color-scheme: dark on :root:not([data-theme])  system fallback
 *
 * Density (default compact):
 *   [data-density="comfortable"]   larger row + padding
 *
 * Reduced motion:
 *   prefers-reduced-motion: reduce  collapses all motion tokens to 0
 *
 * Fonts: the house faces (Gotham / Gotham Narrow / JetBrains Mono) are
 * declared in tw-fonts.css. Any page loading this file WITHOUT the
 * legacy tw-design-tokens-v4.css MUST load tw-fonts.css, or headings
 * silently drop to the system stack (see EDITOR-CONVERSION-PLAN 3b).
 *
 * v4 compatibility block (2026-07-22, editor-conversion arc step 1):
 * the section at the bottom of this file defines every legacy v4-named
 * token the SHARED editor sheets/scripts still consume, so the shared
 * shell no longer depends on tw-design-tokens-v4.css being linked.
 * Values are byte-identical to that sheet's (aliases where it aliased,
 * literals with dark variants where it carried real values). Removing
 * a surface's tw-design-tokens-v4.css link therefore no longer strips
 * the shell's ink/font. New code must use the canonical names; the v4
 * names die when the M2 codemod lands.
 * =================================================================== */

:root {
  /* ====== Surfaces ====== */
  --surface-base:     #FBF8F2;
  --surface-raised:   #FFFFFF;
  --surface-overlay:  #FAF6E9;
  --surface-sunk:     #F3EEE3;
  /* Hover wash for a control or row sitting on --surface-base/--surface-raised.
   * Sits one step warmer/darker than --surface-base #FBF8F2 and one step
   * lighter than --surface-sunk #F3EEE3, so a hovered control reads as
   * "lifting toward pressed" without becoming a well. --text-primary on it
   * measures 16.6:1 (AAA), --text-tertiary 5.6:1 (AA). */
  --surface-hover:    #F7F2E6;
  --surface-scrim:    rgba(28, 20, 16, 0.45);

  /* ====== Borders ====== */
  --border-subtle:    #EFE7D3;
  --border-strong:    #D9CFBD;
  --border-focus:     #1F6FEB;

  /* ====== Text ====== */
  --text-primary:     #1C1410;
  --text-secondary:   #3D342E;
  --text-tertiary:    #6B5F55;
  --text-quaternary:  #888888;
  --text-inverse:     #F2ECD9;
  /* #1F6FEB measured 4.37:1 on --surface-base #FBF8F2 - below the 4.5:1 AA
     minimum for body text. Darkened to 5.42:1. Hover was already fine (6.08:1)
     and is unchanged, so the hover still reads as a state change. */
  --text-link:        #1B5FD6;
  --text-link-hover:  #1858C7;

  /* ====== Brand ====== */
  --accent-primary:         #8A6500;
  --accent-primary-pressed: #5E4500;
  --accent-primary-bg:      #FCF6E6;
  --accent-primary-hi:      #C89200;
  /* Text/glyph colour painted ON an --accent-primary fill (Track A
   * 2026-06-10). Light theme: white on the deep gold (5.3:1). Dark
   * theme: charcoal on the bright stage gold (10.8:1). */
  --accent-contrast:        #FFFFFF;

  /* ====== Mechanic panels (music-mashup / music-themed / sounds-like
   * family). Warm beige background + dark gold accent. Distinct from
   * generic --border-subtle / --accent-primary; used for the boxed
   * "stage mechanic" inspector panels in the editors. ====== */
  --mechanic-panel-bg:     #E5E0D5;
  --mechanic-panel-accent: #9B7400;

  /* ====== Question card ink ======
   * Body-ink colour for the tw-question-card surface (catalogue + grid
   * thumbnail). Sits one step lighter than --text-primary, used for
   * mechanic-icon glyphs + minor metadata rows. */
  --card-ink-body:    #3A3A42;

  /* ====== Stage ink cream ======
   * Off-white cream used by stage chrome (BBZ MC modals, overlay
   * scoreboard, player-join, grid view, mechanic style defaults).
   * Distinct from --stage-ink (#F2ECD9) which is the warmer brand
   * cream. --stage-ink-cream is the cooler neutral white used inside
   * the navy stage surfaces for body copy + controls. */
  --stage-ink-cream:  #F0F0F0;

  /* ====== Stage navy mid ======
   * Intermediate navy that sits between --stage (#0A1628) and
   * --stage-hi (#132240). Used as the gradient mid-stop on overlay
   * scoreboard backgrounds, BBZ MC actions row, player-reconnect
   * text colour, and element-media SVG fill. */
  --stage-navy-mid:   #0E1B34;

  /* ====== Audio trim dialog (purple modal skin) ======
   * The tw-audio-trim modal uses a deliberate purple palette distinct
   * from the rest of the editor chrome to signal "destructive media
   * edit". These tokens keep the colour family centralised so a future
   * skin swap is a one-line change. */
  --audio-trim-purple-border: #3A2F5A;
  --audio-trim-text-soft:     #9A8AB8;
  --audio-trim-canvas-bg:     #0E0920;

  /* ====== Semantic state ====== */
  --state-success:     #2E6B3E;
  --state-success-bg:  #DDEEDF;
  /* #B06A14 measured 3.64:1 on its own --state-warning-bg - the worst of the
     three state pairs and well below AA. Darkened to 4.96:1. Success (#2E6B3E
     on #DDEEDF) and error (#A83A2A on #F7DCD6) both already pass. */
  --state-warning:     #935711;
  --state-warning-bg:  #FBEBD2;
  --state-error:       #A83A2A;
  --state-error-bg:    #F7DCD6;
  --state-info:        #2B5E8A;
  --state-info-bg:     #D6E4F0;

  /* ====== Pool tints (semantic; distinct from v4 names) ====== */
  --pool-internal:     #3B6B4A;
  --pool-internal-bg:  #F0F7EC;
  --pool-external:     #3E4F87;
  --pool-external-bg:  #EDF0F9;
  --pool-archive:      #6A5784;
  --pool-archive-bg:   #F1ECF6;

  /* ====== Stage (slide canvas brand: navy + gold) ======
   * The dark "stage" is a brand surface, not a theme surface; the canvas
   * stays navy in both light and dark themes (broadcast slides should
   * look the same to viewers regardless of editor chrome theme).
   *
   * Canonical names (no -v4 / -navy suffix variants):
   *   --stage           primary navy canvas fill          (#0A1628)
   *   --stage-hi        raised navy (gradient top)        (#132240)
   *   --stage-lo        sunken navy (gradient bottom)     (#050B17)
   *   --stage-ink       cream text/ink on stage           (#F2ECD9)
   *   --stage-gold      brand gold                        (#FFC000)
   *   --stage-gold-dim  pressed/dimmed gold               (#BF9000)
   *   --stage-gold-hi   bright/highlight gold             (#FFD84D)
   *   --stage-bg-deep   slide-strip neutral near-black    (#1a1a1a)
   *
   * The v4 layer (`tw-design-tokens-v4.css`) declares longer-name
   * variants (--stage-v4, --stage-navy, --stage-gold-v4, etc.) that
   * resolve to the same hex values; this canonical block is the source
   * of truth for any module that loads only `tw-tokens.css`. */
  --stage:             #0A1628;
  --stage-hi:          #132240;
  --stage-lo:          #050B17;
  --stage-ink:         #F2ECD9;
  --stage-gold:        #FFC000;
  --stage-gold-dim:    #BF9000;
  --stage-gold-hi:     #FFD84D;
  --stage-bg-deep:     #1a1a1a;

  /* ====== Editor chrome (dark-on-dark UI surfaces) ======
   * Inline-style dark chrome used by overlay tools that float over the
   * stage/canvas: media browser, orphan gallery, slide sorter, canvas
   * timeline scrubber. These surfaces stay dark in both light + dark
   * themes (the host is reading them against the navy stage). M2's hex
   * sweep parked the recurring cluster as `var(--legacy, #...)` fallback
   * placeholders; this block promotes them to a real semantic family.
   *
   *   --editor-chrome-bg-base      deepest dark fill (input wells)
   *   --editor-chrome-bg-elevated  raised dark surface (canvas timeline)
   *   --editor-chrome-surface      generic dark button / control body
   *   --editor-chrome-border       hairline border on dark chrome
   *
   * Consumers wrap as `var(--editor-chrome-surface, #2b2b2b)` etc., so
   * the inlined hex remains as a fallback if the token cascade fails. */
  --editor-chrome-bg-base:     #0d0d0d;
  --editor-chrome-bg-elevated: #1a1a2e;
  --editor-chrome-surface:     #2b2b2b;
  --editor-chrome-border:      #444;

  /* ====== Skeleton loading (placeholder shimmer) ======
   * --skeleton-base      resting fill of a placeholder block
   * --skeleton-highlight the brighter band the shimmer sweep travels in
   * Consumed by the skeleton primitive in tw-state-patterns.js. Themed
   * here so the shimmer never hard-codes a hex value. */
  --skeleton-base:      #ECE4D2;
  --skeleton-highlight: #F7F1E1;

  /* ====== Type scale (Gotham) ====== */
  /* Point the semantic families at the house font variables from tw-fonts.css.
     JetBrains Mono became the house mono on 2026-07-15, but this token was never
     repointed - so every surface using --type-mono has been rendering SYSTEM
     mono, and the adoption never reached the layer surfaces actually consume.
     The literal fallbacks matter: several surfaces (bonus_review, bbz_editor,
     bank_editor_v5, admin-*) load tw-tokens.css WITHOUT tw-fonts.css and take
     their Gotham faces from the legacy tw-design-tokens-v4.css instead. var()
     resolves at use time, so load order is irrelevant, but an absent --tw-* must
     still land on the right stack. */
  --type-family-sans: var(--tw-font, 'Gotham','Inter','Söhne',-apple-system,system-ui,sans-serif);
  --type-family-mono: var(--tw-mono, 'JetBrains Mono',ui-monospace,SFMono-Regular,Menlo,Consolas,monospace);

  --type-display-large: 800 56px/64px var(--type-family-sans);
  --type-display:       700 32px/40px var(--type-family-sans);
  --type-h1:            700 24px/30px var(--type-family-sans);
  --type-h2:            700 18px/24px var(--type-family-sans);
  --type-h3:            500 14px/20px var(--type-family-sans);
  --type-body:          400 14px/20px var(--type-family-sans);
  --type-body-strong:   500 14px/20px var(--type-family-sans);
  --type-caption:       400 12px/16px var(--type-family-sans);
  --type-mono:          400 13px/18px var(--type-family-mono);

  --type-h3-tracking:   0.06em;
  --type-h3-transform:  uppercase;

  /* ====== Spacing scale ====== */
  --space-1: 4px;
  --space-2: 8px;
  --space-3: 12px;
  --space-4: 16px;
  --space-5: 24px;
  --space-6: 32px;
  --space-7: 48px;
  --space-8: 64px;

  /* ====== Radius ====== */
  --radius-xs:   3px;
  --radius-sm:   5px;
  --radius-md:   8px;
  --radius-lg:   12px;
  --radius-xl:   18px;
  --radius-pill: 999px;

  /* ====== Elevation ====== */
  --elev-0: none;
  --elev-1: 0 1px 0 rgba(28, 20, 16, 0.04);
  --elev-2: 0 1px 3px rgba(28, 20, 16, 0.06), 0 0 0 0.5px rgba(28, 20, 16, 0.04);
  --elev-3: 0 6px 20px rgba(28, 20, 16, 0.08), 0 1px 3px rgba(28, 20, 16, 0.05);
  --elev-4: 0 24px 64px rgba(28, 20, 16, 0.14), 0 2px 8px rgba(28, 20, 16, 0.06);

  /* ====== Motion ====== */
  --motion-instant:    0ms;
  --motion-quick:      120ms;
  --motion-standard:   200ms;
  --motion-deliberate: 300ms;

  --ease-standard:   cubic-bezier(0.4, 0, 0.2, 1);
  --ease-enter:      cubic-bezier(0, 0, 0.2, 1);
  --ease-exit:       cubic-bezier(0.4, 0, 1, 1);
  --ease-emphasized: cubic-bezier(0.22, 1, 0.36, 1);

  /* ====== Density (compact default) ====== */
  --row-h:        32px;
  --pad-x:        8px;
  --pad-y:        4px;
  --gap:          var(--space-2);
  --target-min:   32px;
  --input-h:      32px;

  /* ====== Z-index scale ====== */
  --z-base:    0;
  --z-raised:  1;
  --z-sticky:  10;
  --z-drawer:  100;
  --z-modal:   1000;
  --z-toast:   2000;
  --z-palette: 3000;
}

/* Comfortable density — larger row + padding for new editors */
[data-density="comfortable"] {
  --row-h:      40px;
  --pad-x:      12px;
  --pad-y:      8px;
  --gap:        var(--space-3);
  --target-min: 40px;
  --input-h:    40px;
}

/* Dark theme — explicit selector.
 *
 * Track A retune (2026-06-10, Andrew-approved W6.1): the original dark set
 * was a warm near-black (#0E0B08 family) authored before the user-facing
 * toggle existed. The approved spec is "charcoal surfaces, off-white ink,
 * the stage gold as accent" - a cool neutral #16181D family that keeps the
 * navy+gold stage brand recognisable inside the editor chrome.
 * Contrast pass (2026-06-10 follow-up, Andrew: "dark mode might be TOO
 * dark - thin lines are hard to see"). Surfaces step wider (base->raised
 * ->overlay luminance steps 1.08/1.10 -> 1.20/1.18) and the border/rule
 * family lifts to clear ~3:1 non-text contrast on the surfaces it sits
 * on. --border-subtle lands 3.9:1 on base / 3.2:1 on raised (2.7:1 on
 * overlay - use --border-strong inside overlays, 4.1:1 there).
 * Contrast (WCAG):
 *   --text-primary  #ECEDEF on #16181D  = 15.2:1  (AAA)
 *   --text-secondary #C3C7CE on #242832 =  8.7:1  (AAA)
 *   --text-tertiary #9CA3AE on #2F3440  =  4.9:1  (AA on every surface)
 *   --border-subtle #6F7685 on #16181D  =  3.9:1  (non-text)
 *   --border-strong #8B94A4 on #2F3440  =  4.1:1  (non-text)
 *   --accent-primary #FFC000 on #16181D = 10.8:1  (AAA; gold-as-text safe)
 *   --accent-contrast #16181D on #FFC000 = 10.8:1 (text on gold fills)
 */
[data-theme="dark"] {
  --surface-base:     #16181D;
  --surface-raised:   #242832;
  --surface-overlay:  #2F3440;
  --surface-sunk:     #111318;
  /* Dark counterpart of --surface-hover, derived the same way: one step
   * lighter than --surface-base #16181D, one step below --surface-raised
   * #242832, so hover still reads as "lifting". --text-primary #ECEDEF on it
   * measures 14.9:1 (AAA), --text-tertiary #9CA3AE 6.6:1 (AA). */
  --surface-hover:    #1D2028;
  --surface-scrim:    rgba(0, 0, 0, 0.6);

  --border-subtle:    #6F7685;
  --border-strong:    #8B94A4;
  --border-focus:     #4D8FF5;

  --text-primary:     #ECEDEF;
  --text-secondary:   #C3C7CE;
  --text-tertiary:    #9CA3AE;
  --text-quaternary:  #828A99;
  --text-inverse:     #16181D;
  --text-link:        #6FA6F8;
  --text-link-hover:  #8FBAFA;

  --accent-primary:         #FFC000;
  --accent-primary-pressed: #FFD84D;
  --accent-primary-bg:      #332B12;
  --accent-primary-hi:      #FFD84D;
  --accent-contrast:        #16181D;

  --mechanic-panel-bg:     #2E333F;
  --mechanic-panel-accent: #E0B321;

  /* Question-card ink picks up the off-white in dark mode so the body
   * text on --surface-raised stays legible. Stage-ink-cream /
   * stage-navy-mid / audio-trim purples are brand-stage surfaces
   * (always navy) and do NOT get a dark variant. */
  --card-ink-body:    #C3C7CE;

  --state-success:     #7DD18B;
  --state-success-bg:  #16271B;
  --state-warning:     #E8A554;
  --state-warning-bg:  #2A2113;
  --state-error:       #F08577;
  --state-error-bg:    #2B1714;
  --state-info:        #79ABF2;
  --state-info-bg:     #141C2B;

  --pool-internal:     #7DD18B;
  --pool-internal-bg:  #16271B;
  --pool-external:     #98A8E8;
  --pool-external-bg:  #181D2D;
  --pool-archive:      #B5A4E3;
  --pool-archive-bg:   #201A2E;

  --skeleton-base:      #2C313C;
  --skeleton-highlight: #3A4150;

  --elev-1: 0 1px 0 rgba(0, 0, 0, 0.3);
  --elev-2: 0 1px 3px rgba(0, 0, 0, 0.5), 0 0 0 0.5px rgba(255, 255, 255, 0.03);
  --elev-3: 0 6px 20px rgba(0, 0, 0, 0.6), 0 1px 3px rgba(0, 0, 0, 0.5);
  --elev-4: 0 24px 64px rgba(0, 0, 0, 0.8), 0 2px 8px rgba(0, 0, 0, 0.5);
}

/* System dark-mode fallback REMOVED (Track A, 2026-06-10).
 *
 * The original :root:not([data-theme]) + prefers-color-scheme block meant
 * an OS-dark user landed on a dark editor with no way back. The approved
 * W6.1 model is explicit + user-selectable: editor-suite pages set
 * data-theme from localStorage 'tw.theme' before first paint (inline FOUC
 * guard, default light); the toggle lives in the profile menu. Pages
 * without the guard now deterministically render the light set. */

/* Reduced motion — collapse motion durations to 0 */
@media (prefers-reduced-motion: reduce) {
  :root,
  [data-theme="dark"],
  [data-theme="light"] {
    --motion-quick:      0ms;
    --motion-standard:   0ms;
    --motion-deliberate: 0ms;
  }
}

/* ===================================================================
 * v4 COMPATIBILITY BLOCK (2026-07-22, editor-conversion arc step 1)
 *
 * Every legacy v4-named token the shared editor sheets/scripts consume
 * (tw-editor-shell.css, tw-editor-shell-components.css,
 * bank-editor-v5.css, tw-pull-drawer.css, tw-quiz-jump.css, plus the
 * inline styles in tw-editor-shell.js / tw-external-bank.js /
 * bank-editor-v5.js). Measured set, not guessed - see
 * _handoff/EDITOR-CONVERSION-PLAN-2026-07-20.md section 3a.
 *
 * Aliases follow the theme through their canonical token. Literal
 * values are byte-identical copies from tw-design-tokens-v4.css
 * (light + dark). When tw-design-tokens-v4.css is ALSO linked it
 * redeclares identical values, so load order is irrelevant.
 * DO NOT use these names in new code.
 * =================================================================== */
:root {
  /* Ink */
  --ink-v4:        var(--text-primary);
  --ink-soft-v4:   var(--text-secondary);
  --ink-muted-v4:  var(--text-tertiary);
  --ink-faint-v4:  #73685E;
  /* Rules */
  --rule-v4:       var(--border-subtle);
  --rule-ghost:    #F5EEDD;
  --rule-focus:    #b8af9a;
  /* Brand */
  --gold-v4:       var(--accent-primary);
  /* Paper canvas (Track A phantom-promotion value) */
  --paper-canvas:  #f4f0eb;
  /* Semantic state */
  --good-v4:       var(--state-success);
  --good-bg-v4:    var(--state-success-bg);
  --warn-v4:       var(--state-warning);
  --warn-bg-v4:    var(--state-warning-bg);
  --danger-v4:     var(--state-error);
  --danger-bg-v4:  var(--state-error-bg);
  --info-v4:       var(--state-info);
  --info-bg-v4:    var(--state-info-bg);
  /* Phantom state literals promoted (components port Stage C, 2026-07-22).
     Previously resolved only via inline fallbacks in the components sheet.
     Dark values derived from the nearest state-token dark value:
     --warn-strong <- dark --state-warning (#E8A554),
     --bad         <- dark --state-error   (#F08577),
     --good-deep   <- dark --state-success (#7DD18B). */
  --warn-strong:   #95431f;
  --bad:           #cc2222;
  --good-deep:     #2e5024;
  /* Pool import (no canonical slot yet) */
  --pool-import:      #B15C1E;
  --pool-import-bg:   #FBE8D8;
  /* Format accents (no canonical slot yet) */
  --fmt-gob:      #7A3B82;
  --fmt-gob-bg:   #EFDEF2;
  --fmt-bbt:      #0F5C7C;
  --fmt-bbt-bg:   #D7E8F0;
  --fmt-bbz:      #B2401E;
  --fmt-bbz-bg:   #FAD9CF;
  /* Type */
  --sans-v4:      var(--type-family-sans);
  --serif-v4:     var(--type-family-sans);
  --mono-v4:      var(--type-family-mono);
  /* Shape */
  --r-sm-v4:      var(--radius-sm);
  --r-md-v4:      var(--radius-md);
  --r-lg-v4:      var(--radius-lg);
  /* Shadows (warm-brown v4 tint, deliberately distinct from --elev-*) */
  --shadow-xs-v4:    0 1px 0 rgba(40,28,12,0.04);
  --shadow-sm-v4:    0 1px 3px rgba(40,28,12,0.06), 0 0 0 0.5px rgba(40,28,12,0.04);
  --shadow-md-v4:    0 6px 20px rgba(40,28,12,0.08), 0 1px 3px rgba(40,28,12,0.05);
  --shadow-lg-v4:    0 24px 64px rgba(40,28,12,0.14), 0 2px 8px rgba(40,28,12,0.06);
  --shadow-inset-v4: inset 0 1px 0 rgba(255,255,255,0.6);
  /* Motion */
  --ease-in:   cubic-bezier(0.64, 0, 0.78, 0);
  --ease-io:   cubic-bezier(0.76, 0, 0.24, 1);
  /* Stage (brand surface; does not flip in dark) */
  --stage-v4:  var(--stage);
}

[data-theme="dark"] {
  --ink-faint-v4:  #969CA7;
  --rule-ghost:    #565D6B;
  --rule-focus:    #8B94A4;
  --paper-canvas:  #111318;
  /* Phantom state literals - dark values derived from nearest state token
     (components port Stage C, 2026-07-22). */
  --warn-strong:   #E8A554;
  --bad:           #F08577;
  --good-deep:     #7DD18B;
  --pool-import:      #E8A06A;
  --pool-import-bg:   #2A1D13;
  --fmt-gob:      #C49BD8;
  --fmt-gob-bg:   #261A2B;
  --fmt-bbt:      #6FB8D8;
  --fmt-bbt-bg:   #12222B;
  --fmt-bbz:      #E89A7A;
  --fmt-bbz-bg:   #2A1812;
  --shadow-xs-v4:    0 1px 0 rgba(0,0,0,0.3);
  --shadow-sm-v4:    0 1px 3px rgba(0,0,0,0.5), 0 0 0 0.5px rgba(255,255,255,0.03);
  --shadow-md-v4:    0 6px 20px rgba(0,0,0,0.6), 0 1px 3px rgba(0,0,0,0.5);
  --shadow-lg-v4:    0 24px 64px rgba(0,0,0,0.8), 0 2px 8px rgba(0,0,0,0.5);
  --shadow-inset-v4: inset 0 1px 0 rgba(255,255,255,0.05);
}

/* ===================================================================
 * Utility classes — semantic type roles + focus ring
 * Components should prefer the var() form; these utilities exist for
 * one-off applications and template authoring.
 * =================================================================== */

.tw-display-large { font: var(--type-display-large); color: var(--text-primary); }
.tw-display       { font: var(--type-display);       color: var(--text-primary); }
.tw-h1            { font: var(--type-h1);            color: var(--text-primary); }
.tw-h2            { font: var(--type-h2);            color: var(--text-primary); }
.tw-h3 {
  font: var(--type-h3);
  color: var(--text-secondary);
  letter-spacing: var(--type-h3-tracking);
  text-transform: var(--type-h3-transform);
}
.tw-body          { font: var(--type-body);          color: var(--text-primary); }
.tw-body-strong   { font: var(--type-body-strong);   color: var(--text-primary); }
.tw-caption       { font: var(--type-caption);       color: var(--text-tertiary); }
.tw-mono          { font: var(--type-mono);          color: var(--text-secondary); }

/* Consistent focus ring across all interactive elements that opt in */
.tw-focus-ring:focus-visible,
[data-tw-focus]:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
  border-radius: inherit;
}

/* ===================================================================
 * B5 (Arc B 2026-05-19): Global :focus-visible ring
 *
 * Every focusable element gets a visible focus ring when reached via
 * keyboard. The opt-in selectors above stay so authors can attach the
 * exact same ring via class. This block is the global safety net.
 *
 * We intentionally target :focus-visible (NOT :focus) so the ring only
 * appears for keyboard navigation; mouse-click focus stays clean. Modern
 * browsers (>= 2020) ship :focus-visible; legacy fallback is a graceful
 * no-op.
 *
 * Elements with their own focus styling (e.g. the auth-input :focus
 * box-shadow on login.html) are unaffected because we don't remove
 * those — we only ADD the outline. If a surface explicitly suppresses
 * outline:none, it must also provide a :focus-visible alternative.
 * =================================================================== */
:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
}
/* Override stray `outline: none` that some legacy CSS files apply for
 * mouse-focus polish. We bring the keyboard ring back specifically. */
button:focus-visible,
a:focus-visible,
[role="button"]:focus-visible,
input:focus-visible,
textarea:focus-visible,
select:focus-visible,
[tabindex]:focus-visible {
  outline: 2px solid var(--border-focus);
  outline-offset: 2px;
}

/* Visually-hidden but screen-reader-accessible (ARIA labels, sr-only) */
.tw-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
