/* ===========================================================================
   MAGISTER DIGITAL  -  "Living Ledger" PARALLAX / DEPTH LAYER
   ---------------------------------------------------------------------------
   Date: 2026-05-30
   Purpose: turn the flat, correctly-architected pages into a scroll-driven
   depth story WITHOUT touching palette, copy, fonts, or schema.

   HARD RULES honoured here:
     - Palette LOCKED: graphite #0F1115 / gold #D4AF37 / platinum #C0C3C7 /
       white / near-white #F7F7F5. No new hues. No blue, no coral.
     - transform + opacity ONLY (compositor thread). Never width/height/top/
       left/box-shadow in any keyframe or scroll animation.
     - Motion is a COSMETIC enhancement layer. Every selling/ranking word is
       already in server HTML. Nothing here is rendered or revealed by JS;
       the only JS is a parallax/track FALLBACK for the same effect CSS does.
     - prefers-reduced-motion + no-JS => the FULL static page, no hidden copy.
       Default (no .js, no animation-timeline support) = static final state.
       We NEVER strand content at opacity:0 without a failsafe  -  the existing
       sitewide reveal failsafe (magister-light-overrides.css) is preserved;
       every new parallax/scrim/split here ships its own reduce/static branch.

   MOTION CEILING (per page, enforced by which selectors fire where):
     <=2 parallax depth layers · <=2 sticky pins · <=1 horizontal track ·
     <=1 marquee · <=7 total interactions. Premium, not circus.
   =========================================================================== */

/* ===========================================================================
   0. TOKENS local to this layer (no palette change  -  these are tuning knobs)
   =========================================================================== */
:root {
  --md-px-ease: cubic-bezier(0.16, 1, 0.3, 1); /* luxury slow-out */
  --md-px-drift: 9%;     /* max parallax bg travel  -  subtle, editorial */
  --md-px-scrim: linear-gradient(
    180deg,
    rgba(15, 17, 21, 0.04) 0%,
    rgba(15, 17, 21, 0.10) 38%,
    rgba(15, 17, 21, 0.62) 100%
  );
  --md-px-scrim-side: linear-gradient(
    90deg,
    rgba(15, 17, 21, 0.78) 0%,
    rgba(15, 17, 21, 0.34) 46%,
    rgba(15, 17, 21, 0) 100%
  );
}

/* ===========================================================================
   1. SCENE DEPTH  -  every .md-scene becomes a layered scene, not a flat band.
   A faint platinum/gold hairline + a 1px ledger-dot texture on graphite gives
   each band foreground/background separation. Cosmetic, no layout shift.
   =========================================================================== */
.md-scene { position: relative; }

/* Chapter seam: a thin gold->transparent rule drawn at the TOP edge of every
   deep (graphite) scene, so chapters read as distinct strata, not one wall. */
.md-section--deep.md-scene::before,
.md-scene--deep::before {
  content: "";
  position: absolute;
  inset-inline: 0;
  top: 0;
  height: 1px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(212, 175, 55, 0.55) 50%,
    transparent 100%
  );
  pointer-events: none;
  z-index: 2;
}

/* ===========================================================================
   2. PARALLAX HERO  -  bg photo drifts slower than copy (depth-1).
   The H1 stays the LCP text element (it is plain DOM text, painted first).
   We parallax ONLY the figure's image inside the visual column, on a
   view()-timeline so it can never hide content from a crawler.
   =========================================================================== */
.md-scene-hero__figure {
  overflow: hidden;          /* clip the drifting image to its frame */
  border-radius: 12px;
}
.md-scene-hero__figure img {
  will-change: transform;
  transform: scale(1.06);     /* headroom for Ken-Burns drift, no CLS */
}

/* Baseline Ken-Burns: a slow, always-on scale/drift so the hero feels alive
   even before any scroll (the LCP photo is above the fold and never "enters"
   the viewport, so a scroll/view timeline alone leaves it static). Time-based
   loop, transform-only, killed by reduced-motion. */
@media (prefers-reduced-motion: no-preference) {
  .md-scene-hero__figure img {
    animation: md-px-kenburns 22s ease-in-out infinite alternate;
  }
  @keyframes md-px-kenburns {
    from { transform: scale(1.05) translate(0, 0); }
    to   { transform: scale(1.12) translate(-1.5%, -2%); }
  }
}

/* On scroll, the hero photo ALSO drifts vertically against the page (depth-1).
   scroll(root) ties it to page scroll so it moves immediately, unlike view()
   which never fires for an already-on-screen hero. The Ken-Burns loop above is
   overridden by this richer scroll animation where the timeline is supported. */
@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    .md-scene-hero__figure img {
      animation: md-px-hero-drift linear both;
      animation-timeline: scroll(root block);
      animation-range: 0 90vh;
    }
    @keyframes md-px-hero-drift {
      from { transform: scale(1.06) translateY(-3%); }
      to   { transform: scale(1.12) translateY(6%); }
    }
  }
}

