/* ============================================================================
   REQUEST STATUS TIMELINE — single source of truth.
   Canonical CSS for the request-status stepper: the connector-linked dots
   (.timeline / .step / .conn / .dot), the active-step radar beacon (.beacon /
   .wave), the past/active/future labels, and the active-step callout. Shared by
   comp-status.html and any future consumer. Pair with _status.js for the
   opening animation + reduced-motion handling. Import AFTER colors_and_type.css.

   Step state classes: .done · .curr / .moreinfo (active) · .pending · .rejected.
   ============================================================================ */

/* Connector reveal drivers (animated by _status.js). `--conn-lead` = px from the
   connector's LEFT edge (this step's centre) to the moving LEADING edge of the
   revealed line; it tracks the next dot's CURRENT scaled outer edge exactly
   (border-box, so the 2px border is included). `--conn-a` = the alpha of that
   leading edge — it tracks the next dot's animated opacity, so the line's right
   tip fades in step with the dot it's reaching. Rest values keep the line fully
   solid (lead far past the box, alpha 1). Registered so WAA can interpolate
   them; a browser without @property support falls back to the solid rest mask. */
@property --conn-lead { syntax: "<length>"; inherits: false; initial-value: 9999px; }
@property --conn-a    { syntax: "<number>"; inherits: false; initial-value: 1; }

/* The timeline is its OWN size container (via the JS-injected `.timeline-wrap`)
   so it collapses to fit whatever WIDTH it is handed — no markup change in any
   consumer (initStatus inserts the wrapper). Container queries below read this
   width. `inline-size` only contains the inline axis, so height still grows with
   the callout/labels; `.conn`/`.beacon`/`.callout::before` are positioned
   against `.step` (position:relative), so the wrapper's containing-block change
   doesn't move them. */
.timeline-wrap { container-type: inline-size; }

/* Variable-length timeline (3–4 steps) — column auto-flow adapts to the count. */
.timeline {
  /* ── Motion tokens (the compound-architecture contract) ───────────────────
     Read by _status.js at play time (getComputedStyle on the .timeline) and by
     the beacon keyframes below. Declared here as the system DEFAULT; overridden
     per-instance via inline style or by the playground Inspector / a preset
     (which set these same custom props on the element). NEVER hardcode these
     durations in _status.js again — keeping them as tokens is what lets the
     motion governor scale them (playbackRate) and the Inspector retune them
     live with zero JS wiring. See notes/architecture.md "Compound architecture". */
  /* --st-dur: per-step entrance duration · --st-stagger: delay between steps ·
     --st-ease: entrance easing (expo-out) · --st-beacon: active-step pulse period */
  --st-dur: 520ms; /* @kind other */
  --st-stagger: 380ms; /* @kind other */
  --st-ease: cubic-bezier(0.16, 1, 0.3, 1); /* @kind other */
  --st-beacon: 2.8s; /* @kind other */
  display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; gap: 0; align-items: start; padding: 12px 0 4px;
}
.step { display: flex; flex-direction: column; align-items: center; position: relative; gap: 7px; padding: 0 6px; }

/* ── Connector ────────────────────────────────────────────────────────────
   Real element so the opening animation can "draw" it (an animated horizontal
   MASK reveal — see `--conn-lead`/`--conn-a` above — NOT scaleX, which
   re-rasterises the line and made the segment look thinner mid-animation, and
   NOT clip-path, which couldn't fade the leading edge). Spans this dot's centre
   → the NEXT dot's LEFT EDGE
   (`right: calc(-50% + 22px)`, the 22px dot radius), so the drawn tip lands on
   the next dot's edge rather than poking into the empty centre while that dot is
   still fading in. The portion under THIS dot (centre→right edge) is hidden by
   it. Colour encodes the SEGMENT's meaning:
     • done → done            : green (both ends complete)
     • done → active (curr/mi): GRADIENT green→warning (crossing into the active)
     • done → rejected        : GRADIENT green→red
     • active → future        : GREY (we are WAITING at the active step)
     • future → future        : grey (pending)
   Only completed/gradient segments are thicker (3px) and re-centred so a colour
   change between adjacent coloured segments never shifts thickness. */
