/* ============================================================
 * Saturn theme — shared design system for dr.eamer.dev
 * --------------------------------------------------------------
 * Datasheet Annual aesthetic. Paper + ink, with a single signal red
 * for moments that ask for attention. Editorial typography (Fraunces
 * variable serif) for display + prose, IBM Plex Mono for tabular and
 * tactile UI labels. Hairline borders, 3 px radius, oldstyle figures.
 *
 * Canonical sources of truth that converge on this file:
 *   ~/projects/saturn/saturn/saturn/viewer/static/saturn.css
 *   ~/html/viewer/gallery.css
 *   ~/projects/ux-lesson-planner/static/css/style.css
 *
 * Usage:
 *   <link rel="stylesheet" href="/shared/saturn-theme.css">
 *   <body class="saturn">
 *
 * Body class is optional (the @import-only path also works). Use the
 * class when you need to scope the styles (e.g. an embeddable widget
 * inside a non-saturn host page).
 *
 * Light only by default. To opt into a manual dark mode toggle for a
 * specific site, see the "Manual dark theme" block at the bottom and
 * set <html data-theme="dark"> from JS persisted to localStorage.
 * The auto prefers-color-scheme: dark override is intentionally absent
 * — Luke wants light to be the canonical surface.
 * ============================================================ */

/* ---------- fonts ----------------------------------------------------- */
@import url('https://fonts.googleapis.com/css2?family=Fraunces:ital,opsz,wght,SOFT,WONK@0,9..144,300..900,0..100,0..1;1,9..144,300..900,0..100,0..1&family=IBM+Plex+Mono:wght@400;500;600&family=Inter:wght@400;500;600;700&display=swap');

/* ---------- tokens ---------------------------------------------------- */
:root {
  /* Palette — paper + ink + one signal red. */
  --paper:        #F4F1EA;       /* main canvas */
  --paper-raise:  #EFEBE1;       /* recessed paper card */
  --paper-clean:  #FAF8F2;       /* lifted paper card (slightly brighter) */
  --ink:          #161616;       /* primary text */
  --ink-soft:     #3F3F3F;       /* secondary text */
  --muted:        #6B6863;       /* hint, eyebrow, supporting */
  --rule:         rgba(22, 22, 22, 0.14);   /* subtle hairline */
  --rule-strong:  rgba(22, 22, 22, 0.32);   /* solid hairline */

  /* Signal colors. Use sparingly. */
  --hot:          #CE3526;       /* signal red — alerts, accent moments */
  --hot-wash:     rgba(206, 53, 38, 0.08);
  --cool:         #2F5D8F;       /* link-blue alternate, calmer than hot */
  --cool-wash:    rgba(47, 93, 143, 0.08);
  --warn:         #B26A00;       /* warnings, drafts, expiring */
  --warn-wash:    rgba(178, 106, 0, 0.10);
  --good:         #2F6C3F;       /* success, saved */
  --good-wash:    rgba(47, 108, 63, 0.10);

  /* Typography stacks. */
  --font-display: 'Fraunces', Charter, 'Iowan Old Style', 'Palatino Linotype', Georgia, serif;
  --font-body:    'Fraunces', Charter, 'Iowan Old Style', 'Palatino Linotype', Georgia, serif;
  --font-sans:    'Inter', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  --font-mono:    'IBM Plex Mono', 'SFMono-Regular', ui-monospace, Menlo, Consolas, monospace;

  /* 8 px baseline grid + reading measure. */
  --step: 8px;
  --measure: 62ch;

  /* Type scale — clamp for the largest size so it rides the viewport. */
  --size-eyebrow: 0.72rem;
  --size-micro:   0.72rem;
  --size-small:   0.86rem;
  --size-body:    1rem;
  --size-lede:    1.18rem;
  --size-h3:      1.35rem;
  --size-h2:      1.72rem;
  --size-h1:      clamp(2.4rem, 4vw + 1rem, 3.8rem);

  /* Geometry. */
  --radius:       3px;          /* default — printed-feeling */
  --radius-card:  6px;          /* lifted cards / dialogs */

  /* Focus. Hot accent so it survives across surfaces. */
  --focus-ring:   2px solid var(--hot);
  --focus-offset: 2px;
}

