/* Tong Shang Academy — tokens + foundation layer.
   Source of truth: root DESIGN.md ("Lantern Hall"). This file is the ONLY place
   raw values live; every other stylesheet consumes the variables. */

/* == 0. Self-hosted faces (latin subset) ========================= */
/* Vendored woff2 in static/fonts/ so the app never pulls a CDN font
   (brittle-connection rule). Spectral = editorial hall voice; Hanken Grotesk
   = interface; IBM Plex Mono = codes and figures. */
@font-face{font-family:'Hanken Grotesk';font-style:normal;font-weight:400;font-display:swap;src:url('../fonts/hanken-400.woff2') format('woff2');}
@font-face{font-family:'Hanken Grotesk';font-style:normal;font-weight:500;font-display:swap;src:url('../fonts/hanken-500.woff2') format('woff2');}
@font-face{font-family:'Hanken Grotesk';font-style:normal;font-weight:700;font-display:swap;src:url('../fonts/hanken-700.woff2') format('woff2');}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:400;font-display:swap;src:url('../fonts/plexmono-400.woff2') format('woff2');}
@font-face{font-family:'IBM Plex Mono';font-style:normal;font-weight:600;font-display:swap;src:url('../fonts/plexmono-600.woff2') format('woff2');}
@font-face{font-family:'Spectral';font-style:italic;font-weight:500;font-display:swap;src:url('../fonts/spectral-500-italic.woff2') format('woff2');}
@font-face{font-family:'Spectral';font-style:normal;font-weight:400;font-display:swap;src:url('../fonts/spectral-400.woff2') format('woff2');}
@font-face{font-family:'Spectral';font-style:normal;font-weight:500;font-display:swap;src:url('../fonts/spectral-500.woff2') format('woff2');}
@font-face{font-family:'Spectral';font-style:normal;font-weight:600;font-display:swap;src:url('../fonts/spectral-600.woff2') format('woff2');}

/* == 1. Palette (theme-invariant raw tokens) ===================== */
:root {
  --ink-950: #051322;
  --navy-900: #071A2E;
  --navy-800: #0B2742;
  --navy-700: #123654;
  --gold-500: #C7A15A;
  --gold-300: #E0C893;
  --bronze-700: #4B3824;
  --bronze-500: #8A7350;
  --paper-50: #F6F1E6;   /* warm lit sheet */
  --paper-100: #EFE8D6;  /* warm page ground */
  --slate-900: #14202B;
  --slate-600: #5D6872;
  --line-light: #DED8CC;
  --line-dark: #243A50;

  --celadon: #2F6E61;    --celadon-bg: #DCEBE5;
  --persimmon: #944D14;  --persimmon-bg: #F4E3CD;
  --plum: #70556F;       --plum-bg: #EEE6EE;
  --porcelain: #2F658A;  --porcelain-bg: #DCE9F1;
  --risk: #B54747;       --risk-bg: #F1DEDA;
  --success: #26745A;
  --warning: #A36A18;

  --ivory: #F3EFE6;
  --ivory-soft: #AEB9C4; /* secondary text on navy/ink */

  /* Type — self-hosted first, system faces as graceful fallback */
  --font-ui: "Hanken Grotesk", "Avenir Next", Aptos, "Segoe UI", system-ui, sans-serif;
  --font-editorial: "Spectral", "Iowan Old Style", Baskerville, Georgia, serif;
  --font-mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Rhythm (4px base) */
  --s-1: 4px; --s-2: 8px; --s-3: 12px; --s-4: 16px;
  --s-5: 24px; --s-6: 32px; --s-7: 48px; --s-8: 64px;
  --r-card: 14px;
  --r-ctl: 9px;
  --t-fast: 180ms;
  --t-screen: 240ms;
  --measure-lesson: 720px;     /* reading column (lessons, focused forms) */
  --measure-page: 1200px;      /* desktop-first dashboard width */
  --measure-console: 1600px;
  --tap-min: 44px;
  --tap-answer: 48px;

  /* Sheets are lit paper "pages" in BOTH themes */
  --sheet: var(--paper-50);
  --sheet-text: var(--slate-900);
  --sheet-soft: var(--slate-600);
  --sheet-line: var(--line-light);

  /* Header is the navy brand shell in BOTH themes */
  --header-bg: var(--navy-900);
  --header-text: var(--ivory);
  --header-soft: var(--ivory-soft);
}

