/* ============================================================================
   FIELD NINE — ds.css
   The Decision Surface: fields, the pinned scrub, instrument chrome, the
   reticle, and the annotations that name what the field is doing.

   The field is the brand. Everything around it is instrument-thin: one nav
   rail, one HUD strip, one inset shared by both. The engine draws on whatever
   ground it is given — it reads its palette from CSS custom properties, so a
   field on paper and a field on ink are the same grammar in two lights.
   ========================================================================= */

/* ---------------------------------------------------------------------------
   1 · THE FIELD
   ------------------------------------------------------------------------ */

.ds-field {
  --ds-ink: var(--fg);
  --ds-dim: var(--fg-4);
  --ds-line: var(--line);
  --ds-accent: var(--accent);
  --ds-bg: var(--bg);

  position: relative;
  isolation: isolate;
  overflow: clip;
  background: var(--ds-bg);
  min-height: 300px;
  border-block: var(--hair) solid var(--line);
  contain: paint;
}
.ds-field canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
}

/* A field with a caption rail under it: the frame owns the top and bottom
   hairlines, the plate value is one step below the page, and a single bevel
   replaces every shadow the system could have used. */
.ds-plate {
  position: relative;
  background: var(--plate);
  border: var(--hair) solid var(--line);
  border-radius: var(--radius);
  overflow: clip;
}
.ds-plate .ds-field { border-block: 0; }

/* — Hero field: full bleed behind the claim. — */
.ds-field--hero {
  position: absolute;
  inset: 0;
  min-height: 100%;
  border-block: 0;
}

/* Reading scrim: white under the headline (bottom-left), opening toward the
   field (upper-right) so the geometry stays the subject. */
.ds-field--hero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background:
    radial-gradient(
      120% 92% at 2% 96%,
      rgb(253 253 251 / 0.94) 0%,
      rgb(253 253 251 / 0.62) 32%,
      rgb(253 253 251 / 0.16) 56%,
      rgb(253 253 251 / 0) 72%
    ),
    linear-gradient(
      to top,
      rgb(253 253 251 / 0.82) 0%,
      rgb(253 253 251 / 0.24) 28%,
      rgb(253 253 251 / 0) 50%
    );
}

/* ---------------------------------------------------------------------------
   2 · RETICLE
   Corner brackets straddling the frame edge, drawn in the signal red. Four
   hairline arms, one 1px cross-hair — a viewfinder, not a border.
   ------------------------------------------------------------------------ */

/* Four arms, each a corner. Drawn with two border edges on one element so
   nothing animates layout: the corner grows on entry via a custom property. */
.reticle {
  --ret-color: var(--accent);
  --ret-size: 18px;
  --ret-weight: 1px;
  --ret-inset: 10px;
  position: relative;
}
.reticle-frame {
  position: absolute;
  inset: var(--ret-inset-block-start, var(--ret-inset)) var(--ret-inset)
    var(--ret-inset) var(--ret-inset);
  z-index: 4;
  pointer-events: none;
  --arm: var(--ret-size);
  --g: var(--ret-color);
}
.reticle-frame i {
  position: absolute;
  width: var(--arm);
  height: var(--arm);
  border: 0 solid var(--g);
}
.reticle-frame i:nth-child(1) {
  top: 0;
  left: 0;
  border-top-width: var(--ret-weight);
  border-left-width: var(--ret-weight);
}
.reticle-frame i:nth-child(2) {
  top: 0;
  right: 0;
  border-top-width: var(--ret-weight);
  border-right-width: var(--ret-weight);
}
.reticle-frame i:nth-child(3) {
  bottom: 0;
  left: 0;
  border-bottom-width: var(--ret-weight);
  border-left-width: var(--ret-weight);
}
.reticle-frame i:nth-child(4) {
  bottom: 0;
  right: 0;
  border-bottom-width: var(--ret-weight);
  border-right-width: var(--ret-weight);
}
.reticle-frame::before,
.reticle-frame::after {
  content: "";
  position: absolute;
  background: var(--g);
  opacity: 0.5;
}
.reticle-frame::before {
  left: 50%;
  top: calc(50% - 5px);
  width: 11px;
  height: 1px;
  transform: translateX(-50%);
}
.reticle-frame::after {
  top: 50%;
  left: calc(50% - 5px);
  height: 11px;
  width: 1px;
  transform: translateY(-50%);
}
/* Arms extend on entry — the viewfinder finding its subject. */
.reticle-frame i {
  animation: retArm var(--t-scene) var(--ease-out) both;
}
.reticle-frame i:nth-child(1) { animation-delay: 120ms; }
.reticle-frame i:nth-child(2) { animation-delay: 200ms; }
.reticle-frame i:nth-child(3) { animation-delay: 280ms; }
.reticle-frame i:nth-child(4) { animation-delay: 360ms; }
@keyframes retArm {
  from { --arm: 0px; opacity: 0; }
  to { --arm: var(--ret-size); opacity: 1; }
}
/* --arm is not animatable without @property; declare it so it interpolates. */
@property --arm {
  syntax: "<length>";
  inherits: true;
  initial-value: 0px;
}

