/* santiagostudios — live-surface build. Page classes, with two deliberate
   exceptions that override engine-owned selectors and are marked as such where
   they appear:
     1. `.sc-failed [data-sc-cue|-in|-stagger]` (below) — the engine-failure
        reveal, which only exists because the engine is dead.
     2. `[data-sc-act="pin"] > [data-sc-stage]` (bottom of file) — overrides
        .sc-stage's `overflow: clip` for WCAG 1.4.4 at 150% text. scrollcraft.css
        says of that block "restyle your own markup, not these"; this one is
        overridden knowingly, with the measurements in the comment there.
   The engine itself carries one local patch — see the provenance block at the
   top of scrollcraft.js. */

:root {
  --sc-canvas:      #F2F1EC;
  --sc-surface:     #FBFAF7;
  --sc-ink:         #191A16;
  --sc-ink-soft:    #5F6156;
  --sc-accent:      #1743D6;
  --sc-accent-ink:  #F5F4EF;
  --sc-font-display: "Archivo", system-ui, sans-serif;
  --sc-font-text:    "Archivo", system-ui, sans-serif;
  --sc-font-mono:    "IBM Plex Mono", ui-monospace, monospace;
  --sc-shadow-color: 75 8% 24%;
  --sc-r-sm: 6px; --sc-r-md: 6px; --sc-r-lg: 6px; --sc-r-pill: 6px;

  /* page-own tokens */
  --hairline: color-mix(in oklab, var(--sc-ink) 16%, transparent);
  --panel-head: color-mix(in oklab, var(--sc-ink) 5%, var(--sc-surface));
  --ok:   #2E6B4F;
  --held: #8A4B12;
}

html { color-scheme: light; }
body { background: var(--sc-canvas); color: var(--sc-ink); }
::selection { background: var(--sc-accent); color: var(--sc-accent-ink); }

/* Engine-failure fallback. scrollcraft.css hides every cued and revealed element
   (opacity: 0) and the engine is what brings them back, per frame, from script.
   The <noscript> block in the head only covers scripting being *disabled* — if
   scrollcraft.js 404s or mount() throws, scripting is enabled, that block never
   applies, and the page ships a blank hero at 200 OK. The inline mount call sets
   .sc-failed on <html> when that happens; this restores everything the engine
   would have driven. Same declarations as the <noscript> rule, deliberately —
   both blocks must stay in step, so any rule added to one goes in the other. */
.sc-failed [data-sc-cue],
.sc-failed [data-sc-in],
.sc-failed [data-sc-stagger] > * { opacity: 1 !important; transform: none !important; }

/* Restoring opacity is not enough on its own, and on two acts it actively hurt.
   Both degraded paths were written and neither was ever loaded: the rules above
   un-hide content that three separate layout mechanisms then make unreadable or
   unreachable. Each fix below pairs with one of them.

   1. Act 3 stacks its three lines at one coordinate (.turn-lines .sc-lede is
      absolutely positioned) and lets opacity pick which one is showing. Force
      all three back to opacity 1 and they render superimposed — the safety net
      turning a legible act into a smear. Put them back in flow instead.
   2. The modules rail is width: max-content (~97rem) inside body's
      overflow-x: clip, and its only two escape hatches are the engine's pan
      transform and the reduced-motion rule in scrollcraft.css. With the engine
      dead and no reduced-motion preference, two of the three module cards are
      off-screen with no way to reach them. Hand the travel back as an ordinary
      scroll region — the same floor scrollcraft.css gives reduced-motion
      readers, and the same fix the pinned stages already got further down. */
.sc-failed .turn-lines { min-height: 0; display: grid; gap: var(--sc-3); }
.sc-failed .turn-lines .sc-lede { position: static; }
.sc-failed [data-sc-act="pan"] > [data-sc-stage] {
  overflow-x: auto; overflow-y: hidden; overscroll-behavior-x: contain;
}

