/* ============================================================
   $ENGINEER — layout repairs
   Loaded LAST, on purpose.

   These rules were previously appended to gen.css, which is machine-extracted
   from module comment headers. One stray prose line in that file makes the CSS
   parser discard everything after it — which silently killed every repair here.
   Keeping them in their own hand-written file means a bad extraction can never
   take the layout down with it.
   ============================================================ */

/* ---- 1. The document index is wider than a narrow viewport ----------------
   The nav is a block inside a flex header, so min-width:auto stops it shrinking
   and it pushes the page sideways. Give it permission to shrink, then scroll
   its own overflow instead of the document's. */
header .eng-nav,
header #eng-nav-list,
.eng-nav,
#eng-nav-list {
  min-width: 0 !important;
  max-width: 100% !important;
  overflow-x: auto !important;
  overflow-y: hidden !important;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
}
.eng-nav::-webkit-scrollbar,
#eng-nav-list::-webkit-scrollbar { display: none; }
#eng-nav-list { flex-wrap: nowrap !important; }
#eng-nav-list > * { flex: 0 0 auto !important; white-space: nowrap; }

@media (max-width: 980px) {
  #eng-nav-list { gap: 14px !important; }
  #eng-nav-list a { font-size: 11px !important; }
}
@media (max-width: 760px) {
  .eng-nav, #eng-nav-list { display: none !important; }
}

/* ---- 2. Marquees are wider than the screen by design ---------------------- */
.mq, .eng-mq, [class*="marquee"] { overflow: hidden !important; max-width: 100vw; }

/* ---- 3. Evidence grid left a 1683px hole ---------------------------------
   Measured cause: the .gfx-wide exposure spans two columns but keeps a 4/3
   aspect box, so it is ~1.7x taller than its single-column neighbours. Its
   whole row balloons to ~700px, the plain items beside it float in the void,
   and the pushed-out sixth plate leaves two empty cells on the next row.
   Row packing was never the cause — cell height was. Neutralize the span so
   all six plates are equal one-column cells: a packed contact sheet with zero
   empty cells at every breakpoint. */
#gallery-forge .gfx-wide { grid-column: auto !important; }

.gf-grid {
  align-items: start !important;
  align-content: start !important;
  grid-auto-flow: dense !important;
  grid-auto-rows: min-content !important;
  grid-template-rows: none !important;
}
.gf-grid > * {
  align-self: start !important;
  grid-row: auto !important;
}
@media (max-width: 760px) {
  .gf-grid { grid-template-columns: 1fr !important; }
}

/* ---- 4. A canvas with no CSS box sizes itself from its own bitmap --------
   Any resize handler then reads that back and doubles it, forever, until the
   document hits the browser ceiling. Pin every injected canvas to the viewport
   so that loop is impossible regardless of what a module does. */
body > canvas:not(#introCanvas):not(#cursorCanvas):not(#heroWeb),
canvas.spy-rail,
canvas[class*="meter"] {
  position: fixed !important;
  max-width: 100vw !important;
  max-height: 100vh !important;
  pointer-events: none;
}
/* #cursorCanvas has position:fixed but no width/height in site.css, so its
   rect comes from the bitmap and site.js's fitCanvas doubles it on every
   resize — the same loop that killed sound.js. A CSS box breaks the loop:
   the rect now always comes from here, never from the bitmap. */
#cursorCanvas {
  width: 100vw !important;
  height: 100vh !important;
}
.snd-toggle {
  position: fixed !important;
  right: 16px; bottom: 92px; z-index: 640;
  width: 38px !important; height: 38px !important;
  max-width: 38px !important; max-height: 38px !important;
  border-radius: 50%; overflow: hidden;
  border: 1px solid var(--hairline);
  background: rgba(7, 6, 5, .86);
  pointer-events: auto;
}
.snd-toggle canvas { position: absolute !important; inset: 0 !important;
  width: 100% !important; height: 100% !important;
  max-width: 38px !important; max-height: 38px !important; }