/* == 2. Themes =================================================== */
:root,
:root[data-theme="dark"] {
  --bg: var(--ink-950);
  --surface: var(--navy-800);
  --surface-hover: var(--navy-700);
  --text: var(--ivory);
  --text-soft: var(--ivory-soft);
  --line: var(--line-dark);
  --shadow-card: 0 0 0 1px var(--line-dark);
  --focus-ring: var(--gold-500);
  color-scheme: dark;
}

:root[data-theme="light"] {
  --bg: var(--paper-100);
  --surface: var(--paper-50);
  --surface-hover: #F6F1E7;
  --text: var(--slate-900);
  --text-soft: var(--slate-600);
  --line: var(--line-light);
  --shadow-card: 0 1px 3px rgba(20, 32, 43, 0.08), 0 1px 2px rgba(20, 32, 43, 0.05);
  --focus-ring: var(--porcelain);
  color-scheme: light;
}

/* == 3. Base ===================================================== */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font-ui);
  font-size: 16px;
  line-height: 1.55;
  min-height: 100dvh;
}
h1, h2, h3, h4 { margin: 0 0 var(--s-3); line-height: 1.2; }
p { margin: 0 0 var(--s-3); }
a { color: inherit; }
img { max-width: 100%; }
button { font: inherit; }

.editorial { font-family: var(--font-editorial); font-weight: 500; letter-spacing: 0.01em; }
.mono { font-family: var(--font-mono); }
.tnum { font-variant-numeric: tabular-nums; }

:focus-visible {
  outline: 2px solid var(--focus-ring);
  outline-offset: 2px;
  border-radius: var(--r-ctl);
}

.icon {
  width: 20px; height: 20px;
  vertical-align: -4px;
  flex: none;
}
.icon-missing { display: inline-block; width: 20px; height: 20px; border-radius: 50%;
  border: 1.5px solid currentColor; opacity: 0.35; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation: none !important; transition: none !important; }
}

/* == 4. Header =================================================== */
.site-header {
  background: var(--header-bg);
  color: var(--header-text);
  padding: calc(env(safe-area-inset-top) + var(--s-3)) var(--s-4) var(--s-3);
  display: flex; align-items: center; gap: var(--s-3);
  border-bottom: 1px solid var(--line-dark);
}
.brand-lockup {
  display: flex; align-items: center; gap: var(--s-3);
  text-decoration: none; color: var(--header-text);
  min-height: var(--tap-min);
}
.brand-lockup img { width: 34px; height: 34px; }
.brand-name { font-family: var(--font-editorial); font-size: 18px; line-height: 1.15; }
@media (max-width: 480px) {
  .site-header { gap: var(--s-2); }
  .brand-name { font-size: 16px; }
}
.brand-name b { color: var(--gold-300); font-weight: 600; }
.header-tools { margin-left: auto; display: flex; align-items: center; gap: var(--s-2); }

.demo-chip {
  display: inline-flex; align-items: center; gap: 6px;
  height: 32px; padding: 0 12px; border-radius: 8px;
  border: 1px solid color-mix(in srgb, var(--gold-500) 45%, transparent);
  color: var(--gold-300); background: color-mix(in srgb, var(--gold-500) 10%, transparent);
  font-size: 13px; font-weight: 600; text-decoration: none;
  transition: background var(--t-fast);
}
.demo-chip:hover { background: color-mix(in srgb, var(--gold-500) 18%, transparent); }
.demo-chip .icon { width: 15px; height: 15px; }
@media (max-width: 480px) { .demo-chip span { display: none; } }

