/* One-page professional portfolio, Quindalyn Denean Peace, Project Manager and Business Analyst (tabblabs.net/qdeneanpeace).
   Its own visual SYSTEM, deliberately not the /andrew page (which is sans-800, centered,
   22px glass cards, pill CTAs, violet/pink/cyan). Here: a LEFT-SET EDITORIAL RECORD.
   Serif display type, hairline rules, square 4px geometry, section numerals, figures in
   tabular numerals, one continuous rail.

   THE FIELD is a LIGHT, rich, multi-hue gradient: 16 soft OVERLAPPING radial blooms over a
   cool tinted base. The palette is teal / seafoam / periwinkle / sky / rose only. There is
   deliberately NO green and NO amber anywhere in it: v2 carried sage and gold blooms which
   overlapped into muddy tan and olive across the lower half of the page. Every hue here is
   in the cool half of the wheel or is rose, so no two blooms can mix down to mud.

   MOTION: every scroll effect is driven from ONE shared rAF loop in app.js. There is no CSS
   scroll-driven-animation path at all - measured on Firefox 151.0.3, which is the browser
   this page is read in, animation-timeline: view()/scroll() and animation-range are ALL
   unsupported, so a CSS path would render silently static for the actual reader. One JS path
   means one behaviour in every browser and nothing that can quietly do nothing.

   FLOOR: html.anim is only set when motion is allowed. Without it (JS off, or
   prefers-reduced-motion) every pinned track collapses to natural height, every scene stacks
   in normal flow, every bar sits at its true width and every figure at its final value. */

:root {
  --ink: #14201d;
  --ink-soft: #3d4f4a;
  --ink-faint: #55635f;
  --rule: rgba(20, 32, 29, 0.14);
  --rule-soft: rgba(20, 32, 29, 0.08);
  --panel: rgba(255, 255, 255, 0.80);
  --panel-solid: rgba(255, 255, 255, 0.93);
  --teal: #0f766e;
  --teal-deep: #0b544e;
  --indigo: #33477e;
  --plum: #7c3f63;
  --serif: Georgia, 'Iowan Old Style', 'Palatino Linotype', 'Book Antiqua', 'Times New Roman', serif;
  --sans: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --maxw: 1120px;
  --r: 4px;
}

* { box-sizing: border-box; margin: 0; padding: 0; }
html { scroll-behavior: smooth; }

body {
  font-family: var(--sans);
  color: var(--ink);
  line-height: 1.62;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  background: #eaf1f2;
  position: relative;
  overflow-x: hidden;
}

/* ---- the light rich multi-hue FIELD: 16 overlapping blooms over a cool tinted base ----
   Horizontal inset stays 0 so the field never widens the page; vertical bleed is safe.

   WHAT WENT WRONG TWICE, and the rule that came out of it.
   v2 blooms carried sage and gold, which overlapped into muddy tan and olive down the
   lower half. v3 removed the mud by dropping every bloom's alpha and enlarging every
   radius - and overcorrected into a near-flat pale blue wash: at 1500-1720px of vertical
   radius on a 12,000px document with the centres only 700-900px apart, FIVE OR SIX blooms
   covered every point of the page at once and averaged each other out. Sixteen blooms were
   in the CSS and two or three were visible on the page.

   So the geometry, not the palette, is what makes a field rich:
     - vertical radius (~1050px) must be near the centre-to-centre SPACING (~800px), not
       twice it. Two neighbours meet; six do not pile up.
     - the colour must hold most of the way out (transparent at 66%, not 74%), so a bloom
       has a CORE and not just a haze.
     - alpha 0.44-0.60, not 0.34-0.46. Light comes from the pale base underneath, not from
       starving every bloom.
     - x alternates hard (left / right / centre) so vertical neighbours are also apart
       horizontally, and no two adjacent blooms share a hue family.
   Palette is teal / seafoam / periwinkle / sky / rose ONLY. No green, no amber, anywhere
   in the field: those two are what mixed down to mud in v2, and nothing that is not in
   this list may be added.
   Verified against a full-document capture of the field with all content hidden, at the
   real document height, not against the fold. */
body::before {
  content: "";
  position: absolute;
  /* bottom inset is 0: a negative bottom would extend the scroll area past the footer and
     leave a trailing band of bare field below it (seen in the v4 footer proof). The footer
     carries its own tint, so the field only needs to reach it. */
  inset: -3% 0 0 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(1660px 1280px at 12% 2%,  rgba(56, 178, 170, 0.54), transparent 71%),
    radial-gradient(1590px 1220px at 86% 7%,  rgba(126, 146, 240, 0.54), transparent 71%),
    radial-gradient(1560px 1200px at 46% 13%, rgba(92, 184, 228, 0.50), transparent 71%),
    radial-gradient(1620px 1240px at 95% 19%, rgba(240, 150, 182, 0.48), transparent 71%),
    radial-gradient(1590px 1220px at 8% 25%, rgba(112, 212, 200, 0.56), transparent 71%),
    radial-gradient(1650px 1270px at 62% 32%, rgba(138, 156, 242, 0.52), transparent 71%),
    radial-gradient(1590px 1220px at 20% 38%, rgba(56, 178, 170, 0.52), transparent 71%),
    radial-gradient(1620px 1240px at 92% 45%, rgba(176, 158, 238, 0.50), transparent 71%),
    radial-gradient(1650px 1270px at 50% 52%, rgba(240, 152, 184, 0.46), transparent 71%),
    radial-gradient(1590px 1220px at 10% 58%, rgba(92, 184, 228, 0.56), transparent 71%),
    radial-gradient(1650px 1270px at 82% 65%, rgba(56, 178, 170, 0.52), transparent 71%),
    radial-gradient(1620px 1240px at 34% 72%, rgba(132, 150, 240, 0.52), transparent 71%),
    radial-gradient(1590px 1220px at 96% 79%, rgba(240, 150, 182, 0.48), transparent 71%),
    radial-gradient(1650px 1270px at 18% 86%, rgba(112, 212, 200, 0.56), transparent 71%),
    radial-gradient(1620px 1240px at 70% 92%, rgba(92, 184, 228, 0.54), transparent 71%),
    radial-gradient(1590px 1220px at 30% 98%, rgba(136, 154, 242, 0.52), transparent 71%),
    linear-gradient(170deg,
      #cfe9e3 0%, #d3e3f4 14%, #e0dcf2 28%, #cfe9ef 42%,
      #ecdde8 56%, #cee7ec 70%, #dcdcf3 84%, #cee9e5 100%);
  background-repeat: no-repeat;
}

.wrap { width: min(var(--maxw), 92vw); margin: 0 auto; }
a { color: inherit; }
img { max-width: 100%; display: block; }

.skip {
  position: absolute; left: -999px; top: 0; z-index: 80;
  background: var(--ink); color: #fff; padding: 12px 18px; font-weight: 700;
}
.skip:focus { left: 0; }
:focus-visible { outline: 3px solid var(--teal-deep); outline-offset: 3px; }

/* =====================================================================
   THE SPINE — the page's connective motion.
   A hairline in the left gutter, fixed, that fills with document progress and carries the
   six numerals. The numeral you are standing in grows, turns teal, and names itself; the
   others sit back. It exists ONLY under .anim and only where there is a real gutter to
   put it in (the wrap is 1120px, so 1340px of viewport is the floor for it), which means
   it can never collide with the content column.
   ===================================================================== */
.spine { display: none; }
@media (min-width: 1400px) {
  .anim .spine {
    display: block; position: fixed; z-index: 30;
    left: calc((100vw - var(--maxw)) / 2 - 110px);
    top: 50%; transform: translateY(-50%);
    width: 92px; pointer-events: none;
    opacity: 1; transition: opacity .3s ease;
  }
}
/* The experience rail and the capability rows are full-bleed and travel LEFT through the
   gutter the spine lives in, so during those two sections the spine would sit on top of a
   travelling card. Hush it while either is active; it fades back the moment they leave. */
.anim .spine.hushed { opacity: 0; }
.spine-track {
  position: absolute; left: 0; top: 0; bottom: 0; width: 2px;
  background: rgba(20, 60, 55, 0.13); overflow: hidden; border-radius: 1px;
}
.spine-fill {
  display: block; width: 100%; height: 100%;
  transform-origin: center top; transform: scaleY(var(--p, 0));
  background: linear-gradient(180deg, var(--teal-deep), var(--teal) 60%, #3aa79c);
}
.spine-nodes { list-style: none; padding-left: 16px; display: grid; gap: 20px; }
.spine-nodes li {
  display: flex; align-items: baseline; gap: 9px;
  transform: translate3d(calc(var(--n, 0) * 5px), 0, 0);
  transition: transform .34s ease;
}
/* the active numeral grows by TRANSFORM, never by font-size: an animated font-size
   relayouts the rail on every step of the transition. */
.sn-num {
  display: inline-block;
  font-family: var(--serif); font-weight: 700;
  font-size: 0.86rem; line-height: 1;
  letter-spacing: 0.04em; font-variant-numeric: tabular-nums;
  color: var(--ink-faint);
  transform-origin: left center;
  transform: scale(calc(1 + var(--n, 0) * 0.42));
  transition: transform .34s ease, color .34s ease;
}
.spine-nodes li.on .sn-num { color: var(--teal-deep); }
.sn-lbl {
  display: inline-block;
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--teal-deep); white-space: nowrap;
  opacity: var(--n, 0); transform: translate3d(calc((var(--n, 0) - 1) * 8px), 0, 0);
  transition: opacity .34s ease, transform .34s ease;
}

/* ---- header: hairline bar, no pills ---- */
header {
  position: sticky; top: 0; z-index: 40;
  background: rgba(238, 245, 246, 0.84);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--rule);
}
.bar {
  display: flex; align-items: center; justify-content: space-between;
  gap: 20px; min-height: 64px; padding: 10px 0;
}
.brand { display: flex; align-items: center; gap: 12px; text-decoration: none; }
.brand .mark {
  width: 34px; height: 34px; flex: none; display: grid; place-items: center;
  background: var(--teal-deep); color: #fff;
  font-family: var(--serif); font-size: 0.82rem; font-weight: 700; letter-spacing: 0.04em;
  border-radius: var(--r);
}
.brand-name {
  font-family: var(--serif); font-size: 1.02rem; font-weight: 700;
  letter-spacing: -0.01em; color: var(--ink); white-space: nowrap;
}
nav ul { display: flex; gap: 26px; list-style: none; }
nav a {
  display: inline-flex; align-items: center; min-height: 44px;
  font-size: 0.86rem; font-weight: 600; letter-spacing: 0.02em;
  color: var(--ink-soft); text-decoration: none;
  border-bottom: 2px solid transparent;
}
nav a:hover, nav a:focus-visible { color: var(--teal-deep); border-bottom-color: var(--teal); }

