/* lib/shared/tokens.css — shared design tokens (Phase 1a, reference-layer step 1).
 * Loaded by a plain <link> in all three app shells; served statically from /lib/
 * (no _redirects wildcard touches it). CSS custom properties resolve live in both
 * stylesheet rules and JS inline styles, so a deleted/renamed token degrades to the
 * browser default rather than white-screening the app (unlike a deleted JS const).
 *
 * Fonts only for now. Radii (4/6/8 head), touch floors (44px), and the semantic
 * accent palette are deliberately NOT defined yet — they get added in 1b when the
 * shared primitives actually consume them. Defining unused vars now is dead CSS.
 * The fallbacks match the shells' own @font-face / Google-Fonts declarations.
 */
:root {
  --font-display: 'Orbitron', sans-serif;        /* headings, display, numerics-as-label */
  --font-body:    'Rajdhani', sans-serif;         /* body / UI text */
  --font-mono:    'Share Tech Mono', monospace;   /* data, stats, monospaced numerics */
}

/* F-52 fluid type scale (ruling responsive-type-scale-concept). Each step is a clamp()
 * anchored MIN@375px -> MAX@1920px; the PREFERRED term mixes rem + vw so every size keeps a
 * rem component — it responds to browser zoom / OS font-size (WCAG 1.4.4 Resize Text, R-F52-1).
 * The 16px (1rem) floor lives on --fs-body and up: READING text + inputs use --fs-body (kills
 * iOS <16px input auto-zoom, R-F52-2). Dense UI labels/stats/chips use the sub-body steps
 * (they scale up with the screen but stay compact — the cockpit's no-scroll law, R-F52-3).
 * Migrate inline `fontSize: N` -> `fontSize: 'var(--fs-*)'` mechanically (R-F52-4). */
:root {
  --fs-2xs:  clamp(0.6250rem, 0.5947rem + 0.129vw, 0.7500rem);  /* micro labels / badges (~10->12) */
  --fs-xs:   clamp(0.7188rem, 0.6808rem + 0.162vw, 0.8750rem);  /* small mono labels, pills (~11.5->14) */
  --fs-sm:   clamp(0.8125rem, 0.7670rem + 0.194vw, 1.0000rem);  /* dense item names, chips (~13->16) */
  --fs-body: clamp(1.0000rem, 0.9545rem + 0.194vw, 1.1875rem);  /* READING text + inputs — 16px FLOOR (~16->19) */
  --fs-lg:   clamp(1.0938rem, 1.0255rem + 0.291vw, 1.3750rem);  /* subheads / stat values (~17.5->22) */
  --fs-xl:   clamp(1.2500rem, 1.1590rem + 0.388vw, 1.6250rem);  /* big stat numerals (~20->26) */
  --fs-2xl:  clamp(1.5625rem, 1.4260rem + 0.583vw, 2.1250rem);  /* display (~25->34) */
  --fs-3xl:  clamp(1.9375rem, 1.7100rem + 0.971vw, 2.8750rem);  /* hero display (~31->46) */
}

/* R-F52-2: form controls never below 16px (kills iOS <16px auto-zoom). Base floor for any input
 * without an inline size; inline-styled inputs are additionally floored to var(--fs-body) at their
 * call sites. */
input, textarea, select { font-size: var(--fs-body); }

/* Text-colour tokens (NPC gold-standard Lap A, A1 — WCAG AA against the near-black bg #02010a).
 * Discharges the Phase-1 R-UI-A2 rider: colour tokens land here in the CSS layer, consumed via
 * var(), never hardcoded in JS. Contrast verified (scripts/check_text_contrast.py):
 *   primary 18.6:1, muted 7.4:1 (AA-normal 4.5:1), faint 4.2:1 (AA-large 3:1, decorative/large only). */
:root {
  --text-primary: #f2f2f2;   /* body / headings */
  --text-muted:   #9a9a9a;   /* secondary labels — AA-normal (>=4.5:1) */
  --text-faint:   #707070;   /* large/decorative only — AA-large (>=3:1) */
}

