/* ============================================================
   M@B — Hero carousel (home page only)
   Added 2026-08-05 with the Modernist hero retheme.

   Loaded by html/hub/index.html ONLY. It is not part of hub.css because
   ~500 lines of slide internals do not belong on /services, /about or
   /contact, none of which render a carousel.

   ⚠️ tools/build.py copies an EXPLICIT whitelist and rmtree's deploy/ first.
   This file ships because it is named in SITE_CSS there. If you rename it and
   do not rename it there too, it vanishes from deploy/ with no error at all —
   the page then renders an unstyled stack of slides.

   ── Why two reds ─────────────────────────────────────────────────────────
   The design calls for one accent, #ec3013. Measured against the ground
   (#f3f2f2) it is 3.76:1, and white on it is 4.20:1 — both BELOW the 4.5:1
   WCAG AA floor for normal-size text.

   That is not a theoretical problem here. `.github/workflows/a11y.yml` runs
   pa11y-ci with BOTH axe and HTML_CodeSniffer at WCAG2AA with `threshold: 0`
   against `/`, so any red text or red-backed text on this page fails the
   build. And the large-text exemption (3:1 at >=24px, or >=18.66px bold)
   cannot be leaned on, because every size in this panel is a `cqw` — at the
   380px width in the acceptance criteria the panel is ~340px, and a label
   that is 24px at the 520px reference is 15.7px there.

   So: --m-accent (#ec3013) is used ONLY where no text is involved — rails,
   markers, rules, chip fills, the square dot. --m-accent-deep (#c62810) is
   used wherever red is text or sits behind text: 5.06:1 on the ground and
   5.66:1 under white. Same hue, same family, passes at any size.
   ============================================================ */

/* ============================================================
   1. HERO SPLIT
   Left column keeps the existing hero content; the carousel is the right
   column. Source order puts the carousel second, so the 900px collapse to a
   single column drops it below the text with no reordering.
   ============================================================ */

.hero-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 520px);
  gap: 56px;
  align-items: center;
}

/* The hero h1 caps at 14ch site-wide. In a half-width column that is already
   the constraint, so it is left alone — but the lede's 640px is wider than
   the column and would otherwise force the grid's 1fr to overflow. */
.hero-grid .hero-lede { max-width: none; }

@media (max-width: 900px) {
  .hero-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: 44px;
  }
  /* Full width on mobile, still capped at the panel's own 520px. */
  .hero-carousel { width: 100%; }
}

/* ============================================================
   2. CAROUSEL SHELL
   ============================================================ */

.hero-carousel {
  max-width: 520px;
}

.hc-viewport {
  position: relative;
  width: 100%;
  max-width: 520px;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: transparent;
}

/* Slides stack. `visibility` rather than opacity alone on the inactive ones,
   for three reasons that all bite:
     - a CTA inside a fully transparent slide is still tabbable, so the tab
       order would walk through six links to reach three;
     - screen readers would announce all three slides at once;
     - pa11y audits whatever is in the tree, so all three slides' text would
       be contrast-checked against a background they are not actually over.
   The delayed `visibility` transition holds the outgoing slide visible for
   the whole crossfade and only then removes it. */
.hc-viewport > [data-hc-slide] {
  position: absolute;
  inset: 0;
  max-width: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.6s ease, visibility 0s linear 0.6s;
}

.hc-viewport > [data-hc-slide].is-active {
  opacity: 1;
  visibility: visible;
  transition: opacity 0.6s ease, visibility 0s linear 0s;
}

/* --- Dots --- */
.hc-dots {
  display: flex;
  gap: 10px;
  margin-top: 20px;
  padding: 0;
}

.hc-dot {
  width: 14px;
  height: 14px;
  padding: 0;
  border: 2px solid var(--m-rule);
  border-radius: 0;
  background: transparent;
  cursor: pointer;
  transition: background 0.2s ease, border-color 0.2s ease;
}

.hc-dot:hover { border-color: var(--m-ink); }

.hc-dot[aria-current="true"] {
  background: var(--m-accent);
  border-color: var(--m-accent);
}