/* ---------- reset + base --------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html {
  -webkit-text-size-adjust: 100%;
  background: var(--paper);
}
body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--size-body);
  line-height: 1.55;
  font-feature-settings: "ss01" on, "onum" on, "kern" on;
  font-variant-numeric: oldstyle-nums proportional-nums;
  font-optical-sizing: auto;
  text-rendering: optimizeLegibility;
}

/* Optional paper grain overlay. Single soft wash so the page does not
 * read as a flat surface. Skip the cool-wash second gradient (it adds
 * busyness — see the toned-down lesson planner adoption). */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: -1;
  opacity: 0.5;
  background:
    radial-gradient(ellipse at 100% 0%, var(--cool-wash), transparent 55%);
}

a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--rule-strong);
  transition: text-decoration-color 120ms ease-out, color 120ms ease-out;
}
a:hover { text-decoration-color: var(--hot); color: var(--ink); }
a:focus-visible {
  outline: var(--focus-ring);
  outline-offset: var(--focus-offset);
  border-radius: 1px;
}

button { font: inherit; cursor: pointer; background: none; border: 0; color: inherit; }
button:focus-visible { outline: var(--focus-ring); outline-offset: var(--focus-offset); }
img, svg { display: block; max-width: 100%; }

/* ---------- accessibility primitives --------------------------------- */
.skip-link {
  position: absolute;
  top: -100vh;
  left: 0;
  padding: calc(var(--step) * 1.5) calc(var(--step) * 2);
  background: var(--ink);
  color: var(--paper);
  font-family: var(--font-mono);
  font-size: var(--size-small);
  text-decoration: none;
  z-index: 100;
  border-radius: var(--radius);
}
.skip-link:focus { top: 0; }

.visually-hidden, .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;
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

/* ---------- layout primitives ---------------------------------------- */
.page {
  max-width: 1240px;
  margin: 0 auto;
  padding: 0 calc(var(--step) * 3);
}
@media (max-width: 768px) {
  .page { padding: 0 calc(var(--step) * 2); }
}

.grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: calc(var(--step) * 3);
}

/* ---------- site head ------------------------------------------------- */
.sitehead {
  border-bottom: 1px solid var(--rule);
  padding: calc(var(--step) * 2) 0;
  margin-bottom: calc(var(--step) * 5);
}
.sitehead .page {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: calc(var(--step) * 2);
  flex-wrap: wrap;
}
.sitehead .wordmark {
  font-family: var(--font-display);
  font-variation-settings: "opsz" 144, "wght" 700, "SOFT" 50, "WONK" 0;
  font-size: 1.6rem;
  letter-spacing: -0.02em;
  line-height: 1;
}
.sitehead .wordmark em {
  color: var(--hot);
  font-style: normal;
  font-variation-settings: "opsz" 144, "wght" 700, "SOFT" 50, "WONK" 1;
}
.sitehead .meta {
  font-family: var(--font-mono);
  font-size: var(--size-micro);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* ---------- typography blocks ---------------------------------------- */
.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--size-eyebrow);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--muted);
  margin: 0 0 calc(var(--step) * 1.5);
}
.eyebrow.hot { color: var(--hot); }