/* The hero's own annotation sits opposite the claim, clear of the bar: the
   field's voice is on the right, the argument is on the left. On a phone the
   geometry takes the whole top band, so the DOM copy would only fight it —
   the vocabulary chain under the headline already names the objects. */
.ds-field--hero .ds-annotate {
  top: calc(var(--nav-h) + clamp(12px, 2.6vh, 30px));
  left: auto;
  max-width: min(34ch, 42%);
  text-align: right;
}
.ds-field--hero .ds-annotate .ds-prop { margin-left: auto; }
@media (max-width: 760px) {
  .ds-field--hero .ds-annotate { display: none; }
}

/* ---------------------------------------------------------------------------
   3 · PINNED SCRUB
   The viewport holds while the field evolves; scroll is the playhead. The
   progress rail is 2px of red across the top edge — the only thing on screen
   that reports position.
   ------------------------------------------------------------------------ */

.ds-scroll-space { position: relative; height: 520svh; }

.ds-pin {
  position: sticky;
  top: 0;
  height: 100svh;
  overflow: clip;
  background: var(--ds-bg, var(--bg));
}
.ds-pin > .ds-field {
  position: absolute;
  inset: 0;
  min-height: 100%;
  border-block: 0;
}
.ds-pin::before {
  content: "";
  position: absolute;
  inset: var(--nav-h) 0 auto 0;
  height: 1px;
  z-index: 5;
  background: var(--line-2);
}
.ds-pin::after {
  content: "";
  position: absolute;
  /* Directly under the bar: a rail hidden behind the chrome reports nothing. */
  top: var(--nav-h);
  left: 0;
  width: 100%;
  height: 2px;
  z-index: 6;
  background: var(--accent);
  transform: scaleX(var(--sp, 0));
  transform-origin: 0 50%;
}

/* Beats: captions pinned to the bottom, one at a time, on the eased clock. */
.ds-beats {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  /* All six captions share one cell: the active one must not move because its
     index moved. Stacked in a column, caption 02 rendered at the top of the
     viewport and behind the bar. */
  display: grid;
}
.ds-beat {
  grid-area: 1 / 1;
  align-self: end;
  padding: 0 0 clamp(88px, 13vh, 132px);
  opacity: 0;
  transform: translateY(14px);
  transition:
    opacity var(--t-panel) var(--ease-out),
    transform var(--t-panel) var(--ease-out);
}
.ds-beat.is-active { opacity: 1; transform: none; }
.ds-beat p {
  max-width: 40ch;
  margin-inline: auto;
  text-align: center;
  font-size: var(--t-lead);
  line-height: 1.5;
  letter-spacing: var(--track-body);
  color: var(--fg-2);
  text-wrap: balance;
  text-shadow: 0 1px 18px var(--ds-bg);
}
.ds-beat b {
  display: block;
  margin-bottom: 14px;
  font-family: var(--font-mono);
  font-size: var(--t-label);
  font-weight: 500;
  font-variant-numeric: tabular-nums slashed-zero;
  letter-spacing: var(--track-label);
  text-transform: uppercase;
  color: var(--fg-3);
}
.ds-beat b i {
  display: inline-block;
  width: 5px;
  height: 5px;
  margin-right: 10px;
  border-radius: 50%;
  background: var(--accent);
  vertical-align: 1px;
  opacity: 0;
  transform: scale(0.4);
  transition:
    opacity var(--t-ui) var(--ease-out),
    transform var(--t-panel) var(--ease-mech);
}
.ds-beat.is-active b i { opacity: 1; transform: none; }