.conn { display: flex; align-items: center; gap: 6px; color: var(--header-soft); font-size: 13px; }
#conn-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--success); }
#conn-dot[data-state="live"] { background: var(--success); }
#conn-dot[data-state="reconnecting"] { background: var(--warning); }
#conn-dot[data-state="offline"] { background: var(--risk); }

.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: var(--tap-min); height: var(--tap-min);
  background: none; border: none; color: var(--header-text);
  border-radius: var(--r-ctl); cursor: pointer;
  transition: background var(--t-fast);
}
.icon-btn:hover { background: var(--navy-700); }
html[data-theme="light"] .icon-sun { display: none; }
html[data-theme="dark"] .icon-moon { display: none; }
@media (min-width: 960px) { .menu-btn { display: none; } }

/* == 5. Layout shells (desktop-first) ============================ */
/* The page fills a wide desktop measure by default; dashboards lay out into
   real multi-column grids at this width. Reading/focused surfaces (lessons,
   the review player, sign-in forms) opt into the narrower reading measure with
   `.is-reading`. Mobile keeps the same content in a single column (full parity). */
.page {
  padding: var(--s-6) var(--s-6) calc(var(--s-8) + env(safe-area-inset-bottom));
  max-width: var(--measure-page);
  margin: 0 auto;
  width: 100%;
}
.page.is-reading { max-width: var(--measure-lesson); }
.page-wide { max-width: var(--measure-console); }
@media (max-width: 720px) {
  .page { padding: var(--s-5) var(--s-4) calc(var(--s-8) + env(safe-area-inset-bottom)); }
}

/* Student bottom tab bar (mobile-first) */
.tabbar {
  position: fixed; inset: auto 0 0 0; z-index: 40;
  display: flex;
  background: var(--header-bg);
  border-top: 1px solid var(--line-dark);
  padding-bottom: env(safe-area-inset-bottom);
}
.tabbar a {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: var(--s-2) 0 var(--s-2);
  min-height: 60px;
  color: var(--header-soft); text-decoration: none; font-size: 13px;
  border-top: 2px solid transparent;
  transition: color var(--t-fast);
}
.tabbar a.active { color: var(--gold-300); border-top-color: var(--gold-500); }
.tabbar a .icon { width: 22px; height: 22px; }

@media (min-width: 768px) {
  .tabbar { display: none; }
  .header-nav { display: flex !important; }
}
.header-nav { display: none; gap: var(--s-1); margin-left: var(--s-5); }
.header-nav a {
  display: flex; align-items: center; gap: var(--s-2);
  padding: var(--s-2) var(--s-3); min-height: var(--tap-min);
  color: var(--header-soft); text-decoration: none; font-size: 14px;
  border-bottom: 2px solid transparent; border-radius: var(--r-ctl) var(--r-ctl) 0 0;
  transition: color var(--t-fast);
}
.header-nav a.active { color: var(--header-text); border-bottom-color: var(--gold-500); }
.header-nav a:hover { color: var(--header-text); }

/* Admin sidebar / drawer */
.admin-layout { display: flex; min-height: calc(100dvh - 61px); }
.admin-sidebar {
  width: 248px; flex: none;
  background: var(--navy-900); color: var(--ivory);
  padding: var(--s-4) var(--s-3);
  display: none; flex-direction: column; gap: var(--s-1);
}
.admin-sidebar a {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3); min-height: var(--tap-min);
  color: var(--ivory-soft); text-decoration: none; font-size: 14px;
  border-left: 2px solid transparent; border-radius: 0 var(--r-ctl) var(--r-ctl) 0;
  transition: background var(--t-fast), color var(--t-fast);
}
.admin-sidebar a.active {
  color: var(--ivory); border-left-color: var(--gold-500); background: var(--navy-800);
}
.admin-sidebar a:hover { color: var(--ivory); background: var(--navy-800); }
.admin-main { flex: 1; min-width: 0; }
@media (min-width: 960px) {
  .admin-sidebar { display: flex; }
}
.admin-bottombar {
  position: fixed; inset: auto 0 0 0; z-index: 40;
  display: flex; background: var(--header-bg);
  border-top: 1px solid var(--line-dark);
  padding-bottom: env(safe-area-inset-bottom);
}
.admin-bottombar a {
  flex: 1; display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: var(--s-2) 0; min-height: 60px;
  color: var(--header-soft); text-decoration: none; font-size: 13px;
  border-top: 2px solid transparent;
}
.admin-bottombar a.active { color: var(--gold-300); border-top-color: var(--gold-500); }
.admin-bottombar a .icon { width: 22px; height: 22px; }
@media (min-width: 960px) { .admin-bottombar { display: none; } }