/* ---- 5. Nothing may extend the document horizontally --------------------- */
html, body { max-width: 100%; overflow-x: clip; }

/* Sections with decorative layers that deliberately sit at the edge get their
   own clip, so they survive even if the root clip is ever removed. #origin
   already has one; these were relying on the root alone. */
#lore, #buy { overflow-x: clip; }

/* ============================================================
   ORIGINAL RECORDING (exhibit A)
   ============================================================ */
.orig-clip { margin: 32px 0 0; max-width: 540px; }
.orig-frame {
  position: relative; overflow: hidden;
  border: 1px solid var(--hairline); background: #000;
}
.orig-frame video { width: 100%; height: auto; display: block; }
.orig-tick { position: absolute; width: 12px; height: 12px;
  border: 1px solid var(--orange); opacity: .85; pointer-events: none; }
.orig-tick.tl { top: 7px; left: 7px; border-right: 0; border-bottom: 0; }
.orig-tick.tr { top: 7px; right: 7px; border-left: 0; border-bottom: 0; }
.orig-tick.bl { bottom: 7px; left: 7px; border-right: 0; border-top: 0; }
.orig-tick.br { bottom: 7px; right: 7px; border-left: 0; border-top: 0; }
.orig-clip figcaption { display: flex; flex-direction: column; gap: 5px; margin-top: 11px; }
.oc-k { font-family: var(--mono); font-size: 10px; letter-spacing: .2em;
  text-transform: uppercase; color: var(--orange); }
.oc-t { font-family: var(--mono); font-size: 11.5px; color: var(--silk-dim); line-height: 1.55; }

/* ============================================================
   MARKET RECORD
   ============================================================ */
#chart { padding-block: clamp(80px, 10vw, 150px); }
#chart .chart-frame {
  position: relative; max-width: 980px; margin: 28px auto 0;
  aspect-ratio: 16 / 10; overflow: hidden;
  border: 1px solid var(--hairline); background: var(--panel);
}
#chart .chart-frame iframe { width: 100%; height: 100%; border: 0; display: block; }
.chart-lock {
  position: absolute; inset: 0; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 15px; text-align: center; padding: 28px;
  background: radial-gradient(circle at 50% 42%, rgba(232, 98, 44, .07), transparent 62%);
}
.chart-lock .cl-k { font-family: var(--mono); font-size: 11px; letter-spacing: .2em;
  text-transform: uppercase; color: var(--orange); }
.chart-lock p { color: var(--silk-dim); max-width: 46ch; font-size: 14.5px; }
.chart-note { font-family: var(--mono); font-size: 11px; letter-spacing: .08em;
  text-align: center; margin-top: 13px; }