/* ---- masthead strip: Dee's introductory phrase as a deliberate wordmark line ----
   A slim tier above the brand/nav bar, hairline-separated, so a self-referential URL reads
   as intentional masthead branding and never crowds the nav. Text is shown verbatim (no
   text-transform, so "Professional Insight:" and the camel-case path are preserved). */
.intro-strip { border-bottom: 1px solid var(--rule-soft); background: rgba(15, 118, 110, 0.05); }
.intro-phrase {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 9px;
  min-height: 34px; padding: 6px 0; font-size: 0.82rem; line-height: 1.35;
}
.intro-lbl {
  font-family: var(--serif); font-weight: 700; letter-spacing: 0.01em;
  color: var(--ink-faint); white-space: nowrap;
}
.intro-url {
  font-family: var(--serif); font-weight: 700; letter-spacing: 0.01em;
  color: var(--teal-deep); text-decoration: none; white-space: nowrap;
  padding: 3px 0; border-bottom: 1px solid transparent;
}
.intro-url:hover, .intro-url:focus-visible { border-bottom-color: var(--teal); }
@media (max-width: 400px) { .intro-phrase { font-size: 0.76rem; } }

/* The disclosure hides its content when closed; the toggle is only used at <=720px. */
.navtoggle { display: none; }

/* DESKTOP / TABLET (>720px): the <details> is inert and the nav shows inline exactly as
   before. A closed <details> hides its content two different ways across engines: older ones
   set display:none on the children (overridden by display:block on nav), newer ones set
   content-visibility:hidden on the ::details-content wrapper (overridden by the pseudo rule).
   Forcing both keeps the nav visible without the disclosure ever being open. */
@media (min-width: 721px) {
  .navdisc > nav { display: block; }
  .navdisc::details-content { content-visibility: visible; }
}

@media (max-width: 1140px) and (min-width: 721px) { .brand-name { display: none; } }

/* MOBILE (<=720px): a real disclosure menu. Bar stays a single tidy row (brand left, Menu
   button right); the 7 links collapse into a dropdown that opens on the Menu button. No JS:
   native <details> drives it, keyboard-operable and complete with the arm script absent. */
@media (max-width: 720px) {
  .brand-name { display: inline; }

  .navdisc { display: block; position: static; }
  .navtoggle {
    display: inline-flex; align-items: center; gap: 10px;
    min-height: 44px; padding: 0 14px; cursor: pointer;
    list-style: none; /* removes the Firefox disclosure triangle */
    font-size: 0.72rem; font-weight: 700; letter-spacing: 0.14em; text-transform: uppercase;
    color: var(--teal-deep);
    background: rgba(255, 255, 255, 0.60);
    border: 1px solid var(--rule); border-radius: var(--r);
    -webkit-tap-highlight-color: transparent;
  }
  .navtoggle::-webkit-details-marker { display: none; } /* removes the Chrome triangle */
  .navtoggle::marker { content: ""; }
  .navtoggle:hover, .navdisc[open] > .navtoggle { border-color: var(--teal); }
  .navtoggle:focus-visible { outline: 3px solid var(--teal-deep); outline-offset: 3px; }

  /* the hamburger glyph: three hairlines that fold to an X when open */
  .navtoggle-ico {
    position: relative; width: 18px; height: 12px; flex: none;
  }
  .navtoggle-ico::before, .navtoggle-ico::after {
    content: ""; position: absolute; left: 0; right: 0; height: 2px;
    background: var(--teal-deep); border-radius: 2px;
    transition: transform .25s ease, top .25s ease, bottom .25s ease;
  }
  .navtoggle-ico::before { top: 1px; box-shadow: 0 5px 0 var(--teal-deep); }
  .navtoggle-ico::after { bottom: 1px; }
  .navdisc[open] .navtoggle-ico::before { top: 5px; transform: rotate(45deg); box-shadow: none; }
  .navdisc[open] .navtoggle-ico::after { bottom: 5px; transform: rotate(-45deg); }

  /* the dropdown panel: closed by default, shown only when the details is [open]. Controlling
     display purely from [open] means the behavior is identical in every browser and never
     depends on the UA's default show/hide of details content. */
  .navdisc > nav {
    display: none;
    position: absolute; left: 0; right: 0; top: 100%;
    background: #eef5f6; /* opaque: the dropdown is a reading surface, so nothing bleeds through it */
    border-bottom: 1px solid var(--rule);
    box-shadow: 0 18px 40px rgba(20, 60, 55, 0.12);
  }
  .navdisc[open] > nav { display: block; }
  .navdisc > nav ul {
    flex-direction: column; gap: 0;
    width: min(var(--maxw), 92vw); margin: 0 auto;
    padding: 6px 0 12px;
  }
  .navdisc > nav li { border-bottom: 1px solid var(--rule-soft); }
  .navdisc > nav li:last-child { border-bottom: 0; }
  nav a {
    display: flex; width: 100%; min-height: 50px;
    border-bottom: 0; padding-left: 2px;
    font-size: 0.94rem; letter-spacing: 0.04em;
  }
  nav a:hover, nav a:focus-visible { border-bottom-color: transparent; color: var(--teal-deep); }
}

/* VERY NARROW (<=380px): brand name + a labelled Menu button overflow the bar, so the toggle
   collapses to an icon-only 44px target. The "Menu" text stays in the DOM but visually-hidden,
   so the button keeps its accessible name. Gap tightens to buy back a few pixels. */
@media (max-width: 380px) {
  .bar { gap: 12px; }
  .navtoggle { padding: 0; min-width: 44px; justify-content: center; gap: 0; }
  .navtoggle-lbl {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
  }
}

/* ---- hero: asymmetric, left-set, rule-anchored ---- */
.hero { padding: 84px 0 56px; }
.hero-grid {
  display: grid; grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: 56px; align-items: start;
}
.eyebrow {
  font-size: 0.74rem; font-weight: 700; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--teal-deep); margin-bottom: 20px;
}
.hero h1 {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(2.15rem, 5.3vw, 3.5rem); line-height: 1.06;
  letter-spacing: -0.02em; color: var(--ink);
}
.hero h1 i { font-style: normal; }
.hero .role {
  margin-top: 12px;
  font-family: var(--serif); font-style: italic; font-weight: 400;
  font-size: clamp(1.15rem, 2.5vw, 1.65rem); line-height: 1.2;
  letter-spacing: -0.01em; color: var(--teal-deep);
}
.hero .lede {
  margin-top: 22px; padding-top: 18px; border-top: 1px solid var(--rule);
  max-width: 60ch; font-size: 1.045rem; color: var(--ink-soft);
}
.hero .avail {
  margin-top: 16px; max-width: 60ch; padding-left: 16px;
  border-left: 3px solid var(--teal); font-size: 0.98rem; color: var(--ink-soft);
}
.hero-side {
  align-self: end;
  background: var(--panel); border: 1px solid var(--rule); border-top: 3px solid var(--teal-deep);
  border-radius: var(--r); padding: 26px 26px 22px;
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  box-shadow: 0 18px 40px rgba(20, 60, 55, 0.10);
}
/* ---- HERO HANDOFF: one frame, two scenes, driven by leaving the hero ----
   At the floor the frame is ordinary flow and both scenes simply stack. Under .anim the
   frame is given a fixed height measured from scene 1 and both scenes are absolutely
   placed inside it, so the swap is transform + opacity only and cannot shift the page. */
.hs-frame { position: relative; }
.hs-scene + .hs-scene {
  margin-top: 22px; padding-top: 20px; border-top: 1px solid var(--rule-soft);
}
/* Scene 1 stays in NORMAL FLOW and is what gives the frame its height; only scene 2 is
   lifted out and overlaid. Nothing here is measured by script and no height is ever
   written, so the swap cannot move a pixel of the page (CLS 0). The min-height is the
   floor for the taller of the two scenes at the narrowest column it is used at. */
.anim .hs-frame { min-height: 296px; }
.anim .hs-scene[data-hs="1"] {
  position: absolute; inset: 0 0 auto 0;
  margin: 0; padding: 0; border-top: 0;
}
/* scene 1 leaves row by row, each row travelling further than the one above it (a
   one-property fan-out: JS writes --h, the rows derive their own distance from it). */