.hc-dot:focus-visible {
  outline: 2px solid var(--m-accent);
  outline-offset: 2px;
}

/* ============================================================
   3. SLIDE PANEL — shared
   `container-type: inline-size` makes 1cqw == 1% of the panel's own width,
   so every size below is a fraction of the panel and the whole thing scales
   as one graphic. That is what keeps it from clipping at any width: the
   content box and the type inside it shrink by the same factor.

   Sizes are cqw and em ONLY. A px floor on the small labels was tried and
   reverted — it makes the type grow relative to the panel as the panel
   shrinks, which is precisely how the held end state overflows 4:5 at 380px.
   ============================================================ */

matb-slide-timeline,
matb-slide-dealflow,
matb-slide-numbers,
matb-slide-dms {
  display: block;
  position: relative;
  width: 100%;
  max-width: 520px;
  aspect-ratio: 4 / 5;
  background: transparent;
  overflow: hidden;
  container-type: inline-size;
  color: var(--m-ink);
  line-height: 1.3;
  -webkit-font-smoothing: antialiased;
}

/* The reveal every `{{ }}` hole in the source drove: opacity plus a small
   translateY. The JS writes el.style.opacity / el.style.transform directly;
   this only declares the transition they animate over. */
.ms-rv {
  opacity: 0;
  transform: translateY(0.5em);
  transition: opacity 0.5s ease, transform 0.5s ease;
  will-change: opacity, transform;
}

/* reset() sets this for one forced reflow so the revert to the pre-run state
   lands instantly instead of easing backwards for half a second. Nothing else
   should ever set it. */
matb-slide-timeline.is-resetting *,
matb-slide-dealflow.is-resetting *,
matb-slide-numbers.is-resetting *,
matb-slide-dms.is-resetting * {
  transition: none !important;
  animation: none !important;
}

.ms-kicker {
  font-size: 2.5cqw;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--m-muted);
  margin: 0;
}

.ms-head {
  font-size: 7cqw;
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.02em;
  margin: 2.6cqw 0 0;
}

.ms-close {
  font-size: 3.1cqw;
  font-weight: 600;
  line-height: 1.35;
  margin: 0;
}

/* CTA. Square, 2px, deep red under white — 5.66:1. */
.ms-cta {
  display: inline-block;
  align-self: flex-start;
  padding: 2.2cqw 3.4cqw;
  border: 2px solid var(--m-accent-deep);
  border-radius: 0;
  background: var(--m-accent-deep);
  color: #fff;
  font-size: 2.9cqw;
  font-weight: 700;
  letter-spacing: 0.02em;
  text-decoration: none;
}

.ms-cta:hover { background: var(--m-ink); border-color: var(--m-ink); }

.ms-cta:focus-visible {
  outline: 2px solid var(--m-accent);
  outline-offset: 2px;
}

/* The pulse is on the CTA's box-shadow only — not transform, which would
   reflow nothing but does force a composite of the whole panel every frame. */
@keyframes ms-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(236, 48, 19, 0.45); }
  50%      { box-shadow: 0 0 0 0.9cqw rgba(236, 48, 19, 0); }
}

.ms-cta.is-pulsing { animation: ms-pulse 2.4s ease-out infinite; }

/* ============================================================
   4. SLIDE 1 — compliance timeline
   ============================================================ */

.t-wrap {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
}

.t-axis {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-top: 3.4cqw;
  padding-bottom: 1.4cqw;
  border-bottom: 2px solid var(--m-rule);
  font-size: 2.3cqw;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--m-muted);
}

.t-axis .t-axis-end { color: var(--m-accent-deep); }

/* Grows to fill whatever the fixed blocks above and below leave. Sized purely
   by content, the four milestones ended a third of the way up the panel and
   left a dead gap above the deadline band; the rails are the spine of this
   slide and should reach it. min-height on the row is the floor for the short
   panel, flex-grow distributes everything past that. */
.t-rows {
  flex: 1;
  display: flex;
  flex-direction: column;
  margin-top: 2.6cqw;
}

