/* ============================================================
   Darian Grant — portfolio
   Tokens from the "Portfolio Images" Figma file.
   Motion + hover states mirrored from dariangrant-marroni-mockup_15.html:
   .rise reveal (24px / .7s / staggered 70ms), button lift, card lift +
   media scale, trust un-rotate, marquee pause, arch lift + thumb scale.
   ============================================================ */

:root{
  --white:#fff;
  --black:#000;
  --black-60:rgba(0,0,0,.6);
  --black-55:rgba(0,0,0,.55);
  --black-35:rgba(0,0,0,.35);
  --grey-50:#7b8085;
  --accent:#5b3a70;
  --green:#39ca39;
  --green-text:#007000;
  --orange-50:#ff4d00;
  --yellow-61:#e7eb4c;
  --azure-42:#0b90cb;
  --azure-40:#0c59c1;
  --star:#f5a623;

  --clair:rgba(255,255,255,.4);      /* the shell fill */
  --nuage:rgba(255,255,255,.6);
  --wash-a:#fff9f1;
  --wash-b:#fdf3fe;
  /* Hold the opening colour through the first half of each section, then
     transition across the second half. The reverse wash keeps the same rule
     measured from its own starting edge. */
  --wash-down:linear-gradient(180deg,var(--wash-a) 0%,var(--wash-a) 50%,var(--wash-b) 100%);
  --wash-up:linear-gradient(0deg,var(--wash-a) 0%,var(--wash-a) 50%,var(--wash-b) 100%);

  --r-shell:40px;
  --r-card:32px;
  --r-arch:20px;
  --r-pill:48px;
  --notch:40px;

  --sombra:0 72px 72px -3px rgba(0,0,0,.06),0 40px 40px -2.5px rgba(0,0,0,.07),
           0 23px 23px -2px rgba(0,0,0,.08),0 14px 14px -2px rgba(0,0,0,.08),
           0 9px 9px -1px rgba(0,0,0,.08),0 5px 5px -1px rgba(0,0,0,.08),
           0 3px 3px -.6px rgba(0,0,0,.08),0 1px 1px -.3px rgba(0,0,0,.08);
  --inner:inset 0 0 8px rgba(0,0,0,.2);
  --glass-shadow:0 8px 40px -20px rgba(0,0,0,.5);
  --img-shadow:0 32px 40px -40px rgba(0,0,0,.33);

  --ease:cubic-bezier(.22,1,.36,1);
  --gutter:22px;                     /* page inset */
  --hero-pad:18px;
  --max:1397px;
  --pad-x:140px;
  --pad-x-step:70px;               /* narrative sections, so the media column gets the room */

  /* TYPE SCALE — values are Figma's unless noted. No decimals anywhere.
     h1 40 and section 36 are Darian's overrides (Figma has 50 and 40). */
  --fs-h1:40px;         /* hero headline */
  --fs-section:36px;    /* section headings */
  --fs-quote:40px;      /* the pull quote */
  --fs-lead:32px;       /* About-me subheading + "An experienced leader..." */
  --fs-title:24px;      /* card + service titles */
  --fs-body:16px;       /* body copy */
  --fs-small:15px;      /* small labels, service body, trust pills */
  --fs-label:14px;      /* nav, section toggle, footer meta */
  --fs-tiny:13px;       /* employer roles, archive subtitle, tags */
  --fs-btn:12px;        /* button label */
  --fs-display:80px;    /* "Kindly hire me." */
  --fs-wordmark:140px;  /* footer DARIAN */

  /* CASE STUDY SCALE — measured from the Figma "Case Study" PDF export.
     Hero is Darian's 50->40 override, as on the home page; everything else
     is the Figma value. No decimals. */
  --fs-case-h1:40px;        /* Figma 50, overridden to match the home hero */
  --fs-case-title:32px;     /* step / section title */
  --fs-case-surtitle:16px;  /* "STEP 04", "CONTEXT" */
  --fs-case-body:16px;      /* body copy, line-height 24 */
  --fs-case-fact:16px;      /* fact pill text and panel labels */
  --fs-case-beat:48px;      /* the narrative interlude, line-height 58 */

  /* Step layout, measured: 345 text / 45 gap / 726 media = 1116 content */
  --case-col:345px;
  --case-gap:45px;
}