.anim .gl-row {
  transform: translate3d(calc(var(--h, 0) * var(--i, 1) * 30px), 0, 0);
  opacity: calc(1 - var(--h, 0) * 1.6);
}
.anim .hs-orgs { opacity: calc((var(--h, 1) - 0.28) * 2.8); }

.glance > div { padding: 13px 0; border-bottom: 1px solid var(--rule-soft); }
.glance > div:last-child { border-bottom: 0; padding-bottom: 0; }
.glance > div:first-child { padding-top: 0; }

.hs-kicker {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--teal-deep);
  padding-bottom: 12px; border-bottom: 1px solid var(--rule); margin-bottom: 4px;
}
.hs-list { list-style: none; }
.hs-list li {
  padding: 11px 0; border-bottom: 1px solid var(--rule-soft);
  font-family: var(--serif); font-size: 1.04rem; color: var(--ink);
}
.hs-list li:last-child { border-bottom: 0; }
/* each name arrives from the right at its own point in the run */
.anim .hs-list li {
  transform: translate3d(calc((1 - var(--o, 1)) * 34px), 0, 0);
  opacity: var(--o, 1);
}
.hs-rule { height: 2px; margin-top: 14px; background: rgba(20, 60, 55, 0.10); overflow: hidden; }
.hs-rule span {
  display: block; height: 100%; width: 100%;
  transform-origin: left center; transform: scaleX(1);
  background: linear-gradient(90deg, var(--teal-deep), var(--teal) 70%, #3aa79c);
}
.anim .hs-rule span { transform: scaleX(var(--h, 1)); }
.glance dt {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: 5px;
}
.glance dd { font-family: var(--serif); font-size: 1.06rem; color: var(--ink); line-height: 1.35; }

@media (max-width: 880px) {
  .hero { padding: 56px 0 40px; }
  .hero-grid { grid-template-columns: 1fr; gap: 34px; }
}

/* ---- section frame ---- */
.band { padding: 52px 0; scroll-margin-top: 88px; }
.band-flush { padding-bottom: 8px; }
@media (max-width: 720px) { .band { scroll-margin-top: 80px; } }
.sec-head {
  display: flex; align-items: baseline; gap: 18px;
  padding-bottom: 16px; margin-bottom: 34px; border-bottom: 1px solid var(--rule);
}
.sec-num {
  font-family: var(--serif); font-size: 0.94rem; font-weight: 700;
  color: var(--teal-deep); letter-spacing: 0.06em; flex: none;
}
.sec-head h2 {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(1.7rem, 3.6vw, 2.5rem); line-height: 1.12;
  letter-spacing: -0.015em; color: var(--ink);
}

/* =====================================================================
   THE PIN PRIMITIVE.
   A .track is a tall scroll runway; .pin sticks inside it for the whole
   run. Without html.anim the runway has NO extra height and nothing
   sticks, so the content simply sits in the page at natural size.
   ===================================================================== */
.track { position: relative; }
.pin { position: relative; }
/* per-track runway heights are declared with each section, so no generic height rule
   here can outrank them on specificity. */
.anim .pin {
  position: sticky; top: 0;
  height: 100vh; min-height: 560px;
  display: flex; align-items: center;
  overflow: hidden;
}

/* the framed stage every pinned scene sits in */
.stage {
  position: relative; width: 100%;
  background: var(--panel-solid);
  border: 1px solid var(--rule); border-top: 3px solid var(--teal-deep);
  border-radius: var(--r);
  padding: 40px 44px 34px;
  box-shadow: 0 22px 54px rgba(20, 60, 55, 0.11);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
}

/* SCENE SET: stacked in flow by default (the floor). Under .anim each scene
   is absolutely placed in one frame and only the active one is shown, so the
   frame's entire contents change as the pin is scrubbed. */
.scene-set { list-style: none; }
.scene + .scene { margin-top: 40px; padding-top: 34px; border-top: 1px solid var(--rule-soft); }

.anim .scene-set { position: relative; }
.anim .scene {
  position: absolute; inset: 0;
  margin: 0; padding: 0; border-top: 0;
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translate3d(0, 14px, 0);
  transition: opacity .34s ease, transform .34s ease, visibility 0s linear .34s;
}
.anim .scene.on {
  opacity: 1; visibility: visible; pointer-events: auto;
  transform: none;
  transition: opacity .34s ease, transform .34s ease, visibility 0s;
}

/* (v3 carried a visually-hidden .sr twin for every counter, so a screen reader would hear
   the final value rather than the digits ticking past. v4 has no counters at all - every
   figure is literal text that nothing rewrites - so the twin has nothing left to protect
   against and is gone. One string per figure, read by eye and by screen reader alike.) */

/* ---- 01 IMPACT ---- */
.anim .track-impact { height: 260vh; }
.anim .stage-impact .scene-set { min-height: 400px; }

.scene-kicker {
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--teal-deep); margin-bottom: 26px;
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 14px;
}
.scene-src {
  font-weight: 600; letter-spacing: 0.06em; text-transform: none;
  color: var(--ink-faint); font-size: 0.74rem;
}

.scope-set { list-style: none; display: grid; grid-template-columns: 1fr 1fr; gap: 40px; }
.scope { display: block; }
.scope-fig {
  display: block; font-family: var(--serif); font-weight: 700;
  font-size: clamp(3rem, 7.6vw, 5.4rem); line-height: 1;
  letter-spacing: -0.03em; color: var(--teal-deep);
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
}
.scope-what {
  display: block; margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--rule);
  font-size: 1.02rem; color: var(--ink); max-width: 34ch;
}
.scope-where {
  display: block; margin-top: 8px;
  font-size: 0.78rem; font-weight: 600; letter-spacing: 0.05em;
  text-transform: uppercase; color: var(--ink-faint);
}

