/* =========================================================
   The Context Seller — portfolio
   Light editorial base + WebGL depth layer
   ========================================================= */

/* ---------- fonts (self-hosted, no external requests) ----------
   One Inter, not six. The variable cut is 47 KB and carries every weight
   from 100 to 900 on a continuous axis — which is what the headline and the
   loader need anyway, since they animate their weight rather than step it.
   Five static cuts alongside it were 112 KB of the same typeface said again. */
@font-face{font-family:'Inter Var';font-style:normal;font-weight:100 900;font-display:swap;src:url('../assets/fonts/inter-latin-wght-normal.woff2') format('woff2-variations')}
@font-face{font-family:'Playfair Display';font-style:normal;font-weight:600;font-display:swap;src:url('../assets/fonts/playfair-display-latin-600-normal.woff2') format('woff2')}
@font-face{font-family:'Playfair Display';font-style:normal;font-weight:700;font-display:swap;src:url('../assets/fonts/playfair-display-latin-700-normal.woff2') format('woff2')}

*,*::before,*::after{margin:0;padding:0;box-sizing:border-box}

:root{
  --ink:#0f0e0d;--ink-2:#2a2825;--ink-3:#4a4744;--ink-4:#6b6760;
  --rule:#e4e2de;--rule-2:#d8d5cf;--bg:#fff;--warm:#faf9f7;--panel:#fff;--dark:#0f0e0d;
  /* One accent, and it is graphite: buttons, rules and marks are the same ink
     as the type, so nothing on the page competes with the photograph.
     The single saturated colour is the cyan already lit across his face —
     --spark for marks on paper, --spark-lit where the ground is dark. */
  --accent:#141312;
  --spark:#2f7d76;                  /* the teal, kept for marks only */
  --spark-lit:#0fb3a1;
  --cur:#ff5f14;                    /* the cursor stays warm */
  --on-ink:#fff;                    /* type sitting on an ink-filled shape */
  --nav-bg:#fff;
  --shadow:rgba(15,14,13,.14);
  --sans:'Inter Var',system-ui,-apple-system,'Segoe UI',sans-serif;
  --sans-var:'Inter Var','Inter',system-ui,-apple-system,'Segoe UI',sans-serif;
  --serif:'Playfair Display',Georgia,serif;
  --col:960px;
  --ease:cubic-bezier(.2,.7,.3,1);
}

html{color-scheme:light}

html{-webkit-text-size-adjust:100%}
html.lenis,html.lenis body{height:auto}
.lenis.lenis-smooth{scroll-behavior:auto!important}
.lenis.lenis-stopped{overflow:hidden}

body{
  font-family:var(--sans);background:var(--bg);color:var(--ink);
  font-size:15px;line-height:1.65;-webkit-font-smoothing:antialiased;
  text-rendering:optimizeLegibility;
  /* clip, not hidden: overflow-x:hidden turns the body into a scroll container,
     which silently kills every position:sticky on the page. clip trims the same
     overhang without creating one. */
  overflow-x:hidden;
  overflow-x:clip;
}

img{max-width:100%;display:block}
::selection{background:var(--ink);color:var(--on-ink)}

.skip{position:absolute;left:-9999px;top:0;z-index:100000;background:var(--ink);color:var(--on-ink);padding:12px 20px;font-size:13px}
.skip:focus{left:12px;top:12px}
:focus-visible{outline:2px solid var(--accent);outline-offset:3px;border-radius:2px}

/* =========================================================
   LOAD CURTAIN
   ========================================================= */
.curtain{
  position:fixed;inset:0;z-index:99999;background:var(--bg);
  display:flex;align-items:center;justify-content:center;
  pointer-events:none;
}
/* the curtain fades itself in from CSS: the count starts while the modules
   are still loading, so it cannot wait on any of them */
.curtain-in{display:flex;flex-direction:column;align-items:center;gap:10px;opacity:0;animation:cin .34s cubic-bezier(.22,1,.36,1) .05s forwards}
@keyframes cin{to{opacity:1}}
/* the count itself: variable weight, tabular figures so the digits never
   jitter sideways while the number runs */
.curtain-num{
  font-family:var(--sans-var);font-weight:800;font-variation-settings:'wght' 800;
  font-size:clamp(64px,11vw,124px);line-height:.9;letter-spacing:-.045em;
  color:var(--ink);font-variant-numeric:tabular-nums;
  display:block;
}
.curtain-mark{
  font-family:var(--sans);font-size:10.5px;font-weight:600;letter-spacing:.34em;
  text-transform:uppercase;color:var(--ink-4);text-indent:.34em;
}
.curtain-bar{
  display:block;width:clamp(150px,20vw,230px);height:2px;background:var(--rule);
  margin-top:6px;overflow:hidden;
}
.curtain-bar>i{
  display:block;width:100%;height:100%;background:var(--spark);
  transform:scaleX(0);transform-origin:0 50%;
}
/* failsafe: if the module never boots, the page still becomes visible */
.curtain{animation:curtain-failsafe 1ms linear 7s forwards}
@keyframes curtain-failsafe{to{opacity:0;visibility:hidden}}

/* =========================================================
   CUSTOM CURSOR
   ========================================================= */
/* the cursor keeps its own warm colour: it is the one mark on the page that
   is not part of the design, so it is allowed to be the one that stands out */
.cursor{position:fixed;top:0;left:0;z-index:99998;pointer-events:none;opacity:0}
.cursor-ring{
  position:absolute;width:30px;height:30px;margin:-15px 0 0 -15px;border-radius:50%;
  border:1.5px solid var(--cur);box-shadow:0 0 16px 1px rgba(255,106,31,.42);
  transition:width .28s var(--ease),height .28s var(--ease),margin .28s var(--ease),opacity .2s;
}
.cursor-dot{
  position:absolute;width:5px;height:5px;margin:-2.5px 0 0 -2.5px;border-radius:50%;
  background:var(--cur);box-shadow:0 0 12px 2px rgba(255,106,31,.6);
}
.cursor.is-hot .cursor-ring{width:52px;height:52px;margin:-26px 0 0 -26px}
.cursor.is-hot .cursor-dot{opacity:0}
body.has-cursor,body.has-cursor a,body.has-cursor button{cursor:none}

