/* css/base.css - Variables, Themes, and Resets */

/* ── Self-hosted webfonts ───────────────────────────────────────────────
   Vendored from the upstream Google Fonts repository under the SIL Open
   Font License 1.1. License texts and a provenance record ship alongside
   the font files in chem-apps/fonts/.

   Self-hosted rather than fetched from Google's CDN, for two reasons:
     1. The suite must render identically with no network at all — flash
        drive, offline classroom, locked-down district machine. A CDN font
        silently falls back to system sans and the layout shifts with it.
     2. A CDN font request transmits the reader's IP address and user-agent
        to a third party on every page load. Nothing else in the platform
        makes an outbound request; this was the last one.

   Full character sets, deliberately NOT subsetted. The suite needs ø
   (Brønsted), en dashes, Δ, °, ⇌ and the grandfathered Unicode subscripts
   in shared data strings — and with no network there is no second chance
   at a missing glyph, it just renders as tofu.

   These are variable fonts: one file covers every weight the suite asks
   for, which is why the per-page weight lists are gone from the <link>
   tags that used to live in each HTML head.
   ────────────────────────────────────────────────────────────────── */
@font-face {
  font-family: "DM Sans";
  src: url("../fonts/DMSans-Variable.woff2") format("woff2");
  font-weight: 100 1000;   /* full wght axis */
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "DM Sans";
  src: url("../fonts/DMSans-Italic-Variable.woff2") format("woff2");
  font-weight: 100 1000;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: "Source Code Pro";
  src: url("../fonts/SourceCodePro-Variable.woff2") format("woff2");
  font-weight: 200 900;    /* full wght axis */
  font-style: normal;
  font-display: swap;
}

/* ── Arrow override ───────────────────────────────────────────────
   Six codepoints only. DM Sans draws its arrowheads as open chevrons with no
   filled triangle; at the 0.9rem of body copy that is about 16px, where the
   chevrons lose their point and read as a lozenge rather than an arrow. Arial
   and Segoe UI fill their heads solid and survive the size. Everything else in
   the suite stays DM Sans — this is a patch, not a font change.

   DECLARED AFTER the main DM Sans faces on purpose: for a shared family, weight
   and style, the last matching declaration wins for the codepoints it claims.

   local() only — nothing is downloaded and nothing new ships. Arial covers
   Windows, macOS and iOS; Arimo is the metric-compatible substitute present on
   ChromeOS. Liberation Sans is deliberately absent: it sits too low on the
   baseline and is no improvement on what it would replace.

   The trailing url() is the safety line. If none of the three local faces
   exist, this falls back to DM Sans explicitly rather than tumbling down the
   stack to whatever sans-serif resolves to — often Liberation or DejaVu on
   Linux. Worst case is therefore exactly today's rendering, never worse.

   Scope notes: the diagonals U+2196-2199 stay with DM Sans, since Arial has no
   glyphs for them. ⇌ ⇄ ⇒ are untouched — never in DM Sans, still falling
   through to the system as they always have. No italic face is declared; no
   italic content currently contains an arrow.
   ─────────────────────────────────────────────────────────────────── */
@font-face {
  font-family: "DM Sans";
  src: local("Arial"), local("Segoe UI"), local("Arimo"),
       url("../fonts/DMSans-Variable.woff2") format("woff2");
  unicode-range: U+2190-2195;   /* ← ↑ → ↓ ↔ ↕ */
  font-weight: 100 1000;
  font-style: normal;
}

[data-theme="light"] {
  --bg: #f8f9fc; --surface: #FFFFFF;
  --primary: #2C5CC5; --primary-light: #EBF0FA; --primary-mid: #D6E1F5; --primary-hover: #234BA0; --primary-glow: rgba(44,92,197,0.12);
  --secondary: #1B9E77; --secondary-light: #E6F6F1; --secondary-muted: #7BBFAB;
  --tertiary: #6B5CA5; --tertiary-light: #F0EDF7;
  --alert: #D95F02; --alert-light: #FDF0E6;
  --highlight: #E6AB02; --highlight-light: #FDF8E6;
  --text: #111827; --text-muted: #5A6270; --text-light: #8B95A5;
  --border: #E5E7EB; --border-light: #F0F1F3;
  --panel: #E0E7F2; --panel-deep: #D0DAE9; --panel-text: #2A3A52; --panel-accent: #3D6ACE;
  --solved-bg: #D0DAE9; --solved-deep: #C2CFE2; --solved-text: #1E2E44; --solved-val: #2C5CC5;
  --shadow-sm: 0 1px 3px rgba(17,24,39,0.05), 0 1px 2px rgba(17,24,39,0.03);
  --shadow-md: 0 4px 14px rgba(17,24,39,0.07), 0 2px 4px rgba(17,24,39,0.03);
  --shadow-lg: 0 10px 32px rgba(17,24,39,0.09), 0 4px 10px rgba(17,24,39,0.04);
  --shadow-primary: 0 3px 14px rgba(44,92,197,0.22);
  --overlay: rgba(17,24,39,0.22);
}

[data-theme="light"][data-scheme="teal"] {
  --primary: #1B9E77; --primary-light: #E6F6F1; --primary-mid: #C5EAE0; --primary-hover: #147A5C; --primary-glow: rgba(27,158,119,0.12);
}
[data-theme="light"][data-scheme="orange"] {
  --primary: #D95F02; --primary-light: #FDF0E6; --primary-mid: #F5D4B8; --primary-hover: #B34E02; --primary-glow: rgba(217,95,2,0.12);
}
[data-theme="light"][data-scheme="amber"] {
  --primary: #C49200; --primary-light: #FDF8E6; --primary-mid: #F0E0A8; --primary-hover: #9E7600; --primary-glow: rgba(196,146,0,0.12);
}
[data-theme="light"][data-scheme="purple"] {
  --primary: #6B5CA5; --primary-light: #F0EDF7; --primary-mid: #D8D0EB; --primary-hover: #564A88; --primary-glow: rgba(107,92,165,0.12);
}