@media (max-width: 700px) { #chart .chart-frame { aspect-ratio: 4 / 5; } }

/* ---- pending social links read as pending, not as broken ---------------- */
.hero-social a .soc-k {
  position: absolute; left: 50%; top: calc(100% + 6px); transform: translateX(-50%);
  font-family: var(--mono); font-size: 8.5px; letter-spacing: .16em;
  text-transform: uppercase; color: var(--silk-dim); white-space: nowrap;
}
.hero-social a { position: relative; }

/* ============================================================
   URGENT REPAIRS — reported visually
   ============================================================ */

/* 1. The original-recording figure was appended to the end of #origin without
      being told about that section's grid, so it landed in an implicit column
      and hung off the left edge, taking its caption with it. Make it own a full
      row, inside the page gutter, in normal flow. */
#origin .orig-clip,
.orig-clip {
  grid-column: 1 / -1 !important;
  grid-row: auto !important;
  justify-self: start !important;
  align-self: start !important;
  position: relative !important;
  left: auto !important; right: auto !important;
  margin-inline: 0 !important;
  margin-block: 34px 0 !important;
  width: min(540px, 100%) !important;
  max-width: 100% !important;
  float: none !important;
  transform: none !important;
}
#origin .orig-clip figcaption,
.orig-clip figcaption {
  position: static !important;
  width: 100% !important;
  padding-inline: 0 !important;
  text-align: left !important;
}
/* keep the whole figure inside the reading column even if the section is full-bleed */
#origin { overflow-x: clip; }
#origin > .wrap, #origin .wrap { position: relative; }

/* 2. Display headings were overflowing their column and getting clipped
      ("DOCUMENTATION" cut off on the right). Let them wrap and never exceed
      their container. */
h1, h2, h3,
.t, [data-text] {
  max-width: 100%;
  overflow-wrap: break-word;
  word-break: normal;
  hyphens: none;
}
h2, .t {
  font-size: clamp(30px, 1.6rem + 3vw, 68px) !important;
  line-height: 1.02;
}
section h2, section .t { padding-right: 4px; }

/* 3. Connector strands between plates were reserving enormous empty height.
      A decorative rule never needs more than a short run. */
[class*="strand"], [data-motion="strand-bridge"],
.eng-strand, .strand-ref, .load-path {
  max-height: 220px !important;
  overflow: hidden;
}
[class*="strand"] svg, [data-motion="strand-bridge"] svg {
  max-height: 220px !important;
  height: auto !important;
}

/* 4. No decorative layer may reserve more than a screen of empty space. */
[aria-hidden="true"]:not(canvas):not(#intro):not(.hero-veil) {
  max-height: 100vh;
}

/* 5. Nothing inside a section may push past the gutter. */
section > *, .wrap > * { max-width: 100%; }

/* ============================================================
   AUDIT REPAIRS — overflow, mobile, motion fallback
   ============================================================ */

/* ---- A1. Spec dimension-rail label was flex:0 0 auto + nowrap ------------
   494px wide at every viewport; its overflow-hidden parent truncated the
   sentence mid-word on anything under ~1100px. Let it shrink and wrap. The
   inline style on the span forces the !important here. */
[data-measure="span"] {
  flex: 1 1 auto !important;
  min-width: 0 !important;
  white-space: normal !important;
  text-align: center;
}

/* ---- A2. Launch bar had no CSS at all ------------------------------------
   launchbar.js builds #launchbar/.lb-* and toggles .on, but no stylesheet
   ever consumed them, so it rendered as an unstyled text block after the
   footer. Bottom bar on every viewport: fixed, hidden below the fold until
   .on, 44px tap targets, safe-area aware. Sits under the index sheet. */
#launchbar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 650;
  transform: translateY(110%);
  transition: transform .35s ease;
  background: rgba(7, 6, 5, .94);
  border-top: 1px solid var(--hairline);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  padding-bottom: env(safe-area-inset-bottom, 0px);
}
#launchbar.on { transform: none; }
.lb-in {
  display: flex; align-items: stretch; justify-content: space-between; gap: 12px;
  max-width: 980px; margin: 0 auto; padding: 8px 16px;
}
.lb-ca {
  display: flex; flex-direction: column; justify-content: center; gap: 2px;
  min-width: 0; min-height: 44px;
  padding: 4px 10px 4px 0;
  background: none; border: 0; cursor: pointer; text-align: left;
  font-family: var(--mono);
}
.lb-k {
  font-size: 9px; letter-spacing: .2em; text-transform: uppercase;
  color: var(--silk-dim);
}
.lb-v {
  font-size: 12.5px; letter-spacing: .05em; color: var(--silk);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
#launchbar[data-state="pending"] .lb-v { color: var(--silk-dim); }
#launchbar[data-state="live"] .lb-v,
#launchbar.lb-flash .lb-v { color: var(--orange-hi); }
.lb-acts { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.lb-btn {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: 44px; min-width: 44px; padding: 0 18px;
  font-family: var(--mono); font-size: 11px; letter-spacing: .16em;
  text-transform: uppercase; text-decoration: none;
  color: var(--silk); background: var(--raised);
  border: 1px solid var(--hairline);
}
.lb-btn.lb-primary {
  background: var(--orange); border-color: var(--orange);
  color: #0B0908; font-weight: 700;
}
/* pre-deploy the primary action is a pointer to the buy plate, not a buy —
   it must not read as a live button */
#launchbar[data-state="pending"] .lb-primary {
  background: var(--raised); border-color: var(--hairline); color: var(--silk-dim);
}

/* ---- A3. Sticky live strip was burying the fixed header ------------------
   .live pinned at top:0 with z-index:500 over the 56/60px header (z 110),
   making the INDEX button untappable past the hero. Pin it below the header
   instead, and lift the index sheet above the strip so its CLOSE row is
   never painted over. */
.live { top: 56px !important; }
@media (min-width: 760px) {
  .live { top: 60px !important; }
}
.eng-sheet { z-index: 700 !important; }

/* ---- A4. Live strip scrolls sideways with no cue -------------------------
   At phone widths only the first cells fit and nothing signals more. A
   right-edge fade marks the cut. */
@media (max-width: 600px) {
  .live-in {
    -webkit-mask: linear-gradient(90deg, #000 82%, transparent);
    mask: linear-gradient(90deg, #000 82%, transparent);
  }
}

/* ---- A5. Scrub scenes overflow their 100svh pin on phones ----------------
   The weave pin stacks media + head + five log entries against ~660px of
   viewport; align-content:center clipped both ends, so entries activated
   while invisible. Mirror the orbit scene's narrow-viewport pattern: show
   only the currently reached entry. Where :has() is supported, earlier
   reached entries collapse too, leaving exactly the latest one. */
@media (max-width: 720px) {
  .eng-scenes.is-scrub .scn--weave:not(.scn-fail) .scn-entry,
  .eng-scenes.is-scrub .scn--descend:not(.scn-fail) .scn-entry { display: none; }
  .eng-scenes.is-scrub .scn--weave:not(.scn-fail) .scn-entry.is-set,
  .eng-scenes.is-scrub .scn--descend:not(.scn-fail) .scn-entry.is-set { display: block; }
  .eng-scenes.is-scrub .scn--weave:not(.scn-fail) .scn-entry.is-set:has(~ .scn-entry.is-set),
  .eng-scenes.is-scrub .scn--descend:not(.scn-fail) .scn-entry.is-set:has(~ .scn-entry.is-set) { display: none; }
  .eng-scenes.is-scrub .scn:not(.scn-fail) .scn-pin { gap: 20px; }
}

/* ---- A6. The tall (9/16) scene media had no definite size below 900px ----
   justify-self:center removed grid stretch and its only child is absolutely
   positioned, so the box could resolve to 0x0 in static mode. A definite
   width plus the existing aspect-ratio gives it a real box everywhere. */
@media (max-width: 899px) {
  .eng-scenes .scn-media--tall { width: min(100%, calc(45svh * 9 / 16)); }
}

/* ---- A7. Section-header stamps stayed invisible without scroll-timelines -
   [data-fx~="stamp"] ships at opacity:0 and nothing adds .stamped; only the
   animation-timeline:view() path inside @supports ever reveals it. Firefox
   and older Safari kept three section headers blank. Where the timeline
   path does not exist, the stamp simply is. */
@supports not (animation-timeline: view()) {
  [data-fx~="stamp"] { opacity: 1 !important; transform: none !important; }
}

/* ---- A8. The telemetry flash keyframe was cyan ---------------------------
   site.css @keyframes flash used rgba(90,209,196,.16) — there is no cyan on
   this site. Same name declared later wins: same pulse, correct ember. */
@keyframes flash {
  0% { background: rgba(232, 98, 44, .16); }
  100% { background: transparent; }
}

/* ---- A9. Reduced motion, guaranteed at the CSS layer ---------------------
   Belt over the JS gates: anything this file animates stops, stamps render
   at rest, the marquee holds still, tension-set numbers arrive set. */
@media (prefers-reduced-motion: reduce) {
  #launchbar { transition: none; }
  [data-fx~="stamp"] { opacity: 1 !important; transform: none !important; }
  .mq-t { animation: none !important; }
  .cell.flash { animation: none !important; }
  [data-tension] { letter-spacing: normal !important; transition: none !important; }
  [data-tension]::after { transform: scaleX(1) !important; transition: none !important; }
}

/* ============================================================
   DENSITY PASS — text-per-pixel in the scrub scenes
   Measured: scenes run 0.227 chars/px against 0.465-0.544 in the
   prose sections. The runway is scroll distance, not pin content,
   so the fix has two halves: shorten the runway (here) and add
   substance to the pin (content work, elsewhere). data-at values
   are fractions of scene height, so every log entry keeps its
   relative trigger point when the runway shrinks.
   ============================================================ */

/* ---- D1. Trim the scrub runway ------------------------------------------
   sections.css allocates 300/240/480svh per scene — 1020svh of travel for
   three pinned screens of text. Cut ~24%: the scrub reads as a document
   being worked through, not a corridor. Orbit gets 360svh: one svh of
   travel per degree of turntable rotation, which is also easier to defend
   on a drawing than 480. The phone values must be restated here because
   this file loads last — an unconditional rule below would silently beat
   the earlier file's max-width query at equal specificity. */
.eng-scenes .scn--weave   { --scn-h: 230svh; }
.eng-scenes .scn--descend { --scn-h: 190svh; }
.eng-scenes .scn--orbit   { --scn-h: 360svh; }
@media (max-width: 720px) {
  .eng-scenes .scn--weave   { --scn-h: 200svh; }
  .eng-scenes .scn--descend { --scn-h: 170svh; }
  .eng-scenes .scn--orbit   { --scn-h: 220svh; }
}

/* ---- D2. Scene plate corner ticks were never drawn ----------------------
   All three scn-media plates ship four i.scn-tick corners in the markup and
   no stylesheet ever consumed the class — twelve invisible elements where
   the register promises registration marks. Same geometry as .orig-tick,
   which is the one corner set that does render. .scn-media is already
   position:relative, so these pin to the plate edge, over the video. */
.scn-tick {
  position: absolute; width: 12px; height: 12px;
  border: 1px solid var(--orange);
  opacity: .85; pointer-events: none; z-index: 2;
}
.scn-tick--tl { top: 7px;    left: 7px;  border-right: 0; border-bottom: 0; }
.scn-tick--tr { top: 7px;    right: 7px; border-left: 0;  border-bottom: 0; }
.scn-tick--bl { bottom: 7px; left: 7px;  border-right: 0; border-top: 0; }
.scn-tick--br { bottom: 7px; right: 7px; border-left: 0;  border-top: 0; }

/* ---- D3. Room for denser log entries ------------------------------------
   The density work will grow entries past one paragraph — measurement
   lines, observed values, follow-up notes. Give a second paragraph a set
   rhythm instead of colliding with the first, and let a trailing mono
   line inside an entry read as a recorded figure, not body copy. */
.scn-entry p + p { margin-top: 7px; }
.scn-entry p + p:last-child {
  font-family: var(--mono);
  font-size: 11.5px; letter-spacing: .04em; line-height: 1.6;
  color: var(--silk-dim);
}

/* ---- D4. Cross-references render anywhere, not only where born ----------
   .eng-ref is styled by scoped module blocks for #origin and #lore only;
   dropped into any other section it renders as bare body text. A global
   default in the same register — the scoped rules still win on
   specificity, so nothing already placed moves. */
.eng-ref {
  font-family: var(--mono);
  font-size: 10px; letter-spacing: .18em; text-transform: uppercase;
  color: var(--silk-dim);
  margin-top: 18px;
  max-width: 100%;
}

/* ---- D5. Sheet stamps stay honest under scrub ---------------------------
   Nothing in D1-D4 animates, so the reduced-motion block above needs no
   addition: shorter travel is not motion, ticks and refs are static ink. */