/* =========================================================
   SCROLL RAIL
   ========================================================= */
.rail{position:fixed;top:0;left:0;right:0;height:2px;z-index:9998;background:transparent;pointer-events:none}
.rail-fill{display:block;height:100%;width:100%;background:var(--ink);transform:scaleX(0);transform-origin:0 50%}

/* =========================================================
   NAV
   ========================================================= */
nav{
  position:fixed;top:0;left:0;right:0;z-index:9997;
  background:var(--nav-bg);backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  border-bottom:1px solid var(--rule);
  padding:0 52px;height:60px;display:flex;align-items:center;justify-content:space-between;
}
.brand{display:flex;align-items:center;gap:11px;text-decoration:none}
/* the mark is black on transparent, so it flips rather than sitting in a box */
.brand img{width:22px;height:22px}
.brand span{font-size:13px;font-weight:600;color:var(--ink)}
.nav-right{display:flex;align-items:center;gap:20px}
.nav-li{
  position:relative;display:inline-flex;align-items:center;gap:6px;
  font-size:12.5px;font-weight:500;color:var(--ink-2);text-decoration:none;transition:color .15s;
}
.nav-li::after{
  content:'';position:absolute;left:0;right:0;bottom:-3px;height:1px;background:var(--ink);
  transform:scaleX(0);transform-origin:100% 50%;transition:transform .38s var(--ease);
}
.nav-li:hover{color:var(--ink)}
.nav-li:hover::after{transform:scaleX(1);transform-origin:0 50%}
.nav-icon::after{display:none}
.nav-cta{
  display:inline-flex;align-items:center;background:var(--ink);color:var(--on-ink);
  padding:9px 22px;border-radius:4px;font-size:12px;font-weight:600;text-decoration:none;
  letter-spacing:.04em;text-transform:uppercase;transition:opacity .15s;white-space:nowrap;
}
.nav-cta:hover{opacity:.85}


.wrap{max-width:var(--col);margin:0 auto;padding:0 52px}

/* =========================================================
   HERO
   ========================================================= */
/* the cutout is anchored to the viewport, not to a column, so the hero has to
   clip it on the x axis — otherwise the overhang widens the page, which
   stretches the fixed nav and pushes the CTA off screen */
.hero{
  padding:130px 0 76px;position:relative;min-height:min(82svh,800px);
  display:flex;align-items:center;overflow-x:clip;isolation:isolate;
}
.hero .wrap{max-width:1180px;width:100%;position:relative;z-index:2}
.hero-grid{display:grid;grid-template-columns:minmax(0,640px);align-items:center}
.hero-eye{
  font-size:11px;font-weight:600;letter-spacing:.15em;text-transform:uppercase;
  color:var(--ink-4);margin-bottom:26px;display:flex;align-items:center;gap:12px;
}
.hero-eye::after{content:'';height:1px;width:56px;background:var(--rule);flex:none}

/* the poster voice: the one place on the page that shouts.
   Set in the variable cut — every letter carries its own weight and the
   weight follows the cursor (js/letters.js), so the type is alive. */
.hero-h1{
  font-family:var(--sans-var);font-weight:800;font-variation-settings:'wght' 800;
  font-size:clamp(29px,4.2vw,53px);line-height:1.02;letter-spacing:-.012em;
  text-transform:uppercase;color:var(--ink);margin-bottom:26px;
}
/* letters grow by transform only — layout never moves, so the copy below the
   headline cannot shift while the type is being played with. nowrap means the
   line can never rewrap as the weights breathe. */
.hero-h1 .lt{
  display:inline-block;font-weight:800;font-variation-settings:'wght' 800;
  will-change:transform;
}
.anim-line>span{white-space:nowrap}
/* the line is a mask only while it is being revealed — after that a letter
   that grows past the column edge must be free to do so */
.hero-h1.is-set .anim-line{overflow:visible}

/* the plume: a warm light that trails the cursor behind the type */
.hero-glow{
  position:absolute;left:0;top:0;z-index:1;pointer-events:none;opacity:0;
  width:clamp(380px,46vw,700px);aspect-ratio:1;border-radius:50%;
  background:radial-gradient(circle closest-side,
    rgba(255,96,20,.34) 0%, rgba(255,84,20,.17) 34%, rgba(255,70,20,0) 70%);
  filter:blur(24px);
}
.hl-accent{color:var(--spark)}

.hero-lead{
  font-size:16.5px;color:var(--ink-2);line-height:1.72;max-width:470px;
  padding-left:16px;border-left:2px solid var(--accent);
}
.hero-lead strong{color:var(--ink);font-weight:600}

