/* =============================================================================
   OpenMFDA Foundry — chipbuild.css
   Scoped to /how-its-built/ only. Loaded after main.css.

   The scroll-driven layer-print sequence. Everything is prefixed .cb- for two
   reasons: it keeps this page's rules out of every other page's way, and it
   avoids four class names that main.js reaches for on every page —
   .reveal, .detail-section, .tool-card and .hero__scroll. Naming a caption
   .detail-section would hand it to initProgrammaticReveals(), which sets
   opacity:0 on anything below the fold. Do not rename these to match the rest
   of the site's vocabulary without checking main.js first.

   NO-JS BEHAVIOUR IS THE DEFAULT, not a fallback bolted on afterwards. The
   markup is a still frame followed by four stacked captions, which is what a
   crawler and a JS-off reader get. Every rule that turns that into a sticky
   scroll-scrubber is gated behind html.js, matching the html.js .reveal idiom
   already used in main.css section 15.
   ========================================================================== */

:root {
  /* The build-plane cyan has no existing site token. It is duplicated in
     scripts/chipbuild_render.py as FACE — the rendered frames and the legend
     swatch must be the same colour, so change both together. */
  --cb-cyan: #6BD4FF;

  /* Void amber IS the site accent, and matches VOIDC in the render script.
     main.css reserves --color-accent as a background for .badge-phase2 only;
     the exception here is a 10px legend key whose whole job is to name a
     colour that appears in the frames. It is a legend, not decoration. */
  --cb-amber: var(--color-accent);

  --cb-bg: var(--color-navy);
  --cb-ink: var(--color-ice);
  --cb-muted: #A9BCD4;
  --cb-hairline: rgba(240, 247, 250, 0.14);

  /* Scroll distance for the whole sequence: ~15vh per frame plus one screen
     of settle. Only applied under html.js — without JS the stage is just its
     content height. */
  --cb-scroll-vh: 1180;
}


/* -- Intro ----------------------------------------------------------------- */
.cb-intro {
  background: var(--cb-bg);
  color: var(--cb-ink);
  padding-block: var(--sp-24) var(--sp-16);
}

.cb-eyebrow {
  font-family: var(--font-mono);
  font-size: var(--fs-300);
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--cb-cyan);
  margin-bottom: var(--sp-4);
}

.cb-intro h1 {
  font-family: var(--font-serif);
  font-size: clamp(2rem, 5vw, 3.4rem);
  line-height: 1.1;
  letter-spacing: -0.015em;
  color: var(--cb-ink);
  margin-bottom: var(--sp-4);
  max-width: 20ch;
}

.cb-intro h1 em {
  font-style: normal;
  color: var(--cb-cyan);
}

.cb-lede {
  font-size: var(--fs-500);
  color: var(--cb-muted);
  max-width: 52ch;
}

.cb-cue {
  margin-top: var(--sp-12);
  font-family: var(--font-mono);
  font-size: var(--fs-300);
  letter-spacing: 0.1em;
  color: var(--cb-muted);
}

.cb-cue span {
  display: inline-block;
  animation: cb-bob 1.9s ease-in-out infinite;
}

@keyframes cb-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(5px); }
}

/* The cue points at a scroll interaction that only exists with JS. */
.cb-cue { display: none; }
html.js .cb-cue { display: block; }


/* -- Stage ----------------------------------------------------------------- */
.cb-stage {
  background: var(--cb-bg);
  color: var(--cb-ink);
  position: relative;
}

/* .cb-sticky also carries .container, so the two panes stay inside the site's
   width constraint instead of spreading across a wide viewport. */
.cb-sticky {
  padding-block: var(--sp-12);
}

.cb-viewer {
  position: relative;
  width: 100%;
  max-width: 740px;
  margin-inline: auto;
  aspect-ratio: 1 / 1;
}

/* Deliberately does NOT set display. `.cb-viewer canvas` is (0,1,1) and would
   outrank the plain `.cb-canvas` (0,1,0) rule below, so a display:block here
   leaks the canvas into the no-JS layout — a blank box stacked under the still,
   doubling the viewer's height. Each element gets its own display rule instead. */
.cb-viewer canvas,
.cb-viewer img {
  width: 100%;
  height: 100%;
  border-radius: var(--radius);
}

.cb-still { display: block; }

/* Without JS the canvas is never painted, so it must not occupy the frame.
   No crossfade is needed on the way in: frame 0 is the empty build platform,
   which renders as flat --color-navy, and an unpainted canvas is transparent
   over that same navy. The blank canvas and frame 0 are the same picture, so
   there is nothing to hide while the first frame loads. */
.cb-canvas { display: none; }
html.js .cb-canvas { display: block; }
html.js .cb-still { display: none; }

.cb-hud {
  position: absolute;
  left: 4%;
  bottom: 5%;
  font-family: var(--font-mono);
  font-size: var(--fs-300);
  letter-spacing: 0.05em;
  line-height: 1.5;
  color: var(--cb-cyan);
  background: rgba(11, 18, 40, 0.66);
  border: 1px solid rgba(107, 212, 255, 0.28);
  border-radius: var(--radius);
  padding: 0.5rem 0.75rem;
}

.cb-hud b {
  color: var(--color-white);
  font-weight: 600;
}

/* The HUD reports the frame's own layer number. With no JS there is no frame
   index to report, so it would be asserting a build height the still does not
   show. It stays hidden rather than lying. */
.cb-hud { display: none; }
html.js .cb-hud { display: block; }


/* -- Captions -------------------------------------------------------------- */
.cb-caps {
  margin-top: var(--sp-12);
}

