/* ============================================================================
   yinuai.com site styles
   Theme tokens carried over from the internal workspace catalog stylesheet
   (dark charcoal + Autodesk teal, per-category accents), rebuilt for a public,
   responsive, accessible site. Styles specific to that tool are not reused.
   ========================================================================= */

/* Self-hosted (src\fonts\, OFL-licensed -- see src\fonts\OFL.txt) rather than a
   Google Fonts <link>, so no third-party font-src has to be allowed alongside
   the site's otherwise fully self-contained CSP. Only the one weight the site
   actually uses (headings/eyebrow are always font-weight: 650) is shipped. */
@font-face {
  font-family: "Space Grotesk";
  font-style: normal;
  font-weight: 650;
  font-display: swap;
  src: url("../fonts/space-grotesk-650.woff2") format("woff2");
}

:root {
  /* Warm mid-grey, not a cool charcoal, deliberately distinct from the
     neutral/blue-tinted greys Autodesk product UIs use. This is about as light
     as the palette goes while keeping all text at WCAG AA and every one of the
     seven category accents above the 3:1 UI floor. Lightening further would
     require re-tuning all seven accents.
     Contrast was verified against the COMPOSITED card surface (--bg + --panel),
     not the base colour. That is where most text actually sits. */
  --bg:         #3a3633;
  --bg-2:       #2e2b28;
  --bg-3:       #44403c;
  --panel:      rgba(255, 255, 255, 0.04);
  --panel-2:    rgba(255, 255, 255, 0.06);
  /* White overlays separate less on a light base than a near-black one, so the
     borders carry more of the card definition than they used to. */
  --border:     rgba(255, 255, 255, 0.15);
  --border-2:   rgba(255, 255, 255, 0.26);

  --text:       #e8eaed;
  --text-dim:   #b4bac1;   /* lifted from the source theme's #9aa0a6 for AA contrast */
  /* Sized for the LIGHTEST surface it lands on, --panel-2: .filter .count uses
     this colour and .filter:hover paints --panel-2 behind it. Tuning to the card
     surface alone leaves that hover state at 4.24:1. */
  --text-faint: #a9b0ba;

  --teal:        #0696d7;
  --teal-bright: #37b6f0;
  --gold:        #c89a20;

  /* category accents */
  --c-FormaDM:     #37b6f0;
  --c-C3D:         #f0954a;
  --c-ReCap:       #bb82f2;
  --c-FormaDesign: #35d4b4;
  --c-IFC:         #6d94f5;
  --c-AIW:         #ef6c83;
  --c-Misc:        #d7ac3c;
  --cat:           var(--teal-bright);

  /* Header backdrop, hero/page-head glows, and the brand hover "pop" all used a
     literal color baked from the dark palette instead of a token, so a second
     theme couldn't reach them. Tokenized here; see [data-theme="light"] below. */
  --head-bg: rgba(46, 43, 40, 0.86);   /* --bg-2 with alpha, spelled out for backdrop-filter */
  --glow-1:  rgba(75, 190, 240, 0.30);
  --glow-2:  rgba(90, 225, 200, 0.16);
  --glow-3:  rgba(75, 190, 240, 0.24);
  --brand-pop: #fff;

  --radius:   14px;
  --radius-s: 9px;
  --shadow:   0 8px 30px rgba(0, 0, 0, 0.45);

  --wrap:  1180px;
  --gap:   clamp(16px, 2.5vw, 28px);
  --sp-section: clamp(56px, 8vw, 104px);

  --font: "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
  /* Headings/eyebrow only -- see the h1-h4/.eyebrow rules below. Body text stays
     on --font. The system stack is kept as a fallback for any glyph outside the
     self-hosted file's Latin subset, and as the whole stack while the font
     file itself loads (font-display: swap covers the flash-of-invisible-text
     case; this covers the flash-of-missing-glyph case). */
  --font-display: "Space Grotesk", "Segoe UI", system-ui, -apple-system,
                  "Helvetica Neue", Arial, sans-serif;
  --mono: "Cascadia Mono", ui-monospace, Consolas, "SF Mono", Menlo, monospace;
}

/* ============================================================================
   Light/bright theme. Opt-in only (src\js\theme-toggle.js sets this attribute
   and persists the choice) -- the dark palette above stays the default so no
   visitor sees a change without asking for one.

   Every color below was re-tuned for a light base, not copied from the dark
   values above: on a light background most of the dark palette's bright
   pastel accents fail as text. Contrast was checked the same way as the dark
   palette (see the --bg comment above): against the actual composited card
   surface (--bg + --panel), aiming for >=4.5:1 wherever a token is used as
   text (category labels, links, row titles) and >=3:1 where it is only ever a
   border or fill (card top edge, left borders, filter pills).
   ========================================================================= */
