/* ==========================================================================
   Dushyant Bhardwaj — portfolio
   Direction: editorial scrapbook. Cream paper, green cutting-mat board,
   a taped sticky note, polaroids. Display serif + geometric sans.
   ========================================================================== */

/* ---------- tokens ---------- */
:root{
  --paper:#F6F3EC;
  --paper-2:#FFFCF5;
  --ink:#12272B;
  --ink-2:#1D3A3E;
  --muted:#788C93;
  --line:#E4DFD3;

  --green-deep:#06503A;
  --green-mid:#0B7A57;
  --green-bright:#14C089;

  --note:#FBE79B;
  --note-2:#F6DA79;
  --accent:#DC4A32;

  --serif:'Playfair Display',Georgia,serif;
  --sans:'Outfit',system-ui,-apple-system,sans-serif;
  --mono:'JetBrains Mono',ui-monospace,monospace;
  --hand:'Caveat','Segoe Script',cursive;

  --r-lg:30px;
  --r-md:18px;
  --r-sm:10px;

  --sh-1:0 1px 2px rgba(18,39,43,.06),0 8px 24px rgba(18,39,43,.07);
  --sh-2:0 2px 6px rgba(18,39,43,.08),0 18px 46px rgba(18,39,43,.13);
  --sh-3:0 30px 70px rgba(6,40,32,.28);

  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);

  --gut:clamp(18px,4vw,48px);
  --max:1180px;
}

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

html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}

body{
  margin:0;
  background:var(--paper);
  color:var(--ink);
  font-family:var(--sans);
  font-weight:400;
  font-size:clamp(15px,.4vw + 14px,17px);
  line-height:1.62;
  -webkit-font-smoothing:antialiased;
  overflow-x:hidden;
}

/* paper tooth — a whisper of grain so the cream doesn't read as flat #fff */
body::before{
  content:"";position:fixed;inset:0;z-index:0;pointer-events:none;opacity:.5;
  background-image:radial-gradient(rgba(18,39,43,.045) 1px,transparent 1px);
  background-size:4px 4px;
}

img{max-width:100%;display:block;height:auto}
ul,ol{margin:0;padding:0;list-style:none}
p{margin:0 0 1em}
p:last-child{margin-bottom:0}
a{color:inherit}
code{font-family:var(--mono);font-size:.88em;background:rgba(18,39,43,.06);padding:.14em .42em;border-radius:5px}
main{position:relative;z-index:1}

::selection{background:var(--note);color:var(--ink)}

:focus-visible{outline:2.5px solid var(--accent);outline-offset:3px;border-radius:4px}

.skip-link{
  position:fixed;top:10px;left:50%;transform:translate(-50%,-160%);
  z-index:120;background:var(--ink);color:var(--paper-2);
  padding:10px 18px;border-radius:999px;font-size:14px;font-weight:500;
  transition:transform .25s var(--ease);
}
.skip-link:focus{transform:translate(-50%,0)}

/* Visible to screen readers, invisible to everyone else. Used for the
   collage heading — the wall reads as decoration but still needs a label. */
.sr-only{
  position:absolute;width:1px;height:1px;padding:0;margin:-1px;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;
}

/* the inline <symbol> sticker sheet — never painted itself */
.sprite{position:absolute;width:0;height:0;overflow:hidden}

/* ---------- type ---------- */
.display{
  font-family:var(--serif);
  font-weight:700;
  letter-spacing:-.018em;
  line-height:1.02;
  margin:0;
  color:var(--ink);
  font-size:clamp(2.1rem,4.6vw,3.5rem);
}

.eyebrow{
  font-size:11.5px;font-weight:600;letter-spacing:.17em;text-transform:uppercase;
  color:var(--muted);margin:0 0 14px;
}
.eyebrow--light{color:rgba(255,255,255,.62)}

/* ==========================================================================
   LOADER  — name, a rule that draws itself, a count to 100
   ========================================================================== */
.loader{
  position:fixed;inset:0;z-index:200;background:var(--paper);
  display:grid;grid-template-rows:auto 1fr auto;
  padding:clamp(22px,4vw,46px);
  transition:transform 1.05s var(--ease-out),opacity .5s ease .5s;
  /* Failsafe: if JS never runs, the curtain still lifts so the site is
     reachable. `forwards` (not `both`) means nothing is applied during the
     delay, so the JS-driven transition above stays in charge when JS works. */
  animation:loaderFailsafe .6s var(--ease-out) 6s forwards;
}
@keyframes loaderFailsafe{
  to{transform:translateY(-101%);opacity:0;visibility:hidden;pointer-events:none}
}
.loader.is-done{transform:translateY(-101%);opacity:0;pointer-events:none}
body.is-loading{overflow:hidden}

.loader__name{
  font-family:var(--sans);font-size:clamp(15px,1.6vw,19px);font-weight:400;
  letter-spacing:.01em;color:#B9B4A8;
}
.loader__rule{
  align-self:center;position:relative;height:1px;width:100%;
  background:var(--line);
}
.loader__fill{
  position:absolute;left:0;top:0;height:100%;width:0;
  background:var(--ink);transition:width .18s linear;
}
.loader__dot{
  position:absolute;top:50%;left:0;width:6px;height:6px;margin:-3px 0 0 -3px;
  background:var(--ink);border-radius:50%;transition:left .18s linear;z-index:2;
}
.loader__count{
  justify-self:end;display:flex;align-items:flex-start;gap:.06em;
  font-family:var(--serif);font-weight:500;color:var(--ink);
  font-size:clamp(4.5rem,17vw,13rem);line-height:.82;letter-spacing:-.03em;
  font-variant-numeric:tabular-nums;
}
.loader__pct{font-size:.26em;line-height:1;margin-top:.28em;font-weight:600}

/* ==========================================================================
   NAV — floating pill, blurred, tightens on scroll
   ========================================================================== */
.nav{
  position:fixed;top:0;left:0;right:0;z-index:90;
  display:flex;justify-content:center;
  padding:clamp(14px,2vw,26px) var(--gut);
  pointer-events:none;
}
.nav__pill{
  pointer-events:auto;
  display:flex;align-items:center;gap:clamp(6px,1.1vw,15px);
  padding:9px 9px 9px clamp(16px,2vw,28px);
  background:rgba(255,252,245,.82);
  border:1px solid rgba(18,39,43,.07);
  border-radius:999px;
  box-shadow:0 1px 2px rgba(18,39,43,.05),0 14px 40px rgba(18,39,43,.10);
  backdrop-filter:blur(16px) saturate(140%);
  -webkit-backdrop-filter:blur(16px) saturate(140%);
  transform:translateY(-130%);
  opacity:0;
  animation:navDrop .95s var(--ease-out) both;
  animation-delay:.15s;
  transition:padding .4s var(--ease),box-shadow .4s var(--ease),background .4s var(--ease);
}
@keyframes navDrop{to{transform:translateY(0);opacity:1}}

.nav.is-stuck .nav__pill{
  padding-top:6px;padding-bottom:6px;
  /* nearly opaque once the page moves, so the huge serif name scrolling
     underneath doesn't read through the glass */
  background:rgba(255,252,245,.96);
  box-shadow:0 1px 2px rgba(18,39,43,.06),0 10px 30px rgba(18,39,43,.14);
}

.nav__pill > a{
  position:relative;text-decoration:none;white-space:nowrap;
  font-size:clamp(14px,.9vw + 10px,16.5px);font-weight:400;color:var(--ink-2);
  padding:7px 10px;border-radius:999px;
  transition:color .25s var(--ease);
}
.nav__pill > a::after{
  content:"";position:absolute;left:10px;right:10px;bottom:4px;height:1.5px;
  background:var(--ink);border-radius:2px;
  transform:scaleX(0);transform-origin:left;
  transition:transform .32s var(--ease);
}
.nav__pill > a:hover::after,
.nav__pill > a.is-active::after{transform:scaleX(1)}
.nav__pill > a.is-active{color:var(--ink);font-weight:500}