.hero-acts{display:flex;flex-wrap:wrap;gap:12px;margin-top:30px}
.btn-p,.btn-s{
  display:inline-flex;align-items:center;gap:9px;padding:14px 26px;border-radius:4px;
  font-size:13px;font-weight:600;letter-spacing:.02em;text-decoration:none;
  transition:transform .3s var(--ease),opacity .18s,background .18s,color .18s;
}
.btn-p{background:var(--accent);color:#fff}
.btn-p:hover{opacity:.9}
.btn-s{color:var(--ink);border:1.5px solid var(--rule-2)}
.btn-s:hover{border-color:var(--ink);background:var(--ink);color:var(--on-ink)}

/* ---- the cutout ----
   Anchored to the top, a few pixels above the fold. The clip pushes in, and
   past two seconds the crown is cut by the edge of the original frame; parking
   that edge just off screen means the cut is never seen, and he reads as
   running off the top of the page rather than as a cropped photograph. */
/* The figure spans the hero exactly, top to bottom. Both ends of the footage
   are cut — the crown by the top of the original frame, the chest by its
   bottom — so both are parked on an edge the eye already reads as an edge:
   the top of the screen, and the line where the next section starts. Nothing
   is faded and nothing floats. */
.hero-figure{
  position:absolute;right:-13%;top:0;bottom:0;z-index:1;
  width:auto;aspect-ratio:900/661;
  pointer-events:none;
}
.hero-figure>*{position:absolute;inset:0;width:100%;height:100%;object-fit:contain;object-position:top}
/* the packed source is twice the height of what it shows; it never gets drawn,
   it is only there for the shader to read frames out of */
#hero-film{opacity:0;pointer-events:none}
.hero-still{transition:opacity .5s ease}
#hero-gl{opacity:0;transition:opacity .8s ease;transform:translate3d(var(--dx,0),var(--dy,0),0)}
body.gl-hero #hero-gl{opacity:1}
body.gl-hero .hero-still{opacity:0}
.hint-dot{width:6px;height:6px;border-radius:50%;background:var(--spark);animation:pulse 2.4s ease-in-out infinite}
@keyframes pulse{0%,100%{opacity:.35;transform:scale(.8)}50%{opacity:1;transform:scale(1.15)}}


/* =========================================================
   PROOF
   ========================================================= */
/* the band that the hero's portrait disappears behind. Its ground has to span
   the window, not the text column — a column-width ground cut a hard step
   through his shoulder wherever the figure was wider than the copy. */
.proof-band{
  position:relative;z-index:2;background:var(--bg);
  border-top:1px solid var(--rule);border-bottom:1px solid var(--rule);
}
.proof{
  padding:42px 0;display:grid;grid-template-columns:repeat(5,1fr);perspective:800px;
}
.pf{text-align:center;position:relative;padding:0 12px}
.pf:not(:last-child)::after{content:'';position:absolute;right:0;top:10px;bottom:10px;width:1px;background:var(--rule)}
.pf-n{font-family:var(--serif);font-size:36px;font-weight:700;color:var(--ink);letter-spacing:-.03em;line-height:1;display:inline-block;font-variant-numeric:tabular-nums}
.pf-l{font-size:11.5px;color:var(--ink-3);margin-top:7px;line-height:1.45}

/* =========================================================
   SHARED SECTION TYPE
   ========================================================= */
.sec{padding:70px 0;position:relative}
.eye{font-size:11px;font-weight:600;letter-spacing:.15em;text-transform:uppercase;color:var(--ink-4);margin-bottom:11px}
.h2{font-family:var(--serif);font-size:clamp(25px,3vw,33px);font-weight:700;color:var(--ink);letter-spacing:-.02em;margin-bottom:22px;line-height:1.18}
.lead{font-size:16px;color:var(--ink-2);line-height:1.8;max-width:690px}
.lead strong{color:var(--ink);font-weight:600}
.lead p+p{margin-top:15px}
.div{border:none;border-top:1px solid var(--rule);margin:0}

.tags{display:flex;flex-wrap:wrap;gap:8px;margin-top:26px}
.tag{font-size:11.5px;color:var(--ink-3);border:1px solid var(--rule);padding:5px 14px;border-radius:2px;transition:border-color .3s,color .3s}
.tag:hover{border-color:var(--accent);color:var(--accent)}

/* =========================================================
   PILLARS
   ========================================================= */
.sec-tight{padding:0 0 70px}
/* A deck rather than three columns of prose. Each card sticks under the one
   before it, so scrolling collects them into a pile you can still read the
   spine of — three short reads instead of one wall of text. */
.pillar-steps{
  list-style:none;display:flex;flex-direction:column;gap:20px;
  max-width:680px;margin:0 auto;
  perspective:1400px;perspective-origin:50% 40%;

}
.pstep{
  position:sticky;top:var(--stick,96px);
  background:var(--panel);border:1px solid var(--rule);border-radius:10px;
  padding:32px 28px;min-height:186px;transform-style:preserve-3d;will-change:transform;
  box-shadow:0 22px 44px -34px var(--shadow);
  transition:border-color .4s var(--ease),background .3s ease;
}
.pstep::before{
  content:'';position:absolute;left:0;top:0;bottom:0;width:2px;border-radius:6px 0 0 6px;
  background:var(--accent);transform:scaleY(0);transform-origin:50% 0;
  transition:transform .55s var(--ease);
}
.pstep:hover::before{transform:scaleY(1)}
.pstep:hover{border-color:var(--rule-2)}
/* each layer sits at its own depth, so tilting the card parts them slightly —
   the card reads as built out of layers rather than printed on one */
.pstep-i{
  font-size:10.5px;font-weight:700;letter-spacing:.14em;color:var(--spark);
  display:block;margin-bottom:12px;transform:translateZ(34px);
}
.pstep-n{
  font-family:var(--serif);font-size:20px;font-weight:700;color:var(--ink);
  margin-bottom:10px;transform:translateZ(20px);
}
.pstep-b{font-size:13.5px;color:var(--ink-2);line-height:1.66;transform:translateZ(8px)}
/* each card parks a little lower than the one before, so the pile keeps a
   visible edge for every card under the top one */
/* The room the finished pile holds still in, as a flex child of its own.
   Sticky is bounded by the container's content box, so padding on the list
   buys nothing — and a margin on the last card buys travel for its siblings
   but not for itself, which left the third card sailing over the other two. */
.pillar-steps::after{content:'';display:block;flex:none;height:44vh}
.pstep:nth-child(1){--stick:96px}
.pstep:nth-child(2){--stick:110px}
.pstep:nth-child(3){--stick:124px}

/* =========================================================
   CASES
   ========================================================= */
.cases{display:flex;flex-direction:column;perspective:1600px}
.case{
  display:grid;grid-template-columns:172px 1fr 118px;gap:36px;padding:30px 0;
  border-top:1px solid var(--rule);align-items:start;position:relative;
  transform-style:preserve-3d;transition:transform .45s var(--ease);will-change:transform;
}
.case:last-child{border-bottom:1px solid var(--rule)}
.case::before{
  content:'';position:absolute;left:-14px;top:30px;bottom:30px;width:2px;background:var(--accent);
  transform:scaleY(0);transform-origin:top;transition:transform .5s var(--ease);
}
.case:hover::before{transform:scaleY(1)}
.case:hover{transform:translateX(4px)}
.cc{font-size:10.5px;font-weight:700;letter-spacing:.1em;text-transform:uppercase;color:var(--spark);padding-top:3px}
.ch{font-size:15.5px;font-weight:600;color:var(--ink);margin-bottom:8px;line-height:1.3}
.cb{font-size:13.5px;color:var(--ink-2);line-height:1.65}
.cm{margin-top:9px;font-size:12.5px;color:var(--ink-3);line-height:1.7}
.cm b{color:var(--ink-2);font-weight:600}
.co{text-align:right}
.co strong{display:block;font-family:var(--serif);font-size:22px;font-weight:700;color:var(--ink);letter-spacing:-.02em;line-height:1.1;margin-bottom:3px}
.co span{font-size:11px;color:var(--ink-4)}


/* =========================================================
   COURSES — the vortex
   ========================================================= */
/* A window onto a night sky, not a light switch for the whole page. Inset from
   the page edges and rounded, so the site stays light and this reads as one
   deliberately dark object sitting on it. */
.vortex{
  position:relative;padding:74px 0 84px;overflow:hidden;isolation:isolate;
  max-width:1560px;margin:26px auto;border-radius:30px;
  border:1px solid var(--rule);
  background:var(--warm);
  color:var(--ink);
}
/* the orrery is drawn behind the belt, and the paper is lifted very slightly
   at the top so the panel has a horizon rather than a flat fill */
.vortex::after{
  content:'';position:absolute;inset:0;z-index:1;pointer-events:none;
  background:linear-gradient(180deg,rgba(15,14,13,.035),transparent 22%);
}
.vortex .h2,.vortex .eye{color:var(--ink)}
.vortex .eye{color:var(--ink-4)}
.vortex-head{position:relative;z-index:3;padding-bottom:20px}
.vortex-cue{
  display:inline-flex;align-items:center;gap:9px;margin-top:16px;
  font-size:11px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--ink-4);
}
body:not(.gl-courses) .vortex-cue{display:none}