:root[data-theme="light"] {
  --bg:         #f5f1eb;
  --bg-2:       #ece5db;
  --bg-3:       #ddd3c4;
  --panel:      rgba(0, 0, 0, 0.035);
  --panel-2:    rgba(0, 0, 0, 0.06);
  --border:     rgba(0, 0, 0, 0.12);
  --border-2:   rgba(0, 0, 0, 0.22);

  --text:       #2a2521;
  --text-dim:   #5b5248;
  --text-faint: #6b6055;

  --teal:        #0a5a87;
  --teal-bright: #0d6fa8;
  --gold:        #7a5e10;

  /* category accents -- darkened/resaturated companions of the dark-mode hues,
     each independently checked for >=4.5:1 as text against --bg above */
  --c-FormaDM:     #0d6fa8;   /* same hue as --teal-bright, matching the dark theme's pairing */
  --c-C3D:         #93530f;
  --c-ReCap:       #7a3fc4;
  --c-FormaDesign: #0a6f5b;
  --c-IFC:         #3355c9;
  --c-AIW:         #c22c48;
  --c-Misc:        #8a6410;

  --head-bg: rgba(236, 229, 219, 0.86);
  /* Dark mode lightens the hero glow against a dark base; the same trick washes
     out a light one, so these are low-alpha tints of the accent hue instead. */
  --glow-1:  rgba(20, 110, 165, 0.14);
  --glow-2:  rgba(20, 140, 120, 0.10);
  --glow-3:  rgba(20, 110, 165, 0.12);
  --brand-pop: #1a1512;
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 16px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; height: auto; display: block; }

a { color: var(--teal-bright); text-decoration: none; }
a:hover { text-decoration: underline; }

:focus-visible {
  outline: 2px solid var(--teal-bright);
  outline-offset: 3px;
  border-radius: 3px;
}

h1, h2, h3, h4 {
  margin: 0;
  line-height: 1.2;
  letter-spacing: -0.02em;
  font-weight: 650;
  font-family: var(--font-display);
}

h1 { font-size: clamp(2rem, 5vw, 3.25rem); }
h2 { font-size: clamp(1.5rem, 3vw, 2.125rem); }
h3 { font-size: 1.125rem; }
h4 { font-size: 1.0625rem; }

p { margin: 0 0 1em; }
ul { margin: 0; padding: 0; }

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: clamp(18px, 5vw, 40px);
}

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}

.skip-link {
  position: absolute; left: 12px; top: -60px; z-index: 100;
  background: var(--teal); color: #fff;
  padding: 10px 16px; border-radius: var(--radius-s);
  transition: top .15s ease;
}
.skip-link:focus { top: 12px; }

/* ── Header ───────────────────────────────────────────────────────────── */

.site-head {
  position: sticky; top: 0; z-index: 50;
  background: var(--head-bg);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
}

.head-inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px; min-height: 64px;
}

.brand {
  display: inline-flex; align-items: center; gap: 10px;
  color: var(--text); font-weight: 650; font-size: 1rem;
  letter-spacing: -0.01em;
}
.brand:hover { text-decoration: none; color: var(--brand-pop); }
.brand img { border-radius: 7px; }

.site-nav { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }

.site-nav a {
  color: var(--text-dim);
  padding: 8px 13px;
  border-radius: var(--radius-s);
  font-size: 0.9375rem;
  font-weight: 500;
}
.site-nav a:hover { color: var(--text); background: var(--panel); text-decoration: none; }
.site-nav a[aria-current="page"] { color: var(--teal-bright); background: var(--panel); }

.theme-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  flex: none;
  width: 36px; height: 36px;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--panel);
  color: var(--text-dim);
  cursor: pointer;
}
.theme-toggle:hover { background: var(--panel-2); color: var(--text); }
.theme-toggle svg { width: 17px; height: 17px; fill: currentColor; }
.theme-toggle .icon-moon { display: none; }
:root[data-theme="light"] .theme-toggle .icon-sun { display: none; }
:root[data-theme="light"] .theme-toggle .icon-moon { display: block; }

/* ── Hero ─────────────────────────────────────────────────────────────── */

.hero {
  position: relative;
  overflow: hidden;
  padding-block: clamp(56px, 10vw, 116px) var(--sp-section);
  /* Lifted from .20/.11, since a glow has to be lighter than what it sits on, and a
     lighter base eats more of the tint before it reads. */
  background:
    radial-gradient(1100px 520px at 12% -10%, var(--glow-1), transparent 62%),
    radial-gradient(760px 440px at 92% 4%, var(--glow-2), transparent 60%),
    var(--bg);
  border-bottom: 1px solid var(--border);
}