/* ---------------------------------------------------------------- chrome -- */
.chrome {
  position: fixed; inset: 0 0 auto 0; z-index: var(--sc-z-chrome);
  display: flex; align-items: center; gap: var(--sc-5);
  padding: var(--sc-3) var(--sc-gutter);
  font-family: var(--sc-font-mono); font-size: var(--sc-t-xs);
  background: color-mix(in srgb, var(--sc-canvas) 88%, transparent);
  backdrop-filter: blur(6px);
  border-bottom: 1px solid var(--hairline);
}
.chrome__mark { font-family: var(--sc-font-display); font-weight: 700; letter-spacing: var(--sc-track-snug); font-size: var(--sc-t-sm); color: var(--sc-ink); text-decoration: none; }
.chrome__proc { color: var(--sc-ink-soft); }
.chrome__tabs { display: flex; gap: var(--sc-4); margin-inline: auto; }
.chrome__tabs a { color: var(--sc-ink-soft); text-decoration: none; padding: 2px 4px; }
.chrome__tabs a:hover { color: var(--sc-ink); }
.chrome__state { display: inline-flex; align-items: center; gap: var(--sc-2); }
.chip {
  display: inline-block; padding: 2px 8px; border-radius: var(--sc-r-sm);
  border: 1px solid var(--hairline); font-weight: 500;
  background: var(--sc-surface); color: var(--sc-ink); min-width: 7.5ch; text-align: center;
}
.chip[data-state="RUNNING"]  { border-color: var(--ok); color: var(--ok); }
.chip[data-state="HELD"]     { border-color: var(--held); color: var(--held); }
.chip[data-state="SHIPPED"]  { border-color: var(--sc-accent); background: var(--sc-accent); color: var(--sc-accent-ink); }
.chrome__sim { color: var(--sc-ink-soft); border-left: 1px solid var(--hairline); padding-left: var(--sc-3); }

/* ---------------------------------------------------------------- panels -- */
.panel {
  background: var(--sc-surface); border: 1px solid var(--hairline);
  border-radius: var(--sc-r-sm); box-shadow: var(--sc-e1);
  display: flex; flex-direction: column; overflow: hidden;
}
.panel__head {
  display: flex; justify-content: space-between; align-items: center; gap: var(--sc-4);
  padding: var(--sc-2) var(--sc-4);
  font-family: var(--sc-font-mono); font-size: var(--sc-t-xs);
  color: var(--sc-ink-soft);
  background: var(--panel-head); border-bottom: 1px solid var(--hairline);
  white-space: nowrap;
}
.panel__head strong { color: var(--sc-ink); font-weight: 500; }
.panel__body { padding: var(--sc-4); }

/* -------------------------------------------------------------- act 1 hero */
.hero-stage { display: grid; align-content: center; gap: var(--sc-6); padding: calc(var(--sc-9) + var(--sc-4)) var(--sc-gutter) var(--sc-6); max-width: var(--sc-maxw); margin-inline: auto; width: 100%; }
.hero-copy h1 {
  font-family: var(--sc-font-display); font-weight: 700;
  font-size: clamp(1.9rem, 1.4rem + 2.4vw, 2.9rem);
  line-height: var(--sc-leading-tight); letter-spacing: var(--sc-track-snug);
  text-wrap: balance; margin: 0 0 var(--sc-3);
}
.hero-copy p { max-width: 48ch; margin: 0; color: var(--sc-ink-soft); }
.console { display: grid; grid-template-columns: minmax(0, 7fr) minmax(0, 5fr); gap: var(--sc-4); align-items: stretch; min-height: 46vh; }
.console .panel__body, .console .log { flex: 1; }
.board { height: 100%; }

/* board */
.board { border-collapse: collapse; width: 100%; font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); }
.board th { text-align: left; font-weight: 500; color: var(--sc-ink-soft); padding: var(--sc-2) var(--sc-3); border-bottom: 1px solid var(--hairline); }
.board td { padding: var(--sc-2) var(--sc-3); border-bottom: 1px solid color-mix(in oklab, var(--sc-ink) 8%, transparent); vertical-align: top; }
.board tr:last-child td { border-bottom: 0; }
.board .t-task { color: var(--sc-ink); }
.board .t-age  { color: var(--sc-ink-soft); }
.status { display: inline-block; padding: 1px 7px; border-radius: var(--sc-r-sm); border: 1px solid var(--hairline); font-size: 0.92em; white-space: nowrap; }
.status[data-s="QUEUED"]   { color: var(--sc-ink-soft); }
.status[data-s="RUNNING"]  { color: var(--ok); border-color: color-mix(in oklab, var(--ok) 45%, transparent); }
.status[data-s="DONE"]     { color: var(--sc-accent-ink); background: var(--ok); border-color: var(--ok); }
.status[data-s="WAITING"]  { color: var(--held); border-color: var(--held); }
.status[data-s="SENT"]     { color: var(--sc-accent-ink); background: var(--sc-accent); border-color: var(--sc-accent); }
.board .t-who { color: var(--sc-ink-soft); }
.board .t-who[data-agent] { color: var(--sc-accent); }