h1, .h1 {
  font-family: var(--font-display);
  font-variation-settings: "opsz" 144, "wght" 600, "SOFT" 50, "WONK" 0;
  font-size: var(--size-h1);
  letter-spacing: -0.025em;
  line-height: 1.02;
  margin: 0 0 calc(var(--step) * 2);
}
h2, .h2 {
  font-family: var(--font-display);
  font-variation-settings: "opsz" 72, "wght" 600, "SOFT" 50;
  font-size: var(--size-h2);
  letter-spacing: -0.015em;
  line-height: 1.15;
  margin: 0 0 calc(var(--step) * 2);
}
h3, .h3 {
  font-family: var(--font-display);
  font-variation-settings: "opsz" 36, "wght" 550, "SOFT" 30;
  font-size: var(--size-h3);
  letter-spacing: -0.01em;
  line-height: 1.25;
  margin: 0 0 calc(var(--step) * 1);
}
.lede {
  font-size: var(--size-lede);
  line-height: 1.5;
  color: var(--ink-soft);
  max-width: var(--measure);
}
p { max-width: var(--measure); margin: 0 0 calc(var(--step) * 2); }
ul, ol { max-width: var(--measure); padding-left: calc(var(--step) * 3); margin: 0 0 calc(var(--step) * 2); }
li { margin-bottom: calc(var(--step) * 0.5); }
strong { font-weight: 600; color: var(--ink); }
em { font-style: italic; }
code {
  font-family: var(--font-mono);
  font-size: 0.92em;
  background: var(--paper-raise);
  padding: 1px 6px;
  border-radius: var(--radius);
  color: var(--ink);
}
pre {
  font-family: var(--font-mono);
  background: var(--paper-raise);
  padding: var(--step) calc(var(--step) * 2);
  border-radius: var(--radius);
  border-left: 3px solid var(--rule-strong);
  overflow-x: auto;
  font-size: var(--size-small);
  margin: 0 0 calc(var(--step) * 2);
}
blockquote {
  border-left: 3px solid var(--rule-strong);
  padding: 0 calc(var(--step) * 2);
  margin: 0 0 calc(var(--step) * 2);
  color: var(--ink-soft);
  font-style: italic;
}

/* ---------- buttons --------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--step);
  padding: calc(var(--step) * 1.25) calc(var(--step) * 2);
  background: var(--ink);
  color: var(--paper);
  border: 1px solid var(--ink);
  border-radius: var(--radius);
  font-family: var(--font-mono);
  font-size: var(--size-small);
  letter-spacing: 0.04em;
  cursor: pointer;
  transition: background 120ms ease-out, color 120ms ease-out, border-color 120ms ease-out;
  min-height: 44px;             /* WCAG 2.5.5 touch target */
  text-decoration: none;
  white-space: nowrap;
}
.btn:hover:not(:disabled) {
  background: var(--hot);
  border-color: var(--hot);
}
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

.btn-ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--rule-strong);
}
.btn-ghost:hover:not(:disabled) {
  border-color: var(--ink);
  background: var(--paper-raise);
}

.btn-small {
  padding: calc(var(--step) * 0.75) calc(var(--step) * 1.25);
  font-size: var(--size-eyebrow);
  min-height: 36px;
}

/* ---------- form fields ---------------------------------------------- */
.field {
  display: flex;
  flex-direction: column;
  gap: var(--step);
}
.field > label {
  font-family: var(--font-mono);
  font-size: var(--size-eyebrow);
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}
.field input[type="text"],
.field input[type="email"],
.field input[type="search"],
.field input[type="number"],
.field input[type="url"],
.field textarea,
.field select {
  font: inherit;
  font-family: var(--font-mono);
  font-size: var(--size-small);
  padding: calc(var(--step) * 1.25) calc(var(--step) * 1.5);
  background: var(--paper-clean);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  color: var(--ink);
  min-height: 44px;
  min-width: 0;
}
.field textarea { min-height: 88px; resize: vertical; }
.field input:focus-visible,
.field textarea:focus-visible,
.field select:focus-visible {
  outline: 2px solid var(--hot);
  outline-offset: -1px;
  border-color: var(--hot);
}
.field .hint {
  font-family: var(--font-mono);
  font-size: var(--size-eyebrow);
  letter-spacing: 0.04em;
  color: var(--muted);
}