/* Contour-line motif, a nod to the civil-engineering subject matter. Purely
   decorative (aria-hidden in the markup): low-opacity, themed via the same
   tokens as everything else so it stays correct in both palettes without its
   own light/dark rules, and kept subtle enough not to compete with the
   WCAG-AA-tuned text sitting on top of it.
   Drawn as a surveyor would: closed rings around one summit, bleeding off the
   top-right corner away from the text column, spaced tighter on the steep side,
   with every fifth ring a heavier index contour. Pinned to the corner at a fixed
   scale rather than stretched to cover the hero, because the hero is far taller
   than it is wide on most screens and a "slice" fit would blow the rings up
   past recognition. non-scaling-stroke keeps line weights constant at any size. */
.hero-motif {
  position: absolute;
  top: 0;
  right: 0;
  width: clamp(520px, 64vw, 980px);
  height: auto;
  z-index: 0;
}
.hero-motif-line {
  fill: none;
  stroke: var(--border-2);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  opacity: 0.55;
}
.hero-motif-index {
  stroke: var(--teal-bright);
  stroke-width: 2;
  opacity: 0.3;
}

.hero-inner { position: relative; z-index: 1; }

.eyebrow {
  margin: 0 0 14px;
  font-size: 0.78rem;
  font-weight: 650;
  font-family: var(--font-display);
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--teal-bright);
}

.hero-eyebrow-row {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 16px;
  margin: 0 0 14px;
}
.hero-eyebrow-row .eyebrow { margin: 0; }

.hero-eyebrow-row .post-audio { margin: 0; }
.hero-eyebrow-row .post-audio audio { width: 220px; max-width: 220px; height: 32px; }

.hero h1 { max-width: 22ch; }

.lede {
  margin-top: 20px;
  font-size: clamp(1.0625rem, 1.7vw, 1.25rem);
  color: var(--text-dim);
  max-width: 66ch;
}

.hero-transition {
  margin: 30px 0 0;
  max-width: 62ch;
  color: var(--text-dim);
}

.hero-points {
  list-style: none;
  margin: 18px 0 0;
  display: grid;
  gap: 12px;
  max-width: 74ch;
}

.hero-points li {
  position: relative;
  padding-left: 28px;
  color: var(--text-dim);
}
.hero-points li::before {
  content: "";
  position: absolute; left: 4px; top: 0.62em;
  width: 8px; height: 8px; border-radius: 50%;
  background: var(--teal-bright);
}

.hero-stood {
  margin: 38px 0 0;
  padding-top: 34px;
  border-top: 1px solid var(--border);
  max-width: 62ch;
}
.hero-stood p { color: var(--text-dim); margin: 12px 0 0; }
.hero-stood .pull { margin-bottom: 0; }

.hero-actions {
  display: flex; flex-wrap: wrap; gap: 12px;
  margin-top: 34px;
}

.btn {
  display: inline-flex; align-items: center; justify-content: center;
  padding: 12px 22px;
  border-radius: var(--radius-s);
  border: 1px solid var(--border-2);
  background: var(--panel);
  color: var(--text);
  font-size: 0.9375rem; font-weight: 600;
  transition: background .16s ease, border-color .16s ease, transform .16s ease;
}
.btn:hover {
  background: var(--panel-2);
  border-color: var(--text-faint);
  text-decoration: none;
  transform: translateY(-1px);
}

.btn-primary {
  background: var(--teal);
  border-color: var(--teal);
  color: #fff;
}
.btn-primary:hover { background: var(--teal-bright); border-color: var(--teal-bright); }