.t-row {
  flex: 1;
  display: grid;
  grid-template-columns: 3.2cqw 1fr;
  column-gap: 2.6cqw;
  min-height: 10.4cqw;
}

/* The rail is the 2px track; .t-fill is the accent growing 0% -> 100%.
   Height, not scaleY — a scaled 2px line blurs on a fractional device pixel
   and the marker riding its end would need the inverse transform. */
.t-rail {
  position: relative;
  width: 2px;
  height: 100%;
  margin-left: 0.6cqw;
  background: var(--m-rule);
}

.t-fill {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 0;
  background: var(--m-accent);
  transition: height 0.8s linear;
}

.t-marker {
  position: absolute;
  top: 0;
  left: 50%;
  width: 12px;
  height: 12px;
  margin-left: -6px;
  background: var(--m-accent);
}

.t-label { padding-bottom: 2.2cqw; }

.t-num {
  font-size: 2.2cqw;
  font-weight: 700;
  letter-spacing: 0.16em;
  color: var(--m-accent-deep);
}

.t-text {
  font-size: 3cqw;
  font-weight: 600;
  line-height: 1.25;
  margin-top: 0.5cqw;
}

/* --- Deadline band --- */
.t-band {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 2cqw;
  margin-top: auto;
  padding: 2.6cqw 3cqw;
  background: var(--m-accent-deep);
  color: #fff;
  /* Slides in from the left as it fades. Overwritten inline by the JS. */
  transform: translateX(-3cqw);
  opacity: 0;
  transition: opacity 0.5s ease, transform 0.5s ease;
}

.t-band-date {
  font-size: 3.6cqw;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.t-band-sub {
  font-size: 2.3cqw;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  opacity: 0.9;
  margin-top: 0.3cqw;
}

.t-count { text-align: right; }

.t-count-days {
  font-size: 5.4cqw;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

.t-count-clock {
  font-size: 2.5cqw;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.06em;
  opacity: 0.9;
  margin-top: 0.4cqw;
}

.t-ready {
  font-size: 3.3cqw;
  font-weight: 700;
  margin: 3cqw 0 0;
}

.t-cta-row { margin-top: 2.2cqw; }

/* ============================================================
   5. SLIDE 2 — deal flow
   ============================================================ */

.d-wrap {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
}

.d-flow {
  position: relative;
  flex: 1;
  display: flex;
}

.d-track { position: relative; width: 56%; }
.d-markers { position: relative; flex: 1; margin-left: 4%; }

/* The path box is inset from the track so the document icon — which is
   centred on its point — still sits fully inside the panel at both ends of
   the path. Without it the doc's bottom half is clipped when it lands. */
.d-pathbox {
  position: absolute;
  left: 0;
  right: 0;
  top: 5cqw;
  bottom: 5cqw;
}

.d-node {
  position: absolute;
  left: 0;
  right: 0;
  transform: translateY(-50%);
}

.d-node-label {
  font-size: 2.2cqw;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--m-muted);
  margin-bottom: 0.8cqw;
}

/* The descriptor under each station name — what M@B works on there.
   Sentence case on purpose where the label above is uppercase; it reads as
   the answer to the label rather than as a second heading. */
.d-node-sub {
  font-size: 2cqw;
  font-weight: 500;
  line-height: 1.25;
  color: var(--m-muted);
  margin: -0.4cqw 0 0.8cqw;
}

.d-node-rule {
  height: 2px;
  background: var(--m-rule);
}

.d-svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  /* Progressive reveal is a clip-path inset, NOT stroke-dasharray.
     With vector-effect="non-scaling-stroke" under preserveAspectRatio="none"
     the dash pattern is resolved in SCREEN space while the path is in user
     space, so a non-uniform scale tiles the dashes and the "one line" breaks
     into fragments that change count with the viewport. The inset is applied
     to rendered output and is immune to that. The -20% left/right bleed keeps
     the stroke's own width from being shaved at the widest bends. */
  clip-path: inset(0 -20% 100% -20%);
}