/* ---------- callouts -------------------------------------------------- */
.callout {
  padding: calc(var(--step) * 2);
  border-left: 3px solid var(--rule-strong);
  border-radius: var(--radius);
  background: var(--paper-raise);
  margin: 0 0 calc(var(--step) * 2);
}
.callout.warn { border-color: var(--warn); background: var(--warn-wash); }
.callout.warn strong { color: var(--warn); }
.callout.hot { border-color: var(--hot); background: var(--hot-wash); }
.callout.hot strong { color: var(--hot); }
.callout.cool { border-color: var(--cool); background: var(--cool-wash); }
.callout.cool strong { color: var(--cool); }
.callout.good { border-color: var(--good); background: var(--good-wash); }
.callout.good strong { color: var(--good); }

/* ---------- stamps + chips (hairline category labels) ---------------- */
.stamp {
  display: inline-flex;
  align-items: center;
  gap: calc(var(--step) * 0.5);
  font-family: var(--font-mono);
  font-size: var(--size-eyebrow);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  padding: calc(var(--step) * 0.5) var(--step);
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  color: var(--ink-soft);
  background: var(--paper);
}
.stamp.hot  { border-color: var(--hot);  color: var(--hot); }
.stamp.cool { border-color: var(--cool); color: var(--cool); }
.stamp.warn { border-color: var(--warn); color: var(--warn); }
.stamp.good { border-color: var(--good); color: var(--good); }

/* ---------- footer ---------------------------------------------------- */
.sitefoot {
  margin-top: calc(var(--step) * 8);
  padding: calc(var(--step) * 3) 0;
  border-top: 1px solid var(--rule);
  font-family: var(--font-mono);
  font-size: var(--size-eyebrow);
  letter-spacing: 0.08em;
  color: var(--muted);
  text-align: center;
}
.sitefoot a {
  color: var(--muted);
  text-decoration: none;
  margin: 0 var(--step);
}
.sitefoot a:hover { color: var(--hot); text-decoration: underline; }