/* ===========================================================================
   3. SCRIM SCENES  -  turn framed photos into full-bleed-feeling scenes.
   Any .md-verticals__media / .md-svc-signature__figure / hero figure gets a
   graphite->transparent scrim so type stays AA over any image AND a slow
   parallax drift. The scrim element already exists in DOM for verticals
   (.md-scrim); we add a ::after scrim to figures that lack one.
   =========================================================================== */
.md-verticals__media,
.md-svc-signature__figure,
.md-asym-feature__media,
.md-pull-quote-section__image,
.md-process__visual {
  position: relative;
  overflow: hidden;
}
/* legal/service images live in bare figures or as .md-asym-feature__img  -  give
   the image's containing figure the clip so the drift stays inside its frame. */
.md-asym-feature__media,
.md-pull-quote-section__image { border-radius: 12px; }
.md-verticals__media img,
.md-svc-signature__figure img,
.md-asym-feature__img,
.md-pull-quote-section__image img {
  will-change: transform;
}

/* Existing .md-scrim overlay  -  deepen for legibility, keep gold-graphite. */
.md-verticals__media .md-scrim {
  position: absolute;
  inset: 0;
  background: var(--md-px-scrim);
  pointer-events: none;
}

/* Figures WITHOUT a scrim element get a bottom scrim via ::after so any
   caption/title layered over them reads. Pure cosmetic overlay. */
.md-svc-signature__figure::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    180deg,
    transparent 0%,
    transparent 52%,
    rgba(15, 17, 21, 0.34) 100%
  );
  pointer-events: none;
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .md-verticals__media img,
    .md-svc-signature__figure img,
    .md-asym-feature__img,
    .md-pull-quote-section__image img {
      animation: md-px-img-drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
      transform: scale(1.08);
    }
    @keyframes md-px-img-drift {
      from { transform: scale(1.12) translateY(-4%); }
      to   { transform: scale(1.04) translateY(4%); }
    }
  }
}

/* ===========================================================================
   4. THE LIVING LEDGER  -  pinned-dwell proof panel.
   The flat teal stat-bar becomes a panel that PINS briefly inside its own
   scope as its mono figures drift up into place, then releases. We CANNOT add
   markup (copy/schema/AEO locked), so the pin is bounded by the ledger's OWN
   sticky context: top is offset under the fixed header, and a clipping ::after
   chapter-seam marks the release. We deliberately do NOT use a page-length
   sticky (it would stay stuck behind every later section  -  verified bug).
   The "living" beat is the figure rise + count-up + a gold underline that
   draws across on entry. Final values = the static DOM text (count-up lands
   exactly on it). Width reserved via tabular-nums so CLS stays 0.
   The ledger stays graphite (#0F1115) per the v4.6.3 cascade  -  gold "›".
   =========================================================================== */

/* A gold tally rail that draws across the band as it enters  -  the visual
   "this is being totalled" cue, transform-only (scaleX), compositor-safe. */
.md-live-ledger { position: relative; overflow: hidden; }
.md-live-ledger::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 100%;
  background: linear-gradient(90deg, var(--md-accent, #D4AF37), rgba(212, 175, 55, 0));
  transform: scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
}

/* Each ledger figure rises into place on entry; the rail draws across.
   transform/opacity only. animation-timeline so it can never hide content. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .md-live-ledger__item {
      animation: md-px-ledger-rise linear both;
      animation-timeline: view();
      animation-range: entry 0% entry 85%;
    }
    @keyframes md-px-ledger-rise {
      from { opacity: 0.3; transform: translateY(12px); }
      to   { opacity: 1;   transform: translateY(0); }
    }
    .md-live-ledger::after {
      animation: md-px-ledger-rail linear both;
      animation-timeline: view();
      animation-range: entry 10% cover 30%;
    }
    @keyframes md-px-ledger-rail {
      from { transform: scaleX(0); }
      to   { transform: scaleX(1); }
    }
  }
}

/* Count-up reserves final width: tabular figures so "0 → 7" never reflows. */
.md-live-ledger__figure,
.md-live-ledger__item > span {
  font-variant-numeric: tabular-nums slashed-zero;
}

/* ===========================================================================
   5. FULL-HEIGHT ALTERNATING SPLIT  -  kills the flat cream text-band/void.
   Service "signature" rows (.md-svc-signature) and any md-scene that pairs a
   figure with copy get a denser, edge-leaning split so no element floats
   centered in margin. Pure layout density; transform-only motion on entry.
   =========================================================================== */
.md-svc-signature {
  display: grid;
  gap: clamp(24px, 4vw, 56px);
  align-items: center;
}
@media (min-width: 900px) {
  .md-svc-signature { grid-template-columns: 1fr 1fr; }
  /* alternate the photo side down the page for rhythm */
  .md-section--deep + .md-section .md-svc-signature .md-svc-signature__figure,
  .md-svc-signature--flip .md-svc-signature__figure { order: 2; }
}
.md-svc-signature__figure {
  border-radius: 12px;
  aspect-ratio: 4 / 3;        /* reserve slot  -  no CLS */
}
.md-svc-signature__figure img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* ===========================================================================
   6. PILLARS DEPTH  -  overlapping/tilted cards drifting at different z-rates.
   The 3 founder-owned pillar cards lift on hover and reveal with a subtle
   stagger + <=3deg tilt that resolves to flat. Adds depth, no gutter voids.
   =========================================================================== */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .md-pillars__card {
      animation: md-px-pillar-drift linear both;
      animation-timeline: view();
      animation-range: entry 5% cover 35%;
      will-change: transform, opacity;
    }
    @keyframes md-px-pillar-drift {
      from { opacity: 0.4; transform: translateY(26px); }
      to   { opacity: 1;   transform: translateY(0); }
    }
    /* differential drift so cards separate in z (depth-2, within ceiling) */
    .md-pillars__card:nth-child(2) { animation-range: entry 5% cover 45%; }
    .md-pillars__card:nth-child(3) { animation-range: entry 5% cover 55%; }
  }
}