.hero-stats {
  display: flex; flex-wrap: wrap; gap: 0;
  margin: 46px 0 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel);
  overflow: hidden;
  max-width: 620px;
}
.hero-stats > div {
  flex: 1 1 150px;
  padding: 20px 24px;
  border-right: 1px solid var(--border);
}
.hero-stats > div:last-child { border-right: none; }
.hero-stats dt {
  font-size: 0.72rem; font-weight: 650;
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--text-faint);
}
.hero-stats dd {
  margin: 6px 0 0;
  font-size: 1.875rem; font-weight: 700;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

/* ── Sections ─────────────────────────────────────────────────────────── */

.section { padding-block: var(--sp-section); }
.section-alt { background: var(--bg-2); border-block: 1px solid var(--border); }

.section-head { margin-bottom: clamp(28px, 4vw, 44px); max-width: 68ch; }
.section-head p { margin: 14px 0 0; color: var(--text-dim); }

.section-more { margin: 38px 0 0; }
.section-more a { font-weight: 600; }

.page-head {
  padding-block: clamp(44px, 7vw, 80px) clamp(28px, 4vw, 44px);
  background:
    radial-gradient(900px 400px at 10% -20%, var(--glow-3), transparent 62%),
    var(--bg);
  border-bottom: 1px solid var(--border);
  position: relative;
  overflow: hidden;
}
.page-head > .wrap { position: relative; z-index: 1; }
.page-head .lede { margin-top: 16px; }

/* Per-page civil-drawing motif (profile, survey points, striping, ...), injected
   by build.py from templates\motifs\, generated by tools\gen_motifs.py. Same
   visual system as .hero-motif: token-themed strokes, one teal accent element,
   non-scaling strokes. The banner is short and wide, the opposite of the hero,
   so the drawing is sized by the banner's height and pinned right. The mask
   fades its left edge out so it never ends in a hard line under the title. */
.page-motif {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: auto;
  max-width: 70vw;
  z-index: 0;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 45%);
          mask-image: linear-gradient(to right, transparent, #000 45%);
}
.page-motif path {
  fill: none;
  vector-effect: non-scaling-stroke;
  stroke-linejoin: round;
  stroke-linecap: round;
}
.page-motif-grid  { stroke: var(--border); stroke-width: 1; opacity: 0.45; }
.page-motif-line  { stroke: var(--border-2); stroke-width: 1; opacity: 0.6; }
.page-motif-dash  { stroke: var(--border-2); stroke-width: 1; opacity: 0.6; stroke-dasharray: 6 5; }
.page-motif-index { stroke: var(--teal-bright); stroke-width: 2; opacity: 0.32; }
.notfound { min-height: 52vh; }

/* ── Project cards ────────────────────────────────────────────────────── */

.grid {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 320px), 1fr));
}

.card {
  display: flex; flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
  position: relative;
  transition: border-color .18s ease, transform .18s ease, box-shadow .18s ease;
}
.card::before {
  content: "";
  position: absolute; inset: 0 0 auto 0; height: 3px;
  background: var(--cat);
  z-index: 2;
}

.card-link { color: inherit; }
.card-link:hover {
  text-decoration: none;
  transform: translateY(-3px);
  border-color: var(--border-2);
  box-shadow: var(--shadow);
}

.card-body {
  padding: 20px 22px 22px;
  display: flex; flex-direction: column; flex: 1;
}

.card-top {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; margin-bottom: 10px;
}
.card-top h3 { letter-spacing: -0.015em; }
.card-title { min-width: 0; }

/* Internal codename under the public title. Only rendered when the two differ. */
.code-name {
  display: block;
  margin-top: 3px;
  font-size: 0.72rem; font-weight: 550;
  letter-spacing: 0.06em;
  color: var(--text-faint);
}
.row-main h4 .code-name { display: inline; margin: 0 0 0 8px; }
.detail-head .code-name { margin: -4px 0 0; font-size: 0.8125rem; }

.cat-label {
  flex: none;
  font-size: 0.7rem; font-weight: 650;
  letter-spacing: 0.07em; text-transform: uppercase;
  color: var(--cat);
}

.blurb { margin: 0 0 16px; color: var(--text-dim); font-size: 0.9375rem; flex: 1; }

.chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }

.chip {
  font-size: 0.72rem; font-weight: 550;
  padding: 4px 9px;
  border-radius: 999px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  color: var(--text-dim);
  white-space: nowrap;
}
.chips-lg .chip { font-size: 0.8125rem; padding: 6px 13px; }

.badge {
  align-self: flex-start;
  font-size: 0.7rem; font-weight: 650;
  letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--text-faint);
}

.card-more {
  padding: 0 22px 20px;
  font-size: 0.875rem; font-weight: 600;
  color: var(--cat);
}
.gallery-card .card-more { color: var(--teal-bright); }

/* Screenshot slot. Omitted by build.py when the image file is absent, so there is
   no placeholder state to style. A card or detail page with no screenshot simply
   starts at its text. */

.shot {
  aspect-ratio: 16 / 10;
  background: var(--bg-3);
  border-bottom: 1px solid var(--border);
  overflow: hidden;
}
.shot img { width: 100%; height: 100%; object-fit: cover; }

/* Fallback tile for a project with no screenshot yet (see render_shot() in
   build.py). --cat is already set by the ancestor .card/.detail wrapper. */
