/* ============================================================
   shutter kif. - black & white, gritty, minimal
   Composed sections sit on a 16:9 stage carrying the exact PSD
   coordinates in percent.
   ============================================================ */

@font-face{font-family:'Helv';src:url('../assets/fonts/Helvetica.woff') format('woff'),url('../assets/fonts/Helvetica.ttf') format('truetype');font-weight:400;font-style:normal;font-display:swap}
@font-face{font-family:'Helv';src:url('../assets/fonts/Helvetica-Oblique.woff') format('woff'),url('../assets/fonts/Helvetica-Oblique.ttf') format('truetype');font-weight:400;font-style:italic;font-display:swap}
@font-face{font-family:'Helv';src:url('../assets/fonts/Helvetica-Bold.woff') format('woff'),url('../assets/fonts/Helvetica-Bold.ttf') format('truetype');font-weight:700;font-style:normal;font-display:swap}
@font-face{font-family:'VCR';src:url('../assets/fonts/VCR.woff') format('woff'),url('../assets/fonts/VCR.ttf') format('truetype');font-weight:400;font-display:swap}

:root{
  --light:#ffffff;
  --dark:#000000;
  --on-light:#000000;
  --on-dark:#ffffff;

  /* paper = the tone actually painted behind the cut-outs.
     --el-inv / --el-blend are derived from it, never from a section name. */
  --paper:var(--dark);
  --el-inv:1;
  --el-blend:screen;

  --f:'Helv','Helvetica Neue',Helvetica,Arial,sans-serif;
  --f-vcr:'VCR','Helv',monospace;
  --ease:cubic-bezier(.16,1,.3,1);
  --gutter:clamp(1rem,3.5vw,3rem);
  --fade-t:7svh;        /* seam bleed, per side - 0 where there is no seam */
  --fade-b:7svh;
}

/* the angle is its own animatable property so the parallax translate on the
   same element stays live instead of being frozen into the keyframes */
@property --spin{syntax:'<angle>';inherits:false;initial-value:0deg}

/* invert - every surface and every cut-out flips together */
body.is-invert{
  --light:#000000;
  --dark:#ffffff;
  --on-light:#ffffff;
  --on-dark:#000000;
  --paper:#ffffff;
  --trk:rgba(255,255,255,.9);
  --trk-hi:#ffffff;
}

body.is-invert .hero__collage{
  mix-blend-mode:multiply;
  filter:grayscale(1) contrast(1.25);
  opacity:.7;
}
body.is-invert .hero__collage::after{
  background:repeating-linear-gradient(0deg,transparent 0 5px,rgba(0,0,0,.15) 6px 7px);
  mix-blend-mode:multiply;
}
body.is-invert .hero__silhouette{
  mix-blend-mode:multiply;
  filter:grayscale(1) contrast(1.6);
  opacity:.12;
}
body.is-invert .hero__dice-poster{
  filter:grayscale(1) contrast(1.5);
  mix-blend-mode:multiply;
  opacity:.75;
}
body.is-invert .demo-viewer::backdrop{
  background:rgba(0,0,0,.65);
}
body.is-invert .rail__open:hover{
  background:#000000;
  color:#ffffff;
}

*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth;-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--dark);color:var(--on-dark);
  font-family:var(--f);line-height:1.5;
  overflow-x:hidden;-webkit-font-smoothing:antialiased;
  transition:background .35s var(--ease),color .35s var(--ease);
}
img{max-width:100%;display:block}
a{color:inherit;text-decoration:none}
::selection{background:currentColor;color:var(--light)}
:focus-visible{outline:2px solid currentColor;outline-offset:4px}

/* A single fixed backdrop behind everything has its tone interpolated on
   scroll, so where two sections differ the tones bleed instead of meeting at a
   hard seam. Each stage paints its own opaque paper over it and fades that
   paper out at the viewport edges, which is where the bleed shows through. */
.backdrop{position:fixed;inset:0;z-index:-1;background:var(--dark);transition:background .35s var(--ease)}
.sec{position:relative;overflow:hidden}

/* ---- paper, and everything derived from it -------------------------------
   The cut-outs are dark artwork printed on an opaque white rectangle (star-a,
   star-b, dice, cards) or dark artwork on transparency (cd-a, cd-b, ic-mail).
   Either way the rule is the same: the artwork must end up LIGHTER than the
   paper and screen onto it, or DARKER than the paper and multiply into it.
   Anything else leaves the source rectangle sitting on top of the type - that
   was the black-box bug on the first and last pages.                        */
.sec--light{--paper:var(--light);color:var(--on-light);--el-blend:multiply;--el-inv:0}
.sec--dark {--paper:var(--dark); color:var(--on-dark); --el-blend:screen;  --el-inv:1}
body.is-invert .sec--light{--el-blend:screen;  --el-inv:1}
body.is-invert .sec--dark {--el-blend:multiply;--el-inv:0}

/* Tracking-overlay ink. The overlay composites with `difference` (see .trk),
   so one neutral grey reads the same faint grey over black paper, over white
   paper and over the cut-outs themselves - no per-paper theming, and it can
   never disappear against the white artwork the way a plain white hairline did.
   Same trick the controls use. */