.vortex-stage{
  position:relative;height:min(80svh,720px);
  perspective:1150px;perspective-origin:50% 50%;
  touch-action:pan-y;cursor:grab;
}
.vortex-stage.is-held{cursor:grabbing}
#vortex-gl{position:absolute;inset:0;width:100%;height:100%;display:block;pointer-events:none;z-index:0}

.vortex-belt{
  position:absolute;inset:0;transform-style:preserve-3d;
  display:flex;align-items:center;justify-content:center;z-index:2;
}
.ccard{
  /* sits above the galaxy's midline, so the disc runs clear underneath it */
  margin-top:-72px;
  /* centred by left/top plus a -50% in the JS transform, not by the flex parent:
     the static position of an absolutely positioned flex child is not something
     to trust, and it was landing the front card off centre */
  position:absolute;left:50%;top:50%;width:min(420px,82vw);min-height:300px;
  padding:28px 30px 24px;border-radius:12px;
  background:var(--panel);border:1px solid var(--rule);
  box-shadow:0 30px 70px -42px var(--shadow);
  /* the card you are meant to read is sharp; the rest are scenery and are
     softened, which is what a lens does and what makes the front one pop */
  filter:blur(2.6px);
  transition:filter .5s var(--ease),box-shadow .5s var(--ease),border-color .45s var(--ease);
  display:flex;flex-direction:column;
  will-change:transform,opacity;backface-visibility:hidden;
  transition:border-color .45s var(--ease),box-shadow .45s var(--ease);
  user-select:none;
}
.ccard.is-front{
  filter:none;
  border-color:color-mix(in srgb,var(--spark) 62%,var(--rule));
  box-shadow:
    0 2px 5px -2px rgba(15,14,13,.10),
    0 26px 44px -22px rgba(15,14,13,.28),
    0 60px 110px -50px rgba(15,14,13,.30);
}
.cc-i{
  font-size:11px;font-weight:800;letter-spacing:.22em;color:var(--spark-lit);margin-bottom:16px;
}
.cc-h{
  font-family:var(--serif);font-size:20px;font-weight:700;color:var(--ink);
  line-height:1.22;letter-spacing:-.015em;margin-bottom:12px;
}
.cc-d{font-size:13.5px;color:var(--ink-2);line-height:1.6;margin-bottom:16px}

/* The skills are the part people scan, so they are shapes before they are
   sentences: short pills, one line each, no bullet characters to parse. On the
   card at the front they take the accent, which is what pulls the eye down to
   them. */
.cc-s{list-style:none;display:flex;flex-wrap:wrap;gap:6px;margin-bottom:auto}
.cc-s li{
  font-size:10.5px;line-height:1.5;letter-spacing:.015em;color:var(--ink-3);
  border:1px solid var(--rule-2);border-radius:999px;padding:3px 10px;
  white-space:nowrap;transition:color .4s var(--ease),border-color .4s var(--ease);
}
.is-front .cc-s li{color:var(--ink-2);border-color:color-mix(in srgb,var(--spark-lit) 34%,var(--rule-2))}
.cc-o{display:flex;align-items:baseline;gap:9px;margin-top:18px}
.cc-o strong{font-family:var(--serif);font-size:24px;font-weight:700;color:var(--spark-lit);
  letter-spacing:-.02em;line-height:1}
.cc-o span{font-size:11.5px;color:var(--ink-4)}
.cc-t{
  margin-top:16px;padding-top:13px;border-top:1px solid var(--rule);
  font-size:10.5px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-4);
}


/* arrows and keys, because a belt you can only drag is a belt most people
   never discover — and one nobody on a keyboard can use at all */
.belt-nav{
  position:absolute;top:50%;z-index:4;transform:translateY(-50%);
  width:46px;height:46px;display:flex;align-items:center;justify-content:center;
  border-radius:50%;border:1px solid var(--rule-2);background:var(--panel);
  color:var(--ink-2);cursor:pointer;
  opacity:.55;transition:opacity .25s,color .25s,border-color .25s,transform .25s var(--ease);
}
.belt-nav:hover{opacity:1;color:var(--ink);border-color:var(--spark-lit)}
.belt-nav:active{transform:translateY(-50%) scale(.94)}
.belt-nav:focus-visible{opacity:1}
.belt-prev{left:clamp(10px,3vw,34px)}
.belt-next{right:clamp(10px,3vw,34px)}
.vortex.is-flat .belt-nav{display:none}
@media(max-width:768px){
  .belt-nav{width:40px;height:40px;opacity:.75}
  .belt-prev{left:6px}.belt-next{right:6px}
}