/* log */
.log { font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); line-height: 1.7; margin: 0; padding: var(--sc-3) var(--sc-4); list-style: none; min-height: 14em; }
.log li { opacity: 0; transform: translateY(6px); transition: opacity 300ms var(--sc-ease-out), transform 300ms var(--sc-ease-out); }
.log li.on { opacity: 1; transform: none; }
.log .lt { color: var(--sc-ink-soft); margin-right: 1ch; }
.log .lk { color: var(--sc-accent); }
.log .lw { color: var(--held); }

/* ------------------------------------------------------------- act 2 cost */
.cost { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); gap: var(--sc-7); align-items: end; }
.cost h2 { font-size: var(--sc-t-xl); font-weight: 700; letter-spacing: var(--sc-track-snug); margin: 0 0 var(--sc-4); text-wrap: balance; }
.cost .sc-body { margin-bottom: var(--sc-5); }
.cost-total { font-family: var(--sc-font-mono); font-size: var(--sc-t-sm); color: var(--sc-ink-soft); }
.cost-total .sc-nums { color: var(--sc-ink); font-size: var(--sc-t-lg); font-weight: 500; }
.bars { width: 100%; display: block; height: auto; }
.bars text { font-family: var(--sc-font-mono); font-size: 11.5px; fill: var(--sc-ink-soft); }
.bars .bar { fill: var(--sc-ink); }
.bars .bar--hot { fill: var(--sc-accent); }
.bars .val { fill: var(--sc-ink); }

/* ------------------------------------------------------------- act 3 turn */
.turn-stage { display: grid; align-content: center; gap: var(--sc-5); padding: calc(var(--sc-9) + var(--sc-4)) var(--sc-gutter) var(--sc-6); max-width: var(--sc-maxw); margin-inline: auto; width: 100%; }
.turn-lines { min-height: 3.2em; position: relative; }
.turn-lines .sc-lede { position: absolute; inset: 0 auto auto 0; margin: 0; max-width: 34ch; font-weight: 600; }

/* ------------------------------------------------------- act 4 governance */
.gov { max-width: 60rem; }
.gov h2 { font-size: var(--sc-t-lg); font-weight: 600; margin: 0 0 var(--sc-6); color: var(--sc-ink); }
.gov dl { display: grid; grid-template-columns: max-content 1fr; column-gap: var(--sc-7); row-gap: var(--sc-4); margin: 0; }
.gov dt { font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); color: var(--sc-accent); padding-top: 2px; }
.gov dd { margin: 0; color: var(--sc-ink-soft); max-width: 52ch; }

/* -------------------------------------------------------------- act 5 gate */
.act--dark { --sc-ink: #F2F1EC; --sc-ink-soft: #A9AA9E; --sc-accent: #8FA5FF; --sc-accent-ink: #121310;
  --hairline: color-mix(in oklab, #F2F1EC 18%, transparent);
  --panel-head: #1B1C18; --sc-surface: #171814; --held: #E0A055; --ok: #7FC9A4;
  color: var(--sc-ink); }
/* grounds are painted on the stage, not drifted: the copy above them is then
   never composited against the wrong ground during stage entry/exit */
.act--dark [data-sc-stage] { background: #121310; }
#contact [data-sc-stage] { background: var(--sc-canvas); }
.gate-stage { display: grid; align-content: center; justify-items: center; gap: var(--sc-5); padding: calc(var(--sc-9) + var(--sc-4)) var(--sc-gutter) var(--sc-6); width: 100%; }
.gate-copy { text-align: center; }
.gate-copy .sc-lede { color: var(--sc-ink); font-weight: 600; margin: 0; }
.gate { width: min(44rem, 100%); box-shadow: var(--sc-e3); }
.gate__body { display: grid; gap: var(--sc-4); padding: var(--sc-5); }
.gate__action { font-size: var(--sc-t-lg); font-weight: 600; line-height: var(--sc-leading-tight); margin: 0; }
.gate__facts { display: flex; flex-wrap: wrap; gap: var(--sc-2) var(--sc-5); margin: 0; padding: 0; list-style: none;
  font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); color: var(--sc-ink-soft); }
.gate__facts b { color: var(--sc-ink); font-weight: 500; }
.gate__facts .no { color: var(--held); }
.approve {
  justify-self: start; margin-top: var(--sc-2);
  font: 500 var(--sc-t-sm) var(--sc-font-mono);
  color: var(--sc-accent-ink); background: var(--sc-accent);
  border: 0; border-radius: var(--sc-r-sm); padding: var(--sc-3) var(--sc-6);
  cursor: pointer; transition: transform 140ms var(--sc-ease-out), opacity 140ms var(--sc-ease-out);
}
.approve:hover { opacity: 0.92; }
.approve:active { transform: translateY(1px); }
.approve[disabled] { opacity: 0.55; cursor: default; }
.gate__progress { display: none; font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); color: var(--sc-ink-soft); }
.gate.is-approved .gate__progress { display: block; }
.gate.is-approved .approve { display: none; }
.ledger {
  width: min(44rem, 100%); font-family: var(--sc-font-mono); font-size: var(--sc-t-xs);
  color: var(--sc-ink-soft); border-top: 1px solid var(--hairline); padding-top: var(--sc-3);
  display: flex; justify-content: space-between; gap: var(--sc-4); flex-wrap: wrap;
}
.ledger b { color: var(--sc-ink); font-weight: 500; }
.gate-outcome { min-height: 1.6em; font-family: var(--sc-font-mono); font-size: var(--sc-t-sm); color: var(--sc-ink); text-align: center; }
/* The approve button is live from the moment the act pins — console.js says so
   itself, because an anchor jump to #gate lands at p≈0 with the button fully on
   screen. This line's cue holds it at opacity 0 until p≈0.66, so an early
   approver had the sentence describing what they just did written into an
   invisible element, present in the accessibility tree and announced, but not
   shown. Nothing re-evaluates a cue on click, so it stayed invisible until they
   scrolled most of an act further. The approve handler adds .is-forced;
   !important is what beats the engine's per-frame inline opacity. */