:root{--trk:rgba(255,255,255,.86);--trk-hi:#ffffff}

.el,.boot__img{filter:invert(var(--el-inv));user-select:none;-webkit-user-drag:none}
.el{position:absolute;pointer-events:none;z-index:1;mix-blend-mode:var(--el-blend);transform:translateY(var(--ty,0))}


/* ============ FIXED CONTROLS ============ */
.ctl{
  position:fixed;right:clamp(1rem,2.2vw,2rem);top:50%;transform:translateY(-50%);z-index:100;
  display:flex;flex-direction:column;align-items:center;gap:.6rem;
  opacity:0;transition:opacity .8s var(--ease) .2s;
}
.is-lit .ctl{opacity:1}
.ctl__b{
  width:52px;height:52px;min-width:52px;min-height:52px;
  padding:0;border:1px solid var(--on-dark);border-radius:0;
  background:var(--dark);color:var(--on-dark);cursor:pointer;
  display:flex;flex-direction:column;align-items:center;justify-content:center;gap:3px;
  font-family:var(--f-vcr);line-height:1;user-select:none;
  transition:background .2s var(--ease),color .2s var(--ease),border-color .2s var(--ease),transform .15s var(--ease),box-shadow .15s var(--ease);
}
.ctl__b:hover,.ctl__b:focus-visible{
  background:var(--on-dark);color:var(--dark);
  transform:translate(-2px,-2px);
  box-shadow:2px 2px 0 var(--on-dark);
  outline:none;
}
.ctl__b:active{
  transform:translate(0,0);box-shadow:none;
}
.ctl__glyph{font-size:.85rem;letter-spacing:.05em;line-height:1}
.ctl__sub{display:flex;flex-direction:column;align-items:center;gap:1px;line-height:1}
.ctl__label{font-size:.52rem;font-weight:700;letter-spacing:.08em;opacity:.85}
.ctl__val{font-size:.48rem;opacity:.65;letter-spacing:.04em;font-family:var(--f-vcr)}
.ctl__b:hover .ctl__val{opacity:.95}
.ctl__bars{display:inline-flex;align-items:flex-end;justify-content:center;gap:2px;height:12px;width:14px}
.ctl__bars i{display:block;width:2px;height:3px;background:currentColor;border-radius:0}
.ctl__b.is-active .ctl__bars i:nth-child(1){animation:eq-bar .8s ease-in-out infinite alternate}
.ctl__b.is-active .ctl__bars i:nth-child(2){animation:eq-bar .6s ease-in-out .2s infinite alternate}
.ctl__b.is-active .ctl__bars i:nth-child(3){animation:eq-bar .9s ease-in-out .4s infinite alternate}
@keyframes eq-bar{0%{height:3px}100%{height:12px}}

/* ============ COMP STAGE ============
   COVER, not contain: the comps are full-bleed, so on a window wider than 16:9
   the stage grows past the viewport and the section crops it. Contain left bare
   backdrop down both sides - that was the "zoomed out" bug.
   The stage also carries the paper the cut-outs blend against, because
   container-type creates a stacking context and isolates the blend from
   anything painted outside it. The mask is anchored to the VIEWPORT, not to the
   stage box, so the seam fade stays on screen no matter how much of the stage
   is cropped away - --vtop/--vbot are where the viewport edges land inside the
   stage, and they change with how the stage is aligned in its section. */
/* Absolutely placed, not grid-aligned: once the stage overflows its section,
   Chrome falls back to `start` for align-self and the crop anchor silently
   moves to the top - which is what cropped the contact marks off the bottom. */
.stage{
  position:absolute;inset:0;margin:auto;   /* centred WITHOUT transform - the
     data-rise reveal owns `transform` on this element and would cancel it */
  width:max(100vw, calc(100svh * 16 / 9));
  height:max(100svh, calc(100vw * 9 / 16));
  container-type:size;
  background:var(--paper);
  --vtop:calc(50% - 50svh);
  --vbot:calc(50% + 50svh);
  -webkit-mask-image:linear-gradient(to bottom,
      transparent var(--vtop),
      #000 calc(var(--vtop) + var(--fade-t)),
      #000 calc(var(--vbot) - var(--fade-b)),
      transparent var(--vbot));
          mask-image:linear-gradient(to bottom,
      transparent var(--vtop),
      #000 calc(var(--vtop) + var(--fade-t)),
      #000 calc(var(--vbot) - var(--fade-b)),
      transparent var(--vbot));
}
/* First and last pages have no seam above / below them, so they get the full
   frame instead of fading into nothing. */
.hero .stage{--fade-t:0}
.contact .stage{--fade-b:0}
.lay{
  position:absolute;margin:0;z-index:5;line-height:1;letter-spacing:-.02em;font-weight:400;
  transform:translateY(var(--ty,0));
  mix-blend-mode:difference;
  color:#ffffff;
}
.lay p{color:#ffffff}

/* tracking overlay - above the cut-outs, under the type, never interactive */
.trk{
  position:absolute;inset:0;width:100%;height:100%;z-index:4;
  pointer-events:none;mix-blend-mode:difference;
  opacity:1 !important;
}
.trk.is-on{opacity:1 !important}

/* ---- 01 front ---- */
.hero{height:100svh;display:grid;place-items:center}
.el--star-a{left:70%;top:6%;width:34%}
.el--star-b{left:-12%;top:54%;width:56%}
/* the stars turn, very slowly and against each other - one revolution is over
   two minutes, so it reads as drift rather than as an animation */
.el--star-a,.el--star-b{transform:translateY(var(--ty,0)) rotate(var(--spin))}
.el--star-a{animation:spin-cw 128s linear infinite}
.el--star-b{animation:spin-ccw 173s linear infinite}
@keyframes spin-cw {to{--spin:360deg}}
@keyframes spin-ccw{to{--spin:-360deg}}
.el--dice  {left:37.35%;top:23.5%;width:25.3%}
#diceCanvas.el--dice{
  aspect-ratio:658/648;
  height:auto;
  pointer-events:auto;
  cursor:grab;
  touch-action:none;
  z-index:3;
  filter:none !important;
  mix-blend-mode:normal !important;
}
#diceCanvas.el--dice:active{cursor:grabbing}
.lay--shutter{left:26.5%;top:43.0%;font-size:4.6cqw;font-style:italic;letter-spacing:-0.02em}
.lay--kif    {left:62.5%;top:43.0%;font-size:4.6cqw;font-style:italic;letter-spacing:-0.02em}

.cue{position:absolute;bottom:1.5rem;left:50%;transform:translateX(-50%);z-index:6;padding:.6rem;mix-blend-mode:difference;color:#ffffff}
.cue i{display:block;width:1px;height:34px;background:currentColor;opacity:.5;position:relative;overflow:hidden}
.cue i::after{content:'';position:absolute;inset:0;background:currentColor;animation:sd 2s cubic-bezier(.65,0,.35,1) infinite}
@keyframes sd{0%{transform:translateY(-100%)}60%,100%{transform:translateY(100%)}}

/* ---- 02 work ---- */
.machine{height:100svh;display:flex;flex-direction:column;position:relative}
/* The flower sits in its own stage BEHIND the WebGL canvas, which is now
   genuinely transparent. Being an ordinary cut-out is the whole point: it keys
   off the paper like every other one - dark on white paper, light on black,
   never a white box on black - and it takes the rotation and the tracker for
   free. It replaced a leopard photograph that could do none of that. */
.machine .stage{z-index:1}
/* Sized and centred in container units, never with a centring transform or the
   independent `translate` property - the tracker reads offsetLeft/offsetTop
   plus the computed `transform`, and either of those would desync the contour
   from the artwork. Source is 1248x1152, so height = width / 1.0833. */
/* Sized against the VIEWPORT, not the stage: the stage overflows on a cover
   crop, so a stage-relative size would bleed the petals off screen. Source is
   1248x1152, so width = height * 1.0833 and half of that is 0.5417.
   Never centred with a transform or the independent `translate` property - the
   tracker reads offsetLeft/offsetTop plus the computed `transform`, and either
   would desync the contour from the artwork. */
.el--flower{
  height:104svh;width:auto;
  left:calc(50cqw - 56.3svh);
  top:calc(50cqh - 52svh);
  transform:translateY(var(--ty,0)) rotate(var(--spin));
  animation:spin-cw 214s linear infinite;
}
.machine__stage{position:relative;flex:1;min-height:0;z-index:2}
#pspCanvas{position:absolute;inset:0;width:100%;height:100%;display:block;touch-action:pan-y}
/* deliberately NOT inverted: the bloom and the screen are emitted light,
   and inverting turns the highlights into black smears */
.machine__loading{position:absolute;inset:auto 0 50% 0;text-align:center;z-index:3;transition:opacity .5s}
.machine__loading.is-off{opacity:0;pointer-events:none}
.spin{display:inline-block;width:11px;height:11px;border:1px solid currentColor;border-top-color:transparent;border-radius:50%;opacity:.5;animation:spin .9s linear infinite}
@keyframes spin{to{transform:rotate(360deg)}}

.rail{
  position:absolute;left:var(--gutter);bottom:clamp(1.6rem,5vh,3.2rem);z-index:6;
  display:flex;align-items:baseline;gap:.45rem;font-family:var(--f-vcr);color:#ffffff;
  mix-blend-mode:difference;
}
.rail b{font-size:clamp(1.8rem,4vw,3rem);font-weight:400;line-height:1;font-variant-numeric:tabular-nums}
.rail i{font-style:normal;font-size:.9rem;opacity:.65}
.rail__open{
  margin-left:.7rem;align-self:center;width:36px;height:36px;display:grid;place-items:center;
  border:1px solid currentColor;font-size:1rem;line-height:1;
  transition:background .25s,color .25s,transform .4s var(--ease);
}
.rail__open:hover{background:#ffffff;color:#000000;transform:translate(2px,-2px)}
.rail.is-swap b{animation:flick .3s steps(2) both}
@keyframes flick{0%{opacity:0}50%,100%{opacity:1}}

.keys{position:absolute;right:var(--gutter);bottom:clamp(1.6rem,5vh,3.2rem);z-index:6;display:flex;gap:.3rem;color:#ffffff;mix-blend-mode:difference}
kbd{
  font-family:var(--f-vcr);font-size:.8rem;border:1px solid currentColor;opacity:.65;
  padding:.1rem .35rem;min-width:24px;text-align:center;
  transition:background .15s,color .15s,opacity .15s,transform .15s;
}
kbd.is-pressed{background:currentColor;color:var(--dark);opacity:1;transform:scale(.92)}

/* ---- 03 about ---- */
.about{height:100svh;display:grid;place-items:center}
.el--cd-a  {left:-5.99%;top:-23.61%;width:41.35%}
.el--cd-b  {left:82.08%;top:45.83%;width:29.90%}
.lay--abouth{left:50%;top:26%;transform:translate(-50%,var(--ty,0));font-family:var(--f-vcr);font-size:3.3cqw;letter-spacing:.06em}
.lay--bio{
  left:50%;top:38%;width:46%;margin-left:-23%;
  font-size:1.65cqw;line-height:1.45;letter-spacing:-0.005em;
  text-align:left;hyphens:manual;
}
.lay--bio p{margin:0}
.lay--bio p+p{margin-top:.65em}

/* ---- 04 contact ---- */
/* Bottom-aligned, not centred: the comp puts the mail and Instagram marks at
   89.7% of a 1080-tall canvas, so centring the cover crop ate them. Cropping
   off the top instead costs nothing - the cards already start above the frame. */
.contact{height:100svh;display:grid;place-items:center}
.contact .stage{
  top:auto;bottom:0;margin-block:0;
  --vtop:calc(100% - 100svh);--vbot:100%;
}
.el--cards{
  left:26.67%;top:-3.52%;width:46.67%;
  aspect-ratio:896/1184;
  pointer-events:auto;
  cursor:pointer;
  mix-blend-mode:normal !important;
  filter:none !important;
}
.el__card{
  position:absolute;
  user-select:none;
  -webkit-user-drag:none;
  mix-blend-mode:normal;
  opacity:1;
  will-change:transform;
  transition:transform .55s var(--ease);
}
.el__card--1{
  width:37%;
  left:46.5%;top:51%;
  transform:translate(-50%,-50%) rotate(-23deg) translateY(var(--ty,0));
  z-index:1;
}
.el__card--2{
  width:47%;
  left:54.5%;top:48.5%;
  transform:translate(-50%,-50%) rotate(-25deg) translateY(var(--ty,0));
  z-index:2;
  filter:drop-shadow(-8px 10px 14px rgba(0,0,0,0.35));
}
.el--cards:hover .el__card--1{
  transform:translate(-58%,-48%) rotate(-28deg) translateY(var(--ty,0)) scale(1.02);
}
.el--cards:hover .el__card--2{
  transform:translate(-42%,-52%) rotate(-20deg) translateY(var(--ty,0)) scale(1.02);
}
.lay--contact{left:23.54%;top:40.0%;font-size:3.78cqw}
.lay--me     {left:64.27%;top:40.0%;font-size:3.78cqw}
.lay--icons{
  left:50%;top:89.7%;transform:translate(-50%,var(--ty,0));
  display:flex;align-items:center;justify-content:center;gap:clamp(12px,1.2cqw,22px);
  mix-blend-mode:normal;
  opacity:1 !important;filter:none !important;
}
.lay__icon-link{
  width:clamp(42px,3.5cqw,52px);height:clamp(42px,3.5cqw,52px);
  display:grid;place-items:center;
  border:1px solid var(--on-dark);color:var(--on-dark);background:var(--dark);
  transition:background .25s var(--ease),color .25s var(--ease),transform .25s var(--ease),border-color .25s var(--ease),box-shadow .2s var(--ease);
}
.lay__icon-link svg{width:52%;height:52%;display:block;stroke:currentColor;transition:transform .25s var(--ease)}
.lay__icon-link:hover{
  background:var(--on-dark);color:var(--dark);
  transform:translate(-2px,-2px);
  box-shadow:2px 2px 0 var(--on-dark);
}
.lay__icon-link:hover svg{transform:scale(1.08)}

/* ============ HERO TELEMETRY ============ */
.hero__telemetry{
  position:absolute;top:clamp(1rem,3vh,2.2rem);left:var(--gutter);z-index:20;
  display:inline-flex;align-items:center;gap:.65rem;
  font-family:var(--f-vcr);font-size:clamp(.68rem,.88vw,.8rem);
  letter-spacing:.08em;color:var(--on-dark);opacity:.75;pointer-events:none;
}
.hero__telemetry .t-dot{color:currentColor;display:inline-block;animation:pulse-dot 2s ease-in-out infinite alternate}
@keyframes pulse-dot{0%{opacity:.35;transform:scale(.85)}100%{opacity:1;transform:scale(1.15)}}
.hero__telemetry .t-sep{opacity:.35}
.hero__telemetry .t-status{opacity:.95;font-weight:700}

/* ============ 03 PROJECTS ============ */
.projects{min-height:100svh;display:grid;place-items:center;background:var(--paper);color:var(--on-dark);border-top:1px solid rgba(255,255,255,.1)}
.projects__inner{width:min(1180px,calc(100% - 2 * var(--gutter)));padding:clamp(4.5rem,10vw,8.5rem) 0}
.projects__intro{max-width:none;margin-bottom:clamp(2rem,5vw,3.5rem)}
.projects__intro-header{display:flex;align-items:flex-end;justify-content:space-between;gap:1.5rem;flex-wrap:wrap;margin-bottom:1rem}
.projects__controls{display:flex;align-items:center;gap:1rem;font-family:var(--f-vcr)}
.projects__page-idx{font-size:clamp(.72rem,1vw,.82rem);letter-spacing:.08em;opacity:.75;white-space:nowrap}
.projects__arrows{display:flex;align-items:center;gap:.4rem}
.projects__arrow{
  width:44px;height:44px;min-width:44px;min-height:44px;
  background:var(--dark);color:var(--on-dark);border:1px solid var(--on-dark);
  font-family:var(--f-vcr);font-size:1.05rem;cursor:pointer;
  display:grid;place-items:center;line-height:1;user-select:none;
  transition:background .2s var(--ease),color .2s var(--ease),transform .15s var(--ease),box-shadow .15s var(--ease);
}
.projects__arrow:not(:disabled):hover{
  background:var(--on-dark);color:var(--dark);
  transform:translate(-2px,-2px);
  box-shadow:2px 2px 0 var(--on-dark);
}
.projects__arrow:disabled{
  opacity:.3;cursor:not-allowed;border-style:dashed;
}
.projects__slider{position:relative;width:100%;overflow:hidden}
.projects__track{display:flex;width:200%;transition:transform .45s cubic-bezier(.16,1,.3,1)}
.projects__track .project-grid{width:50%;flex:0 0 50%}
.eyebrow{font-family:var(--f-vcr);font-size:.78rem;letter-spacing:.12em;opacity:.62;text-transform:uppercase}
.projects h2{font-size:clamp(3rem,8vw,7rem);font-weight:400;letter-spacing:-.07em;line-height:.9;margin:.7rem 0 1.5rem}
.projects__intro p{max-width:36rem;font-size:clamp(1rem,1.8vw,1.25rem);opacity:.72}
.project-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:currentColor;border:1px solid currentColor}
.project-card{--card-ink:var(--on-dark);--card-paper:var(--paper);min-width:0;min-height:380px;padding:clamp(1.4rem,3vw,2.2rem);display:flex;flex-direction:column;background:var(--paper);transition:background .3s var(--ease),color .3s var(--ease),transform .3s var(--ease)}
.project-card:hover,.project-card:focus-within{--card-ink:var(--paper);--card-paper:var(--on-dark);background:var(--card-paper);color:var(--card-ink)}
.project-card__meta{display:flex;align-items:center;justify-content:space-between;gap:.5rem;margin-bottom:.4rem;width:100%}
.project-card__idx{font-family:var(--f-vcr);font-size:.76rem;letter-spacing:.08em;opacity:.6}
.project-badge{font-family:var(--f-vcr);font-size:.65rem;letter-spacing:.06em;padding:.12rem .45rem;border:1px solid currentColor;border-radius:0;opacity:1;white-space:nowrap}
.project-badge--award{color:#fbbf24;border-color:#fbbf24;font-weight:700}
.project-card:hover .project-badge{border-color:currentColor;color:inherit}
.project-card h3{font-size:clamp(1.4rem,2.2vw,2rem);font-weight:400;letter-spacing:-.04em;margin:1.8rem 0 .7rem}
.project-card p{font-size:.92rem;line-height:1.55;opacity:.72}
.project-tags{display:flex;flex-wrap:wrap;gap:.35rem;margin:1.2rem 0}
.project-tag{font-family:var(--f-vcr);font-size:.65rem;letter-spacing:.05em;padding:.15rem .42rem;border:1px solid currentColor;opacity:.55;background:transparent;transition:opacity .2s,border-color .2s}
.project-card:hover .project-tag{opacity:.85}
.project-links{margin-top:auto;padding-top:1.4rem;display:flex;flex-wrap:wrap;gap:.55rem;align-items:center}
/* Paired tokens stay opposite even when a card or the page is inverted. */
.project-link{font-family:var(--f-vcr);font-size:.74rem;letter-spacing:.06em;padding:.65rem .75rem;min-height:44px;border:1px solid var(--card-ink);display:inline-flex;align-items:center;gap:.3rem;background:var(--card-paper);color:var(--card-ink)}
.project-link--primary,.project-link:hover,.project-link:focus-visible{background:var(--card-ink);color:var(--card-paper)}
.project-link--primary:hover,.project-link--primary:focus-visible{background:var(--card-paper);color:var(--card-ink);box-shadow:inset 0 0 0 2px var(--card-ink)}
.project-link:focus-visible{outline-color:var(--card-ink)}

/* ============ 04 SPECS ============ */
.specs{min-height:100svh;display:grid;place-items:center;background:var(--paper);color:var(--on-dark);border-top:1px solid rgba(255,255,255,.1)}
.specs__inner{width:min(1180px,calc(100% - 2 * var(--gutter)));padding:clamp(4.5rem,10vw,8.5rem) 0}
.specs__intro{max-width:680px;margin-bottom:clamp(2.5rem,7vw,4.5rem)}
.specs h2{font-size:clamp(2.8rem,7vw,6rem);font-weight:400;letter-spacing:-.06em;line-height:.95;margin:.7rem 0 1.2rem}
.specs__intro p{max-width:38rem;font-size:clamp(1rem,1.8vw,1.25rem);opacity:.72}
.spec-grid{display:grid;grid-template-columns:repeat(3,1fr);gap:1px;background:currentColor;border:1px solid currentColor}
.spec-card{padding:clamp(1.5rem,3vw,2.4rem);background:var(--paper);display:flex;flex-direction:column;transition:background .3s var(--ease),color .3s var(--ease)}
.spec-card:hover{background:var(--on-dark);color:var(--paper)}
.spec-card__head{margin-bottom:1.6rem;padding-bottom:1rem;border-bottom:1px dashed currentColor}
.spec-card__idx{font-family:var(--f-vcr);font-size:.74rem;letter-spacing:.1em;opacity:.55;display:block;margin-bottom:.4rem}
.spec-card h3{font-size:clamp(1.2rem,1.7vw,1.45rem);font-weight:400;letter-spacing:-.02em;line-height:1.2;margin:0}
.spec-list{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:1.1rem;font-size:.88rem;line-height:1.55;opacity:.82}
.spec-list li{position:relative;padding-left:1.15rem}
.spec-list li::before{content:'>';position:absolute;left:0;top:0;font-family:var(--f-vcr);opacity:.55}
.spec-list strong{font-weight:700;letter-spacing:-.01em}

/* ============ MOTION ============
   Nothing here is a page transition - each piece drifts at its own rate as you
   scroll, so a section reads as a moving composition rather than a flat still. */
.el,.lay{will-change:transform}
.rev{opacity:0;filter:blur(6px)}
.rev.is-shown{opacity:1;filter:none;visibility:visible;
  transition:opacity 1.1s var(--ease),filter 1.1s var(--ease)}
/* cut-outs keep their invert while revealing - a bare `filter:none` here would
   drop the polarity and bring the black boxes straight back */
.el.rev{filter:invert(var(--el-inv)) blur(9px)}
.el.rev.is-shown{filter:invert(var(--el-inv));
  transition:opacity 1.1s var(--ease),filter 1.1s var(--ease)}
.rev.is-shown.d1{transition-delay:.08s}
.rev.is-shown.d2{transition-delay:.16s}

/* ============ REVEAL ============ */
[data-rise]{opacity:0;transform:translateY(18px);filter:blur(6px)}
.is-lit [data-rise]{opacity:1;transform:none;filter:none;transition:opacity .9s var(--ease) .1s,transform 1s var(--ease) .1s,filter .9s var(--ease) .1s}

/* ============ NARROW SCREENS ============
   Carefully centered and tuned for mobile ergonomics.
   Cut-outs use calc(50% - width/2) so offsetLeft is centered without
   matrix translation, keeping motion tracking contours perfectly locked. */
@media (max-width:820px){
  /* Keep standard document scroll intact */
  html{
    overflow-x:clip;
    overflow-y:auto;
  }
  body{
    overflow-x:clip;
    overflow-y:visible;
    max-width:100vw;
    width:100%;
  }
  .keys{display:none}
  .hero,.about,.contact{height:auto;min-height:100svh;place-items:center}
  .stage,.contact .stage{
    position:relative;inset:auto;margin:0;
    width:100vw;height:auto;min-height:100svh;container-type:normal;
    -webkit-mask-image:none;mask-image:none;
  }

  /* Fixed controls placed discreetly in top header area */
  .ctl{
    top:max(1.1rem, env(safe-area-inset-top, 16px));
    right:max(1.1rem, env(safe-area-inset-right, 16px));
    bottom:auto;left:auto;
    transform:none;
    flex-direction:row;
    gap:1rem;
    z-index:99;
  }
  .ctl__b{font-size:.82rem;padding:.2rem 0}

  /* ---- 01 Hero ---- */
  /* Dice centered horizontally via calc to avoid matrix translate offset */
  .el--dice{
    width:min(44vw, 190px);
    left:calc(50% - min(22vw, 95px));
    top:34%;
    transform:translateY(var(--ty,0));
    max-width:220px;
  }
  #diceCanvas.el--dice{
    transition:transform 0.12s cubic-bezier(0.4,0,0.2,1);
  }
  #diceCanvas.el--dice:active{
    transform:translateY(var(--ty,0)) scale(0.96);
  }
  /* Flanking type around the dice: 'law' [DICE] 'x.' */
  .lay--shutter{
    right:calc(50% + min(22vw, 95px) + 0.5rem);
    left:auto;
    top:40%;
    transform:translateY(var(--ty,0));
    font-size:clamp(2.4rem, 11vw, 3.6rem);
    font-style:italic;
    letter-spacing:-0.03em;
    text-align:right;
    white-space:nowrap;
  }
  .lay--kif{
    left:calc(50% + min(22vw, 95px) + 0.5rem);
    right:auto;
    top:40%;
    transform:translateY(var(--ty,0));
    font-size:clamp(2.4rem, 11vw, 3.6rem);
    font-style:italic;
    letter-spacing:-0.03em;
    text-align:left;
    white-space:nowrap;
  }

  .el--star-a{left:auto;right:-18%;top:7%;width:48%;animation-duration:45s}
  .el--star-b{left:-20%;top:68%;width:75%;animation-duration:45s}

  /* ---- 02 Work / Machine ---- */
  .machine{height:100svh;min-height:100svh;position:relative}
  .machine .stage{
    position:absolute;inset:0;margin:0;
    width:100%;height:100%;min-height:100%;
  }
  .machine__stage{position:relative;width:100%;height:100%;flex:1;min-height:0;z-index:2}
  #pspCanvas{position:absolute;inset:0;width:100%;height:100%;display:block;touch-action:pan-y}
  .el--flower{
    width:min(120vw, 560px);
    height:auto;
    left:calc(50% - min(60vw, 280px));
    top:20%;
    transform:translateY(var(--ty,0)) rotate(var(--spin));
  }
  .rail{
    left:50%;bottom:clamp(1.5rem, 5vh, 2.5rem);
    transform:translateX(-50%);
    justify-content:center;
  }

  /* Mobile 3D optimization */
  #diceCanvas,#pspCanvas{
    will-change:transform;
    transform:translateZ(0);
    backface-visibility:hidden;
    -webkit-backface-visibility:hidden;
  }
  .machine__stage canvas{image-rendering:optimizeSpeed}

  /* ---- 01 Hero Telemetry ---- */
  .hero__telemetry{
    top:max(4.6rem, calc(env(safe-area-inset-top, 16px) + 3.2rem));
    left:var(--gutter);
    right:var(--gutter);
    font-size:.62rem;
    gap:.35rem;
    flex-wrap:wrap;
    line-height:1.4;
    max-width:calc(100vw - 2 * var(--gutter));
  }

  /* ---- 03 Projects ---- */
  .projects{min-height:auto;display:block}
  .projects__inner{padding:clamp(4rem, 12vw, 6rem) var(--gutter) clamp(3rem, 8vw, 4.5rem)}
  .projects h2{font-size:clamp(2.6rem, 11vw, 4.5rem);line-height:.95;max-width:10ch}
  .projects__intro{margin-bottom:2.2rem}
  .project-grid{display:flex;flex-direction:column;gap:1.2rem;border:0;background:none}
  .project-card{
    min-height:0;
    margin-bottom:0;
    padding:1.6rem 1.4rem;
    border:1px solid currentColor;
    background:rgba(255,255,255,0.02);
    display:flex;
    flex-direction:column;
  }
  .project-card:hover,.project-card:active{
    background:var(--on-dark);
    color:var(--paper);
    transform:translateY(-2px);
  }
  .project-card h3{margin:1.2rem 0 .5rem;font-size:1.5rem;letter-spacing:-.03em}
  .project-card p{font-size:.92rem;line-height:1.55;opacity:.75}
  .project-tags{margin:1rem 0;gap:.3rem}
  .project-tag{font-size:.62rem;padding:.12rem .38rem}
  .project-links{padding-top:1.2rem;gap:.5rem}
  .project-link{font-size:.72rem;padding:.35rem .7rem}

  /* ---- 04 Specs ---- */
  .specs{min-height:auto;display:block}
  .specs__inner{padding:clamp(3rem, 10vw, 5rem) var(--gutter) clamp(4rem, 12vw, 6rem)}
  .specs h2{font-size:clamp(2.4rem, 10vw, 4rem);line-height:.95;max-width:10ch}
  .specs__intro{margin-bottom:2.2rem}
  .spec-grid{display:flex;flex-direction:column;gap:1.2rem;border:0;background:none}
  .spec-card{
    padding:1.6rem 1.4rem;
    border:1px solid currentColor;
    background:rgba(255,255,255,0.02);
  }
  .spec-card:hover,.spec-card:active{
    background:var(--on-dark);
    color:var(--paper);
    transform:translateY(-2px);
  }
  .spec-card h3{font-size:1.25rem}
  .spec-list{gap:.9rem;font-size:.84rem}

  /* ---- 04 About ---- */
  .lay--abouth{
    left:50%;top:18%;
    transform:translate(-50%,var(--ty,0));
    font-size:clamp(1.3rem, 6vw, 2rem);
    text-align:center;
    width:90%;
    letter-spacing:.08em;
  }
  .lay--bio{
    left:50%;top:30%;
    width:88%;max-width:440px;
    transform:translate(-50%,var(--ty,0));
    margin-left:0;
    font-size:clamp(0.95rem, 3.8vw, 1.15rem);
    line-height:1.65;
    text-align:left;
  }
  .el--cd-a{left:-12%;top:-6%;width:46%;max-width:210px}
  .el--cd-b{left:auto;right:-10%;top:74%;width:44%;max-width:190px}

  /* ---- 05 Contact ---- */
  /* Stacked clean typography above centered interactive cards */
  .lay--contact{
    left:50%;top:16%;
    transform:translate(-50%,var(--ty,0));
    font-size:clamp(2.8rem, 13vw, 4.4rem);
    letter-spacing:-.03em;
    text-align:center;
    white-space:nowrap;
  }
  .lay--me{
    left:50%;top:calc(16% + clamp(2.8rem, 13vw, 4.4rem) * 0.86);
    transform:translate(-50%,var(--ty,0));
    font-size:clamp(2.8rem, 13vw, 4.4rem);
    letter-spacing:-.03em;
    text-align:center;
    white-space:nowrap;
  }
  /* Cards centered without matrix translateX to preserve contour tracking sync */
  .el--cards{
    width:min(58vw, 250px);
    left:calc(50% - min(29vw, 125px));
    top:40%;
    transform:translateY(var(--ty,0));
    max-width:320px;
  }
  .lay--icons{
    left:50%;top:84%;
    transform:translate(-50%,var(--ty,0));
    gap:1.5rem;
  }
  .lay__icon-link{
    width:52px;height:52px;min-width:52px;min-height:52px;
    transition:transform 0.2s cubic-bezier(0.4,0,0.2,1);
  }
  .lay__icon-link:active{transform:scale(0.92)}
}

/* Extra small screens adjustments */
@media (max-width:360px){
  .lay--shutter,.lay--kif{font-size:2.1rem}
  .el--dice{width:140px;left:calc(50% - 70px)}
  .lay--contact,.lay--me{font-size:2.5rem}
}

/* ============ PORTFOLIO COMPOSITION ============ */
.skip-link{position:fixed;top:1rem;left:1rem;z-index:110;padding:.75rem;background:var(--on-dark);color:var(--dark);transform:translateY(-200%)}
.skip-link:focus{transform:none}
.site-nav{position:absolute;inset:0 0 auto;z-index:50;display:flex;align-items:center;gap:clamp(.7rem,3vw,3rem);padding:.6rem var(--gutter);border-bottom:1px solid currentColor;background:var(--dark);color:var(--on-dark);font-family:var(--f-vcr);font-size:clamp(.7rem,1vw,.9rem);text-transform:uppercase}
.site-nav a{display:inline-flex;align-items:center;min-height:44px}
.site-nav a:hover{text-decoration:underline;text-underline-offset:5px}
.site-nav__mark{margin-right:auto;font-family:var(--f);font-weight:700;font-size:1.4rem;letter-spacing:-.08em}

.theme-toggle,
.lang-toggle{display:inline-flex;align-items:center;justify-content:center;min-width:44px;min-height:44px;padding:.5rem .75rem;border:1px solid currentColor;background:transparent;color:currentColor;font-family:var(--f-vcr);font-size:.85rem;cursor:pointer;transition:background .2s var(--ease),color .2s var(--ease)}
.theme-toggle:hover,
.lang-toggle:hover{background:var(--on-dark);color:var(--dark)}
.theme-toggle__icon{font-size:1.2rem;line-height:1}
.lang-toggle__label{font-weight:700}
.hero{height:max(100svh,680px)}
.hero .stage{position:absolute;inset:0;width:100%;height:100%;min-height:0;container-type:size;mask-image:none;-webkit-mask-image:none}
.hero .hero__telemetry{top:5.2rem;right:var(--gutter);flex-wrap:wrap;gap:.5rem;font-size:clamp(.6rem,.85vw,.8rem);opacity:1}
.hero .lay--shutter{left:50%;top:53%;right:auto;transform:translate(-50%,-50%);font-size:min(26cqw,24cqh);font-weight:700;line-height:.82;letter-spacing:-.09em;white-space:nowrap;text-align:center}
.hero__dice-poster{position:absolute;z-index:2;left:41%;top:22%;width:18%;height:auto;filter:grayscale(1) contrast(2) invert(1);mix-blend-mode:screen;opacity:.82;pointer-events:none;object-fit:contain}
.hero #diceCanvas{left:41%;top:22%;width:18%;max-width:none;transform:none}
.hero__caption{position:absolute;right:var(--gutter);bottom:8%;margin:0;font-size:clamp(1.8rem,4vw,4rem);font-weight:700;line-height:.9;letter-spacing:-.06em;text-transform:uppercase}
.archive-label{position:absolute;top:2rem;left:var(--gutter);right:var(--gutter);z-index:6;display:flex;justify-content:space-between;gap:1rem;font-family:var(--f-vcr);font-size:.8rem;font-weight:400}
.archive-label span{font-family:var(--f);text-transform:none}
.projects,.specs{border-top:2px solid currentColor}
.projects__inner,.specs__inner{width:calc(100% - 2 * var(--gutter));max-width:1600px;margin-inline:auto}
.projects__intro,.specs__intro{max-width:none}
.projects h2,.specs h2{font-weight:700;text-transform:uppercase;font-size:clamp(3.6rem,10vw,10rem);line-height:.82;letter-spacing:-.065em}
.eyebrow{opacity:1}
.project-card__meta{flex-wrap:wrap;align-items:flex-start}
.project-card h3{font-weight:700;font-size:clamp(2rem,3.4vw,3.8rem);line-height:1;overflow-wrap:anywhere}
.project-card__idx,.project-tag{opacity:.8}
.project-card p{opacity:1}
.spec-card__head{border-bottom-style:solid}
.spec-card h3{font-weight:700}
.demo-viewer{width:min(1440px,96vw);height:92dvh;max-width:96vw;max-height:92dvh;margin:auto;padding:0;border:2px solid var(--on-dark);background:var(--dark);color:var(--on-dark)}
.demo-viewer[open]{display:flex;flex-direction:column}
.demo-viewer::backdrop{background:rgba(0,0,0,.85)}
.demo-viewer__bar{display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:.75rem 1rem;border-bottom:2px solid currentColor}
.demo-viewer__bar h2{margin:0;font-size:clamp(1.2rem,3vw,2rem);font-weight:700;letter-spacing:-.04em}
.demo-viewer__bar form{flex-shrink:0}
.demo-viewer__close,.demo-viewer__help a{display:inline-flex;align-items:center;justify-content:center;min-height:44px;padding:.5rem .75rem;border:1px solid var(--on-dark);background:var(--on-dark);color:var(--dark);font:inherit;cursor:pointer}
.demo-viewer__close:hover,.demo-viewer__close:focus-visible,.demo-viewer__help a:hover,.demo-viewer__help a:focus-visible{background:var(--dark);color:var(--on-dark);outline-color:var(--on-dark)}
.demo-viewer__help{display:flex;align-items:center;justify-content:space-between;gap:.75rem;padding:.75rem 1rem;font-size:.8rem}
.demo-viewer__help p{margin:0;max-width:65ch}
.demo-viewer__help a{flex-shrink:0}
.demo-viewer iframe{flex:1;min-height:0;width:100%;border:0;border-top:1px solid var(--on-dark);background:#fff;transition:all .3s var(--ease)}
body.has-demo{overflow:hidden}
@media (max-width:820px){
  .site-nav{gap:.75rem;padding-inline:1rem}
  .ctl{top:auto;bottom:max(1rem, env(safe-area-inset-bottom, 14px));right:max(1rem, env(safe-area-inset-right, 14px));transform:none;flex-direction:row;gap:.5rem;padding:0;background:transparent}
  .ctl__b{width:46px;height:46px;min-width:46px;min-height:46px}
  .hero .hero__telemetry{font-size:.6rem;line-height:1.7}
  .hero .lay--shutter{font-size:28cqw;left:50%;top:54%}
  .hero__dice-poster{top:25%;left:37%;width:26%}
  .hero #diceCanvas{top:25%;left:37%;width:26%}
  .hero__caption{font-size:clamp(1.6rem,7vw,3rem);bottom:12%}
  .projects__inner,.specs__inner{padding-inline:0}
  .project-grid,.spec-grid{gap:0;border-top:1px solid currentColor}
  .project-card,.spec-card{border-top:0;background:var(--paper)}
  .project-card:hover,.project-card:active{background:var(--card-paper);color:var(--card-ink);transform:none}
  .demo-viewer{width:100%;height:100dvh;max-width:100%;max-height:100dvh}
  .demo-viewer__bar{padding-top:max(.75rem,env(safe-area-inset-top))}
  .demo-viewer__help{align-items:flex-start;flex-direction:column}
}

/* ============ REDUCED MOTION ============ */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  .cue i::after,.rail.is-swap b{animation:none}
  .el--star-a,.el--star-b,.el--flower{animation:none;transform:translateY(var(--ty,0))}
  .rev{opacity:1;filter:none;visibility:visible;transition:none}
  .el.rev,.el.rev.is-shown{opacity:1;filter:invert(var(--el-inv));transition:none}
  .el,.lay{--ty:0 !important}
  [data-rise]{opacity:1;transform:none;filter:none;transition:none}
  .lay__icon-link,.rail__open,.ctl__b::after{transition:none}
}

/* ============ HERO COLLAGE + SAFARI WINDOW ============ */
.hero__frame{position:absolute;pointer-events:none;z-index:2;border:1px solid currentColor;opacity:.7}
.hero__frame--outer{inset:9% 3.5% 5%;border-width:2px}
.hero__frame--outer i{position:absolute;width:18px;height:18px;border-color:currentColor;border-style:solid}
.hero__frame--outer i:nth-child(1){top:-2px;left:-2px;border-width:3px 0 0 3px}
.hero__frame--outer i:nth-child(2){top:-2px;right:-2px;border-width:3px 3px 0 0}
.hero__frame--outer i:nth-child(3){bottom:-2px;left:-2px;border-width:0 0 3px 3px}
.hero__frame--outer i:nth-child(4){right:-2px;bottom:-2px;border-width:0 3px 3px 0}
.hero__frame--inner{inset:24% 37% 52%;border-style:dashed;opacity:.32}
.hero__collage{position:absolute;z-index:1;margin:0;overflow:hidden;border:1px solid currentColor;filter:grayscale(1) contrast(1.35);mix-blend-mode:screen;opacity:.65;transform:rotate(var(--tilt));}
.hero__collage::after{content:'';position:absolute;inset:0;background:repeating-linear-gradient(0deg,transparent 0 5px,rgba(255,255,255,.18) 6px 7px);mix-blend-mode:screen;pointer-events:none}
.hero__collage img{width:100%;height:100%;object-fit:cover;opacity:.8}
.hero__collage--left{left:4%;top:21%;width:14%;height:18%;--tilt:-7deg}
.hero__collage--right{right:4%;bottom:18%;width:15%;height:20%;--tilt:6deg}
.hero__silhouette{position:absolute;z-index:0;left:40%;top:58%;width:20%;height:28%;object-fit:contain;filter:grayscale(1) contrast(1.7);mix-blend-mode:screen;opacity:.1;transform:rotate(-8deg);pointer-events:none}

.browser-titlebar{display:flex;align-items:center;justify-content:space-between;gap:1rem;padding:.6rem 1rem;border-bottom:2px solid currentColor;background:var(--dark);color:var(--on-dark)}
.browser-tag{font-family:var(--f-vcr);font-size:.76rem;letter-spacing:.08em;opacity:.75;flex-shrink:0}
.browser-titlebar h2{margin:0;flex:1;text-align:center;font-family:var(--f-vcr);font-size:.85rem;letter-spacing:.08em;text-transform:uppercase}
.browser-titlebar form{display:flex;flex-shrink:0}
.demo-viewer__close{width:32px;height:32px;min-height:32px;padding:0;border:1px solid currentColor;border-radius:0;background:transparent;color:inherit;font-family:var(--f-vcr);font-size:.85rem;line-height:1;display:grid;place-items:center;cursor:pointer;transition:background .2s,color .2s}
.demo-viewer__close:hover,.demo-viewer__close:focus-visible{background:var(--on-dark);color:var(--dark);outline:none}
.demo-viewer__help{background:var(--dark);border-bottom:1px solid currentColor}
.demo-viewer__help a{background:var(--on-dark);color:var(--dark)}

/* Keep the original hero silhouette: lowercase top mark, split wordmark,
   and no editorial border or telemetry strip competing with the artwork. */
.site-nav__mark{text-transform:none}
.hero__frame,.hero__telemetry{display:none}
.hero .lay--shutter,.hero .lay--kif{top:43%;right:auto;transform:none;font-size:4.6cqw;font-style:italic;font-weight:400;line-height:1;letter-spacing:-.02em;white-space:nowrap}
.hero .lay--shutter{left:26.5%;text-align:left}
.hero .lay--kif{left:62.5%;right:auto}
.hero__dice-poster{left:37.35%;top:23.5%;width:25.3%}
.hero #diceCanvas{left:37.35%;top:23.5%;width:25.3%}

@media (max-width:820px){
  .hero .lay--shutter,.hero .lay--kif{font-size:clamp(2.4rem,11vw,3.6rem);top:40%}
  .hero .lay--shutter{right:calc(50% + min(22vw,95px) + .5rem);left:auto;text-align:right}
  .hero .lay--kif{left:calc(50% + min(22vw,95px) + .5rem);right:auto}
  .hero__dice-poster,.hero #diceCanvas{top:34%;left:calc(50% - min(22vw,95px));width:min(44vw,190px)}
  .hero__frame--outer{inset:12% 4% 5%}
  .hero__frame--inner{inset:25% 29% 52%}
  .hero__collage--left{left:5%;top:24%;width:22%;height:15%}
  .hero__collage--right{right:5%;bottom:20%;width:23%;height:16%}
  .hero__silhouette{left:35%;top:59%;width:30%;height:25%}
}

/* Sharp rectangular mobile-proportioned window for mobile demos */
.demo-viewer.is-mobile {
  width: min(420px, 94vw);
  max-width: 420px;
  height: min(844px, 90dvh);
}
.demo-frame.is-mobile {
  width: 100%;
  height: 100%;
  max-width: 100%;
  max-height: 100%;
  margin: 0;
  border: 0;
  border-top: 1px solid var(--on-dark);
  border-radius: 0;
  box-shadow: none;
  background: #000;
}
body.is-invert .demo-frame.is-mobile {
  border-color: var(--on-dark);
  box-shadow: none;
}
