/* ==========================================================================
   PHASES -- the roadmap, graded by its own checks
   ==========================================================================
   Its own file rather than appended to preview.css, for one concrete reason:
   `.cyber-card`, `.cyber-card--chamfer` and `.cyber-corner` are each defined
   TWICE in that file (L2996 and L3517) and the later block wins. Anything
   inserted between them is silently overridden. Loading after preview.css
   puts this block last in the cascade without depending on where it lands.

   TWO AXES, NEVER ONE. "Shipped" is history; "verified" is what a check
   says. A single progress bar collapses them and implies a measurement
   nobody ran -- the exact claim this protocol exists to refuse. Every card
   carries a BUILD axis and a GATES axis and they never merge into a figure.

   ON THE COLOUR OF AN UNGRADED PHASE. Amber (--unchecked) is this system's
   UNCHECKED semantic and it is correct at CHIP scale, on one named check. At
   CARD scale it telegraphs alarm, and an unrun check is not an alarm. So an
   ungraded phase is --ember-dim: the same hue as a graded one, unlit.
   Absence of glow reads as not-yet-measured; a hostile hue would read as
   failed. No card shell in this section ever uses --fail. Nothing here has
   failed.

   ON MOTION. The UNCHECKED chips do NOT pulse. preview.css already recorded
   that lesson twice: animated stripes "made settled data read as perpetually
   loading" (L4295) and blinking dots "read as filler" (L4298). A pulsing
   gate would say "working on it" about work nobody is doing. Motion lives in
   arrival and in one non-looping verdict demonstration, never in state.
   ========================================================================== */

#phases { position: relative; margin: var(--sp-3xl) 0; }
#phases::before {
  content: ""; position: absolute; inset: -32px -24px;
  background: radial-gradient(ellipse at 50% 0%, var(--ember-halo), transparent 70%);
  pointer-events: none; z-index: -1;
}

.phase-list { list-style: none; padding: 0; margin: 0; display: grid; gap: var(--sp-lg); }

.phase-card {
  --phase-accent: var(--ember-dim);
  --phase-halo: transparent;
  position: relative;
  padding: var(--sp-lg) var(--sp-lg) var(--sp-lg) calc(var(--sp-lg) + 6px);
  content-visibility: auto; contain-intrinsic-size: auto 260px;
}
/* An inset rail rather than border-left: the chamfer clip-path shears a real
   border off at the corners. */
.phase-card::after {
  content: ""; position: absolute; inset: 0 auto 0 0; width: 3px;
  background: var(--phase-accent); box-shadow: 0 0 12px var(--phase-halo);
  transform: scaleY(0.6); transform-origin: top;
  transition: transform var(--d-3) var(--e-out-soft);
}
.phase-card:hover::after, .phase-card:focus-within::after { transform: scaleY(1); }
.phase-card:hover, .phase-card:focus-within { border-color: var(--border-highlight); }

.phase-card--shipped { --phase-accent: var(--ember); --phase-halo: var(--ember-glow); }
.phase-card--devnet  { --phase-accent: var(--sol-cyan); --phase-halo: var(--sol-glow); }
.phase-card--gated   { --phase-accent: var(--flame-warm); }
.phase-card--open    { --phase-accent: var(--ember-dim); }
.phase-card--open .cyber-corner { opacity: 0.35; }
.phase-card--gated::before {
  content: ""; position: absolute; inset: 0; pointer-events: none; z-index: 0;
  background: repeating-linear-gradient(-45deg,
    rgba(255, 138, 31, 0.055) 0 6px, transparent 6px 14px);
}
.phase-card > * { position: relative; z-index: 1; }

.phase-head { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-sm); }
.phase-ordinal {
  font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--text-muted);
}
.phase-card h3 {
  flex: 1 1 100%; margin: var(--sp-xs) 0 0 0;
  font-family: var(--font-display); font-size: clamp(17px, 2.1vw, 21px); line-height: 1.25;
}
.phase-card--open h3 { color: var(--text-secondary); }