.gate-outcome.is-forced { opacity: 1 !important; transform: none !important; }

/* ----------------------------------------------------------- act 6 modules */
#modules [data-sc-stage] { display: grid; align-content: center; }
.rail { display: flex; align-items: center; gap: var(--sc-5); padding-inline: var(--sc-gutter); width: max-content; }
.rail .module { align-self: stretch; }
.rail__lead { width: clamp(20rem, 30vw, 26rem); display: grid; align-content: center; gap: var(--sc-4); flex: none; }
.rail__lead h2 { font-size: var(--sc-t-xl); font-weight: 700; letter-spacing: var(--sc-track-snug); margin: 0; text-wrap: balance; }
.module { width: clamp(19rem, 27vw, 24rem); flex: none; }
.module .panel__body { display: grid; gap: var(--sc-3); padding: var(--sc-5); }
.module h3 { font-size: var(--sc-t-lg); font-weight: 600; margin: 0; letter-spacing: var(--sc-track-snug); }
.module p { margin: 0; color: var(--sc-ink-soft); font-size: var(--sc-t-sm); line-height: 1.55; }
.rail__note { width: clamp(14rem, 20vw, 18rem); flex: none; display: grid; align-content: center; }
.rail__note p { font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); color: var(--sc-ink-soft); margin: 0; }
/* The ease-in used to start at opacity 0.55. Element opacity composites the
   panel and its text together onto the canvas, so at 0.55 the module copy
   (#5F6156 on #FBFAF7, 5.6:1 opaque) resolved to about 2.4:1 and did not cross
   4.5:1 until p ≈ 0.46. On a 3.6-viewport act that is roughly 1.7 screens of
   scrolling during which the only place the page says what the studio sells sat
   below WCAG 1.4.3 AA — and the rule is gated on no-preference, i.e. nearly
   every visitor. The floor is now 0.9, which measures 4.8:1 at p=0. The fade is
   shallower; the 10px rise carries the movement instead. Content that is
   readable at all must be readable to AA, and a decorative ramp is not a reason
   to park text under the threshold for a screen and a half. */
@media (prefers-reduced-motion: no-preference) {
  .rail > *:not(:first-child) {
    opacity: calc(0.9 + var(--sc-p, 1) * 0.1);
    transform: translateY(calc((1 - var(--sc-p, 1)) * 10px));
  }
}