/* ===========================================================================
   7. WHO-THIS-IS-FOR  -  fill the band, kill the right-margin void.
   The real DOM is .md-qualify__cols (a 1fr/1fr card pair). The earlier 58/42
   override targeted a non-existent .md-qualify__lists and stranded the cards
   in the left half with an empty right third (verified dead-space bug). Fix:
   let the two cards grow to a balanced, centered max-width so they fill the
   chapter, add a platinum seam between them, and seat the CTA full-width
   underneath. Cards lift on entry (transform/opacity) for depth, no pin.
   =========================================================================== */
@media (min-width: 760px) {
  .md-scene--qualify .md-qualify__cols {
    max-width: 1120px;
    margin-inline: auto;
    gap: clamp(28px, 4vw, 56px);
    position: relative;
  }
  /* platinum hairline seam between the two qualifier columns */
  .md-scene--qualify .md-qualify__cols::before {
    content: "";
    position: absolute;
    top: 8%;
    bottom: 8%;
    left: 50%;
    width: 1px;
    background: linear-gradient(180deg, transparent, rgba(192, 195, 199, 0.5), transparent);
    transform: translateX(-50%);
    pointer-events: none;
  }
}
.md-scene--qualify .md-scene__cta {
  margin-top: clamp(28px, 4vw, 48px);
  text-align: center;
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .md-scene--qualify .md-qualify__col {
      animation: md-px-qualify-rise linear both;
      animation-timeline: view();
      animation-range: entry 0% cover 30%;
    }
    @keyframes md-px-qualify-rise {
      from { opacity: 0.45; transform: translateY(20px); }
      to   { opacity: 1;    transform: translateY(0); }
    }
  }
}

/* ===========================================================================
   8. VERTICALS HORIZONTAL TRACK  -  sticky pinned, scrubbed horizontally.
   ONE per page (the horizontal budget). On desktop the 3-up grid becomes a
   pinned track that translates X on vertical scroll. Mobile => snap carousel.
   CSS animation-timeline:scroll() drives it; JS fallback scrubs the same X.
   No scroll-jacking  -  the page scrolls normally; the track just translates.
   =========================================================================== */
.md-verticals__grid {
  position: relative;
}

/* MOBILE: horizontal snap carousel (the mobile-length fix). */
@media (max-width: 859px) {
  .md-verticals__grid {
    display: grid;
    grid-auto-flow: column;
    grid-auto-columns: 82%;
    gap: var(--md-space-4, 16px);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    scroll-padding-inline: 18px;
    padding-bottom: 8px;
  }
  .md-verticals__card { scroll-snap-align: center; }
}

/* DESKTOP track: the grid is pinned and translated on scroll. We only engage
   this when scroll-timeline is supported AND motion is allowed  -  otherwise the
   plain 3-up grid (already good) stays. Guarded so it never breaks layout. */