/* F-52 Amendment 2 (R-F52-5): semantic TEXT colours, each contrast-verified as SMALL text
 * (>=4.5:1, WCAG 1.4.3) against the near-black base #02010a AND the app's white-tinted panels
 * (checked to #222228, the lightest small-text panel) via scripts/check_text_contrast.py.
 * Colour-temperature rule: these are the HIGH-LUMINANCE tints used for TEXT; saturated mid-tones
 * (e.g. raw #ff3b3b) stay on FILLS/BORDERS/LARGE numerals only, never small body text. Migrate
 * inline hex -> these tokens (same mechanic as the type tokens, R-F52-4). */
:root {
  --text-success: #4ecb71;   /* success / positive (green)   ~7.6:1 min */
  --text-danger:  #ff6b6b;   /* danger / critical (red)      ~5.0:1 min */
  --text-warning: #ff8a3d;   /* warning (orange)             ~6.7:1 min */
  --text-caution: #ffc857;   /* caution / attention (gold)   ~10:1 min */
  --text-info:    #7ec4ff;   /* info / neutral-accent (sky)  ~8.9:1 min */
}

/* Premium chrome depth — the launchpad card's layered shadow (one light source, top).
 * Consumed by the shared HubCard (lib/shared/ui.jsx); reusable by any premium surface. */
:root {
  --depth-card: 0 1px 0 rgba(255,255,255,.07) inset, 0 10px 24px rgba(0,0,0,.40);
}

/* Desktop-curation layout tokens (F-46, contract G10; ruling desktop-curation-program §3 + A1).
 * Consumed by the desktop layout primitives (lib/shared/desktopLayout.jsx) and each D-lap's
 * adopted surfaces. Inert on mobile — nothing references them below the 1024px desktop threshold. */
:root {
  --desk-rail-w:      320px;   /* master-detail rail width */
  --desk-read-max:    720px;   /* reading / wizard column — STRUCTURED content (cards, forms, trees) */
  --desk-prose-max:   66ch;    /* free-flowing paragraph cap (A1: 66 cpl ideal; caps prose inside a 720 col) */
  --desk-content-max: 1280px;  /* widest any desktop content region may grow */
  --desk-gutter:      24px;    /* grid / pane gutter */
}

/* HubCard entrance — cards cascade in (compositor: opacity + transform). Per-card stagger via inline
 * animation-delay. fill=backwards: holds the hidden state through the delay, then reverts to the
 * element's own style (so HubCard's inline press-transform still works after). Static for reduced-motion. */
@keyframes hubcard-rise { from { opacity: 0; transform: translateY(12px); } to { opacity: 1; transform: none; } }
.hubcard-enter { animation: hubcard-rise .45s cubic-bezier(.2,.7,.3,1) backwards; }
@media (prefers-reduced-motion: reduce) { .hubcard-enter { animation: none; } }

/* THE PANEL MOTION LAW (ruling panel-premium-presentation-concept §3): panels + menus enter with ONE
   fast fade (0.2–0.25s), optional +4–6px rise — NO slides, NO springs, NO stagger. Premium is the
   composed still FRAME, not the transition. Reduced-motion → instant. */
@keyframes panel-fade { from { opacity: 0; transform: translateY(5px); } to { opacity: 1; transform: none; } }
.panel-enter { animation: panel-fade .22s ease forwards; }
@media (prefers-reduced-motion: reduce) { .panel-enter { animation: none; } }

/* SuiteBar skip-to-content (navigation Phase 1, WCAG 2.4.1) — off-screen until keyboard-focused.
 * The dice cinematic hide reuses the shared .autohide-chrome convention (defined below). */
.suite-skip { position: absolute; left: 8px; top: -56px; z-index: 1000; background: #0b0f17; color: #fff;
  padding: 9px 14px; border-radius: 8px; font-family: var(--font-mono); font-size: 12px; letter-spacing: .1em;
  text-decoration: none; transition: top .15s ease; }