.vortex-count{
  position:absolute;right:clamp(18px,4vw,48px);top:10px;z-index:3;
  display:flex;align-items:baseline;gap:4px;font-variant-numeric:tabular-nums;pointer-events:none;
}
.vortex-count b{font-family:var(--serif);font-size:26px;font-weight:700;color:var(--ink);line-height:1}
.vortex-count i{font-style:normal;color:var(--ink-4);font-size:13px}
.vortex-count span{font-size:13px;color:var(--ink-4)}

/* no WebGL or reduced motion: an ordinary grid of the same cards */
.vortex.is-flat .vortex-stage{height:auto;perspective:none;cursor:auto}
.vortex.is-flat #vortex-gl,.vortex.is-flat .vortex-count{display:none}
.vortex.is-flat .vortex-belt{
  position:static;transform-style:flat;display:grid;gap:14px;
  grid-template-columns:repeat(auto-fit,minmax(250px,1fr));
  max-width:var(--col);margin:0 auto;padding:0 52px;
}
.vortex.is-flat .ccard{
  position:static;width:auto;transform:none!important;opacity:1!important;visibility:visible!important;
  min-height:0;
}

@media(max-width:768px){
  /* the card is tall on a narrow screen and the stage is short, so the -72px
     lift was carrying the front card up over the section heading. It sits in
     its stage instead, and the card itself gets tighter. */
  .vortex{margin:16px 14px;border-radius:20px;padding:52px 0 60px}
  .vortex-stage{height:min(76svh,580px)}
  .ccard{margin-top:0;width:min(420px,86vw);min-height:0;padding:22px 20px 20px}
  .cc-i{margin-bottom:11px}
  .cc-h{font-size:21px;margin-bottom:9px}
  .cc-d{font-size:13px;line-height:1.55;margin-bottom:13px}
  .cc-s li{font-size:11px;padding:4px 9px}
  .cc-o{margin-top:14px}
  .vortex.is-flat .vortex-stage{height:auto}
  .vortex.is-flat .vortex-belt{padding:0 20px}
}

/* =========================================================
   AGENT CONSTELLATION
   ========================================================= */
.agents{padding:70px 0;background:var(--warm);border-top:1px solid var(--rule);border-bottom:1px solid var(--rule)}
.constellation{margin-top:30px;position:relative}
/* the ring stands in a pool of light with a shadow on the floor beneath it —
   cards floating on flat paper read as a screenshot, not an object */