.shot-fallback {
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg,
    color-mix(in srgb, var(--cat) 16%, var(--bg-3)) 0%, var(--bg-3) 100%);
}
.shot-fallback-mark {
  font-family: var(--font-display);
  font-size: clamp(2.5rem, 9vw, 4.5rem);
  font-weight: 650;
  letter-spacing: -0.01em;
  color: color-mix(in srgb, var(--cat) 55%, var(--text-faint));
  user-select: none;
}

/* ── Category cards (home) ────────────────────────────────────────────── */

.cat-grid {
  display: grid;
  gap: var(--gap);
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 300px), 1fr));
}

.cat-card {
  padding: 24px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--cat);
  border-radius: var(--radius);
  background: var(--panel);
}
.cat-card-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; margin-bottom: 12px;
}
.cat-card h3 { color: var(--cat); }
.cat-card p { margin: 0; color: var(--text-dim); font-size: 0.9375rem; }

.count {
  flex: none;
  font-size: 0.75rem; font-weight: 650;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}

/* ── Public Gallery ───────────────────────────────────────────────────── */

.gallery-card {
  border: 1px solid var(--border);
  border-left: 3px solid var(--teal-bright);
  border-radius: var(--radius);
  background: var(--panel);
  overflow: hidden;
}
a.gallery-card {
  display: flex; flex-direction: column;
  color: inherit;
  transition: border-color .18s ease, transform .18s ease, box-shadow .18s ease;
}
a.gallery-card:hover {
  text-decoration: none;
  transform: translateY(-3px);
  border-color: var(--border-2);
  box-shadow: var(--shadow);
}
.gallery-card-body { padding: 24px; }
.gallery-blurb { margin: 8px 0 0; color: var(--text-dim); font-size: 0.9375rem; }
.gallery-by { margin: 14px 0 0; font-size: 0.8125rem; color: var(--text-faint); }

.gallery-empty {
  padding: 24px;
  border-style: dashed;
  border-left-style: dashed;
  background: transparent;
  text-align: center;
}
.gallery-empty p { margin: 0; color: var(--text-faint); font-style: italic; }

.how-to-submit { color: var(--text-dim); padding-left: 1.25em; }
.how-to-submit li { margin-bottom: 8px; }

/* ── Tech Stack ───────────────────────────────────────────────────────── */

.tech-llm-note {
  padding: 24px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--teal-bright);
  border-radius: var(--radius);
  background: var(--panel);
  margin-bottom: clamp(28px, 4vw, 44px);
}
.tech-llm-note h2 { margin-bottom: 8px; }
.tech-llm-note p { margin: 0; color: var(--text-dim); }

#tech-catalog .cat-section h2 {
  color: var(--text);
  font-size: 1.125rem;
  margin-bottom: 14px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
}

.tech-list {
  list-style: none;
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 280px), 1fr));
}
.tech-item {
  padding: 14px 18px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel);
}
.tech-item-head {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px;
}
.tech-name { font-weight: 650; }
.tech-count {
  flex: none;
  font-size: 0.75rem; font-weight: 600;
  color: var(--text-faint);
  font-variant-numeric: tabular-nums;
}
.tech-projects {
  margin: 8px 0 0;
  font-size: 0.8125rem;
  color: var(--text-dim);
}
.tech-projects a { color: var(--teal-bright); }

/* ── ROI Calculator ───────────────────────────────────────────────────── */

.calc-wrap {
  display: grid;
  gap: clamp(28px, 4vw, 44px);
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 340px), 1fr));
  align-items: start;
  padding-block: clamp(28px, 4vw, 44px) var(--sp-section);
}

.calc-field { margin-bottom: 16px; }
.calc-field label {
  display: block;
  font-size: 0.8125rem; font-weight: 600;
  color: var(--text-dim);
  margin-bottom: 6px;
}
.calc-field input, .calc-field textarea {
  font: inherit;
  width: 100%;
  padding: 10px 14px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel);
  color: var(--text);
}
.calc-field input { font-variant-numeric: tabular-nums; }
.calc-field textarea { resize: vertical; }
.calc-field input::placeholder, .calc-field textarea::placeholder { color: var(--text-faint); }
.calc-field input:focus, .calc-field textarea:focus { border-color: var(--teal); outline: none; }

.calc-output {
  padding: 24px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--teal-bright);
  border-radius: var(--radius);
  background: var(--panel);
}