.conn {
  position: absolute; top: 21px; left: 50%; right: calc(-50% + 22px); height: 2px;
  background: var(--color-border-default); z-index: 0;
  /* Opening-animation reveal + seamless fade. The mask is TRANSPARENT up to
     20px (just inside this dot's 22px edge — so the under-dot stretch is NEVER
     painted, even while the dot itself is still fading in: that stub being
     exposed was the "line starts from the dot's centre / all lines visible at
     the start frame" bug), then OPAQUE from 20px and ramping to `--conn-a` at
     the leading edge (`--conn-lead`), then a hard cliff to transparent. So the
     line GROWS out from the previous dot's EDGE and its right tip both ends on
     the next dot's edge AND shares that dot's opacity. The 2px under-dot overlap
     (20px vs the 22px radius) hides the left seam; `--conn-lead` carries a small
     +overlap past the next dot's edge (set in _status.js) so there's no gap on
     the right. At rest (lead 9999px, a 1) the whole box is solid. */
  -webkit-mask-image: linear-gradient(to right, transparent 0, transparent 20px, #000 20px, rgba(0,0,0,var(--conn-a)) var(--conn-lead), rgba(0,0,0,0) var(--conn-lead));
          mask-image: linear-gradient(to right, transparent 0, transparent 20px, #000 20px, rgba(0,0,0,var(--conn-a)) var(--conn-lead), rgba(0,0,0,0) var(--conn-lead));
}
.step.done .conn { height: 4px; top: 20px; background: var(--gov-color-success-500); }
.step.done:has(+ .step.curr) .conn,
.step.done:has(+ .step.moreinfo) .conn {
  background: linear-gradient(to right, var(--gov-color-success-500), var(--gov-color-warning-500));
}
.step.done:has(+ .step.rejected) .conn {
  background: linear-gradient(to right, var(--gov-color-success-500), var(--gov-color-error-500));
}

/* ── Dot ──────────────────────────────────────────────────────────────────
   border-box so the 44px is the OUTER size (content-box made it 48px and the
   2px connector at top:21px then sat ~2px high of the true centre). */
.dot {
  box-sizing: border-box; position: relative;
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  font-size: 17px; font-weight: 500; color: var(--color-fg-tertiary);
  z-index: 2; flex: none;
}
/* The circle background lives on its OWN element so the opening animation can
   SCALE it (the "pop") WITHOUT scaling — and sub-pixel re-rasterising/snapping —
   the glyph/number, which stay in .dot's flow (centred, unscaled). z-index:-1
   keeps the circle behind them (normal flow paints above a negative layer). */
.dot__bg {
  position: absolute; inset: 0; border-radius: 50%; box-sizing: border-box;
  background: var(--color-bg-raised); border: 2px solid var(--color-border-default);
  z-index: -1; transform-origin: center;
}
.dot i { font-size: 25px; line-height: 1; }                 /* check / question / x — bigger */
.dot .bi-stopwatch { font-size: 22px; transform: translateY(-0.5px); }  /* optical centre (stem sits high) */
.step.done .dot__bg     { background: var(--gov-color-success-500); border-color: var(--gov-color-success-500); }
.step.rejected .dot__bg { background: var(--gov-color-error-500);   border-color: var(--gov-color-error-500); }
/* Active step: FILLED (white icon) like the past steps — not hollow. */
.step.curr .dot__bg,
.step.moreinfo .dot__bg { background: var(--gov-color-warning-500); border-color: var(--gov-color-warning-500); }
.step.done .dot, .step.rejected .dot, .step.curr .dot, .step.moreinfo .dot { color: #fff; }

/* ── Beacon / radar pulse on the active, NON-FINAL step ─────────────────────
   Three hollow rings emitted in succession (2nd when the 1st is mid-sweep, then
   the 3rd), fast-then-slowing (ease-out), then a short pause before repeating.
   The `.beacon` sits BEHIND the dot + callout (z:1) but ABOVE the connectors
   (z:0), so rings sweep over the lines without filling/cutting them. Runs only
   once the timeline has finished its opening animation (`.timeline.played`). */
.beacon {
  position: absolute; top: 0; left: 50%; transform: translateX(-50%);
  width: 44px; height: 44px; z-index: 1; pointer-events: none;
}
.wave {
  position: absolute; inset: 0; border-radius: 50%;
  border: 2px solid var(--gov-color-warning-500);
  opacity: 0;
}
@keyframes beacon {
  0%   { transform: scale(0.7); opacity: 0.5; }
  45%  { transform: scale(2);   opacity: 0; }
  100% { transform: scale(2);   opacity: 0; }
}
.step.curr.beaconing .wave,
.step.moreinfo.beaconing .wave { animation: beacon var(--st-beacon, 2.8s) cubic-bezier(0.1, 0.6, 0.3, 1) infinite; }
.step.curr.beaconing .wave:nth-child(2),
.step.moreinfo.beaconing .wave:nth-child(2) { animation-delay: 0.5s; }
.step.curr.beaconing .wave:nth-child(3),
.step.moreinfo.beaconing .wave:nth-child(3) { animation-delay: 1s; }

/* Frozen beacon — two static rings so the active step keeps its "special" look
   when motion is off. Driven by `html.motion-frozen` (JS sets it for BOTH the
   real reduced-motion test AND the playground's "instant" speed) and mirrored
   under the @media query as a no-JS fallback. */
html.motion-frozen .wave { animation: none !important; }
html.motion-frozen .step.curr .wave:nth-child(1),
html.motion-frozen .step.moreinfo .wave:nth-child(1) { opacity: 0.16; transform: scale(1.7); }
html.motion-frozen .step.curr .wave:nth-child(2),
html.motion-frozen .step.moreinfo .wave:nth-child(2) { opacity: 0.42; transform: scale(1.18); }
html.motion-frozen .step.curr .wave:nth-child(3),
html.motion-frozen .step.moreinfo .wave:nth-child(3) { opacity: 0; }
@media (prefers-reduced-motion: reduce) {
  .wave { animation: none !important; }
  .step.curr .wave:nth-child(1), .step.moreinfo .wave:nth-child(1) { opacity: 0.16; transform: scale(1.7); }
  .step.curr .wave:nth-child(2), .step.moreinfo .wave:nth-child(2) { opacity: 0.42; transform: scale(1.18); }
  .step.curr .wave:nth-child(3), .step.moreinfo .wave:nth-child(3) { opacity: 0; }
}

/* ── Labels — distinct PAST / ACTIVE / FUTURE ─────────────────────────── */
.lab { font-size: 13px; font-weight: 500; text-align: center; color: var(--color-fg-default); line-height: 1.25; }
.sub { font-size: 11.5px; color: var(--color-fg-secondary); text-align: center; font-family: var(--font-sans); }  /* standard font, NOT mono */
.step.pending .lab { color: var(--color-fg-tertiary); font-weight: 400; font-size: 12.5px; }

/* ── Active-step callout — tooltip-like box with an upward spike to the dot.
   `--cobg`/`--cobd`/`--cofg` drive body + the rotated-square spike + text so the
   arrow matches the 1px border exactly and fills with the (theme-flipping) bg.
   Dark uses OPAQUE pre-blended fills — the gov *-50 tokens are TRANSLUCENT in
   dark (rgba …,0.14), which let the spike's hidden geometry + the body's top
   border show through. ── */
.callout {
  --cobg: var(--gov-color-warning-50); --cobd: var(--gov-color-warning-500); --cofg: var(--gov-color-warning-700);
  position: relative; margin-top: 11px; z-index: 2;
  display: flex; flex-direction: column; align-items: center; gap: 1px;
  padding: 7px 12px; border-radius: 8px;
  border: 1px solid var(--cobd); background: var(--cobg);
  max-width: 168px;
}
.callout .lab { font-size: 14.5px; font-weight: 600; color: var(--cofg); }
.callout .sub { color: var(--cofg); opacity: 0.85; }
.callout::before {
  content: ""; position: absolute; top: -5px; left: 50%;
  width: 9px; height: 9px; transform: translateX(-50%) rotate(45deg);
  background: var(--cobg);
  border-left: 1px solid var(--cobd); border-top: 1px solid var(--cobd);
}
.step.rejected .callout { --cobg: var(--gov-color-error-50); --cobd: var(--gov-color-error-500); --cofg: var(--gov-color-error-700); }
body[data-theme="dark"] .callout                { --cobg: #34301c; }   /* opaque dark amber */
body[data-theme="dark"] .step.rejected .callout { --cobg: #341f22; }   /* opaque dark red */

/* ── Responsive collapse (container-query driven) ───────────────────────────
   The timeline never wraps or shrinks its labels; instead it DROPS steps as the
   container narrows, always keeping the meaningful ones:

     • full   (≥430px) : every step.
     • 2-step (250–429): the ACTIVE/terminal step ("focus") + the step right
                         BEFORE it in the DOM ("previous"). Because previous is
                         DOM-adjacent to focus, the connector between them keeps
                         its EXACT per-segment colour/gradient (done→active green→
                         warning, done→rejected green→red, done→done green) — none
                         of the line-drawing logic changes, it just spans a wider
                         gap (`.conn` is percentage-based, so it self-corrects).
     • mini   (<250px) : focus only, no connectors.

   "Focus" = the .curr/.moreinfo/.rejected step, or — when the request is fully
   complete (no active/terminal step) — the LAST step. Hidden steps are
   display:none; the grid's 1fr columns redistribute across whatever remains.
   These selectors qualify on `.timeline` itself, which is why the timeline must
   be a DESCENDANT of the size container (`.timeline-wrap`), not the container. */

/* focus = active / terminal, else the last step of an all-complete timeline */
@container (250px <= width < 430px) {
  .timeline .step { display: none; }
  .timeline .step.curr,
  .timeline .step.moreinfo,
  .timeline .step.rejected,
  .timeline:not(:has(.curr, .moreinfo, .rejected)) .step:last-child,
  /* previous = the DOM-adjacent step before focus (its .conn carries the gradient) */
  .timeline .step:has(+ .step.curr),
  .timeline .step:has(+ .step.moreinfo),
  .timeline .step:has(+ .step.rejected),
  .timeline:not(:has(.curr, .moreinfo, .rejected)) .step:nth-last-child(2) { display: flex; }
  /* The focus step's OWN outgoing connector points at a now-hidden step — hide it
     so only the previous step's connector (prev→focus) remains. */
  .timeline .step.curr .conn,
  .timeline .step.moreinfo .conn,
  .timeline .step.rejected .conn { display: none; }
}
@container (width < 250px) {
  .timeline .step { display: none; }
  .timeline .step.curr,
  .timeline .step.moreinfo,
  .timeline .step.rejected,
  .timeline:not(:has(.curr, .moreinfo, .rejected)) .step:last-child { display: flex; }
  .timeline .conn { display: none; }
}

/* ── Request-status PILL (.rstat) ───────────────────────────────────────────
   The compact one-line status token used on request cards / lists — same
   request-status component family as the timeline, so it lives here (single
   source of truth; consumed by the dataset preview + the prototype). `cls`
   keys mirror window.REQUEST_STATUS_META: submitted / in-review / more-info /
   approved / rejected. The gov *-50 fills are translucent in dark, so the pill
   tints correctly on the dark surface with no extra theme rules — same recipe
   as the chips. */
.rstat {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12px; font-weight: 600; line-height: 1;
  padding: 4px 10px; border-radius: 999px; white-space: nowrap;
}
.rstat i { font-size: 12px; }
.rstat--submitted { background: var(--gov-color-info-50);    color: var(--gov-color-info-700); }
.rstat--in-review { background: var(--gov-color-warning-50); color: var(--gov-color-warning-700); }
.rstat--more-info { background: var(--gov-color-warning-50); color: var(--gov-color-warning-700); }
.rstat--approved  { background: var(--gov-color-success-50); color: var(--gov-color-success-700); }
.rstat--rejected  { background: var(--gov-color-error-50);   color: var(--gov-color-error-700); }