.constellation::before{
  content:'';position:absolute;left:50%;top:46%;translate:-50% -50%;
  width:min(1180px,96%);aspect-ratio:2.1;z-index:0;pointer-events:none;
  background:
    radial-gradient(ellipse 46% 40% at 50% 92%,rgba(15,14,13,.16),rgba(15,14,13,0) 70%),
    radial-gradient(ellipse 62% 56% at 50% 44%,#ffffff 0%,rgba(255,255,255,0) 72%);
}
.canvas-wrap{z-index:1}
.constellation-inner{position:relative}
.canvas-wrap{position:relative}

/* no plate behind the ring — the cards are the object */
#agents-gl{display:none;width:100%;height:470px;touch-action:pan-y;cursor:grab}
body.gl-agents #agents-gl{display:block}

/* the strip owns the middle now, so the detail reads underneath it */
.node-panel{
  display:none;margin:26px auto 0;max-width:560px;text-align:center;
  transition:opacity .16s ease;
}
body.gl-agents .node-panel{display:block}
.node-panel.is-swapping{opacity:0}
.np-tag{
  font-size:11.5px;font-weight:700;letter-spacing:.16em;text-transform:uppercase;
  /* a quiet outlined pill: a solid block of colour here was the loudest thing
     on a page whose whole point is that the photograph carries the colour */
  color:var(--ink-3);display:inline-block;margin-bottom:14px;
  padding:6px 15px;border-radius:13px;
  background:transparent;border:1px solid var(--rule-2);
  transition:color .3s ease,border-color .3s ease;
}
.np-name{font-family:var(--serif);font-size:28px;font-weight:700;color:var(--ink);margin-bottom:12px;line-height:1.15}
.np-body{font-size:14px;color:var(--ink-2);line-height:1.66}
.np-meta{font-size:12.5px;color:var(--ink-3);line-height:1.6;margin-top:10px}
.np-link{
  display:inline-flex;align-items:center;gap:7px;margin-top:16px;
  font-size:12px;font-weight:600;letter-spacing:.04em;text-decoration:none;
  color:#fff;background:var(--np-accent,var(--ink));
  padding:9px 18px;border-radius:20px;transition:opacity .2s ease;
}
.np-link:hover{opacity:.86}
.np-link[hidden]{display:none}
/* The products list is the section's source of truth: the canvas strip reads
   it, and it is the section itself wherever the strip cannot run — under 768px
   and without WebGL. So it is a designed card grid first, and only collapses to
   a screen-reader source once the strip has taken over. */
.mas-grid{
  list-style:none;display:grid;gap:14px;margin-top:34px;
  grid-template-columns:repeat(auto-fit,minmax(280px,1fr));
}
.ag{
  position:relative;background:var(--warm);border:1px solid var(--rule);border-radius:6px;
  padding:22px 24px;overflow:hidden;
  transition:transform .4s var(--ease),border-color .4s var(--ease),box-shadow .4s var(--ease);
}
.ag::before{
  content:'';position:absolute;left:0;top:0;bottom:0;width:2px;background:var(--accent);
  transform:scaleY(0);transform-origin:top;transition:transform .45s var(--ease);
}
.ag:hover{transform:translateY(-3px);border-color:var(--rule-2);box-shadow:0 18px 40px -30px var(--shadow)}
.ag:hover::before{transform:scaleY(1)}
.ag-n{font-size:16px;font-weight:700;color:var(--ink);letter-spacing:-.01em;margin-bottom:7px;line-height:1.25}
.ag-b{font-size:13.5px;color:var(--ink-2);line-height:1.62}
.ag-link{margin-top:8px;font-size:13px}
.ag-link a{color:var(--spark)}

/* the strip is live and says the same thing, so the list steps out of the
   picture — but stays readable to anything the canvas cannot serve */
body.gl-agents .mas-grid{
  position:absolute;width:1px;height:1px;margin:0;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;
}

.ring-hint{
  margin-top:22px;text-align:center;
  font-size:10px;font-weight:600;letter-spacing:.16em;text-transform:uppercase;color:var(--ink-4);
  pointer-events:none;
}
body:not(.gl-agents) .ring-hint{display:none}

/* =========================================================
   3D WEB EXPERIENCES
   The single dark room on the site, and the only one that earns it: these are
   screens, and a screen only reads as lit if the room around it is not.
   ========================================================= */
.lab{
  position:relative;isolation:isolate;overflow:hidden;
  max-width:1560px;margin:26px auto;border-radius:30px;
  padding:78px 0 84px;
  background:#0b0c0e;
  --ink:#f6f5f3;--ink-2:#c9c6c1;--ink-3:#9b9791;--ink-4:#7c7873;
  --rule:#232528;--rule-2:#33363a;
  color:var(--ink);
}
/* the glow the two screens throw into the room */
.lab::before{
  content:'';position:absolute;inset:0;z-index:0;pointer-events:none;
  background:
    radial-gradient(ellipse 40% 34% at 27% 58%,rgba(120,190,205,.16),transparent 70%),
    radial-gradient(ellipse 40% 34% at 73% 58%,rgba(120,190,205,.13),transparent 70%),
    radial-gradient(ellipse 90% 60% at 50% 0%,rgba(255,255,255,.05),transparent 60%);
}
.lab .wrap{position:relative;z-index:1}
.lab-eye{
  font-size:11px;font-weight:600;letter-spacing:.15em;text-transform:uppercase;
  color:var(--ink-4);margin-bottom:11px;
}
.lab-h{
  font-family:var(--serif);font-size:clamp(25px,3vw,33px);font-weight:700;
  color:var(--ink);letter-spacing:-.02em;line-height:1.18;margin-bottom:14px;
}
.lab-lead{font-size:15px;color:var(--ink-2);line-height:1.72;max-width:560px;margin-bottom:44px}

.lab-grid{list-style:none;display:grid;grid-template-columns:repeat(2,1fr);gap:34px}
.lab-item{display:flex;flex-direction:column;gap:14px}

.lab-frame{
  display:block;text-decoration:none;color:inherit;
  transition:transform .5s var(--ease);
}
.lab-frame:hover{transform:translateY(-4px)}

/* No drawn bezel: the recordings are of a real machine in a real room, so a
   fake laptop wrapped around a photograph of a laptop would be a laptop in a
   laptop. The footage is the frame. */
.lab-screen{
  display:block;position:relative;aspect-ratio:3/4;overflow:hidden;border-radius:14px;
  background:#0e1013;
  transition:box-shadow .5s var(--ease);
  box-shadow:
    0 0 0 1px rgba(255,255,255,.06),
    0 34px 70px -34px rgba(0,0,0,.95),
    0 0 60px -14px rgba(150,205,215,.20);
}
.lab-screen video,.lab-screen img{
  position:absolute;inset:0;width:100%;height:100%;object-fit:cover;display:block;
}
/* the room around the shot sinks a little, so the lit screen inside it leads */
.lab-screen::after{
  content:'';position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(ellipse 80% 64% at 50% 40%,transparent 42%,rgba(6,8,10,.52) 100%);
}
.lab-frame:hover .lab-screen{
  box-shadow:
    0 0 0 1px rgba(255,255,255,.10),
    0 40px 84px -34px rgba(0,0,0,.95),
    0 0 82px -12px rgba(150,205,215,.34);
}

.lab-foot{display:flex;align-items:baseline;justify-content:space-between;gap:14px;margin-top:16px}
.lab-name{font-size:14.5px;font-weight:600;color:var(--ink)}
.lab-go{
  display:inline-flex;align-items:center;gap:6px;flex:none;
  font-size:11px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:rgba(150,215,222,.9);transition:color .25s;
}
.lab-frame:hover .lab-go{color:#bfeef4}
.lab-note{font-size:13.5px;color:var(--ink-3);line-height:1.62}

@media(max-width:768px){
  .lab{margin:16px 14px;border-radius:20px;padding:56px 0 62px}
  .lab-grid{grid-template-columns:1fr;gap:30px}
  .lab-lead{margin-bottom:32px}
  .lab-foot{flex-direction:column;align-items:flex-start;gap:6px}
}
@media (prefers-reduced-motion: reduce){
  .lab-frame:hover{transform:none}
}

/* =========================================================
   TESTIMONIALS
   ========================================================= */
.testi{display:grid;grid-template-columns:repeat(2,1fr);gap:16px;perspective:1900px}
.tc{
  background:var(--warm);border:1px solid var(--rule);border-radius:4px;padding:28px;
  transform-style:preserve-3d;transition:transform .5s var(--ease),box-shadow .5s,border-color .5s;
}
.tc:hover{transform:translateY(-3px);box-shadow:0 10px 26px -14px var(--shadow);border-color:var(--rule-2)}
.tq{font-size:14.5px;color:var(--ink);line-height:1.72;margin-bottom:16px;font-style:italic}
.tq::before{content:'\201C';font-family:var(--serif);font-size:32px;color:var(--spark);opacity:.5;display:block;line-height:1;margin-bottom:6px;font-style:normal}
.tw{font-size:11.5px;color:var(--ink-3);font-weight:500;letter-spacing:.03em}

/* =========================================================
   BOOKS
   ========================================================= */
/* Paper, not a lightbox. The jackets are the only colour that belongs here —
   everything around them is the same white the rest of the page is set on. */
.books-wrap{background:var(--warm);border-top:1px solid var(--rule);border-bottom:1px solid var(--rule)}
.books-in{padding:76px 52px}
.be{font-size:11px;font-weight:600;letter-spacing:.15em;text-transform:uppercase;color:var(--ink-4);margin-bottom:11px}
.bh{font-family:var(--serif);font-size:30px;font-weight:700;color:var(--ink);letter-spacing:-.02em;margin-bottom:38px}

.shelf{display:none;position:relative;margin-bottom:12px}
body.gl-books .shelf{display:block}
/* on a dark ground the jackets sat in their own shadow. On paper they need a
   contact shadow or they read as stickers rather than objects. */
.shelf::after{
  content:'';position:absolute;left:12%;right:12%;bottom:34px;height:46px;z-index:-1;
  background:radial-gradient(ellipse at 50% 50%,rgba(15,14,13,.20),rgba(15,14,13,0) 68%);
  filter:blur(7px);pointer-events:none;
}
#books-gl{width:100%;height:380px;display:block;touch-action:pan-y;cursor:grab}
#books-gl:active{cursor:grabbing}
.shelf-hint{
  position:absolute;right:0;bottom:6px;font-size:9.5px;font-weight:600;
  letter-spacing:.16em;text-transform:uppercase;color:var(--ink-4);
}

.bg{list-style:none;display:grid;grid-template-columns:repeat(3,1fr);gap:20px;perspective:1900px}
.bg>li{display:flex}
.bk{
  background:var(--panel);border:1px solid var(--rule);border-radius:8px;
  padding:24px;transition:transform .5s var(--ease),box-shadow .4s var(--ease),border-color .3s;
  transform-style:preserve-3d;text-decoration:none;
  display:flex;flex-direction:column;gap:16px;width:100%;
  box-shadow:0 18px 40px -34px var(--shadow);
}
a.bk:hover{border-color:var(--rule-2);transform:translateY(-3px);box-shadow:0 26px 54px -30px var(--shadow)}

/* the real jacket, at the size of a thumbnail. A printed cover on white wants
   a real shadow under it — that is what stops it looking like a sticker. */
.bk-art{flex:none;width:88px;align-self:flex-start;border-radius:3px;overflow:hidden;
  box-shadow:0 12px 26px -14px rgba(15,14,13,.5);background:var(--warm)}
.bk-art img{width:100%;height:auto;display:block}
.bk-art-blank{
  display:flex;align-items:center;justify-content:center;text-align:center;
  height:130px;padding:8px;border:1px dashed var(--rule-2);background:transparent;box-shadow:none;
}
.bk-art-blank em{font-family:var(--serif);font-size:11px;font-style:normal;color:var(--ink-4);line-height:1.3}
.bk-txt{display:block;min-width:0}

.bk-go{
  display:block;margin-top:13px;font-size:11px;font-weight:600;
  letter-spacing:.08em;text-transform:uppercase;color:var(--ink);
}
.bk-go em{display:block;font-style:normal;font-weight:500;letter-spacing:.06em;color:var(--ink-4);margin-top:3px}
a.bk .bk-go{color:var(--spark)}
.bk-go.bk-soon{color:var(--ink-4)}
.is-soon{opacity:.78}
.bkn{font-family:var(--serif);font-size:18px;font-weight:700;color:var(--ink);margin-bottom:6px}
.bktag{font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;color:var(--ink-4);margin-bottom:11px}
.bkd{font-size:13px;color:var(--ink-2);line-height:1.62}

/* =========================================================
   CTA + FOOTER
   ========================================================= */
.cta-sec{padding:90px 0;text-align:center;background:var(--warm);border-top:1px solid var(--rule)}
.ce{font-size:11px;font-weight:600;letter-spacing:.15em;text-transform:uppercase;color:var(--ink-4);margin-bottom:16px}
.ch2{font-family:var(--serif);font-size:clamp(26px,3.5vw,40px);font-weight:700;color:var(--ink);letter-spacing:-.02em;max-width:540px;margin:0 auto 14px;line-height:1.15}
.cs{font-size:16px;color:var(--ink-2);margin:0 auto 34px;max-width:460px;line-height:1.7}
.cbtns{display:flex;gap:12px;justify-content:center;flex-wrap:wrap;margin-bottom:20px}
.cbp,.cbs{display:inline-flex;align-items:center;gap:8px;padding:14px 30px;border-radius:4px;text-decoration:none;font-size:13px;font-weight:600}
.cbp{background:var(--ink);color:var(--on-ink);transition:opacity .15s}
.cbp:hover{opacity:.85}
.cbs{background:var(--bg);color:var(--ink);border:1.5px solid var(--ink);transition:background .15s,color .15s}
.cbs:hover{background:var(--ink);color:var(--on-ink)}
.cmeta{font-size:12px;color:var(--ink-4);margin-top:26px}
.cmeta a{color:var(--ink-3);text-decoration:none;border-bottom:1px solid var(--rule)}
.cmeta a:hover{color:var(--ink)}

footer{border-top:1px solid var(--rule);padding:22px 0;display:flex;justify-content:space-between;align-items:center}
.fl{font-size:12px;font-weight:600;color:var(--ink-2)}
.fr{font-size:11px;color:var(--ink-4)}


/* =========================================================
   CLOSING PLEDGE + FLOATING DOCK
   ========================================================= */
/* three short promises about what the call actually is — the copy says "I don't
   do sales calls", these are what make that believable */
.pledge{
  list-style:none;display:grid;grid-template-columns:repeat(3,1fr);gap:18px;
  max-width:820px;margin:34px auto 0;text-align:left;
}
.pledge li{
  display:grid;grid-template-columns:18px 1fr;grid-template-rows:auto auto;
  column-gap:11px;row-gap:3px;align-items:start;
  padding:16px 18px;border:1px solid var(--rule);border-radius:8px;background:var(--panel);
  transition:border-color .35s var(--ease),transform .35s var(--ease);
}
.pledge li:hover{border-color:var(--rule-2);transform:translateY(-2px)}
.pledge svg{width:16px;height:16px;color:var(--spark-lit);grid-row:1;margin-top:2px}
.pledge b{grid-column:2;font-size:13px;font-weight:700;color:var(--ink);letter-spacing:-.005em}
.pledge span{grid-column:2;font-size:12.5px;color:var(--ink-3);line-height:1.55}
.pledge a{color:var(--spark);text-decoration:none;border-bottom:1px solid var(--rule-2)}
.pledge a:hover{color:var(--ink)}

/* the dock: a way to act from anywhere past the hero */
.dock{
  position:fixed;left:50%;bottom:20px;z-index:9996;
  display:flex;align-items:center;gap:18px;
  padding:11px 12px 11px 22px;border-radius:999px;
  border:1px solid var(--rule-2);background:var(--nav-bg);
  box-shadow:0 20px 50px -24px var(--shadow);
  backdrop-filter:blur(16px);-webkit-backdrop-filter:blur(16px);
  transform:translate(-50%,140%);opacity:0;pointer-events:none;
  transition:transform .55s var(--ease),opacity .4s var(--ease);
  max-width:calc(100vw - 28px);
}
.dock.is-up{transform:translate(-50%,0);opacity:1;pointer-events:auto}
.dock-t{font-size:13px;color:var(--ink-2);white-space:nowrap}
.dock-t b{color:var(--ink);font-weight:600}
.dock-a{display:flex;align-items:center;gap:8px;flex:none}
.dock-p,.dock-w{
  display:inline-flex;align-items:center;gap:7px;text-decoration:none;
  font-size:12.5px;font-weight:600;border-radius:999px;white-space:nowrap;
  transition:opacity .18s,background .18s,color .18s,border-color .18s;
}
.dock-p{background:var(--accent);color:#fff;padding:10px 18px}
.dock-p:hover{opacity:.88}
.dock-w{color:var(--ink-2);border:1px solid var(--rule-2);padding:9px 16px}
.dock-w:hover{color:var(--ink);border-color:var(--ink-4)}

@media(max-width:760px){
  .pledge{grid-template-columns:1fr;gap:10px;margin-top:26px}
  .dock{left:12px;right:12px;bottom:12px;transform:translateY(140%);
    padding:10px;gap:10px;justify-content:space-between}
  .dock.is-up{transform:translateY(0)}
  .dock-t{display:none}
  .dock-a{width:100%;gap:8px}
  .dock-p{flex:1;justify-content:center;padding:12px 14px}
  .dock-w{flex:none;padding:11px 14px}
  .dock-wt{display:none}
}
@media (prefers-reduced-motion: reduce){
  .dock{transition:none}
}

/* =========================================================
   ANIMATION BASE
   ========================================================= */
.anim-line{display:block;overflow:hidden}
.anim-line>span{display:block;will-change:transform,opacity}
.js-fade{will-change:transform,opacity}

/* =========================================================
   RESPONSIVE
   ========================================================= */
/* the copy column and the cutout share the width; as it narrows the figure
   gives ground first, then steps under the text entirely */
@media(max-width:1180px){
  .hero-grid{grid-template-columns:minmax(0,54%)}
  .hero-figure{right:-6%;height:min(84svh,760px)}
}
@media(max-width:900px){
  .hero-grid{grid-template-columns:minmax(0,60%)}
  .hero-figure{right:-14%;height:min(72svh,620px);opacity:.9}
  .hero-lead{font-size:15.5px}
}
@media(max-width:768px){
  nav{padding:0 18px;height:56px}
  .brand span{font-size:12px}
  .nav-right{gap:14px}
  .nav-li:not(.nav-icon){display:none}
  /* brand + icon + CTA together ran past 390px and clipped the button */
  .nav-cta{padding:8px 13px;font-size:11px;letter-spacing:.02em}
  .wrap{padding:0 20px}
  .books-in{padding:60px 20px}

  /* on a phone there is no room beside the copy, so the cutout goes under it,
     full width, still bleeding off the bottom of the section */
  .hero{padding:92px 0 0;min-height:auto;display:block}
  .hero .wrap{max-width:none}
  .hero-grid{grid-template-columns:1fr}
  .hero-h1{font-size:clamp(23px,7.6vw,40px)}
  .hero-lead{font-size:15px}
  .hero-acts{margin-top:24px}
  .btn-p,.btn-s{padding:13px 20px;font-size:12.5px}
  /* the chest is cut by the foot of the frame, so it is tucked under the band
     below, which paints over it */
  .hero-figure{
    position:relative;right:auto;top:auto;bottom:auto;opacity:1;
    height:auto;width:min(580px,118%);margin:22px auto -34px;
  }

  /* the nav is shorter here, so the pile parks higher */
  .pillar-steps{gap:16px}
  .pillar-steps::after{height:32vh}
  .pstep{padding:26px 22px;min-height:0}
  .pstep:nth-child(1){--stick:78px}
  .pstep:nth-child(2){--stick:90px}
  .pstep:nth-child(3){--stick:102px}
  .proof{grid-template-columns:repeat(2,1fr);row-gap:26px}
  .proof .pf:nth-child(5){grid-column:span 2}
  .pf::after{display:none}


  .case{grid-template-columns:1fr;gap:9px}
  .co{text-align:left}

  body.gl-agents .constellation-inner{grid-template-columns:1fr;gap:14px}
  #agents-gl{height:420px}
  .constellation{margin-top:14px}
  .constellation::before{aspect-ratio:1.25}
  /* the strip runs here too now, so the list stays a source rather than
     appearing underneath it saying the same thing again */
  #books-gl{height:280px}
  .testi{grid-template-columns:1fr}
  .bg{grid-template-columns:1fr}
  footer{flex-direction:column;gap:8px;text-align:center}
  .cursor{display:none}
  body.has-cursor,body.has-cursor a,body.has-cursor button{cursor:auto}
}

/* =========================================================
   REDUCED MOTION — full static fallback
   ========================================================= */
@media (prefers-reduced-motion: reduce){
  html{scroll-behavior:auto}
  .curtain{display:none}
  .anim-line>span,.js-fade{opacity:1!important;transform:none!important}
  .case,.ag,.tc,.bk,.pstep{transition:none!important;transform:none!important}
  .pstep{position:static}
  .pillar-steps::after{display:none}
  .cursor,.rail{display:none}
  .hint-dot{animation:none}
  #hero-gl,#agents-gl,#books-gl{display:none!important}
  /* no shader, so the still cutout stands in — it carries its own alpha and
     is therefore right on either background */
  .hero-still{opacity:1!important}
  #hero-film{display:none!important}

  .hero{min-height:auto}
  .mas-grid{position:static!important;width:auto!important;height:auto!important;overflow:visible!important;clip:auto!important;clip-path:none!important;white-space:normal!important}
  .shelf{display:none!important}
}