.nav__cta{
  display:inline-flex;align-items:center;gap:8px;
  background:var(--ink);color:var(--paper-2)!important;
  padding:11px 20px!important;border-radius:999px;font-weight:500!important;
  transition:transform .3s var(--ease),background .3s var(--ease),box-shadow .3s var(--ease)!important;
}
.nav__cta::after{display:none}
.nav__cta:hover{
  background:#0A1B1E;transform:translateY(-2px);
  box-shadow:0 8px 22px rgba(18,39,43,.32);
}
.nav__cta svg{
  width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round;
  transition:transform .35s var(--ease);
}
.nav__cta:hover svg{transform:translateY(2px)}

.nav__burger{
  display:none;pointer-events:auto;
  width:46px;height:46px;border:1px solid rgba(18,39,43,.08);
  background:rgba(255,252,245,.86);backdrop-filter:blur(14px);
  border-radius:999px;cursor:pointer;
  flex-direction:column;align-items:center;justify-content:center;gap:5px;
  box-shadow:var(--sh-1);
}
.nav__burger span{
  display:block;width:17px;height:1.6px;background:var(--ink);border-radius:2px;
  transition:transform .35s var(--ease),opacity .25s ease;
}
.nav__burger[aria-expanded="true"] span:first-child{transform:translateY(3.3px) rotate(45deg)}
.nav__burger[aria-expanded="true"] span:last-child{transform:translateY(-3.3px) rotate(-45deg)}

/* ==========================================================================
   BOARD — the green cutting mat everything is pinned to
   ========================================================================== */