/* The beat legend: six marks down the right edge, the active one lit, the
   rest held at a whisper. It is drawn from the captions, so it can never
   disagree with the scrub, and every mark is a scrub control. */
.ds-index {
  position: absolute;
  top: 50%;
  right: max(var(--gutter), (100% - var(--max)) / 2);
  z-index: 4;
  display: grid;
  gap: 10px;
  justify-items: end;
  transform: translateY(-50%);
  font-size: var(--t-micro);
  letter-spacing: 0.13em;
  text-transform: uppercase;
}
.ds-index li { display: block; }
.ds-index button {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 3px 0 3px 10px;
  color: var(--fg-4);
  cursor: pointer;
  font: inherit;
  letter-spacing: inherit;
  text-transform: inherit;
  transition: color var(--t-ui) var(--ease-out);
}
.ds-index-n { font-variant-numeric: tabular-nums slashed-zero; }
.ds-index-t { position: relative; }
.ds-index-t::after {
  content: "";
  position: absolute;
  left: calc(100% + 10px);
  top: 50%;
  width: 0;
  height: 1px;
  background: var(--accent);
  transition: width var(--t-panel) var(--ease-out);
}
.ds-index button:hover { color: var(--fg-2); }
.ds-index button.is-on { color: var(--fg); }
.ds-index button.is-on .ds-index-n { color: var(--accent); }
.ds-index button.is-on .ds-index-t::after { width: 22px; }

@media (max-width: 860px) {
  .ds-index {
    top: auto;
    bottom: calc(var(--nav-h) + 8px);
    left: var(--gutter);
    right: var(--gutter);
    transform: none;
    grid-auto-flow: column;
    justify-content: space-between;
    gap: 4px;
  }
  .ds-index-t { display: none; }
  .ds-index button { padding: 6px 2px; }
  .ds-index-t::after { display: none; }
  .ds-index button.is-on { box-shadow: inset 0 -1px 0 var(--accent); }
}

@media (max-width: 560px) {
  .ds-index { display: none; }
}

/* Full-bleed fields own the whole viewport, so their rail is the viewport rail
   and they start below the fixed bar rather than under it. A field that already
   sits inside the content column inherits that column's inset and adds
   nothing — double insetting is the most common way a rail stops being a rail. */
.ds-field--hero .ds-annotate,
.ds-pin .ds-annotate { top: calc(var(--nav-h) + 22px); }

.ds-field--hero .ds-annotate { right: max(var(--gutter), (100% - var(--max)) / 2); }
.ds-field--hero .ds-hud { padding-inline: max(var(--gutter), (100% - var(--max)) / 2); }
.ds-pin .ds-annotate { left: max(var(--gutter), (100% - var(--max)) / 2); }
.ds-pin .ds-hud { padding-inline: max(var(--gutter), (100% - var(--max)) / 2); }
.ds-pin .ds-beat { padding-inline: max(var(--gutter), (100% - var(--max)) / 2); }

/* ---------------------------------------------------------------------------
   4 · INSTRUMENT CHROME
   The HUD is a strip, not a card: a hairline, three readouts, no chrome.
   ------------------------------------------------------------------------ */

.ds-hud {
  position: absolute;
  inset: auto 0 0 0;
  z-index: 4;
  display: flex;
  align-items: center;
  gap: clamp(16px, 2.4vw, 34px);
  padding: 12px 0;
  border-top: var(--hair) solid var(--line);
  background: color-mix(in srgb, var(--ds-bg) 86%, transparent);
  -webkit-backdrop-filter: blur(10px);
  backdrop-filter: blur(10px);
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  font-variant-numeric: tabular-nums slashed-zero;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-3);
}
.ds-hud b { font-weight: 500; color: var(--accent); }
.ds-hud span:last-child { margin-left: auto; text-align: right; }