.suite-skip:focus { top: 8px; outline: 2px solid #4EAAFF; outline-offset: 2px; }

/* Immersive-hide (navigation Phase 2b) — on the immersive modules an app sets body.holo-immersive and
 * BOTH bars (suite + bottom) hide for full focus. The Player/GM bars are flex children, so they must FREE
 * their layout space (display:none) — a transform alone leaves the bar's space and crops the module's
 * bottom content (e.g. the wizard's NEXT/talent tree). The dice cinematic reuses the shared .autohide-chrome
 * (those bars are fixed/out-of-flow, so they fade via opacity without cropping). */
body.holo-immersive .suite-bar,
body.holo-immersive .bottom-nav { display: none !important; }   /* !important — beats the components' inline display:flex */

/* Auto-hide chrome (SHARED — extracted from dice/index.html for the IA lap, ONE home). Elements with
 * class `.autohide-chrome` fade out when the app sets body.chrome-hidden (via SWRPGUI.useAutoHideChrome
 * on idle). Fade-out 250ms (ambient), fade-in 175ms (faster on interaction). Reduced-motion / a pin key
 * disable the toggle upstream (chrome stays at opacity 1). Used by the dice cinematic AND the bars-resident
 * NPC library. `.dice-autohide` is retired — renamed app-neutral. */
.autohide-chrome { transition: opacity 175ms ease-in; }
body.chrome-hidden .autohide-chrome { opacity: 0; pointer-events: none; transition: opacity 250ms ease-out; }

/* slideUpBounce — bottom-sheet entrance (ITM-V1 store overlay/pickers; also referenced by the
 * player saved-toast since UXQP — the keyframe never existed until now, so that animation was a
 * silent no-op). Slight overshoot, settles fast. */
@keyframes slideUpBounce {
  0%   { transform: translateY(26px); opacity: 0; }
  70%  { transform: translateY(-3px); opacity: 1; }
  100% { transform: translateY(0); opacity: 1; }
}

/* F-46 dc3 v3 — the OVERVIEW desktop COCKPIT responsive grid (ruling dc3-v3-cockpit-concept §4.3).
   The PAGE never scrolls at any desktop width ≥1024; zones scroll internally. The sheet sits in the
   D2 detail pane (viewport − rail), so breakpoints key on viewport with the ~320px rail accounted for.
   ≥1280: three columns (STATE · SKILLS · WEAPONS+TALENTS). <1280: two columns — STATE spans both rows,
   SKILLS top, WEAPONS+TALENTS bottom (each with its own internal overflow). */
.dc3-cockpit { display:grid; gap:14px; height:100%; padding:14px 20px 16px; overflow:hidden; box-sizing:border-box;
  grid-template-columns: 300px minmax(0,1fr) 348px; grid-template-areas: "state skills right"; }
.dc3-a-state { grid-area:state; } .dc3-a-skills { grid-area:skills; } .dc3-a-right { grid-area:right; }
@media (max-width:1360px){ .dc3-cockpit { grid-template-columns: 284px minmax(0,1fr) 312px; } }
@media (max-width:1279px){ .dc3-cockpit {
  grid-template-columns: 264px minmax(0,1fr); grid-template-rows: minmax(0,1fr) minmax(0,1fr);
  grid-template-areas: "state skills" "state right"; } }
/* LOADOUT desktop tab (dc3 sheet-tabs): three no-scroll columns — WEAPONS | ARMOR+GEAR | CREDITS+ENC.
 * Reflows 3→2→stack; each variable zone (.lo-zone) scrolls internally with a fade edge (run-off = opt 1). */
.loadout-grid { display:grid; gap:14px; height:100%; overflow:hidden; box-sizing:border-box;
  grid-template-columns: minmax(0,1.15fr) minmax(0,1fr) 300px; }
@media (max-width:1360px){ .loadout-grid { grid-template-columns: minmax(0,1.1fr) minmax(0,1fr) 272px; } }
@media (max-width:1120px){ .loadout-grid { grid-template-columns: minmax(0,1fr) 264px; } .loadout-grid > .lo-c3 { grid-column: 1 / -1; } }
.lo-col { display:flex; flex-direction:column; gap:14px; min-height:0; }
.lo-zone { position:relative; flex:1 1 auto; min-height:0; display:flex; flex-direction:column; }
.lo-zone > .lo-scroll { flex:1 1 auto; min-height:0; overflow-y:auto; padding-bottom:8px; }
.lo-zone::after { content:""; position:absolute; left:0; right:6px; bottom:0; height:30px; pointer-events:none;
  background:linear-gradient(180deg,transparent,rgba(3,4,8,.92)); }

/* FORCE desktop tab (dc3 sheet-tabs, ruling desktop-sheet-tabs-brief §5; master-detail per Jason
 * 2026-07-20): 2-col no-scroll — FORCE POWERS list (master, left) | RIGHT rail = Force Rating status
 * + the selected power's TREE rendered inline below it. Reuses .lo-col/.lo-zone/.lo-scroll; the powers
 * zone AND the tree zone scroll internally, the page never scrolls. Rail is 400px so the 4-col tree
 * grid (~372px) fits without horizontal scroll. Session-only commit (§6, no schema). */
.force-grid { display:grid; gap:14px; height:100%; overflow:hidden; box-sizing:border-box;
  grid-template-columns: minmax(0,1fr) 400px; }
@media (max-width:1200px){ .force-grid { grid-template-columns: minmax(0,1fr) 360px; } }
/* the inline-tree detail zone: a framed, clipped, positioned box the tree's absolute panel fills */
.fp-tree-zone { position:relative; flex:1 1 auto; min-height:0; overflow:hidden; border-radius:13px;
  border:1px solid rgba(255,255,255,.10); box-shadow:0 1px 0 rgba(255,255,255,.03) inset,0 10px 26px rgba(0,0,0,.22); }
.fp-tree-empty { flex:1 1 auto; min-height:0; display:flex; flex-direction:column; }

/* ADVANCEMENT desktop tab (dc3 sheet-tabs, clearance sheet-advancement-mockup-clearance §4.2): the growth
 * workshop — SKILLS+FORCE (left) | TALENT trees (center) | DETAILS+SPECIALIZATIONS (right). XP hero strip
 * sits above (in flow). Reflows 3→2→stack; each .lo-zone scrolls internally, the page never scrolls (§4.6).
 * Bands cover real tablets (ruling advancement-ipad-band-triage §3/C2): 3-col ≥1280 (center keeps ≥~620px
 * so the tree renders 1:1 above the legibility floor); 2-col TABLET band 1024–1279 (an 11" iPad ls, 1194,
 * lands here, not the squeezed 3-col that made the micro-tree); 12.9" iPad ls (1366) gets the tightened
 * 3-col (≤1440), never the full desktop grid. Session-only commit (no schema). */
.adv-workshop { flex:1 1 auto; display:grid; gap:14px; min-height:0; overflow:hidden; box-sizing:border-box;
  grid-template-columns: 292px minmax(0,1fr) 300px; }
@media (max-width:1440px){ .adv-workshop { grid-template-columns: 272px minmax(0,1fr) 284px; } }
@media (max-width:1279px){ .adv-workshop { grid-template-columns: minmax(0,1fr) 280px;
  grid-template-rows: minmax(0,1fr) minmax(0,1.7fr); }   /* SKILLS compact on top; the TALENT tree (hero) gets the larger share of the left column so it scrolls less on short tablet heights */
  .adv-workshop > .adv-c3 { grid-row: 1 / -1; grid-column: 2; } }
@media (min-width:1600px){ .adv-workshop { grid-template-columns: 340px minmax(0,1fr) 340px; } }

/* PROFILE desktop dossier (ruling profile-desktop §3A/§3B): pinned identity RAIL | internal-scroll
 * READING column. The page is fixed (no scroll 1024→1920); the reading column + the rail's lower zone
 * scroll internally. Reflows the rail narrower on tight widths, wider at 1600+. Layout only, no schema. */
.profile-dossier { grid-template-columns: 360px minmax(0,1fr); }
@media (max-width:1180px){ .profile-dossier { grid-template-columns: 300px minmax(0,1fr); } }
@media (min-width:1600px){ .profile-dossier { grid-template-columns: 400px minmax(0,1fr); } }

/* Skills grid: 2-col by default. F-52 §3d — on wide screens fill the lifted width by DENSITY, not
 * stretch: side panels widen + skills goes 3-col so rows don't disperse into voids at 1600+. */
.dc3-skill-grid { grid-template-columns: minmax(0,1fr) minmax(0,1fr); }
@media (min-width:1600px){
  .dc3-cockpit { grid-template-columns: 344px minmax(0,1fr) 400px; }
  .dc3-skill-grid { grid-template-columns: minmax(0,1fr) minmax(0,1fr) minmax(0,1fr); }
}