.calc-stats { display: grid; gap: 14px; }
.calc-stats > div {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px;
  padding-bottom: 14px;
  border-bottom: 1px solid var(--border);
}
.calc-stats > div:last-child { border-bottom: none; padding-bottom: 0; }
.calc-stats dt { font-size: 0.8125rem; color: var(--text-dim); }
.calc-stats dd {
  margin: 0;
  font-size: 1.125rem; font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.calc-disclaimer { margin: 18px 0 0; font-size: 0.8125rem; color: var(--text-faint); }

/* ── Suggestions ──────────────────────────────────────────────────────── */

.suggestion-form { max-width: 52ch; }
.suggestion-form-status {
  margin: 0 0 14px;
  font-size: 0.875rem;
  color: var(--text-dim);
  min-height: 1.3em;
}
.suggestion-form-status.is-error { color: var(--gold); }

.suggestions-status { padding-block: 8px 4px; color: var(--text-dim); }

.suggestions-list {
  list-style: none;
  display: grid;
  gap: 12px;
  margin-top: clamp(20px, 3vw, 32px);
}
.suggestion-item {
  padding: 16px 20px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--teal-bright);
  border-radius: var(--radius);
  background: var(--panel);
}
.suggestion-item-head {
  display: flex; align-items: flex-start; justify-content: space-between;
  gap: 14px;
}
.suggestion-title { font-size: 1.0625rem; }
.suggestion-description { margin: 8px 0 0; color: var(--text-dim); font-size: 0.9375rem; }
.suggestion-meta { margin: 10px 0 0; font-size: 0.8125rem; color: var(--text-faint); }

.suggestion-added-badge {
  display: inline-block;
  margin-left: 8px;
  padding: 2px 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--teal-bright) 20%, transparent);
  color: var(--teal-bright);
  font-size: 0.6875rem; font-weight: 650;
  letter-spacing: 0.05em; text-transform: uppercase;
}

.suggestion-vote {
  flex: none;
  display: inline-flex; align-items: center; gap: 4px;
  padding: 7px 13px;
  border: 1px solid var(--border-2);
  border-radius: 999px;
  background: var(--panel-2);
  color: var(--text);
  font: inherit;
  font-size: 0.8125rem; font-weight: 650;
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: background .15s ease, border-color .15s ease;
}
.suggestion-vote:hover { background: var(--panel); border-color: var(--teal-bright); }
.suggestion-vote.is-voted {
  border-color: var(--teal-bright);
  color: var(--teal-bright);
  cursor: default;
}
.suggestion-vote:disabled { opacity: 0.7; cursor: default; }

/* ── The Process ──────────────────────────────────────────────────────── */

.process-disclaimer {
  padding: 18px 20px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--teal-bright);
  border-radius: var(--radius);
  background: var(--panel);
  color: var(--text-dim);
  font-size: 0.9375rem;
  font-style: italic;
  margin-bottom: clamp(28px, 4vw, 44px);
}

.process-case { margin-bottom: clamp(36px, 5vw, 56px); }

.process-beats { display: grid; gap: 14px; margin: 20px 0; }
.process-beat {
  padding: 16px 20px;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--panel);
}
.process-prompt {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.875rem;
  color: var(--text);
  margin: 0 0 8px;
}
.process-note { margin: 0; font-size: 0.8125rem; color: var(--text-faint); }

.process-outcome { color: var(--text-dim); }
.process-link { margin-top: 8px; }
.process-link a { font-weight: 600; }

/* ── Sample Prompts ───────────────────────────────────────────────────── */

.prompt-list { display: grid; gap: 14px; margin-top: 16px; }
.prompt-card {
  padding: 18px 20px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--teal-bright);
  border-radius: var(--radius);
  background: var(--panel);
}
.prompt-template {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.9375rem;
  color: var(--text);
  margin: 0 0 10px;
}
.prompt-note { margin: 0; font-size: 0.8125rem; color: var(--text-dim); }

/* ── Catalog: filter bar + rows ───────────────────────────────────────── */

.filter-bar {
  display: flex; flex-wrap: wrap; align-items: center;
  justify-content: space-between; gap: 16px;
  padding-block: 28px 8px;
}

.filters { display: flex; flex-wrap: wrap; gap: 8px; }

.filter {
  font: inherit;
  font-size: 0.8125rem; font-weight: 600;
  padding: 8px 14px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text-dim);
  cursor: pointer;
  transition: background .15s ease, color .15s ease, border-color .15s ease;
}
.filter:hover { background: var(--panel-2); color: var(--text); }
.filter.is-active {
  background: color-mix(in srgb, var(--cat) 20%, transparent);
  border-color: var(--cat);
  color: var(--text);
}
.filter .count { margin-left: 5px; }

.search-wrap { flex: 1 1 240px; max-width: 340px; }

#search {
  font: inherit;
  width: 100%;
  padding: 10px 15px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--text);
}
#search::placeholder { color: var(--text-faint); }
#search:focus { border-color: var(--teal); outline: none; }

.no-results { padding: 40px 0; color: var(--text-dim); }