.drawer-scrim {
  position: fixed; inset: 0; z-index: 49; background: rgba(5, 19, 34, 0.55);
  opacity: 0; pointer-events: none; transition: opacity var(--t-screen);
}
.drawer {
  position: fixed; top: 0; bottom: 0; left: 0; z-index: 50; width: 272px;
  background: var(--navy-900); color: var(--ivory);
  padding: calc(env(safe-area-inset-top) + var(--s-4)) var(--s-3) var(--s-4);
  transform: translateX(-100%); transition: transform var(--t-screen);
  display: flex; flex-direction: column; gap: var(--s-1);
  overflow-y: auto;
}
[data-drawer="open"] .drawer { transform: translateX(0); }
[data-drawer="open"] .drawer-scrim { opacity: 1; pointer-events: auto; }
.drawer a {
  display: flex; align-items: center; gap: var(--s-3);
  padding: var(--s-3); min-height: var(--tap-min);
  color: var(--ivory-soft); text-decoration: none; font-size: 15px;
  border-left: 2px solid transparent;
}
.drawer a.active { color: var(--ivory); border-left-color: var(--gold-500); }

/* == 6. Surfaces and cards ======================================= */
.card {
  background: var(--surface);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
  padding: var(--s-4);
}
.sheet {
  background: var(--sheet);
  color: var(--sheet-text);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
  padding: var(--s-5) var(--s-4);
}
.sheet .soft, .sheet small { color: var(--sheet-soft); }
.divider { border: 0; border-top: 1px solid var(--line); margin: var(--s-5) 0; }

.empty-state {
  display: flex; flex-direction: column; align-items: center; gap: var(--s-2);
  padding: var(--s-6) var(--s-4);
  color: var(--text-soft);
  text-align: center;
}
.empty-state .icon { width: 28px; height: 28px; opacity: 0.6; }

/* == 6b. Dundal co-brand footer (V2_SPEC.md §7) =================== */
/* Quiet attribution footer for student shell + welcome. Consumes existing
   tokens only (--text-soft, --s-*, --line) -- no new palette values. */
.dundal-cobrand {
  display: flex; align-items: center; justify-content: center; gap: var(--s-2);
  padding: var(--s-5) var(--s-4) var(--s-4);
  color: var(--text-soft);
  font-size: 12px;
}
/* On the student shell, this footer sits in normal flow AFTER the fixed
   bottom tab bar (which occupies no flow space) -- give it the same
   clearance .page already reserves for its own content, or the tabbar would
   overlap it when scrolled to the very bottom. Desktop hides the tab bar at
   the same 768px breakpoint tokens.css already uses, so the extra space is
   dropped there too. */
.student-shell-foot.dundal-cobrand {
  padding-bottom: calc(60px + var(--s-4) + env(safe-area-inset-bottom));
}
@media (min-width: 768px) {
  .student-shell-foot.dundal-cobrand { padding-bottom: calc(var(--s-4) + env(safe-area-inset-bottom)); }
}
.dundal-logo { max-height: 20px; width: auto; display: block; }
.dundal-logo-dark { display: none; }
:root[data-theme="dark"] .dundal-logo-dark { display: block; }
:root[data-theme="dark"] .dundal-logo-light { display: none; }

