/* ════════════════════════════════════════════════════════════════
   Zeafar: Rafael Bautista, personal AI-builder brand
   Pattern: Portfolio Grid.  Style: Motion-Driven.  (ui-ux-pro-max)
   Archivo headings / Space Grotesk body.

   BRAND: black and white only. No hue anywhere in the chrome. The
   portfolio pieces are the only colour on the site, which is the
   point: the interface stays out of the way and the work carries it.

   Losing hue means interaction can no longer be signalled by colour,
   so it is carried by fill inversion (white block, black label),
   1px rules, weight and underline instead.

   Greys are true neutrals (r=g=b), not the Zinc ramp, which carries a
   blue cast that is visible the moment it sits next to a pure grey.

   Contrast, measured on the #0A0A0A page:
     #FFFFFF 19.80   #E5E5E5 15.72   #A3A3A3 7.85   #8C8C8C 5.89
   Every text tier clears AA on its own, including the most muted
   one, so nothing depends on size to stay legible.
   ════════════════════════════════════════════════════════════════ */

:root{
  --bg:#0A0A0A;
  --bg-2:#141414;
  --surface:rgba(255,255,255,.04);
  --surface-2:rgba(255,255,255,.07);
  --line:rgba(255,255,255,.10);
  --line-2:rgba(255,255,255,.20);

  --white:#fff;
  --txt-2:#E5E5E5;           /* 15.44:1 */
  --txt-3:#A3A3A3;           /*  7.60:1 */
  --txt-4:#8C8C8C;           /*  5.68:1, still AA at body size */

  --accent:#FFFFFF;          /* 19.90:1 */
  --accent-solid:#FFFFFF;    /* fill, with --bg text at 19.90:1 */
  --accent-dim:rgba(255,255,255,.12);

  /* state colour is the one exception: an error that reads as
     "just more white" is not an error message. Both clear AA. */
  --err:#FCA5A5;
  --ok:#86EFAC;

  --shell:1240px;
  --r-sm:8px;
  --r:14px;
  --r-lg:22px;
  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);
}

*,*::before,*::after{box-sizing:border-box}
html{-webkit-text-size-adjust:100%;scroll-behavior:smooth}
body{
  margin:0;background:var(--bg);color:var(--white);
  font:400 17px/1.6 'Space Grotesk',system-ui,-apple-system,'Segoe UI',sans-serif;
  -webkit-font-smoothing:antialiased;overflow-x:hidden;
}
/* height:auto is not optional here. Every img carries width/height attributes
   to reserve layout space and avoid shift, but once max-width caps the width the
   attribute height stays fixed and the image stretches vertically. Components
   that want a fixed height (.hstrip, .piece__img) override this. */
img,svg{max-width:100%;height:auto;display:block}
h1,h2,h3,h4{font-family:'Archivo',system-ui,sans-serif;color:var(--white);margin:0;
  line-height:1.04;letter-spacing:-.035em;font-weight:600}
h1{font-size:clamp(2.9rem,7.2vw,6rem);font-weight:700}
h2{font-size:clamp(2.1rem,4.6vw,3.6rem)}
h3{font-size:1.3rem;line-height:1.22;letter-spacing:-.02em}
p{margin:0}
a{color:var(--accent);text-decoration:none}
ul,ol{margin:0;padding:0;list-style:none}
::selection{background:var(--accent);color:var(--bg)}

.shell{width:100%;max-width:var(--shell);margin-inline:auto;padding-inline:clamp(20px,5vw,44px)}
.sec{padding:clamp(76px,9vw,140px) 0;position:relative;overflow-x:clip}
.sec--tight{padding-block:clamp(52px,6vw,84px)}

.skip{position:absolute;left:-9999px;top:0;z-index:300;background:var(--accent);color:var(--bg);padding:12px 18px}
.skip:focus{left:0}
.vh{position:absolute;width:1px;height:1px;margin:-1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap;border:0}
:where(a,button,input,textarea,select,summary):focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:4px}

/* ── scroll progress ── */
.progress{
  position:fixed;top:0;left:0;height:2px;width:100%;z-index:200;transform-origin:0 50%;
  transform:scaleX(0);background:linear-gradient(90deg,#fff,rgba(255,255,255,.25));
}

/* ── ambient glow, parallaxed ── */
.glow{position:fixed;inset:0;z-index:-1;pointer-events:none;overflow:hidden}
.glow i{position:absolute;display:block;border-radius:50%;filter:blur(90px);opacity:.5}
.glow i:nth-child(1){width:52vw;height:52vw;left:-12vw;top:-16vw;background:rgba(255,255,255,.07)}
.glow i:nth-child(2){width:40vw;height:40vw;right:-10vw;top:24vh;background:rgba(255,255,255,.05)}
.glow i:nth-child(3){width:36vw;height:36vw;left:28vw;bottom:-14vw;background:rgba(255,255,255,.04)}

/* ── labels ── */
.eyebrow{
  font-family:'Space Grotesk',monospace;font-size:.8rem;font-weight:500;letter-spacing:.2em;
  text-transform:uppercase;color:var(--txt-4);margin-bottom:18px;
  display:inline-flex;align-items:center;gap:10px;
}
.eyebrow::before{content:"";width:26px;height:1px;background:var(--accent)}
.lede{font-size:clamp(1.05rem,1.5vw,1.24rem);color:var(--txt-2);max-width:36em;line-height:1.62}

/* ── buttons ── */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:10px;position:relative;
  font-family:'Space Grotesk',sans-serif;font-size:1rem;font-weight:500;
  min-height:54px;padding:0 30px;border-radius:999px;border:1px solid transparent;
  cursor:pointer;text-align:center;overflow:hidden;
  transition:transform .3s var(--ease),background .25s var(--ease),
             border-color .25s var(--ease),color .25s var(--ease);
}
.btn svg{width:16px;height:16px;flex:none;transition:transform .3s var(--ease)}
.btn--pri{background:var(--accent-solid);color:var(--bg)}
.btn--pri:hover{background:var(--txt-2);transform:translateY(-2px)}
.btn--pri:hover svg{transform:translateX(3px)}
.btn--ghost{background:transparent;color:var(--white);border-color:var(--line-2)}
.btn--ghost:hover{border-color:var(--white);background:var(--surface);transform:translateY(-2px)}
.btn--full{width:100%}
.btn:active{transform:none}

/* ══ NAV ══ */
.nav{
  position:sticky;top:0;z-index:100;background:rgba(10,10,10,.7);
  -webkit-backdrop-filter:blur(18px);backdrop-filter:blur(18px);
  border-bottom:1px solid transparent;transition:border-color .3s var(--ease),background .3s var(--ease);
}
.nav.is-stuck{border-bottom-color:var(--line);background:rgba(10,10,10,.9)}
.nav__in{display:flex;align-items:center;justify-content:space-between;gap:20px;min-height:76px}
.logo{display:inline-flex;align-items:baseline;gap:9px;min-height:44px;color:var(--white);
  font-family:'Archivo',sans-serif;font-weight:700;font-size:1.3rem;letter-spacing:-.04em}