@supports (animation-timeline: scroll()) {
  @media (min-width: 980px) and (prefers-reduced-motion: no-preference) {
    .md-scene--verticals.md-track-on {
      /* The pin runway. With only 3 cards the horizontal overflow is modest, so
         a 220vh runway left the track frozen for most of the scroll (the "blank
         pinned tail" Codex flagged). Trimmed to 165vh so the track finishes on
         the last card right as the pin releases  -  no dead dwell. */
      min-height: 165vh;
    }
    .md-scene--verticals.md-track-on > .md-container {
      position: -webkit-sticky;
      position: sticky;
      top: 0;
      min-height: 100vh;
      display: flex;
      flex-direction: column;
      justify-content: center;
      overflow: hidden;
    }
    .md-scene--verticals.md-track-on .md-verticals__grid {
      display: flex;
      gap: clamp(20px, 2vw, 32px);
      width: max-content;
      will-change: transform;
      animation: md-px-track linear both;
      animation-timeline: scroll(nearest block);
    }
    .md-scene--verticals.md-track-on .md-verticals__card {
      flex: 0 0 clamp(380px, 32vw, 500px);
    }
    /* CONTENT-WIDTH-BASED translate (Codex bug fix): the old fixed -58% assumed
       a wide track and overscrolled a 3-card track into a blank tail. Here 100%
       == the track's OWN content width (it is width:max-content), so
       `-100% + visibleWidth` ends with the track's right edge flush to the
       visible right edge  -  exactly on the last card, for ANY card count.
         visibleWidth = container content width = min(1320px, 100vw) - 2*padding
       P2 Codex fix (2026-05-30): the endpoint hardcoded 80px of total padding
       (--md-space-7 * 2, the >=1024px value). But the .md-container padding is
       RESPONSIVE: --md-space-6 (32px) at 720-1023px => 64px total. In the
       980-1023px band the track therefore subtracted 16px too much and finished
       with a blank tail. Use a var that tracks the ACTUAL responsive padding so
       the endpoint is correct at every width (64px in this band, 80px >=1024px).
       min(0px, ...) clamps to 0 if the track is narrower than the viewport, so a
       short track simply doesn't translate (no blank tail, no overscroll). */
    /* Default for the 980-1023px band: container padding is --md-space-6 (32px)
       => 64px total. The >=1024px override (--md-space-7, 80px total) is set in
       a sibling top-level block below to avoid relying on @media nesting. */
    .md-scene--verticals.md-track-on { --md-track-pad: 64px; }
    @keyframes md-px-track {
      from { transform: translateX(0); }
      to   { transform: translateX(min(0px, calc(min(1320px, 100vw) - var(--md-track-pad, 80px) - 100%))); }
    }
    /* gold progress rail under the pinned track */
    .md-scene--verticals.md-track-on > .md-container::after {
      content: "";
      position: absolute;
      left: clamp(24px, 5vw, 80px);
      right: clamp(24px, 5vw, 80px);
      bottom: clamp(28px, 5vh, 56px);
      height: 2px;
      background: rgba(192, 195, 199, 0.18); /* platinum track */
      transform-origin: 0 50%;
    }
    .md-scene--verticals.md-track-on > .md-container::before {
      content: "";
      position: absolute;
      left: clamp(24px, 5vw, 80px);
      bottom: clamp(28px, 5vh, 56px);
      height: 2px;
      width: clamp(24px, 5vw, 80px);
      background: var(--md-accent, #D4AF37);
      z-index: 1;
      animation: md-px-rail linear both;
      animation-timeline: scroll(nearest block);
    }
    @keyframes md-px-rail {
      from { width: clamp(24px, 5vw, 80px); }
      to   { width: calc(100% - 2 * clamp(24px, 5vw, 80px)); }
    }
  }
}
/* P2 sibling override (no @media nesting): at >=1024px the .md-container
   padding becomes --md-space-7 (40px) => 80px total, so the track endpoint must
   subtract 80px, not 64px. Same @supports + reduced-motion guard as above. */
@supports (animation-timeline: scroll()) {
  @media (min-width: 1024px) and (prefers-reduced-motion: no-preference) {
    .md-scene--verticals.md-track-on { --md-track-pad: 80px; }
  }
}

/* ===========================================================================
   9. OFFER PIN + GIANT-NUMBER PROOF  -  the second of <=2 pins.
   The offer card pins (already position:sticky in homepage-journey); we add a
   giant mono numeral treatment for any [data-countup] beside it. Count-up
   reserves width; server HTML shows the final value.
   =========================================================================== */
.md-scene--offer { position: relative; }
[data-countup] {
  font-variant-numeric: tabular-nums slashed-zero;
  font-feature-settings: "tnum", "zero";
}

/* ===========================================================================
   10. FAQ BENTO-LITE  -  tessellating grid, no gutter voids, one cell oversized.
   =========================================================================== */
@media (min-width: 880px) {
  .md-faq-cards {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: 1px;                          /* hairline tessellation, no void gutter */
    background: rgba(192, 195, 199, 0.16); /* platinum seam shows through gap */
    border: 1px solid rgba(192, 195, 199, 0.16);
    border-radius: 12px;
    overflow: hidden;
  }
  .md-faq-cards__card {
    margin: 0;
    border-radius: 0;
    background: var(--md-bg, #0F1115);
  }
  /* first card oversized for bento rhythm */
  .md-faq-cards__card:first-child { grid-column: 1 / -1; }
}
.md-faq-cards__card {
  transition: transform 0.5s var(--md-px-ease), border-color 0.5s var(--md-px-ease);
}
@media (prefers-reduced-motion: no-preference) {
  .md-faq-cards__card:hover { transform: translateY(-3px); }
}

/* ===========================================================================
   11. FINAL CTA SCRIM  -  gold card seated in a full-bleed graphite scrim scene.
   Background graphite texture drifts slowly behind the rising gold card.
   =========================================================================== */
.md-final-cta--gold,
.md-scene--final {
  position: relative;
  overflow: hidden;
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .md-final-cta--gold::before,
    .md-scene--final::before {
      content: "";
      position: absolute;
      inset: -10% 0;
      background-image: radial-gradient(circle at center, rgba(212, 175, 55, 0.06) 1px, transparent 1.6px);
      background-size: 26px 26px;
      pointer-events: none;
      will-change: transform;
      animation: md-px-scrim-drift linear both;
      animation-timeline: view();
      animation-range: entry 0% exit 100%;
    }
    @keyframes md-px-scrim-drift {
      from { transform: translateY(-5%); }
      to   { transform: translateY(5%); }
    }
  }
}

/* ===========================================================================
   12. 🔴 FOUNDER BAND  -  giant-name interlock + edge-bleed portrait (WORST #1).
   The founder hero name is set huge in Playfair and physically overlaps a
   full-height, edge-bleeding portrait with a side scrim so the name stays AA.
   The portrait drifts slowly on a sine-like translateY (CSS view-timeline) as
   the name holds. Credentials ledger below fills the former void.
   This targets the founder pages' existing .md-scene--hero (single-portrait).
   =========================================================================== */
body.page-founder .md-scene--hero .md-scene-hero__figure,
.md-founder-hero .md-scene-hero__figure {
  border-radius: 12px;
  aspect-ratio: 3 / 4;            /* portrait, full-height feel */
  max-height: 78vh;
}
body.page-founder .md-scene--hero .md-scene-hero__figure::before,
.md-founder-hero .md-scene-hero__figure::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--md-px-scrim-side);
  pointer-events: none;
  z-index: 1;
}
/* giant Playfair name overlap  -  uses the existing H1, scaled up, hanging into
   the portrait gutter. Stays plain DOM text (LCP-safe, AEO-safe). */