.cb-cap + .cb-cap {
  margin-top: var(--sp-12);
}

.cb-cap__n {
  font-family: var(--font-mono);
  font-size: var(--fs-300);
  letter-spacing: 0.14em;
  color: var(--cb-muted);
  margin-bottom: var(--sp-3);
}

.cb-cap h2 {
  font-family: var(--font-serif);
  font-size: clamp(1.3rem, 2.4vw, 1.95rem);
  line-height: 1.18;
  color: var(--cb-ink);
  margin-bottom: var(--sp-3);
}

.cb-cap p {
  color: var(--cb-muted);
  max-width: 46ch;
}

.cb-key {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 2px;
  margin-right: 0.45rem;
  vertical-align: middle;
}

.cb-key--face  { background: var(--cb-cyan); }
.cb-key--void  { background: var(--cb-amber); }

.cb-progress {
  display: none;
  margin-top: var(--sp-8);
  height: 2px;
  max-width: 280px;
  background: rgba(240, 247, 250, 0.14);
  border-radius: 2px;
  overflow: hidden;
}

html.js .cb-progress { display: block; }

.cb-progress i {
  display: block;
  height: 100%;
  width: 0;
  background: var(--cb-cyan);
}


/* -- Scroll-scrubber layout: JS only ---------------------------------------- */
html.js .cb-stage {
  height: calc(var(--cb-scroll-vh) * 1vh);
}

html.js .cb-sticky {
  position: sticky;
  top: 0;
  height: 100vh;
  display: grid;
  grid-template-columns: 1fr;
  align-content: center;
  gap: var(--sp-6);
  padding-block: var(--sp-6);
}

html.js .cb-viewer {
  max-height: 58vh;
  max-width: min(740px, 58vh);
}

html.js .cb-caps {
  margin-top: 0;
  position: relative;
  min-height: 12.5rem;
}

html.js .cb-cap {
  position: absolute;
  inset-inline: 0;
  top: 0;
  opacity: 0;
  transform: translateY(12px);
  transition: opacity 0.4s ease, transform 0.4s ease;
  pointer-events: none;
}

html.js .cb-cap + .cb-cap { margin-top: 0; }

html.js .cb-cap.is-on {
  opacity: 1;
  transform: none;
  pointer-events: auto;
}

@media (min-width: 60em) {
  html.js .cb-sticky {
    /* The viewer earns the wider column: the caption is ~46ch of text and the
       chip's features are small enough that every pixel counts. */
    grid-template-columns: 1.3fr 1fr;
    align-items: center;
    gap: var(--sp-8);
  }

  html.js .cb-viewer {
    max-height: 82vh;
    max-width: min(740px, 82vh);
  }
}


/* -- Outro ----------------------------------------------------------------- */
.cb-outro {
  background: var(--cb-bg);
  color: var(--cb-ink);
  padding-block: var(--sp-16) var(--sp-24);
  border-top: 1px solid var(--cb-hairline);
}

.cb-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--sp-8);
  margin-top: var(--sp-8);
}

.cb-fact__n {
  font-family: var(--font-serif);
  font-size: var(--fs-700);
  color: var(--cb-cyan);
}

.cb-fact__l {
  font-size: var(--fs-300);
  color: var(--cb-muted);
  margin-top: var(--sp-1);
}

.cb-note {
  margin-top: var(--sp-12);
  padding: var(--sp-4) var(--sp-6);
  border-left: 2px solid var(--cb-amber);
  background: rgba(240, 157, 81, 0.08);
  font-size: var(--fs-300);
  color: var(--cb-muted);
  max-width: 64ch;
}

.cb-note b {
  color: var(--cb-amber);
  font-weight: 600;
}

.cb-note + .cb-note { margin-top: var(--sp-4); }

.cb-outro a {
  color: var(--cb-cyan);
}

.cb-links {
  margin-top: var(--sp-12);
  font-size: var(--fs-300);
}

/* main.css gives .site-footer a 96px margin-top, which is right on every other
   page: it separates a dark footer from light page content. Here the outro is
   already --color-navy — the footer's own colour — so that margin renders as a
   white seam between two identical dark blocks. This stylesheet is loaded on
   /how-its-built/ only, so collapsing it here leaves the site-wide rule intact. */
.site-footer { margin-top: 0; }


/* -- Reduced motion -------------------------------------------------------- */
/* The scrubber itself is scroll-driven, not self-animating, so it stays. Only
   the decorative bob and the caption cross-fade are motion. main.css section 3
   already kills transitions globally under this query; this stops the bob. */
@media (prefers-reduced-motion: reduce) {
  .cb-cue span { animation: none; }
}

/* --------------------------------------------------------------------------
   Design review 2026-10-03, item 9 (the build page): no empty navy opening.
   Until the build starts, the viewer shows the page's still (frame 60, the
   same picture a reader without scripts gets) and hides the counter, so the
   still is never labelled layer 0; chipbuild.js sets and clears
   data-preview. The scroll sequence and its layer/frame table are unchanged.
   -------------------------------------------------------------------------- */
.cb-intro { padding-block: 3rem 1.5rem; }
.cb-cue { margin-top: 1.5rem; }
html.js .cb-sticky { align-content: start; padding-block: 1.5rem; }
html.js .cb-viewer[data-preview] .cb-still { display: block; }
html.js .cb-viewer[data-preview] .cb-canvas,
html.js .cb-viewer[data-preview] .cb-hud { display: none; }

@media (max-width: 40rem) {
  .cb-intro { padding-block: 2rem 1rem; }
  html.js .cb-sticky { gap: 1rem; }
}