.d-svg path {
  fill: none;
  stroke: #8f8a87;
  stroke-linecap: round;
}

/* The travelling document. An HTML element, not an SVG node, so its inner
   rules are ordinary boxes and it never inherits the non-uniform scale that
   preserveAspectRatio="none" applies to everything inside the <svg>. */
.d-doc {
  position: absolute;
  left: 0;
  top: 0;
  width: 7cqw;
  height: 8.6cqw;
  margin: -4.3cqw 0 0 -3.5cqw;
  padding: 1.1cqw 0.9cqw;
  background: #fff;
  border: 2px solid var(--m-ink);
  opacity: 0;
  transition: opacity 0.35s ease;
}

.d-doc i {
  display: block;
  height: 2px;
  margin-bottom: 0.9cqw;
  background: var(--m-rule);
}

.d-doc i:nth-child(2) { width: 78%; }
.d-doc i:nth-child(3) { width: 55%; }

/* --- Markers --- */
.d-marker {
  position: absolute;
  left: 0;
  right: 0;
  display: flex;
  align-items: center;
  gap: 1.4cqw;
  transform: translateY(-50%);
}

.d-chip {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 4.4cqw;
  height: 4.4cqw;
  background: var(--m-accent-deep);
  color: #fff;
  font-size: 2.8cqw;
  font-weight: 700;
  line-height: 1;
  transition: background 0.3s ease;
}

.d-marker.is-resolved .d-chip { background: var(--m-positive); }

.d-chip-text {
  font-size: 2.4cqw;
  font-weight: 600;
  line-height: 1.2;
  transition: color 0.3s ease;
}

.d-marker.is-resolved .d-chip-text { color: var(--m-positive); }

.d-close {
  margin-top: 2.4cqw;
  padding-top: 2.4cqw;
  border-top: 2px solid var(--m-rule);
}

/* ============================================================
   6. SLIDE 3 — numbers
   ============================================================ */

.n-wrap {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
}

.n-sub {
  font-size: 2.5cqw;
  font-weight: 700;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--m-accent-deep);
  margin: 0.6cqw 0 0;
}

/* ⚠️ The vertical budget here is tight and the slack is NOT proportional.
   Everything else in this panel is cqw, so it scales with the panel and the
   layout holds at any width — but the 2px rules do not scale. Four card rules
   plus the CTA's two borders are a FIXED 12px, which at the 520px reference is
   2.3cqw of the 125cqw the 4:5 panel gives you and at 340px is 3.5cqw. Budget
   for it here rather than at one width: the first version fit at 520 and hung
   the CTA 8px below the panel at BOTH sizes, because a constant overflow does
   not shrink when you shrink the panel. */
/* Same reasoning as .t-rows: the four cards distribute the free space instead
   of stacking at the top and leaving a 400px hole above the closing line. */
.n-cards {
  flex: 1;
  display: flex;
  flex-direction: column;
  margin-top: 2.4cqw;
}

.n-card {
  flex: 1;
  padding-top: 1.5cqw;
  border-top: 2px solid var(--m-rule);
  margin-bottom: 1.8cqw;
}

.n-label {
  font-size: 2.2cqw;
  font-weight: 700;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--m-muted);
}

/* Fixed height in em so a value line holds its row whatever it contains. It
   was introduced when card 4 swapped its odometer for a line of copy mid-run;
   that swap is gone (2026-08-09, the card is a statutory figure now), but the
   floor still earns its place — it keeps the four rows aligned while an
   odometer is mid-count and drops its leading zeros out of flow. */
.n-value {
  display: flex;
  align-items: center;
  min-height: 1.05em;
  margin-top: 1cqw;
  font-size: 8.4cqw;
  font-weight: 700;
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}

.n-card-accent .n-value { color: var(--m-accent-deep); }

.n-resolved {
  font-size: 5.2cqw;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--m-accent-deep);
}

/* Card 2 carries copy rather than a figure, because SB 766's remedies are
   non-exclusive and no per-violation dollar amount can be sourced to it. The
   value line is smaller than an odometer would be so the two real numbers on
   this slide stay the loudest things on it. */