body.page-founder .md-scene--hero .md-scene-hero__h1,
.md-founder-hero .md-scene-hero__h1 {
  font-size: clamp(2.6rem, 5.4vw + 1rem, 5.2rem);
  line-height: 1.02;
  letter-spacing: -0.01em;
}
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    body.page-founder .md-scene--hero .md-scene-hero__figure img,
    .md-founder-hero .md-scene-hero__figure img {
      will-change: transform;
      animation: md-px-founder-sine linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }
    @keyframes md-px-founder-sine {
      0%   { transform: scale(1.08) translateY(-3%); }
      50%  { transform: scale(1.08) translateY(2%); }
      100% { transform: scale(1.08) translateY(-2%); }
    }
  }
}

/* ===========================================================================
   13. GLOBAL FAILSAFES  -  reduced-motion + no-JS resolve to the static state.
   These OVERRIDE every animation above. Nothing is left hidden. This is the
   AEO/no-content-stranding guarantee for this layer.
   =========================================================================== */
@media (prefers-reduced-motion: reduce) {
  .md-scene-hero__figure img,
  .md-verticals__media img,
  .md-svc-signature__figure img,
  .md-asym-feature__img,
  .md-pull-quote-section__image img,
  .md-live-ledger__item,
  .md-live-ledger::after,
  .md-pillars__card,
  .md-qualify__col,
  .md-faq-cards__card,
  .md-final-cta--gold::before,
  .md-scene--final::before,
  body.page-founder .md-scene--hero .md-scene-hero__figure img,
  .md-founder-hero .md-scene-hero__figure img {
    animation: none !important;
    transform: none !important;
    opacity: 1 !important;
  }
  .md-scene-hero__figure img,
  .md-verticals__media img,
  .md-svc-signature__figure img,
  .md-asym-feature__img,
  .md-pull-quote-section__image img,
  body.page-founder .md-scene--hero .md-scene-hero__figure img,
  .md-founder-hero .md-scene-hero__figure img { transform: scale(1) !important; }
  /* un-pin everything so reduced-motion users get a plain stacked page */
  .md-live-ledger,
  .md-scene--verticals.md-track-on > .md-container { position: static !important; }
  .md-live-ledger::after { transform: scaleX(1) !important; }
  .md-scene--verticals.md-track-on { min-height: 0 !important; }
  .md-scene--verticals.md-track-on .md-verticals__grid {
    display: grid !important;
    transform: none !important;
    animation: none !important;
    width: auto !important;
  }
}