.logo span{width:8px;height:8px;border-radius:50%;background:var(--accent);flex:none;
  align-self:center;box-shadow:0 0 14px var(--accent);animation:pulse 2.6s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:1;transform:scale(1)}50%{opacity:.55;transform:scale(.82)}}
.logo b{font-weight:400;color:var(--txt-4);font-size:.82rem;letter-spacing:0;
  font-family:'Space Grotesk',sans-serif}

.menu{display:flex;align-items:center;gap:2px}
.menu>a:not(.btn){
  font-size:.97rem;color:var(--txt-3);padding:10px 15px;border-radius:999px;position:relative;
  transition:color .2s var(--ease),background .2s var(--ease);
}
.menu>a:not(.btn):hover,.menu>a.is-active{color:var(--white);background:var(--surface)}
.menu .btn{margin-left:12px;min-height:46px;padding:0 24px;font-size:.94rem}

.burger{display:none;position:relative;width:46px;height:46px;padding:0;
  border:1px solid var(--line-2);background:transparent;border-radius:12px;cursor:pointer}
.burger span{position:absolute;left:50%;width:18px;height:1.5px;margin:0;background:var(--white);
  transform:translateX(-50%);transition:top .28s var(--ease),transform .28s var(--ease),opacity .18s}
.burger span:nth-child(1){top:15px}
.burger span:nth-child(2){top:22px}
.burger span:nth-child(3){top:29px}
.burger[aria-expanded="true"] span:nth-child(1){top:22px;transform:translateX(-50%) rotate(45deg)}
.burger[aria-expanded="true"] span:nth-child(2){opacity:0}
.burger[aria-expanded="true"] span:nth-child(3){top:22px;transform:translateX(-50%) rotate(-45deg)}
/* was 960px, raised because the nav carries eight links now */
@media (max-width:1180px){
  .burger{display:block}
  .menu{position:absolute;left:0;right:0;top:100%;flex-direction:column;align-items:stretch;gap:0;
    background:var(--bg);border-bottom:1px solid var(--line);padding:10px clamp(20px,5vw,44px) 22px;
    clip-path:inset(0 0 100% 0);opacity:0;pointer-events:none;
    transition:clip-path .36s var(--ease),opacity .2s}
  .menu.is-open{clip-path:inset(0 0 0 0);opacity:1;pointer-events:auto}
  .menu>a:not(.btn){padding:15px 8px;border-radius:10px}
  .menu .btn{margin:12px 0 0}
}

/* ══ HERO ══ */
.hero__in{min-height:clamp(600px,86vh,860px);display:flex;flex-direction:column;
  justify-content:center;padding-block:clamp(70px,9vw,120px)}
.hero h1{max-width:15em;margin:0}
.hero h1 em{font-style:normal;color:var(--accent);position:relative;white-space:nowrap}
.hero .lede{margin-top:30px}
.hero__cta{display:flex;flex-wrap:wrap;gap:13px;margin-top:40px}
.hero__meta{margin-top:44px;display:flex;flex-wrap:wrap;gap:10px 26px;
  font-size:.92rem;color:var(--txt-4);align-items:center}
.hero__meta i{font-style:normal;display:inline-flex;align-items:center;gap:8px}
.hero__meta i::before{content:"";width:5px;height:5px;border-radius:50%;background:var(--accent)}

/* headline words rise individually */
.word{display:inline-block;overflow:hidden;vertical-align:bottom}
.word>span{display:inline-block;transform:translateY(105%);
  transition:transform .85s var(--ease-out)}
.js .is-in .word>span,.js .word.is-in>span{transform:none}
.word:nth-child(1)>span{transition-delay:.02s}
.word:nth-child(2)>span{transition-delay:.08s}
.word:nth-child(3)>span{transition-delay:.14s}
.word:nth-child(4)>span{transition-delay:.20s}
.word:nth-child(5)>span{transition-delay:.26s}
.word:nth-child(6)>span{transition-delay:.32s}
.word:nth-child(7)>span{transition-delay:.38s}
.word:nth-child(8)>span{transition-delay:.44s}

/* ══ STATS ══ */
.stats{display:grid;gap:1px;background:var(--line);border-block:1px solid var(--line)}
@media (min-width:640px){.stats{grid-template-columns:repeat(3,1fr)}}
@media (min-width:1020px){.stats{grid-template-columns:repeat(5,1fr)}}
.stat{background:var(--bg);padding:34px 26px;text-align:center;
  transition:background .35s var(--ease)}
