/* ==========================================================================
   REFINR STUDIOS — stylesheet
   ========================================================================== */

:root{
  --cream:  #F7F4ED;
  --paper:  #FCFCFC;
  --blue:   #2978F3;
  --yellow: #F0CD3E;
  --orange: #FF5324;
  --green:  #12A05C;
  --ink:    #141414;
  --muted:  #6B6659;
  --line:   #E3DECF;
  --glass:  rgba(252,252,252,.46);   /* the nav pill, over whatever scrolls under it
                                        - a lens, so it stays well under half opaque */
  --glass-edge:   rgba(255,255,255,.70);
  --glass-top:    rgba(255,255,255,.9);    /* lit top edge */
  --glass-bottom: rgba(30,25,10,.09);      /* shadowed bottom edge */
  --glass-sheen:  rgba(255,255,255,.52);
  --glass-rim:    rgba(255,255,255,.62);    /* the lit bevel around the rim */
  --glass-spec:   rgba(255,255,255,.42);    /* highlight that follows the cursor */
  --glass-tintA:  rgba(120,175,255,.30);    /* the colour a lens splits off, cool */
  --glass-tintB:  rgba(255,186,124,.24);    /* and warm, on the opposite edge */

  /* The logo PNG and the client marks are black artwork. On a dark page they
     would vanish, so the theme hands them a filter instead of a new file —
     the source images are never touched. */
  --logo-filter:none;
  --mark-filter:brightness(0);

  /* Hero props that are near-black and float on the page background rather
     than on a coloured surface: the reel, the clapperboard, the camera, the
     doodles. -in is whatever sits inside them and has to go the other way. */
  --il-ink:    #171717;
  --il-ink-in: #F7F4ED;
  --il-navy:   #16202F;

  /* Helvetica everywhere. It is not installed on Windows or Android, so Arial
     carries those — it is metrically identical, so nothing shifts. Mac and iOS
     get the real thing. --serif keeps its name but is now the display role. */
  --serif:  'Helvetica Neue', Helvetica, Arial, sans-serif;
  --body:   'Helvetica Neue', Helvetica, Arial, sans-serif;
  --hand:   'Grape Nuts', 'Schoolbell', cursive;
  --chalk:  'Schoolbell', cursive;

  --max:  1120px;
  --gut:  clamp(20px, 5vw, 40px);
}

/* ---------- dark theme ------------------------------------------------------
   Only the palette changes. Everything downstream reads these variables, so the
   layout, spacing and artwork stay exactly as they are. The accents are lifted
   a little because the same blue that reads well on cream goes muddy on black.
   Light is the default. Dark only applies when the switch has been pressed —
   the OS preference is deliberately not consulted.
   -------------------------------------------------------------------------- */

:root[data-theme="dark"]{
  --cream:  #14120D;
  --paper:  #1C1913;
  --blue:   #5C9BFF;
  --yellow: #F5D65C;
  --orange: #FF6E42;
  --green:  #2ABE77;
  --ink:    #F4F0E6;
  --muted:  #A49C8A;
  --line:   #322C22;
  --glass:  rgba(28,25,19,.42);
  --glass-edge:   rgba(255,255,255,.16);
  --glass-top:    rgba(255,255,255,.20);
  --glass-bottom: rgba(0,0,0,.34);
  --glass-sheen:  rgba(255,255,255,.09);
  --glass-rim:    rgba(255,255,255,.26);
  --glass-spec:   rgba(255,255,255,.20);
  --glass-tintA:  rgba(120,175,255,.26);
  --glass-tintB:  rgba(255,170,110,.18);
  --logo-filter:invert(1);
  --mark-filter:brightness(0) invert(1);

  --il-ink:    #EFE9DA;
  --il-ink-in: #16130E;
  --il-navy:   #C6D6EF;
}

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

/* The gutter is held open whether or not a scrollbar is currently drawn. Two
   places take the page out of scroll - the preloader and the open modal - and
   without this the scrollbar disappears with them, the centred nav pill slides
   sideways by half a scrollbar, and the whole page jumps. */
html{ scroll-behavior:smooth; scroll-padding-top:110px; scrollbar-gutter:stable; }

body{
  margin:0;
  background:var(--cream);
  color:var(--ink);
  font-family:var(--body);
  font-size:16px;
  line-height:1.65;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}

img,svg{ max-width:100%; display:block; }
a{ color:inherit; text-decoration:none; }
button{ font:inherit; border:0; background:none; color:inherit; cursor:pointer; }
ul{ margin:0; padding:0; list-style:none; }

/* ---------- shared -------------------------------------------------- */

.section{
  max-width:var(--max);
  margin:0 auto;
  padding:clamp(56px, 9vw, 110px) var(--gut);
}

.h2{
  font-family:var(--serif);
  font-size:clamp(30px, 4.4vw, 50px);
  font-weight:700; line-height:1.1; letter-spacing:-.025em;
  margin:0;
}

.sec-head{
  display:flex; align-items:flex-end; justify-content:space-between;
  gap:24px; flex-wrap:wrap; margin-bottom:clamp(28px,4vw,48px);
}
.sec-sub{ margin:0; color:var(--muted); font-size:15px; }
.sec-note{
  font-family:var(--hand);
  font-size:clamp(26px,3.4vw,38px);
  margin:0 0 clamp(26px,4vw,44px);
  color:var(--ink);
}

.namaste{
  font-family:var(--hand);
  font-size:clamp(34px,5vw,54px);
  color:var(--blue);
  margin:0 0 6px;
}

.statement{
  font-family:var(--serif);
  font-size:clamp(28px, 4.6vw, 54px);
  font-weight:400; line-height:1.24; letter-spacing:-.028em;
  margin:0; max-width:16ch;
  max-width:min(940px, 100%);
}

/* highlighter marks */
.hl{ position:relative; white-space:nowrap; font-weight:700; }
.hl::before{
  content:''; position:absolute; left:-.08em; right:-.08em; bottom:.04em; height:.42em;
  z-index:-1; border-radius:3px;
  transform:scaleX(0); transform-origin:left;
  transition:transform .7s cubic-bezier(.2,.8,.2,1) .25s;
}
.is-in .hl::before{ transform:scaleX(1); }
.hl-blue::before{ background:rgba(41,120,243,.28); }
.hl-orange::before{ background:rgba(255,83,36,.28); }
.hl-yellow::before{ background:rgba(240,205,62,.55); }

.btn{
  display:inline-flex; align-items:center; gap:8px;
  padding:12px 22px; border-radius:100px;
  font-size:14px; font-weight:700;
  transition:transform .25s cubic-bezier(.2,.8,.2,1), background .25s, color .25s;
  white-space:nowrap;
}
.btn i{ font-style:normal; transition:transform .25s; }
.btn:hover i{ transform:translate(2px,-2px); }
.btn-ink{ background:var(--ink); color:var(--cream); }
.btn-ink:hover{ background:var(--blue); }
.btn-line{ border:1.5px solid var(--ink); }
.btn-line:hover{ background:var(--ink); color:var(--cream); }
.btn.big{ padding:16px 30px; font-size:16px; }

.link-arrow{ font-size:14px; font-weight:700; }
.link-arrow i{ font-style:normal; display:inline-block; transition:transform .25s; }
.link-arrow:hover i{ transform:translateX(4px); }

/* ---------- logo ----------------------------------------------------- */

.logo{ display:inline-flex; align-items:center; --logo-h:22.3px; }

/* The real wordmark: assets/logo.png, already black on transparent and cropped
   tight to the artwork, so it just needs a height. It is what shows — the text
   below only steps in if the file fails, and this way round nobody ever sees
   the stand-in flash up first while the image is still decoding. The ratio is
   the file's own 660x315, so the space is held and nothing shifts. */
.logo-img{
  height:var(--logo-h); width:auto; aspect-ratio:660 / 315;
  display:block; filter:var(--logo-filter);
}
.logo.no-img .logo-img{ display:none; }

/* Stand-in wordmark, set to sit as close to the real mark as type allows:
   two very tight lines of equal width, heavy geometric lowercase, a three
   stroke sparkle off the final r and a full stop after it. */
.logo-text{
  display:none; flex-direction:column; line-height:.78;
  /* Poppins on purpose, not the site font — this stand-in only appears if the
     logo file fails to load, so it has to imitate the mark, not the body copy. */
  font-family:'Poppins', var(--body); font-weight:900; letter-spacing:-.045em;
  font-size:calc(var(--logo-h) * .49);
}
.logo.no-img .logo-text{ display:flex; }
.logo-text b{ position:relative; font-weight:900; display:block; }
.lm-1{ letter-spacing:.075em; padding-right:.36em; }

.lm-spark{
  position:absolute; right:-.16em; top:-.42em;
  width:.46em; height:.46em; overflow:visible;
}
.lm-dot{
  position:absolute; right:.04em; bottom:.1em;
  width:.16em; height:.16em; border-radius:50%;
  background:currentColor;
}
.logo-big{ --logo-h:32.3px; }
.logo-xl{ --logo-h:45.9px; }

/* ---------- nav ------------------------------------------------------ */

.nav{
  position:fixed; top:16px; left:0; right:0; z-index:80;
  padding:0 var(--gut);
  transition:transform .45s cubic-bezier(.2,.8,.2,1);
}
.nav.is-hidden{ transform:translateY(-140%); }
/* Liquid glass. Frosted glass is one flat blur across the whole panel; a lens
   is not flat - light bends hardest where the surface curves, so the rim
   carries a lit bevel, splits a little colour, and a highlight slides across
   the surface as you move over it.

   Four things stacked over the blurred backdrop:
     the pill    the blur, the tint, and a sheen that dies before the middle
     ::before    the rim, masked to a ring, screened so it lifts the backdrop
     ::after     the specular highlight, driven by the cursor from JS
     insets      the lit top edge and the shadowed bottom one, for thickness

   The rim deliberately has no backdrop-filter of its own. Nesting one inside
   an element that already has one does not give a crisper edge - the child
   samples the parent's already blurred result, so the two blurs stack. */
.nav-pill{
  position:relative;
  max-width:var(--max); margin:0 auto;
  display:flex; align-items:center; gap:20px;
  border-radius:100px;
  padding:10px 10px 10px 22px;
  background:
    linear-gradient(180deg, var(--glass-sheen), transparent 54%),
    var(--glass);
  /* more transparent than frosted glass and pushed harder on saturation, so
     what is behind stays readable as colour instead of going milky */
  -webkit-backdrop-filter:blur(16px) saturate(215%) brightness(1.06);
  backdrop-filter:blur(16px) saturate(215%) brightness(1.06);
  border:1px solid var(--glass-edge);
  box-shadow:0 14px 40px rgba(30,25,10,.16),
             0 3px 10px rgba(30,25,10,.06),
             inset 0 1px 0 var(--glass-top),
             inset 0 -1px 0 var(--glass-bottom);
}