/* ---------- print ---------------------------------------------------- */
@media print {
  html, body { background: white; color: black; font-size: 11pt; }
  body::before { display: none; }
  .sitehead, .sitefoot, .skip-link, .btn { display: none !important; }
  a { color: black; text-decoration: none; }
  a[href^='http']::after {
    content: ' (' attr(href) ')';
    font-size: 0.85em;
    color: #666;
  }
  pre { border: 1px solid #ccc; }
  blockquote { border-color: #999; }
}

/* ============================================================
 * Theme variants.
 * ============================================================
 * Default at :root above is the saturn paper-and-ink palette. Add a
 * body class to retune the palette for sites that have a different
 * personality but want to share the rest of the system (typography,
 * spacing, components, accessibility). All variants are LIGHT mode —
 * Luke wants light to stay canonical; sites that need dark add a
 * manual toggle keyed off <html data-theme="dark"> at the very bottom
 * of this file.
 *
 * ── How to pick a variant ────────────────────────────────────
 *   <body>                       paper + ink + signal red. Editorial
 *                                feel. Use for: /, /saturn, /planner,
 *                                /viewer, /me — anything that should
 *                                read like the same publication.
 *
 *   <body class="theme-bright">  GitHub-Light: clean white, blue
 *                                accent, success/warning/danger in
 *                                familiar GitHub colors. Use for:
 *                                /downloads, /code, /code/api*.
 *                                Code-and-tooling surfaces where a
 *                                paper aesthetic would feel
 *                                out-of-character.
 *
 * Both variants share every component class (.btn, .field, .callout,
 * .stamp, .sitehead, etc.) — only the palette tokens move.
 *
 * Add a new variant only when an existing site won't fit either of
 * these two shells. Two is plenty for now.
 * ============================================================ */
.theme-bright {
  --paper:        #ffffff;
  --paper-raise:  #f6f8fa;       /* GitHub canvas */
  --paper-clean:  #ffffff;
  --ink:          #1f2328;       /* GitHub fg */
  --ink-soft:     #424a53;
  --muted:        #656d76;
  --rule:         rgba(31, 35, 40, 0.10);
  --rule-strong:  #d0d7de;       /* GitHub border */

  /* Signal becomes blue on bright. Hot stays available as an alarm. */
  --hot:          #cf222e;       /* GitHub danger — alerts, errors */
  --hot-wash:     rgba(207, 34, 46, 0.08);
  --cool:         #0969da;       /* GitHub accent — primary link/CTA */
  --cool-wash:    rgba(9, 105, 218, 0.08);
  --warn:         #9a6700;       /* GitHub attention */
  --warn-wash:    rgba(154, 103, 0, 0.10);
  --good:         #1a7f37;       /* GitHub success */
  --good-wash:    rgba(26, 127, 55, 0.10);

  /* On bright, Inter reads better than Fraunces for body. The user's
     downloads/code pages are dense + dev-flavored; flip body to sans.
     Headings stay Fraunces so the editorial moment isn't lost. */
  --font-body: 'Inter', system-ui, -apple-system, BlinkMacSystemFont,
               'Segoe UI', Roboto, sans-serif;

  /* Skip the warm radial wash — it reads odd on a true white page. */
}
.theme-bright body::before { display: none; }

/* On bright, primary buttons should call out with cool blue, not ink.
   Override the .btn behavior to match. */
.theme-bright .btn {
  background: var(--cool);
  border-color: var(--cool);
}
.theme-bright .btn:hover:not(:disabled) {
  background: #0860ca;
  border-color: #0860ca;
}
.theme-bright .btn-ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--rule-strong);
}
.theme-bright .btn-ghost:hover:not(:disabled) {
  border-color: var(--cool);
  color: var(--cool);
  background: var(--cool-wash);
}

/* Wordmark accent flips from hot to cool on bright. */
.theme-bright .sitehead .wordmark em {
  color: var(--cool);
}

/* ============================================================
 * Manual dark theme — opt-in only.
 * ============================================================
 * Set <html data-theme="dark"> from JS to enable. Persist the user's
 * choice in localStorage. DO NOT use prefers-color-scheme: dark — Luke
 * wants light to stay canonical, with dark available only as a teacher-
 * level preference on sites that explicitly support both (e.g. /viewer).
 *
 * The dark variant overrides on top of whichever theme variant is
 * active (saturn or bright); both produce reasonable dark surfaces.
 * ============================================================ */
:root[data-theme="dark"] {
  --paper:        #17160F;
  --paper-raise:  #1E1D15;
  --paper-clean:  #25241B;
  --ink:          #F3EFE4;
  --ink-soft:     #C9C4B6;
  --muted:        #8A8578;
  --rule:         rgba(243, 239, 228, 0.14);
  --rule-strong:  rgba(243, 239, 228, 0.32);
  --hot:          #F1604E;
  --hot-wash:     rgba(241, 96, 78, 0.10);
  --cool:         #6FA3D2;
  --cool-wash:    rgba(111, 163, 210, 0.10);
}
:root[data-theme="dark"] .theme-bright {
  /* GitHub Dark Dimmed when bright + dark stack together. */
  --paper:        #0d1117;
  --paper-raise:  #161b22;
  --paper-clean:  #161b22;
  --ink:          #c9d1d9;
  --ink-soft:     #a8b1ba;
  --muted:        #8b949e;
  --rule:         rgba(201, 209, 217, 0.10);
  --rule-strong:  #30363d;
  --cool:         #58a6ff;
  --cool-wash:    rgba(88, 166, 255, 0.12);
  --hot:          #f85149;
  --good:         #3fb950;
  --warn:         #d29922;
}