.cat-section { padding-block: clamp(32px, 5vw, 52px); }
.cat-section h2 { color: var(--cat); scroll-margin-top: 84px; }
.cat-blurb { margin: 12px 0 26px; color: var(--text-dim); max-width: 74ch; }

.rows { list-style: none; display: grid; gap: 10px; }

.row {
  display: flex; flex-wrap: wrap; align-items: flex-start;
  justify-content: space-between; gap: 16px;
  padding: 18px 22px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--cat);
  border-radius: var(--radius-s);
  background: var(--panel);
  transition: background .15s ease, border-color .15s ease;
}
.row:hover { background: var(--panel-2); }

.row-main { flex: 1 1 320px; min-width: 0; }
.row-main h4 { margin-bottom: 5px; }
.row-main h4 a { color: var(--text); }
.row-main h4 a:hover { color: var(--cat); }
.row-main p { margin: 0; color: var(--text-dim); font-size: 0.9375rem; }

.row-meta {
  display: flex; flex-direction: column; align-items: flex-end; gap: 6px;
  flex: 0 1 auto;
}
.row-meta .chips { margin: 0; justify-content: flex-end; }

/* ── Project detail ───────────────────────────────────────────────────── */

.detail { padding-block: clamp(28px, 5vw, 48px) var(--sp-section); }

.crumbs {
  display: flex; align-items: center; gap: 9px;
  font-size: 0.875rem; color: var(--text-faint);
  margin-bottom: 26px;
}

.detail-head { max-width: 74ch; margin-bottom: 34px; }
.detail-tags {
  display: flex; align-items: center; gap: 14px;
  margin-bottom: 14px;
}
.detail-head h1 { font-size: clamp(1.875rem, 4.5vw, 2.75rem); }

.detail .shot {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  margin-bottom: clamp(32px, 5vw, 52px);
  box-shadow: var(--shadow);
}

/* Inline post illustrations (e.g. the-genie): unlike .shot, these vary a lot
   in aspect ratio, so there's no fixed aspect-ratio/object-fit crop here. */
.detail .fable-img {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  margin-bottom: clamp(24px, 4vw, 40px);
  overflow: hidden;
}
.detail .fable-img img { display: block; width: 100%; height: auto; }

/* A caption paragraph directly under a .fable-img (e.g. a quoted couplet)
   reads as a plain markdown paragraph on purpose, not raw HTML: the audio
   narration in gen_audio.py skips raw-HTML lines, and this text needs to be
   spoken. The tight coupling to the image above is done with :has() instead
   of a caption-specific class. */
.detail .fable-img:has(+ p) { margin-bottom: 6px; }
.detail .fable-img + p {
  text-align: center;
  font-style: italic;
  color: var(--text-dim);
  margin: 0 0 clamp(24px, 4vw, 40px);
}

.detail-body {
  display: grid;
  gap: clamp(28px, 4vw, 44px);
  max-width: 78ch;
}
.detail-block h2 {
  font-size: 1.25rem;
  margin-bottom: 14px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
}
.detail-block p { color: var(--text-dim); margin: 0; }

.features { list-style: none; display: grid; gap: 11px; }
.features li {
  position: relative;
  padding-left: 26px;
  color: var(--text-dim);
}
.features li::before {
  content: "";
  position: absolute; left: 2px; top: 0.58em;
  width: 9px; height: 9px;
  border-radius: 2px;
  background: var(--cat);
}

.detail-nav {
  display: flex; flex-wrap: wrap; gap: 14px; justify-content: space-between;
  margin-top: var(--sp-section);
  padding-top: 28px;
  border-top: 1px solid var(--border);
}
.detail-nav a {
  display: flex; flex-direction: column; gap: 4px;
  padding: 16px 22px;
  border: 1px solid var(--border);
  border-radius: var(--radius-s);
  background: var(--panel);
  color: var(--text);
  flex: 1 1 220px;
  transition: background .15s ease, border-color .15s ease;
}
.detail-nav a:hover {
  text-decoration: none;
  background: var(--panel-2);
  border-color: var(--border-2);
}
.detail-next { align-items: flex-end; text-align: right; }
.detail-nav .dir {
  font-size: 0.72rem; font-weight: 650;
  letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--text-faint);
}
.detail-nav .nm { font-weight: 600; }

/* ── Prose (about) ────────────────────────────────────────────────────── */

.prose {
  padding-block: clamp(36px, 6vw, 64px) var(--sp-section);
  max-width: 76ch;
}
.prose p { color: var(--text-dim); font-size: 1.0625rem; }
.prose h2 {
  font-size: 1.375rem;
  margin: clamp(34px, 5vw, 52px) 0 16px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border);
}