/* The ring is two copies of the same mask - the outer one over the whole box,
   the inner one clipped to the content box - with the inner punched out of the
   outer, which leaves just the band between them. */
.nav-pill::before{
  content:''; position:absolute; inset:0;
  border-radius:inherit; pointer-events:none;
  padding:1.4px;
  background:linear-gradient(135deg,
    var(--glass-rim)   0%,
    var(--glass-tintA) 17%,
    transparent        40%,
    transparent        60%,
    var(--glass-tintB) 83%,
    var(--glass-rim)  100%);
  -webkit-mask:linear-gradient(#000,#000) content-box, linear-gradient(#000,#000);
          mask:linear-gradient(#000,#000) content-box, linear-gradient(#000,#000);
  -webkit-mask-composite:xor;
          mask-composite:exclude;
  mix-blend-mode:screen;
}

/* Where the light catches. --mx and --my are written by the pointer; with no
   pointer it sits parked off to the left and stays invisible. */
.nav-pill::after{
  content:''; position:absolute; inset:0;
  border-radius:inherit; pointer-events:none;
  opacity:0; transition:opacity .35s ease;
  background:radial-gradient(170px 74px at var(--mx,-20%) var(--my,50%),
                             var(--glass-spec), transparent 68%);
  mix-blend-mode:screen;
}
.nav-pill.is-lit::after{ opacity:1; }

.nav-pill > *{ position:relative; z-index:1; }

.nav-links{ display:flex; gap:6px; margin-left:auto; font-size:14px; font-weight:500; }

/* Each link is its own little pill. The fill grows from the middle on hover,
   which is the same gesture the buttons use, so the whole bar reacts the one
   way rather than the links behaving differently from the call to action. */
.nav-links a{
  position:relative; padding:7px 14px; border-radius:100px;
  transition:color .25s, transform .25s cubic-bezier(.2,.8,.2,1);
}
.nav-links a::before{
  content:''; position:absolute; inset:0;
  border-radius:inherit; background:var(--ink);
  transform:scale(.6); opacity:0;
  transition:transform .3s cubic-bezier(.34,1.4,.5,1), opacity .22s;
  z-index:-1;
}
.nav-links a:hover{ color:var(--paper); transform:translateY(-1px); }
.nav-links a:hover::before{ transform:scale(1); opacity:1; }

/* the one you are on keeps the blue and the underline it always had */
.nav-links a::after{
  content:''; position:absolute; left:14px; right:14px; bottom:3px;
  height:2px; background:var(--blue);
  transform:scaleX(0); transform-origin:center;
  transition:transform .25s;
}
.nav-links a.is-current{ color:var(--blue); }
.nav-links a.is-current::after{ transform:scaleX(1); }
.nav-links a.is-current:hover{ color:var(--paper); }
.nav-links a.is-current:hover::after{ background:var(--paper); }

.burger{ width:42px; height:42px; border-radius:50%; background:var(--ink); display:none; flex-direction:column; align-items:center; justify-content:center; gap:4px; }
.burger span{ width:17px; height:2px; background:var(--cream); display:block; transition:.25s; }
.burger.is-open span:nth-child(1){ transform:translateY(6px) rotate(45deg); }
.burger.is-open span:nth-child(2){ opacity:0; }
.burger.is-open span:nth-child(3){ transform:translateY(-6px) rotate(-45deg); }

/* ---------- hero ------------------------------------------------------ */

.hero{
  max-width:var(--max); margin:0 auto;
  padding:clamp(88px,11vw,132px) var(--gut) 0;
}
.scene{ width:100%; height:auto; overflow:visible; }
.scene .il-hand{ font-family:var(--hand); font-size:15px; fill:#3A342A; }
.scene .il-tiny{ font-family:var(--body); font-size:9px; font-weight:700; letter-spacing:.1em; fill:#3A342A; }
.scene .il-note{ font-size:26px; fill:var(--ink); }

/* The props that sit on the page background rather than on the desk. As CSS
   these beat the fill/stroke attributes still on the markup, which stay put as
   the value any browser without custom properties would fall back to. */
.scene .il-ink{    fill:var(--il-ink); }
.scene .il-ink-in{ fill:var(--il-ink-in); }
.scene .il-ink-s{  stroke:var(--il-ink); }
.scene .il-navy{   fill:var(--il-navy); }

.scene .sun{ animation:pulse 3.4s ease-in-out infinite; transform-origin:410px 216px; }
@keyframes pulse{ 0%,100%{ transform:scale(1);} 50%{ transform:scale(1.12);} }
.scene .cloud-a{ animation:driftx 7s ease-in-out infinite; }
@keyframes driftx{ 0%,100%{ transform:translateX(0);} 50%{ transform:translateX(-10px);} }
.scene .playhead{ animation:scrub 6s ease-in-out infinite; }
@keyframes scrub{ 0%,100%{ transform:translateX(-108px);} 50%{ transform:translateX(140px);} }
.scene .steam{ animation:steam 3s ease-in-out infinite; opacity:.9; }
@keyframes steam{ 0%,100%{ transform:translateY(0); opacity:.85;} 50%{ transform:translateY(-6px); opacity:.45;} }
.scene .reel{ transform-box:fill-box; transform-origin:center; animation:spin 18s linear infinite; }
@keyframes spin{ to{ transform:rotate(360deg);} }
.scene .clapper-arm{ transform-box:fill-box; transform-origin:left bottom; animation:clap 5s ease-in-out infinite; }
@keyframes clap{ 0%,72%,100%{ transform:rotate(0);} 80%{ transform:rotate(-22deg);} }
.scene .clouds path{ animation:driftx 9s ease-in-out infinite; }
.il-head{ transform-box:fill-box; transform-origin:center bottom; transition:rotate .35s ease-out; }

/* ---------- intro ------------------------------------------------------ */

.intro{
  max-width:var(--max); margin:0 auto;
  padding:clamp(36px,6vw,72px) var(--gut) clamp(20px,4vw,40px);
}
.intro-meta{ display:flex; gap:10px; flex-wrap:wrap; margin-top:34px; }
.pill-link{
  display:inline-flex; align-items:center; gap:8px;
  padding:9px 18px; border-radius:100px;
  border:1px solid var(--line); background:var(--paper);
  font-size:14px; font-weight:500;
  transition:transform .25s cubic-bezier(.2,.8,.2,1), border-color .25s;
}
.pill-link:hover{ border-color:var(--ink); }
.pill-link.is-static{ cursor:default; }
.dot-live{ width:8px; height:8px; border-radius:50%; background:var(--green); animation:blink 2s ease-in-out infinite; }
@keyframes blink{ 0%,100%{opacity:1} 50%{opacity:.3} }

/* word-by-word reveal */
.wrd{ display:inline-block; opacity:0; transform:translateY(9px); }
.wrd.in{ animation:wordIn .55s cubic-bezier(.25,.7,.3,1) forwards; animation-delay:var(--d,0s); }
@keyframes wordIn{ to{ opacity:1; transform:none; } }

/* ---------- work -------------------------------------------------------- */

.filters{ display:flex; flex-wrap:wrap; gap:8px; }
.chip{
  padding:9px 18px; border-radius:100px;
  border:1px solid var(--line); background:var(--paper);
  font-size:13.5px; font-weight:500;
  transition:.22s;
}
.chip em{ font-style:normal; color:var(--muted); }
.chip:hover{ border-color:var(--ink); }
.chip.is-active{ background:var(--ink); border-color:var(--ink); color:var(--cream); }
.chip.is-active em{ color:var(--cream); opacity:.6; }

.work-list{ display:grid; grid-template-columns:repeat(2,1fr); gap:clamp(18px,2.6vw,30px); }

.wcard{
  background:var(--paper);
  border:1px solid var(--line);
  border-radius:24px; overflow:hidden;
  display:flex; flex-direction:column;
  transition:transform .35s cubic-bezier(.2,.8,.2,1), box-shadow .35s, border-color .35s;
  animation:pop .4s backwards;
  outline:none;
}
.wcard:hover{ transform:translateY(-6px); box-shadow:0 20px 40px rgba(30,25,10,.09); border-color:#D6CFBC; }
.wcard:focus-visible{ box-shadow:0 0 0 3px var(--blue); }
.wcard.is-hidden{ display:none; }
@keyframes pop{ from{ opacity:0; transform:translateY(16px);} }

/* Neutral hold, same as the reel cards. Every project has a real clip now, so
   this is only on screen for the moment before the first frame decodes — the
   coloured tints that used to sit here read as content rather than as loading. */
.wthumb{ position:relative; aspect-ratio:16/10; background:#16130E; }
.wtag{
  position:absolute; left:14px; top:14px; z-index:3;
  background:var(--paper); color:var(--ink);
  font-size:11px; font-weight:700; letter-spacing:.05em;
  padding:5px 12px; border-radius:100px;
}
.wthumb::after{
  content:''; position:absolute; inset:0;
  background:rgba(20,20,20,.28); opacity:0; transition:opacity .35s;
}
.wcard:hover .wthumb::after{ opacity:1; }
.wplay{
  position:absolute; left:50%; top:50%; z-index:2;
  width:56px; height:56px; margin:-28px 0 0 -28px;
  display:grid; place-items:center; border-radius:50%;
  background:var(--cream); color:var(--ink);
  font-size:15px; padding-left:3px;
  transform:scale(.5); opacity:0;
  transition:transform .35s cubic-bezier(.2,.8,.2,1), opacity .3s;
}
.wcard:hover .wplay{ transform:scale(1); opacity:1; }


.wbody{ padding:22px 24px 24px; display:flex; flex-direction:column; gap:12px; flex:1; }
.wbody h3{ font-family:var(--serif); font-size:23px; font-weight:700; margin:0; line-height:1.2; }
.wbody > p{ margin:0; font-size:14.5px; color:var(--muted); }
.wstats{ display:flex; flex-direction:column; gap:6px; font-size:13.5px; }
.wstats li{ position:relative; padding-left:18px; }
.wstats li::before{
  content:''; position:absolute; left:0; top:.62em;
  width:7px; height:7px; border-radius:50%; background:var(--orange);
}
.read-more{ margin-top:auto; font-size:14px; font-weight:700; }
.read-more i{ font-style:normal; display:inline-block; transition:transform .25s; }
.wcard:hover .read-more i{ transform:translateX(5px); }

.work-empty{ display:none; text-align:center; margin:36px 0 0; color:var(--muted); }
.work-empty.is-on{ display:block; }

/* ---------- reels ---------------------------------------------------------- */

/* pan-y hands vertical scrolling back to the browser and keeps horizontal for
   the drag below — without it the page scrolls and the rail moves at once */
.reel-rail{ cursor:grab; user-select:none; overflow:hidden; touch-action:pan-y; }
.reel-rail.dragging{ cursor:grabbing; }
.reel-track{
  display:flex; align-items:flex-start; gap:clamp(12px,1.6vw,20px);
  width:max-content; will-change:transform;
}

.reel-card{
  position:relative; flex:none; margin:0;
  width:clamp(168px,17vw,232px);
  aspect-ratio:9/16;
  border-radius:18px; overflow:hidden;
  /* only ever visible for the moment before the first frame decodes */
  background:#16130E;
  transition:transform .35s cubic-bezier(.2,.8,.2,1), box-shadow .35s;
  outline:none;
}
.reel-rail:not(.dragging) .reel-card:hover{ transform:translateY(-6px); box-shadow:0 18px 36px rgba(30,25,10,.16); }
.reel-card:focus-visible{ box-shadow:0 0 0 3px var(--blue); }

.reel-card video{
  position:absolute; inset:0; width:100%; height:100%;
  /* contain so a clip that is not exactly 9:16 is shown whole rather than
     having its edges taken off */
  object-fit:contain; opacity:0; transition:opacity .45s;
  pointer-events:none;
}
.reel-card.is-playing video{ opacity:1; }

.reel-play{
  position:absolute; left:50%; top:50%; z-index:2;
  width:46px; height:46px; margin:-23px 0 0 -23px;
  display:grid; place-items:center; border-radius:50%;
  /* these sit on video, so the chip stays light in both themes — which means
     the glyph has to be pinned dark rather than following --ink */
  background:rgba(252,252,252,.92); color:#141414;
  font-size:13px; padding-left:3px;
  opacity:0; transform:scale(.6);
  transition:opacity .3s, transform .35s cubic-bezier(.2,.8,.2,1);
}
.reel-card.has-video:hover .reel-play{ opacity:1; transform:scale(1); }
/* an empty slot is not tappable, so it must not offer a play button */
.reel-card.is-empty{ cursor:default; }

.reel-cap{
  position:absolute; inset:auto 0 0 0; z-index:2; margin:0;
  padding:26px 14px 12px;
  font-size:13px; font-weight:700; color:#fff;
  background:linear-gradient(transparent,rgba(20,16,8,.6));
}


/* ---------- reel lightbox --------------------------------------------------- */

.reel-box{ position:fixed; inset:0; z-index:230; display:none; }
.reel-box.is-open{ display:grid; place-items:center; }
.reel-box-bg{ position:absolute; inset:0; background:rgba(12,10,6,.82); backdrop-filter:blur(8px); animation:fade .3s; }

/* The reel lands in a card, not as a bare clip — the same object that sits in
   the rail, just lifted out and given its name. */
.reel-stage{
  position:relative; z-index:2;
  display:flex; flex-direction:column;
  background:var(--paper);
  border:1px solid var(--line);
  border-radius:26px;
  padding:13px 13px 0;
  box-shadow:0 34px 80px rgba(0,0,0,.55);
  /* the open animation is run from JS — it grows out of the card you tapped,
     so it needs that card's live position and cannot live in a keyframe */
  transform-origin:center;
  will-change:transform;
}
.reel-screen{
  height:min(72vh,640px); aspect-ratio:9/16;
  border-radius:16px; overflow:hidden; background:#000;
}
.reel-meta{
  display:flex; align-items:center; justify-content:space-between; gap:18px;
  padding:11px 5px 13px;
}
.reel-name{
  margin:0; font-size:14.5px; font-weight:700; letter-spacing:-.01em;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap;   /* long titles yield first */
}
.reel-meta-r{ display:flex; align-items:center; gap:11px; flex:none; }
.reel-time{
  font-size:12px; color:var(--muted);
  font-variant-numeric:tabular-nums;   /* so the row does not twitch each second */
}
.reel-count{
  font-size:12px; color:var(--muted);
  font-variant-numeric:tabular-nums; letter-spacing:.06em;
}

/* the chrome catches up a beat after the reel has landed */
.reel-box.is-open .reel-close,
.reel-box.is-open .reel-arrow,
.reel-box.is-open .reel-sound{
  animation:reelChrome .34s cubic-bezier(.2,.8,.2,1) .16s backwards;
}
@keyframes reelChrome{ from{ opacity:0; scale:.72; } }
.reel-screen video{ width:100%; height:100%; object-fit:contain; display:block; }

.reel-close,.reel-arrow{
  position:absolute; z-index:3;
  width:46px; height:46px; border-radius:50%;
  /* these sit on video, so the chip stays light in both themes — which means
     the glyph has to be pinned dark rather than following --ink */
  background:rgba(252,252,252,.92); color:#141414;
  display:grid; place-items:center; font-size:19px; line-height:1;
  transition:.25s;
}
.reel-close:hover,.reel-arrow:hover{ background:#fff; transform:scale(1.06); }
.reel-close{ top:22px; right:22px; font-size:24px; }
.reel-prev{ left:max(22px,6vw); top:50%; margin-top:-23px; }
.reel-next{ right:max(22px,6vw); top:50%; margin-top:-23px; }

/* the mute button lives on the card, so it follows the card's own palette
   rather than the pinned-light treatment the on-video chips need */
.reel-sound{
  width:26px; height:26px; border-radius:50%; flex:none;
  border:1px solid var(--line); background:transparent; color:var(--ink);
  display:grid; place-items:center;
  cursor:pointer; transition:.22s;
}
.reel-sound:hover{ background:var(--ink); color:var(--paper); border-color:var(--ink); }
.reel-sound svg{ width:14px; height:14px; }
.reel-sound .wave-1,.reel-sound .wave-2{ opacity:0; }
.reel-sound .mute-x{ opacity:1; }
.reel-sound[aria-pressed="true"]{ background:var(--blue); color:#fff; border-color:var(--blue); }
.reel-sound[aria-pressed="true"] .wave-1,.reel-sound[aria-pressed="true"] .wave-2{ opacity:1; }
.reel-sound[aria-pressed="true"] .mute-x{ opacity:0; }

body.reel-open{ overflow:hidden; }

@media (max-width:760px){
  .reel-prev,.reel-next{ display:none; }
  .reel-screen{ height:min(70vh,560px); }
}

/* ---------- brands ticker ------------------------------------------------ */

.brands{ padding-block:clamp(30px,5vw,60px); }
.ticker{ overflow:hidden; border-block:1px solid var(--line); padding:20px 0; }
.ticker-track{
  display:flex; align-items:center; gap:36px; white-space:nowrap;
  animation:slide 28s linear infinite; padding-right:36px;
  will-change:transform;
}
/* the rail never stops: no pause on hover, and nothing else touches its speed */
.ticker-track span{ font-family:var(--serif); font-size:clamp(22px,3vw,34px); font-weight:700; }

/* Each brand shows its wordmark as text until a real logo file turns up in
   assets/, at which point JS swaps to the image. Logos sit desaturated so the
   rail stays calm, and come up to full colour when you hover it. */
.ticker-track .brand{ display:inline-flex; align-items:center; flex:none; }
.ticker-track .brand img{
  display:none;
  /* --mh is set per brand in the markup to even out their optical weight */
  height:calc(clamp(24px,3.2vw,38px) * var(--mh,1)); width:auto;
  /* it is a flex item, so it needs to be told not to shrink and not to obey
     the global max-width:100%, or it collapses to zero width */
  flex:none; max-width:none;
  /* brightness(0) flattens each mark to solid ink, so the rail stays black and
     white and sits with the rest of the type. Transparent parts, like the hp
     knockout, keep showing the cream through. */
  filter:var(--mark-filter); opacity:.8;
  transition:opacity .35s ease;
}
.ticker-track .brand.has-logo img{ display:block; }
.ticker-track .brand.has-logo b{ display:none; }
.ticker:hover .brand.has-logo img{ opacity:1; }
.ticker-track i{ color:var(--orange); font-size:10px; font-style:normal; }
@keyframes slide{ to{ transform:translateX(-50%); } }

/* ---------- services ------------------------------------------------------ */

.svc-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:clamp(14px,2vw,22px); }
.svc{
  background:var(--paper); border:1px solid var(--line);
  border-radius:22px; padding:24px;
  display:flex; flex-direction:column; gap:12px;
  transition:transform .32s cubic-bezier(.2,.8,.2,1), box-shadow .32s;
}
.svc:hover{ transform:translateY(-6px); box-shadow:0 18px 36px rgba(30,25,10,.09); }
.svc-ic{ width:46px; height:46px; border-radius:14px; display:grid; place-items:center; color:#fff; }
.svc-ic svg{ width:22px; height:22px; }
.bg-orange{ background:var(--orange); }
.bg-blue{ background:var(--blue); }
.bg-yellow{ background:var(--yellow); color:#141414; }  /* tile stays yellow in both themes */
.bg-green{ background:var(--green); }
.svc h3{ font-family:var(--serif); font-size:20px; font-weight:700; margin:0; }
.svc p{ margin:0; font-size:14px; color:var(--muted); }

/* ---------- principles ---------------------------------------------------- */

/* ---------- principles: kinetic text panel ---------- */

.principles{ max-width:none; padding-inline:0; }
.prin-panel{
  background:#FF5324; color:#fff;   /* a fixed coloured surface — it reads the same in both themes */
  border-radius:34px;
  margin-inline:var(--gut);
  padding:clamp(44px,7vw,90px) clamp(20px,4vw,50px) clamp(30px,4vw,54px);
  text-align:center;
  overflow:hidden;
}

.prin-head{
  font-family:var(--serif); font-style:italic; font-weight:700;
  font-size:clamp(19px,2.5vw,30px);
  margin:0 0 clamp(26px,4vw,46px);
  display:inline-block; position:relative;
}
.prin-head em{ font-style:italic; }
.prin-ul{
  position:absolute; right:0; bottom:-13px;
  width:62%; height:14px; color:var(--yellow); overflow:visible;
}

.kinetic{ display:flex; flex-direction:column; gap:clamp(2px,.6vw,8px); }
.kinetic li{
  transition:translate .18s linear;
  will-change:translate;
}

/* the centred line; the marker and tags hang outside it so the word
   never shifts when they appear */
.prin-line{ position:relative; display:inline-block; }
.prin-mask{ display:block; overflow:hidden; }

.prin-word{
  display:inline-block;
  font-family:var(--body); font-weight:700;
  font-size:clamp(26px,5.8vw,70px);
  line-height:1.08; letter-spacing:-.02em;
  text-transform:uppercase;
  color:rgba(255,255,255,.42);
  translate:0 108%;
  transition:color .55s ease, scale .55s cubic-bezier(.2,.8,.2,1);
  cursor:default;
}
.principles.is-in .prin-word{
  animation:kineticIn .9s cubic-bezier(.2,.9,.25,1) forwards;
  animation-delay:calc(var(--i) * .12s);
}
@keyframes kineticIn{ to{ translate:0 0; } }

.kinetic li.is-active .prin-word{ color:#fff; scale:1.03; }

/* pointing marker */
.prin-mark{
  position:absolute; right:calc(100% + 20px); top:50%;
  width:clamp(28px,3vw,44px); height:auto;
  color:#fff;
  opacity:0; translate:-16px -50%;
  transition:opacity .45s, translate .45s cubic-bezier(.2,.9,.25,1);
}
.kinetic li.is-active .prin-mark{ opacity:1; translate:0 -50%; }

/* side annotations */
.prin-tags{
  position:absolute; left:calc(100% + 20px); top:50%;
  display:flex; flex-direction:column;
  text-align:left; white-space:nowrap;
  font-family:var(--serif); font-style:italic;
  font-size:clamp(10px,1vw,13px); line-height:1.45;
  color:#F0CD3E;
  opacity:0; translate:14px -50%;
  transition:opacity .45s, translate .45s cubic-bezier(.2,.9,.25,1);
}
.prin-tags i{ font-style:italic; }
.kinetic li.is-active .prin-tags{ opacity:1; translate:0 -50%; }

.prin-note{
  font-family:var(--hand);
  font-size:clamp(19px,2.4vw,27px);
  color:rgba(255,255,255,.9);
  margin:clamp(20px,3vw,34px) 0 0;
  min-height:1.4em;
  animation:noteIn .45s ease;
}
@keyframes noteIn{ from{ opacity:0; translate:0 8px; } }

.prin-doodles{ width:120px; height:50px; margin:22px auto 0; }

/* ---------- notes --------------------------------------------------------- */

.note-grid{ display:grid; grid-template-columns:repeat(3,1fr); gap:clamp(14px,2vw,22px); }
.ncard{
  background:var(--paper); border:1px solid var(--line);
  border-radius:22px; padding:16px 16px 22px;
  transition:transform .32s cubic-bezier(.2,.8,.2,1), box-shadow .32s;
}
.ncard:hover{ transform:translateY(-6px); box-shadow:0 18px 36px rgba(30,25,10,.09); }
.nthumb{ display:block; aspect-ratio:16/10; border-radius:14px; margin-bottom:16px; }
.nt-1{ background:linear-gradient(135deg,#FF5324,#F0CD3E); }
.nt-2{ background:linear-gradient(135deg,#2978F3,#B4A7F5); }
.nt-3{ background:linear-gradient(135deg,#12A05C,#F0CD3E); }
.ncard h3{ font-family:var(--serif); font-size:18px; font-weight:700; margin:0 0 8px; line-height:1.25; }
.ncard p{ margin:0; font-size:13px; color:var(--muted); }

/* ---------- testimonials --------------------------------------------------- */

@keyframes fade{ from{ opacity:0; transform:translateY(10px);} }

.q-stage{ position:relative; padding-block:clamp(20px,4vw,40px); }

.q-art{
  width:100%; height:auto; display:block;
  max-width:900px; margin-inline:auto;
}
.q-char{ transform-box:fill-box; transform-origin:bottom center; }
.q-char-a{ animation:sway2 6s ease-in-out infinite; }
.q-char-b{ animation:sway2 6s ease-in-out .8s infinite reverse; }
@keyframes sway2{ 0%,100%{ rotate:0deg; } 50%{ rotate:1.6deg; } }
.q-bits{ animation:sway 7s ease-in-out infinite; }

/* eyes track the cursor; JS sets the translate */
.q-eyes{ transition:translate .18s ease-out; }

/* the wave, and the hello that comes with it */
.q-arm{ transform-box:fill-box; }
.q-arm-a{ transform-origin:100% 78%; }
.q-arm-b{ transform-origin:0% 78%; }
.q-char.is-near .q-arm{ animation:wave2 .55s ease-in-out 3; }
@keyframes wave2{ 0%,100%{ rotate:0deg; } 30%{ rotate:-16deg; } 70%{ rotate:9deg; } }

.q-bubble{
  transform-box:fill-box; transform-origin:bottom center;
  opacity:0; scale:.5;
  transition:opacity .3s, scale .38s cubic-bezier(.2,1.5,.4,1);
}
.q-char.is-near .q-bubble{ opacity:1; scale:1; }
.q-hi{ font-family:var(--hand); font-size:34px; fill:#fff; }
.q-char-a .q-hi{ fill:var(--yellow); }

/* the mouth widens into a proper grin */
.q-mouth{ transition:d .3s ease; }
.q-char.is-near .q-mouth{ stroke-width:9; }

/* The title used to be placed against .q-stage, whose height the cards decide.
   Five cards made that box much taller, so 38% of it landed in the middle of
   the pile instead of on the illustration. It now hangs off its own box with
   the art, and nothing below can move it. */
.q-head{ position:relative; }

.q-title{
  position:absolute; left:54%; top:40%; z-index:3;
  translate:-50% 0;
  font-family:var(--serif); font-weight:700;
  font-size:clamp(30px,4.6vw,54px); line-height:1.06;
  color:#fff; text-align:center; margin:0;
  text-shadow:0 2px 14px rgba(30,20,10,.45);
  pointer-events:none;
}
.q-title em{ font-style:italic; font-weight:400; }

.q-cards{ position:relative; margin-top:clamp(18px,3vw,34px); }
.q-card{
  background:var(--paper);
  border:1px solid var(--line);
  border-radius:16px;
  padding:18px 20px 20px;
  margin:0;
  box-shadow:0 16px 34px rgba(30,25,10,.13);
  transition:transform .4s cubic-bezier(.2,.8,.2,1), box-shadow .4s;
}
.q-card:hover{ transform:translateY(-8px) rotate(0deg) !important; box-shadow:0 24px 46px rgba(30,25,10,.18); }
.q-card header{ display:flex; align-items:flex-start; justify-content:space-between; gap:12px; margin-bottom:12px; }
.q-card strong{ display:block; font-size:16px; font-weight:700; }
.q-card em{ font-family:var(--serif); font-style:italic; font-size:12.5px; color:var(--muted); }
/* softened ink rather than a fixed slate, so the quote keeps its weight
   against whichever paper is under it */
.q-card p{ margin:0; font-size:14px; color:#443F55; color:color-mix(in srgb, var(--ink) 82%, transparent); }
.q-av{ width:44px; height:44px; border-radius:50%; flex:none; }
.av-1{ background:linear-gradient(140deg,#FF5324,#F0CD3E); }
.av-2{ background:linear-gradient(140deg,#2978F3,#12A05C); }
.av-3{ background:linear-gradient(140deg,#B4A7F5,#FF5324); }
.av-4{ background:linear-gradient(140deg,#12A05C,#F0CD3E); }
.av-5{ background:linear-gradient(140deg,#1B3A6B,#B4A7F5); }

@media (min-width:901px){
  /* Columns rather than absolute placement: the cards flow and pack themselves,
     so adding or dropping a client does not mean re-measuring the pile by hand.
     The tilts are what keep it looking scattered. */
  /* Columns pack the cards themselves, so a client can be added or dropped
     without re-measuring anything. No vertical offsets here on purpose — which
     column a card lands in depends on how tall its quote is, so nudging them
     by hand just moved the holes around. The tilts do the scattering. */
  .q-cards{ columns:3; column-gap:20px; }
  .q-card{
    break-inside:avoid; -webkit-column-break-inside:avoid;
    display:inline-block; width:100%; margin:0 0 20px;
  }
  .qc-1{ transform:rotate(-1.2deg); }
  .qc-2{ transform:rotate(1.4deg); }
  .qc-3{ transform:rotate(-0.8deg); }
  .qc-4{ transform:rotate(1.1deg); }
  .qc-5{ transform:rotate(-1.4deg); }
}
@media (max-width:900px){
  .q-cards{ display:flex; flex-direction:column; gap:14px; margin-top:22px; }
  .q-title{ position:static; translate:0; color:var(--ink); text-shadow:none; margin-bottom:18px; }
  .q-art{ margin-bottom:10px; }
}


/* ---------- contact: envelope ------------------------------------------------ */

.contact{ text-align:center; max-width:none; padding-inline:0; }

.envelope{
  position:relative; perspective:1500px;
  max-width:var(--max); margin-inline:auto;
  background:var(--yellow);
  border-radius:30px;
  padding:0 clamp(14px,3vw,44px);
  overflow:hidden;
}
.env-inner{
  background:var(--cream);
  clip-path:polygon(0 0, 100% 0, 100% 58%, 50% 100%, 0 58%);
  padding:clamp(46px,7vw,86px) clamp(18px,4vw,44px) 0;
}

/* ---- the sealed flap ---- */

.env-flap{
  position:absolute; left:0; right:0; top:0; height:64%;
  background:linear-gradient(#EBC634,#DDB728);
  clip-path:polygon(0 0, 100% 0, 50% 100%);
  transform-origin:top center;
  transform:rotateX(0deg);
  transition:transform 1.1s cubic-bezier(.65,0,.25,1);
  z-index:4; padding:0 0 clamp(10px,2vw,22px);
  display:flex; align-items:flex-end; justify-content:center;
}
.envelope.is-open .env-flap{ transform:rotateX(-178deg); }

.env-seal{
  width:clamp(40px,5vw,62px); aspect-ratio:1; border-radius:50%;
  background:#FF5324; color:#fff;   /* a fixed coloured surface — it reads the same in both themes */
  display:grid; place-items:center;
  font-size:clamp(17px,2vw,24px);
  box-shadow:0 5px 14px rgba(120,60,0,.28);
  animation:sealPulse 2.4s ease-in-out infinite;
}
@keyframes sealPulse{ 0%,100%{ scale:1; } 50%{ scale:1.09; } }

.env-hint{
  position:absolute; left:50%; bottom:6%; translate:-50% 0; z-index:5;
  font-family:var(--hand); font-size:clamp(17px,2vw,23px); color:#8A6B12;
  transition:opacity .4s;
}
.envelope.is-open .env-hint{ opacity:0; }

/* contents wait until the flap is up */
.env-line,.env-mail,.contact-actions{
  opacity:0; translate:0 16px;
  transition:opacity .6s ease, translate .6s cubic-bezier(.2,.8,.2,1);
}
.envelope.is-open .env-line{ opacity:1; translate:0 0; transition-delay:.55s; }
.envelope.is-open .env-mail{ opacity:1; translate:0 0; transition-delay:.7s; }
.envelope.is-open .contact-actions{ opacity:1; translate:0 0; transition-delay:.82s; }

.env-line{
  font-family:var(--serif); font-weight:700;
  font-size:clamp(26px,4.4vw,52px); line-height:1.18; letter-spacing:-.01em;
  margin:0 0 26px;
}
.env-line em{ font-style:italic; font-weight:400; }

.env-mail{
  display:inline-block; color:var(--blue);
  font-size:clamp(15px,1.7vw,19px); font-weight:500;
  border-bottom:1.5px solid transparent; transition:border-color .25s;
}
.env-mail:hover{ border-bottom-color:var(--blue); }

.contact-actions{ display:flex; gap:12px; justify-content:center; flex-wrap:wrap; margin:26px 0 0; }

.bouquet{ width:100%; height:auto; margin-top:clamp(6px,2vw,18px); }
/* Bottom centre is right for a flower — the head opens out of the stem rather
   than swelling from thin air. */
.bq-flower{ transform-box:fill-box; transform-origin:bottom center; scale:0; opacity:0; }

/* the flowers only bloom once the envelope has been opened */
/* It used to run 0.1 to 1 on a spring that overshot hard. A tenth of full size
   is most of the width of the bunch away from where the head belongs, so every
   flower visibly travelled into place instead of opening, and 27 of them doing
   it at once was the mess. Starting near full size keeps each head where it
   belongs and lets the fade carry the reveal. */
.envelope.is-open .bq-flower{
  animation:bloom .62s cubic-bezier(.22,1.16,.36,1) forwards;
  animation-delay:calc(.9s + var(--fd,0s));
}
@keyframes bloom{ from{ scale:.62; opacity:0; } to{ scale:1; opacity:1; } }
.bq-bits{ animation:sway 6s ease-in-out infinite; opacity:0; transition:opacity .6s 1.6s; }
.envelope.is-open .bq-bits{ opacity:1; }

/* stems grow up out of the envelope as it opens, leaves unfurl with them */
.bq-stems{ transform-box:fill-box; transform-origin:bottom center; scale:1 0; }
.envelope.is-open .bq-stems{
  scale:1 1;
  transition:scale .7s cubic-bezier(.2,.9,.3,1) .8s;
}

.bq-leaves{ opacity:0; transform-box:fill-box; transform-origin:bottom center; scale:.7; }
.envelope.is-open .bq-leaves{
  opacity:1; scale:1;
  transition:opacity .6s ease 1s, scale .7s cubic-bezier(.2,1.2,.4,1) 1s, translate .4s ease-out;
}

/* Once the bloom has finished playing, the flowers hand themselves over to the
   pointer. These have to stay scoped under .envelope.is-open, otherwise the
   bloom rule above out-ranks them, its forwards fill pins scale:1, and :hover
   never gets a look in. */
.envelope.is-open .bq-flower.bloomed{
  animation:none;
  scale:1; opacity:1;
  cursor:pointer;
  transition:scale .4s cubic-bezier(.2,1.55,.35,1);
}
.envelope.is-open .bq-flower.bloomed:hover{ scale:1.612; }

/* each layer drifts a different amount, so the bunch has depth */
.bq-back,.bq-mid,.bq-front{ transition:translate .4s ease-out; }

/* ---------- footer --------------------------------------------------------- */

.footer{
  max-width:var(--max); margin:0 auto;
  padding:36px var(--gut) 44px;
  border-top:1px solid var(--line);
}
.f-top{ display:flex; align-items:center; justify-content:space-between; gap:24px; flex-wrap:wrap; }
.f-links{ display:flex; gap:22px; flex-wrap:wrap; font-size:14px; }
.f-links a:hover{ color:var(--blue); }
.f-social{ display:flex; gap:8px; }
.f-social a{
  width:38px; height:38px; border-radius:12px;
  border:1px solid var(--line); background:var(--paper);
  display:grid; place-items:center; transition:.25s;
}
.f-social a svg{ width:18px; height:18px; }
.f-social a:hover{ background:var(--ink); color:var(--cream); border-color:var(--ink); }
.copy{ margin:26px 0 0; font-size:13px; color:var(--muted); }

/* ---------- preloader ------------------------------------------------------- */

.preloader{
  position:fixed; inset:0; z-index:300;
  background:var(--paper);
  display:grid; place-items:center;
  /* it leaves by sliding up off the top, so the site is revealed rather than
     faded in — the curve holds still for a beat then goes quickly */
  transition:translate .7s cubic-bezier(.76,0,.24,1), visibility .7s;
}
.preloader.is-done{ translate:0 -100%; visibility:hidden; }

.pre-inner{
  display:flex; flex-direction:column; align-items:center;
  gap:clamp(20px,3.4vw,34px);
  transition:opacity .26s ease, translate .44s cubic-bezier(.6,0,.3,1);
}
/* the contents lift a touch sooner than the panel, so the exit has some depth */
.preloader.is-done .pre-inner{ opacity:0; translate:0 -26px; }

/* everything arrives in order rather than all at once */
@keyframes preRise{ from{ opacity:0; translate:0 14px; } }
@keyframes preGrow{ from{ opacity:0; scale:0 1; } }
@keyframes prePop{  from{ opacity:0; scale:.35; } }
@keyframes preDraw{ to{ stroke-dashoffset:0; } }

.pre-mark{ animation:preRise .5s cubic-bezier(.2,.8,.2,1) both; }

/* ---- the four filling pills ---- */
.pre-bars{ display:flex; align-items:center; gap:9px; }
.pre-bars i{
  width:var(--w); height:9px; border-radius:100px;
  /* only ever visible for the moment before the first frame decodes */
  background:#16130E;
  transform-origin:left center;
  transition:background .26s ease, box-shadow .26s ease;
  animation:preGrow .38s cubic-bezier(.34,1.42,.5,1) both;
  animation-delay:calc(.16s + var(--i) * .06s);
}
.pre-bars i.is-active{
  background:var(--blue);
  box-shadow:0 0 0 4px rgba(41,120,243,.15);
}
.pre-bars i.is-full{ background:var(--ink); box-shadow:none; }

.pre-note{
  margin:0; font-size:11px; font-weight:600;
  letter-spacing:.26em; text-transform:uppercase;
  color:var(--muted);
  animation:preRise .46s cubic-bezier(.2,.8,.2,1) .3s both;
}
.pre-dots i{ font-style:normal; opacity:0; animation:preDot 1.4s steps(1) infinite; }
.pre-dots i:nth-child(2){ animation-delay:.24s; }
.pre-dots i:nth-child(3){ animation-delay:.48s; }
@keyframes preDot{ 0%{ opacity:0; } 30%,100%{ opacity:1; } }

/* ---- corner marks ---- */
/* Each carries two animations: one to pop it in, one to keep it alive. They
   drive different properties (scale/opacity vs rotate) so they compose. */
.pre-deco{ position:absolute; color:var(--blue); pointer-events:none; }
.pd-spark-a{
  width:clamp(22px,2.6vw,34px); top:clamp(46px,7vh,84px); left:clamp(30px,5vw,80px);
  animation:prePop .5s cubic-bezier(.34,1.42,.5,1) .06s both,
            preSpin 9s linear .5s infinite;
}
.pd-spark-b{
  width:clamp(20px,2.3vw,30px); bottom:clamp(60px,10vh,110px); right:clamp(40px,7vw,110px);
  animation:prePop .5s cubic-bezier(.34,1.42,.5,1) .18s both,
            preSpin 11s linear .6s infinite reverse;
}
@keyframes preSpin{ to{ rotate:360deg; } }
.pd-dots{
  width:clamp(20px,2.2vw,28px); top:clamp(48px,7vh,86px); right:clamp(30px,5vw,80px);
  color:var(--ink);
  animation:prePop .5s cubic-bezier(.34,1.42,.5,1) .12s both;
}
.pd-wave{
  width:clamp(200px,24vw,340px); bottom:0; left:0;
  translate:-10% 30%;
}
/* the wave inks itself in rather than just appearing. 380 comfortably exceeds
   the path length, so it starts fully hidden and finishes fully drawn. */
.pd-wave path{
  stroke-dasharray:380; stroke-dashoffset:380;
  animation:preDraw .8s cubic-bezier(.35,0,.2,1) .1s forwards;
}

body.is-loading{ overflow:hidden; }

/* ---------- cursor ---------------------------------------------------------- */

.cursor,.cursor-echo{ display:none; }

@media (pointer:fine){
  body,a,button,.wcard,.chip{ cursor:none; }

  /* A solid blue dot that swells into a bold black circle over anything you
     can act on. Sizes and colours are the only thing the states change. */
  .cursor{
    position:fixed; top:0; left:0; z-index:251;
    width:16px; height:16px; margin:-8px 0 0 -8px;
    border-radius:50%;
    background:var(--blue);
    display:grid; place-items:center;
    pointer-events:none; will-change:transform;
    transition:width .3s cubic-bezier(.34,1.4,.5,1), height .3s cubic-bezier(.34,1.4,.5,1),
               margin .3s cubic-bezier(.34,1.4,.5,1), background .25s ease,
               opacity .22s ease;
  }

  /* The echoes. Sizes and fades step down along the tail; each one's position
     is chased in JS so they only string out while the pointer is moving. */
  /* display has to be restored here — the rule above the media query hides
     both layers for touch, and only .cursor gets a display of its own below */
  .cursor-echo{ display:block; position:fixed; inset:0; z-index:250; pointer-events:none;
                transition:opacity .22s ease; }

  /* Off the page entirely: the pointer is over the browser chrome or another
     window, and the real system cursor is showing there instead. */
  .cursor.is-out,.cursor-echo.is-out{ opacity:0; }
  .cursor-echo i{
    position:absolute; top:0; left:0;
    width:var(--s); height:var(--s);
    margin:calc(var(--s) / -2) 0 0 calc(var(--s) / -2);
    border-radius:50%;
    background:var(--blue);
    opacity:var(--o);
    will-change:transform;
  }
  .cursor-echo i:nth-child(1){ --s:13px; --o:.55; }
  .cursor-echo i:nth-child(2){ --s:11px; --o:.42; }
  .cursor-echo i:nth-child(3){ --s:9px;  --o:.30; }
  .cursor-echo i:nth-child(4){ --s:7px;  --o:.20; }
  .cursor-echo i:nth-child(5){ --s:5px;  --o:.12; }

  .cursor-label{
    font-size:10px; font-weight:700; letter-spacing:.08em;
    color:var(--cream); opacity:0; transition:opacity .2s;
    white-space:nowrap;
  }

  /* over a link or card: bold black circle */
  .cursor.is-link{
    width:34px; height:34px; margin:-17px 0 0 -17px;
    background:var(--ink);
  }
  /* over anything carrying a data-cursor word: bigger still, and it speaks */
  .cursor.is-label{
    width:64px; height:64px; margin:-32px 0 0 -32px;
    background:var(--ink);
  }
  .cursor.is-label .cursor-label{ opacity:1; }
  /* the echoes are left blue even when the dot goes black — that contrast is
     what makes the tail read against the circle */
}

/* ---------- back to top + toast ---------------------------------------------- */

.to-top{
  position:fixed; right:20px; bottom:20px; z-index:210;
  width:46px; height:46px; border-radius:50%;
  background:#141414; color:#FFFFFF;
  display:grid; place-items:center; font-size:17px;
  opacity:0; pointer-events:none; transform:translateY(14px);
  transition:opacity .3s, transform .3s, background .25s;
}
.to-top.is-on{ opacity:1; pointer-events:auto; transform:none; }
.to-top:hover{ background:var(--blue); }

.toast{
  position:fixed; left:50%; bottom:26px; z-index:260;
  transform:translate(-50%,18px);
  background:#141414; color:#FFFFFF;
  padding:11px 20px; border-radius:100px; font-size:13.5px; font-weight:500;
  opacity:0; pointer-events:none; transition:.3s;
}
.toast.is-on{ opacity:1; transform:translate(-50%,0); }

/* ---------- modal -------------------------------------------------------------- */

.modal{ position:fixed; inset:0; z-index:220; display:none; }
.modal.is-open{ display:block; }
.modal-backdrop{ position:absolute; inset:0; background:rgba(30,25,10,.5); backdrop-filter:blur(6px); animation:fade .3s; }
.modal-card{
  position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
  width:min(900px,92vw); max-height:88vh; overflow:auto;
  background:var(--paper); border-radius:26px;
  display:flex; flex-wrap:wrap;
  animation:modalIn .4s cubic-bezier(.2,.8,.2,1);
}
@keyframes modalIn{ from{ opacity:0; transform:translate(-50%,-46%) scale(.96);} }
/* The panel fills the column and the clip sits centred inside it at its own
   ratio. Pinning the panel to the footage instead left the card's white showing
   under it whenever the copy ran longer; letting the dark run the full height
   turns that leftover space into a frame around the video rather than a gap. */
.modal-visual{
  position:relative; flex:1 1 340px;
  align-self:stretch; min-height:260px;
  background:#0E0C08;
}
.modal-visual .wplay.big{ width:70px; height:70px; margin:-35px 0 0 -35px; font-size:19px; transform:scale(1); opacity:1; }
.modal-body{ flex:1 1 320px; padding:30px; }
.modal-tag{
  display:inline-block; padding:5px 13px; border-radius:100px;
  background:rgba(41,120,243,.12); color:var(--blue);
  font-size:11px; font-weight:700; letter-spacing:.06em; text-transform:uppercase;
}
.modal-body h3{ font-family:var(--serif); font-size:30px; font-weight:700; margin:14px 0 10px; line-height:1.15; }
.modal-body > p{ margin:0 0 18px; font-size:14.5px; color:var(--muted); }
.modal-body .wstats{ margin-bottom:20px; }
.modal-meta{ display:flex; gap:34px; margin:0 0 22px; }
.modal-meta dt{ font-size:11px; font-weight:700; letter-spacing:.07em; color:var(--muted); }
.modal-meta dd{ margin:4px 0 0; font-size:14px; font-weight:700; }
.modal-nav{ display:flex; gap:8px; }
.modal-close{
  position:absolute; right:12px; top:12px; z-index:4;
  width:38px; height:38px; border-radius:50%;
  background:#141414; color:#FFFFFF;
  display:grid; place-items:center; font-size:21px; line-height:1; transition:.25s;
}
.modal-close:hover{ background:var(--orange); transform:rotate(90deg); }
body.modal-open{ overflow:hidden; }

/* ---------- scroll reveal ------------------------------------------------------ */


/* The section itself no longer moves - it is only the trigger. Everything
   inside it is a node, and the nodes arrive one after another down the page
   with a hairline drawing out of the heading ahead of them, so a section
   builds itself step by step instead of sliding up as one block.

   The container is left visible on purpose: if the JS never runs, no element
   is ever tagged as a node and the whole page simply shows, unanimated. */
.reveal{ opacity:1; transform:none; }

.flow-node{
  opacity:0;
  /* the dark mode scroll ripple writes here. Kept as its own custom property
     so it composes with the hover lifts that use the same translate slot,
     instead of the two of them overwriting each other. */
  translate:0 var(--wave, 0px);
}
.reveal.is-in .flow-node{
  opacity:1;
  /* backwards, not forwards: the node holds the hidden first frame through
     its delay, then hands control back to the ordinary declarations. A
     forwards fill would pin translate and scale at their end values and
     outrank the hover lifts on chips and cards, which use the same
     properties. */
  animation:flowIn .58s cubic-bezier(.22,.9,.28,1) backwards;
  animation-delay:calc(var(--fi,0) * 55ms);
}
@keyframes flowIn{
  from{ opacity:0; translate:0 20px; scale:.985; }
  to  { opacity:1; translate:0 0;    scale:1; }
}

/* the connector out of the section heading, drawn just before the content
   underneath it lands. It sits in the head's bottom margin, on the same left
   edge the cards start from. */
.section .sec-head{ position:relative; }
.section .sec-head::after{
  content:''; position:absolute; left:0; top:100%;
  width:1.5px; height:clamp(14px,2.2vw,26px);
  background:linear-gradient(var(--blue), transparent);
  scale:1 0; transform-origin:top;
}
.section.reveal.is-in .sec-head::after{
  animation:flowLine .45s cubic-bezier(.3,.8,.3,1) .1s forwards;
}
@keyframes flowLine{ to{ scale:1 1; } }

/* ==========================================================================
   RESPONSIVE
   ========================================================================== */

@media (max-width:900px){
  .svc-grid{ grid-template-columns:repeat(2,1fr); }
  .note-grid{ grid-template-columns:1fr; }
}

/* Two across at every size, phones included. Stacking them one per row meant
   scrolling through six screens to see six pieces of work, when the whole point
   of the section is taking them in at a glance.

   Two cards on a 375px screen leaves about 160px each, which is enough for the
   still and the title but not for the write-up — so on a phone the card is
   trimmed to the thumbnail and the name. Everything cut here is already in the
   panel that opens when the card is tapped, so nothing is actually lost. */
@media (max-width:640px){
  .work-list{ gap:12px; }
  .wcard{ border-radius:18px; }
  .wbody{ padding:12px 13px 15px; gap:6px; }
  .wbody h3{ font-size:15px; letter-spacing:-.01em; }
  .wbody > p,
  .wstats,
  .read-more{ display:none; }
  .wtag{ left:9px; top:9px; font-size:9.5px; padding:4px 9px; }
  .wplay{ width:44px; height:44px; margin:-22px 0 0 -22px; font-size:13px; }
}

@media (max-width:760px){
  html{ scroll-padding-top:96px; }

  .nav-links{
    position:absolute; top:calc(100% + 10px); left:0; right:0;
    background:var(--paper); border:1px solid var(--line); border-radius:22px;
    flex-direction:column; align-items:center; gap:16px; padding:22px;
    box-shadow:0 14px 34px rgba(30,25,10,.1);
    display:none; margin-left:0;
  }
  .nav-links.is-open{ display:flex; }
  .nav-pill{ position:relative; }
  .nav-pill .btn-ink{ margin-left:auto; }
  .burger{ display:flex; }

  .prin-panel{ border-radius:26px; }
  .prin-ul{ width:74%; bottom:-10px; }
  .prin-mark,.prin-tags{ display:none; }
  .modal-visual{ flex-basis:100%; min-height:200px; }
  .modal-body{ padding:22px; }
  .env-inner{ clip-path:polygon(0 0,100% 0,100% 66%,50% 100%,0 66%); }
}

/* Below this the four things in the bar start squeezing each other — the
   burger was being compressed out of its own circle. Trimming the widest item
   gives the rest their proper size back. */
@media (max-width:420px){
  .nav-pill .btn-ink{ padding-inline:15px; font-size:13px; }
  .nav-pill{ gap:8px; padding-left:13px; }
}

@media (max-width:520px){
  .svc-grid{ grid-template-columns:1fr; }
  .intro-meta{ flex-direction:column; align-items:flex-start; }
  .contact-actions{ flex-direction:column; }
  .contact-actions .btn{ justify-content:center; }
}

/* ==========================================================================
   FUN LAYER
   ========================================================================== */


/* ---------- mode switch ---------------------------------------------------
   A quiet pill with both glyphs in the track. The knob slides across and
   carries a light copy of whichever glyph it lands on, so the icon you can
   read is always the mode you are actually in.
   -------------------------------------------------------------------------- */

.mode-switch{
  --h:34px;
  --pad:3px;
  --knob:calc(var(--h) - var(--pad) * 2 - 2px);   /* -2px for the border */
  /* how far the knob has to travel: the inside of the track, less the knob
     and the gap it keeps at each end */
  --travel:calc(var(--h) * 2.05 - 2px - var(--knob) - var(--pad) * 2);
  --ease:cubic-bezier(.34,1.42,.5,1);             /* settles with a small overshoot */
  position:relative; flex:none;
  width:calc(var(--h) * 2.05); height:var(--h);
  padding:0; border-radius:100px;
  /* Fixed in both themes on purpose. The switch is a physical little object,
     not a surface — inverting it would leave a black knob on a black track. */
  background:#FAF8F3;
  border:1px solid #DED8C8;
  box-shadow:0 1px 2px rgba(20,16,6,.12);
  cursor:pointer;
  transition:border-color .25s;
  -webkit-tap-highlight-color:transparent;
}
.mode-switch:hover{ border-color:#BDB5A0; }
.mode-switch:focus-visible{ outline:2px solid var(--blue); outline-offset:3px; }

/* the two glyphs that stay put */
.ms-track{
  position:absolute; inset:0;
  display:flex; align-items:center; justify-content:space-between;
  padding:0 calc(var(--pad) + 4px);
}
.ms-ico{ width:15px; height:15px; color:#2B2B2B; }

/* The knob rides on translate, not left — left would relayout on every frame,
   translate stays on the compositor and holds 60fps. */
.ms-knob{
  position:absolute; top:var(--pad); left:var(--pad);
  width:var(--knob); height:var(--knob);
  border-radius:50%;
  background:#141414; color:#FFFFFF;
  display:grid; place-items:center;
  box-shadow:0 1px 3px rgba(20,16,6,.25);
  translate:0;
  transition:translate .48s var(--ease);
  will-change:translate;
}
:root[data-theme="dark"] .ms-knob{ translate:var(--travel); }

/* The glyphs do not just crossfade — the outgoing one spins away and shrinks
   while the incoming one spins in, so the swap reads as one movement. */
.ms-knob svg{
  position:absolute; width:14px; height:14px;
  transition:opacity .26s ease, rotate .48s var(--ease), scale .48s var(--ease);
}
.ms-k-sun{  opacity:1; rotate:0deg;    scale:1; }
.ms-k-moon{ opacity:0; rotate:-70deg;  scale:.4; }
:root[data-theme="dark"] .ms-k-sun{  opacity:0; rotate:70deg; scale:.4; }
:root[data-theme="dark"] .ms-k-moon{ opacity:1; rotate:0deg;  scale:1; }

/* A little squash in the direction of travel. It only runs while .theme-anim
   is on the page, which is exactly the window the knob is moving in. */
.theme-anim .ms-knob{ animation:knobSquish .48s var(--ease); }
@keyframes knobSquish{
  0%{   scale:1 1; }
  38%{  scale:1.14 .88; }
  70%{  scale:.95 1.05; }
  100%{ scale:1 1; }
}

.mode-switch:active{ scale:.96; }
.mode-switch{ transition:border-color .25s, scale .18s ease; }

/* ---- the circular wipe between themes ----
   The browser lays the incoming page over the outgoing one. Both get a default
   fade of their own, which would show through the circle as a grey wash, so
   both are turned off and only the clip-path in script.js animates. */
::view-transition-old(root),
::view-transition-new(root){
  animation:none;
  mix-blend-mode:normal;
}
/* the new theme has to sit on top for the circle to read as a reveal */
::view-transition-old(root){ z-index:1; }
::view-transition-new(root){ z-index:2; }

/* Only while the palette is actually swapping, so the flip crossfades instead
   of snapping. Removed straight after, to leave every other transition alone. */
.theme-anim,.theme-anim *,.theme-anim *::before,.theme-anim *::after{
  transition:background-color .34s ease, border-color .34s ease, color .34s ease,
             fill .34s ease, stroke .34s ease, box-shadow .34s ease, filter .34s ease !important;
  transition-delay:0s !important;
}

/* ...but the switch is the one thing that has to keep its own timing while the
   palette swaps, and the blanket rule above would replace it — its transition
   list has no translate, rotate or opacity in it, so the knob would jump
   instead of sliding. These are more specific, so they win the !important. */
.theme-anim .ms-knob{
  transition:translate .48s var(--ease) !important;
}
.theme-anim .ms-knob svg{
  transition:opacity .26s ease, rotate .48s var(--ease), scale .48s var(--ease) !important;
}


/* ---- clickable illustration props ---- */
.scene [data-prop]{ cursor:pointer; }
.scene [data-prop]:hover{ filter:drop-shadow(0 0 0 rgba(0,0,0,0)) brightness(1.06); }

.scene [data-prop="clapper"].hit .clapper-arm{ animation:clapHard .55s cubic-bezier(.3,1.4,.5,1); }
@keyframes clapHard{ 0%{ transform:rotate(0);} 35%{ transform:rotate(-34deg);} 100%{ transform:rotate(0);} }

.scene [data-prop="reel"].hit .reel{ animation:spin .5s linear 3; }

.scene [data-prop="coffee"].hit .steam{ animation:steamBurst .7s ease-out; }
@keyframes steamBurst{ 0%{ transform:translateY(0) scale(1); opacity:1;} 100%{ transform:translateY(-30px) scale(1.5); opacity:0;} }

.scene [data-prop="cactus"].hit .cactus-body{
  transform-box:fill-box; transform-origin:bottom center;
  animation:wiggle .55s ease;
}
@keyframes wiggle{ 0%,100%{ transform:rotate(0);} 25%{ transform:rotate(7deg);} 60%{ transform:rotate(-6deg);} }

.scene [data-prop="monitor"].hit .clip{ animation:shuffle .6s cubic-bezier(.3,1.3,.5,1); }
.scene [data-prop="monitor"].hit .clip-2{ animation-delay:.06s; }
.scene [data-prop="monitor"].hit .clip-3{ animation-delay:.12s; }
@keyframes shuffle{ 0%,100%{ y:290px; } 45%{ y:276px; } }

.scene [data-prop="camera"].hit{ animation:shake .4s ease; }
@keyframes shake{ 25%{ rotate:-2deg; } 75%{ rotate:2deg; } }

/* camera flash */
.flash{
  position:fixed; inset:0; z-index:290; background:#fff;
  opacity:0; pointer-events:none;
}
.flash.pop{ animation:flashPop .5s ease-out; }
@keyframes flashPop{ 0%{ opacity:.92; } 100%{ opacity:0; } }

/* ---- particles (sparkles + confetti) ---- */
.fx{ position:fixed; inset:0; z-index:270; pointer-events:none; overflow:hidden; }
.fx b{ position:absolute; display:block; border-radius:2px; will-change:transform,opacity; }

/* ---- hero hint ---- */
.hero-hint{
  display:flex; align-items:center; gap:10px;
  margin:18px auto 0; width:max-content;
  font-family:var(--hand); font-size:22px; color:var(--muted);
  transition:opacity .5s;
}
.hero-hint.is-done{ opacity:0; pointer-events:none; }
.hint-arrow{ width:52px; height:34px; color:var(--orange); animation:nudge 2.2s ease-in-out infinite; }
@keyframes nudge{ 0%,100%{ transform:translateY(0) rotate(0);} 50%{ transform:translateY(-6px) rotate(-5deg);} }

/* ---- treasure hunt HUD ---- */
.hunt{
  position:fixed; left:20px; bottom:20px; z-index:210;
  display:flex; align-items:center; gap:10px;
  background:#141414; color:#FFFFFF;
  padding:9px 16px; border-radius:100px;
  font-size:13px; font-weight:500;
  opacity:0; transform:translateY(14px); pointer-events:none;
  transition:opacity .35s, transform .35s;
}
.hunt.is-on{ opacity:1; transform:none; }
.hunt.all-done{ background:var(--blue); }
.hunt-eye{ color:var(--yellow); }
.hunt-pips{ display:flex; gap:4px; }
.hunt-pips i{ width:6px; height:6px; border-radius:50%; background:rgba(255,255,255,.28); transition:.3s; }
.hunt-pips i.on{ background:var(--yellow); transform:scale(1.3); }

/* ---- draggable stickers ---- */
/* The stickers are placed as a percentage of the page, which works on a wide
   screen where the margins are empty. On a phone there are no margins — they
   land straight on the copy — and dragging them is a mouse pastime anyway. */
@media (max-width:860px){
  .stickers{ display:none; }
}

.stickers{ position:absolute; inset:0; pointer-events:none; z-index:5; }
.stk{
  position:absolute; left:var(--x); top:var(--y);
  width:64px; height:64px; padding:0;
  transform:translate(-50%,-50%) rotate(var(--r));
  pointer-events:auto; touch-action:none;
  transition:filter .25s;
  animation:sway 6s ease-in-out infinite;
}
.stk:hover{ filter:drop-shadow(0 6px 12px rgba(30,25,10,.25)); }
.stk.dragging{ animation:none; z-index:9; filter:drop-shadow(0 12px 20px rgba(30,25,10,.3)); }
@keyframes sway{ 0%,100%{ translate:0 0; } 50%{ translate:0 -10px; } }
.stk-badge{
  display:grid; place-items:center; width:64px; height:64px;
  border-radius:18px; background:#141414; color:#F0CD3E;
  font-family:var(--body); font-size:12px; font-weight:700; line-height:1.15;
  border:3px solid #141414;
}
.stk-badge.alt{ background:#2978F3; color:#fff; border-color:#2978F3; font-size:11px; }

/* ---- back to top ring ---- */
.to-top{ position:fixed; }
.to-top .ring{ position:absolute; inset:0; width:46px; height:46px; transform:rotate(-90deg); }
.to-top .ring circle{
  fill:none; stroke:var(--yellow); stroke-width:3; stroke-linecap:round;
  stroke-dasharray:132; stroke-dashoffset:132;
}
.to-top i{ font-style:normal; position:relative; z-index:2; }

/* ---- scramble ---- */

/* ---- konami / sticker rain ---- */
.rain{ position:fixed; inset:0; z-index:265; pointer-events:none; overflow:hidden; }
.rain span{ position:absolute; font-size:26px; will-change:transform; }

/* ==========================================================================
   STROKE DRAWING
   ========================================================================== */

/* any .draw svg inks its paths in when it scrolls into view.
   JS measures each path and sets --len; the dash pair does the rest. */
.draw path{
  stroke-dasharray:var(--len,600);
  stroke-dashoffset:var(--len,600);
}
.draw.is-drawn path{
  animation:ink 1.1s cubic-bezier(.55,0,.25,1) forwards;
  animation-delay:var(--pd,0s);
}
@keyframes ink{ to{ stroke-dashoffset:0; } }

.h2-wrap{ position:relative; display:inline-block; }
.underline{
  position:absolute; left:0; bottom:-14px;
  width:100%; height:20px; color:var(--orange);
  overflow:visible;
}

.big-arrow{
  width:min(220px,42vw); height:auto; margin:0 auto 6px;
  color:var(--blue); transform:rotate(-3deg);
}
.big-arrow path:last-child{ --pd:.75s; }

/* the right-gutter squiggle, inked by scroll position */
.spine{
  position:fixed; right:14px; top:50%; translate:0 -50%;
  width:74px; height:min(74vh,700px); z-index:40;
  pointer-events:none; overflow:visible;
  opacity:0; transition:opacity .5s;
}
.spine.is-on{ opacity:1; }
.spine-ghost{ color:var(--line); }
.spine-ink{ color:var(--blue); stroke-dashoffset:var(--off,9999); }
.spine-dot{ fill:var(--orange); stroke:var(--cream); stroke-width:3; }

/* ==========================================================================
   PROJECT PREVIEW LOOPS
   ========================================================================== */

.pv{
  position:absolute; inset:0; width:100%; height:100%;
  color:rgba(255,255,255,.92); fill:rgba(255,255,255,.92);
  opacity:0; transition:opacity .45s;
}
.wthumb.is-playing .pv,.wcard:hover .pv{ opacity:1; }
.pv *{ animation-play-state:paused; }
.wthumb.is-playing .pv *{ animation-play-state:running; }
.wcard:hover .pv *{ animation-duration:1.4s; }

/* video takes over whenever a real file is supplied */
.wthumb video{
  position:absolute; inset:0; width:100%; height:100%;
  /* contain, not cover — the clips are a mix of 16:9 and 16:10, and cover was
     shaving the sides off whichever did not match the card */
  object-fit:contain; opacity:0; transition:opacity .45s;
}
.wthumb.is-playing video{ opacity:1; }
.wthumb.has-video .pv{ display:none; }

/* the same clip, full size, inside the project modal */
.modal-visual video{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:contain; background:#0E0C08;
}
.modal-visual.has-video .wplay{ display:none; }

/* These clips are decorative loops, not a player. controls are already off in
   JS; this stops any browser drawing its own chrome over them anyway. */
.wthumb video,
.modal-visual video{ pointer-events:none; }

.wthumb video::-webkit-media-controls,
.modal-visual video::-webkit-media-controls,
.wthumb video::-webkit-media-controls-enclosure,
.modal-visual video::-webkit-media-controls-enclosure,
.wthumb video::-webkit-media-controls-panel,
.modal-visual video::-webkit-media-controls-panel,
.wthumb video::-webkit-media-controls-start-playback-button,
.modal-visual video::-webkit-media-controls-start-playback-button{
  display:none !important;
  opacity:0 !important;
  -webkit-appearance:none;
}

/* --- motion graphics loop --- */
.m1{ animation:slideIn 2.8s ease-in-out infinite; }
.m2{ animation:slideIn 2.8s ease-in-out .18s infinite; }
.m3{ animation:slideIn 2.8s ease-in-out .36s infinite; }
.m4{ animation:pulseIn 2.8s ease-in-out infinite; transform-box:fill-box; transform-origin:center; }
.wipe{ fill:rgba(255,255,255,.35); animation:wipeAcross 2.8s ease-in-out infinite; }
@keyframes slideIn{
  0%,8%{ transform:translateX(-26px); opacity:0; }
  22%,72%{ transform:translateX(0); opacity:1; }
  90%,100%{ transform:translateX(22px); opacity:0; }
}
@keyframes pulseIn{
  0%,10%{ scale:0; opacity:0; } 28%,70%{ scale:1; opacity:1; } 92%,100%{ scale:.3; opacity:0; }
}
@keyframes wipeAcross{ 0%{ transform:translateX(0);} 100%{ transform:translateX(240px);} }

/* --- editing loop --- */
.head{ fill:#fff; animation:scrubX 3.6s cubic-bezier(.5,0,.5,1) infinite; }
@keyframes scrubX{ 0%,100%{ transform:translateX(0);} 50%{ transform:translateX(128px);} }
.c1,.c2,.c3,.c4,.c5,.c6{ animation:blink2 3.6s ease-in-out infinite; }
.c2{ animation-delay:.4s; } .c3{ animation-delay:.8s; }
.c4{ animation-delay:1.2s; } .c5{ animation-delay:1.6s; } .c6{ animation-delay:2s; }
@keyframes blink2{ 0%,100%{ opacity:.45; } 18%{ opacity:1; } }

/* --- 3d loop --- */
.orb{ transform-box:fill-box; transform-origin:center; }
.o1{ transform-box:fill-box; transform-origin:center; animation:tiltA 4s linear infinite; }
.o2{ transform-box:fill-box; transform-origin:center; animation:tiltB 4s linear infinite; }
.ball{ transform-box:fill-box; transform-origin:center; animation:breathe 4s ease-in-out infinite; }
@keyframes tiltA{ from{ rotate:0deg; } to{ rotate:360deg; } }
@keyframes tiltB{ from{ rotate:90deg; } to{ rotate:450deg; } }
@keyframes breathe{ 0%,100%{ scale:1; } 50%{ scale:1.22; } }


/* ---------- interactive pass -------------------------------------------------
   Everything clickable answers back with the same gesture as the nav links: an
   ink fill that grows from the middle on a spring curve, a small lift, and the
   text flipping to cream. Written here, near the end, so it lands after the
   base rules for each component but before the responsive blocks.

   Two things to know about how the fill is built:

   - The pseudo sits at z-index:-1, which paints it above the element's own
     background but below its text. That only holds inside a stacking context,
     so every element carrying a fill also gets isolation:isolate - without it
     the pseudo escapes upward and disappears behind the element's background.
   - inset:0 on an absolutely positioned pseudo resolves against the padding
     box, so the fill stops inside the border and the ring stays visible.

   Elements marked data-magnetic get an inline transform from the JS while the
   cursor is over them, so their hover motion uses the standalone translate and
   scale properties instead - those compose with transform rather than losing
   to it.
   -------------------------------------------------------------------------- */

/* --- filter chips --- */
.chip{ position:relative; isolation:isolate; }
.chip::after{
  content:''; position:absolute; inset:0; z-index:-1;
  border-radius:inherit; background:var(--ink);
  transform:scale(.62); opacity:0;
  transition:transform .3s cubic-bezier(.34,1.4,.5,1), opacity .22s;
}
.chip:hover{ color:var(--cream); border-color:var(--ink); translate:0 calc(-1px + var(--wave, 0px)); }
.chip:hover em{ color:var(--cream); opacity:.55; }
.chip:hover::after{ transform:scale(1); opacity:1; }
.chip:active{ translate:0 var(--wave, 0px); }

/* --- the email pill in the intro --- */
.pill-link{ position:relative; isolation:isolate; }
.pill-link::after{
  content:''; position:absolute; inset:0; z-index:-1;
  border-radius:inherit; background:var(--ink);
  transform:scale(.62); opacity:0;
  transition:transform .3s cubic-bezier(.34,1.4,.5,1), opacity .22s;
}
.pill-link:not(.is-static):hover{ color:var(--cream); border-color:var(--ink); }
.pill-link:not(.is-static):hover::after{ transform:scale(1); opacity:1; }
/* the live dot keeps its green, it just needs to stop blending into the fill */
.pill-link:not(.is-static):hover .dot-live{ box-shadow:0 0 0 2px rgba(255,255,255,.18); }

/* --- buttons lift as well as fill --- */
.btn{ transition:transform .25s cubic-bezier(.2,.8,.2,1), translate .25s cubic-bezier(.2,.8,.2,1), background .25s, color .25s, border-color .25s; }
.btn:hover{ translate:0 -2px; }
.btn:active{ translate:0 0; }
.btn-line:hover{ border-color:var(--ink); }

/* --- service cards: the tile is the thing that reacts --- */
.svc{ transition:transform .32s cubic-bezier(.2,.8,.2,1), box-shadow .32s, border-color .32s; }
.svc-ic{ transition:transform .42s cubic-bezier(.34,1.3,.5,1), box-shadow .42s; }
.svc h3{ transition:color .25s; }
.svc:hover .svc-ic{ transform:scale(1.09) rotate(-7deg); box-shadow:0 10px 20px rgba(30,25,10,.18); }
.svc:hover h3{ color:var(--blue); }

/* --- brand marks: each one answers on its own, the rail never stops --- */
.ticker-track .brand.has-logo img{
  transition:opacity .35s ease, transform .35s cubic-bezier(.2,.8,.2,1);
}
.ticker-track .brand.has-logo:hover img{ opacity:1; transform:scale(1.14); }

/* --- the address on the envelope: underline sweeps in from the left --- */
.env-mail{ position:relative; transition:border-color .25s, color .25s, translate .25s cubic-bezier(.2,.8,.2,1); }
.env-mail::after{
  content:''; position:absolute; left:0; right:0; bottom:-1.5px; height:1.5px;
  background:var(--blue); transform:scaleX(0); transform-origin:right;
  transition:transform .34s cubic-bezier(.2,.8,.2,1);
}
.env-mail:hover{ border-bottom-color:transparent; }
.env-mail:hover::after{ transform:scaleX(1); transform-origin:left; }

/* --- footer links get the same sweep --- */
.f-links a{
  position:relative; padding-bottom:3px;
  transition:color .25s, translate .25s cubic-bezier(.2,.8,.2,1);
}
.f-links a::after{
  content:''; position:absolute; left:0; right:0; bottom:0; height:1.5px;
  background:var(--blue); transform:scaleX(0); transform-origin:right;
  transition:transform .34s cubic-bezier(.2,.8,.2,1);
}
.f-links a:hover{ color:var(--blue); translate:0 -1px; }
.f-links a:hover::after{ transform:scaleX(1); transform-origin:left; }

/* --- social square: magnetic, so it leans on scale rather than transform --- */
.f-social a{
  transition:background .25s, color .25s, border-color .25s,
             scale .3s cubic-bezier(.34,1.4,.5,1), translate .3s cubic-bezier(.34,1.4,.5,1);
}
.f-social a:hover{ scale:1.08; translate:0 -3px; }
.f-social a:active{ scale:.96; translate:0 0; }

/* --- back to top --- */
.to-top{ transition:opacity .3s, transform .3s, background .25s, scale .3s cubic-bezier(.34,1.4,.5,1); }
.to-top.is-on:hover{ background:var(--blue); scale:1.08; }
.to-top.is-on:active{ scale:.94; }

/* --- theme switch --- */
.mode-switch{ transition:border-color .25s, scale .28s cubic-bezier(.34,1.4,.5,1); }
.mode-switch:hover{ scale:1.04; }
.mode-switch:active{ scale:.97; }

/* --- lightbox and modal controls --- */
.reel-close:active,.reel-arrow:active{ transform:scale(.94); }
/* These two had no styling at all - bare buttons sitting under the modal
   copy. Same round outline as the rest of the small controls. */
.q-arrow{
  width:42px; height:42px; border-radius:50%;
  border:1.5px solid var(--line); background:var(--paper);
  display:grid; place-items:center; font-size:16px; line-height:1;
  transition:background .25s, color .25s, border-color .25s,
             transform .28s cubic-bezier(.34,1.4,.5,1);
}
.q-arrow:hover{ background:var(--ink); color:var(--cream); border-color:var(--ink); transform:scale(1.06); }
.q-arrow:active{ transform:scale(.94); }

/* Touch screens have no hover, and leaving these on means the first tap paints
   the state and the finger has to land somewhere else to clear it. */
@media (hover:none){
  .chip:hover::after,
  .pill-link:not(.is-static):hover::after{ opacity:0; }
  .chip:not(.is-active):hover,
  .pill-link:not(.is-static):hover{ color:inherit; border-color:var(--line); }
  .f-links a:hover::after,.env-mail:hover::after{ transform:scaleX(0); }
  .btn:hover,.f-social a:hover,.f-links a:hover{ translate:0 0; }
  .chip:hover{ translate:0 var(--wave, 0px); }
  .f-social a:hover{ scale:1; }
  .svc:hover .svc-ic{ transform:none; box-shadow:none; }
  .q-arrow:hover,.reel-close:hover,.reel-arrow:hover{ transform:none; }
}
@media (max-width:1180px){ .spine{ display:none; } }

@media (max-width:760px){
  .underline{ bottom:-10px; }
  .big-arrow{ width:150px; }
  .hunt{ left:12px; bottom:12px; font-size:12px; padding:8px 13px; }
  .stk{ width:48px; height:48px; }
  .stk-badge{ width:48px; height:48px; font-size:10px; border-radius:14px; }
  .mode-switch{ --h:30px; }
  .hero-hint{ font-size:18px; }
  .hint-arrow{ width:40px; }

  /* The bar had a lot of air in it once the theme switch and the burger were
     the only things left beside the logo. Tighter padding and a smaller gap
     put the weight back on the mark. */
  .nav{ top:10px; }
  .nav-pill{ padding:8px 8px 8px 16px; gap:10px; }
  /* only the one in the bar. Left unscoped this also shrank the preloader
     mark and the footer mark to nav size, which on a phone left the loading
     screen with a 20px logo and nothing for it to shrink from. */
  .nav .logo{ --logo-h:20px; }
  .logo-xl{ --logo-h:34px; }
  .logo-big{ --logo-h:26px; }
  .burger{ width:38px; height:38px; }

  /* the treasure-hunt counter is a desktop game; on a phone it just sits on
     top of the page with nothing to click */
  .hunt{ display:none; }
}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{ animation:none !important; transition-duration:.01ms !important; }
  html{ scroll-behavior:auto; }
  .wrd{ opacity:1; transform:none; }
  .flow-node{ opacity:1; }
  .sec-head::after{ scale:1 1; }
  .hl::before{ transform:scaleX(1); }
  .stickers,.fx,.flash,.rain,.spine{ display:none; }
  .draw path{ stroke-dasharray:none; stroke-dashoffset:0; }
  .pv{ opacity:1; }
  .env-flap,.env-hint{ display:none; }
  .bq-flower,.bq-bits,.bq-leaves{ scale:1; opacity:1; }
  .bq-stems{ scale:1 1; }
  .env-line,.env-mail,.contact-actions{ opacity:1; translate:0; }
}