.board{
  position:relative;overflow:hidden;
  border-radius:var(--r-lg);
  background:
    radial-gradient(120% 120% at 8% 0%,rgba(255,255,255,.13) 0%,transparent 46%),
    linear-gradient(128deg,var(--green-deep) 0%,var(--green-mid) 52%,var(--green-bright) 128%);
  box-shadow:var(--sh-3);
  isolation:isolate;
}
.board__grid{
  position:absolute;inset:0;z-index:0;pointer-events:none;
  background-image:
    linear-gradient(rgba(255,255,255,.16) 1px,transparent 1px),
    linear-gradient(90deg,rgba(255,255,255,.16) 1px,transparent 1px),
    linear-gradient(rgba(255,255,255,.055) 1px,transparent 1px),
    linear-gradient(90deg,rgba(255,255,255,.055) 1px,transparent 1px);
  background-size:220px 220px,220px 220px,44px 44px,44px 44px;
  mask-image:radial-gradient(130% 100% at 50% 0%,#000 35%,transparent 100%);
  -webkit-mask-image:radial-gradient(130% 100% at 50% 0%,#000 35%,transparent 100%);
}
.board--teal{background:radial-gradient(120% 120% at 8% 0%,rgba(255,255,255,.12) 0%,transparent 46%),linear-gradient(128deg,#0A3B4C 0%,#0F6B7A 55%,#19A5A0 128%)}
.board--ink{background:radial-gradient(120% 120% at 8% 0%,rgba(255,255,255,.10) 0%,transparent 46%),linear-gradient(128deg,#16302F 0%,#25494A 55%,#3C6C63 128%)}
.board--warm{background:radial-gradient(120% 120% at 8% 0%,rgba(255,255,255,.12) 0%,transparent 46%),linear-gradient(128deg,#7A3520 0%,#A85433 55%,#D08A4A 128%)}

/* ---------- hero ---------- */
/* The nav is a fixed pill roughly 66px tall plus its own padding, and it does
   not scale with viewport height — so hero padding must clear it in px, not vh,
   or the pill lands on top of the name on short screens. */
.hero{padding:clamp(124px,13vw,186px) var(--gut) clamp(40px,7vw,80px)}
.board__inner{
  position:relative;z-index:1;
  max-width:var(--max);margin:0 auto;
  display:grid;grid-template-columns:minmax(0,1.04fr) minmax(0,.96fr);
  gap:clamp(28px,4.6vw,64px);align-items:center;
  padding:clamp(34px,5vw,64px) clamp(24px,4.4vw,60px) clamp(30px,3.6vw,44px);
}
.board__status{
  margin:0;display:inline-flex;align-items:center;gap:9px;
  font-size:13.5px;color:rgba(255,255,255,.74);letter-spacing:.01em;
  animation:fadeUp 1s var(--ease-out) both;animation-delay:1.5s;
}
.board__status .dot{
  width:7px;height:7px;border-radius:50%;background:#7BE8B4;
  box-shadow:0 0 0 4px rgba(123,232,180,.22);
  animation:pulse 2.6s ease-in-out infinite;
}
@keyframes pulse{
  0%,100%{box-shadow:0 0 0 3px rgba(123,232,180,.20)}
  50%{box-shadow:0 0 0 8px rgba(123,232,180,0)}
}

/* ---------- sticky note ---------- */
.note-col{position:relative;z-index:2}

.note{
  position:relative;
  background:linear-gradient(168deg,var(--note) 0%,var(--note-2) 100%);
  padding:clamp(28px,3.4vw,44px) clamp(24px,3vw,40px) clamp(30px,3.4vw,42px);
  border-radius:3px;
  box-shadow:
    0 1px 1px rgba(84,62,0,.14),
    0 14px 26px rgba(6,40,32,.24),
    0 34px 60px rgba(6,40,32,.22);
  transform:rotate(-1.4deg);
}
/* the curl of a real note lifting off the board */
.note::after{
  content:"";position:absolute;right:0;bottom:0;width:52px;height:52px;
  background:linear-gradient(135deg,transparent 50%,rgba(255,255,255,.5) 50%,var(--note-2) 52%);
  box-shadow:-3px -3px 8px rgba(84,62,0,.13);
  border-radius:0 0 3px 0;
}
.note__name{
  font-family:var(--serif);font-weight:800;
  font-size:clamp(2.5rem,5.6vw,4.35rem);line-height:.94;letter-spacing:-.028em;
  margin:0 0 .34em;color:#1A1403;
}
.note__role{
  font-family:var(--serif);font-weight:700;
  font-size:clamp(1rem,1.5vw,1.32rem);line-height:1.3;letter-spacing:-.01em;
  color:#2A2205;margin:0 0 1em;
}
.note__role span{opacity:.4;margin:0 .3em;font-weight:500}
.note__bio{
  font-size:clamp(14.5px,1.02vw,16.5px);line-height:1.72;color:#3B3110;
  max-width:44ch;margin:0;
}

/* ---------- tape & clip ---------- */
.tape{
  position:absolute;z-index:3;
  width:88px;height:26px;
  background:linear-gradient(100deg,rgba(255,255,255,.34),rgba(255,255,255,.62) 40%,rgba(255,255,255,.3));
  border-left:1px solid rgba(255,255,255,.22);
  border-right:1px solid rgba(255,255,255,.22);
  box-shadow:0 1px 4px rgba(6,40,32,.14);
  backdrop-filter:blur(1px);
}
.tape--note{top:-13px;left:50%;margin-left:-44px;transform:rotate(-1.6deg)}
.tape--l{top:-11px;left:8%;transform:rotate(-6deg);width:72px;height:22px}
.tape--r{top:-11px;right:8%;transform:rotate(5deg);width:72px;height:22px}

.clip{
  position:absolute;z-index:4;top:-19px;right:16%;
  width:15px;height:40px;border:2.6px solid var(--accent);border-radius:9px;
  box-shadow:0 2px 5px rgba(6,40,32,.22);
}
.clip::after{
  content:"";position:absolute;inset:5px 3.4px 9px;
  border:2.2px solid var(--accent);border-radius:6px;opacity:.85;
}

/* ---------- hero stickers: a three-row conveyor ----------
   The eight draggable SVG tiles are gone. In their place, three rows of small
   photo-stickers from the camera roll that scroll continuously and travel
   against each other. Each row's markup lists every sticker once; script.js
   clones the <ul> track until it more than fills the row, then animates a
   single translate loop whose distance is exactly one track-width — so the
   frame where the loop restarts is identical to the frame before it and the
   seam cannot be seen.

   Hovering a sticker zooms it and lifts it above its neighbours while the row
   slows underneath rather than stopping, which is what the brief asked for:
   "it just keeps on moving". The angle each sticker rests at is an nth-child
   rotation, so the strip reads as a scatter rather than a filmstrip. */
.pin{position:relative;z-index:1;min-width:0}
.stk{
  --sgap:clamp(10px,1.3vw,18px);
  position:relative;
  display:flex;flex-direction:column;gap:clamp(8px,1vw,14px);
  /* fade the two ends into the board so stickers arrive and leave rather than
     pop at a hard edge */
  -webkit-mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
          mask-image:linear-gradient(90deg,transparent,#000 7%,#000 93%,transparent);
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
}
.stk__row{
  position:relative;
  display:flex;overflow:hidden;
  /* overflow is hidden to bury the marquee seam, so a hovered sticker can only
     grow into this padding, never past the row's box. It therefore has to clear
     the zoom's vertical gain: (max sticker height * (s - 1) / 2). At s=1.85 and
     a 70px sticker that's ~30px, which this padding stays above. */
  padding:clamp(22px,3vw,34px) 0;
}
.stk__track{
  display:flex;flex:none;align-items:center;
  gap:var(--sgap);
  /* padding-right must equal the gap. The row lays tracks flush against each
     other, so this is the only thing standing between the last sticker of one
     copy and the first sticker of the next — set to anything else and the seam
     shows up as a rhythm change. It also makes the element's own width exactly
     one loop period, which is what script.js measures. */
  list-style:none;margin:0;padding:0 var(--sgap) 0 0;
  will-change:transform;
  animation:stkScroll var(--dur,48s) linear infinite;
}
/* Alternating travel. Rather than a second keyframe, the middle row simply runs
   the same one backwards — so the two directions can never drift out of step
   with each other, and --shift means the same thing in both. */
.stk__row[data-dir="-1"] .stk__track{animation-direction:reverse}
.stk__i{flex:none;display:flex;margin:0;padding:0}
.stk__btn{
  --k:1;
  display:block;padding:0;border:0;background:none;cursor:zoom-in;
  height:clamp(50px,6.4vw,70px);
  border-radius:11px;
  transform:rotate(calc(var(--rot,0deg) * var(--k))) scale(var(--s,1));
  transition:transform .42s var(--ease),filter .42s var(--ease),box-shadow .42s var(--ease);
  filter:drop-shadow(0 2px 4px rgba(4,24,20,.26)) drop-shadow(0 10px 20px rgba(4,24,20,.24));
  -webkit-tap-highlight-color:transparent;
}
.stk__btn img{
  display:block;height:100%;width:auto;
  border-radius:9px;
  border:3px solid #FFFBF2;                 /* the white sticker die-cut edge */
  background:#FFFBF2;pointer-events:none;
}
/* A gentle resting scatter — five angles cycled across the whole strip.
   NB: script.js reads each original sticker's computed --rot and pins it
   inline before cloning, so copies keep their angle across the loop seam.
   These stay the single source of the scatter; edit the angles here freely. */
.stk__i:nth-child(5n+1) .stk__btn{--rot:-5deg}
.stk__i:nth-child(5n+2) .stk__btn{--rot:4deg}
.stk__i:nth-child(5n+3) .stk__btn{--rot:-2.5deg}
.stk__i:nth-child(5n+4) .stk__btn{--rot:6deg}
.stk__i:nth-child(5n+5) .stk__btn{--rot:-7deg}
/* zoom: straighten, grow, throw a bigger shadow, and rise above the row.
   Gated to real pointers. On a touch screen :hover sticks after a tap, which
   would leave a sticker frozen large; there, the tap just opens the lightbox
   and focus-visible (keyboard) is the only thing that zooms in place.

   The row slowing down under a hovered sticker is deliberately *not* here.
   Doing it in CSS means changing animation-duration on a running animation,
   which re-maps its progress and teleports the strip backwards the instant the
   pointer arrives. script.js changes playbackRate instead, which holds the
   current position and only alters what happens next. */
@media (hover:hover){
  .stk__btn:hover{
    --k:0;--s:1.85;z-index:5;position:relative;
    filter:drop-shadow(0 10px 16px rgba(4,24,20,.30)) drop-shadow(0 24px 44px rgba(4,24,20,.30));
  }
}
/* Keyboard focus zooms too, but less far. The outline scales with the element,
   so its outer edge ends up (h/2 + 6.5px) * --s from the sticker's centre — at
   1.85 that clears the row's padding by ~11px and overflow:hidden shaves the
   top and bottom off the focus ring, which is the one indicator that has to
   stay whole. 1.45 keeps the entire ring inside the padding at both ends of the
   height clamp, and still reads as a clear zoom. */
.stk__btn:focus-visible{
  --k:0;--s:1.45;z-index:5;position:relative;
  filter:drop-shadow(0 10px 16px rgba(4,24,20,.30)) drop-shadow(0 24px 44px rgba(4,24,20,.30));
  outline:2.5px solid #FFF8E1;outline-offset:4px;
}
/* One loop travels exactly one track-width, which script.js measures and writes
   to --shift. Because the track is the same list repeated, the frame where the
   loop restarts is pixel-identical to the frame before it, so the seam can't be
   seen. translate3d on both ends keeps it on the compositor. */
@keyframes stkScroll{
  from{transform:translate3d(0,0,0)}
  to{transform:translate3d(calc(var(--shift,0px) * -1),0,0)}
}
.pin__hint{
  display:inline-flex;align-items:center;gap:8px;
  margin:clamp(10px,1.6vw,20px) 0 0;padding-left:2px;
  font-size:13px;letter-spacing:.02em;color:rgba(255,255,255,.6);
  transition:filter .55s var(--ease);
}
.pin__hint svg{
  width:16px;height:16px;flex:none;
  fill:none;stroke:currentColor;stroke-width:1.5;
  stroke-linecap:round;stroke-linejoin:round;
}
/* Once a sticker has actually been moved, the instruction has served its
   purpose — script.js adds .is-used and it fades out.
   It has to be `filter`, not `opacity`: the entrance animation is declared
   `both`, and a filled animation beats any normal declaration, inline or not,
   so `opacity:0` here could never win. `filter` isn't in the keyframes. */
.pin.is-used .pin__hint{filter:opacity(0)}

/* ---------- the stat row under the board ---------- */
.board__foot{
  position:relative;z-index:1;
  max-width:var(--max);margin:0 auto;
  padding:0 clamp(24px,4.4vw,60px) clamp(26px,3.4vw,38px);
  display:flex;flex-wrap:wrap;align-items:center;
  justify-content:space-between;gap:clamp(16px,2.4vw,30px);
}
.stats{
  display:flex;flex-wrap:wrap;gap:clamp(10px,1.5vw,16px);
  list-style:none;margin:0;padding:0;
}
.chit{
  background:var(--paper-2);border-radius:var(--r-sm);
  padding:12px 17px;min-width:126px;
  box-shadow:0 2px 4px rgba(6,40,32,.16),0 12px 26px rgba(6,40,32,.2);
  transform:rotate(var(--r,0deg));
  transition:transform .45s var(--ease),box-shadow .45s var(--ease);
}
.chit:nth-child(1){--r:-1.4deg}
.chit:nth-child(2){--r:1deg}
.chit:nth-child(3){--r:-.6deg}
.chit b{
  display:block;font-family:var(--serif);font-weight:700;
  font-size:clamp(1.15rem,1.7vw,1.5rem);line-height:1.05;color:var(--ink);
  letter-spacing:-.02em;
}
.chit span{
  display:block;font-size:11.5px;color:var(--muted);letter-spacing:.02em;
  margin-top:3px;line-height:1.35;
}
.chit:hover{--r:0deg;transform:translateY(-5px);box-shadow:0 4px 8px rgba(6,40,32,.18),0 22px 40px rgba(6,40,32,.28)}

/* ==========================================================================
   SECTIONS
   ========================================================================== */
.section{
  max-width:var(--max);margin:0 auto;
  padding:clamp(64px,10vw,132px) var(--gut);
}
.section__head{max-width:640px;margin-bottom:clamp(38px,5.5vw,68px)}
.section__lead{
  margin:18px 0 0;color:var(--muted);
  font-size:clamp(15px,1.05vw,17.5px);line-height:1.7;max-width:56ch;
}

/* ---------- journey ---------- */
.job{
  display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1fr);
  gap:clamp(26px,4.4vw,64px);align-items:center;
  padding:clamp(26px,4vw,52px) 0;
  border-top:1px solid var(--line);
}
.job:first-of-type{border-top:0}

.board--sm{border-radius:var(--r-md);box-shadow:0 18px 44px rgba(6,40,32,.2)}
.board--sm .board__grid{background-size:150px 150px,150px 150px,30px 30px,30px 30px}
.job__card{position:relative}
.board--sm > .paper{position:relative;z-index:1}

.paper{
  margin:clamp(34px,4vw,52px) clamp(22px,3vw,40px);
  background:linear-gradient(172deg,#FFFDF7,#F7F2E6);
  border-radius:var(--r-sm);
  padding:clamp(24px,3vw,34px) clamp(20px,2.6vw,30px);
  box-shadow:0 2px 4px rgba(6,40,32,.14),0 16px 32px rgba(6,40,32,.22);
  transform:rotate(-.7deg);
  transition:transform .5s var(--ease),box-shadow .5s var(--ease);
}
.job:hover .paper{transform:rotate(0) translateY(-5px);box-shadow:0 4px 8px rgba(6,40,32,.16),0 24px 46px rgba(6,40,32,.28)}

.paper__kicker{
  font-size:11px;font-weight:600;letter-spacing:.15em;text-transform:uppercase;
  color:#93887A;margin:0 0 12px;
}
.paper__word{
  font-family:var(--serif);font-weight:800;letter-spacing:-.028em;
  font-size:clamp(2rem,4.3vw,3.1rem);line-height:.96;color:var(--ink);
  margin:0 0 10px;
}
.paper__word em{
  display:block;font-family:var(--sans);font-style:normal;font-weight:500;
  font-size:clamp(.85rem,1.1vw,1rem);letter-spacing:.04em;text-transform:uppercase;
  color:var(--muted);margin-top:6px;
}
.paper__date{
  font-family:var(--mono);font-size:12px;color:#8C8173;margin:0 0 16px;letter-spacing:-.01em;
}
.paper__stack{display:flex;flex-wrap:wrap;gap:6px}
.paper__stack li{
  font-size:11.5px;font-weight:500;color:#5F6F6B;
  background:rgba(18,39,43,.055);border:1px solid rgba(18,39,43,.05);
  padding:4px 10px;border-radius:999px;
}

.job__meta{
  display:flex;flex-wrap:wrap;align-items:baseline;gap:14px;
  margin:0 0 10px;
}
.job__meta b{
  font-size:12px;font-weight:600;letter-spacing:.14em;text-transform:uppercase;color:var(--ink);
}
.job__meta span{font-family:var(--mono);font-size:12.5px;color:var(--muted)}
.job__title{font-size:clamp(1.9rem,3.6vw,2.9rem);margin-bottom:.5em}
.job__text > p{color:var(--ink-2);max-width:52ch}

.bullets{margin-top:20px;display:grid;gap:9px}
.bullets li{
  position:relative;padding-left:20px;
  font-size:14.5px;color:var(--muted);line-height:1.6;
}
.bullets li::before{
  content:"";position:absolute;left:2px;top:.62em;
  width:6px;height:6px;border-radius:50%;background:var(--green-mid);opacity:.55;
}

.link-out{
  display:inline-flex;align-items:center;gap:6px;margin-top:20px;
  font-size:14px;font-weight:500;color:var(--ink);text-decoration:none;
  border-bottom:1.5px solid rgba(18,39,43,.2);padding-bottom:2px;
  transition:border-color .3s var(--ease),gap .3s var(--ease);
}
.link-out svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.link-out:hover{border-color:var(--ink);gap:10px}

/* ---------- work grid ---------- */
.grid{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,330px),1fr));
  gap:clamp(30px,4.4vw,60px) clamp(24px,3.4vw,44px);
}
.proj{min-width:0}

.proj__cover{
  position:relative;display:block;overflow:hidden;
  aspect-ratio:4/3;border-radius:var(--r-md);
  text-decoration:none;
  box-shadow:0 2px 4px rgba(18,39,43,.08),0 14px 32px rgba(18,39,43,.14);
  transition:transform .55s var(--ease),box-shadow .55s var(--ease);
}
.proj__cover::after{
  content:"";position:absolute;inset:0;
  background-image:linear-gradient(rgba(255,255,255,.07) 1px,transparent 1px),linear-gradient(90deg,rgba(255,255,255,.07) 1px,transparent 1px);
  background-size:34px 34px;
  opacity:.9;
}
.proj__cover--mint {background:linear-gradient(132deg,#0C6349,#16C089)}
.proj__cover--felt {background:linear-gradient(132deg,#0A3644,#158A8C)}
.proj__cover--slate{background:linear-gradient(132deg,#20303A,#5B7889)}
.proj__cover--clay {background:linear-gradient(132deg,#7E3421,#CE7044)}
.proj__cover--sky  {background:linear-gradient(132deg,#123F63,#3189CC)}
.proj__cover--plum {background:linear-gradient(132deg,#3F2255,#8E52AC)}
.proj__cover--amber{background:linear-gradient(132deg,#7E5209,#DBA231)}

.proj__mark{
  position:absolute;z-index:1;right:-.04em;bottom:-.30em;
  font-family:var(--serif);font-weight:800;letter-spacing:-.05em;
  font-size:clamp(6rem,15vw,10.5rem);line-height:1;
  color:rgba(255,255,255,.16);
  transition:transform .7s var(--ease),color .55s var(--ease);
}
.proj__cover:hover .proj__mark{transform:translate(-6px,-8px) scale(1.03);color:rgba(255,255,255,.22)}

.proj__peek{
  position:absolute;z-index:2;left:50%;top:50%;
  display:inline-flex;align-items:center;gap:8px;white-space:nowrap;
  background:var(--paper-2);color:var(--ink);
  padding:13px 24px;border-radius:999px;
  font-size:14.5px;font-weight:500;
  box-shadow:0 8px 26px rgba(6,40,32,.3);
  transform:translate(-50%,-50%) scale(.86);opacity:0;
  transition:transform .42s var(--ease-out),opacity .3s ease;
}
.proj__peek svg{width:15px;height:15px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.proj__cover:hover .proj__peek,
.proj__cover:focus-visible .proj__peek{transform:translate(-50%,-50%) scale(1);opacity:1}
.proj__cover:hover{transform:translateY(-6px);box-shadow:0 6px 12px rgba(18,39,43,.1),0 26px 54px rgba(18,39,43,.2)}

.proj__tag{
  font-family:var(--mono);font-size:11.5px;color:var(--muted);
  margin:20px 0 9px;letter-spacing:-.01em;
}
.proj__title{font-size:clamp(1.5rem,2.5vw,2.05rem);margin-bottom:.42em}
.proj__desc{color:var(--muted);font-size:15px;line-height:1.65;max-width:46ch}

.chips{display:flex;flex-wrap:wrap;gap:6px;margin-top:16px}
.chips li{
  font-size:11.5px;font-weight:500;color:var(--ink-2);
  background:rgba(18,39,43,.05);border:1px solid rgba(18,39,43,.045);
  padding:4px 10px;border-radius:999px;
}
.proj__links{display:flex;gap:18px;margin:16px 0 0}
.proj__links a{
  font-size:14px;font-weight:500;text-decoration:none;color:var(--ink);
  border-bottom:1.5px solid rgba(18,39,43,.22);padding-bottom:2px;
  transition:border-color .28s var(--ease),color .28s var(--ease);
}
.proj__links a:hover{border-color:var(--accent);color:var(--accent)}

/* reveal of the hidden three.
   The animation is bound to .is-revealed (added by JS on expand) rather than
   to .is-more, because a hidden element's animation would otherwise run and
   finish while it was still display:none. */
.proj.is-more.is-revealed{
  animation:cardIn .7s var(--ease-out) both;
}
@keyframes cardIn{from{opacity:0;transform:translateY(26px) scale(.985)}to{opacity:1;transform:none}}

.more-wrap{display:flex;justify-content:center;margin-top:clamp(40px,5vw,66px)}
.pill-btn{
  display:inline-flex;align-items:center;gap:10px;cursor:pointer;
  font-family:var(--sans);font-size:15px;font-weight:600;color:var(--ink);
  background:var(--paper-2);border:1px solid rgba(18,39,43,.09);
  padding:14px 28px;border-radius:999px;
  box-shadow:var(--sh-1);
  transition:transform .34s var(--ease),box-shadow .34s var(--ease);
}
.pill-btn svg{
  width:16px;height:16px;fill:none;stroke:currentColor;stroke-width:2.2;
  stroke-linecap:round;stroke-linejoin:round;
  transition:transform .4s var(--ease);
}
.pill-btn:hover{transform:translateY(-3px);box-shadow:var(--sh-2)}
.pill-btn[aria-expanded="true"] svg{transform:rotate(180deg)}

/* ---------- expertise ---------- */
.skills{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,232px),1fr));
  gap:clamp(14px,1.8vw,22px);
}
.skill-card{
  background:var(--paper-2);border:1px solid var(--line);
  border-radius:var(--r-md);padding:clamp(22px,2.4vw,28px);
  box-shadow:0 1px 2px rgba(18,39,43,.04);
  transition:transform .4s var(--ease),box-shadow .4s var(--ease);
}
.skill-card:hover{transform:translateY(-4px);box-shadow:var(--sh-2)}
.skill-card__h{
  font-family:var(--serif);font-weight:700;font-size:1.22rem;letter-spacing:-.015em;
  margin:0 0 16px;padding-bottom:12px;border-bottom:1px solid var(--line);color:var(--ink);
}
.lvl{display:grid;gap:11px}
.lvl li{display:flex;justify-content:space-between;align-items:baseline;gap:12px}
.lvl span{font-size:14.5px;font-weight:500;color:var(--ink-2)}
.lvl em{
  font-family:var(--mono);font-style:normal;font-size:11px;color:var(--muted);
  white-space:nowrap;letter-spacing:-.01em;
}

.note-strip{
  position:relative;margin-top:clamp(26px,3.4vw,40px);
  background:linear-gradient(168deg,var(--note),var(--note-2));
  border-radius:3px;padding:clamp(22px,2.6vw,30px) clamp(22px,3vw,34px);
  box-shadow:0 1px 1px rgba(84,62,0,.12),0 12px 26px rgba(18,39,43,.12);
  transform:rotate(-.5deg);
  max-width:760px;
}
.note-strip p{color:#3B3110;font-size:15px;margin:0}
.note-strip b{font-weight:600}
.note-strip a{color:#3B3110;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:2.5px}
.note-strip a:hover{color:var(--accent)}

/* ---------- certifications ---------- */
.certs__h{
  font-family:var(--serif);font-weight:700;letter-spacing:-.02em;color:var(--ink);
  font-size:clamp(1.14rem,1.7vw,1.42rem);
  margin:clamp(32px,3.8vw,50px) 0 clamp(13px,1.5vw,19px);
}
.certs{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,264px),1fr));
  gap:clamp(9px,1.1vw,13px);list-style:none;margin:0;padding:0;
}
.cert{
  height:100%;display:flex;flex-direction:column;justify-content:center;gap:5px;
  background:var(--paper-2);border:1px solid var(--line);
  /* the accent edge is the only colour in this block, so it reads as a stack of
     filed credentials rather than another row of cards */
  border-left:3px solid var(--accent);
  border-radius:var(--r-sm);padding:13px 15px;text-decoration:none;
  transition:transform .35s var(--ease),box-shadow .35s var(--ease);
}
.cert:hover,.cert:focus-visible{transform:translateX(3px);box-shadow:var(--sh-2)}
.cert__n{
  display:flex;align-items:baseline;gap:7px;
  font-size:14.5px;font-weight:600;color:var(--ink);line-height:1.3;
}
/* decorative — the link text already says where it goes */
.cert__n::after{
  content:"↗";flex:none;font-size:10.5px;font-weight:400;color:var(--muted);
  transition:transform .35s var(--ease);
}
.cert:hover .cert__n::after{transform:translate(2px,-2px)}
.cert__m{font-family:var(--mono);font-size:10.5px;letter-spacing:-.01em;color:var(--muted)}

/* ---------- writing ---------- */
.posts{display:grid;gap:0;border-top:1px solid var(--line)}
.post{
  display:grid;grid-template-columns:auto 1fr auto;
  gap:clamp(16px,3vw,40px);align-items:baseline;
  padding:clamp(22px,2.6vw,30px) 4px;
  border-bottom:1px solid var(--line);
  text-decoration:none;
  transition:padding-left .38s var(--ease),background .38s var(--ease);
}
.post:hover{padding-left:16px;background:rgba(255,252,245,.7)}
.post__date{font-family:var(--mono);font-size:12px;color:var(--muted);white-space:nowrap}
.post__title{
  font-family:var(--serif);font-weight:700;letter-spacing:-.018em;
  font-size:clamp(1.2rem,2vw,1.6rem);line-height:1.2;color:var(--ink);margin:0;
}
.post__excerpt{margin:8px 0 0;font-size:14.5px;color:var(--muted);max-width:60ch}
.post__meta{
  display:flex;align-items:center;gap:12px;
  font-family:var(--mono);font-size:11.5px;color:var(--muted);white-space:nowrap;
}
.post__draft{
  font-family:var(--sans);font-size:10.5px;font-weight:600;letter-spacing:.1em;
  text-transform:uppercase;color:#9A7B12;
  background:rgba(246,218,121,.4);border:1px solid rgba(246,218,121,.7);
  padding:3px 9px;border-radius:999px;
}
.post__arrow{
  width:16px;height:16px;fill:none;stroke:var(--ink);stroke-width:1.8;
  stroke-linecap:round;stroke-linejoin:round;
  opacity:0;transform:translate(-5px,4px);
  transition:opacity .3s ease,transform .34s var(--ease);
}
.post:hover .post__arrow{opacity:1;transform:none}

.posts-empty{
  padding:clamp(34px,5vw,54px);text-align:center;
  border:1px dashed var(--line);border-radius:var(--r-md);
  background:rgba(255,252,245,.6);
}
.posts-empty h3{font-family:var(--serif);font-weight:700;font-size:1.4rem;margin:0 0 8px;letter-spacing:-.015em}
.posts-empty p{color:var(--muted);font-size:14.5px;margin:0}

/* ---------- student life ---------- */
.timeline{
  position:relative;display:grid;gap:0;
  grid-template-columns:repeat(auto-fit,minmax(min(100%,150px),1fr));
  margin-bottom:clamp(38px,5vw,62px);
  border-top:2px solid var(--line);
}
.timeline li{position:relative;padding:26px 16px 0 0}
.timeline li::before{
  content:"";position:absolute;top:-7px;left:0;
  width:12px;height:12px;border-radius:50%;
  background:var(--paper);border:2px solid #C8C2B2;
  transition:background .3s var(--ease),border-color .3s var(--ease),transform .3s var(--ease);
}
.timeline li:hover::before{background:var(--green-mid);border-color:var(--green-mid);transform:scale(1.18)}
.timeline li.is-now::before{
  background:var(--accent);border-color:var(--accent);
  box-shadow:0 0 0 5px rgba(220,74,50,.16);
}
.timeline b{
  display:block;font-family:var(--serif);font-weight:700;font-size:1.3rem;
  letter-spacing:-.02em;color:var(--ink);margin-bottom:6px;
}
.timeline span{display:block;font-size:13.5px;color:var(--muted);line-height:1.55;padding-right:8px}

.moments{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(min(100%,260px),1fr));
  gap:clamp(16px,2vw,24px);
}
.moment{
  position:relative;
  background:var(--paper-2);border:1px solid var(--line);
  border-radius:var(--r-md);padding:clamp(22px,2.4vw,28px);
  box-shadow:0 1px 2px rgba(18,39,43,.04);
  transition:transform .42s var(--ease),box-shadow .42s var(--ease);
}
.moment:hover{transform:translateY(-5px) rotate(-.4deg);box-shadow:var(--sh-2)}
.moment h3{
  font-family:var(--serif);font-weight:700;font-size:1.3rem;letter-spacing:-.018em;
  margin:0 0 10px;color:var(--ink);
}
.moment p{font-size:14.5px;color:var(--muted);line-height:1.65}
.moment__tag{
  font-family:var(--mono)!important;font-size:11.5px!important;
  color:var(--ink-2)!important;margin-top:14px!important;letter-spacing:-.01em;
}
.moment--note{
  background:linear-gradient(168deg,var(--note),var(--note-2));
  border:0;border-radius:3px;transform:rotate(-1deg);
  box-shadow:0 1px 1px rgba(84,62,0,.12),0 12px 26px rgba(18,39,43,.13);
  margin-top:8px;
}
.moment--note h3{color:#1A1403}
.moment--note p{color:#3B3110}
.moment--note:hover{transform:rotate(0) translateY(-5px)}
.moment--wide{grid-column:span 2}
.moment__links{margin-top:14px!important}
.moment__links a{
  display:inline-flex;align-items:center;gap:6px;
  font-family:var(--sans);font-size:14px;font-weight:500;color:var(--ink);
  text-decoration:none;border-bottom:1.5px solid rgba(18,39,43,.2);padding-bottom:2px;
  transition:border-color .3s var(--ease),gap .3s var(--ease);
}
.moment__links svg{width:14px;height:14px;fill:none;stroke:currentColor;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.moment__links a:hover{border-color:var(--ink);gap:10px}

/* ---------- contact ---------- */
.section--contact{padding-bottom:clamp(40px,6vw,80px)}
.board--contact{padding:clamp(44px,7vw,104px) clamp(26px,5vw,80px)}
.contact{position:relative;z-index:1;max-width:820px}
.contact__h{
  color:#FFFDF6;font-size:clamp(2.6rem,7vw,5.2rem);font-weight:800;
  letter-spacing:-.03em;line-height:.98;margin:0 0 clamp(26px,3.4vw,42px);
}
.contact__mail{
  display:inline-block;text-decoration:none;
  font-family:var(--serif);font-weight:600;letter-spacing:-.022em;
  font-size:clamp(1.3rem,3.6vw,2.6rem);color:#FFFDF6;
  border-bottom:2px solid rgba(255,255,255,.32);padding-bottom:5px;
  transition:border-color .34s var(--ease),transform .34s var(--ease);
  word-break:break-word;
}
.contact__mail:hover{border-color:#FFFDF6;transform:translateY(-2px)}
.contact__alt{
  margin:clamp(20px,2.6vw,30px) 0 0;
  font-size:14.5px;color:rgba(255,255,255,.66);
}
.contact__alt a{color:rgba(255,255,255,.9);text-decoration:none;border-bottom:1px solid rgba(255,255,255,.28)}
.contact__alt a:hover{border-color:rgba(255,255,255,.9)}

.socials{display:flex;flex-wrap:wrap;gap:10px;margin-top:clamp(26px,3.4vw,40px)}
.socials a{
  display:inline-block;text-decoration:none;
  font-size:14.5px;font-weight:500;color:#FFFDF6;
  background:rgba(255,255,255,.10);border:1px solid rgba(255,255,255,.2);
  padding:11px 20px;border-radius:999px;
  transition:background .3s var(--ease),transform .3s var(--ease),border-color .3s var(--ease);
}
.socials a:hover{background:rgba(255,255,255,.2);border-color:rgba(255,255,255,.42);transform:translateY(-3px)}

/* ---------- footer ---------- */
.foot{
  position:relative;z-index:1;
  max-width:var(--max);margin:0 auto;
  padding:clamp(26px,3.4vw,40px) var(--gut) clamp(40px,5vw,60px);
  display:flex;flex-wrap:wrap;justify-content:space-between;gap:12px;
  border-top:1px solid var(--line);
  font-size:13.5px;color:var(--muted);
}
.foot p{margin:0}
.foot a{color:var(--ink);text-decoration:none;border-bottom:1px solid rgba(18,39,43,.2)}
.foot a:hover{border-color:var(--ink)}

/* ==========================================================================
   GALLERY — a wall of prints in a masonry, click one to enlarge

   Masonry is done with CSS `columns`, not grid: it lets every photo keep its
   true aspect ratio instead of being cropped into a uniform cell, and it needs
   no JavaScript to measure anything.
   ========================================================================== */
.gal{
  columns:3;
  column-gap:clamp(16px,2.2vw,28px);
}

.gal__item{
  --r:-1deg;              /* resting angle, varied per photo below */
  --ty:0px;               /* lift on hover */
  position:relative;
  break-inside:avoid;
  margin:0 0 clamp(20px,2.6vw,32px);
  background:#FFFDF8;
  border-radius:4px;
  padding:9px 9px 0;
  box-shadow:0 1px 2px rgba(18,39,43,.06),0 10px 26px rgba(18,39,43,.09);
  transform:rotate(var(--r)) translateY(var(--ty));
  transition:transform .5s var(--ease),box-shadow .5s var(--ease);
}
/* prints don't stack at identical angles — nudge every few */
.gal__item:nth-child(2n){--r:1.2deg}
.gal__item:nth-child(3n){--r:-.5deg}
.gal__item:nth-child(4n){--r:1.7deg}
.gal__item:nth-child(5n){--r:-1.5deg}
.gal__item:nth-child(7n){--r:.7deg}

.gal__item:hover,
.gal__item:focus-within{
  --r:0deg;--ty:-7px;
  z-index:4;
  box-shadow:0 3px 8px rgba(18,39,43,.09),0 24px 52px rgba(18,39,43,.17);
}

.gal__btn{
  display:block;position:relative;
  width:100%;margin:0;padding:0;border:0;
  background:none;cursor:zoom-in;line-height:0;
  border-radius:2px;overflow:hidden;
}
.gal__btn img{
  display:block;width:100%;height:auto;border-radius:2px;
  transition:transform .8s var(--ease);
}
.gal__item:hover .gal__btn img{transform:scale(1.045)}

.gal__zoom{
  position:absolute;right:10px;bottom:10px;
  width:34px;height:34px;border-radius:999px;
  display:grid;place-items:center;
  background:rgba(18,39,43,.72);
  -webkit-backdrop-filter:blur(6px);backdrop-filter:blur(6px);
  opacity:0;transform:translateY(7px) scale(.9);
  transition:opacity .34s var(--ease),transform .34s var(--ease);
}
.gal__zoom svg{
  width:16px;height:16px;fill:none;stroke:#FFFDF6;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round;
}
.gal__item:hover .gal__zoom,
.gal__btn:focus-visible .gal__zoom{opacity:1;transform:none}

.gal__cap{display:flex;flex-direction:column;gap:2px;padding:11px 3px 13px}
.gal__title{
  font-family:var(--serif);font-weight:600;font-size:15px;
  letter-spacing:-.012em;line-height:1.34;color:var(--ink);
}
.gal__meta{font-family:var(--mono);font-size:11px;color:var(--muted);letter-spacing:-.01em}

/* staggered arrival — opacity only, so the hover transform stays free */
.gal__item{opacity:0}
.gal.is-in .gal__item{animation:galIn .7s var(--ease-out) both}
@keyframes galIn{from{opacity:0}to{opacity:1}}
.gal.is-in .gal__item:nth-child(1){animation-delay:.02s}
.gal.is-in .gal__item:nth-child(2){animation-delay:.09s}
.gal.is-in .gal__item:nth-child(3){animation-delay:.16s}
.gal.is-in .gal__item:nth-child(4){animation-delay:.23s}
.gal.is-in .gal__item:nth-child(5){animation-delay:.30s}
.gal.is-in .gal__item:nth-child(6){animation-delay:.37s}
.gal.is-in .gal__item:nth-child(n+7){animation-delay:.44s}

/* ==========================================================================
   LIGHTBOX — full-size viewer
   ========================================================================== */
.lb{
  position:fixed;inset:0;z-index:200;
  display:grid;place-items:center;
  padding:clamp(52px,7vw,84px) clamp(52px,7vw,90px);
  background:rgba(9,22,25,.88);
  -webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px);
  opacity:0;transition:opacity .34s var(--ease);
}
.lb[hidden]{display:none}
.lb.is-open{opacity:1}
body.lb-open{overflow:hidden}

.lb__fig{
  margin:0;max-width:100%;
  display:flex;flex-direction:column;align-items:center;gap:14px;
  transform:scale(.965);
  transition:transform .44s var(--ease-out);
}
.lb.is-open .lb__fig{transform:none}
.lb__fig img{
  display:block;width:auto;height:auto;
  max-width:100%;max-height:calc(100vh - clamp(150px,22vh,230px));
  background:#FFFDF8;padding:clamp(6px,.9vw,12px);border-radius:3px;
  box-shadow:0 30px 80px rgba(0,0,0,.5);
}
.lb__cap{
  display:flex;flex-wrap:wrap;align-items:baseline;justify-content:center;
  gap:6px 14px;text-align:center;color:rgba(255,253,246,.92);
}
.lb__cap > span:first-child{
  font-family:var(--serif);font-weight:600;letter-spacing:-.015em;
  font-size:clamp(15px,1.4vw,19px);
}
.lb__count{font-family:var(--mono);font-size:11.5px;color:rgba(255,253,246,.55)}

.lb__close,.lb__nav{
  position:absolute;z-index:2;
  width:46px;height:46px;border-radius:999px;
  display:grid;place-items:center;
  background:rgba(255,255,255,.1);border:1px solid rgba(255,255,255,.22);
  color:#FFFDF6;cursor:pointer;
  transition:background .28s var(--ease),border-color .28s var(--ease),transform .34s var(--ease);
}
.lb__close svg,.lb__nav svg{
  width:18px;height:18px;fill:none;stroke:currentColor;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round;
}
.lb__close:hover,.lb__nav:hover{background:rgba(255,255,255,.2);border-color:rgba(255,255,255,.46)}
.lb__close{top:clamp(12px,2.4vw,28px);right:clamp(12px,2.4vw,28px)}
.lb__close:hover{transform:rotate(90deg)}
.lb__nav{top:50%;margin-top:-23px}
.lb__nav--prev{left:clamp(8px,1.8vw,26px)}
.lb__nav--next{right:clamp(8px,1.8vw,26px)}
.lb__nav--prev:hover{transform:translateX(-3px)}
.lb__nav--next:hover{transform:translateX(3px)}

/* ==========================================================================
   COLLAGE WALL — the closing beat

   Every piece is placed by hand through four inline custom properties:
   --x/--y position it, --w sets its width, --rot its angle. --d is its
   parallax depth and --i its stacking order and arrival delay.

   The transform reads --rot through `calc(var(--rot) * var(--k))` rather than
   using it directly. That indirection matters: --rot is declared inline, and
   an inline declaration beats any stylesheet rule, so `:hover{--rot:0deg}`
   could never win. --k is not inline, so hover sets it to 0 and the piece
   straightens. Same reason the mobile query can reset left/top/width — those
   are real properties, not the inline custom ones.
   ========================================================================== */
.cw-wrap{
  position:relative;
  max-width:var(--max);margin:0 auto;
  padding:clamp(16px,3vw,44px) var(--gut) clamp(28px,4.5vw,64px);
}
.cw{position:relative;aspect-ratio:2/1;min-height:420px}

.cw__item{
  position:absolute;left:var(--x);top:var(--y);width:var(--w);
  z-index:var(--i,1);
  transform:
    translate3d(calc(var(--mx,0px) * var(--d,1)),calc(var(--my,0px) * var(--d,1)),0)
    rotate(calc(var(--rot,0deg) * var(--k,1)))
    scale(var(--s,1));
  transition:transform .55s var(--ease),box-shadow .5s var(--ease);
}
.cw__item:hover,.cw__item:focus-visible{--k:0;z-index:60}

/* ---- photo prints ---- */
.cw__ph{
  margin:0;
  background:#FFFDF8;padding:6px;border-radius:3px;
  box-shadow:0 1px 2px rgba(18,39,43,.08),0 14px 32px rgba(18,39,43,.14);
}
.cw__ph img{display:block;width:100%;height:auto;border-radius:2px}
.cw__ph:hover{
  --s:1.09;
  box-shadow:0 4px 10px rgba(18,39,43,.1),0 30px 62px rgba(18,39,43,.24);
}

/* ---- die-cut stickers ---- */
.cw__st{display:block;line-height:0}
.cw__st svg{
  display:block;width:100%;height:auto;
  filter:drop-shadow(0 3px 7px rgba(18,39,43,.2));
}
.cw__st:hover{--s:1.18}

/* ---- the coffee cup, with its own lettering ---- */
.cw__cup{
  display:flex;flex-direction:column;align-items:center;gap:1px;
  text-align:center;line-height:1;
}
.cw__cup svg{
  display:block;width:58%;height:auto;
  filter:drop-shadow(0 3px 8px rgba(18,39,43,.18));
}
.cw__cup b{
  font-family:var(--mono);font-weight:500;letter-spacing:.04em;
  font-size:clamp(9px,.95vw,13px);color:var(--ink);margin-top:3px;
}
.cw__cup i{
  font-family:var(--hand);font-style:normal;font-weight:600;
  font-size:clamp(10px,1.1vw,15px);line-height:1.02;color:var(--green-deep);
}
.cw__cup:hover{--s:1.1}

/* ---- hand-lettered scraps ---- */
.cw__scrap{
  display:block;width:auto;white-space:nowrap;text-align:center;
  font-family:var(--hand);font-weight:700;
  font-size:clamp(16px,2vw,28px);line-height:1;color:var(--ink);
}
.cw__scrap em{
  display:block;font-style:normal;font-weight:600;
  font-size:.56em;color:var(--muted);letter-spacing:.01em;
}
.cw__scrap--b{color:var(--accent)}
.cw__scrap:hover{--s:1.07}

/* ---- the taped blog pill ---- */
.cw__pill{
  display:flex;align-items:center;justify-content:center;gap:8px;
  width:auto;white-space:nowrap;
  padding:.78em 1.15em;border-radius:999px;
  background:linear-gradient(168deg,var(--note),var(--note-2));
  color:#1A1403;text-decoration:none;
  font-family:var(--sans);font-weight:600;font-size:clamp(12px,1.05vw,15px);
  box-shadow:0 1px 1px rgba(84,62,0,.14),0 12px 26px rgba(18,39,43,.15);
}
.cw__pill svg{
  flex:none;width:1em;height:1em;fill:none;stroke:currentColor;
  stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round;
  transition:transform .3s var(--ease);
}
.cw__pill:hover{--s:1.07}
.cw__pill:hover svg{transform:translate(2px,-2px)}
/* the tape is tuned for the green board; on cream it needs more body */
.cw__pill .tape{
  top:-9px;left:50%;width:54px;height:18px;margin-left:-27px;
  background:linear-gradient(100deg,rgba(255,255,255,.5),rgba(255,255,255,.8) 40%,rgba(255,255,255,.44));
  border-left-color:rgba(18,39,43,.05);border-right-color:rgba(18,39,43,.05);
  box-shadow:0 1px 3px rgba(18,39,43,.13);
}

/* ---- arrival: each piece fades in on its own beat, photos and stickers
        also drop the last few pixels. Only opacity is animated on the item
        itself so the hover transform is never fighting a filled keyframe. ---- */
.cw__item{opacity:0}
.cw.is-in .cw__item{
  animation:cwFade .7s var(--ease-out) both;
  animation-delay:calc(var(--i,1) * 60ms);
}
.cw.is-in .cw__ph img,
.cw.is-in .cw__st svg{
  animation:cwDrop .85s var(--ease-out) both;
  animation-delay:calc(var(--i,1) * 60ms);
}
@keyframes cwFade{from{opacity:0}to{opacity:1}}
@keyframes cwDrop{from{transform:translateY(20px) scale(.93)}to{transform:none}}


/* ==========================================================================
   MOTION — the load sequence, then scroll reveals
   ========================================================================== */
@keyframes fadeUp{from{opacity:0;transform:translateY(22px)}to{opacity:1;transform:none}}

/* hero pieces settle onto the board once the loader lifts */
.note{opacity:0;animation:noteIn 1.1s var(--ease-out) both;animation-delay:.85s}
@keyframes noteIn{
  from{opacity:0;transform:rotate(-5deg) translateY(38px) scale(.96)}
  to{opacity:1;transform:rotate(-1.4deg) translateY(0) scale(1)}
}
/* the three sticker rows fade up in sequence, then the stat row arrives.
   This has to sit on .stk__row and not on the track: the track already owns the
   scroll animation, and a second animation on the same element would fight it
   for `transform` — last one declared wins and the conveyor would stop. */
@keyframes stkIn{
  from{opacity:0;transform:translateY(18px)}
  to{opacity:1;transform:none}
}
.stk__row{opacity:0;animation:stkIn .8s var(--ease-out) both}
.stk__row:nth-child(1){animation-delay:.98s}
.stk__row:nth-child(2){animation-delay:1.10s}
.stk__row:nth-child(3){animation-delay:1.22s}
.pin__hint{opacity:0;animation:fadeUp .9s var(--ease-out) both;animation-delay:1.78s}

.stats .chit{opacity:0;animation:fadeUp .8s var(--ease-out) both}
.stats .chit:nth-child(1){animation-delay:1.44s}
.stats .chit:nth-child(2){animation-delay:1.54s}
.stats .chit:nth-child(3){animation-delay:1.64s}

/* scroll reveals */
.reveal{opacity:0;transform:translateY(26px);transition:opacity .8s var(--ease-out),transform .8s var(--ease-out)}
.reveal.is-in{opacity:1;transform:none}
/* hero children animate on load, not on scroll */
.hero .reveal{opacity:1;transform:none;transition:none}

/* ==========================================================================
   RESPONSIVE
   ========================================================================== */
@media (max-width:1080px){
  /* seven links plus a CTA is a wide pill — tighten it before the burger
     takes over at 900px, or it runs past the gutter */
  .nav__pill{gap:2px;padding:8px 8px 8px 16px}
  .nav__pill > a{font-size:14.5px;padding:7px 8px}
  .nav__pill > a::after{left:8px;right:8px}

  .board__inner{grid-template-columns:1fr;gap:clamp(30px,5vw,48px)}
  .note{max-width:640px}
  /* One column, so the marquee gets the full note width — which is what a
     conveyor wants. Nothing to cap here (unlike the old percentage-positioned
     scatter); the rows just re-measure their track-width on resize. */
  .board__foot{padding-top:clamp(4px,1vw,10px)}
}

@media (max-width:900px){
  .nav{justify-content:flex-end;align-items:flex-start}
  .nav__pill{
    position:absolute;top:calc(100% - 6px);right:var(--gut);
    flex-direction:column;align-items:stretch;gap:2px;
    padding:14px;border-radius:22px;min-width:236px;
    opacity:0;transform:translateY(-14px) scale(.97);pointer-events:none;
    animation:none;
    transition:opacity .3s var(--ease),transform .38s var(--ease);
  }
  .nav.is-open .nav__pill{opacity:1;transform:none;pointer-events:auto}
  .nav__pill > a{padding:11px 14px;border-radius:12px;font-size:16px}
  .nav__pill > a::after{display:none}
  .nav__pill > a:hover{background:rgba(18,39,43,.05)}
  .nav__cta{justify-content:center;margin-top:8px}
  .nav__burger{display:flex}

  .job{grid-template-columns:1fr;gap:26px}
  .job__card{order:-1}
  .moment--wide{grid-column:auto}
  .post{grid-template-columns:1fr;gap:8px}
  .post__meta{margin-top:6px}
  .post__arrow{display:none}

  .gal{columns:2}

  /* The hand-placed wall can't survive a phone, so below here the CSS stops
     reading --x/--y/--w and lets everything reflow into a loose pile. The
     angles stay: they're what makes it read as a collage and not a row. */
  .cw{
    position:static;aspect-ratio:auto;min-height:0;
    display:flex;flex-wrap:wrap;align-items:center;justify-content:center;
    gap:clamp(14px,3.4vw,26px);
  }
  .cw__item{position:relative;left:auto;top:auto;width:auto}
  .cw__ph{width:clamp(122px,31vw,196px)}
  .cw__st{width:clamp(40px,9.5vw,58px)}
  .cw__cup{width:clamp(74px,17vw,104px)}
  .cw__pill{order:-1;flex:0 0 auto;margin-bottom:6px}
  .cw__scrap{font-size:clamp(19px,5vw,26px)}
}

@media (max-width:560px){
  .loader{padding:18px}
  .note{padding:26px 22px 30px}
  .note::after{width:38px;height:38px}
  /* The conveyor survives a phone — it's the one thing on the board that needs
     no width at all. Two adjustments: smaller stickers (three rows of 70px eats
     a phone screen), and less breathing room, since without hover there is no
     tap zoom to leave space for. Nothing here sets touch-action, so a vertical
     swipe over the strip still scrolls the page — the stickers are plain
     buttons, and a tap opens the lightbox. */
  .stk{--sgap:11px;gap:7px}
  .stk__row{padding:7px 0}
  .stk__btn{height:clamp(44px,13vw,56px);cursor:pointer}
  .stk__btn img{border-width:2.5px}
  /* A paired keyboard can still focus a sticker here, but 7px of row padding
     can't hold the 1.85x focus zoom — it would grow ~24px into the gap and the
     row's overflow:hidden would slice its top and bottom off. So drop the grow
     at this width; --k:0 still straightens it and the outline (2.5px + 4px
     offset = 6.5px < 7px) stays inside the padding as the focus cue. */
  .stk__btn:focus-visible{--s:1}
  .pin__hint{display:none}   /* "hover to zoom" means nothing without a pointer */
  .board__foot{flex-direction:column;align-items:flex-start;gap:18px}
  .stats{width:100%}
  .chit{flex:1 1 128px;min-width:0;padding:11px 14px}
  .timeline{border-top:0;gap:20px}
  .timeline li{padding:0 0 0 22px}
  .timeline li::before{top:6px;left:0}
  .timeline li::after{
    content:"";position:absolute;left:5.5px;top:22px;bottom:-20px;width:1px;background:var(--line);
  }
  .timeline li:last-child::after{display:none}
  .proj__links,.socials{gap:12px}
  .foot{flex-direction:column;gap:6px}

  .gal{columns:1}
  .gal__item{max-width:420px;margin-inline:auto}
  .lb{padding:44px 10px}
  .lb__nav{width:40px;height:40px;margin-top:-20px}
  .lb__fig img{max-height:calc(100vh - 190px)}
}

/* ==========================================================================
   REDUCED MOTION — keep the layout, drop the movement
   ========================================================================== */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{
    animation-duration:.01ms!important;animation-delay:0ms!important;
    animation-iteration-count:1!important;transition-duration:.01ms!important;
  }
  .reveal{opacity:1!important;transform:none!important}
  /* .note and .chit each declare their own resting rotation, so making them
     visible is enough — no need to restate the angles here. */
  .note,.pin__hint,.chit,.board__status{opacity:1!important}
  /* The conveyor is the movement, so there is nothing to slow down — it stops
     being a conveyor. The blanket .01ms rule above would otherwise leave every
     track parked at its end frame, translated a full track-width sideways.
     Instead the rows become a static wrapping pile, which is the same shape the
     phone layout falls back to, and script.js skips cloning entirely so no
     sticker appears twice. */
  .stk{-webkit-mask-image:none;mask-image:none}
  .stk__row{
    overflow:visible;padding:0;opacity:1!important;
    animation:none!important;transform:none!important;
  }
  .stk__track{
    animation:none!important;transform:none!important;
    flex-wrap:wrap;justify-content:center;padding:0;
  }
  .nav__pill{transform:none!important;opacity:1!important}
  .proj__peek{opacity:1;transform:translate(-50%,-50%) scale(1)}
  /* the wall keeps its angles, loses the parallax and the drop-in */
  .gal__item,.cw__item{opacity:1!important}
  .cw__item{--mx:0px!important;--my:0px!important}
  .cw__ph img,.cw__st svg{transform:none!important}
  .gal__item:hover .gal__btn img{transform:none}
}

@media print{
  .loader,.nav,.proj__peek,.lb,.cw-wrap,.gal__zoom{display:none!important}
  body{background:#fff}
  .board{box-shadow:none;background:#f0f0f0!important}
  .gal{columns:2}
  .gal__item{opacity:1!important;transform:none!important;box-shadow:none;border:1px solid #ddd}
}
