/* ===========================================================
   Welkin Light Media — brand system
   Palette + type lifted from Welkin Light Brand Guidelines
   =========================================================== */
:root{
  --ink:#030912; --navy:#040F20; --surf:#0B1A2E; --surf2:#0E2240; --surf3:#12294A;
  --blue:#2F6FB7; --blue-br:#4E9BE8; --glow:#7FC0FF; --pale:#EAF4FF;
  --mist:#AEC1D1; --dim:#8296A8; --slate:#42424C; --white:#fff;
  --line:rgba(174,193,209,.14); --line-2:rgba(174,193,209,.26);

  --c1:#3F93F7; --c2:#00A396; --c3:#9D67EC; --c4:#C97D13;

  --shell:1240px; --pad:clamp(20px,5vw,48px);
  --r:16px; --r-lg:26px;
  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);
  --nav-h:76px;
  color-scheme:dark;
}
*,*::before,*::after{box-sizing:border-box}
/* overflow-x on the BODY alone does not hold: when html stays `visible` the
   overflow propagates to the root, which then scrolls. Measured on a 375px
   iPhone SE - body was hidden, html visible, and the page still scrolled 3px
   sideways. `clip` rather than `hidden` here because hidden on the root
   makes it a scroll container and breaks position:sticky, which the nav
   uses. */
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%;overflow-x:clip}
body{
  margin:0;background:var(--navy);color:var(--mist);
  font:400 16px/1.65 Montserrat,system-ui,-apple-system,"Segoe UI",sans-serif;
  -webkit-font-smoothing:antialiased;overflow-x:hidden;
}
body.is-locked{overflow:hidden}
img{max-width:100%;height:auto;display:block}
a{color:inherit}
h1,h2,h3,h4{color:var(--white);font-weight:600;line-height:1.08;letter-spacing:-.025em;margin:0}
p{margin:0}
ul,ol,dl,dd{margin:0;padding:0;list-style:none}
:focus-visible{outline:2px solid var(--glow);outline-offset:3px;border-radius:4px}
::selection{background:var(--blue);color:#fff}

.shell{width:min(var(--shell),100% - var(--pad)*2);margin-inline:auto}
.skip{position:fixed;left:12px;top:-100px;z-index:999;background:var(--blue-br);color:#001024;
  padding:10px 16px;border-radius:8px;font-weight:600;transition:top .2s}
.skip:focus{top:12px}
.grad{background:linear-gradient(100deg,var(--blue-br) 0%,var(--pale) 55%,var(--blue-br) 100%);
  -webkit-background-clip:text;background-clip:text;color:transparent;background-size:200% 100%;
  animation:sheen 9s linear infinite}
@keyframes sheen{to{background-position:200% 0}}

/* ───────── loader ───────── */
.loader{position:fixed;inset:0;z-index:300;background:var(--navy);display:grid;place-content:center;
  justify-items:center;gap:22px;transition:opacity .7s var(--ease),visibility .7s}
.loader.is-done{opacity:0;visibility:hidden;pointer-events:none}
/* Failsafe: if JS never runs (error, blocked file, slow parse) the loader would
   otherwise cover the page forever. CSS retires it on its own after 5s. */
.loader{animation:loaderFailsafe 0s linear 5s forwards}
@keyframes loaderFailsafe{to{opacity:0;visibility:hidden;pointer-events:none}}
.loader__mark{width:clamp(74px,12vw,112px);color:var(--blue-br)}
.loader__mark svg{width:100%;height:auto;display:block}
.loader__mark svg path{opacity:0}
/* opacity ONLY. rayIn used to animate transform as well, on the very
   same paths the feather flap animates transform on. Two animations
   touching one property means the later layer wins outright - so the
   feathers sat frozen at rayIn's start state, matrix(.9,0,0,.9,0,10),
   scaled down and shoved 10px below their hinges. That is what made a
   wing look detached during loading. Keep transform out of here. */
@keyframes rayIn{from{opacity:0}to{opacity:1}}
.loader__txt{font-size:11px;letter-spacing:.42em;text-transform:uppercase;color:var(--dim);
  opacity:0;animation:fadeUp .8s .5s var(--ease-out) forwards}
.loader__bar{width:160px;height:2px;background:var(--line);overflow:hidden;border-radius:2px}
.loader__bar i{display:block;height:100%;width:0;background:linear-gradient(90deg,var(--blue),var(--pale));transition:width .3s linear}
@keyframes fadeUp{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}

/* ───────── shader stage ───────── */
.stage{position:fixed;inset:0;z-index:0;pointer-events:none;background:var(--navy)}
.stage canvas{position:absolute;inset:0;width:100%;height:100%;display:block;opacity:0;transition:opacity 1.2s var(--ease)}
.stage canvas.is-on{opacity:1}
/* The stage shader beats the same wing mark the hero plate does. Two wing
   animations stacked on one screen read as a collision, so on the page that
   has the hero plate the stage is held back until the hero has scrolled
   away. The gate class is added by JS; without JS nothing is added and the
   stage simply shows, which is the right way round for a failsafe. */
.stage canvas,.stage__fallback,.stage__grain{transition:opacity .9s var(--ease)}
body.hero-gate:not(.past-hero) .stage canvas,
body.hero-gate:not(.past-hero) .stage__fallback,
body.hero-gate:not(.past-hero) .stage__grain{opacity:0}
.stage__fallback{position:absolute;inset:0;background:url(/assets/bg-shader.jpg) center 42%/cover no-repeat;
  opacity:.9;transition:opacity .8s var(--ease)}
.stage.has-gl .stage__fallback{opacity:0}
.stage__vignette{position:absolute;inset:0;
  background:
    linear-gradient(100deg,rgba(4,12,26,.9) 0%,rgba(4,12,26,.72) 34%,rgba(4,12,26,.12) 62%,transparent 86%),
    radial-gradient(135% 90% at 50% 40%,transparent 0%,rgba(4,15,32,.5) 64%,var(--navy) 100%),
    linear-gradient(180deg,rgba(4,15,32,.72) 0%,transparent 24%,transparent 60%,var(--navy) 100%)}
@media(max-width:900px){.stage__vignette{background:
    linear-gradient(180deg,rgba(4,12,26,.86) 0%,rgba(4,12,26,.6) 42%,rgba(4,12,26,.82) 100%),
    radial-gradient(135% 90% at 50% 40%,transparent 0%,rgba(4,15,32,.5) 64%,var(--navy) 100%)}}
.stage__grain{position:absolute;inset:-50%;opacity:.045;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='160' height='160'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/></filter><rect width='160' height='160' filter='url(%23n)'/></svg>");
  animation:grain 7s steps(6) infinite}
@keyframes grain{0%{transform:translate(0,0)}20%{transform:translate(-4%,2%)}40%{transform:translate(3%,-3%)}
60%{transform:translate(-2%,4%)}80%{transform:translate(4%,1%)}100%{transform:translate(0,0)}}

/* ───────── nav ───────── */
.nav{position:fixed;top:0;left:0;right:0;z-index:120;transition:transform .45s var(--ease)}
.nav.is-hidden{transform:translateY(-110%)}
.nav__inner{width:min(var(--shell),100% - var(--pad)*2);margin-inline:auto;height:var(--nav-h);
  display:flex;align-items:center;gap:clamp(14px,3vw,34px);
  padding-inline:0;border-radius:999px;transition:all .45s var(--ease)}
.nav.is-stuck .nav__inner{height:62px;margin-top:10px;padding-inline:clamp(14px,2.4vw,22px);
  background:rgba(8,24,46,.62);backdrop-filter:blur(18px) saturate(1.4);-webkit-backdrop-filter:blur(18px) saturate(1.4);
  border:1px solid var(--line);box-shadow:0 18px 50px -24px rgba(0,0,0,.9)}
.nav__brand{display:flex;align-items:center;gap:11px;text-decoration:none;flex:0 0 auto;min-height:32px}
.nav__mark{width:30px;color:var(--blue-br);display:block;transition:transform .5s var(--ease),color .3s}
.nav__mark svg{width:100%;height:auto;display:block}
.nav__brand:hover .nav__mark{transform:translateY(-2px) scale(1.05);color:var(--glow)}
.nav__word{font-size:12.5px;font-weight:600;letter-spacing:.17em;text-transform:uppercase;color:var(--white);white-space:nowrap}
.nav__word em{font-style:normal;color:var(--dim)}
.nav__links{position:relative;display:flex;gap:4px;margin-left:auto}
.nav__links a{position:relative;z-index:1;padding:9px 15px;border-radius:999px;text-decoration:none;
  font-size:13.5px;font-weight:500;color:var(--mist);transition:color .25s;white-space:nowrap}
.nav__links a:hover,.nav__links a.is-active{color:var(--white)}
.nav__pill{position:absolute;z-index:0;top:0;left:0;height:100%;border-radius:999px;
  background:rgba(79,155,232,.16);border:1px solid rgba(126,192,255,.22);opacity:0;
  transition:transform .42s var(--ease),width .42s var(--ease),opacity .3s}
.nav__links:hover .nav__pill,.nav__pill.is-on{opacity:1}
.nav__cta{flex:0 0 auto}
@media(max-width:1080px){.nav .nav__links,.nav .nav__cta{display:none!important}}

.burger{display:none;margin-left:auto;width:42px;height:42px;border:1px solid var(--line);border-radius:12px;
  background:rgba(10,26,48,.5);cursor:pointer;flex-direction:column;align-items:center;justify-content:center;gap:5px}
.burger i{width:16px;height:1.5px;background:var(--mist);display:block;transition:transform .35s var(--ease),opacity .25s}
.burger[aria-expanded="true"] i:first-child{transform:translateY(3.25px) rotate(45deg)}
.burger[aria-expanded="true"] i:last-child{transform:translateY(-3.25px) rotate(-45deg)}
@media(max-width:1080px){.nav .burger{display:flex}}

.drawer{position:fixed;inset:0;z-index:110;background:rgba(4,12,26,.93);
  backdrop-filter:blur(22px);-webkit-backdrop-filter:blur(22px);
  display:grid;align-content:center;gap:34px;padding:var(--pad);
  opacity:0;visibility:hidden;transition:opacity .4s var(--ease),visibility .4s}
.drawer.is-open{opacity:1;visibility:visible}
/* Was clamp(28px,8vw,44px) - at the 1080px drawer breakpoint that resolved to
   the full 44px and six links filled the whole screen. Half that reads as a
   menu rather than a title wall, and the row keeps a 52px hit area. */
.drawer nav{display:grid;gap:2px}
.drawer nav a{position:relative;display:flex;align-items:center;gap:15px;min-height:52px;
  width:fit-content;font-size:clamp(21px,3.2vw,29px);font-weight:300;
  color:var(--white);text-decoration:none;letter-spacing:-.03em;
  opacity:0;transition:color .3s var(--ease)}

/* The entrance IS the motion: each label wipes in from the left under a
   clip-path while it slides, so the menu draws itself line by line instead of
   fading in as a block. No leading dash, bar or dot - those read as template
   filler, same reason the section eyebrows lost theirs. */
.drawer.is-open nav a{animation:drawerIn .5s var(--ease-out) forwards}
@keyframes drawerIn{
  from{opacity:0;transform:translateX(-18px);clip-path:inset(0 100% 0 0)}
  to  {opacity:1;transform:none;clip-path:inset(0 -2px 0 0)}
}
.drawer.is-open nav a:nth-child(1){animation-delay:.04s}
.drawer.is-open nav a:nth-child(2){animation-delay:.09s}
.drawer.is-open nav a:nth-child(3){animation-delay:.14s}
.drawer.is-open nav a:nth-child(4){animation-delay:.19s}
.drawer.is-open nav a:nth-child(5){animation-delay:.24s}
.drawer.is-open nav a:nth-child(6){animation-delay:.29s}
.drawer.is-open nav a:nth-child(7){animation-delay:.34s}
.drawer.is-open nav a:nth-child(n+8){animation-delay:.39s}

/* Each row carries a glyph from the page sprite. It is the interactive part:
   it lifts and brightens on hover and marks the current page, which a static
   dash never did. The underline sits on the label only, so it tracks the word
   rather than the whole row. */
.dico{width:21px;height:21px;flex:0 0 21px;color:var(--blue-br);opacity:.62;
  transition:transform .36s var(--ease-out),opacity .3s var(--ease)}
.drawer nav a span{position:relative}
.drawer nav a span::after{content:"";position:absolute;left:0;right:0;bottom:-3px;
  height:1px;background:var(--blue-br);
  transform:scaleX(0);transform-origin:left center;
  transition:transform .34s var(--ease-out)}
.drawer nav a:hover,.drawer nav a:focus-visible,.drawer nav a.is-active{color:var(--blue-br)}
.drawer nav a:hover span::after,.drawer nav a:focus-visible span::after,
.drawer nav a.is-active span::after{transform:scaleX(1)}
.drawer nav a:hover .dico,.drawer nav a:focus-visible .dico{
  opacity:1;transform:translateX(3px) scale(1.09)}
.drawer nav a.is-active .dico{opacity:1}

@media (prefers-reduced-motion: reduce){
  .drawer.is-open nav a{animation:none;opacity:1;transform:none;clip-path:none}
  .drawer nav a span::after,.dico{transition:none}
}
.drawer__foot{display:grid;gap:4px;font-size:14px;color:var(--dim);border-top:1px solid var(--line);padding-top:22px}
.drawer__foot a{text-decoration:none;min-height:30px;display:flex;align-items:center}

/* ───────── buttons ───────── */
.btn{display:inline-flex;align-items:center;justify-content:center;gap:9px;
  padding:13px 25px;border-radius:999px;font-size:13.5px;font-weight:600;letter-spacing:.02em;
  text-decoration:none;cursor:pointer;border:1px solid transparent;position:relative;
  transition:transform .3s var(--ease),border-color .3s,background .3s,color .3s,box-shadow .4s}
.btn--lg{padding:17px 36px;font-size:15px}
.btn--ghost{border-color:var(--line-2);color:var(--white);background:rgba(174,193,209,.05)}
.btn--ghost:hover{border-color:var(--blue-br);background:rgba(79,155,232,.14);transform:translateY(-2px)}
.btn--glow{background:linear-gradient(135deg,var(--blue) 0%,var(--blue-br) 100%);color:#021022;overflow:hidden;
  box-shadow:0 0 0 0 rgba(79,155,232,.5),0 14px 34px -14px rgba(47,111,183,.95)}
.btn--glow span{position:relative;z-index:1}
.btn--glow::after{content:"";position:absolute;inset:0;border-radius:inherit;
  background:linear-gradient(110deg,transparent 28%,rgba(255,255,255,.55) 50%,transparent 72%);
  transform:translateX(-120%);transition:transform .75s var(--ease)}
.btn--glow:hover{transform:translateY(-2px);box-shadow:0 0 34px -4px rgba(79,155,232,.55),0 20px 42px -16px rgba(47,111,183,1)}
.btn--glow:hover::after{transform:translateX(120%)}

/* ───────── shared section ───────── */
/* One gap between topics, not two. Every .sec carried the same padding top
   AND bottom, so consecutive sections stacked twice the intended rhythm -
   up to 296px of dead space between topics at desktop. The sibling rule
   collapses that to a single gap, and the base value is pulled in as well. */
/* The rule sits on the join, so the gap has to be symmetrical around it or
   the line crowds whichever side has less room. Zeroing padding-top put it
   hard against the next heading. Equal padding both sides centres it, and the
   base value is small enough that the total gap is still roughly half the
   original 2 x clamp(76px,11vw,148px). */
.sec{position:relative;z-index:1;padding:clamp(46px,5.6vw,78px) 0}

/* A hairline marks the join now that the whitespace no longer does it alone.
   NOTE: this gold is NOT in the Welkin Light Brand Guidelines palette - it
   was added on request. Change --rule to var(--line-2) to go back to brand. */
:root{--gold:#C8A24A;--rule:rgba(200,162,74,.42)}
.sec + .sec::before,.sec + .cta::before,.cta + .sec::before{
  content:"";position:absolute;top:0;left:50%;transform:translateX(-50%);
  width:min(var(--shell),100% - var(--pad)*2);height:1px;
  background:linear-gradient(90deg,transparent,var(--rule) 22%,var(--rule) 78%,transparent)}
.sec__head{max-width:760px;margin-bottom:clamp(40px,6vw,68px)}
.sec__title{font-size:clamp(30px,5.2vw,58px);font-weight:300;letter-spacing:-.035em}
.sec__lede{margin-top:20px;font-size:clamp(15px,1.6vw,18px);font-weight:300;color:var(--mist);max-width:62ch}
.eyebrow{display:inline-flex;align-items:center;font-size:11px;font-weight:600;
  letter-spacing:.26em;text-transform:uppercase;color:var(--blue-br);margin-bottom:20px}

/* reveal */
.reveal{opacity:0;transform:translateY(26px);filter:blur(6px);
  transition:opacity .85s var(--ease-out),transform .85s var(--ease-out),filter .85s var(--ease-out)}
.reveal.is-in{opacity:1;transform:none;filter:none}
/* Same reasoning: scroll-reveal content must never be permanently invisible. */
.reveal{animation:revealFailsafe 0s linear 4s forwards}
@keyframes revealFailsafe{to{opacity:1;transform:none;filter:none}}
.reveal[data-delay="1"]{transition-delay:.1s}.reveal[data-delay="2"]{transition-delay:.2s}
.reveal[data-delay="3"]{transition-delay:.3s}.reveal[data-delay="4"]{transition-delay:.4s}

/* ───────── hero ───────── */
.hero{position:relative;z-index:1;min-height:100svh;display:flex;flex-direction:column;
  justify-content:center;padding:calc(var(--nav-h) + 48px) 0 110px}
/* Dialled back from clamp(40px,9vw,104px): at 104px the headline was the
   whole first screen and pushed the copy and stats under the fold. */
.hero__title{font-size:clamp(33px,5.8vw,68px);font-weight:200;letter-spacing:-.04em;line-height:1.02;max-width:15ch}
.hero__title .w{display:inline-block;opacity:0;filter:blur(14px);transform:translateY(.34em) scale(.97);
  transition:opacity .9s var(--ease-out),filter .9s var(--ease-out),transform .9s var(--ease-out)}
.hero__title.is-in .w{opacity:1;filter:none;transform:none}
.hero__sub{margin-top:22px;max-width:46ch;font-size:clamp(14.5px,1.35vw,17px);font-weight:300;line-height:1.68}
.hero__sub em{font-style:normal;color:var(--white);font-weight:500}
.hero__cta{margin-top:30px;display:flex;flex-wrap:wrap;gap:13px}
/* Four explicit columns, not auto-fit. With four stats, a 52px gap and a
   720px cap, `repeat(auto-fit,minmax(150px,1fr))` could not fit the fourth
   (4x150 + 3x52 = 756) and silently dropped "Campaign posts delivered" onto
   a second row, out of line with the rest. Explicit counts also make the
   responsive steps predictable: 4 across, 2 across, then 1. */
.hero__stats{margin-top:clamp(48px,7vw,80px);display:grid;gap:clamp(16px,2.6vw,34px);
  grid-template-columns:repeat(4,minmax(0,1fr));max-width:840px;margin-inline:auto;
  align-items:start;justify-items:center;text-align:center;
  border-top:1px solid var(--line);padding-top:26px}
.hero__stats > div{display:flex;flex-direction:column;align-items:center}
.hero__stats dt{justify-content:center}
.hero__stats dd{text-align:center;text-wrap:balance}
@media(max-width:700px){
  .hero__stats{grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(18px,4vw,28px)}
}
/* 380px was too eager - a 360px phone has room for two across, and four
   stacked rows pushed the whole hero down a screen. */
@media(max-width:330px){
  .hero__stats{grid-template-columns:minmax(0,1fr);gap:14px}
}
.hero__stats dt{display:flex;align-items:center;gap:11px;
  font-size:clamp(27px,3.4vw,38px);font-weight:200;color:var(--white);letter-spacing:-.04em;
  font-variant-numeric:tabular-nums;line-height:1}
.hero__stats dd{margin-top:9px;font-size:12.5px;line-height:1.5;color:var(--dim);letter-spacing:.02em}

.scrollcue{position:absolute;left:50%;transform:translateX(-50%);bottom:28px;
  display:grid;justify-items:center;gap:9px;text-decoration:none;color:var(--dim)}
@media(max-width:980px){.scrollcue{display:none}}
.scrollcue__line{width:1px;height:46px;background:linear-gradient(var(--blue-br),transparent);position:relative;overflow:hidden}
.scrollcue__line::after{content:"";position:absolute;top:-46px;left:0;width:1px;height:46px;
  background:linear-gradient(transparent,var(--pale));animation:cue 2.4s var(--ease) infinite}
@keyframes cue{to{top:46px}}
.scrollcue__txt{font-size:9.5px;letter-spacing:.34em;text-transform:uppercase}
@media(max-width:700px){.scrollcue{display:none}}

/* ───────── marquee ───────── */
.marquee{position:relative;z-index:1;border-block:1px solid var(--line);padding:22px 0;overflow:hidden;
  background:rgba(4,13,27,.55);backdrop-filter:blur(6px);
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 9%,#000 91%,transparent);
  mask-image:linear-gradient(90deg,transparent,#000 9%,#000 91%,transparent)}
.marquee__track{display:flex;align-items:center;gap:40px;width:max-content;animation:slide 38s linear infinite}
.marquee:hover .marquee__track{animation-play-state:paused}
.marquee span{font-size:clamp(14px,1.8vw,19px);font-weight:300;color:var(--mist);
  letter-spacing:.04em;white-space:nowrap;opacity:.78;transition:opacity .3s,color .3s}
.marquee span:hover{opacity:1;color:var(--white)}
.marquee b{width:5px;height:5px;border-radius:50%;background:var(--blue);flex:0 0 auto}
@keyframes slide{to{transform:translateX(-50%)}}

/* ───────── bento services ───────── */
.bento{display:grid;gap:14px;grid-template-columns:repeat(4,1fr)}
.bcard{position:relative;overflow:hidden;padding:clamp(22px,2.6vw,32px);border-radius:var(--r-lg);
  border:1px solid var(--line);background:linear-gradient(165deg,rgba(14,34,64,.72),rgba(7,18,36,.62));
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  transition:border-color .45s var(--ease),transform .45s var(--ease),background .45s}
.bcard::before{content:"";position:absolute;inset:0;border-radius:inherit;opacity:0;pointer-events:none;
  background:radial-gradient(420px circle at var(--mx,50%) var(--my,0%),rgba(79,155,232,.17),transparent 62%);
  transition:opacity .4s}
.bcard:hover{border-color:rgba(126,192,255,.34);transform:translateY(-4px)}
.bcard:hover::before{opacity:1}
.bcard--lg{grid-column:span 2;grid-row:span 2}
.bcard--wide{grid-column:span 2}
.bcard__no{font-size:11px;letter-spacing:.28em;color:var(--blue-br);font-weight:600}
.bcard h3{margin:14px 0 11px;font-size:clamp(18px,2.1vw,25px);font-weight:500;letter-spacing:-.02em}
.bcard--lg h3{font-size:clamp(23px,3vw,34px)}
.bcard p{font-size:14.5px;font-weight:300;line-height:1.65;color:var(--mist)}
.bcard ul{margin-top:20px;display:flex;flex-wrap:wrap;gap:7px}
.bcard li{font-size:11.5px;padding:6px 12px;border-radius:999px;border:1px solid var(--line);
  color:var(--dim);background:rgba(174,193,209,.04)}
.bcard__glyph{position:absolute;right:-14px;top:-14px;width:120px;height:120px;opacity:.14;
  color:var(--blue-br);transition:opacity .45s var(--ease),transform .7s var(--ease)}
.bcard--lg .bcard__glyph{width:200px;height:200px;right:-26px;top:-26px}
.bcard:hover .bcard__glyph{opacity:.3;transform:rotate(8deg) scale(1.06)}
.bcard__glyph svg{width:100%;height:100%;display:block}
@media(max-width:980px){.bento{grid-template-columns:repeat(2,1fr)}
  .bcard--lg{grid-column:span 2;grid-row:auto}.bcard--wide{grid-column:span 2}}
@media(max-width:560px){.bento{grid-template-columns:1fr}
  .bcard--lg,.bcard--wide{grid-column:span 1}}

/* ───────── work deck ───────── */
.sec--work{padding-bottom:clamp(76px,11vw,148px)}
/* `flex` is a layout property, so hint the compositor only while it moves */

/* ───────── metrics ───────── */
.metrics{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(290px,1fr))}
.mcard{margin:0;padding:clamp(22px,2.4vw,28px);border-radius:var(--r-lg);border:1px solid var(--line);
  background:linear-gradient(170deg,rgba(11,26,46,.88),rgba(6,16,32,.8));
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px)}
.mcard h3{font-size:12px;font-weight:600;letter-spacing:.2em;text-transform:uppercase;color:var(--dim)}
.mcard__hero{margin-top:12px;font-size:clamp(30px,3.6vw,40px);font-weight:200;color:var(--white);
  letter-spacing:-.04em;line-height:1;font-variant-numeric:tabular-nums;display:flex;align-items:baseline;gap:10px;flex-wrap:wrap}
.mcard__unit{font-size:.5em;color:var(--dim);font-weight:300}
.delta{font-size:11.5px;font-weight:600;letter-spacing:.04em;padding:4px 9px;border-radius:999px}
.delta--up{color:#6FE3CE;background:rgba(0,163,150,.16);border:1px solid rgba(0,163,150,.32)}
.mcard__sub{margin-top:9px;font-size:12px;color:var(--dim)}
.chart{margin-top:22px}
.chart svg{width:100%;height:auto;display:block;overflow:visible}
.legend{margin-top:16px;display:flex;flex-wrap:wrap;gap:8px 16px}
.legend li{display:flex;align-items:center;gap:7px;font-size:12px;color:var(--mist)}
.legend i{width:9px;height:9px;border-radius:2px;flex:0 0 auto}
.legend b{color:var(--white);font-weight:500;font-variant-numeric:tabular-nums}
.mcard__data{margin-top:18px;border-top:1px solid var(--line);padding-top:12px}
.mcard__data summary{font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--dim);
  cursor:pointer;list-style:none}
.mcard__data summary::-webkit-details-marker{display:none}
.mcard__data summary::before{content:"+ ";color:var(--blue-br)}
.mcard__data[open] summary::before{content:"− "}
.mcard__data table{width:100%;border-collapse:collapse;margin-top:12px;font-size:12.5px}
.mcard__data caption{text-align:left;font-size:11px;color:var(--dim);padding-bottom:8px}
.mcard__data th,.mcard__data td{text-align:left;padding:6px 0;border-bottom:1px solid var(--line);color:var(--mist)}
.mcard__data th{color:var(--dim);font-weight:500;font-size:11px;letter-spacing:.1em;text-transform:uppercase}
.mcard__data td:last-child{text-align:right;font-variant-numeric:tabular-nums;color:var(--white)}
.tip{position:fixed;z-index:200;pointer-events:none;padding:8px 12px;border-radius:10px;
  background:rgba(6,16,32,.96);border:1px solid var(--line-2);font-size:12px;color:var(--white);
  box-shadow:0 14px 34px -14px #000;opacity:0;transform:translate(-50%,-130%);transition:opacity .15s;white-space:nowrap}
.tip.is-on{opacity:1}
.tip i{display:inline-block;width:8px;height:8px;border-radius:2px;margin-right:7px;vertical-align:-1px}
.tip b{font-variant-numeric:tabular-nums}

/* ───────── process arc ───────── */
.arc{position:relative}
.arc__svg{width:100%;height:clamp(120px,16vw,220px);margin-bottom:-18px}
#arcPath{stroke-dasharray:var(--len);stroke-dashoffset:var(--len)}
.steps{display:grid;gap:clamp(24px,3vw,32px);grid-template-columns:repeat(auto-fit,minmax(210px,1fr));
  border-top:1px solid var(--line);padding-top:clamp(28px,4vw,44px)}
.steps li{position:relative;padding-top:4px}
.steps span{font-size:11px;letter-spacing:.3em;color:var(--blue-br);font-weight:600}
.steps h3{margin:12px 0 10px;font-size:clamp(19px,2.3vw,26px);font-weight:400;letter-spacing:-.025em}
.steps p{font-size:14px;font-weight:300;line-height:1.68;color:var(--mist)}

/* ───────── about ───────── */
.about{display:grid;gap:clamp(36px,6vw,78px);grid-template-columns:1.15fr .85fr;align-items:center}
.about__copy p{font-size:clamp(15px,1.6vw,17px);font-weight:300;line-height:1.78;margin-top:20px;max-width:58ch}
.pillars{margin-top:38px;display:grid;gap:2px;border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.pillars li{padding:20px 22px;background:rgba(11,26,46,.6);display:grid;gap:5px}
.pillars strong{font-size:12px;letter-spacing:.2em;text-transform:uppercase;color:var(--blue-br);font-weight:600}
.pillars span{font-size:14px;font-weight:300;color:var(--mist)}
.about__art{display:grid;place-items:center;position:relative;aspect-ratio:1}
.about__art::before{content:"";position:absolute;inset:8%;border-radius:50%;
  background:radial-gradient(circle,rgba(47,111,183,.3),transparent 66%);filter:blur(28px);animation:breathe 7s var(--ease) infinite}
@keyframes breathe{0%,100%{transform:scale(1);opacity:.75}50%{transform:scale(1.14);opacity:1}}
.about__mark{position:relative;width:76%;color:var(--blue-br)}
.about__mark svg{width:100%;height:auto;display:block;filter:drop-shadow(0 0 32px rgba(79,155,232,.42))}
.about__mark svg path{animation:hover 9s var(--ease) infinite}
@keyframes hover{0%,100%{transform:translateY(0)}50%{transform:translateY(-10px)}}
@media(max-width:900px){.about{grid-template-columns:1fr}.about__art{max-width:300px;margin-inline:auto}}

/* ───────── pricing tiers ───────── */
.toggle{display:inline-flex;align-items:center;gap:13px;margin-top:28px;font-size:13px;color:var(--dim)}
.toggle b{color:var(--blue-br);font-weight:600}
.toggle__sw{width:48px;height:27px;border-radius:999px;border:1px solid var(--line-2);
  background:rgba(174,193,209,.07);cursor:pointer;padding:3px;display:flex;transition:background .3s,border-color .3s}
.toggle__sw i{width:19px;height:19px;border-radius:50%;background:var(--mist);transition:transform .35s var(--ease),background .3s}
.toggle__sw[aria-checked="true"]{background:rgba(79,155,232,.25);border-color:var(--blue-br)}
.toggle__sw[aria-checked="true"] i{transform:translateX(21px);background:var(--pale)}
.tiers{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(265px,1fr));align-items:start}
.tier{position:relative;padding:clamp(26px,3vw,34px);border-radius:var(--r-lg);border:1px solid var(--line);
  background:linear-gradient(170deg,rgba(11,26,46,.8),rgba(6,16,32,.72));
  backdrop-filter:blur(12px);-webkit-backdrop-filter:blur(12px);
  transition:transform .45s var(--ease),border-color .45s}
.tier:hover{transform:translateY(-5px);border-color:rgba(126,192,255,.3)}
.tier--feature{border-color:rgba(79,155,232,.45);
  background:linear-gradient(170deg,rgba(18,41,74,.92),rgba(8,20,40,.86));
  box-shadow:0 30px 80px -40px rgba(47,111,183,.9)}
.tier__flag{position:absolute;top:-11px;left:clamp(26px,3vw,34px);font-size:10px;letter-spacing:.2em;
  text-transform:uppercase;font-weight:600;color:#021022;background:linear-gradient(135deg,var(--blue),var(--blue-br));
  padding:5px 12px;border-radius:999px}
.tier h3{font-size:clamp(21px,2.4vw,27px);font-weight:500;letter-spacing:-.02em}
.tier__for{margin-top:8px;font-size:13.5px;font-weight:300;color:var(--dim);min-height:2.8em}
.tier__price{margin:22px 0;display:flex;align-items:baseline;gap:3px;color:var(--white)}
.tier__price em{font-style:normal;font-size:20px;font-weight:300;color:var(--mist)}
.tier__price span{font-size:clamp(36px,4.6vw,48px);font-weight:200;letter-spacing:-.045em;
  font-variant-numeric:tabular-nums;transition:opacity .25s}
.tier__price span.is-swap{opacity:0}
.tier__price i{font-style:normal;font-size:13px;color:var(--dim);margin-left:6px;font-weight:300}
.tier ul{display:grid;gap:11px;margin-bottom:26px;border-top:1px solid var(--line);padding-top:20px}
.tier li{font-size:13.5px;font-weight:300;color:var(--mist);padding-left:22px;position:relative}
.tier li::before{content:"";position:absolute;left:0;top:.56em;width:9px;height:1.5px;background:var(--blue-br)}
.tier .btn{width:100%}
.tiers__note{margin-top:26px;font-size:12.5px;color:var(--dim);max-width:62ch}

/* ───────── quotes ───────── */
.quotes{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(265px,1fr))}
.quote{margin:0;padding:clamp(26px,3vw,34px);border-radius:var(--r-lg);border:1px solid var(--line);
  background:linear-gradient(170deg,rgba(11,26,46,.7),rgba(6,16,32,.62));position:relative;overflow:hidden}
.quote::before{content:"“";position:absolute;top:-24px;right:12px;font-size:150px;line-height:1;
  color:rgba(79,155,232,.1);font-weight:700}
.quote blockquote{margin:0;font-size:clamp(15px,1.7vw,17.5px);font-weight:300;line-height:1.72;color:var(--white)}
.quote figcaption{margin-top:24px;border-top:1px solid var(--line);padding-top:16px;display:grid;gap:3px}
.quote strong{font-size:14px;color:var(--white);font-weight:600}
.quote span{font-size:12px;color:var(--dim)}

/* ───────── CTA band ───────── */
.cta{position:relative;z-index:1;padding:clamp(70px,10vw,128px) 0 0;overflow:hidden;text-align:center}
.cta__in{display:grid;justify-items:center;gap:20px;position:relative;z-index:2}
.cta h2{font-size:clamp(34px,7vw,76px);font-weight:200;letter-spacing:-.045em}
.cta p{max-width:50ch;font-size:clamp(15px,1.7vw,18px);font-weight:300}
.cta .btn{margin-top:12px}
.cta__ghost{position:relative;z-index:1;margin-top:-.1em;font-size:clamp(76px,21vw,260px);font-weight:700;
  letter-spacing:-.06em;line-height:.78;text-align:center;
  background:linear-gradient(180deg,rgba(79,155,232,.26),rgba(79,155,232,0) 78%);
  -webkit-background-clip:text;background-clip:text;color:transparent;user-select:none;
  transform:translateY(16%)}

/* ───────── contact ───────── */
.contact{display:grid;gap:clamp(32px,5vw,64px);grid-template-columns:.9fr 1.1fr;align-items:start}
.contact__copy p{margin-top:20px;font-size:clamp(15px,1.6vw,17px);font-weight:300;line-height:1.75;max-width:46ch}
.promise{margin-top:30px;display:grid;gap:12px}
.promise li{font-size:13.5px;font-weight:300;color:var(--mist);padding-left:24px;position:relative}
.promise li::before{content:"";position:absolute;left:0;top:.5em;width:11px;height:1.5px;background:var(--blue-br)}
.form{display:grid;gap:14px;grid-template-columns:1fr 1fr;padding:clamp(24px,3vw,34px);
  border-radius:var(--r-lg);border:1px solid var(--line);
  background:linear-gradient(170deg,rgba(14,34,64,.78),rgba(7,18,36,.7));
  backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px)}
.field{display:grid;gap:7px}
.field--full{grid-column:1/-1}
.field label{font-size:11.5px;letter-spacing:.14em;text-transform:uppercase;color:var(--dim);font-weight:500}
.field label i{font-style:normal;text-transform:none;letter-spacing:0;opacity:.7}
.field input,.field select,.field textarea{
  width:100%;padding:13px 15px;border-radius:12px;border:1px solid var(--line);
  background:rgba(3,9,18,.5);color:var(--white);font:400 15px/1.5 Montserrat,sans-serif;
  min-height:48px;transition:border-color .3s,background .3s,box-shadow .3s}
/* iOS Safari zooms the viewport when a focused field is under 16px */
@media(max-width:860px){.field input,.field select,.field textarea{font-size:16px}}
.field textarea{resize:vertical;min-height:112px}
.field select{appearance:none;cursor:pointer;
  background-image:url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='8' fill='none'><path d='M1 1l5 5 5-5' stroke='%238296A8' stroke-width='1.5' stroke-linecap='round'/></svg>");
  background-repeat:no-repeat;background-position:right 15px center}
.field input:focus,.field select:focus,.field textarea:focus{
  outline:none;border-color:var(--blue-br);background:rgba(3,9,18,.75);box-shadow:0 0 0 3px rgba(79,155,232,.16)}
.field.is-bad input,.field.is-bad textarea{border-color:#E8705F}
.field .err{font-size:11.5px;color:#FF9D8E}
.hp{position:absolute;left:-9999px;width:1px;height:1px;opacity:0}
.form__submit{grid-column:1/-1;justify-self:start;margin-top:4px}
.form__note{grid-column:1/-1;font-size:13px;margin-top:2px;min-height:1.2em}
.form__note.is-ok{color:#6FE3CE}.form__note.is-bad{color:#FF9D8E}
@media(max-width:860px){.contact{grid-template-columns:1fr}.form{grid-template-columns:1fr}}

.tiles{margin-top:clamp(36px,5vw,56px);display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
.tile{padding:24px;border-radius:var(--r);border:1px solid var(--line);background:rgba(11,26,46,.5);
  display:grid;gap:9px;transition:border-color .4s,transform .4s var(--ease)}
.tile:hover{border-color:rgba(126,192,255,.3);transform:translateY(-3px)}
.tile span{font-size:11px;letter-spacing:.24em;text-transform:uppercase;color:var(--blue-br);font-weight:600}
.tile a{font-size:15px;color:var(--white);text-decoration:none;font-weight:400;word-break:break-word}
.tile a:hover{color:var(--blue-br)}
.tile address{font-style:normal;font-size:13.5px;font-weight:300;line-height:1.6;color:var(--mist)}

/* ───────── footer ───────── */
.foot{position:relative;z-index:1;margin-top:clamp(60px,9vw,110px);border-top:1px solid var(--line);
  background:linear-gradient(180deg,rgba(3,9,18,.72),rgba(3,9,18,.96));overflow:hidden}
.foot__grid{display:grid;gap:clamp(28px,4vw,48px);grid-template-columns:1.5fr 1fr 1fr 1.3fr;
  padding:clamp(48px,6vw,76px) 0 clamp(32px,4vw,48px)}
.foot__brand img{width:clamp(180px,20vw,232px);opacity:.95}
.foot__brand p{margin-top:18px;font-size:13.5px;font-weight:300;color:var(--dim);max-width:30ch}
.foot h3{font-size:11px;letter-spacing:.24em;text-transform:uppercase;color:var(--blue-br);
  font-weight:600;margin-bottom:16px}
.foot__nav{display:grid;gap:10px;align-content:start}
.foot__nav a{font-size:14px;font-weight:300;color:var(--mist);text-decoration:none;
  width:max-content;min-height:26px;display:flex;align-items:center;
  transition:color .25s,transform .25s var(--ease)}
.foot__nav a:hover{color:var(--white);transform:translateX(3px)}
.foot__contact{display:grid;gap:10px;align-content:start}
.foot__contact a{font-size:14px;font-weight:300;color:var(--mist);text-decoration:none;
  min-height:26px;display:flex;align-items:center;transition:color .25s}
.foot__contact a:hover{color:var(--white)}
.foot__contact address{font-style:normal;font-size:13.5px;font-weight:300;color:var(--dim);line-height:1.6;margin-top:4px}
@media(max-width:900px){.foot__grid{grid-template-columns:1fr 1fr}}
@media(max-width:520px){.foot__grid{grid-template-columns:1fr}}

.foot__wordmark{font-size:clamp(66px,16.5vw,210px);font-weight:700;letter-spacing:-.055em;line-height:.74;
  text-align:center;white-space:nowrap;user-select:none;margin-bottom:-.2em;
  background:linear-gradient(180deg,rgba(79,155,232,.16),rgba(79,155,232,0) 72%);
  -webkit-background-clip:text;background-clip:text;color:transparent}

.foot__bar{border-top:1px solid var(--line);position:relative;z-index:2;background:rgba(3,9,18,.8)}
.foot__barin{display:flex;flex-wrap:wrap;gap:14px;align-items:center;justify-content:space-between;
  padding:22px 0;font-size:12.5px;color:var(--dim)}
.by{display:inline-flex;align-items:center;gap:9px}
.by__txt{color:var(--dim)}
.by__mark{width:19px;color:var(--blue-br);display:block;transition:transform .5s var(--ease),color .3s}
.by__mark svg{width:100%;height:auto;display:block}
.by:hover .by__mark{transform:translateY(-2px) scale(1.12);color:var(--glow)}
.by__name{color:var(--white);font-weight:600;letter-spacing:.1em;text-transform:uppercase;font-size:11.5px}

/* ───────── reduced motion ───────── */
@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important;scroll-behavior:auto!important}
  .reveal{opacity:1;transform:none;filter:none}
  .hero__title .w{opacity:1;filter:none;transform:none}
  .marquee__track{animation:none;flex-wrap:wrap;width:auto;justify-content:center}
  .stage__grain{display:none}
}

/* ---------- the mark as the pointer ----------
   Only ever applied once JS has added .has-cur, so no-JS and touch sessions
   never lose their cursor. .cur-native hands it straight back over fields. */
html.has-cur,html.has-cur a,html.has-cur button,html.has-cur summary,
html.has-cur [role="button"],html.has-cur label{cursor:none}
html.has-cur.cur-native,html.has-cur.cur-native *{cursor:auto}
html.has-cur input,html.has-cur textarea,html.has-cur select,
html.has-cur [contenteditable]{cursor:auto}

.cur{position:fixed;left:0;top:0;z-index:400;pointer-events:none;
  width:0;height:0;opacity:0;transition:opacity .25s var(--ease);
  will-change:transform}
.cur.is-on{opacity:1}
.cur__mark{position:absolute;left:0;top:0;display:block;width:26px;height:26px;
  transform:translate(-50%,-50%);color:var(--glow);
  filter:drop-shadow(0 0 7px rgba(79,155,232,.55));
  transition:width .28s var(--ease-out),height .28s var(--ease-out),
             color .25s,opacity .25s}
.cur__mark svg{width:100%;height:100%;display:block;overflow:visible}
/* the mark already flaps on its own; over something clickable it opens up */
.cur.is-hot .cur__mark{width:40px;height:40px;color:var(--pale)}
.cur.is-press .cur__mark{width:21px;height:21px}

@media (prefers-reduced-motion: reduce){.cur{display:none}}
@media (max-width:1079px){.cur{display:none}}

/* ===========================================================
   Wing mark — slow flap
   The mark ships as three groups (left wing, right wing, body).
   Feathers carry --i so the outer ones lag, which is what makes
   the stroke read as a wing rather than a rotating shape.
   =========================================================== */
.mark svg,.nav__mark svg,.by__mark svg,.about__mark svg,.loader__mark svg{overflow:visible}
.wing,.wmbody{transform-box:view-box;transform-origin:var(--ox) var(--oy)}
.wing path{transform-box:view-box;transform-origin:var(--ox) var(--oy)}
.wing--l{animation:flapL var(--flap,6s) cubic-bezier(.42,0,.58,1) infinite}
.wing--r{animation:flapR var(--flap,6s) cubic-bezier(.42,0,.58,1) infinite}
.wmbody{animation:bodyBob var(--flap,6s) cubic-bezier(.42,0,.58,1) infinite}
.wing path{animation:feather var(--flap,6s) cubic-bezier(.42,0,.58,1) infinite;
  animation-delay:calc(var(--i) * .055s)}
.wing--r path{animation-name:featherR}

@keyframes flapL{0%,100%{transform:rotate(0deg)}42%{transform:rotate(7.5deg)}70%{transform:rotate(-2.2deg)}}
@keyframes flapR{0%,100%{transform:rotate(0deg)}42%{transform:rotate(-7.5deg)}70%{transform:rotate(2.2deg)}}
@keyframes feather{0%,100%{transform:rotate(0deg)}46%{transform:rotate(3.4deg)}74%{transform:rotate(-1deg)}}
@keyframes featherR{0%,100%{transform:rotate(0deg)}46%{transform:rotate(-3.4deg)}74%{transform:rotate(1deg)}}
@keyframes bodyBob{0%,100%{transform:translateY(0)}42%{transform:translateY(-2.5%)}}

.loader__mark{--flap:3.2s}
.nav__mark{--flap:7s}
.by__mark{--flap:7.5s}
.about__mark{--flap:6.4s}

/* loader: rays arrive, then the flap takes over */
.loader__mark svg path{opacity:0}

/* Scoped to .wing path, not "svg path". The old selector also caught the
   ten .wmbody paths, which have no --ox/--oy of their own - they resolved to
   transform-origin 0 0, so the body sheared away from the wings as it span.
   The body only fades in; bodyBob on the group does its motion.

   Both layers now carry the --i stagger, so the rays arrive outward from the
   hinge and the flap keeps that phase instead of moving as one slab. */
.loader.is-run .loader__mark .wing path{
  animation:rayIn .5s var(--ease-out) both,
            feather var(--flap) cubic-bezier(.42,0,.58,1) infinite;
  animation-delay:calc(var(--i) * .055s), calc(.9s + var(--i) * .055s)}
.loader.is-run .loader__mark .wing--r path{animation-name:rayIn,featherR}
.loader.is-run .loader__mark .wmbody path{
  animation:rayIn .5s var(--ease-out) .25s both}

/* ───────── footer shader plate ───────── */
.foot{isolation:isolate}
.foot__stage{position:absolute;inset:0;z-index:0;pointer-events:none;overflow:hidden}
/* The rendered footer plate (Kling v3.0, from the client's own image.png).
   Same opt-in rule as the hero: preload="none", no autoplay attribute, and
   .vid-on only once it is actually playing - so the WebGL plate and its
   static fallback stay in charge under no JS, blocked autoplay, reduced
   motion and save-data, with no video bytes fetched in any of them. */
.foot__vid{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transition:opacity 1s var(--ease);pointer-events:none}

.foot__stage canvas{position:absolute;inset:0;width:100%;height:100%;display:block;
  opacity:0;transition:opacity 1.2s var(--ease)}
.foot__stage canvas.is-on{opacity:.85}
.foot__stage .fb{position:absolute;inset:0;background:url(/assets/bg-shader.jpg) center 60%/cover no-repeat;
  opacity:.5;transition:opacity .8s var(--ease)}
.foot__stage.has-gl .fb{opacity:0}
/* explicit layering: fallback still < WebGL plate < video < vignette */
.foot__stage .fb{z-index:1}
.foot__stage canvas{z-index:2}
.foot__vid{z-index:3}
.foot__stage .fv{z-index:4}
.foot__stage.vid-on .foot__vid{opacity:.92}
.foot__stage.vid-on canvas,.foot__stage.vid-on canvas.is-on,
.foot__stage.vid-on .fb,.foot__stage.has-gl.vid-on .fb{opacity:0}

.foot__stage .fv{position:absolute;inset:0;
  background:linear-gradient(180deg,var(--navy) 0%,rgba(4,15,32,.55) 22%,rgba(3,9,18,.72) 72%,rgba(3,9,18,.95) 100%)}
.foot__grid,.foot__wordmark,.foot__bar{position:relative;z-index:1}

@media (prefers-reduced-motion: reduce){
  .wing,.wing path,.wmbody{animation:none!important}
}

/* ===========================================================
   Page components — real-content build
   =========================================================== */
.nav__links a.is-active{color:var(--white)}

/* hero chips */
.chips{margin-top:30px;display:flex;flex-wrap:wrap;gap:8px}
.chips li{font-size:11px;font-weight:600;letter-spacing:.17em;padding:8px 15px;border-radius:999px;
  border:1px solid var(--line);color:var(--mist);background:rgba(174,193,209,.05);
  transition:border-color .35s,color .35s,background .35s}
.chips li:hover{border-color:var(--blue-br);color:var(--white);background:rgba(79,155,232,.13)}

/* inner-page hero */
.phero{position:relative;z-index:1;padding:calc(var(--nav-h) + clamp(70px,11vw,130px)) 0 clamp(40px,6vw,70px)}
.phero__title{font-size:clamp(32px,6.4vw,78px);font-weight:200;letter-spacing:-.045em;line-height:1.02;max-width:17ch}
.phero__title .w{display:inline-block;opacity:0;filter:blur(14px);transform:translateY(.3em) scale(.97);
  transition:opacity .9s var(--ease-out),filter .9s var(--ease-out),transform .9s var(--ease-out)}
.phero__title.is-in .w{opacity:1;filter:none;transform:none}
.phero__sub{margin-top:26px;max-width:72ch;font-size:clamp(15px,1.6vw,17.5px);font-weight:300;line-height:1.78}

/* client logo marquee */
.logos{position:relative;z-index:1;border-block:1px solid var(--line);padding:clamp(28px,4vw,44px) 0;overflow:hidden;
  background:rgba(4,13,27,.6);backdrop-filter:blur(8px);-webkit-backdrop-filter:blur(8px);
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
          mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent)}
.logos__track{display:flex;width:max-content;animation:slide 78s linear infinite}
.logos:hover .logos__track{animation-play-state:paused}
.logos__run{display:flex;align-items:center;gap:clamp(32px,4.5vw,64px);padding-right:clamp(32px,4.5vw,64px)}
.logos__run li{flex:0 0 auto}
/* Client marks render in full colour at rest. They were greyscaled and
   dimmed until hover, which on a dark ground made several of them almost
   invisible and hid whose logos these actually are. Hover now only scales. */
.logos__run img{width:clamp(92px,10vw,138px);height:auto;aspect-ratio:1;object-fit:contain;
  opacity:1;transition:transform .45s var(--ease)}
.logos__run img:hover{transform:scale(1.08)}

/* bento cards with real photography */
.bcard{display:flex;flex-direction:column}
.bcard__shot{position:relative;margin:-999px -999px 20px;margin:0 0 20px;border-radius:14px;overflow:hidden;
  aspect-ratio:16/10;background:rgba(3,9,18,.6)}
.bcard--lg .bcard__shot{aspect-ratio:16/11}
.bcard__shot img{width:100%;height:100%;object-fit:cover;
  filter:saturate(.78) brightness(.82) contrast(1.05);
  transition:transform .8s var(--ease),filter .6s var(--ease)}
.bcard__shot::after{content:"";position:absolute;inset:0;
  background:linear-gradient(170deg,rgba(47,111,183,.26),rgba(4,15,32,.5));mix-blend-mode:multiply}
.bcard:hover .bcard__shot img{transform:scale(1.06);filter:saturate(1) brightness(.95) contrast(1.03)}
.bcard h3{margin:0 0 10px}
.bcard p{margin-top:auto}
.sec__more{margin-top:clamp(28px,4vw,42px)}

/* portfolio panels with real screenshots */
/* cover, not contain: the screenshots are 2.25:1 and the open panel is 1.75:1,
   so `contain` letterboxed them and the panel gradient showed through the bands.
   `cover` + a centred crop fills the panel and keeps the shot centred. */

/* services page rows */
.srows{display:grid;gap:14px}
.srow{display:grid;grid-template-columns:auto 240px 1fr;gap:clamp(18px,3vw,34px);align-items:center;
  padding:clamp(18px,2.4vw,24px);border-radius:var(--r-lg);border:1px solid var(--line);
  background:linear-gradient(165deg,rgba(14,34,64,.6),rgba(7,18,36,.52));
  transition:border-color .45s var(--ease),transform .45s var(--ease)}
.srow:hover{border-color:rgba(126,192,255,.3);transform:translateY(-3px)}
.srow__no{font-size:11px;letter-spacing:.24em;color:var(--blue-br);font-weight:600}
.srow__media{border-radius:14px;overflow:hidden;aspect-ratio:4/3;background:rgba(3,9,18,.6)}
.srow__media img{width:100%;height:100%;object-fit:cover;filter:saturate(.8) brightness(.86);
  transition:transform .8s var(--ease)}
.srow:hover .srow__media img{transform:scale(1.06)}
.srow__copy h3{font-size:clamp(19px,2.3vw,27px);font-weight:500;letter-spacing:-.02em}
.srow__copy p{margin-top:11px;font-size:14.5px;font-weight:300;line-height:1.7;color:var(--mist);max-width:62ch}
@media(max-width:820px){
  /* stack: number, then image, then copy — a two-column grid left the
     copy in the auto column and the 1fr column empty at 0px */
  .srow{grid-template-columns:1fr;gap:16px}
  .srow__media{aspect-ratio:16/9}
  .srow__copy p{max-width:none}
}

/* services page pillar cards */
.pcards{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(265px,1fr))}
.pcard{padding:clamp(24px,3vw,32px);border-radius:var(--r-lg);border:1px solid var(--line);
  background:linear-gradient(170deg,rgba(11,26,46,.74),rgba(6,16,32,.66));
  transition:border-color .45s,transform .45s var(--ease)}
.pcard:hover{border-color:rgba(126,192,255,.3);transform:translateY(-4px)}
.pcard h3{font-size:clamp(17px,1.9vw,20px);font-weight:500;line-height:1.35;letter-spacing:-.015em}
.pcard h4{margin:18px 0 12px;font-size:11px;letter-spacing:.22em;text-transform:uppercase;
  color:var(--blue-br);font-weight:600}
.pcard ul{display:grid;gap:9px}
.pcard li{font-size:13.5px;font-weight:300;color:var(--mist);padding-left:20px;position:relative}
.pcard li::before{content:"";position:absolute;left:0;top:.58em;width:9px;height:1.5px;background:var(--blue-br)}

/* about page */
.about__photo{margin:0;border-radius:var(--r-lg);overflow:hidden;border:1px solid var(--line);
  background:rgba(3,9,18,.6);position:relative}
.about__photo::after{content:"";position:absolute;inset:0;
  background:linear-gradient(180deg,rgba(47,111,183,.16),rgba(4,15,32,.5));mix-blend-mode:multiply}
.about__photo img{width:100%;height:100%;object-fit:cover;aspect-ratio:4/5;filter:saturate(.86) contrast(1.04)}
.about .hero__stats{max-width:none}
.vm{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}
.vmcard{padding:clamp(26px,3.2vw,38px);border-radius:var(--r-lg);border:1px solid var(--line);
  background:linear-gradient(170deg,rgba(11,26,46,.74),rgba(6,16,32,.66))}
.vmcard h2{font-size:clamp(22px,2.6vw,30px);font-weight:400;letter-spacing:-.025em}
.vmcard p{margin-top:16px;font-size:14.5px;font-weight:300;line-height:1.78;color:var(--mist)}

/* testimonial cards hold profiles, not pull-quotes */
.quote blockquote{font-size:14.5px;font-weight:300;line-height:1.75;color:var(--mist)}
.quote figcaption{margin-top:20px}

/* ===========================================================
   Short viewports — landscape phones, split-screen tablets.
   The full-height hero costs too much scroll when there is
   only ~390px of height to spend.
   =========================================================== */
@media (max-height:540px) and (orientation:landscape){
  .hero{min-height:auto;padding:calc(var(--nav-h) + 26px) 0 54px}
  .hero__title{font-size:clamp(30px,6vw,52px)}
  .hero__sub{margin-top:18px}
  .hero__cta{margin-top:24px}
  .hero__stats{margin-top:28px;padding-top:18px}
  .phero{padding:calc(var(--nav-h) + 42px) 0 34px}
  .scrollcue{display:none}
  .drawer{align-content:start;padding-top:calc(var(--nav-h) + 12px);overflow-y:auto}
  .drawer nav a{font-size:clamp(19px,4.6vw,24px);min-height:48px;padding-left:22px}
}

/* Tall-but-narrow safety: never let the deck exceed the screen */
@media (max-width:820px){
}

/* Notched devices — keep content clear of the home indicator / cutouts */
@supports(padding:max(0px)){
  .nav__inner{padding-left:max(0px,env(safe-area-inset-left));padding-right:max(0px,env(safe-area-inset-right))}
  .nav.is-stuck .nav__inner{padding-left:max(clamp(14px,2.4vw,22px),env(safe-area-inset-left));
    padding-right:max(clamp(14px,2.4vw,22px),env(safe-area-inset-right))}
  .foot__bar{padding-bottom:env(safe-area-inset-bottom)}
  .drawer{padding-left:max(var(--pad),env(safe-area-inset-left));
    padding-right:max(var(--pad),env(safe-area-inset-right))}
}

/* ===========================================================
   Hero carousel — Welkin's own brand imagery
   =========================================================== */
.hero{position:relative;overflow:hidden}
/* ---- hero: the light-wings plate, flapping ----
   Scaling the whole banner moved the background with the wings, which read as
   a zoom rather than a beat. The plate is keyed to a matte instead
   (assets/hero/wings-mask.png, cut out of the supplied PNG by luminance in
   tools/wings_matte.py), so the artwork is a shape rather than a rectangle
   and its colour is a CSS gradient we can retune without re-exporting.

   The matte is greyscale + ALPHA on purpose: CSS masks read the alpha channel
   by default, so a plain greyscale PNG masks nothing at all.

   Three layers share that one matte:
     .hero__plate  everything EXCEPT the wings - ground line, outer rays,
                   ambient haze. Static, because none of that flaps.
     .hero__wing   the wings alone, isolated by an elliptical window centred
                   on the point where they converge, split down the middle and
                   hinged there.
   plate and wing carry opposite windows (--cut / --win), so together they
   still add up to the supplied composition.

   The halves are separated by soft mask edges (--seamL / --seamR), not a hard
   clip. A clipped edge opens a wedge on one side of the hinge and doubles up
   on the other; overlapping clips then painted a bright stripe straight down
   the middle. Fading them into each other cross-dissolves instead, so the
   seam has nothing to show at any angle.

   The hero box is taller than the viewport (see SKILL.md), so this is pinned
   to the first viewport or the convergence point lands below the fold. --oy
   is that point: hinge, glow centre and window centre at once.

   NOTE: do not name these .wing* - mark.svg ships groups with those classes. */
.hero__wings{position:absolute;left:0;right:0;top:0;height:100vh;height:100svh;
  --oy:80%;
  --win:radial-gradient(44% 58% at 50% var(--oy),#000 44%,transparent 96%);
  --cut:radial-gradient(44% 58% at 50% var(--oy),transparent 44%,#000 96%);
  --seamL:linear-gradient(90deg,#000 0 45%,transparent 53%);
  --seamR:linear-gradient(90deg,transparent 47%,#000 55%);
  --lit:radial-gradient(56% 50% at 50% var(--oy),#fff 0%,#EAF4FF 11%,#A8D4FF 29%,#4E9BE8 57%,#2F6FB7 84%,#214E82 100%);
  z-index:0;pointer-events:none;overflow:hidden}

/* no mask support: show the supplied plate as-is rather than bare gradients.
   It sits on the container so the sizing rules below can move it without
   also moving the gradient the masked version paints. */
.hero__wings{background:url(/assets/hero/wings.jpg) center center/cover no-repeat}
.hero__plate,.hero__wing{display:none}

@supports (mask-image:url(#m)) or (-webkit-mask-image:url(#m)){
  .hero__wings{background:none}

  .hero__plate,.hero__wing{display:block;position:absolute;inset:0;
    background:var(--lit);
    -webkit-mask-size:var(--ms,cover),100% 100%,100% 100%;
            mask-size:var(--ms,cover),100% 100%,100% 100%;
    -webkit-mask-position:var(--mp,center),center,center;
            mask-position:var(--mp,center),center,center;
    -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
    -webkit-mask-composite:source-in;mask-composite:intersect}

  .hero__plate{opacity:.92;
    -webkit-mask-image:url(/assets/hero/wings-mask.png),var(--cut),var(--cut);
            mask-image:url(/assets/hero/wings-mask.png),var(--cut),var(--cut)}

  .hero__wing{transform-origin:50% var(--oy);backface-visibility:hidden;
    animation:wingL 4.6s cubic-bezier(.4,.04,.28,1) infinite}
  .hero__wing--l{-webkit-mask-image:url(/assets/hero/wings-mask.png),var(--win),var(--seamL);
                         mask-image:url(/assets/hero/wings-mask.png),var(--win),var(--seamL)}
  .hero__wing--r{animation-name:wingR;
    -webkit-mask-image:url(/assets/hero/wings-mask.png),var(--win),var(--seamR);
            mask-image:url(/assets/hero/wings-mask.png),var(--win),var(--seamR)}
}

/* The rendered wingbeat (Kling v3.0, off the same supplied banner). It layers
   OVER the masked build rather than replacing it: the CSS plate is the
   poster, the no-JS answer, the blocked-autoplay answer, the
   prefers-reduced-motion answer, and the portrait answer - a 16:9 clip
   object-fit:cover in a tall phone viewport crops the wing tips away, which
   the mask handles properly. widgets.js only adds .vid-on once the file is
   actually playing, so every one of those paths degrades to the plate with
   nothing missing. */
.hero__vid{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transition:opacity 1s var(--ease);pointer-events:none}
.hero__wings.vid-on .hero__vid{opacity:.95}
.hero__wings.vid-on .hero__plate,
.hero__wings.vid-on .hero__wing,
.hero__wings.vid-on .hero__core{opacity:0;animation-play-state:paused}

.hero__core{position:absolute;left:50%;top:var(--oy);width:min(42vw,520px);aspect-ratio:1;
  transform:translate(-50%,-50%);border-radius:50%;mix-blend-mode:screen;
  background:radial-gradient(circle,rgba(176,220,255,.40) 0%,rgba(79,155,232,.14) 32%,transparent 64%);
  animation:heroCore 4.6s cubic-bezier(.4,.04,.28,1) infinite}

/* fast up-stroke, slower recovery - a beat, not a metronome */
@keyframes wingL{
  0%,100%{transform:rotate(0)       scaleY(1)}
  12%    {transform:rotate(4deg)    scaleY(1.035)}
  40%    {transform:rotate(-2.4deg) scaleY(.984)}
  68%    {transform:rotate(1.1deg)  scaleY(1.01)}
  88%    {transform:rotate(-.4deg)  scaleY(.997)}
}
@keyframes wingR{
  0%,100%{transform:rotate(0)       scaleY(1)}
  12%    {transform:rotate(-4deg)   scaleY(1.035)}
  40%    {transform:rotate(2.4deg)  scaleY(.984)}
  68%    {transform:rotate(-1.1deg) scaleY(1.01)}
  88%    {transform:rotate(.4deg)   scaleY(.997)}
}
@keyframes heroCore{
  0%,100%{opacity:.72;transform:translate(-50%,-50%) scale(1)}
  12%    {opacity:1;  transform:translate(-50%,-50%) scale(1.15)}
  68%    {opacity:.84;transform:translate(-50%,-50%) scale(1.04)}
}
@media(prefers-reduced-motion:reduce){
  .hero__wing,.hero__core{animation:none}
}

/* The old scrim was heavily left-weighted because the backdrop was a photo.
   The plate is symmetrical, so this keeps contrast under the left-aligned
   copy without cutting the composition in half. */
.hero__scrim{position:absolute;inset:0;
  background:
    linear-gradient(100deg,rgba(4,12,26,.74) 0%,rgba(4,12,26,.44) 30%,rgba(4,12,26,.1) 62%,rgba(4,12,26,.02) 100%),
    linear-gradient(180deg,rgba(4,15,32,.8) 0%,transparent 24%,transparent 54%,var(--navy) 100%)}

/* Keyed to ASPECT, not width: the thing being fixed is "viewport much taller
   than a 16:9 plate", which a landscape 800px pane does not have and a
   portrait tablet does. Width breakpoints got this backwards and zoomed the
   plate to 190% on an ordinary small laptop. Below, the plate is sized to the
   width and floated as a band, and --oy moves to where the wings now meet. */
@media(max-aspect-ratio:4/5){
  .hero__wings{--oy:56%;--ms:200% auto;--mp:center 28%;
    background-size:200% auto;background-position:center 28%}
  .hero__plate{opacity:.85}
  .hero__core{width:62vw}
}
@media(max-width:900px){
  .hero__scrim{background:linear-gradient(180deg,rgba(4,12,26,.7) 0%,rgba(4,12,26,.44) 30%,rgba(4,12,26,.9) 76%,var(--navy) 100%)}
}

.hero .shell{position:relative;z-index:2}

/* ===========================================================
   WhatsApp float
   =========================================================== */
.wa{position:fixed;z-index:130;right:max(18px,env(safe-area-inset-right));
  bottom:max(18px,env(safe-area-inset-bottom));display:flex;flex-direction:column;align-items:flex-end;gap:12px}
.wa__fab{width:56px;height:56px;border-radius:50%;border:0;cursor:pointer;
  background:#25D366;color:#042a14;display:grid;place-items:center;
  box-shadow:0 10px 28px -8px rgba(37,211,102,.7),0 4px 14px rgba(0,0,0,.45);
  transition:transform .3s var(--ease),box-shadow .3s}
.wa__fab svg{width:32px;height:32px}
.wa__fab:hover{transform:translateY(-3px) scale(1.04);box-shadow:0 16px 36px -8px rgba(37,211,102,.85)}
.wa__fab::after{content:"";position:absolute;inset:0;border-radius:50%;border:2px solid #25D366;
  animation:waRing 2.8s var(--ease) infinite;pointer-events:none}
@keyframes waRing{0%{transform:scale(1);opacity:.55}70%{transform:scale(1.5);opacity:0}100%{opacity:0}}
.wa__panel{width:min(312px,calc(100vw - 36px));padding:18px;border-radius:18px;
  border:1px solid var(--line);background:rgba(9,22,41,.96);
  backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);
  box-shadow:0 26px 60px -22px rgba(0,0,0,.95);
  transform-origin:bottom right;animation:waIn .3s var(--ease-out)}
@keyframes waIn{from{opacity:0;transform:translateY(10px) scale(.96)}to{opacity:1;transform:none}}
.wa__head{display:grid;gap:3px;padding-bottom:12px;border-bottom:1px solid var(--line)}
.wa__head strong{color:var(--white);font-size:15px;font-weight:600}
.wa__head span{font-size:11.5px;color:var(--dim)}
.wa__hint{margin:12px 0 10px;font-size:12.5px;color:var(--mist);font-weight:300;line-height:1.55}
.wa__opts{display:grid;gap:6px}
.wa__opts a{display:block;padding:11px 13px;border-radius:11px;font-size:13.5px;font-weight:400;
  color:var(--white);text-decoration:none;border:1px solid var(--line);background:rgba(174,193,209,.05);
  transition:border-color .25s,background .25s,transform .25s var(--ease)}
.wa__opts a:hover{border-color:#25D366;background:rgba(37,211,102,.14);transform:translateX(3px)}
.wa__fab{position:relative}
@media(max-width:700px){.wa__fab{width:52px;height:52px}.wa__fab svg{width:29px;height:29px}}

/* ===========================================================
   Social feeds
   =========================================================== */
.feeds{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(290px,1fr));align-items:start}
.feed{border-radius:var(--r-lg);border:1px solid var(--line);overflow:hidden;
  background:linear-gradient(170deg,rgba(11,26,46,.76),rgba(6,16,32,.68));
  transition:border-color .45s var(--ease)}
.feed:hover{border-color:rgba(126,192,255,.3)}
.feed header{display:flex;align-items:center;gap:12px;padding:18px 20px;border-bottom:1px solid var(--line)}
.feed__ico{width:34px;height:34px;flex:0 0 auto;border-radius:10px;display:grid;place-items:center;
  background:rgba(79,155,232,.14);color:var(--blue-br)}
.feed__ico svg{width:19px;height:19px}
.feed header h3{font-size:14px;font-weight:600;letter-spacing:.01em}
.feed header a{font-size:12px;color:var(--dim);text-decoration:none;
  min-height:24px;display:inline-flex;align-items:center}
.feed header a:hover{color:var(--blue-br)}
.feed__body{min-height:150px;padding:16px}
.feed__body iframe{width:100%;border:0;display:block;border-radius:10px;background:#0b1a2e}
.feed__note{font-size:13px;font-weight:300;color:var(--mist);line-height:1.65}
.feed__note a{color:var(--blue-br)}
.feed__cta{margin-top:14px}

/* ===========================================================
   EDITORIAL PASS
   Removing the generic-template signatures: animated gradient
   text, pulsing dot eyebrows, uniform glassmorphism, soft
   24px radii everywhere, and decorative line icons. The brand
   plate and the real photography carry the page instead.
   =========================================================== */

/* 1 — flat, confident headline. No animated gradient sheen. */
.grad{background:none;-webkit-background-clip:border-box;background-clip:border-box;
  color:var(--blue-br);animation:none}

/* 2 — eyebrow becomes a rule and a label, not a blinking dot */
.eyebrow{letter-spacing:.22em;color:var(--mist)}

/* 3 — panels are flat and ruled, not frosted glass */
.bcard,.quote,.vmcard,.pcard,.srow,.form,.tier,.feed,.mcard{
  backdrop-filter:none;-webkit-backdrop-filter:none;
  background:#071526;border-color:rgba(174,193,209,.16)}
.bcard:hover,.pcard:hover,.srow:hover,.tile:hover,.feed:hover{
  background:#0A1C33;border-color:rgba(126,192,255,.38);transform:none}
.tile{background:#071526}
.nav.is-stuck .nav__inner{backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px)}

/* 4 — tighter, editorial geometry. Pills stay on buttons only. */
:root{--r:4px;--r-lg:6px}
.bcard__shot,.srow__media,.feed__body iframe,.about__photo{border-radius:4px}
.bcard__shot{border-radius:3px}

/* 5 — the invented line icons go; numerals and rules do the work */
.bcard__glyph{display:none}
.bcard__no,.srow__no,.steps span{color:var(--dim);font-weight:500;letter-spacing:.2em}
.bcard h3,.srow__copy h3{letter-spacing:-.03em}

/* 6 — give the cards a hairline top rule so the grid reads as a system */
.bcard::after{content:"";position:absolute;left:0;right:0;top:0;height:1px;
  background:linear-gradient(90deg,var(--blue),transparent 70%);opacity:.6}
.bcard{position:relative}

/* 7 — remove the halo on the about mark; the guidelines forbid effects on it */
.about__mark svg{filter:none}
.about__art::before{display:none}

/* ===========================================================
   Icon system — one sprite, currentColor, 1.5px strokes.
   Geometry follows the mark's angular rays, not stock line art.
   =========================================================== */
.sprite{position:absolute;width:0;height:0;overflow:hidden}
.ic{width:22px;height:22px;display:block;color:var(--blue-br);flex:0 0 auto}
.ic--tile{width:24px;height:24px;margin-bottom:4px}

.bcard__ico{display:block;margin-bottom:16px}
.bcard__ico .ic{width:26px;height:26px}
.bcard--lg .bcard__ico .ic{width:32px;height:32px}
.bcard:hover .ic{color:var(--glow)}
.bcard .ic,.srow .ic,.tile .ic{transition:color .35s var(--ease),transform .35s var(--ease)}
.bcard:hover .bcard__ico .ic{transform:translateY(-2px)}

.srow{grid-template-columns:auto auto 240px 1fr}
.srow__ico{display:grid;place-items:center;width:40px;height:40px;border-radius:4px;
  border:1px solid var(--line);background:rgba(79,155,232,.07)}
.srow:hover .srow__ico{border-color:rgba(126,192,255,.4)}
@media(max-width:820px){.srow{grid-template-columns:auto auto 1fr}}
@media(max-width:560px){.srow{grid-template-columns:auto 1fr}.srow__ico{display:none}}

.tile{position:relative}
.tile .ic--tile{color:var(--blue-br)}
.tile:hover .ic{color:var(--glow)}

/* ───────── contact page ───────── */
.ctl{margin-top:34px;display:grid;gap:2px;border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.ctl li{padding:22px 24px;background:#071526;display:grid;gap:5px}
.ctl h3{font-size:12px;letter-spacing:.2em;text-transform:uppercase;color:var(--blue-br);font-weight:600}
.ctl p{font-size:13.5px;font-weight:300;color:var(--dim)}
.ctl a{font-size:16px;color:var(--white);text-decoration:none;font-weight:400;
  min-height:26px;display:inline-flex;align-items:center;width:max-content}
.ctl a:hover{color:var(--blue-br)}
.ctl address{font-style:normal;font-size:14px;font-weight:300;line-height:1.6;color:var(--mist)}

.faqs{display:grid;gap:2px;border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.faq{background:#071526}
.faq summary{cursor:pointer;list-style:none;padding:20px 56px 20px 24px;position:relative;
  font-size:15px;font-weight:400;color:var(--white);transition:background .25s,color .25s}
.faq summary::-webkit-details-marker{display:none}
.faq summary:hover{background:#0A1C33}
.faq summary::after{content:"";position:absolute;right:24px;top:50%;width:11px;height:11px;
  border-right:1.5px solid var(--blue-br);border-bottom:1.5px solid var(--blue-br);
  transform:translateY(-70%) rotate(45deg);transition:transform .3s var(--ease)}
.faq[open] summary::after{transform:translateY(-30%) rotate(-135deg)}
.faq[open] summary{color:var(--blue-br)}
.faq p{padding:0 24px 22px;font-size:14.5px;font-weight:300;line-height:1.75;color:var(--mist);max-width:78ch}
.sec--faq .sec__lede a{margin-top:6px}

/* ===========================================================
   Hero vertical rhythm — tightened so the hero fits one screen
   on common laptop heights instead of overflowing by ~270px.
   =========================================================== */
.hero{padding:calc(var(--nav-h) + 34px) 0 72px}
.hero__sub{margin-top:20px}
.chips{margin-top:22px}
.hero__cta{margin-top:26px}
.hero__stats{margin-top:clamp(26px,4vw,48px);padding-top:20px}
@media(min-height:860px){
  .hero{padding:calc(var(--nav-h) + 48px) 0 96px}
  .hero__stats{margin-top:clamp(40px,6vw,68px)}
}

/* ───────── contact map ───────── */
.map{margin:22px 0 0;max-width:420px}
.map h3{font-size:11px;letter-spacing:.24em;text-transform:uppercase;color:var(--blue-br);
  font-weight:600;margin-bottom:12px}
.map__frame{border:1px solid var(--line);border-radius:var(--r);overflow:hidden;
  background:#071526;line-height:0}
.map__frame iframe{width:100%;height:220px;border:0;display:block}
.map figcaption{margin-top:10px}
.map figcaption a{font-size:13px;color:var(--mist);text-decoration:none;
  display:inline-flex;align-items:center;gap:7px;min-height:26px}
.map figcaption a::after{content:"→";color:var(--blue-br);transition:transform .25s var(--ease)}
.map figcaption a:hover{color:var(--white)}
.map figcaption a:hover::after{transform:translateX(3px)}
@media(max-width:860px){.map{max-width:none}.map__frame iframe{height:200px}}

/* ===========================================================
   Portfolio — index grid and case-study pages
   =========================================================== */
.works{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}
.work{display:block;text-decoration:none;border:1px solid var(--line);border-radius:var(--r-lg);
  overflow:hidden;background:#071526;transition:border-color .4s var(--ease),transform .4s var(--ease)}
.work:hover{border-color:rgba(126,192,255,.4);transform:translateY(-4px)}
.work__shot{display:block;aspect-ratio:16/9;overflow:hidden;background:#04101f}
.work__shot img{width:100%;height:100%;object-fit:cover;object-position:center top;
  transition:transform .8s var(--ease);filter:saturate(.9) brightness(.9)}
.work:hover .work__shot img{transform:scale(1.04);filter:none}
.work__meta{display:grid;gap:6px;padding:20px 22px}
.work__sector{font-size:10.5px;letter-spacing:.22em;text-transform:uppercase;color:var(--blue-br);font-weight:600}
.work__name{font-size:clamp(19px,2.2vw,23px);color:var(--white);font-weight:500;letter-spacing:-.02em}
.pcard__soon{font-size:12px;color:var(--dim);font-weight:300}

.phero--cs .phero__title{max-width:none}
.cs__shot{margin:0 0 clamp(34px,5vw,56px);border:1px solid var(--line);border-radius:var(--r-lg);
  overflow:hidden;background:#04101f;line-height:0}
.cs__shot img{width:100%;height:auto;display:block}
.cs{display:grid;gap:clamp(30px,5vw,60px);grid-template-columns:1.6fr .9fr;align-items:start}
.cs__body{display:grid;gap:clamp(26px,3.5vw,38px)}
.cs__block h2{font-size:clamp(19px,2.3vw,25px);font-weight:500;letter-spacing:-.025em;margin-bottom:12px}
.cs__block p{font-size:15px;font-weight:300;line-height:1.8;color:var(--mist);max-width:68ch}
.cs__facts{display:grid;gap:2px;border:1px solid var(--line);border-radius:var(--r);overflow:hidden}
.cs__facts div{padding:16px 20px;background:#071526;display:grid;gap:4px}
.cs__facts dt{font-size:10.5px;letter-spacing:.22em;text-transform:uppercase;color:var(--blue-br);font-weight:600}
.cs__facts dd{font-size:14.5px;color:var(--white);font-weight:300}
@media(max-width:900px){.cs{grid-template-columns:1fr}}

.nxts{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(220px,1fr))}
.nxt{display:grid;gap:5px;padding:22px 24px;border:1px solid var(--line);border-radius:var(--r);
  background:#071526;text-decoration:none;transition:border-color .4s,transform .4s var(--ease)}
.nxt:hover{border-color:rgba(126,192,255,.4);transform:translateY(-3px)}
.nxt span{font-size:10.5px;letter-spacing:.22em;text-transform:uppercase;color:var(--blue-br);font-weight:600}
.nxt strong{font-size:17px;color:var(--white);font-weight:400;letter-spacing:-.02em}


/* the inline Portfolio link in a case-study eyebrow needs a real hit area */
.phero--cs .eyebrow a{min-height:26px;display:inline-flex;align-items:center}

/* ===========================================================
   SERVICES — rebuilt around the real source images.
   They are ~900x900 squares; the old cards cropped them into
   16:10 banners, which both wasted the frame and under-resolved
   it. Square frames use the whole photograph and land ~2.5x on
   a retina screen. One wide feature card breaks the grid so the
   row reads as a composition, not a tiled template.
   =========================================================== */


/* numerals dropped from the service cards and rows at the client's request */
.srow{grid-template-columns:auto 240px 1fr}
@media(max-width:820px){.srow{grid-template-columns:auto 1fr}}
@media(max-width:560px){.srow{grid-template-columns:1fr}}

/* ===========================================================
   SOCIAL — three designed channel cards.
   Replaces the Facebook Page Plugin: a 500px light-themed
   cross-origin widget that could not be styled to match a dark
   page, left the grid lopsided when the X card self-removed,
   and pulled Meta tracking into every page view.
   =========================================================== */
.chans{display:grid;gap:14px;grid-template-columns:repeat(auto-fit,minmax(252px,1fr))}
/* Card chrome lives on the inner anchor, NOT on .chan.
   .chan shares its element with .fan__card, and `.chan:hover` at (0,2,0)
   beat `.fan__card` at (0,1,0) - so on hover its `transform:translateY(-4px)`
   replaced the fan's translate/rotate/scale outright. The card snapped out
   from under the pointer, un-hovered, snapped back, re-hovered: a continuous
   shake for as long as the pointer stayed near it.

   Nothing below changes geometry on hover, deliberately. Any transform on a
   hovered card can move it off the pointer and restart that loop; the fan
   already lifts the focused card, which is the affordance that matters. */
.chan{display:block;text-decoration:none}
.chan__hit{position:relative;overflow:hidden;display:grid;gap:7px;align-content:start;
  padding:clamp(20px,2.4vw,28px);border:1px solid var(--line);border-radius:var(--r-lg);
  background:#071526;text-decoration:none;color:inherit;min-height:100%;
  transition:border-color .45s var(--ease),background .45s}
.chan__hit:hover,.chan__hit:focus-visible{border-color:rgba(126,192,255,.42);background:#0A1C33}

/* the ray motif from the mark, fanning from the corner */
.chan__rays{position:absolute;inset:0;pointer-events:none;opacity:.14;
  background:
    repeating-conic-gradient(from 210deg at 108% -8%,
      var(--blue-br) 0deg 1.1deg, transparent 1.1deg 5.5deg);
  -webkit-mask-image:radial-gradient(120% 90% at 100% 0%,#000,transparent 72%);
          mask-image:radial-gradient(120% 90% at 100% 0%,#000,transparent 72%);
  transition:opacity .5s var(--ease),transform .9s var(--ease)}
.chan__hit:hover .chan__rays{opacity:.3;transform:scale(1.08) rotate(-2deg)}

.chan__ico{width:46px;height:46px;display:grid;place-items:center;border-radius:4px;
  border:1px solid var(--line);background:rgba(79,155,232,.09);margin-bottom:8px;
  transition:border-color .4s,background .4s,transform .4s var(--ease)}
.chan__ico .ic{width:23px;height:23px}
.chan__hit:hover .chan__ico{border-color:rgba(126,192,255,.45);background:rgba(79,155,232,.18);
  transform:translateY(-2px)}
.chan__name{font-size:19px;font-weight:500;color:var(--white);letter-spacing:-.02em}
.chan__handle{font-size:12.5px;color:var(--blue-br);font-weight:500;letter-spacing:.02em}
.chan__desc{margin-top:6px;font-size:13.5px;font-weight:300;line-height:1.65;color:var(--mist)}
.chan__go{margin-top:16px;display:inline-flex;align-items:center;gap:8px;min-height:26px;
  font-size:12px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--white)}
.chan__go::after{content:"→";color:var(--blue-br);transition:transform .3s var(--ease)}
.chan__hit:hover .chan__go::after{transform:translateX(5px)}

/* ===========================================================
   Reels — auto-advancing slide rows (client social work,
   our own posts, case-study photography)
   --v is the slides-per-view contract read by widgets.js,
   so the breakpoints live here and the script stays generic.
   =========================================================== */
/* min-width:0 matters: a reel inside a grid or flex parent would otherwise
   size that track to the track's intrinsic width - every slide laid end to
   end - and push the whole page sideways. */
.reel{--v:3;position:relative;min-width:0;max-width:100%}
.reel__win{overflow:hidden;border-radius:var(--r);max-width:100%}
.reel__track{display:flex;gap:14px;margin:0;padding:0;list-style:none;
  transition:transform .56s var(--ease-out);will-change:transform}
.reel__slide{flex:0 0 calc((100% - 14px * (var(--v) - 1)) / var(--v));
  margin:0;border:1px solid var(--line);border-radius:var(--r);overflow:hidden;
  background:#04101f;line-height:0}
.reel__slide img{width:100%;height:100%;object-fit:cover;display:block;
  filter:saturate(.96) brightness(.94);transition:filter .5s var(--ease),transform .7s var(--ease)}
.reel__slide:hover img{filter:none;transform:scale(1.03)}
.reel--portrait .reel__slide{aspect-ratio:4/5}
.reel--square   .reel__slide{aspect-ratio:1/1}
.reel--wide     .reel__slide{aspect-ratio:16/9}
.reel.is-static .reel__track{transform:none !important;justify-content:flex-start}
.reel.is-static .reel__slide.is-clone{display:none}

.reel__ctrls{display:flex;align-items:center;gap:8px;margin-top:14px}
.reel__arw,.reel__pause{display:inline-flex;align-items:center;justify-content:center;
  width:36px;height:36px;min-width:36px;padding:0;border:1px solid var(--line-2);
  border-radius:999px;background:rgba(7,21,38,.7);color:var(--mist);cursor:pointer;
  transition:border-color .3s,color .3s,background .3s}
.reel__arw:hover,.reel__pause:hover{border-color:rgba(126,192,255,.5);color:var(--white);
  background:rgba(14,34,64,.9)}
.reel__arw svg{width:17px;height:17px}
.reel__pause .pz{width:9px;height:11px;border-left:2.5px solid currentColor;
  border-right:2.5px solid currentColor}
.reel__pause[aria-pressed="true"] .pz{width:0;height:0;border:none;
  border-left:9px solid currentColor;border-top:6px solid transparent;
  border-bottom:6px solid transparent;margin-left:2px}
.reel__status{position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}

@media(max-width:900px){.reel{--v:2}}
@media(max-width:560px){.reel{--v:1}.reel__track{gap:12px}
  .reel__slide{flex-basis:calc((100% - 12px * (var(--v) - 1)) / var(--v))}}

/* ---------- client rows on the portfolio page ---------- */
.smm{display:grid;grid-template-columns:minmax(0,1fr);gap:clamp(40px,6vw,72px)}
.smm__row{display:grid;grid-template-columns:minmax(0,1fr);gap:18px;min-width:0}
.smm__head{display:flex;flex-wrap:wrap;align-items:center;gap:8px 16px}
/* The client's own mark, where we can identify it with certainty. Sits on a
   panel because several of these logos are white-on-transparent and would
   otherwise disappear into the navy. */
.smm__logo{flex:0 0 auto;display:flex;align-items:center;justify-content:center;
  width:60px;height:38px;padding:5px 8px;border:1px solid var(--line);
  border-radius:8px;background:rgba(255,255,255,.055)}
.smm__logo img{max-width:100%;max-height:100%;object-fit:contain;display:block}
.smm__name{font-size:clamp(20px,2.4vw,27px);color:var(--white);font-weight:500;letter-spacing:-.025em}
.smm__sector{font-size:10.5px;letter-spacing:.22em;text-transform:uppercase;
  color:var(--blue-br);font-weight:600}
.smm__count{font-size:12.5px;color:var(--dim);font-weight:300;margin-left:auto}
/* inline-block + padding so the hit area clears the project's 24px floor
   even when this sits mid-sentence as a text link */
.smm__link{display:inline-block;font-size:13px;font-weight:600;color:var(--white);
  text-decoration:none;line-height:1.5;padding:3px 0;
  border-bottom:1px solid rgba(126,192,255,.45)}
.smm__link:hover{border-bottom-color:var(--glow)}
@media(max-width:560px){.smm__count{margin-left:0;width:100%}}

/* ---------- case study: how the work came about ---------- */
.story{display:grid;gap:clamp(26px,4vw,40px);counter-reset:st}
.story__step{display:grid;grid-template-columns:auto 1fr;gap:clamp(16px,3vw,28px);align-items:start}
.story__n{counter-increment:st;display:inline-flex;align-items:center;justify-content:center;
  width:44px;height:44px;border-radius:999px;border:1px solid rgba(126,192,255,.34);
  background:rgba(14,34,64,.6);color:var(--glow);font-size:14px;font-weight:600}
.story__n::before{content:counter(st,decimal-leading-zero)}
.story__b h3{font-size:clamp(17px,2.1vw,21px);font-weight:500;letter-spacing:-.025em;margin-bottom:9px}
.story__b p{font-size:15px;font-weight:300;line-height:1.8;color:var(--mist);max-width:66ch}
@media(max-width:560px){.story__step{grid-template-columns:1fr;gap:12px}
  .story__n{width:38px;height:38px;font-size:13px}}

.csx{display:grid;grid-template-columns:minmax(0,1fr);gap:clamp(54px,8vw,104px)}
.csx__entry{display:grid;grid-template-columns:minmax(0,1fr);min-width:0;gap:clamp(22px,3.4vw,34px);
  padding-bottom:clamp(44px,6vw,72px);border-bottom:1px solid var(--line)}
.csx__entry:last-child{border-bottom:0;padding-bottom:0}
.csx__banner{margin:0;border:1px solid var(--line);border-radius:var(--r-lg);
  overflow:hidden;background:#04101f;line-height:0}
.csx__banner img{width:100%;height:auto}
.csx__soon{font-size:14.5px;font-weight:300;color:var(--dim);line-height:1.75;max-width:62ch}

/* ---------- our own posts ---------- */
.posts__note{font-size:13px;color:var(--dim);font-weight:300;margin-top:12px}
.cs__sub{font-size:clamp(16px,2vw,20px);font-weight:500;letter-spacing:-.02em;
  color:var(--white);margin:0 0 14px}

/* ===========================================================
   Services + testimonials — motion
   Everything here animates transform/opacity/filter only, so
   none of it touches layout, and all of it stops dead under
   prefers-reduced-motion at the end of this block.
   =========================================================== */


/* ---------- testimonials ---------- */
/* Decorative brand light, not a person. No stock faces and no generated
   ones: the quotes below are unverified and inventing a face for them
   would compound that. See SKILL.md. */
.quote{position:relative;overflow:hidden;isolation:isolate}
.quote::after{content:"";position:absolute;inset:-40% -10% auto -10%;height:170%;
  z-index:-1;pointer-events:none;opacity:.17;
  background:url(/assets/bg-shader.jpg) center 40%/cover no-repeat;
  mix-blend-mode:screen;
  animation:quoteDrift 18s ease-in-out infinite alternate}
.quotes .quote:nth-child(2)::after{animation-delay:-6s}
.quotes .quote:nth-child(3)::after{animation-delay:-12s}
@keyframes quoteDrift{
  from{transform:translate3d(-2.5%,0,0) scale(1.06)}
  to  {transform:translate3d(2.5%,-2%,0) scale(1.14)}
}

/* the quote glyph breathes instead of sitting there as dead ornament */
.quote::before{animation:quoteGlow 7s var(--ease) infinite}
@keyframes quoteGlow{
  0%,100%{opacity:.1;transform:translateY(0)}
  50%    {opacity:.2;transform:translateY(-4px)}
}
.quote{transition:transform .5s var(--ease-out),border-color .5s}
.quote:hover{transform:translateY(-5px);border-color:rgba(126,192,255,.4)}
.quote:hover::after{opacity:.26}

@media (prefers-reduced-motion: reduce){
  .quote::after,.quote::before{animation:none}
  .quote:hover{transform:none}
}

/* ---------- footer and form glyphs ----------
   The footer was three columns of bare text links. Each link now carries a
   glyph from the same sprite the drawer uses, so the two navigations read as
   one system. The icon is the hover affordance: it brightens and steps right
   while the label underlines. */
.foot__nav a,.foot__contact a,.foot__addr{
  display:flex;align-items:center;gap:10px;min-height:30px}
.fic{width:16px;height:16px;flex:0 0 16px;color:var(--blue-br);opacity:.6;
  transition:opacity .3s var(--ease),transform .3s var(--ease-out)}
.foot__nav a:hover .fic,.foot__contact a:hover .fic,
.foot__nav a:focus-visible .fic,.foot__contact a:focus-visible .fic{
  opacity:1;transform:translateX(2px)}
.foot__addr{color:var(--dim);align-items:flex-start;margin:0}
.foot__addr .fic{margin-top:4px}
.foot__addr address{font-style:normal}

/* form labels: a glyph sized to the label, never inside the input where it
   would collide with the value or the caret */
.field label{display:flex;align-items:center;gap:8px}
.lic{width:14px;height:14px;flex:0 0 14px;color:var(--blue-br);opacity:.65;
  transition:opacity .3s}
.field:focus-within .lic{opacity:1}

/* ---------- client name strip ----------
   Runs opposite the logo row so the two read as one band rather than a
   repeat. Only the 26 identified clients appear here; see CLIENT_NAMES. */
.logos__track--names{margin-top:14px}
.logos__run--names{display:flex;align-items:center;gap:0;list-style:none;margin:0;padding:0}
.logos__run--names li{flex:0 0 auto;padding:0 26px;position:relative;
  font-size:12px;letter-spacing:.2em;text-transform:uppercase;font-weight:500;
  color:var(--dim);white-space:nowrap}
.logos__run--names li::after{content:"";position:absolute;right:0;top:50%;
  transform:translateY(-50%);width:3px;height:3px;border-radius:50%;
  background:var(--rule)}
.logos__track--names .logos__run{animation-direction:reverse;animation-duration:64s}
@media(max-width:700px){.logos__run--names li{font-size:11px;padding:0 18px}}

/* ---------- stat glyphs ----------
   Sits beside the numeral, not inside it: the count-up rewrites the
   textContent of whatever holds data-count, so the icon lives in the <dt>
   and the number in its own span. The glyph draws itself in alongside the
   count, using the same inherited stroke-dashoffset trick as the service
   icons (descendant selectors cannot reach inside <use>). */
/* Still used by the stat glyphs. It was first written for the services
   cards, so it went out with them when that grid became a deck - keep it
   here, next to its only remaining caller. */
@keyframes drawIco{from{stroke-dashoffset:74}to{stroke-dashoffset:0}}
.sic{width:clamp(20px,2.3vw,26px);height:clamp(20px,2.3vw,26px);flex:0 0 auto;
  color:var(--blue-br);opacity:.72;stroke-dasharray:74;
  animation:drawIco 1.3s var(--ease-out) .25s forwards}
.hero__stats div:nth-child(2) .sic{animation-delay:.4s}
.hero__stats div:nth-child(3) .sic{animation-delay:.55s}
.hero__stats div:nth-child(4) .sic{animation-delay:.7s}
.hero__stats .snum{font-variant-numeric:tabular-nums}
@media(max-width:560px){.sic{width:19px;height:19px}.hero__stats dt{gap:9px}}
@media (prefers-reduced-motion: reduce){.sic{animation:none;stroke-dasharray:none}}

/* the tag carries the glyph and the number together */


/* ---------- glow ring button ----------
   A conic ring that rotates around the primary CTA. @property is what makes
   the angle interpolate at all - a plain custom property is an unanimatable
   string, so without the descriptor the ring would jump, not turn. Browsers
   without @property (Firefox < 128) simply get the static border they had,
   which is why the ring lives on a pseudo-element and not the button. */
@property --ring{syntax:'<angle>';initial-value:0deg;inherits:false}
@keyframes ringSpin{to{--ring:360deg}}

/* A true ring, not a wash. The first attempt put the conic behind the button
   at z-index:-1 and painted a solid block over it; with the button's own
   semi-transparent background that bled across the face. Masking is the
   right tool: paint the gradient over the whole box, then subtract the
   content box so only the padding ring survives. No stacking games, and it
   sits above the button fill where a border belongs. */
.btn--glow{position:relative}
.btn--glow > *{position:relative;z-index:1}
.btn--glow::before{
  content:"";position:absolute;inset:0;border-radius:inherit;padding:1.6px;
  background:conic-gradient(from var(--ring),
    transparent 0turn,
    var(--blue-br) .12turn,
    var(--pale) .22turn,
    transparent .4turn,
    transparent .6turn,
    var(--blue-br) .72turn,
    var(--pale) .84turn,
    transparent 1turn);
  -webkit-mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
  -webkit-mask-composite:xor;
          mask:linear-gradient(#000 0 0) content-box,linear-gradient(#000 0 0);
          mask-composite:exclude;
  animation:ringSpin 5.5s linear infinite;
  opacity:.85;transition:opacity .35s var(--ease);pointer-events:none}
.btn--glow:hover::before,.btn--glow:focus-visible::before{opacity:1;animation-duration:2.6s}

@media (prefers-reduced-motion: reduce){
  .btn--glow::before{animation:none;opacity:.55}
}

/* ===========================================================
   Fan carousel — projects and the quote deck
   Cards sit on an arc from two numbers JS writes per card:
   --d  signed distance from the focused card (drives rotation
        and horizontal spread, so it has to keep its sign)
   --ad absolute distance (drives drop, shrink and dimming)
   CSS owns every pixel of the result, so the whole thing
   animates on transform/opacity alone.
   =========================================================== */
.fan{position:relative;z-index:1}
/* No perspective and no isolation here. Every transform below is 2D
   (translate/rotate/scale), so `perspective` bought nothing and only forced
   the cards into a 3D rendering context - which composited them away
   entirely: layout and hit-testing stayed correct while the stage painted
   nothing at all. If a card ever needs real depth, add translateZ, not a
   perspective on the parent. */
/* The outermost cards are meant to run past the frame - that is what makes
   it a hand rather than a row - so the stage clips them. `overflow-x:clip`
   rather than `hidden` because clip does not create a scroll container and,
   unlike hidden, leaves the other axis genuinely visible: the focused card
   lifts above the stage and casts a shadow below it, and `overflow:hidden`
   would force overflow-y to auto and cut both off. Older Safari falls back
   to hidden and loses a little of that shadow, which is the right trade. */
.fan__stage{position:relative;display:grid;place-items:center;
  height:clamp(330px,38vw,430px);touch-action:pan-y}
/* Clip the 2D fans ONLY. Any overflow other than visible forces
   transform-style back to `flat`, which collapsed the ring's cylinder
   completely - the stage rendered empty while every measurement still looked
   right. The 3D skins must stay unclipped. */
.fan--work .fan__stage,.fan--quote .fan__stage{overflow:hidden}
@supports (overflow-x:clip){
  .fan--work .fan__stage,.fan--quote .fan__stage{overflow-x:clip;overflow-y:visible}
}

.fan__card{
  --d:0;--ad:0;
  /* The lateral step has to be a real fraction of the card width or the
     neighbours hide behind the focused one and it stops reading as a hand.
     At 8.5vw the step was ~15% of a 440px card; this puts it nearer 40%. */
  --spread:clamp(104px,19vw,215px);   /* lateral step per card */
  --tilt:7.5deg;                      /* rotation per card */
  --drop:clamp(12px,1.7vw,24px);      /* how far neighbours sink */
  grid-area:1/1;position:relative;
  width:min(var(--fan-w,440px),78vw);
  transform:
    translateX(calc(var(--d) * var(--spread)))
    translateY(calc(var(--ad) * var(--drop)))
    rotate(calc(var(--d) * var(--tilt)))
    scale(calc(1 - var(--ad) * .07));
  opacity:calc(1 - var(--ad) * .22);
  z-index:calc(50 - var(--ad));
  transition:transform .62s var(--ease-out),opacity .5s var(--ease);
  will-change:transform}

/* The focused card leaves the neighbours' scale rather than continuing it -
   that discontinuity is what makes it read as picked up out of the spread
   instead of just the nearest one. */
.fan__card.is-focus{
  transform:translateY(calc(-1 * clamp(10px,1.3vw,18px))) rotate(0deg) scale(1.06);
  opacity:1;z-index:60}

.fan__card:not(.is-focus){cursor:pointer}
@media(hover:hover){
  .fan__card:not(.is-focus):hover{opacity:calc(1 - var(--ad) * .12)}
}

/* ---- project skin ---- */
.fan--work{--fan-w:400px}
.fan--work .fan__hit{display:block;border-radius:var(--r-lg);overflow:hidden;
  border:1px solid var(--line);text-decoration:none;color:inherit;
  background:linear-gradient(170deg,rgba(13,32,60,.95),rgba(5,14,28,.95));
  box-shadow:0 26px 60px -30px rgba(0,0,0,.95);transition:border-color .4s}
.fan__card.is-focus .fan__hit{border-color:rgba(126,192,255,.34)}
.fan--work img{width:100%;height:auto;aspect-ratio:16/10;object-fit:cover;display:block}
.fan__meta{display:flex;align-items:baseline;justify-content:space-between;gap:12px;
  padding:15px 18px 17px}
.fan__meta h3{font-size:clamp(15px,1.6vw,18px);font-weight:500;letter-spacing:-.02em}
.fan__go{font-size:11px;letter-spacing:.16em;text-transform:uppercase;color:var(--blue-br);
  opacity:0;transform:translateX(-5px);transition:opacity .4s,transform .4s var(--ease-out)}
.fan__card.is-focus .fan__go{opacity:1;transform:none}

/* ---- quote skin ---- */
.fan--quote{--fan-w:390px}
.fan--quote .fan__card{margin:0;padding:clamp(24px,2.8vw,34px);border-radius:var(--r-lg);
  border:1px solid var(--line);background:linear-gradient(170deg,rgba(12,29,54,.96),rgba(5,13,26,.96));
  box-shadow:0 26px 60px -32px rgba(0,0,0,.95)}
.fan--quote .fan__card.is-focus{border-color:rgba(126,192,255,.32)}
.fan--quote blockquote{margin:0;font-size:clamp(14px,1.55vw,16.5px);font-weight:300;
  line-height:1.72;color:var(--white)}
.fan--quote figcaption{margin-top:20px;border-top:1px solid var(--line);padding-top:14px;
  display:flex;align-items:center;gap:12px}
/* Initials, not a headshot. See the note on TESTIMONIALS in build.py. */
.qav{flex:0 0 38px;width:38px;height:38px;border-radius:50%;display:grid;place-items:center;
  font-size:12.5px;font-weight:600;letter-spacing:.04em;color:var(--pale);
  background:linear-gradient(150deg,rgba(79,155,232,.26),rgba(47,111,183,.16));
  border:1px solid rgba(126,192,255,.26)}
.qwho{display:grid;gap:2px;min-width:0}
.fan--quote strong{font-size:13.5px;color:var(--white);font-weight:600}
.qco{font-size:11.5px;color:var(--dim);letter-spacing:.03em}

/* ---- controls ---- */
.fan__ctrls{display:flex;align-items:center;justify-content:center;gap:10px;margin-top:clamp(14px,2vw,22px)}
.fan__btn{width:38px;height:38px;flex:0 0 38px;display:grid;place-items:center;
  border-radius:50%;border:1px solid var(--line);background:rgba(10,26,48,.5);
  color:var(--mist);cursor:pointer;transition:color .3s,border-color .3s,background .3s}
.fan__btn:hover{color:var(--white);border-color:rgba(126,192,255,.4);background:rgba(16,38,66,.7)}
.fan__btn .ic{width:17px;height:17px}
.fan__dots{display:flex;align-items:center;gap:7px;padding:0 6px}
.fan__dot{width:24px;height:24px;padding:0;border:0;background:none;cursor:pointer;
  display:grid;place-items:center}
.fan__dot::before{content:"";width:6px;height:6px;border-radius:50%;
  background:var(--line);transition:background .3s,width .3s var(--ease-out)}
.fan__dot.is-on::before{width:18px;border-radius:3px;background:var(--blue-br)}

@media(max-width:900px){
  .fan__card{--spread:clamp(72px,13vw,118px);--tilt:6deg}
}
/* Below this a fan is unreadable - the cards overlap to slivers - so the
   neighbours step aside and it becomes a plain one-card carousel. */
@media(max-width:620px){
  .fan__stage{height:auto;min-height:clamp(260px,62vw,330px)}
  .fan__card{width:min(100%,420px);
    transform:translateX(calc(var(--d) * 102%)) scale(1);opacity:0;
    pointer-events:none}
  .fan__card.is-focus{transform:none;opacity:1;pointer-events:auto}
  .fan--work img{aspect-ratio:16/11}
}

@media (prefers-reduced-motion: reduce){
  .fan__card{transition:none}
  .fan__go{transition:none}
}

/* ---------- reel slider: a coverflow of tall portraits ----------
   The active reel faces you; its neighbours slide out, turn toward it and
   soften. Unlike the fan, this one genuinely needs 3D - rotateY is the whole
   effect - so the stage declares perspective AND preserve-3d together. The
   fan deliberately has neither; a 3D context around purely 2D transforms is
   what composited that one away. */
.fan--reel .fan__stage{
  perspective:1200px;perspective-origin:50% 50%;
  height:clamp(330px,44vw,470px)}
.fan--reel .fan__card{
  --spread:clamp(96px,13vw,190px);
  width:min(250px,56vw);margin:0;
  /* Nine posts means the outermost card is four steps out. At -26deg a step
     that is 104deg - past edge-on, so it renders as a sliver - and the .26
     opacity ramp hit zero at the same point. Gentler per-step values keep
     the whole reel on screen and legible. */
  transform:
    translateX(calc(var(--d) * var(--spread)))
    rotateY(calc(var(--d) * -16deg))
    scale(calc(1 - var(--ad) * .07));
  opacity:calc(1 - var(--ad) * .19);
  filter:saturate(calc(1 - var(--ad) * .18));
  transform-style:preserve-3d}
.fan--reel .fan__card.is-focus{
  transform:translateZ(60px) rotateY(0deg) scale(1.04);
  opacity:1;filter:none}
.fan--reel img{width:100%;height:auto;aspect-ratio:4/5;object-fit:cover;display:block;
  border-radius:var(--r-lg);border:1px solid var(--line);
  box-shadow:0 28px 60px -30px rgba(0,0,0,.95)}
.fan--reel .fan__card.is-focus img{border-color:rgba(126,192,255,.34)}

/* ---------- circular slider: a true 3D ring ----------
   Cards are seated around a cylinder and the STAGE spins; each card keeps a
   fixed angle. The far side stays visible through the gaps - no
   backface-visibility:hidden - which is what makes it read as a cylinder
   rather than a shallow fan. --r is the cylinder radius. */
.fan--ring .fan__stage{
  /* Four channels means four faces 90deg apart - geometrically a box, so
     the radius has to stay close to the card width or the side faces go
     edge-on and vanish. */
  --r:clamp(190px,23vw,290px);
  perspective:1100px;transform-style:preserve-3d;
  height:clamp(300px,34vw,380px);
  transform:rotateY(var(--rot,0deg));
  transition:transform .7s var(--ease-out)}
.fan--ring .fan__card{
  width:min(250px,66vw);
  transform:rotateY(var(--a,0deg)) translateZ(var(--r));
  transform-style:preserve-3d;
  transition:opacity .5s var(--ease)}
.fan--ring .fan__card:not(.is-focus){opacity:.42;pointer-events:none}
.fan--ring .fan__card.is-focus{opacity:1}

@media (prefers-reduced-motion: reduce){
  .fan--ring .fan__stage,.fan--reel .fan__card{transition:none}
}

/* Below this the 3D reads as clutter on a small screen, so both collapse to
   the same single-card carousel the fan uses. */
@media(max-width:620px){
  .fan--reel .fan__stage{perspective:none;height:auto;min-height:clamp(300px,74vw,400px)}
  .fan--reel .fan__card{width:min(100%,280px);
    transform:translateX(calc(var(--d) * 110%));opacity:0;filter:none}
  .fan--reel .fan__card.is-focus{transform:none;opacity:1}
  .fan--ring .fan__stage{perspective:none;transform-style:flat;transform:none;
    height:auto;min-height:230px}
  /* :not(.is-focus) is needed here, not a bare .fan__card. The desktop rule
     that dims the back of the cylinder is `.fan--ring .fan__card:not(.is-focus)`
     at (0,3,0); a plain `.fan--ring .fan__card` is (0,2,0) and loses to it
     whatever the media query says, so every card stayed at .42 opacity and
     sat in a row running off the side of the phone. */
  .fan--ring .fan__card{width:min(100%,320px);
    transform:translateX(calc(var(--d) * 110%))}
  .fan--ring .fan__card:not(.is-focus){opacity:0;pointer-events:none}
  .fan--ring .fan__card.is-focus{transform:none;opacity:1;pointer-events:auto}
}

/* ---------- services hover tabs ----------
   A list beside one framed picture. The active row takes the frame's own
   colour and sits flush against it, so the pair reads as one object rather
   than a menu next to an image. */
.htabs{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);
  gap:clamp(18px,2.4vw,34px);align-items:stretch}
.htabs__list{display:flex;flex-direction:column;gap:4px;min-width:0}

.htab{position:relative;display:block;width:100%;text-align:left;cursor:pointer;
  border:1px solid transparent;border-right:0;
  border-radius:var(--r-lg) 0 0 var(--r-lg);
  background:transparent;color:var(--mist);
  padding:clamp(13px,1.5vw,17px) clamp(15px,1.8vw,21px);
  transition:background .4s var(--ease),color .3s,border-color .4s}
.htab__row{display:flex;align-items:center;gap:12px}
.htab__ico{width:17px;height:17px;flex:0 0 17px;color:var(--blue-br);opacity:.55;
  transition:opacity .3s}
.htab__name{font-size:clamp(15px,1.7vw,19px);font-weight:400;letter-spacing:-.02em;
  color:var(--white);flex:1 1 auto}
.htab__no{font-size:10.5px;letter-spacing:.2em;color:var(--dim)}

/* The description opens with grid-template-rows 0fr -> 1fr: it animates to
   the content's real height with no max-height guess to get wrong. */
.htab__descwrap{display:grid;grid-template-rows:0fr;
  transition:grid-template-rows .45s var(--ease-out)}
.htab__desc{overflow:hidden;display:block;font-size:13.5px;font-weight:300;
  line-height:1.65;color:var(--mist);opacity:0;
  transition:opacity .35s var(--ease),padding .45s var(--ease-out)}
.htab.is-on .htab__descwrap{grid-template-rows:1fr}
.htab.is-on .htab__desc{opacity:1;padding-top:9px;padding-left:29px}

.htab.is-on{background:var(--tabface,rgba(13,32,60,.95));
  border-color:var(--line);color:var(--white)}
.htab.is-on .htab__ico{opacity:1}
@media(hover:hover){.htab:not(.is-on):hover .htab__name{color:var(--pale)}}

.htabs__frame{position:relative;overflow:hidden;min-height:clamp(300px,36vw,460px);
  border-radius:0 var(--r-lg) var(--r-lg) 0;border:1px solid var(--line);
  background:var(--tabface,rgba(13,32,60,.95))}
.htabs__shot{position:absolute;inset:0;width:100%;height:100%;object-fit:cover;
  opacity:0;transform:scale(1.04);
  transition:opacity .6s var(--ease),transform 1.2s var(--ease-out)}
.htabs__shot.is-on{opacity:.88;transform:scale(1)}

@media(max-width:860px){
  /* Stacked, so the tab can no longer join the frame edge-to-edge - give
     both their full radius back and let hover fall away to tap. */
  .htabs{grid-template-columns:minmax(0,1fr)}
  .htabs__frame{border-radius:var(--r-lg);min-height:clamp(230px,52vw,330px);order:-1}
  .htab{border-right:1px solid transparent;border-radius:var(--r-lg)}
  .htab.is-on{border-color:var(--line)}
}
@media (prefers-reduced-motion: reduce){
  .htab,.htab__descwrap,.htab__desc,.htabs__shot{transition:none}
}

/* ---------- social channels ----------
   These were a 3D ring (scrolltide's circular slider) but there are only
   four channels: a cylinder puts two of them exactly 90deg edge-on at all
   times - measured 44px wide against the front card's 303px - and a third
   facing away. The reference uses many more cards. Four items want a fan,
   which this page already does well, so the ring CSS stays available for a
   list long enough to justify it. */
.fan--chan{--fan-w:300px}
.fan--chan .fan__stage{height:clamp(300px,32vw,380px)}
.fan--chan .fan__card{--spread:clamp(96px,15vw,180px);--tilt:6.5deg}
.fan--chan .chan__hit{box-shadow:0 26px 60px -32px rgba(0,0,0,.95)}
.fan--chan .fan__card.is-focus .chan__hit{border-color:rgba(126,192,255,.34)}
@media(max-width:620px){
  .fan--chan .fan__stage{height:auto;min-height:230px}
  .fan--chan .fan__card{width:min(100%,320px);transform:translateX(calc(var(--d) * 110%))}
  .fan--chan .fan__card:not(.is-focus){opacity:0;pointer-events:none}
  .fan--chan .fan__card.is-focus{transform:none;opacity:1;pointer-events:auto}
}