/* == 7. Controls ================================================= */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--s-2);
  min-height: var(--tap-min);
  padding: var(--s-2) var(--s-5);
  border-radius: var(--r-ctl);
  border: 1px solid transparent;
  font-size: 15px; font-weight: 600; font-family: var(--font-ui);
  text-decoration: none; cursor: pointer;
  transition: background var(--t-fast), border-color var(--t-fast), transform var(--t-fast);
  width: 100%;
}
@media (min-width: 768px) { .btn { width: auto; } }
.btn-primary { background: var(--gold-500); color: var(--slate-900); }
.btn-primary:hover { background: var(--gold-300); }
.btn-ghost { background: transparent; color: var(--text); border-color: var(--line); }
.btn-ghost:hover { border-color: var(--text-soft); }
.btn-navy { background: var(--navy-700); color: var(--ivory); }
.btn-navy:hover { background: var(--navy-800); }

label { display: block; font-size: 14px; font-weight: 600; margin-bottom: var(--s-1); }
.field { margin-bottom: var(--s-4); }
input[type="text"], input[type="password"], textarea, select {
  width: 100%;
  min-height: var(--tap-min);
  padding: var(--s-2) var(--s-3);
  font-size: 16px; font-family: var(--font-ui);
  color: var(--sheet-text);
  background: var(--sheet);
  border: 1px solid var(--sheet-line);
  border-radius: var(--r-ctl);
}
input::placeholder, textarea::placeholder { color: var(--sheet-soft); }
.hint { font-size: 13px; color: var(--text-soft); margin-top: var(--s-1); }

/* V2_SPEC.md §1: friendly WORD-WORD codes (up to "LANTERN-KAIRABA", 15 chars)
   replaced the old short alphanumeric code, so the fixed 26px/0.35em set for
   a 6-character code is replaced with a fluid size that never drops below
   the 16px mobile-input floor or overflows a 375px viewport. */
/* input.code-input: matches the specificity of the base input[type=text] rule
   above and comes later in the cascade, so the mono face actually applies. */
input.code-input, .code-input {
  font-family: var(--font-mono);
  font-size: clamp(16px, 5.6vw, 24px); letter-spacing: 0.1em; text-transform: uppercase;
  text-align: center;
}

/* Role picker: bounded cards, ≥48px, no capsules */
.role-grid { display: grid; grid-template-columns: 1fr; gap: var(--s-2); }
@media (min-width: 560px) { .role-grid { grid-template-columns: 1fr 1fr; } }
.role-option { position: relative; }
.role-option input { position: absolute; opacity: 0; inset: 0; }
.role-option .role-card {
  display: flex; align-items: center; gap: var(--s-3);
  min-height: var(--tap-answer);
  padding: var(--s-3);
  background: var(--sheet); color: var(--sheet-text);
  border: 1px solid var(--sheet-line); border-radius: var(--r-ctl);
  cursor: pointer;
  transition: border-color var(--t-fast), background var(--t-fast);
}
.role-option .role-card .icon { color: var(--porcelain); width: 22px; height: 22px; }
.role-option .role-card small { display: block; color: var(--sheet-soft); font-size: 12.5px; line-height: 1.35; }
.role-option input:checked + .role-card {
  border-color: var(--gold-500);
  box-shadow: inset 2px 0 0 var(--gold-500);
  background: var(--paper-100);
}
.role-option input:focus-visible + .role-card {
  outline: 2px solid var(--focus-ring); outline-offset: 2px;
}

/* Status tags (compact, non-interactive only) */
.tag {
  display: inline-flex; align-items: center; gap: 6px;
  height: 24px; padding: 0 10px;
  border-radius: 12px;
  font-size: 12.5px; font-weight: 600;
}
.tag-celadon { background: var(--celadon-bg); color: var(--celadon); }
.tag-persimmon { background: var(--persimmon-bg); color: var(--persimmon); }
.tag-porcelain { background: var(--porcelain-bg); color: var(--porcelain); }
.tag-plum { background: var(--plum-bg); color: var(--plum); }
.tag-risk { background: var(--risk-bg); color: var(--risk); }