*{box-sizing:border-box}
html{scroll-behavior:smooth}
body{
  margin:0;background:var(--white);color:var(--black);
  font-family:Inter,system-ui,-apple-system,"Segoe UI",sans-serif;
  -webkit-font-smoothing:antialiased;
}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
h1,h2,h3,h4,p,figure,blockquote{margin:0}
ul{margin:0;padding:0;list-style:none}
:focus-visible{outline:3px solid #1a56db;outline-offset:3px;border-radius:6px}

/* ---- page frame: everything is inset from the page edge ---- */
.frame{padding:0 var(--gutter)}
.slabs{max-width:var(--max);margin:0 auto}
/* radii on the end sections rather than overflow:hidden on the stack — overflow:hidden would make .slabs a scroll container and break scrollIntoView/anchors */
.slabs > section:first-child{border-top-left-radius:var(--r-shell);border-top-right-radius:var(--r-shell)}
.slabs > section:last-child{border-bottom-left-radius:var(--r-shell);border-bottom-right-radius:var(--r-shell)}
.inner{padding:140px var(--pad-x)}
.inner--tight{padding-bottom:32px}
.wide{max-width:1117.45px;margin:0 auto}

.slab--down{background-image:var(--wash-down)}
.slab--up{background-image:var(--wash-up)}

/* ---- reveal on scroll (mirrors .rise from the reference) ---- */
.rise{opacity:0;transform:translateY(24px);transition:opacity .7s var(--ease),transform .7s var(--ease)}
.rise.in{opacity:1;transform:none}
@media (prefers-reduced-motion:reduce){
  .rise{opacity:1;transform:none;transition:none}
  .marquee__track{animation:none}
}

/* ---------- shared ---------- */
.stars{display:flex;gap:3px}
.stars svg{width:18px;height:18px;fill:var(--star)}
.avatars{display:flex}
.avatars img{width:45px;height:45px;border-radius:50%;object-fit:cover;
  border:3px solid var(--white);box-shadow:0 2px 6px rgba(0,0,0,.18);margin-left:-14px}
.avatars img:first-child{margin-left:0}
.trustline{display:flex;align-items:center;gap:16px;flex-wrap:wrap}
.trustline__text{display:flex;flex-direction:column;justify-content:center;gap:2px}
.trustline__label{font-size:var(--fs-small);font-weight:300;color:var(--black-60);line-height:24px}

/* button — reference: 1px translucent border, 3px outline, radial highlight,
   hover adds a white veil + lifts 2px, active returns to 0 */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:12px;width:fit-content;
  padding:6px 24px 6px 6px;border-radius:var(--r-pill);
  background-color:var(--black);
  background-image:radial-gradient(circle at 90% 10%,#ffffff52,#fff0);
  color:var(--white);
  border:1px solid var(--clair);outline:3px solid var(--nuage);outline-offset:0;
  font-size:var(--fs-btn);font-weight:500;letter-spacing:1.6px;text-transform:uppercase;
  box-shadow:var(--sombra);
  transition:background-image .3s var(--ease),transform .1s linear;
}
.btn:hover{background-image:linear-gradient(#fff3,#fff3),radial-gradient(circle at 90% 10%,#ffffff52,#fff0);transform:translateY(-2px)}
.btn:active{transform:translateY(0)}
.btn img{width:42px;height:42px;border-radius:50%;object-fit:cover;object-position:55% 16%;flex:none;border:2px solid var(--white)}
.btn--bare{gap:16px;padding:16px 32px}

/* status pill — reference values exactly */
/* The green status is a frame: 24px line-height on the text, 8px padding above
   and below inside the frame. Applies everywhere this dot-and-text pair appears. */
.status{display:flex;align-items:center;gap:8px;margin-top:8px;margin-left:-12px;padding:8px 12px;
  font-size:var(--fs-label);line-height:24px;font-weight:500;color:var(--green-text)}
.status i{width:8px;height:8px;border-radius:40px;background:var(--green);outline:4px solid rgba(57,202,57,.3);outline-offset:0;flex:none}
.status--dark{color:#4ade80}

.label{display:flex;align-items:center;gap:16px;padding-bottom:32px;flex-wrap:wrap}
.label h2{font-size:var(--fs-section);font-weight:700;letter-spacing:-.5px}
.label .rule{height:2px;width:56px;flex:none;
  background:linear-gradient(90deg,var(--orange-50) 0%,var(--yellow-61) 31%,var(--azure-42) 68%,var(--azure-40) 100%)}
.label__aside{margin-left:auto;font-size:var(--fs-label);letter-spacing:1.4px;text-transform:uppercase;color:var(--black-60);
  background:none;border:0;font-family:inherit;cursor:pointer;transition:color .2s var(--ease)}
summary:hover .label__aside{color:var(--black)}

/* the translucent "cards-shell" that sits behind card groups and single cards */
.shell{background:var(--clair);border:1px solid rgba(255,255,255,.5);
  border-radius:var(--r-shell);padding:16px;box-shadow:var(--glass-shadow)}

/* =================== HERO =================== */
.hero{padding:var(--hero-pad) var(--gutter)}
/* Viewport-relative hero height, take three (2026-08-21) -- mirrors the
   technique the Et voila marquee already uses successfully
   (.showcase-carousel__rows further down): give the element that already
   carries the padding a real, hard `height` (not min-height) equal to the
   full target. Because this element has box-sizing:border-box (see the
   reset near the top of this file), the padding is automatically carved
   OUT of that same declared height -- no separate "subtract the padding a
   second time" math needed on some inner element, the way the last two
   attempts required. That old approach's real flaw wasn't the arithmetic,
   it was relying on `min-height`, which lets a box grow past its target
   whenever its content needs more room (an image sized off its column's
   width, home hero padding needing to clear the nav, etc) -- and every
   hero-height bug this session traces back to content outgrowing that
   supposedly-fixed target. `height` is a hard ceiling: content now has to
   actually fit inside it. See case-hero__media's img rule further down --
   the image is now sized off the available HEIGHT the same way the
   marquee's images already are, instead of off the column's width, so it
   shrinks to fit instead of forcing this box to grow.
   Scoped to --case/--alt only, not the bare .hero the retired
   home-hero-previous.html still uses -- that page keeps its original,
   simpler min-height:710px sizing (see .hero__card below), untouched. */
/* The section itself is the viewport-height frame. Because the global reset
   uses border-box sizing, its 18px top and bottom padding are already included
   in this height; subtracting them here made the hero 36px too short and
   exposed the next section on load. Keep the vh fallback for older browsers
   and prefer dvh where it is available so mobile browser chrome is handled. */
.hero--case,.hero--alt{height:100vh;height:100dvh}
.hero__wrap{max-width:var(--max);margin:0 auto;position:relative;height:100%}
.hero__card{
  position:relative;border-radius:var(--r-shell);min-height:710px;
  padding:clamp(90px,11vw,140px) var(--pad-x) 64px;
  display:flex;align-items:center;justify-content:space-between;gap:48px;flex-wrap:wrap;
  background-image:var(--wash-down);
}
.hero__content{max-width:570px;display:flex;flex-direction:column;gap:12px;position:relative;z-index:2}
/* v5 fluid treatment, capped at 40px instead of 50 */
.hero__headline{font-size:clamp(32px,4.6vw,var(--fs-h1));font-weight:700;line-height:1.12;
  letter-spacing:-2px;max-width:520px}
.hero__headline .accent{display:block;transition:color .35s var(--ease),
  opacity .35s var(--ease),transform .35s var(--ease)}
.hero__headline .accent.is-out{opacity:0;transform:translateY(-10px)}
/* The live hero stacks complete titles, then animates each word separately.
   Keeping every phrase in the same grid cell prevents the line from jumping
   as titles of different lengths rotate through. */
[data-roles].role-rotator{display:grid !important;min-height:1.2em;align-items:center;
  transition:none}
.role-rotator__role{grid-area:1/1;display:inline-flex;gap:.24em;align-items:baseline;
  white-space:nowrap;pointer-events:none}
.role-rotator__word{display:inline-block;will-change:transform,opacity}
.hero__headline .role-rotator__role{justify-self:start;justify-content:flex-start}
.heroalt__title .role-rotator__role{justify-self:center;justify-content:center}
.hero__headline .rest{display:block;background:linear-gradient(90deg,#000,var(--grey-50));
  -webkit-background-clip:text;background-clip:text;color:transparent}
.hero__sub{font-size:var(--fs-body);line-height:24px;font-weight:300;color:var(--black-60);max-width:464px;margin-top:12px}
.hero__actions{display:flex;flex-direction:column;align-items:flex-start;margin-top:24px}
.hero__media{flex:0 1 548px;display:grid;place-items:center}
.hero__media img{width:100%;max-width:500px;aspect-ratio:500/311;object-fit:cover;
  border-radius:12px;box-shadow:var(--img-shadow);background:#eef0f3}

/* Fixed nav. Hides on scroll-down, returns on scroll-up (0.3s ease, 10px
   threshold, rAF-throttled, always shown at the very top of the page).
   It hugs its content, sits flush to the top and left of the screen, and its
   content starts at the page gutter so the logo lines up with everything below.
   Corners: 0 top-left / 0 top-right / 40 bottom-right / 0 bottom-left. No shadow. */
.navbar-shell{
  position:fixed;top:0;left:0;right:0;z-index:60;pointer-events:none;
  transition:transform .3s ease;
}
.navbar-shell.nav-hidden{transform:translateY(-110%)}
/* full-width white strip above the bar, matching the hero's top padding. It gives
   the top-right shoulder something to curve out of, and makes the bar look seated
   rather than clipped when it slides back in. */
.navbar-cap{position:relative;height:var(--hero-pad);background:var(--white)}
/* same centring as the slabs, so the right shoulder sits on the right gutter line */
.navbar-cap__row{position:relative;height:100%;margin:0 auto;
  max-width:calc(var(--max) + var(--gutter) * 2);padding:0 var(--gutter)}
/* same centring maths as .frame + .slabs, so the logo sits on the exact gutter
   line the sections below use — including past 1440px, where that line moves in */
.navbar-row{display:flex;max-width:calc(var(--max) + var(--gutter) * 2);
  margin:0 auto;padding-left:var(--gutter)}
.navbar{
  position:relative;pointer-events:auto;width:fit-content;height:80px;
  display:flex;align-items:center;gap:32px;padding-right:32px;
  background:var(--white);border-radius:0 0 var(--notch) 0;
}
/* white bleeds off the left edge of the screen, so the bar still reads as flush
   left while its content stays on the gutter line */
.navbar::before{content:"";position:absolute;top:0;bottom:0;right:100%;
  width:100vw;background:var(--white)}
/* concave shoulder where the bar's white sweeps away to the right. Built from a
   transparent radial gradient rather than a solid fill, so it works over the hero
   card at the top and over any section colour once the nav is floating. */
.nav-notch{position:absolute;width:var(--notch);height:var(--notch);pointer-events:none;
  background:radial-gradient(circle var(--notch) at 100% 100%,
    transparent 0 var(--notch), var(--white) calc(var(--notch) + .5px));
}
.nav-notch--tr{top:0;left:100%}
/* mirrored under the bar, sitting on the gutter line — that is where the nav's
   white actually meets the card below, so the curve is visible there */
.nav-notch--bl{top:100%;left:0}
/* and the mirror of that on the right, under the cap, so the white curves into the
   section instead of ending in a hard corner. Gradient is flipped horizontally. */
.nav-notch--cr{top:100%;right:var(--gutter);
  background:radial-gradient(circle var(--notch) at 0% 100%,
    transparent 0 var(--notch), var(--white) calc(var(--notch) + .5px))}
.brand{display:flex;align-items:center;gap:8px;font-size:24px;font-weight:600;letter-spacing:-.48px}
.brand__mark{width:45px;height:24px;flex:none;display:block}
.nav-toggle{
  display:none;appearance:none;width:44px;height:44px;padding:0;border:0;
  border-radius:50%;background:rgba(0,0,0,.055);color:var(--black);cursor:pointer;
  place-items:center;transition:background .2s var(--ease)
}
.nav-toggle:hover{background:rgba(0,0,0,.1)}
.nav-toggle:focus-visible{outline:2px solid var(--black);outline-offset:3px}
.nav-toggle__icon{position:relative;width:20px;height:14px;display:block}
.nav-toggle__line{
  position:absolute;left:0;width:20px;height:2px;border-radius:2px;
  background:currentColor;transform-origin:center;transition:transform .22s var(--ease),opacity .15s ease
}
.nav-toggle__line:nth-child(1){top:0}
.nav-toggle__line:nth-child(2){top:6px}
.nav-toggle__line:nth-child(3){top:12px}
.nav{display:flex;align-items:center;flex-wrap:wrap}
.nav a{font-size:var(--fs-label);font-weight:500;letter-spacing:1.4px;padding:8px 13px;border-radius:var(--r-pill);
  transition:opacity .2s var(--ease)}
.nav a:hover{opacity:.5}
.notch{position:absolute;width:var(--notch);height:var(--notch);background:var(--white);pointer-events:none;z-index:4}
/* contact-block notches: fill is the hero card colour at that corner with the 40%
   white veil already blended in, so the curve does not read as a different shade */
.notch i{display:block;width:100%;height:100%;background:#fbf8fc}

.hero__contact{
  position:absolute;right:0;bottom:0;z-index:5;height:66px;display:flex;align-items:center;gap:16px;
  padding:0 40px;background:var(--white);border-top-left-radius:var(--notch)
}
.hero__contact span{font-size:var(--fs-small);font-weight:300;color:var(--black-60)}
.icon-sq{width:24px;height:24px;display:grid;place-items:center;color:rgba(0,0,0,.75);
  transition:transform .1s linear,opacity .1s linear;opacity:.75}
.icon-sq:hover{opacity:1;transform:translateY(-2px)}
.icon-sq svg{width:18px;height:18px;display:block}
.notch--cr{right:0;bottom:66px}
.notch--cr i{border-bottom-right-radius:var(--notch)}
/* the matching curve on the left edge of the contact block */
.notch--cl{right:100%;bottom:0}
.notch--cl i{border-bottom-right-radius:var(--notch)}

@media (max-width:900px){
  /* stays fixed so it still hides on scroll-down and returns on scroll-up.
     --nav-h is measured in script.js and used to keep the hero clear of it. */
  .navbar-row{padding:0;max-width:none}
  .navbar{width:100%;height:auto;padding:12px var(--gutter);border-radius:0;
    flex-wrap:wrap;gap:12px;background:var(--white)}
  .navbar::before{display:none}
  /* the shoulders sit outside the bar; with a full-width bar that overflows */
  .nav-notch{display:none}
  .hero{padding-top:calc(var(--nav-h,116px) + var(--hero-pad))}
  .notch,.hero__contact{display:none}
  .hero__card{padding-top:48px;min-height:0;height:auto}
  .hero__headline{margin-top:0}
  /* The case-study hero remains exactly one viewport tall here too. Its
     stacked grid below gives the media whatever height remains after the
     text, allowing the image to shrink instead of lengthening the section. */
}

@media (max-width:700px){
  .navbar{min-height:68px}
  .nav-toggle{display:grid;margin-left:auto;flex:none}
  .nav{width:100%}
  .nav a{display:block;width:100%;padding:14px 0;border-radius:0;border-bottom:1px solid rgba(0,0,0,.09)}
  .nav a:last-child{border-bottom:0}

  /* With JavaScript available, the links become an overlay beneath the fixed
     bar. Without it they remain visible in the natural wrapped fallback. */
  .js .navbar{flex-wrap:nowrap}
  .js .nav{
    position:absolute;top:100%;left:0;right:0;display:grid;align-items:stretch;
    width:auto;padding:8px var(--gutter) 18px;background:var(--white);
    border-top:1px solid rgba(0,0,0,.07);box-shadow:0 14px 24px rgba(0,0,0,.08);
    opacity:0;visibility:hidden;pointer-events:none;transform:translateY(-8px);
    transition:opacity .18s var(--ease),transform .18s var(--ease),visibility 0s linear .18s
  }
  .js .navbar-shell.nav-open .nav{
    opacity:1;visibility:visible;pointer-events:auto;transform:translateY(0);
    transition-delay:0s
  }
  .navbar-shell.nav-open .nav-toggle__line:nth-child(1){transform:translateY(6px) rotate(45deg)}
  .navbar-shell.nav-open .nav-toggle__line:nth-child(2){opacity:0;transform:scaleX(.4)}
  .navbar-shell.nav-open .nav-toggle__line:nth-child(3){transform:translateY(-6px) rotate(-45deg)}
}

/* =================== SELECTED WORK =================== */
.grid-3{display:grid;grid-template-columns:repeat(3,minmax(0,1fr));gap:16px}
@media (max-width:980px){.grid-3{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:640px){.grid-3{grid-template-columns:1fr}}

.card{
  display:flex;flex-direction:column;gap:20px;height:100%;
  padding:24px 21.6px;border-radius:var(--r-card);background:var(--white);
  border:3px solid var(--white);box-shadow:var(--inner);
  transition:transform .3s var(--ease),box-shadow .3s var(--ease)
}
.card:hover{transform:translateY(-4px);box-shadow:var(--inner),0 16px 40px -20px rgba(0,0,0,.2)}
.card__media{border-radius:12px;overflow:hidden;box-shadow:var(--img-shadow);aspect-ratio:305.75/191.09;background:#eef0f3}
.card__media img{width:100%;height:100%;object-fit:cover;object-position:top center;transition:transform .6s var(--ease)}
.card:hover .card__media img{transform:scale(1.02)}
.card h3{font-size:var(--fs-title);font-weight:600;line-height:28px;margin-bottom:9px}
.card p{font-size:var(--fs-small);font-weight:300;line-height:20px;color:rgba(0,0,0,.9)}
.card__logo{margin-top:auto;align-self:flex-end;width:90px;max-height:90px;display:grid;place-items:end}
.card__logo img{width:90px;height:auto;max-height:90px;object-fit:contain;object-position:right bottom;opacity:.9}

/* =================== OLDER WORK =================== */
.grid-4{display:grid;grid-template-columns:repeat(4,minmax(0,1fr));gap:16px}
@media (max-width:1100px){.grid-4{grid-template-columns:repeat(3,minmax(0,1fr))}}
@media (max-width:820px){.grid-4{grid-template-columns:repeat(2,minmax(0,1fr))}}
@media (max-width:520px){.grid-4{grid-template-columns:1fr}}

.arch{
  display:flex;align-items:center;justify-content:space-between;gap:14px;height:100%;
  padding:11px 11px 11px 18px;border-radius:var(--r-arch);
  background:var(--white);border:2px solid rgba(255,255,255,.1);box-shadow:var(--inner);
  transition:transform .3s var(--ease)
}
.arch:hover{transform:translateY(-3px)}
.arch strong{display:block;font-size:var(--fs-body);font-weight:600;color:var(--black)}
.arch span{display:block;font-size:var(--fs-tiny);font-weight:300;color:var(--black-60);margin-top:2px}
.arch__thumb{width:74px;height:54px;flex:none;border-radius:12px;overflow:hidden;background:#eef0f3;
  box-shadow:0 8px 16px -10px rgba(20,28,50,.7)}
.arch__thumb img{width:100%;height:100%;object-fit:cover;transition:transform .3s var(--ease)}
.arch:hover .arch__thumb img{transform:scale(1.04)}
details.older>summary{list-style:none;cursor:pointer}
details.older>summary::-webkit-details-marker{display:none}
.older:not([open]) .older__intro,.older:not([open]) .grid-4{display:none}
.older__intro{color:var(--black-60);font-size:var(--fs-body);line-height:24px;font-weight:300;margin-bottom:28px;max-width:820px}

/* =================== ABOUT ME =================== */
/* heading fills, 40px gap, paragraph capped at 700px */
.aboutme__head{display:flex;gap:40px;align-items:flex-start;flex-wrap:wrap}
.aboutme__head .label{padding-bottom:0}
.aboutme__head .label{flex:1 1 auto}
.aboutme__head h3{flex:1 1 400px;max-width:700px;min-width:min(400px,100%);
  font-size:var(--fs-lead);font-weight:500;line-height:1.25}

/* employer marquee — 256x256 cards, each inside its own cards-shell */
.marquee{position:relative;overflow:hidden;padding:40px 0 140px;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent)}
.marquee__track{display:flex;gap:24px;width:max-content;animation:marquee 39s linear infinite}
@keyframes marquee{to{transform:translateX(-50%)}}
.emp-shell{flex:none;padding:16px;border-radius:var(--r-shell);
  background:var(--clair);border:1px solid rgba(255,255,255,.5);box-shadow:var(--glass-shadow)}
/* a different element to the cards: 5px white border at 10%, no inner shadow */
/* same treatment as .bio__frame: 5px white-10% border, 32px radius, sombra */
.emp{width:256px;height:256px;display:flex;flex-direction:column;align-items:center;
  padding:24px;border-radius:var(--r-card);background:var(--white);background-clip:padding-box;
  border:5px solid rgba(255,255,255,.1);box-shadow:var(--sombra);text-align:center}
/* fixed logo area, so the first role line sits at the same height in every box and a
   second line drops below it instead of pushing the first one up */
.emp__logo{flex:1;width:200px;display:grid;place-items:center}
.emp__logo img{width:200px;height:auto;max-height:100%;object-fit:contain}
.emp__roles{flex:none;height:54px;font-size:var(--fs-tiny);line-height:18px;color:var(--black-60)}

/* the wrapping .inner supplies the 140 above; this keeps the 140 below, which
   together with .aboutme__grid padding-top gives the gap before the pills */
/* the quote-section supplies the 280px above and below; the figure adds none */
.quote{text-align:center;padding:0}
.quote blockquote{font-size:var(--fs-quote);font-weight:700;letter-spacing:-.5px;line-height:1.2}
.quote figcaption{display:inline-flex;align-items:center;gap:12px;margin-top:28px;text-align:left}
.quote figcaption img{width:40px;height:40px;border-radius:50%;object-fit:cover}
.quote figcaption strong{display:block;font-size:var(--fs-body);font-weight:600}
.quote figcaption small{display:block;font-size:var(--fs-small);font-weight:300;color:var(--black-60)}

.aboutme__grid{display:grid;grid-template-columns:1fr 1fr;gap:clamp(32px,5vw,64px);
  align-items:center;padding-top:140px}
@media (max-width:900px){.aboutme__grid{grid-template-columns:1fr}}
.aboutme__copy h3{font-size:var(--fs-lead);font-weight:700;line-height:1.25;margin:20px 0 18px;letter-spacing:-.5px}
.aboutme__copy > p{color:var(--black-60);font-size:var(--fs-body);font-weight:300;line-height:24px;margin-bottom:32px;max-width:44ch}

/* trust pills — shell rotates, white pill inside stays level; hover un-rotates */
.trusts{display:flex;flex-direction:column;gap:20px}
.trust-shell{background:var(--clair);border:1px solid rgba(255,255,255,.5);border-radius:var(--r-shell);
  padding:8px;box-shadow:0 32px 40px -20px rgba(0,0,0,.2);transform:rotate(0);transition:transform .35s var(--ease)}
.trust-shell:nth-child(odd){transform:rotate(-1deg)}
.trust-shell:nth-child(even){transform:rotate(2deg)}
.trust-shell:hover{transform:rotate(0)}
.trust{display:flex;align-items:center;gap:24px;background:var(--white);
  border:2px solid rgba(255,255,255,.1);border-radius:var(--r-card);padding:16px 24px;
  font-size:var(--fs-small);line-height:28px;font-weight:300;color:var(--black-60);
  box-shadow:var(--inner),0 8px 24px rgba(0,0,0,.1)}
.trust strong{color:var(--black);font-weight:600}
.trust svg{width:32px;height:32px;flex:none;stroke:var(--black-35);fill:none;stroke-width:1.5}
.trust__content{min-width:0}
.trust__content > ul{display:flex;flex-direction:column;gap:6px;margin-top:12px;padding-left:22px;list-style:disc}
.trust__content > ul > li{padding-left:2px}

/* =================== SERVICES =================== */
/* Same visual treatment as the employer logo cards: 5px white border at 10%,
   the layered --sombra shadow. Deliberately different from the case study cards. */
.svc{display:flex;flex-direction:column;gap:16px;height:100%;padding:20px 20px 24px;
  border-radius:var(--r-card);background:var(--white);background-clip:padding-box;
  border:5px solid rgba(255,255,255,.1);box-shadow:var(--sombra);
  transition:transform .3s var(--ease),box-shadow .3s var(--ease)}
.svc:hover{transform:translateY(-4px);box-shadow:var(--sombra),0 16px 40px -20px rgba(0,0,0,.2)}
.svc__stack{position:relative;height:clamp(150px,16vw,205px);margin-bottom:4px}
.svc__stack img{position:absolute;border-radius:12px;object-fit:cover;box-shadow:var(--sombra);background:#eef0f3;
  transition:transform .6s var(--ease)}
.svc__stack img:nth-of-type(1){left:0;top:9%;width:62%;height:74%;transform:rotate(-2deg)}
.svc__stack img:nth-of-type(2){right:1%;top:0;width:56%;height:90%;transform:rotate(1.6deg)}
.svc:hover .svc__stack img:nth-of-type(1){transform:rotate(-2deg) scale(1.02)}
.svc:hover .svc__stack img:nth-of-type(2){transform:rotate(1.6deg) scale(1.02)}
.svc h3{font-size:var(--fs-title);font-weight:700;line-height:30px}
.svc p{font-size:var(--fs-small);font-weight:300;line-height:20px;color:var(--black-60)}
/* pill — reference: #ebebeb, 10px radius, 13px, 5px 10px */
.svc__tags{display:flex;flex-wrap:wrap;gap:8px;padding-top:8px}
.svc__tags span{display:inline-flex;align-items:center;gap:6px;font-size:var(--fs-tiny);font-weight:400;
  color:var(--black-55);background:#ebebeb;border-radius:10px;padding:5px 10px}
.svc__tags svg{width:14px;height:14px;stroke:var(--black-35);stroke-width:1.6;fill:none}

/* =================== BIO =================== */
.bio{display:flex;gap:76px;align-items:flex-start;flex-wrap:wrap}
.bio__shell{flex:0 1 auto;max-width:100%;padding:16px;border-radius:var(--r-shell);
  background:var(--clair);border:1px solid rgba(255,255,255,.5);filter:drop-shadow(0 8px 20px rgba(0,0,0,.05))}
.bio__frame{border:5px solid rgba(255,255,255,.1);border-radius:var(--r-card);overflow:hidden;
  box-shadow:var(--sombra);width:425px;max-width:100%;aspect-ratio:425/497;background:#eef0f3}
.bio__frame img{width:100%;height:100%;object-fit:cover}
.bio__body{flex:1 1 420px;min-width:0;display:flex;flex-direction:column;gap:23.7px;padding:20px 20px 20px 0}
.bio__body .bio__hi{font-size:32px;font-weight:600;line-height:normal;color:var(--black)}
.bio__body h3{font-size:var(--fs-body);font-weight:600;line-height:24px}
.bio__body p{font-size:var(--fs-body);font-weight:400;line-height:24px;color:var(--black-60)}
@media (max-width:900px){.bio{gap:32px}.bio__body{padding:0}}

/* =================== CTA =================== */
/* Same marquee-inspired technique as the hero sections above: give the
   padded outer element a hard height (border-box carves its own padding
   out automatically), then have the inner element fill 100% of that --
   no hand-maintained hero-pad*4 math. */
.cta-outer{padding:var(--hero-pad) var(--gutter);height:calc(100vh - (var(--hero-pad) * 2))}
.cta{position:relative;max-width:var(--max);margin:0 auto;border-radius:var(--r-shell);overflow:hidden;
  background:radial-gradient(120% 90% at 50% 40%,#3a3a3a,#242424 70%);color:#fff;
  height:100%;
  display:grid;place-items:center;padding:clamp(56px,8vw,96px) 24px;text-align:center}
.cta__inner{position:relative;z-index:2;display:flex;flex-direction:column;align-items:center;
  gap:0;width:min(820px,92%)}
.cta__inner .trustline{justify-content:center}
.cta h2{margin-top:40px}
.cta .status{margin:32px 0 0}
.cta .btn{margin-top:64px}
.cta__email{margin-top:32px}
.cta h2{font-size:var(--fs-display);font-weight:600;letter-spacing:-2px;line-height:84px}
.cta .trustline__label{color:rgba(255,255,255,.75)}
.cta .btn{background-color:#1c1c1c;border:1px solid rgba(255,255,255,.25);outline:3px solid rgba(255,255,255,.16)}
.cta__email{font-size:var(--fs-small);font-weight:300;color:rgba(255,255,255,.65)}
/* mouse-tracked parallax, same idea as the reference's cta-canvas */
.cta__canvas{position:absolute;inset:0;z-index:1;transition:transform .5s ease}
.cta__group{position:absolute;inset:0;transition:transform .5s ease;transform:var(--drift,translate(0,0))}
.cta__float{position:absolute;aspect-ratio:16/11;background:#333;
  border-radius:12px;overflow:hidden;box-shadow:0 20px 40px -18px rgba(0,0,0,.9);
  --rot:0deg;transform:rotate(var(--rot))}
.cta__float img{width:100%;height:100%;object-fit:cover}
.cta__float.l1{left:3%;top:17%;width:12%;--rot:-8deg}
.cta__float.l2{left:-2%;bottom:13%;width:10.5%;aspect-ratio:16/12;--rot:6deg}
.cta__float.l3{left:8%;bottom:40%;width:11%;--rot:-4deg}
.cta__float.r1{right:4%;top:24%;width:13%;--rot:8deg}
.cta__float.r2{right:13%;bottom:5%;width:12%;--rot:-6deg}
.cta__float.r3{right:2%;bottom:36%;width:10.5%;--rot:4deg}
@media (max-width:900px){.cta__canvas{display:none}.cta-outer{height:auto}.cta{height:auto}
  .cta h2{margin-top:24px}.cta .btn{margin-top:40px}}

/* =================== FOOTER =================== */
.foot{display:flex;align-items:flex-end;justify-content:space-between;gap:24px;flex-wrap:wrap;
  max-width:var(--max);margin:0 auto;padding:20px var(--gutter) 36px}
.foot__mark{display:flex;font-size:var(--fs-wordmark);font-weight:700;letter-spacing:-4px;
  line-height:.85;overflow:hidden;padding-bottom:.06em}
/* each letter rises in on its own beat; the N then flips a second later */
.foot__ch{display:inline-block;transform:translateY(105%);opacity:0;
  transition:transform .6s var(--ease),opacity .6s var(--ease);
  transition-delay:calc(var(--i) * 70ms)}
.foot__mark.in .foot__ch{transform:translateY(0);opacity:1}
.foot__mark.flipped .foot__n{transform:scaleX(-1);transition-delay:0s}
@media (prefers-reduced-motion:reduce){
  .foot__ch{transform:none;opacity:1;transition:none}
  .foot__mark.flipped .foot__n{transform:scaleX(-1)}
}
.foot__meta{display:flex;align-items:center;gap:12px;font-size:var(--fs-label);font-weight:300;color:var(--black-60);padding-bottom:12px}
.foot__meta .icon-sq{width:18px;height:18px}
.foot__meta .icon-sq svg{width:18px;height:18px}
.foot__meta a:hover{text-decoration:underline}
.foot .icon-sq{color:rgba(0,0,0,.6)}

/* awaiting Darian's input */
.needs{outline:2px dashed var(--orange-50);outline-offset:3px}
.needs-note{display:block;margin-top:8px;font-size:12px;line-height:16px;color:var(--orange-50)}

/* section padding steps down only when 140px no longer fits */
@media (max-width:1100px){ :root{--pad-x:72px;--pad-x-step:72px} .inner{padding:96px var(--pad-x)} }
@media (max-width:700px){ :root{--pad-x:24px;--pad-x-step:24px} .inner{padding:64px var(--pad-x)} .marquee{padding:24px 0 72px} }

/* the type scale only steps down on small screens; every value stays a whole pixel */
@media (max-width:700px){
  :root{--fs-section:28px;--fs-quote:26px;--fs-lead:24px;--fs-title:20px;
        --fs-display:40px;--fs-wordmark:56px}
  .hero__headline{line-height:36px}
  .cta h2{line-height:44px}
}



/* ============================================================
   CASE STUDY
   Every number below is measured from the Figma "Case Study" PDF export
   (1440pt board), not inferred. Reuses the home page primitives: .frame /
   .slabs alternate the wash, .inner holds the 140px rhythm, .rise reveals.
   ============================================================ */

.section{position:relative}

/* ---- case hero ----
   Same rule as the home hero and the closing block: exactly one screen minus
   the page padding, so it fills the viewport on load without forcing a
   scroll to see its own bottom edge. Was a fixed 690px, which is what the
   design spec's "Hero height" row already called out as wrong.
   Viewport height, take three (2026-08-21): the outer `.hero--case` section
   now carries a hard `height:calc(100vh - hero-pad*2)` itself (see the HERO
   block near the top of this file), and thanks to box-sizing:border-box its
   own padding is already carved out of that number automatically. So this
   card just needs to fill 100% of whatever space its parent (.hero__wrap,
   itself 100% of the section) hands it -- no separate hero-pad*4 arithmetic
   to hand-maintain. The two earlier attempts (a plain 100vh-hero-pad*2, then
   the *4 "fix") both used min-height, which let the card grow taller than
   its target whenever the image inside needed more room -- confirmed with
   Playwright measurements and a red-line overflow screenshot. `height` is a
   hard ceiling instead: the image is now sized off this card's available
   HEIGHT (see .case-hero__media below), so it shrinks to fit rather than
   forcing the card past its target. */
.hero--case .hero__card{height:100%;min-height:0;align-items:center;
  padding-left:var(--pad-x-step);padding-right:var(--pad-x-step)}
/* Measured from the export: the glass panel is 594px and sits flush to the right
   content edge, leaving 498px for the text. A grid, not flex — flex-wrap was
   dropping the image below the text because the two basis values plus the gap
   exceeded the 1116px content width. */
/* Proportional tracks, not a fixed 594px: at 1440 the 498/594 split lands
   exactly on the measured values, and below that the ratio holds instead of
   the text column collapsing. */
.case-hero{display:grid;grid-template-columns:minmax(0,440px) minmax(0,1fr);
  gap:24px;align-items:center;width:100%;height:100%}
.case-hero__text{min-width:0;display:flex;flex-direction:column;gap:24px}
/* surtitle + gradient rule. Shared by the hero and every step. */
.case-hero__label{display:flex;align-items:center;gap:16px}
.case-hero__label span:first-child{font-size:var(--fs-case-surtitle);font-weight:500;
  letter-spacing:1.6px;text-transform:uppercase;color:var(--black-60);white-space:nowrap}
.case-hero__label .rule{height:2px;width:56px;flex:none;border-radius:2px;
  background:linear-gradient(90deg,var(--orange-50) 0%,var(--yellow-61) 31%,
    var(--azure-42) 68%,var(--azure-40) 100%)}
.case-hero__title{font-size:clamp(32px,4.6vw,var(--fs-case-h1));font-weight:700;
  line-height:1.22;letter-spacing:-1.5px;
  /* same black -> grey fade as the home page headline */
  background:linear-gradient(90deg,var(--black),var(--grey-50));
  -webkit-background-clip:text;background-clip:text;color:transparent}
.case-hero__lead{font-size:var(--fs-case-body);line-height:24px;font-weight:300;
  color:var(--black-60);max-width:440px}
/* Below 600px high, reduce the text continuously so its intrinsic height does
   not become the grid row's minimum and prevent the neighbouring image from
   continuing to shrink with the viewport. Values meet the normal scale at
   exactly 600px, so there is no breakpoint jump. */
@media (min-width:901px) and (max-height:600px){
  .case-hero__text{gap:clamp(12px,4dvh,24px)}
  .case-hero__title{font-size:clamp(26px,6.67dvh,40px)}
  .case-hero__lead{font-size:clamp(13px,2.67dvh,16px);line-height:1.5}
}
/* Same technique the Et voila marquee already uses for its images
   (.showcase-carousel__item img further down): size the image off the
   available HEIGHT, not its own natural width, so it shrinks to fit a
   shorter window instead of forcing the card (and section) to grow past
   their target height. min-width keeps it from shrinking into nothing on a
   very short-but-wide window; below the 900px stacking breakpoint this all
   reverts to plain full-width sizing (see that release below). */
/* min-height:0 at every link below is required, not decorative: both grid
   items (.case-hero__media, a column of .case-hero) and flex items
   (the img, inside .step__shell's column flex) get an invisible "automatic
   minimum size" from the spec that's based on their content's own intrinsic
   size -- for a replaced element like an img with an aspect-ratio, that
   floors it at roughly its natural size and silently overrides `height:100%`
   once the container gets shorter than that floor. Confirmed with Playwright:
   without these, the image stopped shrinking below ~500px tall and started
   overflowing its card at short window heights. */
.case-hero__media{min-width:0;min-height:0;height:100%;display:grid;place-items:center;
  container-type:size;container-name:case-hero-media}
/* The normal step shell deliberately fills its column, but that made the hero
   frame much wider than its height-limited image. In the hero only, calculate
   the shell from the available width AND height. The 3/2 image then owns the
   shell's size, with exactly 16px visible on every edge. */
.case-hero__media .step__shell{width:min(100cqw,calc((100cqh - 32px) * 1.5 + 32px));
  height:auto;max-width:100%;max-height:100%;min-height:0;padding:16px;align-items:center}
.case-hero__media .step__shell img{width:100%;height:auto;max-width:none;min-width:0;min-height:0}

/* ---- steps ----
   Measured split: a narrow 345px reading column beside a wide media stage.
   Text and media are TOP aligned, not centred. */
.step__grid{display:grid;grid-template-columns:var(--case-col) minmax(0,1fr);
  gap:var(--case-gap);align-items:start}
/* text-right mirrors the layout: the track sizes flip too, so the text column
   stays 345px on both sides. DOM order stays text-then-media, so reading order
   and tab order are unchanged. */
.step--right .step__grid{grid-template-columns:minmax(0,1fr) var(--case-col)}
.step--right .step__text{order:2}
.step--right .step__media{order:1}
/* The reading column sticks while the taller media column scrolls past it, as
   on the live site. Clears the fixed nav using the height script.js publishes.
   Sticky only engages when the media side is taller; otherwise it is inert. */
.step__text{display:flex;flex-direction:column;gap:24px;min-width:0;
  position:sticky;top:calc(var(--nav-h,116px) + 40px);align-self:start}
.step__title{font-size:var(--fs-case-title);font-weight:700;line-height:39px;
  letter-spacing:-.5px}

/* The media stage: the same translucent cards-shell used on the home page,
   20px padding, holding one or more stacked screenshots. */
.step__media{min-width:0}
.step__shell{background:var(--clair);border:1px solid rgba(255,255,255,.5);
  border-radius:var(--r-shell);padding:20px;display:flex;flex-direction:column;gap:16px;
  box-shadow:0 32px 40px -20px rgba(0,0,0,.2)}
.step__shell img{width:100%;aspect-ratio:3/2;object-fit:cover;border-radius:16px;
  background:#eef0f3;box-shadow:var(--img-shadow);transition:transform .5s var(--ease)}
.step__shell img:hover{transform:scale(1.015)}

/* ePACT's Context media is a working product video rather than a screenshot.
   It uses the complete media column, matching the live site's 16:9 embed. */
.step__shell--video{padding:0;overflow:hidden}
.step__video{display:block;width:100%;aspect-ratio:16/9;height:auto;border:0}
.step__shell--mixed{padding:20px;gap:16px}
.step__shell--mixed .step__video{border-radius:16px}

/* Horizontal screenshot reel used by the STAT Legacy context section.
   Two identical groups make the -50% translation seamless. */
.step__shell--marquee{display:block;overflow:hidden;padding-left:1px;padding-right:1px}
.step-marquee{height:clamp(480px,65vh,720px);overflow:hidden;border-radius:16px;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 5%,#000 95%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 5%,#000 95%,transparent)}
.step-marquee__track{display:flex;width:max-content;height:100%;animation:step-marquee 52s linear infinite}
.step-marquee__group{display:flex;gap:16px;height:100%;padding-right:16px}
.step-marquee img{flex:none;width:auto;height:100%;aspect-ratio:auto;object-fit:contain;border-radius:12px}
@keyframes step-marquee{to{transform:translateX(-50%)}}
@media (prefers-reduced-motion:reduce){.step-marquee__track{animation:none}}
@media (max-width:900px){
  .step-marquee{height:clamp(300px,50vh,420px)}
}

/* Body copy. Every selector is scoped to a direct child of .step__body so no
   rule here can reach into a nested component. */
.step__body > p{font-size:var(--fs-case-body);line-height:24px;font-weight:300;
  color:var(--black-60)}
.step__body > p + p,
.step__body > ul + p,
.step__body > ol + p,
.step__body > p + ul,
.step__body > p + ol{margin-top:24px}
.step__body > p.step__subhead + p{margin-top:4px}
.step__body > p > strong,
.step__body > ul > li > strong,
.step__body > ol > li > strong{color:var(--black);font-weight:600}
.step__body > ul{display:flex;flex-direction:column;gap:8px}
.step__body > ul > li{position:relative;padding-left:22px;font-size:var(--fs-case-body);
  line-height:24px;font-weight:300;color:var(--black-60)}
.step__body > ul > li::before{content:"";position:absolute;left:4px;top:10px;
  width:5px;height:5px;border-radius:50%;background:var(--black-35)}
/* .35 alpha measured at 2.43:1 against the wash, failing AA. --black-60
   is the body copy colour and measures 5.63:1 at this size. */
/* Numbered lists (Step 16) -- there was no rule for <ol> at all before this,
   so it fell back to the browser default (serif-ish spacing, tight
   line-height, no colour), which is why it looked broken next to the <ul>
   steps. Reuses the same font/colour/rhythm as the bullet list; the numbers
   are the browser's own ::marker rather than a custom dot, positioned the
   same way so wrapped lines still align under the text, not the number. */
.step__body > ol{display:flex;flex-direction:column;gap:8px;padding-left:20px}
.step__body > ol > li{padding-left:2px;font-size:var(--fs-case-body);
  line-height:24px;font-weight:300;color:var(--black-60)}
.step__body > ol > li::marker{color:var(--black-35)}
.step__body > p.step__note{font-size:var(--fs-tiny);line-height:20px;color:var(--black-60)}

/* Inline links inside case-study prose (body copy, fact-panel items,
   interludes, asides). Colour only -- text-decoration is already off
   globally (see the base `a` reset) -- applied to the shared component
   classes so every future case study gets this automatically without
   per-page CSS. Same blue/hover pair as the step CTA link below. */
.step__body a,.fact-card a,.interlude__line a,.aside__line a{color:var(--azure-40)}
.step__body a:hover,.fact-card a:hover,.interlude__line a:hover,.aside__line a:hover{
  color:var(--azure-42)}

/* Native, accessible evidence disclosure used by research-heavy case studies. */
.step-accordion{margin-top:24px;border-top:1px solid rgba(0,0,0,.14);
  border-bottom:1px solid rgba(0,0,0,.14)}
.step-accordion summary{position:relative;cursor:pointer;padding:18px 36px 18px 0;
  color:var(--black);font-weight:600;line-height:24px;list-style:none}
.step-accordion summary::-webkit-details-marker{display:none}
.step-accordion summary::before,.step-accordion summary::after{content:"";position:absolute;
  top:29px;right:4px;width:16px;height:2px;border-radius:2px;background:currentColor;
  transition:transform .2s var(--ease)}
.step-accordion summary::after{transform:rotate(90deg)}
.step-accordion[open] summary::after{transform:rotate(0)}
.step-accordion ol{margin:0 0 20px;padding-left:22px;list-style:decimal}
.step-accordion li{padding-left:4px;margin-top:8px}

.step__link{display:inline-flex;align-items:center;gap:10px;align-self:flex-start;
  font-size:var(--fs-small);font-weight:500;color:var(--azure-40);
  transition:color .2s var(--ease),transform .2s var(--ease)}
.step__link:hover{color:var(--azure-42);transform:translateY(-1px)}
.step__link-icon{width:18px;height:18px;flex:none;transition:transform .2s var(--ease)}
.step__link:hover .step__link-icon{transform:translateX(3px)}

/* ---- facts grid ----
   Measured: two 542px cards, 32px gutter, pills inset 20px inside each card. */
.facts__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:32px;
  align-items:start}
/* Optional beat heading (cs.factsHeading) that sits beside the first panel --
   confirmed against the live site's own strategy section: it's a plain flow
   item, not a fixed pairing, so it just lands in the grid's second cell.
   Size measured from Webflow's TextBlock-Strategy-Large style: 4rem/120%. */
.facts__heading{align-self:start;font-size:clamp(32px,4.5vw,64px);line-height:1.2;
  font-weight:700;color:var(--black);letter-spacing:-1px}
.fact-card{background:#fefbf7;border:1px solid rgba(255,255,255,.6);
  border-radius:var(--r-shell);padding:25px 20px;display:flex;flex-direction:column;gap:20px;
  box-shadow:0 32px 40px -28px rgba(0,0,0,.18)}
.label--sm{padding-bottom:0}
.label--sm h3{font-size:var(--fs-case-fact);font-weight:500;letter-spacing:1.6px;
  text-transform:uppercase;color:var(--black-60)}
.fact-card__intro{font-size:var(--fs-case-fact);line-height:24px;font-weight:600;color:var(--black)}
.fact-card__list{display:flex;flex-direction:column;gap:16px}

/* The fact pills ARE the home page's trust pills — same trustItem() component,
   same shell, border, opacity, drop shadow, inset shadow, icon size, rotation
   and hover un-rotate. The ONLY difference is the fill, which comes from the
   panel's data-tone. Tints sampled pixel-exact from the Figma board; Figma uses
   a tint only, no coloured bar. Live-site bar values if ever wanted back:
   problem #b72619 / goal #4fab31 / success #e9c642 / constraint grey. */
.fact-card[data-tone] .trust{background:var(--fact-bg)}
/* The panel is already a glass card, so the pill's own glass shell would be a
   second layer of the same thing. Strip it back to a transparent carrier and
   keep only what it is still doing: the rotation and the hover un-rotate. */
.fact-card__list .trust-shell{background:none;border:0;box-shadow:none;padding:0}

/* Tilt stays inside -2 to 2 and sits well under it, with a flat 0 in the cycle
   so not every pill leans. The same five values run in each panel but start at a
   different point per tone, so no two columns lean the same way down the grid. */
[data-tone="problem"] .fact-card__list .trust-shell:nth-child(5n+1){transform:rotate(-1.5deg)}
[data-tone="problem"] .fact-card__list .trust-shell:nth-child(5n+2){transform:rotate(0.8deg)}
[data-tone="problem"] .fact-card__list .trust-shell:nth-child(5n+3){transform:rotate(0deg)}
[data-tone="problem"] .fact-card__list .trust-shell:nth-child(5n+4){transform:rotate(1.4deg)}
[data-tone="problem"] .fact-card__list .trust-shell:nth-child(5n+5){transform:rotate(-0.7deg)}
[data-tone="goal"] .fact-card__list .trust-shell:nth-child(5n+1){transform:rotate(0deg)}
[data-tone="goal"] .fact-card__list .trust-shell:nth-child(5n+2){transform:rotate(1.4deg)}
[data-tone="goal"] .fact-card__list .trust-shell:nth-child(5n+3){transform:rotate(-0.7deg)}
[data-tone="goal"] .fact-card__list .trust-shell:nth-child(5n+4){transform:rotate(-1.5deg)}
[data-tone="goal"] .fact-card__list .trust-shell:nth-child(5n+5){transform:rotate(0.8deg)}
[data-tone="constraint"] .fact-card__list .trust-shell:nth-child(5n+1){transform:rotate(1.4deg)}
[data-tone="constraint"] .fact-card__list .trust-shell:nth-child(5n+2){transform:rotate(-0.7deg)}
[data-tone="constraint"] .fact-card__list .trust-shell:nth-child(5n+3){transform:rotate(-1.5deg)}
[data-tone="constraint"] .fact-card__list .trust-shell:nth-child(5n+4){transform:rotate(0.8deg)}
[data-tone="constraint"] .fact-card__list .trust-shell:nth-child(5n+5){transform:rotate(0deg)}
[data-tone="success"] .fact-card__list .trust-shell:nth-child(5n+1){transform:rotate(0.8deg)}
[data-tone="success"] .fact-card__list .trust-shell:nth-child(5n+2){transform:rotate(0deg)}
[data-tone="success"] .fact-card__list .trust-shell:nth-child(5n+3){transform:rotate(1.4deg)}
[data-tone="success"] .fact-card__list .trust-shell:nth-child(5n+4){transform:rotate(-0.7deg)}
[data-tone="success"] .fact-card__list .trust-shell:nth-child(5n+5){transform:rotate(-1.5deg)}
/* same specificity as the angle rules, declared later, so hover wins */
[data-tone] .fact-card__list .trust-shell:hover{transform:rotate(0)}

[data-tone="problem"]    {--fact-bg:#fcefef}
[data-tone="goal"]       {--fact-bg:#f5fff0}
[data-tone="constraint"] {--fact-bg:#ececec}
[data-tone="success"]    {--fact-bg:#fef9e2}

/* ---- narrative interlude and asides ----
   48px regular, 58px line-height, full 1116 content width. The one-line asides
   use the same component on the live site, so they share the treatment. */
.interlude .inner,
.aside .inner{display:flex;flex-direction:column;gap:40px}
.interlude__line,
.aside__line{font-size:clamp(28px,3.4vw,var(--fs-case-beat));font-weight:500;
  line-height:1.21;letter-spacing:-1px;color:var(--black);max-width:1116px}
.aside .inner{padding-top:100px;padding-bottom:100px}

/* ---- responsive ---- */
@media (max-width:1200px){
  :root{--case-col:300px;--case-gap:36px}
}
@media (max-width:1100px){
  .facts__grid{gap:24px}
}
@media (max-width:900px){
  /* Both selectors are needed: media queries add no specificity, so the plain
     .step__grid (0,1,0) loses to .step--right .step__grid (0,2,0) and the
     right-side steps would keep their second fixed column and overflow. */
  .step__grid,
  .step--right .step__grid{grid-template-columns:1fr;gap:40px}
  /* stacked layout: nothing to scroll past, so release the sticky column */
  .step__text{position:static}
  /* text above media at every step, whichever side it was on */
  .step--right .step__text{order:0}
  .step--right .step__media{order:0}
  .facts__grid{grid-template-columns:1fr}
  /* Release viewport-driven sizing at the exact breakpoint where the hero
     stacks. From here down it follows the same natural text-then-media flow as
     the case-study steps, allowing the image to use the full column width. */
  .hero--case{height:auto;min-height:0}
  .hero--case .hero__wrap{height:auto}
  .hero--case .hero__card{height:auto;min-height:0}
  .case-hero{grid-template-columns:1fr;grid-template-rows:auto auto;
    gap:40px;height:auto;min-height:0;align-items:start}
  .case-hero__media{height:auto;display:block;container-type:normal}
  .case-hero__media .step__shell{width:100%;height:auto;max-width:100%;max-height:none}
  .case-hero__media .step__shell img{width:100%;height:auto;max-width:none}
}
@media (max-width:700px){
  /* four nested paddings (inner + card + pill + icon gutter) left the fact text
     only 126px at 390px wide, so they come down together */
  .fact-card{padding:16px;border-radius:var(--r-card);gap:16px}
  .step__shell{padding:12px;border-radius:var(--r-card)}
  .step__shell img{border-radius:12px;gap:12px}
  .aside .inner{padding-top:64px;padding-bottom:64px}
  .interlude .inner,.aside .inner{gap:28px}
  /* the surtitle was forced to one line (case-hero__label span:first-child,
     white-space:nowrap) so the gradient rule always sits right after the
     text. Short surtitles ("Moz 2.0") never hit the edge, but a longer one
     ("My AI Finance Consolidator") pushed the rule past the viewport at
     375px and 320px -- found via Playwright overflow check, not reported.
     Letting it wrap on narrow screens keeps the rule on-screen either way. */
  .case-hero__label{flex-wrap:wrap;row-gap:8px}
  .case-hero__label span:first-child{white-space:normal}
}

/* ---- "Et voila!" showcase ----
   Heading is the interlude beat; every image uses the step glass shell. The
   first item runs full width, the rest pair up, so a three-image set reads as
   one hero shot plus a supporting pair. */
.showcase .inner{display:flex;flex-direction:column;gap:48px}
.showcase__heading{max-width:1116px}
.showcase__lead{font-size:var(--fs-case-body);line-height:24px;font-weight:300;
  color:var(--black-60);max-width:640px;margin-top:-24px}
.showcase__grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:32px}
.showcase__item{margin:0;min-width:0;display:flex;flex-direction:column;gap:16px}
.showcase__item--wide{grid-column:1 / -1}
.showcase__item figcaption{font-size:var(--fs-small);line-height:20px;font-weight:300;
  color:var(--black-60);padding-left:4px}
/* The lead image is shown whole. Everything else in the system crops to a fixed
   ratio, but a showcase image is the artefact itself, so nothing is cut off. */
.showcase__item--wide .step__shell img{aspect-ratio:auto;height:auto;object-fit:contain}
@media (max-width:900px){
  .showcase .inner{gap:32px}
  .showcase__grid{grid-template-columns:1fr;gap:24px}
  .showcase__item--wide .step__shell img{aspect-ratio:auto}
}

/* Step / context / outcome sections sit at 70px so the media column gets the
   extra 140px. The facts grid, interlude, asides, "Et voila!", hero and closing
   block keep the 140px rhythm. Below 1100px --pad-x-step tracks --pad-x, so the
   responsive scale is unaffected. */
.step > .inner{padding-left:var(--pad-x-step);padding-right:var(--pad-x-step)}

/* ---- "Et voila!" preview + full-size link ----
   A capped preview keeps the page short and avoids shipping a multi-megabyte
   hi-res image on load; the link opens the full thing for anyone who wants it. */
.showcase__item--preview .step__shell{position:relative}
.showcase__item--preview .step__shell img{max-height:620px;object-fit:cover;
  object-position:top center}
/* soft fade so the crop reads as deliberate rather than cut off */
.showcase__item--preview .step__shell::after{content:"";position:absolute;
  left:20px;right:20px;bottom:20px;height:120px;border-radius:0 0 16px 16px;
  pointer-events:none;background:linear-gradient(to bottom,rgba(255,255,255,0),rgba(255,255,255,.92))}
/* 40px below the image, and the link is centred on the page */
.showcase__actions{display:flex;align-items:center;justify-content:center;
  gap:24px;flex-wrap:wrap;margin-top:24px}
.showcase__actions .step__link{align-self:center}
@media (max-width:700px){
  .showcase__item--preview .step__shell img{max-height:420px}
  .showcase__item--preview .step__shell::after{left:12px;right:12px;bottom:12px;height:80px}
}

/* ---- "Et voila!" multi-row marquee (the other shape this section takes) ----
   Same infinite-scroll mechanic as the employer logo row (.marquee /
   .marquee__track / @keyframes marquee, all reused as-is so this respects the
   existing prefers-reduced-motion override for free).
   Matches the live site: full-bleed, no glass panel. The rows sit OUTSIDE
   .inner (same pattern as aboutMe()'s employer marquee) so they run edge to
   edge instead of sitting inside the standard 140px side padding -- a glass
   background behind the whole block was tried first and removed 2026-08-20,
   it didn't read well against the section wash. Sized to fill the screen
   height -- same convention as the hero and closing block -- so all rows read
   as one full-screen showcase. Multi-row showcases deliberately remove the
   base marquee's side fade. Its padding is included inside the fixed viewport
   height via border-box sizing. Forced at every width, including mobile. */
.showcase-carousel{padding-bottom:0}
/* The heading is the only content inside .inner on this variant. Its bottom
   padding is therefore the complete heading-to-marquee gap. */
.showcase-carousel .inner{padding-bottom:48px}
.showcase-carousel__rows{display:flex;flex-direction:column;gap:4px;
  width:100%;height:100vh;height:100dvh;margin-top:0;
  padding:16px;background:#d6d0da}
.showcase-carousel .marquee{padding:0;flex:1;min-height:0;
  -webkit-mask-image:none;mask-image:none}
.showcase-carousel .marquee__track{gap:4px;height:100%;animation-duration:78s}
.showcase-carousel .marquee__track--reverse{animation-direction:reverse}
/* Each item sizes off the row's own height (not a fixed px width) so the
   images naturally fill whatever vertical space the rows have, at their
   own aspect ratio -- the marquee track's width is just the sum of that,
   which .marquee's own overflow:hidden already clips cleanly either way. */
.showcase-carousel__item{flex:none;height:100%}
.showcase-carousel__item img{height:100%;width:auto;display:block;
  border-radius:0;background:#eef0f3;
  box-shadow:0 2px 4px rgba(0,0,0,.08),0 12px 24px -12px rgba(0,0,0,.35)}

/* ============================================================
   ALTERNATE HERO (header_alt01_circle / header_alt02_mouse)
   One centred text stack, two image behaviours. Geometry measured from
   Darian's PDF exports: 40px eyebrow, 64px title, 16/24 subline, and images
   placed by angle + radius from the centre of the card.
   ============================================================ */
/* place-content as well as place-items: the base .hero__card carries
   justify-content:space-between for the flex layout, which in grid parks the
   single track at the start. place-items alone only centres inside the track. */
.hero--alt{--halo-scale:.65}         /* one dial for every halo image */
/* Same viewport rule as the closing block: exactly one screen minus the page
   padding, so the hero never forces a scroll to see its own bottom edge. */
/* The nav overlays the top of the card, so the top padding carries the nav
   height too. place-content then centres the stack in the space between the
   bottom of the nav and the bottom of the section, not the whole card. */
.hero--alt .hero__card{display:grid;place-items:center;place-content:center;
  overflow:hidden;min-height:0;
  padding:calc(96px + var(--nav-h,98px) - var(--hero-pad)) var(--pad-x) 96px;
  /* Same marquee-inspired technique as .hero--case above: the outer
     .hero--alt section now carries a hard height itself (100vh minus its
     own padding, border-box handles the subtraction), so this card just
     fills 100% of that -- no hand-maintained hero-pad*4 math. Risk to watch:
     unlike the case-hero image, this card has no "shrink to fit" content of
     its own, so overflow:hidden here means text could clip at very short
     window heights rather than the card growing to fit it -- flagged for
     verification, not yet hit in testing. */
  height:100%}

/* ---- centred text stack ---- */
.heroalt{position:relative;z-index:3;display:flex;flex-direction:column;
  align-items:center;text-align:center;gap:0;max-width:760px}
.heroalt__eyebrow{font-size:var(--fs-lead);font-weight:400;letter-spacing:-1px;
  color:var(--grey-50);line-height:normal}
.heroalt__title{font-size:var(--fs-h1);font-weight:700;line-height:normal;
  letter-spacing:-1.5px}
.heroalt__title .accent{display:inline-block;
  transition:color .35s var(--ease),opacity .35s var(--ease),transform .35s var(--ease)}
.heroalt__title .accent.is-out{opacity:0;transform:translateY(-10px)}
/* Measured gaps: 8 title -> sentence, 32 sentence -> button, 32 button -> the
   status frame (its own 8px padding sits inside that frame, not on top of it). */
.heroalt__sub{font-size:var(--fs-body);line-height:24px;font-weight:300;
  color:var(--black-60);max-width:520px;margin-top:8px}
.heroalt .btn{margin-top:32px}
.heroalt .status{margin-top:32px;margin-left:0}

/* Home-hero entrance: the name is the one immediately visible anchor. The
   rest waits one second (set in script.js), then fades in as a single beat. */
.hero--alt .heroalt.rise{opacity:1;transform:none;transition:none}
.hero--alt .heroalt > :not(.heroalt__eyebrow),
.hero--alt .halo,
.hero--alt .hero__contact{opacity:0;transition-property:opacity;
  transition-duration:2000ms;transition-timing-function:ease}
/* The backdrop establishes the scene first. The content then follows in a
   compact 150ms cadence, with the job title starting 400ms into the image
   fade so the sequence reads clearly without feeling disconnected. */
.hero--alt .halo{transition-delay:0ms}
.hero--alt .heroalt__title{transition-delay:400ms}
.hero--alt .heroalt__sub{transition-delay:550ms}
.hero--alt .heroalt .btn{transition-delay:700ms}
.hero--alt .heroalt .status{transition-delay:850ms}
.hero--alt .hero__contact{transition-delay:1000ms}
.hero--alt.hero-intro-ready .heroalt > :not(.heroalt__eyebrow),
.hero--alt.hero-intro-ready .halo,
.hero--alt.hero-intro-ready .hero__contact{opacity:1}
@media (prefers-reduced-motion:reduce){
  .hero--alt .heroalt > :not(.heroalt__eyebrow),
  .hero--alt .halo,
  .hero--alt .hero__contact{opacity:1;transition:none}
}

/* ---- the image halo, shared by both variants ----
   Each item is placed by the --x / --y the build computed from its measured
   angle and radius, so the mock sits where the export does. */
.halo{position:absolute;inset:0;z-index:1;pointer-events:none}
.halo__ring{position:absolute;top:50%;left:50%;width:0;height:0}
.halo__item{position:absolute;top:0;left:0;width:calc(var(--w) * var(--halo-scale));
  transform:translate(-50%,-50%) translate(var(--x),var(--y)) rotate(var(--tilt));
  will-change:transform}
/* explicit height from the measured export: holds the layout before the
   image loads, and keeps the composition true to the board */
/* Drop shadow measured off Darian's export: peak ~0.45 alpha immediately under
   the edge, falling to nothing over roughly 40px. --img-shadow was far too
   light for this (its -40px spread all but erases it). This is the same value
   the closing block already uses on its floating mockups. */
.halo__item img{width:100%;height:calc(var(--h) * var(--halo-scale));object-fit:cover;
  border-radius:8px;background:#e9ebee;
  box-shadow:0 2px 4px rgba(0,0,0,.08),0 20px 40px -18px rgba(0,0,0,.9)}
.halo__flip{transform-style:preserve-3d}

/* ---- variant 1: the ring turns ----
   The ring rotates; every item counter-rotates at the same speed so the
   screenshots stay upright while travelling round the circle. */
@keyframes halo-spin{to{transform:rotate(360deg)}}
@keyframes halo-spin-back{to{transform:translate(-50%,-50%) translate(var(--x),var(--y))
  rotate(calc(var(--tilt) - 360deg))}}
@keyframes halo-flip{
  0%,88%{transform:rotateY(0)}
  94%{transform:rotateY(180deg)}
  100%{transform:rotateY(360deg)}
}
.halo--spin .halo__ring{animation:halo-spin 90s linear infinite}
.halo--spin .halo__item{animation:halo-spin-back 90s linear infinite}
/* staggered so they do not all flip at once */
.halo--spin .halo__flip{animation:halo-flip 24s ease-in-out infinite;
  animation-delay:calc(var(--i) * -3s)}

/* ---- variant 2: the pointer moves them ----
   Depth 1 travels further than depth 2, as in the closing block. */
.hero--mouse .halo__item{transition:transform .5s var(--ease)}
.hero--mouse .halo__item{transform:translate(-50%,-50%) translate(var(--x),var(--y))
  rotate(var(--tilt)) translate(var(--drift,0,0))}

@media (prefers-reduced-motion:reduce){
  .halo--spin .halo__ring,.halo--spin .halo__item,.halo--spin .halo__flip{animation:none}
}
@media (max-width:1100px){
  .hero--alt{--halo-scale:.55}
}
@media (max-width:900px){
  /* the halo would collide with the text at this size, so it steps back */
  .halo{opacity:.35;transform:scale(.72)}
}
@media (max-width:700px){
  .halo{display:none}
  .heroalt{width:100%;min-width:0}
  .heroalt__title{max-width:100%}
  .heroalt__title .role-rotator__role--long{
    font-size:clamp(24px,7vw,28px);letter-spacing:-.75px
  }
  .heroalt__sub{width:100%;max-width:320px}
  /* release the fixed viewport-height target once stacked -- matches the
     halo's own hide-breakpoint here, kept separate from the case-hero's
     900px release since the alt hero has no 2-column layout to stack. */
  .hero--alt{height:auto}
  .hero--alt .hero__card{height:auto}
}

/* ---- philosophy quote ----
   Standard section padding. The 280px break either side comes from this
   section's 140 plus the neighbouring section's own 140, so it does not need
   doubling here (and doubling it also broke the responsive step-down). */