/* Annotations: a researcher's marks in the field's negative space. */
.ds-annotate {
  position: absolute;
  z-index: 3;
  top: clamp(22px, 6%, 56px);
  left: 0;
  max-width: min(340px, 62%);
  pointer-events: none;
}
.ds-annotate .ds-mark {
  margin: 0 0 10px;
  font-family: var(--font-display);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: var(--track-mark);
  text-transform: uppercase;
  color: var(--fg);
}
.ds-annotate .ds-mark .crimson { color: var(--accent); }
.ds-annotate .ds-prop {
  margin: 0;
  max-width: 32ch;
  font-size: 0.8125rem;
  line-height: 1.6;
  letter-spacing: 0.005em;
  color: var(--fg-2);
  text-wrap: pretty;
}
.ds-mark,
.ds-prop { transition: opacity var(--t-ui) ease; }
.ds-mark.is-fading,
.ds-prop.is-fading { opacity: 0; }
.ds-annotate .ds-chain {
  display: block;
  margin-top: 10px;
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  font-variant-numeric: tabular-nums slashed-zero;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--fg-4);
}

/* Figure: a caption rail beneath the field, ruled like a plate in a report. */
.figure { position: relative; }
.figure-cap {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 18px;
  font-family: var(--font-mono);
  font-size: var(--t-micro);
  font-variant-numeric: tabular-nums slashed-zero;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--fg-4);
}
.figure-cap b { font-weight: 500; color: var(--accent); }

/* ---------------------------------------------------------------------------
   5 · RESPONSIVE
   The field shrinks before it loses information: the HUD wraps, the
   annotations narrow, the caption drops to a smaller measure.
   ------------------------------------------------------------------------ */

@media (max-width: 860px) {
  .ds-scroll-space { height: 440svh; }
  .ds-pin { height: 100svh; }
  .ds-beat { padding-bottom: clamp(96px, 15vh, 140px); }
  .ds-beat p { font-size: 1rem; max-width: 34ch; }
  .ds-hud { gap: 10px 16px; padding-inline: var(--gutter); flex-wrap: wrap; }
  .ds-hud span { min-width: 0; }
  .ds-hud span:last-child { margin-left: 0; }
  .ds-annotate { max-width: 74%; top: 20px; }
  .ds-annotate .ds-prop { font-size: 0.78125rem; }
  .reticle { --ret-size: 13px; --ret-inset: 8px; }
}

@media (max-width: 560px) {
  .ds-hud span:nth-child(2) { display: none; }
  .ds-beat p { max-width: 30ch; font-size: 0.97rem; }
}

@media (min-width: 1500px) {
  .ds-annotate { max-width: 380px; }
  .ds-beat p { font-size: 1.3125rem; max-width: 44ch; }
}

/* ---------------------------------------------------------------------------
   6 · REDUCED MOTION
   The pinned chapter collapses into a stack: the field becomes a still plate,
   every caption is lit at once, and the information that the scrub carried is
   carried by the captions instead.
   ------------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  .ds-scroll-space { height: auto !important; }
  .ds-pin { position: relative; height: auto; }
  .ds-pin > .ds-field {
    position: relative;
    inset: auto;
    min-height: 56svh !important;
  }
  .ds-pin::after { display: none; }
  .ds-beats { position: static; display: block; }
  .ds-beat {
    opacity: 1;
    transform: none;
    padding: 28px var(--gutter);
    border-bottom: var(--hair) solid var(--line);
    text-align: left;
  }
  .ds-beat:last-child { border-bottom: 0; }
  .ds-beat p { margin-inline: 0; text-align: left; max-width: 48ch; }
  .ds-beat b i { opacity: 1; transform: none; }
  .reticle-frame i { animation: none; --arm: var(--ret-size); opacity: 1; }
}

@media (prefers-reduced-transparency: reduce) {
  .ds-hud { background: var(--ds-bg); -webkit-backdrop-filter: none; backdrop-filter: none; }
  .ds-field--hero::after { background: linear-gradient(to top, rgb(253 253 251 / 0.97) 0%, rgb(253 253 251 / 0.4) 40%, rgb(253 253 251 / 0) 66%); }
}