/* Flash messages */
.flash-stack { margin: 0 0 var(--s-4); display: flex; flex-direction: column; gap: var(--s-2); }
.flash {
  background: var(--porcelain-bg); color: var(--porcelain);
  border-radius: var(--r-ctl);
  padding: var(--s-3) var(--s-4);
  font-size: 14.5px;
}

/* Stat tiles (admin landing / overview) */
.stat-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--s-3); }
@media (min-width: 768px) { .stat-row { grid-template-columns: repeat(4, 1fr); } }
.stat { padding: var(--s-4); }
.stat .stat-num { font-family: var(--font-editorial); font-size: 34px; font-weight: 500; font-variant-numeric: tabular-nums; letter-spacing: -0.01em; }
.stat .stat-label { font-size: 13px; color: var(--text-soft); }

/* == 8. Signature primitives (DESIGN.md "Lantern Hall") ========== */
/* The brass meridian: one hairline of segments carrying progress across a
   lesson, a class, a mastery. Fill segments with .on; mark the live one .now. */
.meridian { display: flex; align-items: center; gap: 5px; }
.meridian > i {
  flex: 1; height: 2px; display: block; border-radius: 2px;
  background: color-mix(in srgb, var(--gold-500) 30%, transparent);
}
.meridian > i.on { background: var(--gold-500); }
.meridian > i.now { background: var(--gold-300); box-shadow: 0 0 0 1px color-mix(in srgb, var(--gold-300) 35%, transparent); }
/* On a paper sheet the empty track reads against paper, not navy */
.sheet .meridian > i { background: var(--sheet-line); }
.sheet .meridian > i.on { background: var(--celadon); }

/* Ledger: figures read like a bank statement — label left, editorial tabular
   figure right, hairline rows. Replaces glowing stat-tile grids. */
.ledger { display: flex; flex-direction: column; }
.led-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: var(--s-4);
  padding: var(--s-3) 0; border-bottom: 1px solid var(--line);
}
.led-row:last-child { border-bottom: 0; }
.led-row .led-k { font-size: 14px; color: var(--text-soft); }
.led-row .led-v {
  font-family: var(--font-editorial); font-weight: 500; font-size: 30px;
  font-variant-numeric: tabular-nums; line-height: 1; color: var(--text);
}
.led-row .led-v.accent { color: var(--gold-300); }
.sheet .led-row { border-color: var(--sheet-line); }
.sheet .led-row .led-v { color: var(--sheet-text); }

/* Engraved plate: the join code and other minted values. Flat, hairline
   brass frame, faint brass wash — no gradient glow. */
.plate {
  border: 1px solid color-mix(in srgb, var(--gold-500) 34%, transparent);
  border-radius: var(--r-card);
  background: color-mix(in srgb, var(--gold-500) 6%, transparent);
  padding: var(--s-4) var(--s-5);
}
.plate .plate-label { font-size: 11px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--text-soft); }
.plate .plate-code {
  font-family: var(--font-mono); font-weight: 600; letter-spacing: 0.12em;
  color: var(--gold-300); font-size: clamp(26px, 6vw, 40px); line-height: 1.1;
}

/* Small-caps tracked label / eyebrow used across surfaces */
.eyebrow { font-size: 12px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--gold-500); font-weight: 600; }

/* Flat, framed distribution bar (live checks, meters). No gradient fills. */
.dist { height: 14px; border: 1px solid var(--line); border-radius: 3px; overflow: hidden; background: transparent; }
.dist > i { display: block; height: 100%; background: var(--celadon); }
.dist.neutral > i { background: var(--navy-700); }
.dist.risk > i { background: color-mix(in srgb, var(--risk) 70%, #000 30%); }