/* NO-JS: the `.js` class is absent => the IntersectionObserver fallback never
   needs to run, and CSS animation-timeline (where supported) drives motion
   anyway. Where animation-timeline is NOT supported and JS is off, everything
   is already at its static final state (no opacity:0 set here without a
   support guard). Ledger sticky degrades to a normal block. Track degrades to
   the 3-up grid. Belt-and-suspenders: force the ledger figures visible. */
html:not(.js) .md-live-ledger__item,
html:not(.js) .md-pillars__card { opacity: 1; transform: none; }

/* Browsers WITHOUT animation-timeline but WITH JS: the JS adds .md-px-js to
   <html> and writes per-element --md-px-y (vertical drift) / --md-px-x (track).
   These rules consume them (transform only). The scale keeps Ken-Burns
   headroom so translateY never exposes a hard edge. When the class is absent
   the elements are simply static  -  never hidden. */
.md-px-js .md-scene-hero__figure img,
.md-px-js .md-verticals__media img,
.md-px-js .md-svc-signature__figure img,
.md-px-js .md-asym-feature__img,
.md-px-js .md-pull-quote-section__image img {
  will-change: transform;
  transform: scale(1.08) translateY(var(--md-px-y, 0));
}
.md-px-js .md-scene--verticals[data-track] .md-verticals__grid {
  will-change: transform; transform: translateX(var(--md-px-x, 0));
}

/* ===========================================================================
   14. SECTION-INTRO BALANCE  -  kill the right-side void on EVERY page.
   ---------------------------------------------------------------------------
   PROBLEM (visual QA #1, repeating on front / seo / legal / home-services):
   .md-section__header was a single left-hugging column capped at max-width:900px
   inside a 1320px container, so a ~420px dead band hung off the right edge on
   every intro/section header. The page read half-empty at the top of each scene.

   FIX (shared CSS, no markup change, fixes all pages at once):
   On wide screens turn the header into an EDITORIAL TWO-COLUMN BAND that fills
   the full container  -  the eyebrow + headline ride the left column, the dek
   (the .md-body / lede paragraph) drops into a right column aligned to the
   headline baseline. Magazine "headline left / standfirst right" composition.
   Headers that carry NO dek paragraph have nothing to seat on the right, so they
   resolve to a CONSTRAINED CENTERED MEASURE instead of sitting left in a void.

   This is layout only (grid + measure)  -  no transform/opacity motion, nothing
   hidden, fully present with JS off and under reduced-motion. AEO/LCP untouched:
   the H1/H2 stays the first painted text node; we only reflow where the dek sits.
   The per-scene inline `style="max-width:..."` final-CTA headers are intentionally
   centered narrow already, so we scope the band to headers WITHOUT inline width.
   =========================================================================== */
@media (min-width: 1024px) {
  /* TWO-COLUMN editorial band: only when a dek paragraph exists to fill the
     right column. :has() keeps headline-only headers out of this branch. */
  .md-section__header:has(> p):not([style*="max-width"]) {
    max-width: none;                 /* was 900px  -  let the band span the container */
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
    column-gap: clamp(40px, 5vw, 96px);
    align-items: end;                /* dek baseline sits with the headline foot */
  }
  /* eyebrow + headline span the left column (they are the 1st/2nd children) */
  .md-section__header:has(> p):not([style*="max-width"]) > .md-eyebrow,
  .md-section__header:has(> p):not([style*="max-width"]) > h2,
  .md-section__header:has(> p):not([style*="max-width"]) > h3 {
    grid-column: 1;
  }
  /* the dek paragraph(s) ride the right column, optical-aligned to the headline */
  .md-section__header:has(> p):not([style*="max-width"]) > p {
    grid-column: 2;
    grid-row: 2;                     /* sit beside the headline row, not the eyebrow */
    align-self: end;
    max-width: 46ch;
    margin-top: 0;
    padding-bottom: 0.35em;          /* nudge the dek baseline onto the headline foot */
  }
  /* the eyebrow holds the first row across the left column so headline+dek pair
     read as one band. Reset the default top-margin stacking on the eyebrow row. */
  .md-section__header:has(> p):not([style*="max-width"]) > .md-eyebrow {
    grid-row: 1;
    margin-top: 0;
  }
  .md-section__header:has(> p):not([style*="max-width"]) > h2,
  .md-section__header:has(> p):not([style*="max-width"]) > h3 {
    grid-row: 2;
    margin-top: 0;
  }

  /* CONSTRAINED CENTERED MEASURE for headline-only headers (no dek). These
     formerly hugged the left in a void; centering the measure removes the
     lopsided empty right margin without inventing markup. Scoped away from the
     inline-narrow final-CTA headers (already centered) and from the verticals
     intro that the horizontal track owns. */
  .md-section__header:not(:has(> p)):not([style*="max-width"]):not(.md-section__header--left) {
    max-width: 60ch;
    margin-inline: auto;
    text-align: center;
  }
  .md-section__header:not(:has(> p)):not([style*="max-width"]):not(.md-section__header--left) > * {
    margin-inline: auto;
  }
}