/* -------------------------------------------------------------- act 7 close */
.close-stage { display: grid; align-content: center; gap: var(--sc-6); padding: calc(var(--sc-9) + var(--sc-4)) var(--sc-gutter) var(--sc-4); max-width: var(--sc-maxw); margin-inline: auto; width: 100%; min-height: 100%; grid-template-rows: 1fr auto; }
.close-grid { display: grid; grid-template-columns: minmax(0, 5fr) minmax(0, 6fr); gap: var(--sc-7); align-items: start; align-self: center; }
.close-grid h2 { font-size: var(--sc-t-xl); font-weight: 700; letter-spacing: var(--sc-track-snug); margin: 0 0 var(--sc-4); text-wrap: balance; }
.form { display: grid; gap: var(--sc-4); padding: var(--sc-5); }
.form label { font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); color: var(--sc-ink-soft); display: grid; gap: var(--sc-2); }
.form input, .form textarea {
  font: 400 var(--sc-t-sm) var(--sc-font-text); color: var(--sc-ink);
  background: var(--sc-canvas); border: 1px solid var(--hairline); border-radius: var(--sc-r-sm);
  padding: var(--sc-3); width: 100%;
}
.form textarea { min-height: 6.5em; resize: vertical; }
.form input:focus-visible, .form textarea:focus-visible { outline: 2px solid var(--sc-accent); outline-offset: 1px; }
.form button {
  justify-self: start; font: 500 var(--sc-t-sm) var(--sc-font-mono);
  color: var(--sc-accent-ink); background: var(--sc-accent); border: 0; border-radius: var(--sc-r-sm);
  padding: var(--sc-3) var(--sc-6); cursor: pointer;
  transition: transform 140ms var(--sc-ease-out), opacity 140ms var(--sc-ease-out);
}
.form button:hover { opacity: 0.92; }
.form button:active { transform: translateY(1px); }
.form-status { font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); color: var(--sc-ink-soft); min-height: 1.4em; margin: 0; }
.form-status.is-ok  { color: var(--ok); }
.form-status.is-err { color: var(--held); }
.foot {
  display: flex; justify-content: space-between; align-items: baseline; gap: var(--sc-4); flex-wrap: wrap;
  border-top: 1px solid var(--hairline); padding-top: var(--sc-4);
  font-family: var(--sc-font-mono); font-size: var(--sc-t-xs); color: var(--sc-ink-soft);
}
.foot b { font-family: var(--sc-font-display); font-weight: 700; color: var(--sc-ink); }

/* ---------------------------------------------------------------- mobile -- */
@media (max-width: 860px) {
  .chrome__tabs, .chrome__proc { display: none; }
  /* The chrome is a non-wrapping flex row and the "simulated run · sample data"
     disclosure is nbsp-bound, so it ran off the right edge of every portrait
     phone — measured overflow 115px at 320w, 76px at 360w, 48px at 390w, still
     11px at 430w. That label is the one the BRIEF's honesty constraints require
     visible at all times, so clipping it is not a cosmetic issue. Let the row
     wrap, and the state cluster wrap again at the narrowest widths. */
  .chrome { flex-wrap: wrap; row-gap: var(--sc-1); }
  .chrome__state { flex-wrap: wrap; row-gap: var(--sc-1); }
  .console, .cost, .close-grid { grid-template-columns: 1fr; }
  .cost { gap: var(--sc-5); }
  .hero-copy h1 { font-size: clamp(1.6rem, 1.2rem + 3.4vw, 2rem); }
  .board .t-age { display: none; }
  .gov dl { grid-template-columns: 1fr; row-gap: var(--sc-2); }
  .gov dd { margin-bottom: var(--sc-3); }
  .log { min-height: 10em; }
  /* The chart is drawn in viewBox user units, so scaling the SVG below 1:1
     shrinks its labels with it: 11.5 units rendered at roughly 6.5 CSS px on a
     390px phone, and being viewBox-relative they ignore the reader's font-size
     setting entirely — there is no browser control that makes them bigger. The
     role="img" label carries the data for assistive tech, so this stranded
     exactly the sighted low-vision reader. Stop scaling below the size the
     labels were drawn for and let the figure scroll instead: same chart, same
     legibility as desktop, and every value still reachable. */
  .cost figure .panel__body { overflow-x: auto; }
  .bars { min-width: 560px; }
}

/* Pinned stages are 100svh with overflow:clip (engine rule). Whenever a stage's
   content is taller than the viewport the clip silently eats the bottom of it:
   the hero's log tail, and on the closing act the contact submit button. Let
   every pinned stage scroll its own content. Where nothing overflows this is a
   no-op and the stage stays sticky.

   Unconditional on purpose. This was keyed to (max-width: 860px),
   (max-height: 760px) — viewport size is the wrong proxy, because content height
   also tracks the reader's font size. Measured at a 20px root font: the hero
   console clipped 165px at 880x770 and 137px at 900x800; at 24px the submit
   button itself clipped 12px at 880x770 and the console clipped even at
   1366x900. All of those sit above both breakpoints, so the escape hatch never
   fired and the clipped content had no way to scroll — WCAG 1.4.4 at 150% text.

   No overscroll-behavior: contain here. A pinned stage fills the viewport, so
   containing the scroll chain would trap the reader inside the stage with
   nowhere to move the pointer; chaining to the page at the stage's end is what
   carries them to the next act. */
[data-sc-act="pin"] > [data-sc-stage] { overflow-x: hidden; overflow-y: auto; }

/* --------------------------------------------------------- reduced motion -- */
@media (prefers-reduced-motion: reduce) {
  .log li { opacity: 1; transform: none; transition: none; }
}