/* One axis now. The gate column was removed -- its content was the named
   check vocabulary, which is internal jargon a visitor has no use for. What
   survives is the fact a reader actually wants: when the work landed. The
   status badge still carries whether its measurements came back, and it is
   still computed from the record rather than typed in. */
.phase-axes {
  display: grid; grid-template-columns: 1fr; gap: var(--sp-md);
  margin: var(--sp-md) 0; padding: var(--sp-sm) 0;
  border-top: 1px solid var(--border-glass);
  border-bottom: 1px solid var(--border-glass);
}
.phase-axis { display: flex; flex-direction: column; gap: 3px; min-width: 0; }
.phase-axis-label {
  font-family: var(--font-mono); font-size: 10px; letter-spacing: 0.14em;
  text-transform: uppercase; color: var(--text-muted);
}
.phase-axis-value {
  font-family: var(--font-mono); font-size: 13px; color: var(--text-primary);
  display: flex; align-items: center; gap: var(--sp-xs);
}
/* Shape, not colour alone: the glyph survives greyscale and all three
   dichromacies, and the text label beside it survives everything. */
.phase-glyph { font-size: 13px; line-height: 1; color: var(--phase-accent); }

.phase-body { margin: 0 0 var(--sp-md) 0; color: var(--text-secondary); font-size: 15px; }

.phase-note {
  margin: var(--sp-md) 0 0 0; font-size: 13px; color: var(--text-muted);
  font-family: var(--font-mono); line-height: 1.6;
}
.phase-note strong { color: var(--flame-warm); font-weight: 700; }
.phase-code {
  display: block; margin-top: var(--sp-sm); padding: var(--sp-sm);
  background: var(--bg-void); border: 1px solid var(--border-glass);
  font-family: var(--font-mono); font-size: 11px; word-break: break-all;
  color: var(--sol-cyan);
}
.phase-code-label {
  display: block; margin-top: 4px; font-family: var(--font-mono);
  font-size: 10px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--text-muted);
}

/* Arrival. Uses the same reveal contract signature-motion.js already owns.
 *
 * VISIBLE BY DEFAULT, and this is not a style preference. The first version
 * of this block started the cards at opacity 0 and cleared it when an
 * IntersectionObserver fired -- and every card shipped invisible, because
 * `content-visibility: auto` above makes the browser skip layout for
 * offscreen cards, so the observer never reports them intersecting and the
 * class that reveals them is never added. Nothing below the fold ever
 * appeared.
 *
 * So the hidden state is opt-in: JS adds `.motion-ready` to the list only
 * once it is actually running and about to observe. A dead script, a thrown
 * error, a browser with no IntersectionObserver, or a print stylesheet now
 * all fall back to fully visible content rather than a blank section. */
.motion-ready .phase-reveal { opacity: 0; transform: translateY(14px) scale(0.985); }
.motion-ready .phase-reveal.revealed {
  animation: phase-in 420ms var(--e-out) forwards;
  animation-delay: calc(var(--i, 0) * 70ms);
}
@keyframes phase-in { to { opacity: 1; transform: none; } }

.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

@media (max-width: 640px) {
  .phase-card { transform: none !important; }
}

/* Belt and braces, matching preview.css's own convention: the blanket
   override there already neuters durations, but each new animation gets an
   intentional still state rather than a frozen mid-frame. */
@media (prefers-reduced-motion: reduce) {
  .motion-ready .phase-reveal { opacity: 1; transform: none; }
  .motion-ready .phase-reveal.revealed { animation: none !important; }
  .phase-card::after { transform: scaleY(1); }
}
@media (pointer: coarse) {
  .motion-ready .phase-reveal { opacity: 1; transform: none; }
  .motion-ready .phase-reveal.revealed { animation: none !important; }
}