[data-theme="dark"] {
  --bg: #111827; --surface: #1E2636;
  --primary: #6B93DB; --primary-light: #1E2C42; --primary-mid: #263550; --primary-hover: #8AAEE6; --primary-glow: rgba(107,147,219,0.15);
  --secondary: #3FC9A0; --secondary-light: #162E27; --secondary-muted: #2D8A6E;
  --tertiary: #9B8DD4; --tertiary-light: #252040;
  --alert: #F08C4A; --alert-light: #2E1E10;
  --highlight: #F0C840; --highlight-light: #2E2810;
  --text: #E5E7EB; --text-muted: #9BA4B4; --text-light: #6B7585;
  --border: #2A3346; --border-light: #232D3E;
  --panel: #1A2436; --panel-deep: #162030; --panel-text: #B0BCE0; --panel-accent: #6B93DB;
  --solved-bg: #162030; --solved-deep: #121C2A; --solved-text: #B0BCE0; --solved-val: #8AAEE6;
  --shadow-sm: 0 1px 3px rgba(0,0,0,0.2);
  --shadow-md: 0 4px 14px rgba(0,0,0,0.3);
  --shadow-lg: 0 10px 32px rgba(0,0,0,0.4);
  --shadow-primary: 0 3px 14px rgba(107,147,219,0.25);
  --overlay: rgba(0,0,0,0.45);
}

[data-theme="dark"][data-scheme="teal"] {
  --primary: #3FC9A0; --primary-light: #162E27; --primary-mid: #1E3D33; --primary-hover: #5DD8B4;
}
[data-theme="dark"][data-scheme="orange"] {
  --primary: #F08C4A; --primary-light: #2E1E10; --primary-mid: #3E2818; --primary-hover: #F5A46E;
}
[data-theme="dark"][data-scheme="amber"] {
  --primary: #F0C840; --primary-light: #2E2810; --primary-mid: #3E3518; --primary-hover: #F5D668;
}
[data-theme="dark"][data-scheme="purple"] {
  --primary: #9B8DD4; --primary-light: #252040; --primary-mid: #302A50; --primary-hover: #B3A6E0;
}

:root {
  --radius: 14px; --radius-lg: 20px; --radius-sm: 8px;
  --font: "DM Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --font-mono: "Source Code Pro", "Menlo", "Consolas", monospace;
  --transition: 200ms cubic-bezier(0.4, 0, 0.2, 1);
  --font-scale: 1;

  /* ── Header brand geometry ────────────────────────────────────────
     --brand-h is the height of the three-line brand stack, written out
     from its own parts so it tracks --font-scale with no measurement:
       wordmark   1.05 line-height × --wordmark-size
       bar        5px margin + --bar
       subtitle   4px margin + (0.76rem × 1.25 line-height) = 0.95rem
     layout.css feeds it straight into the squircle's width AND height.
     If any of those three type sizes change, this calc changes with it.
     ──────────────────────────────────────────────────────── */
  --wordmark-size: 1.3rem;
  --bar: clamp(2px, 0.17rem, 4px);
  --brand-h: calc(var(--wordmark-size) * 1.05 + 0.90rem + 9px + var(--bar));
  /* Right inset on the spectrum bar so it stops just short of the 't' in
     'chemistry', leaving 'try' hanging past the end. DELIBERATE — not a
     rounding error. 1.34em is the measured advance of 'try' in DM Sans
     Bold at -0.03em tracking, i.e. 18.9% of the wordmark. */
  --bar-inset: 1.34em;
}

*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

/* ── Hide number input spinners (platform-wide hard rule — student-facing fields) ── */
input[type="number"]::-webkit-outer-spin-button,
input[type="number"]::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
input[type="number"] { -moz-appearance: textfield; }

html { font-size: calc(18px * var(--font-scale)); -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
body { font-family: var(--font); background: var(--bg); color: var(--text); line-height: 1.6; min-height: 100vh; transition: background var(--transition), color var(--transition); }
::selection { background: var(--primary-light); color: var(--primary); }

/* The Dark Mode Link Fix */
a { color: var(--primary); text-decoration: none; transition: color var(--transition); }
a:hover { color: var(--primary-hover); text-decoration: underline; }
[data-theme="dark"] a { color: var(--primary-hover); } 
[data-theme="dark"] a:hover { color: #A6C8FF; }

@keyframes fadeInUp { from { opacity:0; transform:translateY(10px) } to { opacity:1; transform:translateY(0) } }
@keyframes fadeIn { from { opacity:0 } to { opacity:1 } }
@keyframes slideDown { from { opacity:0; max-height:0 } to { opacity:1; max-height:3000px } }
@keyframes scaleIn { from { opacity:0; transform:scale(.96) } to { opacity:1; transform:scale(1) } }
@keyframes copyFlash { 0%{opacity:1} 70%{opacity:1} 100%{opacity:0} }

.anim-up { animation: fadeInUp .35s ease-out forwards }
.anim-fade { animation: fadeIn .3s ease-out forwards }
.anim-slide { animation: slideDown .4s ease-out forwards; overflow:hidden }
.anim-scale { animation: scaleIn .3s ease-out forwards }