.approach { list-style: none; display: grid; gap: 13px; margin-bottom: 1em; }
.approach li {
  position: relative;
  padding-left: 26px;
  color: var(--text-dim);
}
.approach li::before {
  content: "";
  position: absolute; left: 2px; top: 0.58em;
  width: 9px; height: 9px; border-radius: 2px;
  background: var(--teal-bright);
}

/* ── Blog ─────────────────────────────────────────────────────────────── */

.post-list {
  list-style: none;
  max-width: 76ch;
  padding-block: clamp(30px, 5vw, 52px) 0;
}
.post-item {
  padding-bottom: 26px;
  margin-bottom: 26px;
  border-bottom: 1px solid var(--border);
}
.post-item:last-child { border-bottom: 0; }
.post-item a { color: inherit; }
.post-item a:hover { color: var(--teal-bright); text-decoration: none; }
.post-item h3 { font-size: 1.25rem; letter-spacing: -0.015em; margin-bottom: 6px; }
.post-item p { color: var(--text-dim); margin: 0; }

.post-meta {
  font-size: 0.8125rem;
  color: var(--text-faint);
  margin: 0 0 8px;
}

/* Only ever rendered by `build.py --drafts`, for local reading. Its presence in
   dist\ is also what makes tools/package_dist.py refuse to build an archive. */
.draft-flag {
  display: inline-block;
  vertical-align: middle;
  margin-right: 10px;
  padding: 3px 9px;
  border-radius: 4px;
  background: #8a5a00;
  color: #fff;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}
.detail-head .post-meta { margin-top: 6px; }

.post-audio { margin-top: 16px; }
.post-audio audio { width: 100%; max-width: 420px; height: 40px; }

.archive-note {
  max-width: 76ch;
  margin: 0 0 var(--sp-section);
  padding-top: 22px;
  border-top: 1px solid var(--border);
  font-size: 0.9375rem;
  color: var(--text-faint);
}

/* Essay page (why-ai). Reuses .prose; adds a pull quote and a sign-off line. */

.essay-block > *:last-child { margin-bottom: 0; }

.pull {
  margin: 22px 0;
  padding: 4px 0 4px 20px;
  border-left: 3px solid var(--teal-bright);
}
.pull p {
  font-size: 1.125rem;
  font-style: italic;
  color: var(--text);
  margin-bottom: 8px;
}
.pull footer {
  font-size: 0.875rem;
  color: var(--text-faint);
}

.sign-off {
  margin: clamp(30px, 5vw, 44px) 0 22px;
  font-weight: 650;
  color: var(--text) !important;
}

/* ── Contact ──────────────────────────────────────────────────────────── */

.contact-inner { max-width: 66ch; }
.contact-inner p { color: var(--text-dim); margin-top: 14px; }

/* ── Footer ───────────────────────────────────────────────────────────── */

.site-foot {
  background: var(--bg-2);
  border-top: 1px solid var(--border);
  padding-block: clamp(36px, 5vw, 56px) 32px;
  font-size: 0.875rem;
}

.foot-top {
  display: flex; flex-wrap: wrap; gap: 20px;
  align-items: flex-start; justify-content: space-between;
  padding-bottom: 26px;
  border-bottom: 1px solid var(--border);
}
.foot-brand { display: flex; flex-direction: column; gap: 4px; }
.foot-brand strong { color: var(--text); font-size: 1rem; }
.foot-brand span { color: var(--text-faint); }

.foot-nav { display: flex; flex-wrap: wrap; gap: 18px; }
.foot-nav a { color: var(--text-dim); }
.foot-nav a:hover { color: var(--teal-bright); }

.disclaimer {
  margin: 24px 0 0;
  max-width: 92ch;
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--text-faint);
}
.copyright { margin: 18px 0 0; color: var(--text-faint); font-size: 0.8125rem; }

/* ── Responsive ───────────────────────────────────────────────────────── */

@media (max-width: 640px) {
  .head-inner { min-height: 58px; }
  .brand span { font-size: 0.9375rem; }
  .site-nav a { padding: 7px 10px; }
  .hero-stats > div { flex-basis: 100%; border-right: none; border-bottom: 1px solid var(--border); }
  .hero-stats > div:last-child { border-bottom: none; }
  .row-meta { align-items: flex-start; }
  .row-meta .chips { justify-content: flex-start; }
  .detail-next { align-items: flex-start; text-align: left; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .card-link:hover, .btn:hover { transform: none; }
}

@media print {
  .site-head, .site-foot, .filter-bar, .detail-nav, .hero-actions { display: none; }
  body { background: #fff; color: #000; }
}