.stat:hover{background:var(--bg-2)}
.stat b{display:block;font-family:'Archivo',sans-serif;font-size:clamp(2rem,3.4vw,2.9rem);
  font-weight:700;letter-spacing:-.045em;line-height:1;
  background:linear-gradient(180deg,#fff,#A3A3A3);-webkit-background-clip:text;
  background-clip:text;color:transparent}
.stat span{display:block;margin-top:10px;font-size:.88rem;color:var(--txt-4);
  letter-spacing:.04em}

/* ══ MARQUEE ══ */
.marq{overflow:hidden;border-bottom:1px solid var(--line);padding-block:20px;position:relative}
.marq::before,.marq::after{content:"";position:absolute;top:0;bottom:0;width:120px;z-index:2}
.marq::before{left:0;background:linear-gradient(90deg,var(--bg),transparent)}
.marq::after{right:0;background:linear-gradient(270deg,var(--bg),transparent)}
.marq ul{display:flex;width:max-content;animation:slide 42s linear infinite}
.marq li{flex:none;padding:0 30px;font-family:'Archivo',sans-serif;font-weight:600;
  font-size:1.05rem;color:var(--txt-4);letter-spacing:-.01em;display:flex;align-items:center;gap:30px}
.marq li::after{content:"";width:5px;height:5px;border-radius:50%;background:var(--accent);opacity:.6}
@keyframes slide{from{transform:translateX(0)}to{transform:translateX(-50%)}}
.marq:hover ul{animation-play-state:paused}

/* ══ SECTION HEAD ══ */
.head{max-width:46rem;margin-bottom:clamp(44px,5vw,72px)}
.head--center{margin-inline:auto;text-align:center}
.head--center .eyebrow{justify-content:center}
.head p{margin-top:22px;color:var(--txt-3);font-size:1.05rem}

/* ══ WORK GRID ══ */
.work{display:grid;gap:clamp(14px,1.6vw,20px)}
@media (min-width:700px){.work{grid-template-columns:repeat(2,1fr)}}
@media (min-width:1060px){.work{grid-template-columns:repeat(3,1fr)}}
.piece{
  position:relative;border-radius:var(--r-lg);overflow:hidden;background:var(--bg-2);
  border:1px solid var(--line);transition:transform .45s var(--ease-out),border-color .35s}
.piece:hover{transform:translateY(-6px);border-color:var(--line-2)}
.piece__img{aspect-ratio:16/10;overflow:hidden;background:var(--bg-2)}
.piece--sq .piece__img{aspect-ratio:1/1}
.piece__img img{width:100%;height:100%;object-fit:cover;object-position:top center;
  transition:transform .9s var(--ease-out),filter .45s;filter:saturate(.92)}
.piece:hover .piece__img img{transform:scale(1.05);filter:saturate(1.05)}
.piece__cap{padding:20px 22px 24px;display:flex;align-items:center;justify-content:space-between;gap:14px}
.piece__cap b{font-family:'Archivo',sans-serif;font-weight:600;font-size:1.02rem;letter-spacing:-.02em}
.piece__cap span{font-size:.82rem;color:var(--txt-4);white-space:nowrap}

/* ══ SERVICE LIST ══ */
.rows{border-top:1px solid var(--line)}
.row{display:grid;gap:10px 32px;padding:30px 0;border-bottom:1px solid var(--line);
  transition:padding-left .4s var(--ease),background .4s var(--ease)}
@media (min-width:820px){
  .row{grid-template-columns:4rem 17rem 1fr;align-items:baseline}
  .row:hover{padding-left:14px;background:linear-gradient(90deg,var(--surface),transparent)}
}
.row__n{font-family:'Space Grotesk',monospace;font-size:.82rem;color:var(--accent)}
.row h3{font-size:1.22rem}
.row p{color:var(--txt-3);font-size:1rem}

/* ══ CARDS ══ */
.cards{display:grid;gap:clamp(14px,1.6vw,20px)}
@media (min-width:640px){.cards{grid-template-columns:repeat(2,1fr)}}
@media (min-width:1020px){.cards--3{grid-template-columns:repeat(3,1fr)}}
.card{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:30px 28px 34px;position:relative;overflow:hidden;
  transition:transform .4s var(--ease-out),background .35s,border-color .35s}
.card:hover{transform:translateY(-5px);background:var(--surface-2);border-color:var(--line-2)}
.card__n{font-family:'Space Grotesk',monospace;font-size:.8rem;color:var(--accent);
  display:block;margin-bottom:18px}
.card h3{margin-bottom:11px}
.card p{color:var(--txt-3);font-size:1rem}

/* ══ QUOTES ══ */
.quotes{display:grid;gap:clamp(14px,1.6vw,18px)}
@media (min-width:760px){.quotes{grid-template-columns:repeat(2,1fr)}}
@media (min-width:1120px){.quotes{grid-template-columns:repeat(4,1fr)}}
.quote{background:var(--surface);border:1px solid var(--line);border-radius:var(--r);
  padding:26px 24px 28px;transition:transform .4s var(--ease-out),border-color .35s}
.quote:hover{transform:translateY(-4px);border-color:var(--line-2)}
.quote p{font-size:1rem;line-height:1.55;color:var(--txt-2)}
.quote footer{margin-top:18px;font-size:.85rem;color:var(--txt-4);
  display:flex;align-items:center;gap:9px}
.quote footer::before{content:"";width:16px;height:1px;background:var(--accent)}

/* ══ ABOUT SPLIT ══ */
.split{display:grid;gap:clamp(34px,5vw,68px);align-items:start}
@media (min-width:940px){.split{grid-template-columns:minmax(0,1fr) minmax(0,.86fr)}}
.split p{color:var(--txt-3);font-size:1.05rem}
.split p+p{margin-top:18px}
.creds{display:grid;gap:2px;background:var(--line);border:1px solid var(--line);
  border-radius:var(--r);overflow:hidden}
.creds div{background:var(--bg);padding:20px 22px}
.creds dt{font-size:.78rem;letter-spacing:.14em;text-transform:uppercase;color:var(--txt-4);margin-bottom:6px}
.creds dd{margin:0;font-size:1rem;color:var(--white)}

/* ══ CONTACT ══ */
.contact{display:grid;gap:clamp(34px,5vw,64px);align-items:start}
.contact>*{min-width:0}
@media (min-width:940px){.contact{grid-template-columns:minmax(0,.92fr) minmax(0,1.08fr)}}
.cinfo{margin-top:34px;display:grid;gap:18px}
.cinfo div{display:flex;gap:14px;align-items:flex-start}
.cinfo svg{width:18px;height:18px;flex:none;margin-top:5px;color:var(--accent)}
.cinfo dt{font-size:.76rem;letter-spacing:.14em;text-transform:uppercase;color:var(--txt-4);margin-bottom:2px}
.cinfo dd{margin:0;font-size:1rem}
.cinfo dd a{color:var(--white);display:inline-flex;align-items:center;min-height:44px;overflow-wrap:anywhere}
.cinfo dd a:hover{color:var(--accent)}

.form{background:var(--surface);border:1px solid var(--line);border-radius:var(--r-lg);
  padding:clamp(24px,3vw,38px);display:grid;gap:18px}
.form h3{font-size:1.4rem}
.form__intro{color:var(--txt-3);font-size:.98rem;margin-top:-8px}
.f{display:grid;gap:7px}
.f2{display:grid;gap:18px}
@media (min-width:560px){.f2{grid-template-columns:1fr 1fr}}
.f label{font-size:.78rem;letter-spacing:.12em;text-transform:uppercase;color:var(--txt-4)}
.f label span{color:var(--accent)}
.f input,.f textarea,.f select{width:100%;min-height:52px;padding:14px 16px;
  font:400 1rem/1.5 'Space Grotesk',sans-serif;color:var(--white);
  background:rgba(255,255,255,.04);border:1px solid var(--line-2);border-radius:var(--r-sm);
  transition:border-color .2s,background .2s,box-shadow .2s}
.f textarea{min-height:124px;resize:vertical}
.f input::placeholder,.f textarea::placeholder{color:var(--txt-4)}
.f input:hover,.f textarea:hover,.f select:hover{border-color:rgba(255,255,255,.36)}
.f input:focus,.f textarea:focus,.f select:focus{outline:none;background:rgba(255,255,255,.07);
  border-color:var(--accent);box-shadow:0 0 0 3px rgba(255,255,255,.18)}
.f.is-bad input,.f.is-bad textarea{border-color:var(--err)}
.err{font-size:.86rem;color:var(--err);min-height:0}
.f.is-bad .err{min-height:1.3em}
.hint{font-size:.86rem;color:var(--txt-4)}
.form__note{font-size:.82rem;color:var(--txt-4);text-align:center}
.form__status{font-size:1rem;text-align:center;min-height:0}
.form__status.is-ok{color:var(--ok);min-height:1.4em}
.form__status.is-bad{color:var(--err);min-height:1.4em}
.send__spin{display:none;width:16px;height:16px;border-radius:50%;
  border:2px solid rgba(255,255,255,.3);border-top-color:#fff;animation:spin .7s linear infinite}
.btn.is-busy{pointer-events:none;opacity:.7}
.btn.is-busy .send__spin{display:block}
@keyframes spin{to{transform:rotate(360deg)}}

/* ══ FOOTER ══ */
.foot{border-top:1px solid var(--line);padding-top:clamp(46px,6vw,70px)}
.foot__in{display:grid;gap:34px;padding-bottom:40px}
@media (min-width:760px){.foot__in{grid-template-columns:1.6fr .8fr 1fr;gap:46px}}
.foot__brand p{color:var(--txt-4);font-size:.96rem;margin-top:16px;max-width:30em}
.foot h2{font-size:.74rem;letter-spacing:.16em;text-transform:uppercase;color:var(--txt-4);
  margin-bottom:12px;font-weight:500;font-family:'Space Grotesk',sans-serif}
.foot nav{display:flex;flex-direction:column;align-items:flex-start}
.foot nav a,.foot__c a{color:var(--txt-2);font-size:.97rem;display:inline-flex;align-items:center;min-height:44px}
.foot nav a:hover,.foot__c a:hover{color:var(--accent)}
.foot__c{display:flex;flex-direction:column;align-items:flex-start}
.foot__c p{color:var(--txt-4);font-size:.94rem}
.foot__base{border-top:1px solid var(--line);display:flex;flex-wrap:wrap;gap:4px 24px;
  justify-content:space-between;align-items:center;padding-block:12px 18px;
  font-size:.88rem;color:var(--txt-4)}

/* ══ SCROLL REVEAL ══ */
.js [data-rise]{opacity:0;transform:translateY(26px);
  transition:opacity .8s var(--ease-out),transform .8s var(--ease-out)}
.js [data-rise].is-in{opacity:1;transform:none}
.js [data-rise-d="1"]{transition-delay:.08s}
.js [data-rise-d="2"]{transition-delay:.16s}
.js [data-rise-d="3"]{transition-delay:.24s}
.js [data-rise-d="4"]{transition-delay:.32s}
.js [data-rise-d="5"]{transition-delay:.40s}
/* grid children stagger themselves */
.js [data-stagger]>*{opacity:0;transform:translateY(30px);
  transition:opacity .8s var(--ease-out),transform .8s var(--ease-out)}
.js [data-stagger].is-in>*{opacity:1;transform:none}
.js [data-stagger].is-in>*:nth-child(1){transition-delay:.00s}
.js [data-stagger].is-in>*:nth-child(2){transition-delay:.07s}
.js [data-stagger].is-in>*:nth-child(3){transition-delay:.14s}
.js [data-stagger].is-in>*:nth-child(4){transition-delay:.21s}
.js [data-stagger].is-in>*:nth-child(5){transition-delay:.28s}
.js [data-stagger].is-in>*:nth-child(6){transition-delay:.35s}
.js [data-stagger].is-in>*:nth-child(7){transition-delay:.42s}
.js [data-stagger].is-in>*:nth-child(8){transition-delay:.49s}
.js [data-stagger].is-in>*:nth-child(9){transition-delay:.56s}

@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation-duration:.001ms!important;animation-iteration-count:1!important;
    transition-duration:.001ms!important}
  .js [data-rise],.js [data-stagger]>*{opacity:1;transform:none}
  .js .word>span{transform:none}
  .marq ul{animation:none;width:auto;flex-wrap:wrap;justify-content:center}
  .progress{display:none}
  .glow i{opacity:.35}
}