/* Browsers WITHOUT :has() support fall back to the original single-column
   header (still valid, just the prior look)  -  no breakage, only no upgrade. */
@supports not (selector(:has(*))) {
  .md-section__header { max-width: 900px; }
}

/* ===========================================================================
   15. NARRATIVE BAND 2-COLUMN  -  kill the flat text-only band voids.
   ---------------------------------------------------------------------------
   PROBLEM (visual QA #2): the "He runs the businesses he markets" band on
   page-brian-hong and the "Brian, Michael, and Dimitry" band on page-about
   rendered as a lone centered paragraph column in a wide void.

   FIX: wrap each in .md-narrative-band (a 2-col grid). The prose keeps its
   centered measure on the LEFT; a credential / named-entity proof aside seats
   on the RIGHT, filling the former void. Stacks to one column on mobile so
   nothing is hidden or reordered for crawlers. The aside is built ONLY from
   facts already present in the page copy (no fabricated numbers).
   =========================================================================== */
.md-narrative-band { display: block; }
.md-narrative-band__copy { max-width: 820px; }
.md-narrative-band__aside { margin-top: clamp(28px, 4vw, 44px); }

/* CONTRAST FAILSAFE (regression guard): wrapping the prose in .md-narrative-band
   pushed the paragraphs one level deeper than `.md-section--deep .md-container > p`
   reaches, so on the graphite brian-hong band they fell back to graphite-on-
   graphite (ratio 1.05, invisible). Re-assert bone copy for the nested band copy
   on deep scenes. The lead/label keep their accent (gold border) treatments. */
.md-section--deep .md-narrative-band__copy > p,
.md-section--deep .md-narrative-band__copy > .md-scene-blind__lead,
.md-section--deep .md-narrative-band__copy > .md-body {
  color: #F7F7F5;
}
.md-section--deep .md-narrative-band__copy > .md-scene-blind__label {
  color: rgba(247, 247, 245, 0.82);   /* bone-soft, AA on graphite */
}

@media (min-width: 1024px) {
  .md-narrative-band {
    display: grid;
    grid-template-columns: minmax(0, 1.5fr) minmax(280px, 0.85fr);
    column-gap: clamp(40px, 5vw, 88px);
    align-items: start;
  }
  .md-narrative-band__copy { max-width: none; }
  .md-narrative-band__aside {
    margin-top: 0;
    position: sticky;
    top: clamp(96px, 12vh, 132px);   /* trails the prose, fills the right rail */
  }
}

/* The aside shell. On deep (graphite) scenes the .md-live-ledger styling already
   gives it a card; we add padding + a gold-edged frame so it reads as a panel. */
.md-narrative-band__aside.md-live-ledger {
  padding: clamp(22px, 2.4vw, 30px);
  border: 1px solid rgba(212, 175, 55, 0.30);
  border-radius: 12px;
  background: rgba(247, 247, 245, 0.04);
}

/* Inside our narrow aside/contact cards, force the ledger row to a SINGLE column
   (the sitewide .md-live-ledger__row goes multi-column auto-fit at >=720px, which
   split our 3-item list into a cramped 2-col grid). Left-align the figures and
   give the chevron a hanging indent so wrapped lines stay readable. */
.md-narrative-band__aside .md-live-ledger__row,
.md-final__contact-card .md-live-ledger__row {
  grid-template-columns: 1fr;
}
.md-narrative-band__aside .md-live-ledger__item,
.md-final__contact-card .md-live-ledger__item {
  padding-inline: 0;
}
.md-narrative-band__aside .md-live-ledger__figure,
.md-final__contact-card .md-live-ledger__figure {
  display: grid;
  grid-template-columns: auto 1fr;     /* chevron column + text column */
  gap: 8px;
  align-items: start;
  text-align: left;
  line-height: 1.5;
}
.md-narrative-band__aside .md-live-ledger__figure::before,
.md-final__contact-card .md-live-ledger__figure::before {
  margin-right: 0;
}
/* Soft-canvas variant (about page sits on bone): light card, gold hairline. */
.md-narrative-band__aside--soft {
  padding: clamp(22px, 2.4vw, 30px);
  border: 1px solid rgba(212, 175, 55, 0.38);
  border-radius: 12px;
  background: #FFFFFF;
  box-shadow: 0 1px 0 rgba(15, 17, 21, 0.04);
}
.md-narrative-band__aside-eyebrow {
  font-family: var(--md-font-mono, "JetBrains Mono", monospace);
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: #D4AF37;                    /* gold reads AA on the graphite (deep) aside */
  margin: 0 0 clamp(14px, 1.6vw, 18px);
}
/* On the SOFT white aside, gold-on-white fails WCAG (ratio ~2.1). Use graphite
   for the eyebrow text there and keep gold only as a leading accent rule. */