.grow-set { list-style: none; display: grid; gap: 20px; }
.grow {
  display: grid; grid-template-columns: 150px minmax(120px, 1fr) minmax(0, 1.5fr);
  gap: 8px 26px; align-items: center;
}
.grow-fig {
  font-family: var(--serif); font-weight: 700;
  font-size: clamp(1.6rem, 2.9vw, 2.35rem); line-height: 1.05;
  letter-spacing: -0.02em; color: var(--teal-deep);
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
}
.grow-track {
  display: block; height: 10px; border-radius: 2px;
  background: rgba(20, 60, 55, 0.09); overflow: hidden;
}
.grow-bar {
  display: block; height: 100%; width: var(--w, 0%);
  transform-origin: left center;
  background: linear-gradient(90deg, var(--teal-deep), var(--teal) 62%, #3aa79c);
  border-radius: 2px;
}
/* Every scrub default is 1, never 0: if a value is never written (gate not yet fired, an
   anchor jump past the section, JS erroring after this file's guard) the row fails to its
   COMPLETE state, never to an empty one. */
.anim .grow-bar { transform: scaleX(var(--g, 1)); }
.anim .grow { opacity: calc(var(--g, 1) * 8); }

/* THE FIGURE RULE (applies to every figure on this page).
   A bar may be at any fraction of itself, because a bar at 40% of its own width makes no
   claim. A NUMBER may not: it is either the true one or it is a lie. So no figure here is
   ever interpolated, masked part-way, or wiped part-way (a left-to-right wipe of "$111M"
   shows "$11", which is a different, false figure). The only thing that changes is
   OPACITY: the figure is absent, or it is true. --f is the reveal, and it defaults to 1,
   so at the floor and on any unwritten path the true figure is simply there. */
.anim .grow-fig { opacity: var(--f, 1); }
.anim .scope-fig {
  opacity: var(--f, 1);
  transform: translate3d(0, calc((1 - var(--f, 1)) * 12px), 0);
}
.grow-what { font-size: 0.96rem; color: var(--ink-soft); }

/* the pin's own progress rail, along the bottom of the stage */
.stage-rail {
  position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: rgba(20, 60, 55, 0.09);
  border-radius: 0 0 var(--r) var(--r); overflow: hidden;
}
.stage-fill {
  display: block; height: 100%; width: 100%;
  transform: scaleX(1); transform-origin: left center;
  background: linear-gradient(90deg, var(--teal-deep), var(--teal) 70%, #3aa79c);
}
.anim .stage-fill { transform: scaleX(var(--p, 1)); }

@media (max-width: 880px) {
  .scope-set { grid-template-columns: 1fr; gap: 30px; }
  .grow { grid-template-columns: 1fr; gap: 6px; }
  .grow-track { max-width: 260px; }
}

/* =====================================================================
   02 EXPERIENCE — the horizontal rail
   ===================================================================== */
.anim .track-exp { height: 400vh; }
.hz { width: 100%; }
.hz-head { padding-bottom: 22px; }
.hz-read { display: none; }
.anim .hz-read { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 16px; min-height: 34px; }
.hz-when {
  font-family: var(--serif); font-size: clamp(1.15rem, 2.3vw, 1.6rem);
  font-weight: 700; letter-spacing: -0.01em; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.hz-org {
  font-size: 0.76rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--teal-deep);
}
.hz-axis {
  position: relative; height: 40px; margin-top: 14px;
  border-top: 1px solid var(--rule);
}
.hz-axis::before {
  content: ""; position: absolute; left: 0; right: 0; top: -2px; height: 3px;
  background: rgba(20, 60, 55, 0.10);
}
.hz-axis-fill {
  position: absolute; left: 0; right: 0; top: -2px; height: 3px;
  transform: scaleX(var(--p, 1)); transform-origin: left center;
  background: linear-gradient(90deg, var(--teal-deep), var(--teal) 70%, #3aa79c);
}
.hz-axis-mark {
  position: absolute; top: -6px; left: 0; width: 11px; height: 11px; margin-left: -5px;
  border-radius: 50%; background: var(--teal-deep);
  box-shadow: 0 0 0 4px rgba(15, 118, 110, 0.16);
  transform: translateX(calc(var(--p, 1) * var(--axisw, 0px)));
}
.hz-tick {
  position: absolute; top: 10px; left: var(--x); transform: translateX(-50%);
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.1em;
  color: var(--ink-faint); font-variant-numeric: tabular-nums; white-space: nowrap;
}
.hz-tick-end { transform: translateX(-90%); }

/* the rail itself: a real ordered list that travels on X */
.hz-rail { list-style: none; display: grid; gap: 22px; padding: 0 4vw; }
.anim .hz-rail {
  display: flex; flex-wrap: nowrap; align-items: stretch; gap: 26px;
  padding-left: max(4vw, calc((100vw - var(--maxw)) / 2));
  padding-right: 12vw;
  transform: translate3d(calc(var(--x, 0) * -1px), 0, 0);
}
.rolecard {
  flex: 0 0 clamp(310px, 30vw, 430px);
  display: flex; flex-direction: column;
  background: var(--panel-solid); border: 1px solid var(--rule);
  border-top: 3px solid var(--teal-deep); border-radius: var(--r);
  padding: 26px 28px 24px;
  box-shadow: 0 18px 44px rgba(20, 60, 55, 0.10);
}
.anim .rolecard {
  opacity: 0.5;
  transition: opacity .3s ease, transform .3s ease;
  transform: translate3d(0, 10px, 0);
}
.anim .rolecard.near { opacity: 1; transform: none; }
.rc-when {
  font-size: 0.73rem; font-weight: 700; letter-spacing: 0.13em;
  text-transform: uppercase; color: var(--teal-deep);
  font-variant-numeric: tabular-nums; margin-bottom: 10px;
}
.rolecard h3 {
  font-family: var(--serif); font-weight: 700; font-size: 1.18rem;
  line-height: 1.24; letter-spacing: -0.01em; color: var(--ink);
}
.rc-org {
  margin-top: 6px; padding-bottom: 14px; margin-bottom: 14px;
  border-bottom: 1px solid var(--rule-soft);
  font-size: 0.9rem; font-weight: 700; color: var(--ink-soft);
}
.rc-place { font-weight: 500; color: var(--ink-faint); margin-left: 8px; }
.rc-points { list-style: none; display: grid; gap: 10px; }
.rc-points li {
  position: relative; padding-left: 16px;
  font-size: 0.925rem; line-height: 1.55; color: var(--ink-soft);
}
.rc-points li::before {
  content: ""; position: absolute; left: 0; top: 0.62em;
  width: 6px; height: 1px; background: var(--teal);
}

@media (max-width: 880px) {
  .anim .track-exp { height: auto; }
  .anim .track-exp .pin { position: static; height: auto; display: block; overflow: visible; }
  .anim .hz-rail {
    display: grid; grid-template-columns: 1fr; gap: 20px;
    padding: 0; transform: none; will-change: auto;
  }
  /* the floor for the column: with no travel there is no near-the-reading-line state, so
     every card reads at full strength. THE PHONE'S OWN MOTION at the foot of this file
     takes this over under html.mmot and lifts each card in as it is reached. */
  .anim .rolecard { opacity: 1; transform: none; }
  .hz-rail { padding: 0; }
  .hz-head { width: min(var(--maxw), 92vw); margin: 0 auto; }
  /* The rolecards' entrance and the readout are handled in THE PHONE'S OWN MOTION at the
     foot of this file: the cards lift in as they are reached, and the time axis is driven
     by how far you have read DOWN the column. */
}

/* =====================================================================
   03 DELIVERY — one stage at a time
   ===================================================================== */
.anim .track-lc { height: 300vh; }
.anim .stage-lc .scene-set { min-height: 250px; }
.lc-scene { padding-left: 84px; position: relative; }
.anim .lc-scene { padding-left: 84px; }
.lc-n {
  position: absolute; left: 0; top: -6px;
  font-family: var(--serif); font-weight: 700;
  font-size: 3.4rem; line-height: 1; letter-spacing: -0.03em;
  color: var(--teal-deep); opacity: 0.32;
  font-variant-numeric: tabular-nums;
}
.lc-scene h3 {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(1.6rem, 3.4vw, 2.3rem); line-height: 1.16;
  letter-spacing: -0.015em; color: var(--ink);
}
.lc-scene p {
  margin-top: 14px; max-width: 52ch;
  font-size: 1.04rem; color: var(--ink-soft);
}
.lc-src {
  display: inline-block; margin-top: 16px;
  font-size: 0.74rem; font-weight: 700; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--ink-faint);
}
.lc-chain {
  position: relative; display: flex; align-items: center; gap: 0;
  height: 22px; margin: 34px 0 26px;
}
.lc-chain::before {
  content: ""; position: absolute; left: 0; right: 0; top: 10px; height: 2px;
  background: rgba(20, 60, 55, 0.10);
}
.lc-chain-fill {
  position: absolute; left: 0; right: 0; top: 10px; height: 2px;
  transform: scaleX(1); transform-origin: left center;
  background: linear-gradient(90deg, var(--teal-deep), var(--teal) 70%, #3aa79c);
}
.anim .lc-chain-fill { transform: scaleX(var(--p, 1)); }
.lc-chain i {
  position: relative; flex: 1 1 0; height: 22px;
}
.lc-chain i::after {
  content: ""; position: absolute; left: 0; top: 6px;
  width: 10px; height: 10px; border-radius: 50%;
  background: #fff; border: 2px solid var(--teal-deep);
  transition: transform .28s ease, background .28s ease;
}
.anim .lc-chain i::after { background: #fff; border-color: rgba(20, 60, 55, 0.24); }
.anim .lc-chain i.on::after { background: var(--teal-deep); border-color: var(--teal-deep); transform: scale(1.25); }

.lc-across-lbl {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: 12px;
}
/* Six chips laid on a centered auto-width grid so the rows are always balanced and no chip
   is ever left orphaned on a line of its own: 3+3 on desktop, stepping to 2+2+2, then 1-per
   row as the viewport narrows. Centered is the through-line. */
.lc-across {
  list-style: none;
  display: grid; grid-template-columns: repeat(3, auto);
  justify-content: center; justify-items: center; gap: 10px 12px;
}
.lc-across li {
  font-size: 0.83rem; font-weight: 600; color: var(--ink-soft);
  padding: 6px 13px; border: 1px solid var(--rule); border-radius: 999px;
  background: rgba(255, 255, 255, 0.55);
}

@media (max-width: 880px) {
  .lc-scene { padding-left: 62px; }
  .anim .lc-scene { padding-left: 62px; }
  .lc-n { font-size: 2.4rem; }
  .lc-across { grid-template-columns: repeat(2, auto); } /* 2+2+2, still balanced */
}
@media (max-width: 560px) {
  .lc-across { grid-template-columns: auto; } /* one centered chip per row */
}

/* =====================================================================
   04 SERVICE — the standing role, then a pinned frame that holds one award
   year at a time, over a cumulative stack that accumulates across all three.
   ===================================================================== */
.anim .track-svc { height: 260vh; }
.anim .stage-svc .scene-set { min-height: 300px; }

.svc-role {
  display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: 26px 40px; align-items: start; margin-bottom: 34px;
  background: var(--panel); border: 1px solid var(--rule);
  border-left: 3px solid var(--teal-deep); border-radius: var(--r);
  padding: 24px 28px;
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
}
.svc-role h3 {
  font-family: var(--serif); font-weight: 700; font-size: 1.3rem;
  letter-spacing: -0.01em; color: var(--ink);
}
.svc-org { margin-top: 6px; font-size: 0.95rem; color: var(--ink-soft); }
.svc-note { font-size: 0.95rem; color: var(--ink); }
.svc-lbl {
  display: block; font-size: 0.68rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: 3px;
}
.svc-after {
  margin-top: 30px; padding-top: 16px; border-top: 1px solid var(--rule);
  font-size: 0.98rem; color: var(--ink-soft); max-width: 52ch;
}

/* ---- one year at a time ---- */
.yr { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1.25fr); gap: 6px 44px; }
.yr-year {
  grid-column: 1; grid-row: 1 / span 2;
  font-family: var(--serif); font-weight: 700;
  font-size: clamp(4rem, 10vw, 7.2rem); line-height: 0.92;
  letter-spacing: -0.04em; color: var(--teal-deep);
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
  align-self: center;
}
.yr-tier {
  grid-column: 2; grid-row: 1; justify-self: start;
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.16em; text-transform: uppercase;
  padding: 5px 12px; border-radius: 2px; margin-bottom: 6px;
}
.tier-gold { background: rgba(122, 79, 11, 0.13); color: #6d4508; }
.tier-silver { background: rgba(20, 32, 29, 0.10); color: #3d4f4a; }
.yr-fig {
  grid-column: 2; grid-row: 2;
  font-family: var(--serif); font-weight: 700;
  font-size: clamp(1.8rem, 3.4vw, 2.6rem); line-height: 1.1;
  letter-spacing: -0.02em; color: var(--ink);
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
  opacity: var(--f, 1);
}
.yr-unit { font-family: var(--sans); font-size: 0.9rem; font-weight: 600; color: var(--ink-faint); }
.yr-track {
  grid-column: 2; grid-row: 3;
  height: 12px; margin-top: 14px; border-radius: 2px;
  background: rgba(20, 60, 55, 0.09); overflow: hidden;
}
.yr-bar {
  display: block; height: 100%; width: var(--w, 100%);
  transform-origin: left center;
  background: linear-gradient(90deg, var(--teal-deep), var(--teal) 70%, #3aa79c);
  border-radius: 2px;
}
.anim .yr-bar { transform: scaleX(var(--g, 1)); }
.yr-note {
  grid-column: 2; grid-row: 4;
  margin-top: 12px; font-size: 0.95rem; color: var(--ink-soft); max-width: 40ch;
}

/* ---- the cumulative stack: persists across all three scenes ---- */
.cume { margin-top: 38px; padding-top: 22px; border-top: 1px solid var(--rule); }
.cume-lbl {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink-faint); margin-bottom: 12px;
}
.cume-track {
  display: flex; gap: 3px; height: 14px;
  background: rgba(20, 60, 55, 0.07); border-radius: 2px; overflow: hidden;
}
.cume-track i {
  display: block; flex: 0 0 var(--seg); height: 100%;
  transform-origin: left center; border-radius: 2px;
  background: linear-gradient(90deg, var(--teal-deep), var(--teal) 70%, #3aa79c);
}
.cume-track i:nth-child(2) { background: linear-gradient(90deg, var(--teal), #3aa79c 70%, #59bdb2); }
.cume-track i:nth-child(3) { background: linear-gradient(90deg, #3aa79c, #74d0c4); }
.anim .cume-track i { transform: scaleX(var(--s, 1)); }

/* THE RUNNING TOTAL. Three true sums (1,313 / 2,376 / 2,706.75) occupy one line; the loop
   shows exactly the one whose segment has FINISHED filling and never a value in between,
   so no total that was never true can be on screen. Without .anim only the final one is
   in the box at all. */
.cume-figs { position: relative; margin-top: 14px; min-height: 2.1rem; }
.cume-fig {
  font-family: var(--serif); font-weight: 700; font-size: 1.6rem;
  letter-spacing: -0.02em; color: var(--ink);
  font-variant-numeric: tabular-nums; font-feature-settings: "tnum" 1;
  display: none;
}
.cume-fig[data-at="2"] { display: block; }
.anim .cume-fig {
  display: block; position: absolute; left: 0; top: 0;
  opacity: 0; transition: opacity .3s ease;
}
.anim .cume-fig.on { opacity: 1; }

@media (max-width: 880px) {
  .svc-role { grid-template-columns: 1fr; gap: 16px; }
  .yr { grid-template-columns: 1fr; gap: 4px; }
  .yr-year { grid-column: 1; grid-row: 1; font-size: clamp(3.2rem, 16vw, 5rem); }
  .yr-tier, .yr-fig, .yr-track, .yr-note { grid-column: 1; }
  .yr-tier { grid-row: 2; } .yr-fig { grid-row: 3; }
  .yr-track { grid-row: 4; } .yr-note { grid-row: 5; }
  .cume-fig { font-size: 1.35rem; }
}

/* =====================================================================
   05 CAPABILITIES — rows that travel horizontally
   ===================================================================== */
.drift { margin: 0 0 46px; overflow: hidden; }
.drift-row { padding: 14px 0; border-bottom: 1px solid var(--rule-soft); }
.drift-row:first-child { border-top: 1px solid var(--rule-soft); }
.drift-lbl {
  width: min(var(--maxw), 92vw); margin: 0 auto 10px;
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--teal-deep);
}
.drift-set {
  list-style: none; display: flex; flex-wrap: wrap; gap: 8px 10px;
  width: min(var(--maxw), 92vw); margin: 0 auto;
}
.anim .drift-set {
  flex-wrap: nowrap; width: auto; margin: 0;
  padding-left: max(4vw, calc((100vw - var(--maxw)) / 2));
  transform: translate3d(calc(var(--dx, 0) * 1px), 0, 0);
}
.drift-set li {
  flex: none;
  font-size: 0.9rem; font-weight: 600; color: var(--ink);
  padding: 9px 16px; border: 1px solid var(--rule); border-radius: 999px;
  background: rgba(255, 255, 255, 0.66);
  white-space: nowrap;
}

.inds { list-style: none; display: grid; grid-template-columns: repeat(3, 1fr); gap: 26px; }
.ind {
  background: var(--panel); border: 1px solid var(--rule);
  border-top: 3px solid var(--teal-deep); border-radius: var(--r);
  padding: 24px 24px 22px;
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
}
.anim .ind {
  opacity: 0; transform: translate3d(0, 18px, 0);
  transition: opacity .5s ease, transform .5s ease;
}
.anim .ind.on { opacity: 1; transform: none; }
.ind h3 {
  font-family: var(--serif); font-weight: 700; font-size: 1.22rem;
  letter-spacing: -0.01em; color: var(--ink);
}
.ind-orgs {
  margin-top: 6px; padding-bottom: 12px; margin-bottom: 12px;
  border-bottom: 1px solid var(--rule-soft);
  font-size: 0.78rem; font-weight: 700; letter-spacing: 0.06em;
  text-transform: uppercase; color: var(--teal-deep);
}
.ind-what { font-size: 0.94rem; color: var(--ink-soft); }

@media (max-width: 880px) {
  .inds { grid-template-columns: 1fr; gap: 18px; }
  .anim .drift-set { flex-wrap: wrap; padding-left: 0; width: min(var(--maxw), 92vw); margin: 0 auto; transform: none; }
  .drift { overflow: visible; }
}

/* =====================================================================
   06 CREDENTIALS — the assembling lattice.
   Six credentials are drawn in from their own scattered offsets into an aligned
   composition as this section crosses the viewport, and the hairline lattice draws
   itself between them as they land. Every offset is a per-item --ox/--oy in the markup
   and the run is a single --a per item, so the whole assembly is one property per tile
   and the browser is only ever given transform + opacity.
   --a defaults to 1, so with no JS nothing is ever offset and the composition is simply
   built already.
   ===================================================================== */
.lattice {
  position: relative;
  display: grid; grid-template-columns: minmax(0, 1.6fr) minmax(0, 1fr);
  gap: 44px; align-items: start; padding-left: 26px;
}
.lat-lbl {
  font-size: 0.7rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--teal-deep);
  padding-bottom: 12px; margin-bottom: 4px; border-bottom: 1px solid var(--rule);
}
.lat-set { list-style: none; display: grid; gap: 2px; }
.lat {
  padding: 16px 0 16px 18px; border-bottom: 1px solid var(--rule-soft);
  position: relative;
}
.lat::before {
  content: ""; position: absolute; left: 0; top: 16px; bottom: 16px; width: 2px;
  background: var(--teal); transform-origin: center top;
}
.anim .lat {
  transform: translate3d(calc(var(--ox, 0) * (1 - var(--a, 1)) * 1px),
                         calc(var(--oy, 0) * (1 - var(--a, 1)) * 1px), 0);
  opacity: var(--a, 1);
}
.anim .lat::before { transform: scaleY(var(--a, 1)); }
.deg {
  display: block; font-family: var(--serif); font-weight: 700;
  font-size: 1.06rem; letter-spacing: -0.01em; color: var(--ink);
}
/* darker than --ink-faint: the credentials lattice sits directly on the field, and a
   periwinkle bloom passes behind the institution line here. This value clears 4.5:1 over
   the strongest bloom in this band (measured against the real painted pixel, not a token). */
.inst { display: block; margin-top: 3px; font-size: 0.9rem; color: #313f3a; }

/* the lattice rules themselves, drawn as the tiles lock */
.lat-grid { position: absolute; inset: 0; pointer-events: none; }
.lat-grid i { position: absolute; background: var(--rule); }
.lg-v {
  left: 0; top: 0; bottom: 0; width: 1px;
  transform-origin: center top; transform: scaleY(var(--l, 1));
}
.lg-h { left: 0; width: 26px; height: 1px; transform-origin: left center; transform: scaleX(var(--l, 1)); }
.lg-h1 { top: 24%; } .lg-h2 { top: 54%; } .lg-h3 { top: 84%; }

@media (max-width: 880px) {
  .lattice { grid-template-columns: 1fr; gap: 26px; padding-left: 18px; }
  .lg-h { width: 18px; }
}

/* =====================================================================
   07 CONTACT — the direct lines, as panel cards on the field.
   Same panel / hairline / serif language as the rest of the page; the three
   accent hues (teal / periwinkle / rose) ride the left rule only. Every link
   is a real, labelled, keyboard-reachable target of at least 44px.
   ===================================================================== */
.contact-grid {
  list-style: none;
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 16px;
}
.con {
  display: flex; flex-direction: column; gap: 6px;
  background: var(--panel-solid); border: 1px solid var(--rule);
  border-left: 3px solid var(--teal-deep); border-radius: var(--r);
  padding: 20px 22px;
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  box-shadow: 0 14px 34px rgba(20, 60, 55, 0.09);
}
.con-teal { border-left-color: var(--teal-deep); }
.con-indigo { border-left-color: var(--indigo); }
.con-rose { border-left-color: var(--plum); }
.con-lbl {
  font-size: 0.68rem; font-weight: 700; letter-spacing: 0.16em;
  text-transform: uppercase; color: var(--ink-faint);
}
.con-val {
  font-family: var(--serif); font-size: 1.06rem; letter-spacing: -0.01em;
  color: var(--ink); text-decoration: none; word-break: break-word;
}
a.con-val {
  display: inline-flex; align-items: center; min-height: 44px;
  color: var(--teal-deep); border-bottom: 1px solid transparent;
  width: max-content; max-width: 100%;
}
a.con-val:hover, a.con-val:focus-visible { border-bottom-color: var(--teal); }
.con-static { color: var(--ink); }

@media (max-width: 720px) {
  .contact-grid { grid-template-columns: 1fr; }
}

/* ---- reveal floor (the entrance, not a feature) ---- */
.anim [data-reveal] {
  opacity: 0; transform: translate3d(0, 16px, 0);
  transition: opacity .55s ease, transform .55s ease;
}
.anim [data-reveal].shown { opacity: 1; transform: none; }

/* ---- footer: the closing rule ---- */
footer {
  position: relative;
  margin-top: 40px; padding: 34px 0;
  border-top: 1px solid var(--rule);
  background: rgba(255, 255, 255, 0.42);
}
.foot-rule {
  position: absolute; left: 0; right: 0; top: -2px; height: 3px; overflow: hidden;
}
.foot-rule span {
  display: block; height: 100%; width: 100%;
  transform-origin: left center; transform: scaleX(1);
  background: linear-gradient(90deg, var(--teal-deep), var(--teal) 55%, #3aa79c 80%, #74d0c4);
}
.anim .foot-rule span { transform: scaleX(var(--c, 1)); }
.foot {
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: 10px 26px;
  font-size: 0.88rem; color: var(--ink-soft);
}
.foot-note { color: var(--ink-faint); }
/* the two lines travel out to their ends as the rule draws */
.anim .foot-a { transform: translate3d(calc((var(--c, 1) - 1) * 26px), 0, 0); }
.anim .foot-b { transform: translate3d(calc((1 - var(--c, 1)) * 26px), 0, 0); }
.foot-a, .foot-b { display: inline-block; }

/* =====================================================================
   THE PHONE. Everything below is scoped to a max-width media query, so no rule
   here can reach the desktop rendering.

   WHY this section exists (2026-07-21): the page was built and judged on a desktop
   viewport, where a 100vh pinned frame is a large canvas. On a phone it is a small
   one, and every scene inside it is single-column and therefore two to three times
   TALLER than the same scene on desktop. Measured with motion ON (which is what a
   phone actually gets - the reduced-motion capture never sees this) at 320x640:

       track-impact  pin=640px  content=800px  ->  160px CUT OFF by overflow:hidden
       track-lc      pin=640px  content=691px  ->   51px CUT OFF

   The clipped 160px was the bottom of the outcomes scene: real figures from her
   record, silently guillotined. On a real person's live portfolio that is not a
   styling nit, it is missing content, so the pin is the thing that goes.

   WHAT REPLACES IT. Nothing is deleted. The three pinned sections fall back to the
   FLOOR each one was designed with and documents in its own header comment above:
   every scene stacks in normal flow, every bar sits at its true width, every figure
   is at its final value, every rail is drawn complete. That floor is a real,
   authored layout (it is what a reduced-motion reader has always been served), not
   a degradation - it just cannot clip, at any phone height, ever.

   Because the pin is gone, the values the rAF loop scrubs (--g / --f / --s / --p and
   the .on classes) must be neutralized here or a row that the loop happens to be
   holding at opacity 0 would sit invisible in the flow. Every one is pinned to its
   complete state below. This mirrors what the experience rail already does at this
   same breakpoint.

   The effects that DO survive on a phone are the ones that were never pin-driven:
   the entrance reveals, the sequenced industry cards, the hero handoff, the
   credentials lattice assembling, and the footer rule drawing. The two with lateral
   travel are damped (see below) so they can no longer throw text off-screen.
   ===================================================================== */

@media (max-width: 880px) {

  /* ---- unpin: the runway collapses and the frame stops being a fixed 100vh box ---- */
  .anim .track { height: auto; }
  .anim .pin {
    position: static; height: auto; min-height: 0;
    display: block; overflow: visible;
  }

  /* ---- scenes return to normal flow, separated by the page's own hairline ---- */
  .anim .scene-set { position: static; min-height: 0; }
  .anim .stage-impact .scene-set,
  .anim .stage-lc .scene-set,
  .anim .stage-svc .scene-set { min-height: 0; }
  .anim .scene,
  .anim .scene.on {
    position: static; inset: auto;
    opacity: 1; visibility: visible; pointer-events: auto;
    transform: none; transition: none;
  }
  .anim .scene + .scene {
    margin-top: 32px; padding-top: 28px; border-top: 1px solid var(--rule-soft);
  }

  /* ---- pin every value the PIN used to scrub to its COMPLETE state ----
     This is the floor for the unpinned layout: with the pin gone the rAF loop no longer
     writes --g / --f / --s / --p here, so every one of them falls to its documented
     default of 1 and the section reads complete. THE PHONE'S OWN MOTION at the foot of
     this file then takes each of these over under html.mmot - which only exists once
     app.js has registered the observers that put them back. If app.js never runs, these
     rules are what is on screen. */
  .anim .grow { opacity: 1; }
  .anim .grow-fig,
  .anim .scope-fig,
  .yr-fig { opacity: 1; transform: none; }
  .anim .grow-bar,
  .anim .yr-bar,
  .anim .cume-track i,
  .anim .stage-fill,
  .anim .lc-chain-fill { transform: scaleX(1); }
  .anim .lc-chain i::after {
    background: var(--teal-deep); border-color: var(--teal-deep);
  }
  /* the running total: only the true final sum, exactly as the no-JS floor shows it */
  .anim .cume-fig { position: static; display: none; opacity: 1; }
  .anim .cume-fig[data-at="2"] { display: block; }

  /* ---- the stage is a card on a 300px-wide screen, not a 1120px one ---- */
  .stage { padding: 28px 20px 26px; }
  .band { padding: 44px 0; }
  .sec-head { gap: 14px; margin-bottom: 26px; }

  /* ---- damp the lateral travel so no entrance can push text off the screen ----
     The multiplier drops from 1px to a fraction; the moves still read as arrivals,
     they just stay inside the column. Measured overshoot before this: the credential
     tiles left the card by up to 58px and the org list by 34px. */
  /* The lateral component is damped hardest: a credential tile is nearly as wide as the
     column it sits in, so ANY sideways offset pushes its text past the screen edge (0.26
     still measured 321px on a 320px screen). The assembly now carries on the vertical
     travel and the fade, which have room; the tiles still land, they just no longer
     reach for a pixel that is not there. */
  .anim .lat {
    transform: translate3d(calc(var(--ox, 0) * (1 - var(--a, 1)) * 0.10px),
                           calc(var(--oy, 0) * (1 - var(--a, 1)) * 0.5px), 0);
  }
  .anim .hs-list li { transform: translate3d(calc((1 - var(--o, 1)) * 14px), 0, 0); }
  .anim .gl-row { transform: translate3d(calc(var(--h, 0) * var(--i, 1) * 12px), 0, 0); }
  /* ...and the card CLIPS that travel at its own border, which is the fix for the one
     defect that was inflating the layout at every scroll position past the hero.
     The glance rows exit to the RIGHT by --i * n px, and --i reaches 4 on the last row.
     Once the hero is behind you --h stays at 1, so that row sits translated ~48px past
     the card edge FOREVER - invisible (the same scrub takes it to opacity 0) but still
     contributing scrollable width. That is what made a 320px screen report a 336px
     layout at baseline: mobile Chrome answers a too-wide layout by zooming the entire
     page out, which is precisely the "everything is tiny" complaint. Clipping at the
     card border keeps the fan-out exactly as designed (the rows now slide out UNDER the
     panel edge, which reads better anyway) and it cannot widen the page. */
  .hero-side { overflow: hidden; }
  /* the footer lines stack on a phone, so "travelling apart" has nowhere to go */
  .anim .foot-a, .anim .foot-b { transform: none; }

  /* ---- nothing on the page renders below 12px (pinch-zoom threshold) ----
     Every one of these is an uppercase tracked micro-label. 0.75rem = 12px exactly. */
  .navtoggle,
  .eyebrow,
  .glance dt,
  .hs-kicker,
  .scene-kicker,
  .scene-src,
  .hz-tick,
  .rc-when,
  .lc-src,
  .lc-across-lbl,
  .svc-lbl,
  .yr-tier,
  .cume-lbl,
  .drift-lbl,
  .lat-lbl,
  .con-lbl { font-size: 0.75rem; }

  /* ---- the brand is a thumb target, not just a 34px mark ---- */
  .brand { min-height: 44px; }

  /* ---- an anchor jump must clear the sticky header ----
     Measured at 320/390/414: the mobile header (masthead strip + brand/Menu bar) is
     101-103px tall, but the anchor offset was 80px, so tapping any of the seven Menu
     links landed the section's numeral and heading UNDERNEATH the bar. The offset has
     to be the header's real height plus a little air, not less than it. */
  .band, #top { scroll-margin-top: 116px; }
}

/* ---- NARROW PHONES (<=560px): reclaim the horizontal gutters the desktop layout
   can afford and a 320px screen cannot. ---- */
@media (max-width: 560px) {
  .stage { padding: 24px 16px 24px; }

  /* the delivery numeral moves from a left gutter to its own line above the heading:
     at 320px that gutter was eating 62 of the 206 usable pixels. */
  .lc-scene,
  .anim .lc-scene { padding-left: 0; }
  .lc-n {
    position: static; display: block; margin-bottom: 2px;
    font-size: 2.1rem; line-height: 1; opacity: 0.36;
  }

  /* capability chips may wrap INSIDE the pill. The longest ("Budget versus actual
     variance reporting") measures ~302px against a 294px column at 320px, so with
     white-space:nowrap it was being clipped by the body's overflow-x guard - the
     text was cut, not merely tight. */
  /* flex:none (0 0 auto) pins each chip to its max-content width, so white-space alone
     does not save it - the chip must also be ALLOWED to shrink and capped at the column. */
  .drift-set li {
    flex: 0 1 auto; max-width: 100%;
    white-space: normal; padding: 8px 14px;
  }
  .drift-row { padding: 12px 0; }

  /* the credentials lattice: one indent, not two nested ones */
  .lattice { padding-left: 14px; }
  .lat { padding-left: 14px; }

  .contact-grid { gap: 12px; }
  .con { padding: 16px 18px; }
}

/* ---- LANDSCAPE PHONE (2026-07-21): touch ergonomics follow the POINTER, not the viewport ----
   A 44px target is a property of the finger, not of the viewport, so it has to hold at every
   width a phone can present. Held sideways this page's own 880px layout rules still apply
   (844 < 880) and the seven-link bar still fits, so nothing here needs to restack - the one
   thing that broke is the brand: the "(max-width:1140px) and (min-width:721px)" rule above
   hides .brand-name at tablet widths, which at 844 leaves the link as nothing but its 34px
   mark. Measured 34x44: tall enough, too narrow. min-width gives the link a full 44px hit
   box while the mark itself stays exactly 34px, so nothing about the masthead looks different.
   Keyed to the pointer and capped at 900px, so a desktop cursor can never reach this block. */
@media (max-width: 900px) and (pointer: coarse) {
  .brand { min-width: 44px; }
}

/* =====================================================================
   THE PHONE'S OWN MOTION  (2026-07-21)

   The block above unpins the three pinned tracks, because a 100vh pin on a phone was
   cutting 160px off the bottom of a real person's figures. That fixed the clipping and
   left the phone STATIC, which is the complaint that got this page rejected three times
   in the first place. This section is the other half of the answer: the phone gets the
   motion back, driven by each element's OWN crossing of the viewport instead of by a
   fixed-height frame. Nothing here is ever held in a box smaller than itself, so the
   clipping cannot come back with it.

   WHAT IS SCOPED WHERE, and why it is .mmot and not .anim:
     .anim   is set by the inline head script the moment motion is allowed - before a
             single line of app.js has run, and even if app.js never runs at all.
     .mmot   is set by app.js itself, inside one synchronous block that also registers
             every entrance observer AND the net that backs them up, and is REMOVED again
             if any of that throws.
   Every displaced state below is scoped to .mmot, so the promise "this will appear" is
   only ever made by the code that can keep it. JS off, app.js blocked, CSP refusal,
   reduced motion: no .mmot, and what renders is the complete floor above.

   WHY EVERY TRANSITION IS DECLARED ON THE LANDED STATE, NEVER ON THE DISPLACED ONE.
   A long document paints while it is still parsing, so the browser can show this page
   COMPLETE before the deferred app.js has run. If the transition lived on the displaced
   state, the moment .mmot landed every heading and figure would visibly FADE OUT and then
   fade back in - which is exactly what the first cut of this section did (measured: the
   01 numeral went 1 -> 0.30 -> 0 -> 1). Declaring the transition on the landed rule makes
   the complete -> displaced step instantaneous and unanimated, and only the displaced ->
   landed step plays. Do not move a transition back onto a base rule here.

   NO FIGURE IS TOUCHED. Bars grow; figures are revealed at their true value by opacity
   alone; the running total steps only between three real sums. Same rule as the desktop.
   ===================================================================== */
@media (max-width: 880px) {

  /* ---- the hero: her name arrives a word at a time ----
     The word units are already in the markup (<span class="w"><i>word</i></span>), split
     at true word boundaries so the flat text a screen reader and title extraction see is
     still "Quindalyn Denean Peace". No mask and no overflow clip: the words simply rise
     and resolve, so no descender can ever be cut and no container can clip a letter.
     Armed by .herin (two animation frames after setup) rather than by the hero's reveal
     observer: the hero is ON SCREEN at load, so an observer that fires in the same frame
     the displaced state is applied leaves the browser no painted start value and it jumps
     straight to the end - which is what happened, and why there was no entrance at all. */
  .mmot .hero-lead h1 .w { display: inline-block; opacity: 0; transform: translate3d(0, 14px, 0); }
  .mmot.herin .hero-lead h1 .w {
    opacity: 1; transform: none;
    transition: opacity .6s ease, transform .6s cubic-bezier(.22, .61, .36, 1);
  }
  .mmot.herin .hero-lead h1 .w:nth-child(1) { transition-delay: 90ms; }
  .mmot.herin .hero-lead h1 .w:nth-child(2) { transition-delay: 210ms; }
  .mmot.herin .hero-lead h1 .w:nth-child(3) { transition-delay: 330ms; }

  /* ---- every section head: the numeral leads, the heading follows ---- */
  .mmot .sec-head .sec-num { opacity: 0; transform: translate3d(-12px, 0, 0); }
  .mmot .sec-head h2 { opacity: 0; transform: translate3d(0, 8px, 0); }
  .mmot .sec-head.shown .sec-num {
    opacity: 1; transform: none;
    transition: opacity .5s ease 60ms, transform .5s cubic-bezier(.22, .61, .36, 1) 60ms;
  }
  .mmot .sec-head.shown h2 {
    opacity: 1; transform: none;
    transition: opacity .5s ease 190ms, transform .5s cubic-bezier(.22, .61, .36, 1) 190ms;
  }

  /* ---- the read-progress rail: down the card's left edge, not across its foot ----
     Pinned, the rail sat at the bottom of a 100vh frame and was always in view. Unpinned,
     the card is taller than the screen, so a rail at its foot would only be reached at the
     end. Turned on its side it runs the whole height of the card and fills as you read it,
     joining the card's own 3px teal top border. Scrubbed continuously by app.js. */
  .mmot .stage-rail {
    top: 0; bottom: 0; left: 0; right: auto;
    width: 3px; height: auto;
    border-radius: 0;
  }
  .mmot .stage-fill {
    width: 100%; height: 100%;
    transform-origin: center top;
    transform: scaleY(var(--p, 1));
    background: linear-gradient(180deg, var(--teal-deep), var(--teal) 70%, #3aa79c);
  }

  /* =============== 01 IMPACT =============== */
  /* the two scope figures, each with its own descriptor arriving behind it */
  .mmot .scope > * { opacity: 0; transform: translate3d(0, 14px, 0); }
  .mmot .scope.si-on > * {
    opacity: 1; transform: none;
    transition: opacity .55s ease, transform .55s cubic-bezier(.22, .61, .36, 1);
  }
  .mmot .scope.si-on > .scope-fig { transition-delay: var(--sd, 0ms); }
  .mmot .scope.si-on > .scope-what { transition-delay: calc(var(--sd, 0ms) + 150ms); }
  .mmot .scope.si-on > .scope-where { transition-delay: calc(var(--sd, 0ms) + 260ms); }

  /* the five outcome rows: the row arrives, its bar grows, and its figure resolves at its
     TRUE value as that bar completes - the pinned sequence, one row at a time instead of
     all five inside one frame. */
  .mmot .grow { opacity: 0; transform: translate3d(0, 14px, 0); }
  .mmot .grow.si-on {
    opacity: 1; transform: none;
    transition: opacity .5s ease var(--sd, 0ms),
                transform .5s cubic-bezier(.22, .61, .36, 1) var(--sd, 0ms);
  }
  .mmot .grow .grow-bar { transform: scaleX(0); }
  .mmot .grow.si-on .grow-bar {
    transform: scaleX(1);
    transition: transform .9s cubic-bezier(.22, .61, .36, 1) calc(var(--sd, 0ms) + 130ms);
  }
  .mmot .grow .grow-fig { opacity: 0; }
  .mmot .grow.si-on .grow-fig {
    opacity: 1;
    transition: opacity .45s ease calc(var(--sd, 0ms) + 640ms);
  }

  /* =============== 02 EXPERIENCE =============== */
  /* The rail cannot travel sideways on a phone, so the AXIS does. How far you have read
     down the column fills the time axis, moves its mark, and names the role you are level
     with - the section's idea (vertical scroll drives horizontal travel) in the one form a
     phone can hold. app.js writes --p continuously; the readout only ever repeats a card's
     own data attribute. */
  .mmot .hz-read { display: block; min-height: 0; margin-bottom: 2px; }
  .mmot .hz-when { display: block; font-size: 1.02rem; line-height: 1.3; }
  .mmot .hz-org { display: block; margin-top: 1px; }
  /* The end ticks are centred on their own point, so at 0% and 98.5% half the label hangs
     outside the column: measured at 320, "2010" started at x=-3.3 and its first glyph was
     shaved by the page's overflow guard. Insetting the axis by a little more than a label's
     half-width gives both ends room; the rail, the fill and --axisw all follow the inset, so
     the mark still travels exactly end to end. */
  .mmot .hz-axis { margin: 10px 18px 0; }

  /* the seven roles lift in as they are reached, their evidence following behind */
  .mmot .rolecard { opacity: 0; transform: translate3d(0, 22px, 0); }
  .mmot .rolecard.si-on {
    opacity: 1; transform: none;
    transition: opacity .55s ease var(--sd, 0ms),
                transform .55s cubic-bezier(.22, .61, .36, 1) var(--sd, 0ms);
  }
  .mmot .rolecard .rc-points li { opacity: 0; transform: translate3d(0, 8px, 0); }
  .mmot .rolecard.si-on .rc-points li {
    opacity: 1; transform: none;
    transition: opacity .4s ease, transform .4s ease;
  }
  .mmot .rolecard.si-on .rc-points li:nth-child(1) { transition-delay: calc(var(--sd, 0ms) + 250ms); }
  .mmot .rolecard.si-on .rc-points li:nth-child(2) { transition-delay: calc(var(--sd, 0ms) + 330ms); }
  .mmot .rolecard.si-on .rc-points li:nth-child(3) { transition-delay: calc(var(--sd, 0ms) + 410ms); }
  .mmot .rolecard.si-on .rc-points li:nth-child(4) { transition-delay: calc(var(--sd, 0ms) + 490ms); }
  .mmot .rolecard.si-on .rc-points li:nth-child(n+5) { transition-delay: calc(var(--sd, 0ms) + 570ms); }

  /* =============== 03 DELIVERY =============== */
  /* seven stages, each staging itself: numeral, heading, what happened, where */
  .mmot .lc-scene > * { opacity: 0; transform: translate3d(0, 12px, 0); }
  .mmot .lc-scene > .lc-n { transform: translate3d(-10px, 0, 0); }
  .mmot .lc-scene.si-on > * {
    opacity: 1; transform: none;
    transition: opacity .5s ease, transform .5s cubic-bezier(.22, .61, .36, 1);
  }
  .mmot .lc-scene.si-on > .lc-n { opacity: 0.32; transition-delay: var(--sd, 0ms); }
  .mmot .lc-scene.si-on > h3 { transition-delay: calc(var(--sd, 0ms) + 90ms); }
  .mmot .lc-scene.si-on > p { transition-delay: calc(var(--sd, 0ms) + 170ms); }
  .mmot .lc-scene.si-on > .lc-src { transition-delay: calc(var(--sd, 0ms) + 250ms); }

  /* the chain draws itself and its seven nodes light in order - the same completion the
     pinned version played out one stage at a time, played out once, at the end. */
  .mmot .lc-chain-fill { transform: scaleX(0); }
  .mmot .lc-chain.run .lc-chain-fill {
    transform: scaleX(1);
    transition: transform 1.5s cubic-bezier(.4, 0, .2, 1) 120ms;
  }
  .mmot .lc-chain i::after { background: #fff; border-color: rgba(20, 60, 55, 0.24); }
  .mmot .lc-chain.run i::after {
    background: var(--teal-deep); border-color: var(--teal-deep);
    transition: background .28s ease, border-color .28s ease;
  }
  .mmot .lc-chain.run i:nth-of-type(1)::after { transition-delay: 180ms; }
  .mmot .lc-chain.run i:nth-of-type(2)::after { transition-delay: 380ms; }
  .mmot .lc-chain.run i:nth-of-type(3)::after { transition-delay: 580ms; }
  .mmot .lc-chain.run i:nth-of-type(4)::after { transition-delay: 780ms; }
  .mmot .lc-chain.run i:nth-of-type(5)::after { transition-delay: 980ms; }
  .mmot .lc-chain.run i:nth-of-type(6)::after { transition-delay: 1180ms; }
  .mmot .lc-chain.run i:nth-of-type(7)::after { transition-delay: 1380ms; }

  /* the six things that run across every stage */
  .mmot .lc-across li { opacity: 0; transform: translate3d(0, 8px, 0); }
  .mmot .lc-across li.si-on {
    opacity: 1; transform: none;
    transition: opacity .4s ease var(--sd, 0ms),
                transform .4s cubic-bezier(.22, .61, .36, 1) var(--sd, 0ms);
  }

  /* =============== 04 SERVICE =============== */
  /* each award year stages: the year, its tier, its bar growing to that year's share, its
     hours figure resolving at the TRUE value as the bar completes, then the note. */
  .mmot .yr > * { opacity: 0; transform: translate3d(0, 12px, 0); }
  .mmot .yr.si-on > * {
    opacity: 1; transform: none;
    transition: opacity .5s ease, transform .5s cubic-bezier(.22, .61, .36, 1);
  }
  .mmot .yr.si-on > .yr-year { transition-delay: var(--sd, 0ms); }
  .mmot .yr.si-on > .yr-tier { transition-delay: calc(var(--sd, 0ms) + 130ms); }
  .mmot .yr.si-on > .yr-track { transition-delay: calc(var(--sd, 0ms) + 200ms); }
  .mmot .yr.si-on > .yr-fig { transition-delay: calc(var(--sd, 0ms) + 900ms); }
  .mmot .yr.si-on > .yr-note { transition-delay: calc(var(--sd, 0ms) + 980ms); }
  .mmot .yr .yr-bar { transform: scaleX(0); }
  .mmot .yr.si-on .yr-bar {
    transform: scaleX(1);
    transition: transform .95s cubic-bezier(.22, .61, .36, 1) calc(var(--sd, 0ms) + 260ms);
  }

  /* the cumulative stack fills segment by segment, and the running total steps
     1,313 -> 2,376 -> 2,706.75. Each of those three is a real sum of real years, and each
     appears only once its own segment has finished filling, so there is no moment at which
     a total that was never true is on screen. Keyframes, not a scrub, so the sequence
     always completes: it cannot be left half-run by where the reader stopped scrolling.
     If .run is never applied, the base state below is the true final sum, alone. */
  .mmot .cume-track i { transform: scaleX(0); }
  .mmot .cume.run .cume-track i {
    transform: scaleX(1);
    transition: transform .55s cubic-bezier(.22, .61, .36, 1) var(--cd, 0ms);
  }
  .mmot .cume-track i:nth-child(1) { --cd: 0ms; }
  .mmot .cume-track i:nth-child(2) { --cd: 700ms; }
  .mmot .cume-track i:nth-child(3) { --cd: 1400ms; }

  /* All three start absent, INCLUDING the final sum. An earlier cut left the final sum
     visible as the .mmot base, so a reader scrolling in saw "2,706.75 hours", then saw it
     pop out the instant .run armed (an animation's fill state is applied instantly, it does
     not fade), then saw it come back. The floor for this figure is the net in app.js, which
     lands .run for anything that has reached the reading line - the same guarantee every
     other displaced state on the phone relies on, and it is set by the same code that set
     .mmot in the first place. */
  .mmot .cume-fig { display: block; position: absolute; left: 0; top: 0; opacity: 0; }
  .mmot .cume.run .cume-fig[data-at="0"] { animation: cumeStep .70s linear .55s both; }
  .mmot .cume.run .cume-fig[data-at="1"] { animation: cumeStep .70s linear 1.25s both; }
  .mmot .cume.run .cume-fig[data-at="2"] { animation: cumeLand .35s ease 1.95s both; }

  /* =============== 05 CAPABILITIES =============== */
  /* The rows cannot travel on a phone without pushing a chip off the screen, so the
     travel becomes an ARRIVAL: each row's chips come in from that row's own direction,
     alternating down the four rows exactly as the desktop drift alternates. The offset is
     10px against a 4vw gutter, so nothing can reach past the screen edge at any width. */
  .mmot .drift-row .drift-set li {
    opacity: 0;
    transform: translate3d(calc(var(--dir, -1) * -10px), 0, 0);
  }
  .mmot .drift-row.si-on .drift-set li {
    opacity: 1; transform: none;
    transition: opacity .45s ease var(--cd, 0ms),
                transform .45s cubic-bezier(.22, .61, .36, 1) var(--cd, 0ms);
  }
  .mmot .drift-row .drift-set li:nth-child(6n+1) { --cd: 0ms; }
  .mmot .drift-row .drift-set li:nth-child(6n+2) { --cd: 60ms; }
  .mmot .drift-row .drift-set li:nth-child(6n+3) { --cd: 120ms; }
  .mmot .drift-row .drift-set li:nth-child(6n+4) { --cd: 180ms; }
  .mmot .drift-row .drift-set li:nth-child(6n+5) { --cd: 240ms; }
  .mmot .drift-row .drift-set li:nth-child(6n+6) { --cd: 300ms; }

  /* =============== 07 CONTACT =============== */
  .mmot .con { opacity: 0; transform: translate3d(0, 14px, 0); }
  .mmot .con.si-on {
    opacity: 1; transform: none;
    transition: opacity .5s ease var(--sd, 0ms),
                transform .5s cubic-bezier(.22, .61, .36, 1) var(--sd, 0ms);
  }
}

/* The running total's two steps. cumeStep holds a true sum for its own segment's turn and
   then hands over; cumeLand brings the final sum in and leaves it there (fill: both). */
@keyframes cumeStep { 0% { opacity: 0 } 14% { opacity: 1 } 86% { opacity: 1 } 100% { opacity: 0 } }
@keyframes cumeLand { from { opacity: 0 } to { opacity: 1 } }

@media (max-width: 560px) {
  /* the delivery numeral sits on its own line here (see above), so it rises rather than
     sliding in from a gutter it no longer has, and it carries this width's own opacity. */
  .mmot .lc-scene > .lc-n { transform: translate3d(0, 10px, 0); }
  .mmot .lc-scene.si-on > .lc-n { opacity: 0.36; transform: none; }
}

/* ---- the hard floor: no motion, ever ---- */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation: none !important;
    transition: none !important;
  }
}