.n-value-text { min-height: 0; }
.n-value-text .n-resolved { font-size: 4.6cqw; }

.n-note {
  margin-top: 0.9cqw;
  font-size: 2.4cqw;
  font-weight: 600;
  line-height: 1.3;
  color: var(--m-muted);
}

/* --- Odometer ---
   One `1em` tall window per digit, clipped, over a 0-9 column that is
   translated by -N em. `1ch` is exactly the advance of "0" in the current
   font, which with tabular-nums is every digit's advance — so the windows
   line up without a hand-tuned width that would drift if the face changed. */
.n-odo {
  display: inline-flex;
  align-items: flex-start;
}

.n-d {
  position: relative;
  display: inline-block;
  width: 1ch;
  height: 1em;
  overflow: hidden;
}

.n-dc {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  transition: transform 0.3s cubic-bezier(0.2, 0.8, 0.2, 1);
}

.n-dc span {
  display: block;
  height: 1em;
  line-height: 1;
  text-align: center;
}

.n-sep { display: inline-block; }

.n-close {
  margin-top: auto;
  padding-top: 2cqw;
}

.n-cta-row { margin-top: 2.4cqw; }

/* ============================================================
   6b. SLIDE 4 — DMS readiness
   Same skeleton as slide 3: rows distribute the free vertical space, the
   closing line and CTA anchor the bottom. The same fixed-pixel budget
   warning applies — four 2px row rules plus the CTA's two borders are a
   constant 12px that does not shrink with the panel.

   The chip is decorative (aria-hidden in the JS); the status text is the
   accessible carrier of each verdict. Colours follow the two-red rule:
   nothing red here at all — the check chip is --m-positive (5.32:1 under
   white), the "none" chip is an outlined box whose em dash is --m-muted.
   ============================================================ */

.v-wrap {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
}

.v-rows {
  flex: 1;
  display: flex;
  flex-direction: column;
  margin-top: 2.4cqw;
}

.v-row {
  flex: 1;
  display: grid;
  grid-template-columns: 5.4cqw 1fr;
  column-gap: 2.6cqw;
  align-items: center;
  padding-top: 1.5cqw;
  border-top: 2px solid var(--m-rule);
  margin-bottom: 1.4cqw;
}

.v-chip {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 5.4cqw;
  height: 5.4cqw;
  border: 2px solid var(--m-rule);
  background: transparent;
  color: var(--m-muted);
  font-size: 3cqw;
  font-weight: 700;
  line-height: 1;
  transition: background 0.3s ease, border-color 0.3s ease, color 0.3s ease;
}

.v-chip.is-ok {
  background: var(--m-positive);
  border-color: var(--m-positive);
  color: #fff;
}

/* .is-none keeps the resting outline — the class exists so reset() has
   something symmetrical to remove, not because it restyles anything. */

.v-name {
  font-size: 3.4cqw;
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.15;
}

.v-status {
  font-size: 2.4cqw;
  font-weight: 600;
  color: var(--m-muted);
  margin-top: 0.4cqw;
}

.v-status.is-ok { color: var(--m-positive); }

.v-note {
  font-size: 2cqw;
  font-weight: 600;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--m-muted);
  margin: 0.4cqw 0 0;
}

.v-close {
  margin-top: auto;
  padding-top: 2cqw;
}

.v-cta-row { margin-top: 2.4cqw; }

/* ============================================================
   7. REDUCED MOTION
   Every slide renders its designed END state immediately and no timer,
   transition or rAF ever runs. The JS checks the same query and takes a
   separate code path; these rules only make sure nothing that is already
   mid-flight keeps easing.
   ============================================================ */

@media (prefers-reduced-motion: reduce) {
  .hc-viewport > [data-hc-slide] { transition: none; }
  .ms-rv,
  .t-fill,
  .t-band,
  .d-doc,
  .d-chip,
  .d-chip-text,
  .n-dc,
  .v-chip { transition: none; }
  .ms-cta.is-pulsing { animation: none; }
}
