/* ============================================================================
   FIGURES — inline SVG schematics, drawn to match the poster.
   ----------------------------------------------------------------------------
   Heavy black line on white, square corners, no plate and no shadow. The
   accent marks the exception path, exactly as it marks a phrase in the prose.

   Every figure follows one contract, so this block drives all five:

     figure.fig[data-fig="Fig. 02"]
       .fig-scroll                  horizontal scroll on narrow screens
         svg.fig-svg
           g.edges  > path.edge     structural line, pathLength="100"
                    > path.edge--x  the exception path (accent)
           g.arrows > path.arrow    plain triangles; no SVG markers
           g.node[data-detail]      focusable; writes into the readout
           g.anno   > text          annotations (.x for accent)
           g.band   > text          layer labels (Fig. 01 only)
       figcaption                   "Fig. 0N" + running text
       p.fig-note[data-note]        readout, filled on hover/focus

   js/figures.js toggles .is-drawn once, the first time a figure is on screen.
   There is no looping animation anywhere: a printed schematic does not pulse,
   and fifteen infinite animations is a battery cost rather than a feature.

   GEOMETRY RULES — the README carries a console snippet that re-checks them:
     · every arrowhead apex must sit ON the edge of the box it points into
     · annotation text must not overlap a box, another text, OR an edge path
     · nothing may fall outside the viewBox
   ========================================================================= */

figure.fig { margin-top: clamp(2rem, 1.4rem + 2vw, 3.2rem); }

.fig-scroll {
  overflow-x: auto;
  scrollbar-width: thin;
  padding-top: clamp(1rem, 0.6rem + 1vw, 1.6rem);
  border-top: 3px solid var(--rule-2);
}

svg.fig-svg { width: 100%; height: auto; }
svg.fig-svg text { -webkit-font-smoothing: antialiased; }

/* ── nodes ───────────────────────────────────────────────────────────────
   Square corners and a heavy stroke. The "key" node is the one the figure is
   actually about, and it gets weight rather than colour. */
.n-box {
  fill: var(--paper);
  stroke: var(--ink);
  stroke-width: 1.6;
  transition: stroke 0.2s var(--ease), fill 0.2s var(--ease), stroke-width 0.2s var(--ease);
}
.n-box--key    { stroke-width: 3.4; }
.n-box--x      { stroke: var(--accent); }
.n-box--dashed { fill: none; stroke: var(--ink-3); stroke-width: 1.2; stroke-dasharray: 5 5; }

.n-title {
  fill: var(--ink);
  font-family: var(--sans);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: -0.015em;
  transition: fill 0.2s var(--ease);
}
.n-sub  { fill: var(--ink-2); font-family: var(--mono); font-size: 9.5px; letter-spacing: 0.02em; }
.n-mono { fill: var(--accent-dark); font-family: var(--mono); font-size: 9.5px; letter-spacing: 0.02em; }

/* Hover, keyboard focus and pinned all resolve to one visual state. */
.node { cursor: default; }
.js .node {
  opacity: 0;
  /* Per-property delays: the entrance is staggered, the hover lift is not —
     a 400ms delay on a hover response feels broken. */
  transition: opacity 0.45s var(--ease), transform 0.18s var(--ease);
  transition-delay: calc(var(--i, 0) * 38ms + 100ms), 0ms;
}
.node:hover, .node:focus-visible { transform: translateY(-2px); }
.js .fig.is-drawn .node { opacity: 1; }

.node:hover .n-box,
.node:focus-visible .n-box,
.node.is-pinned .n-box { stroke: var(--accent); stroke-width: 3.4; }
.node:hover .n-title,
.node:focus-visible .n-title,
.node.is-pinned .n-title { fill: var(--accent-dark); }

/* ── edges ───────────────────────────────────────────────────────────────
   pathLength="100" normalises every path, so one dash value draws any of them
   in over the same duration regardless of real geometry. */
.edge {
  fill: none;
  stroke: var(--ink);
  stroke-width: 1.6;
  stroke-linecap: butt;
}
.js .edge {
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
  transition: stroke-dashoffset 1s var(--ease-soft);
}
.js .fig.is-drawn .edge { stroke-dashoffset: 0; }

.edge--x { stroke: var(--accent); }

.arrow { fill: var(--ink); }
.js .arrow { opacity: 0; transition: opacity 0.4s var(--ease) 0.65s; }
.js .fig.is-drawn .arrow { opacity: 1; }
.arrow--x { fill: var(--accent); }

/* ── annotations & band labels ──────────────────────────────────────────── */
.anno text {
  fill: var(--ink-3);
  font-family: var(--mono);
  font-size: 9.5px;
  letter-spacing: 0.04em;
}
.anno text.x { fill: var(--accent-dark); }

.band text {
  fill: var(--ink-3);
  font-family: var(--mono);
  font-size: 9.5px;
  font-weight: 600;
  letter-spacing: 0.16em;
}
.band .tick { stroke: var(--ink-3); stroke-width: 1.2; }

/* ── caption + readout ───────────────────────────────────────────────────
   Side by side under the diagram: the caption states what to take away, the
   readout answers "what is that box". The readout has a fixed minimum height
   so swapping its text never reflows the page. */
.fig-foot {
  display: grid;
  grid-template-columns: minmax(0, 1.45fr) minmax(0, 1fr);
  gap: clamp(1.2rem, 0.6rem + 2vw, 3rem);
  margin-top: 1.2rem;
}
figcaption { font-size: 13.5px; line-height: 1.55; color: var(--ink-2); }
figcaption b {
  display: block;
  margin-bottom: 0.35rem;
  font-family: var(--mono);
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--accent-dark);
}

.fig-note {
  min-height: 6.5rem;
  padding-top: 0.7rem;
  border-top: 3px solid var(--accent);
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink-2);
}
.fig-note b {
  display: block;
  margin-bottom: 0.35rem;
  font-family: var(--mono);
  font-size: var(--t-micro);
  font-weight: 600;
  letter-spacing: 0.17em;
  text-transform: uppercase;
  color: var(--ink);
}
.fig-note.is-idle { color: var(--ink-3); }

/* Fig. 01 is portrait — it sits at a readable width instead of stretching a
   five-layer stack across the full page. */
figure.fig--portrait .fig-scroll > svg.fig-svg { max-width: 640px; }

/* ══ RESPONSIVE ═══════════════════════════════════════════════════════════
   A diagram whose labels shrink to 6px is worse than a scrollbar. Wide
   figures keep a real minimum width and scroll. */
@media (max-width: 900px) {
  .fig-foot { grid-template-columns: 1fr; }
  .fig-note { min-height: 0; }
}

@media (max-width: 860px) {
  .fig-scroll > svg.fig-svg { min-width: 700px; }
  figure.fig--portrait .fig-scroll > svg.fig-svg { min-width: 0; }
  figcaption::after {
    content: " ⟷ scroll the figure";
    font-family: var(--mono);
    font-size: 11.5px;
    color: var(--accent-dark);
  }
  figure.fig--portrait figcaption::after { content: none; }
}

@media print {
  .fig-scroll { overflow: visible; }
  .fig-scroll > svg.fig-svg { min-width: 0; }
  .fig-note { display: none; }         /* an interaction, not content */
  figcaption::after { content: none; }
  .fig-foot { grid-template-columns: 1fr; }
  .js .node { opacity: 1 !important; }
  .js .edge { stroke-dashoffset: 0 !important; }
  .js .arrow { opacity: 1 !important; }
}