/* ══════════════════════════════════════════════
   GALLERY  (work.html)
   ══════════════════════════════════════════════ */
.filters{display:flex;flex-wrap:wrap;gap:9px;margin-bottom:clamp(30px,4vw,46px)}
.filters button{
  font-family:'Space Grotesk',sans-serif;font-size:.92rem;color:var(--txt-3);
  background:var(--surface);border:1px solid var(--line);border-radius:999px;
  padding:0 20px;min-height:44px;cursor:pointer;
  transition:color .25s var(--ease),background .25s var(--ease),border-color .25s var(--ease);
}
.filters button:hover{color:var(--white);border-color:var(--line-2)}
.filters button[aria-pressed="true"]{background:var(--white);color:var(--bg);border-color:var(--white)}
.filters .count{color:var(--txt-4);font-size:.85rem;margin-left:4px}
/* the active pill inverts to a white fill, so its count needs dark text
   rather than the muted grey, which only reached 3.38:1 on white */
.filters button[aria-pressed="true"] .count{color:#3F3F3F}

/* columns keep varied aspect ratios honest without cropping the artwork */
.gal{columns:1;column-gap:16px}
@media (min-width:620px){.gal{columns:2}}
@media (min-width:1000px){.gal{columns:3}}
@media (min-width:1400px){.gal{columns:4}}
.tile{
  break-inside:avoid;margin:0 0 16px;position:relative;display:block;width:100%;
  border-radius:var(--r);overflow:hidden;background:var(--bg-2);border:1px solid var(--line);
  cursor:zoom-in;padding:0;text-align:left;
  transition:transform .45s var(--ease-out),border-color .3s var(--ease);
}
.tile:hover{transform:translateY(-4px);border-color:var(--line-2)}
.tile img{width:100%;height:auto;display:block;transition:transform .8s var(--ease-out)}
.tile:hover img{transform:scale(1.03)}
.tile figcaption{
  position:absolute;inset:auto 0 0 0;padding:26px 16px 13px;
  font-size:.84rem;color:var(--white);
  background:linear-gradient(transparent,rgba(10,10,10,.9));
  opacity:0;transform:translateY(8px);
  transition:opacity .3s var(--ease),transform .3s var(--ease);
}
.tile:hover figcaption,.tile:focus-visible figcaption{opacity:1;transform:none}
.gal--hidden{display:none}
.gal__empty{color:var(--txt-4);padding:40px 0}

/* ── lightbox ── */
.lb{
  position:fixed;inset:0;z-index:250;display:none;
  background:rgba(10,10,10,.94);-webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  align-items:center;justify-content:center;padding:clamp(16px,4vw,56px);
}
.lb[open],.lb.is-open{display:flex}
.lb img{max-width:100%;max-height:82vh;width:auto;height:auto;border-radius:var(--r);
  box-shadow:0 30px 80px rgba(0,0,0,.6)}
.lb__cap{position:absolute;left:0;right:0;bottom:22px;text-align:center;color:var(--txt-3);font-size:.9rem}
.lb__x,.lb__prev,.lb__next{
  position:absolute;width:52px;height:52px;border-radius:50%;cursor:pointer;
  background:var(--surface-2);border:1px solid var(--line-2);color:var(--white);
  display:grid;place-items:center;transition:background .25s,border-color .25s;
}
.lb__x:hover,.lb__prev:hover,.lb__next:hover{background:rgba(255,255,255,.16);border-color:var(--white)}
.lb__x{top:20px;right:20px}
.lb__prev{left:20px;top:50%;transform:translateY(-50%)}
.lb__next{right:20px;top:50%;transform:translateY(-50%)}
.lb svg{width:20px;height:20px}
@media (max-width:600px){
  .lb__prev{left:12px}.lb__next{right:12px}
  .lb__x{top:12px;right:12px}
}

/* ── page header for interior pages ── */
.phead{border-bottom:1px solid var(--line)}
.phead__in{padding-block:clamp(64px,8vw,116px)}
.phead h1{font-size:clamp(2.6rem,6vw,4.6rem);max-width:16em}
.phead .lede{margin-top:26px}

/* ── hero motion ─────────────────────────────────────────────────
   Node mesh behind the headline. Only stroke-dashoffset and opacity
   animate, so the whole thing composites without touching layout. The
   radial mask fades it out at the edges and keeps the text readable. */
.hero{position:relative;isolation:isolate}
.hero__fx{position:absolute;inset:0;z-index:-1;overflow:hidden;pointer-events:none;
  -webkit-mask-image:linear-gradient(180deg,#000 62%,transparent);
          mask-image:linear-gradient(180deg,#000 62%,transparent)}
.hero__fx svg{width:100%;height:100%;display:block}
.fx__mesh path{fill:none;stroke:rgba(255,255,255,.16);stroke-width:1}
.fx__flow{filter:drop-shadow(0 0 7px rgba(255,255,255,.6))}
.fx__flow path{fill:none;stroke:#fff;stroke-width:2.2;stroke-linecap:round;
  stroke-dasharray:64 2600;stroke-dashoffset:2664;
  animation:fxFlow var(--d,4s) linear var(--o,0s) infinite}
.fx__nodes circle{fill:#fff;opacity:.4;
  animation:fxNode var(--d,4s) ease-in-out var(--o,0s) infinite}
@keyframes fxFlow{to{stroke-dashoffset:0}}
@keyframes fxNode{0%,100%{opacity:.22}50%{opacity:.85}}

/* the headline sits above the mesh */
.hero__in{position:relative;z-index:1}

/* ── scroll reveals: directional and clip variants ───────────────
   [data-rise] alone still means "fade up". These give sideways entry
   for split layouts and a wipe for media, so a long page does not
   repeat one motion twenty times. */
.js [data-rise="left"]{transform:translateX(-34px)}
.js [data-rise="right"]{transform:translateX(34px)}
.js [data-rise="scale"]{transform:scale(.94)}
.js [data-rise="left"].is-in,
.js [data-rise="right"].is-in,
.js [data-rise="scale"].is-in{transform:none}
.js [data-clip]{clip-path:inset(0 0 100% 0);opacity:1;
  transition:clip-path 1s var(--ease-out)}
.js [data-clip].is-in{clip-path:inset(0 0 0 0)}

/* section headings get a rule that draws itself in */
.head h2::after,.phead__in h1::after{content:"";display:block;height:2px;width:0;
  margin-top:22px;background:linear-gradient(90deg,var(--accent),transparent);
  transition:width 1.1s var(--ease-out) .15s}
.js [data-rise].is-in .head h2::after,.js .head[data-rise].is-in h2::after,
.js .phead__in [data-rise].is-in::after{width:min(220px,42%)}

/* gallery tiles reveal in reading order as each row arrives */
.js .tile{opacity:0;transform:translateY(24px) scale(.97);
  transition:opacity .6s var(--ease-out),transform .6s var(--ease-out),
             border-color .3s,box-shadow .3s}
.js .tile.is-in{opacity:1;transform:none}
.js .tile{transition-delay:calc(var(--i,0) * 45ms)}

@media (prefers-reduced-motion:reduce){
  .fx__flow path,.fx__nodes circle{animation:none}
  .fx__flow path{stroke-dashoffset:0;opacity:.35}
  .js [data-rise="left"],.js [data-rise="right"],.js [data-rise="scale"]{transform:none}
  .js [data-clip]{clip-path:none}
  .js .tile{opacity:1;transform:none;transition-delay:0s}
  .head h2::after,.phead__in h1::after{width:min(220px,42%);transition:none}
}

/* ── hero work wall ──────────────────────────────────────────────
   Columns of real pieces drifting at different speeds. Each column
   holds its list twice and moves exactly -50%, so the loop closes on
   itself with no JS and no runtime cloning. */
.hero__in{display:grid;grid-template-columns:minmax(0,1.05fr) minmax(0,.95fr);
  gap:clamp(30px,5vw,72px);align-items:center}
.hero__copy{min-width:0}
.hero__wall{min-width:0;position:relative}

.hwall{display:grid;grid-template-columns:repeat(3,1fr);gap:14px;
  height:clamp(460px,72vh,720px);overflow:hidden;
  -webkit-mask-image:linear-gradient(180deg,transparent,#000 14%,#000 86%,transparent);
          mask-image:linear-gradient(180deg,transparent,#000 14%,#000 86%,transparent)}
.hwall__col{overflow:hidden}
.hwall__run{display:flex;flex-direction:column;gap:14px;
  animation:hwall var(--t,40s) linear infinite;animation-direction:var(--dir,normal)}
.hwall img{width:100%;height:auto;border-radius:10px;border:1px solid var(--line);
  background:var(--bg-2)}
@keyframes hwall{from{transform:translateY(0)}to{transform:translateY(-50%)}}

/* the wall pauses while a pointer is over it, so a piece can be looked at */
.hero__wall:hover .hwall__run,.hero__wall:focus-within .hwall__run{animation-play-state:paused}

/* mobile falls back to one horizontal strip under the copy */
.hstrip{display:none;overflow:hidden;margin-top:34px;
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent);
          mask-image:linear-gradient(90deg,transparent,#000 8%,#000 92%,transparent)}
.hstrip__run{display:flex;gap:12px;width:max-content;animation:hstrip 46s linear infinite}
.hstrip img{height:112px;width:auto;border-radius:9px;border:1px solid var(--line)}
@keyframes hstrip{from{transform:translateX(0)}to{transform:translateX(-50%)}}

@media (max-width:900px){
  .hero__in{grid-template-columns:1fr}
  .hwall{display:none}
  .hstrip{display:block}
  .hero__wall{order:2}
}

/* monochrome emphasis: no hue left to lean on, so the key phrase
   inverts to a white block with black type */
.hero h1 em{font-style:normal;color:var(--bg);background:var(--white);
  padding:0 .14em;box-decoration-break:clone;-webkit-box-decoration-break:clone}

/* inline prose links need a non-colour affordance for the same reason */
.lede a,.sec p a,.foot__brand a,.prose a{text-decoration:underline;
  text-underline-offset:3px;text-decoration-thickness:1px;
  text-decoration-color:var(--line-2)}
.lede a:hover,.sec p a:hover,.prose a:hover{text-decoration-color:var(--white)}

@media (prefers-reduced-motion:reduce){
  .hwall__run,.hstrip__run{animation:none}
}

/* The headline was sized for a full-width hero. Now that it shares the row with
   the work wall it needs its own scale, or it wraps to six lines and pushes the
   buttons below the fold. */
.hero h1{font-size:clamp(2.25rem,4.3vw,4.05rem);max-width:12em}
.hero .lede{margin-top:24px;font-size:clamp(1rem,1.15vw,1.09rem);max-width:34em}
.hero__cta{margin-top:32px}
.hero__meta{margin-top:32px}
.hero__in{min-height:auto;padding-block:clamp(96px,11vh,150px) clamp(56px,7vh,96px)}
@media (max-width:900px){
  .hero h1{font-size:clamp(2.4rem,8.4vw,3.6rem);max-width:14em}
  .hero__in{padding-block:clamp(84px,10vh,120px) 56px}
}

/* ── long-form prose ─────────────────────────────────────────────
   The pages carry real copy now, so body text needs a measure it can
   be read at and headings need room around them. */
.prose{max-width:74ch}
.prose h2{font-size:clamp(1.55rem,2.7vw,2.25rem);margin:clamp(38px,4vw,58px) 0 20px}
.prose h2:first-child{margin-top:0}
.prose h3{font-size:1.13rem;margin:30px 0 10px;color:var(--white)}
.prose p{color:var(--txt-3);margin-bottom:16px;max-width:68ch}
.prose p b{color:var(--white);font-weight:600}

/* ── service index links ── */
.slinks{display:grid;grid-template-columns:repeat(auto-fit,minmax(268px,1fr));gap:12px}
.slink{display:grid;grid-template-columns:1fr auto;align-items:center;gap:14px;
  padding:20px 22px;border:1px solid var(--line);border-radius:var(--r);
  background:var(--surface);color:var(--white);
  transition:border-color .25s,background .25s,transform .25s var(--ease-out)}
.slink b{display:block;font-weight:600;font-size:1.04rem}
.slink span{display:block;color:var(--txt-3);font-size:.92rem;margin-top:5px}
.slink svg{width:16px;height:16px;color:var(--txt-4);transition:transform .3s var(--ease-out),color .25s}
.slink:hover,.slink:focus-visible{border-color:var(--line-2);background:var(--surface-2);transform:translateY(-2px)}
.slink:hover svg,.slink:focus-visible svg{transform:translateX(4px);color:var(--white)}

/* ── expanded service rows ── */
.rows--detail{display:grid;gap:0}
.srv{display:grid;grid-template-columns:auto 1fr;gap:20px;align-items:start;
  padding:clamp(26px,3vw,38px) 0;border-top:1px solid var(--line)}
.srv:last-child{border-bottom:1px solid var(--line)}
.srv__body{min-width:0}
.srv h3{font-size:clamp(1.35rem,2.2vw,1.75rem);margin-bottom:10px}
.srv__sum{color:var(--txt-2);max-width:60ch}
.srv__for{color:var(--txt-4);font-size:.94rem;margin-top:12px;max-width:60ch}
.srv__for b{color:var(--txt-3);font-weight:600}
.srv__list{margin-top:18px;display:grid;gap:9px}
.srv__list li{position:relative;padding-left:22px;color:var(--txt-3);font-size:.96rem;max-width:62ch}
.srv__list li::before{content:"";position:absolute;left:0;top:.62em;width:9px;height:1px;
  background:var(--line-2)}
@media (max-width:620px){
  .srv{grid-template-columns:1fr;gap:8px}
}

/* ── pricing plans ───────────────────────────────────────────── */
.plans{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:18px;align-items:start}
.plan{position:relative;border:1px solid var(--line);border-radius:var(--r-lg);padding:32px 28px;
  background:var(--surface);display:flex;flex-direction:column;gap:14px}
.plan--pop{border-color:var(--white);background:var(--surface-2)}
.plan__tag{position:absolute;top:-11px;left:28px;background:var(--white);color:var(--bg);
  font-size:.72rem;letter-spacing:.08em;text-transform:uppercase;font-weight:600;
  padding:4px 10px;border-radius:99px}
.plan h3{font-size:1.35rem}
.plan__for{color:var(--txt-4);font-size:.94rem}
.plan__price{font-family:'Archivo',sans-serif;font-size:clamp(2rem,3.4vw,2.7rem);font-weight:700;
  letter-spacing:-.03em;line-height:1}
.plan__price span{font-size:.9rem;font-weight:400;color:var(--txt-4);letter-spacing:0}
.plan__list{display:grid;gap:9px;margin:6px 0 18px}
.plan__list li{position:relative;padding-left:22px;color:var(--txt-3);font-size:.95rem}
.plan__list li::before{content:"";position:absolute;left:0;top:.62em;width:9px;height:1px;background:var(--line-2)}
.plan .btn{margin-top:auto}

.addons{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:12px}
.addon{border:1px solid var(--line);border-radius:var(--r);padding:18px 20px;background:var(--surface)}
.addon b{display:block;font-weight:600}
.addon span{display:block;margin-top:6px;color:var(--white);font-weight:600}
.addon i{font-style:normal;color:var(--txt-4);font-weight:400;font-size:.88rem}

/* ── case studies ────────────────────────────────────────────── */
.cases{display:grid;gap:0}
.case{padding:clamp(30px,4vw,48px) 0;border-top:1px solid var(--line)}
.case:last-child{border-bottom:1px solid var(--line)}
.case__head{margin-bottom:22px}
.case__head h2{font-size:clamp(1.5rem,2.8vw,2.1rem)}
.case__meta{color:var(--txt-4);font-size:.94rem;margin-top:8px}
.case__grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(240px,1fr));gap:26px}
.case__grid h3{font-size:.78rem;letter-spacing:.1em;text-transform:uppercase;color:var(--txt-4);
  margin-bottom:10px;font-weight:600}
.case__grid p{color:var(--txt-3);font-size:.97rem}
.case__link{margin-top:22px}
.case__link a{text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--line-2)}

/* ── blog ────────────────────────────────────────────────────── */
.pcards{display:grid;grid-template-columns:repeat(auto-fit,minmax(290px,1fr));gap:16px}
.pcard{display:flex;flex-direction:column;gap:10px;padding:26px 26px 24px;border:1px solid var(--line);
  border-radius:var(--r-lg);background:var(--surface);color:var(--white);
  transition:border-color .25s,background .25s,transform .3s var(--ease-out)}
.pcard:hover,.pcard:focus-visible{border-color:var(--line-2);background:var(--surface-2);transform:translateY(-3px)}
.pcard__cat{font-size:.72rem;letter-spacing:.1em;text-transform:uppercase;color:var(--txt-4);font-weight:600}
.pcard h2{font-size:1.2rem;line-height:1.25}
.pcard p{color:var(--txt-3);font-size:.93rem;flex:1}
.pcard__meta{color:var(--txt-4);font-size:.85rem}
.phead--post .lede{max-width:60ch}
.post__meta{color:var(--txt-4);font-size:.92rem;margin-top:16px}
.eyebrow a{text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--line-2)}

/* four-up card grid, and a wider deliverables list */
.cards--4{display:grid;grid-template-columns:repeat(auto-fit,minmax(230px,1fr));gap:16px}
.srv__list--wide{grid-template-columns:repeat(auto-fit,minmax(300px,1fr));display:grid;gap:10px 28px}

/* ── founder band ─────────────────────────────────────────────
   The portrait is a cutout with no background of its own, so on a
   near-black page it would float. The frame gives it a surface to
   stand on and the ::after fades the bottom edge into --bg, which
   is what a real photo backdrop would do.
   ───────────────────────────────────────────────────────────── */
.founder{display:grid;gap:clamp(30px,4vw,60px);align-items:center}
@media (min-width:900px){.founder{grid-template-columns:minmax(0,.62fr) minmax(0,1fr)}}
.founder__frame{position:relative;border:1px solid var(--line);border-radius:var(--r-lg);
  overflow:hidden;background:
    radial-gradient(120% 80% at 50% 0%,var(--surface-2),transparent 68%),var(--bg-2)}
.founder__frame img{display:block;width:100%;height:auto;
  transition:transform .6s var(--ease-out)}
.founder__frame:hover img{transform:scale(1.02)}
.founder__frame::after{content:"";position:absolute;inset:auto 0 0 0;height:34%;
  background:linear-gradient(to top,var(--bg),transparent);pointer-events:none}
.founder__frame figcaption{position:absolute;left:0;right:0;bottom:0;z-index:1;
  padding:20px 22px;font-size:.85rem;color:var(--txt-3)}
.founder__frame figcaption b{display:block;font-family:'Archivo',sans-serif;font-weight:600;
  font-size:1.02rem;letter-spacing:-.02em;color:var(--white);margin-bottom:3px}
.founder h2{font-size:clamp(1.7rem,3vw,2.5rem);margin-bottom:20px}
.founder p{color:var(--txt-3);font-size:1.05rem}
.founder p+p{margin-top:18px}
.founder .lede{color:var(--txt-2);font-size:1.12rem}
.facts{display:flex;flex-wrap:wrap;gap:8px;margin-top:26px;padding:0;list-style:none}
.facts li{font-size:.84rem;color:var(--txt-3);border:1px solid var(--line);
  border-radius:999px;padding:7px 14px;background:var(--surface)}
.facts a{text-decoration:underline;text-underline-offset:3px;text-decoration-color:var(--line-2)}

/* ── CV rows ──────────────────────────────────────────────────
   Same rhythm as the service rows, but the first column carries a
   date range rather than a two-digit index, so it needs the width.
   ───────────────────────────────────────────────────────────── */
@media (min-width:820px){
  .rows--cv .row{grid-template-columns:9.5rem minmax(0,15rem) minmax(0,1fr)}
}
.rows--cv .row__n{white-space:nowrap;color:var(--txt-4);letter-spacing:.02em}
.rows--cv .row h3 span{display:block;font-family:'Space Grotesk',monospace;font-size:.8rem;
  font-weight:400;color:var(--txt-4);margin-top:6px;letter-spacing:.02em}

/* the nav carries eight items now, so it needs to breathe */
@media (min-width:1181px){
  .menu{gap:clamp(10px,1.2vw,20px)}
  .menu>a{font-size:.94rem;white-space:nowrap}
}

/* ════════════════════════════════════════════════════════════════
   3D, INTRO REVEAL AND CURSOR

   No library. Vanta needs Three.js, Locomotive hijacks scroll and
   AOS duplicates the IntersectionObserver already here. Everything
   below is CSS transforms plus about 90 lines of JS, so first load
   stays where it was.

   Only transform and opacity animate, so the compositor does the
   work and the main thread stays free for the 180-tile gallery.
   ════════════════════════════════════════════════════════════════ */

/* ── intro: the wordmark draws itself, then lifts away ── */
/* Default hidden. Only JS ever dismisses this curtain, so if scripting is
   off it must never appear at all, or a no-JS visitor stares at a black
   screen forever. The inline <script> in <head> adds .js synchronously
   before first paint, so JS users see no flash of un-curtained page. */
.intro{display:none}
.js .intro{position:fixed;inset:0;z-index:9999;background:var(--bg);
  display:grid;place-items:center;transition:opacity .5s ease,visibility .5s}
.intro[hidden],.intro.is-done{opacity:0;visibility:hidden;pointer-events:none}
.intro svg{width:min(58vw,520px);height:auto;overflow:visible}
.intro text{font-family:'Archivo',system-ui,sans-serif;font-weight:700;
  font-size:88px;letter-spacing:-.045em;
  fill:transparent;stroke:#fff;stroke-width:.9;
  stroke-dasharray:var(--len,900);stroke-dashoffset:var(--len,900);
  animation:introDraw 1.15s cubic-bezier(.5,0,.2,1) forwards,
            introFill .5s ease .95s forwards}
.intro__bar{transform-origin:50% 100%;transform:scaleY(0);
  animation:introBar .5s cubic-bezier(.5,0,.2,1) 1.25s forwards}
@keyframes introDraw{to{stroke-dashoffset:0}}
@keyframes introFill{to{fill:#fff;stroke-width:0}}
@keyframes introBar{0%{transform:scaleY(0)}100%{transform:scaleY(1)}}
/* the page underneath settles in as the curtain leaves */
.js body.is-loading{overflow:hidden}
.intro-out main,.intro-out .nav{animation:pageIn .7s cubic-bezier(.16,1,.3,1) both}
@keyframes pageIn{from{opacity:0;transform:translateY(14px)}to{opacity:1;transform:none}}

/* ── custom cursor: a particle trail on one canvas ──
   A dot and a ring were too faint to read on a black page once the
   native pointer was hidden, so this paints a trail instead. The
   canvas covers the viewport and never intercepts a click. difference
   blending keeps it legible over the black chrome and over the colour
   portfolio pieces alike. */
.curfx{position:fixed;inset:0;z-index:9997;pointer-events:none;
  mix-blend-mode:difference}
/* the native pointer is only hidden after the canvas has painted a frame */
html.has-cur,html.has-cur a,html.has-cur button,html.has-cur .tile,
html.has-cur input,html.has-cur textarea,html.has-cur select{cursor:none}

/* ── 3D: depth on entry and tilt on hover ── */
.work,.cards,.plans,.pcards,.slinks,.gal,.cases{perspective:1400px}
.js .tile,.js .card,.js .piece,.js .plan,.js .pcard,.js .slink{
  transform-style:preserve-3d;will-change:transform}
/* entry now arrives with rotation, not just a slide */
.js [data-stagger]>*{transform:translateY(30px) rotateX(9deg)}
.js [data-stagger].is-in>*{transform:none}
.js .tile{transform:translateY(24px) rotateX(11deg) scale(.97)}
.js .tile.is-in{transform:none}
/* JS writes the transform inline; this only supplies the easing so the
   tilt settles instead of snapping when the pointer leaves */
.js .tile,.js .card,.js .piece,.js .plan,.js .pcard,.js .slink{
  transition:transform .25s var(--ease-out),opacity .6s var(--ease-out),
             border-color .25s,background .25s}
.tilt{transition-duration:.08s}

/* ── scroll-driven 3D, where the browser supports it natively ──
   animation-timeline: view() runs entirely off the main thread. It is
   an enhancement: without it the IntersectionObserver reveal already
   put everything on screen, so nothing depends on it. */
@supports (animation-timeline: view()){
  .js .sec > .shell > .head{
    animation:depth linear both;
    animation-timeline:view();
    animation-range:entry 5% cover 32%;
  }
  @keyframes depth{
    from{opacity:.25;transform:perspective(1200px) translateZ(-90px) rotateX(7deg)}
    to{opacity:1;transform:none}
  }
  .hwall__col{animation:wallDepth linear both;animation-timeline:view();
    animation-range:entry 0% cover 60%}
  @keyframes wallDepth{
    from{transform:perspective(1000px) rotateY(9deg) translateZ(-40px)}
    to{transform:none}
  }
}

@media (prefers-reduced-motion:reduce){
  .intro,.js .intro{display:none}
  .intro text,.intro__bar{animation:none;fill:#fff;stroke-width:0;transform:none}
  .curfx{display:none}
  html.has-cur,html.has-cur a,html.has-cur button,html.has-cur input,
  html.has-cur textarea,html.has-cur select,html.has-cur .tile{cursor:auto}
  .js [data-stagger]>*,.js .tile{transform:none}
  .tilt{transform:none !important}
  .intro-out main,.intro-out .nav{animation:none}
  @supports (animation-timeline: view()){
    .js .sec > .shell > .head,.hwall__col{animation:none}
  }
}
/* touch devices get no custom cursor and no tilt */
@media (hover:none){
  .curfx{display:none}
  html.has-cur,html.has-cur a,html.has-cur button,html.has-cur input,
  html.has-cur textarea,html.has-cur select,html.has-cur .tile{cursor:auto}
  .tilt{transform:none !important}
}

/* The eight-item nav plus the CTA leaves no room for the founder sublabel
   until well past 1300px; below that it wrapped onto a second line and
   pushed the bar out of alignment. */
@media (max-width:1400px){ .logo b{display:none} }
@media (min-width:1181px){ .menu>a:not(.btn){padding:10px 11px;font-size:.93rem} }

/* The curtain used to cross-fade, which left the wordmark hanging as a ghost
   over the hero for half a second. It now lifts and scales away instead, and
   goes faster than the page fades in, so the two never overlap visibly. */
.js .intro{transition:opacity .38s ease,visibility .38s,transform .38s cubic-bezier(.5,0,.2,1)}
.js .intro.is-done{transform:translateY(-4%) scale(1.04)}
.js .intro svg{transition:opacity .22s ease}
.js .intro.is-done svg{opacity:0}

/* ── WebGL hero layer ──
   Sits over the SVG mesh. When WebGL is running, the SVG steps back so
   the two do not fight; when it is not, the SVG is the whole effect. */
.scene3d{position:absolute;inset:0;width:100%;height:100%;display:block}
.hero__fx.has-3d .fx__mesh path{stroke:rgba(255,255,255,.07)}
.hero__fx.has-3d .fx__nodes circle{opacity:.12}
@media (prefers-reduced-motion:reduce){ .scene3d{display:none} }

/* ── scroll reveals, part two ─────────────────────────────────────
   The first pass revealed whole containers. A 13-element FAQ landing
   in one go is not a reveal, it is a repaint. These variants give the
   long blocks per-child timing and give the page some direction, so a
   full scroll is not the same fade-up nine times running. */

/* per-child flow: each heading and paragraph waits its own turn */
.js [data-flow] > *{opacity:0;transform:translateY(22px);
  /* deliberately quicker than the display blocks: this is body copy, and a
     reader who scrolls fast should never be waiting on a paragraph to fade */
  transition:opacity .38s var(--ease-out),transform .45s var(--ease-out)}
.js [data-flow] > *.is-in{opacity:1;transform:none}
/* headings inside a flow lead very slightly from the left */
.js [data-flow] > h2{transform:translate(-14px,20px)}
.js [data-flow] > h3{transform:translate(-9px,16px)}
.js [data-flow] > h2.is-in,.js [data-flow] > h3.is-in{transform:none}

/* alternating entry: odd children from the left, even from the right */
.js [data-alt][data-stagger] > *:nth-child(odd){transform:translate(-26px,22px)}
.js [data-alt][data-stagger] > *:nth-child(even){transform:translate(26px,22px)}
.js [data-alt][data-stagger].is-in > *{transform:none}

/* work grid arrives out of depth rather than sliding */
.js [data-depth][data-stagger] > *{transform:perspective(1100px) translateZ(-120px) translateY(34px)}
.js [data-depth][data-stagger].is-in > *{transform:none}

@supports (animation-timeline: view()){
  /* the stats band drifts against the scroll, off the main thread */
  .js [data-drift]{animation:drift linear both;animation-timeline:view();
    animation-range:entry 0% exit 100%}
  @keyframes drift{from{transform:translateY(26px)}to{transform:translateY(-26px)}}
  /* quotes tilt upright as they pass through the viewport */
  .js .quote{animation:quoteTilt linear both;animation-timeline:view();
    animation-range:entry 10% cover 45%}
  @keyframes quoteTilt{
    from{transform:perspective(1000px) rotateX(9deg) translateY(18px);opacity:.45}
    to{transform:none;opacity:1}
  }
}

@media (prefers-reduced-motion:reduce){
  .js [data-flow] > *,
  .js [data-alt][data-stagger] > *,
  .js [data-depth][data-stagger] > *{opacity:1;transform:none;transition:none}
  @supports (animation-timeline: view()){
    .js [data-drift],.js .quote{animation:none}
  }
}