.md-narrative-band__aside--soft .md-narrative-band__aside-eyebrow {
  color: #0F1115;
  display: flex;
  align-items: center;
  gap: 10px;
}
.md-narrative-band__aside--soft .md-narrative-band__aside-eyebrow::before {
  content: "";
  width: 22px;
  height: 2px;
  background: #D4AF37;                /* gold accent tick, decorative */
  flex: 0 0 auto;
}
.md-narrative-band__aside-foot {
  margin: clamp(14px, 1.6vw, 18px) 0 0;
  font-size: 14px;
  line-height: 1.5;
  color: var(--md-fg-muted, #5A5F66);
}
.md-narrative-band__aside--soft .md-narrative-band__aside-foot { color: #4A4F56; }

/* Named-entity / credential row used on the about aside (soft canvas). */
.md-entity-row { list-style: none; margin: 0; padding: 0; display: grid; gap: clamp(14px, 1.6vw, 18px); }
.md-entity-row > li {
  display: grid;
  gap: 3px;
  padding-left: 16px;
  border-left: 2px solid var(--md-accent, #D4AF37);
}
.md-entity-row__name {
  font-family: var(--md-font-display, "Playfair Display", serif);
  font-size: clamp(1.05rem, 0.6vw + 0.9rem, 1.25rem);
  line-height: 1.2;
  color: #0F1115;
}
.md-entity-row__role {
  font-family: var(--md-font-mono, "JetBrains Mono", monospace);
  font-size: 12.5px;
  letter-spacing: 0.02em;
  color: #5A5F66;
}

/* ===========================================================================
   16. FRONT-PAGE FINAL CONTACT BAND  -  the cta-row was ~50% empty (visual QA #3).
   Pair the CTA stack with a "what lands on the call" credential card on the
   right so the band fills the full container instead of hugging the left.
   =========================================================================== */
.md-final__contact { display: block; }
.md-final__contact-card.md-live-ledger {
  margin-top: clamp(28px, 4vw, 40px);
  padding: clamp(22px, 2.4vw, 30px);
  border: 1px solid rgba(212, 175, 55, 0.30);
  border-radius: 12px;
  background: rgba(247, 247, 245, 0.04);
}
@media (min-width: 1024px) {
  .md-final__contact {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(300px, 0.9fr);
    column-gap: clamp(40px, 5vw, 88px);
    align-items: center;
  }
  .md-final__contact-card.md-live-ledger { margin-top: 0; }
}

/* ===========================================================================
   17. TASK 5  -  verticals CTA band was a tall near-empty strip with one lone
   "Book a strategy call" link. Tighten the lead-in margin and pair the CTA with
   a one-line supporting aside so the band reads intentional, not empty. The
   aside is platinum-on-graphite (AA). Wraps under the button on narrow screens.
   =========================================================================== */
.md-scene__cta--verticals {
  margin-top: clamp(28px, 3.2vw, 40px);   /* was --md-space-8 (48px) flat */
  align-items: baseline;
  row-gap: 10px;
}
.md-scene--deep .md-scene__cta--verticals .md-scene__cta-aside,
.md-section--deep .md-scene__cta--verticals .md-scene__cta-aside {
  color: rgba(247, 247, 245, 0.82);
  max-width: 46ch;
}

/* ===========================================================================
   18. GSAP SIGNATURE BEATS  -  support styles for motion-init.js (Task 3).
   These ONLY take effect once GSAP has run (it sets inline styles / wraps
   words). Each beat has a static/no-JS/reduced-motion fallback already:
     - headline words: server H1 text is final; .md-word is display:inline-block
       so the line wraps identically whether or not GSAP animates it.
     - verticals track: CSS @supports scroll() OR the GSAP pin OR the plain
       3-up grid (no-JS / reduced-motion) all resolve to readable cards.
     - offer pin: .md-offer__item carry .reveal, so without GSAP they still
       reveal via the IntersectionObserver + failsafe keyframe.
   =========================================================================== */
.md-scene-hero__h1 .md-word { will-change: transform, opacity; }

/* When GSAP pins the offer sticky aside, give the detail column breathing room
   so the pinned card and the scrubbing checklist never collide. Desktop only;
   the CSS sticky already handles the non-GSAP path. */
@media (min-width: 980px) and (prefers-reduced-motion: no-preference) {
  .md-scene--offer[data-beat] .md-offer__detail { position: relative; z-index: 1; }
}

/* Hard reduced-motion / no-JS guarantee for the new beats: never strand the
   headline words or offer items hidden. (motion-init.js never runs under
   reduced-motion, but belt-and-suspenders for the CSS layer.) */
@media (prefers-reduced-motion: reduce) {
  .md-scene-hero__h1 .md-word { transform: none !important; opacity: 1 !important; }
}
html:not(.js) .md-scene-hero__h1 .md-word { transform: none; opacity: 1; }
