/* =========================================================
   FONTS — self-hosted variable woff2, character-subset only.

   These are NOT axis-instanced. An earlier version pinned Archivo's
   width axis at 125 to save 30 kB, which broke the display font in
   Safari: instancing stripped every named instance from fvar, left the
   STAT table still advertising a wdth axis the font no longer had, and
   set usWidthClass to 7 (Expanded) while the @font-face descriptor
   still said normal. CoreText rejects a variable font on any of those
   three, and falls back silently — so the wide display face vanished in
   Safari while Chrome, which is far more forgiving, rendered it fine.

   Every axis, every named instance and the STAT table are therefore
   left exactly as the foundry shipped them, and width is selected in
   CSS via font-stretch, the way it was before self-hosting. Only the
   character set is subset: latin plus the punctuation this page uses.

   If you ever subset these again: subset glyphs, do not instance axes.
   ========================================================= */
@font-face{
  font-family:"Archivo";
  src:url("../fonts/archivo-var.woff2") format("woff2");
  font-weight:100 900;font-stretch:62% 125%;font-style:normal;font-display:swap;
}
@font-face{
  font-family:"Inter Tight";
  src:url("../fonts/inter-tight-var.woff2") format("woff2");
  font-weight:100 900;font-style:normal;font-display:swap;
}
@font-face{
  font-family:"JetBrains Mono";
  src:url("../fonts/jetbrains-mono-var.woff2") format("woff2");
  font-weight:100 800;font-style:normal;font-display:swap;
}

/* =========================================================
   SYNTHSOLDIER
   Monochrome system. Light is the only accent.
   Signature: the 45° chamfer, taken from the logo's letterforms.
   ========================================================= */

:root{
  --void:#000;
  --ink:#08090B;
  --surface:#0F1115;
  --hairline:#22252C;
  --muted:#767C88;
  --bone:#E9EAED;
  --white:#FFF;

  --display:"Archivo",system-ui,sans-serif;
  --body:"Inter Tight",system-ui,sans-serif;
  --mono:"JetBrains Mono",ui-monospace,monospace;

  --pad:clamp(20px,5vw,72px);
  --gap:clamp(28px,4vw,56px);
  --sect:clamp(88px,13vh,168px);
  --cham:18px;
  --ease:cubic-bezier(.16,1,.3,1);
}

*,*::before,*::after{box-sizing:border-box}
html{scroll-behavior:smooth;scroll-padding-top:calc(clamp(72px,9vh,96px) + env(safe-area-inset-top,0px));-webkit-text-size-adjust:100%}
body{
  margin:0;background:var(--ink);color:var(--bone);
  font-family:var(--body);font-size:clamp(1rem,.95rem + .25vw,1.125rem);
  line-height:1.62;-webkit-font-smoothing:antialiased;
}
img,svg,iframe,picture{display:block;max-width:100%}
.tile__media picture,.menu__media picture{display:block;width:100%;height:100%}
img{height:auto}
ul{list-style:none;margin:0;padding:0}
h1,h2,h3{font-size:inherit;font-weight:inherit;margin:0}
a{color:inherit;text-decoration:none}
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer}
:focus-visible{outline:2px solid var(--white);outline-offset:4px;border-radius:1px}

/* film grain — keeps flat black from reading as a default dark theme */
body::after{
  content:"";position:fixed;inset:0;z-index:9998;pointer-events:none;
  opacity:.035;mix-blend-mode:overlay;
  background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

.vh{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;
  clip:rect(0 0 0 0);white-space:nowrap;border:0}

.skip{position:fixed;left:-9999px;top:12px;z-index:10000;background:var(--white);color:#000;padding:10px 16px}
.skip:focus{left:12px}

.wrap{width:min(1360px,100%);margin-inline:auto;padding-inline:var(--pad)}
.section{padding-block:var(--sect);position:relative}

/* ---------- type roles ---------- */
.display{
  font-family:var(--display);font-weight:800;font-stretch:125%;
  text-transform:uppercase;letter-spacing:-.03em;line-height:.94;margin:0;
}
.mono{
  font-family:var(--mono);font-weight:500;font-size:.6875rem;
  text-transform:uppercase;letter-spacing:.14em;line-height:1.4;
}
.mono i{font-style:normal;opacity:.45;padding-inline:.35em}
.label{color:var(--muted);margin:0 0 clamp(28px,4vw,52px)}

.frame{
  position:relative;overflow:hidden;background:var(--surface);
  clip-path:polygon(0 0,calc(100% - var(--cham)) 0,100% var(--cham),100% 100%,var(--cham) 100%,0 calc(100% - var(--cham)));
}

.section__head{display:flex;justify-content:space-between;align-items:baseline;gap:24px;flex-wrap:wrap}
.more{color:var(--muted);transition:color .3s var(--ease)}
.more i{display:inline-block;transition:transform .3s var(--ease);padding-left:.5em}
.more:hover{color:var(--white)}
.more:hover i{transform:translateX(5px)}

/* ---------- nav ---------- */
/*
   The page ships viewport-fit=cover, so the layout viewport IS the whole
   screen: the status bar sits on top of the page, not above it. Two separate
   things follow, and every previous attempt at the iOS gap fixed at most one
   of them.

   1. POSITION. The logo/menu row has to clear the status bar, so the top
      padding carries env(safe-area-inset-top). Where a UA insets the viewport
      itself instead (older iOS without cover, every desktop browser), that
      value is 0 and the calc collapses to the plain 18px — the one rule is
      correct in both regimes, so there is no second inset to undo.

   2. SURFACE. Painting the bar from the header's own top edge is not enough.
      iOS Safari does not keep a fixed element pinned to the visual viewport
      while its toolbars collapse; for the length of that transition the
      header is composited up to a status bar's height too low, and whatever
      scrolls through the strip it vacates is fully legible. No amount of
      moving the header can cover a strip the header is not in.

      So the bar's surface lives on ::before, bled generously off the top of
      the screen. At rest that bleed is off-canvas and costs nothing; mid
      transition it is the thing holding the strip opaque.

   The surface stays frosted. Note that 72% black plus a 14px blur is not
   fully opaque, so display type passing under the bar is still faintly
   legible — that is the intended look, and it is a different thing from the
   sharp, undimmed text the gap used to expose. If it ever reads as too much
   show-through, raise the alpha on .nav.is-stuck::before; nothing else needs
   to move.

   The blur is switched by the .is-stuck class rather than faded with opacity,
   which is how this rule worked before. WebKit applies backdrop-filter as a
   backdrop effect, so an element left at opacity:0 can still blur what is
   behind it — that would smear the hero while the bar is meant to be
   invisible.
*/
.nav{
  position:fixed;top:0;left:0;right:0;z-index:100;
  display:flex;justify-content:space-between;align-items:center;
  padding-top:calc(18px + env(safe-area-inset-top,0px));
  padding-bottom:18px;
  padding-left:max(var(--pad),env(safe-area-inset-left,0px));
  padding-right:max(var(--pad),env(safe-area-inset-right,0px));
}
.nav::before{
  content:"";position:absolute;z-index:0;pointer-events:none;
  left:0;right:0;bottom:0;
  top:calc(-200px - env(safe-area-inset-top,0px));
  background:transparent;
  border-bottom:1px solid transparent;
  transition:background .4s var(--ease),border-color .4s var(--ease);
}
.nav.is-stuck::before{
  background:rgba(8,9,11,.72);
  backdrop-filter:blur(14px);-webkit-backdrop-filter:blur(14px);
  border-bottom-color:var(--hairline);
}
/* The surface is a positioned pseudo-element, so it paints over anything in
   the header that is not itself positioned. Lifting the two children above it
   is what keeps them visible — and it is done this way, rather than dropping
   the surface to z-index:-1, because a negative layer has to composite
   against the page behind its own stacking context, which is exactly where
   backdrop-filter is least reliable in WebKit. */
.nav__logo,.nav__toggle{position:relative;z-index:1}
.nav__logo{display:block}
.nav__logo .logo{width:clamp(96px,10vw,132px);height:auto;fill:var(--bone);transition:fill .3s var(--ease)}
.nav__logo:hover .logo{fill:var(--white)}
.nav__toggle{display:flex;align-items:center;gap:12px;padding:8px 0;color:var(--bone)}
.nav__bars{display:block;width:26px;height:10px;position:relative}
.nav__bars i{position:absolute;left:0;width:100%;height:1px;background:currentColor;transition:transform .4s var(--ease)}
.nav__bars i:first-child{top:0}
.nav__bars i:last-child{bottom:0}
.is-open .nav__bars i:first-child{transform:translateY(4.5px) rotate(45deg)}
.is-open .nav__bars i:last-child{transform:translateY(-4.5px) rotate(-45deg)}

/* ---------- menu overlay ---------- */
/* The two hard shadows are the same bleed the nav uses, done without a
   pseudo-element because this one is a grid container. inset:0 is only the
   viewport iOS Safari believes in mid toolbar-collapse, and a full-screen
   overlay that comes up a status bar short shows the page through the top
   edge. Solid --ink, no blur, no spread: seamless with the panel itself. */
.menu{position:fixed;inset:0;z-index:99;background:var(--ink);display:grid;grid-template-columns:1fr;
  box-shadow:0 -200px 0 var(--ink),0 200px 0 var(--ink);
  padding:env(safe-area-inset-top,0px) env(safe-area-inset-right,0px) env(safe-area-inset-bottom,0px) env(safe-area-inset-left,0px);
  opacity:0;visibility:hidden;transition:opacity .5s var(--ease),visibility .5s}
.menu.is-open{opacity:1;visibility:visible}
.menu__nav{display:flex;flex-direction:column;justify-content:center;gap:clamp(32px,6vh,64px);
  padding:96px var(--pad) 48px;min-height:0;overflow-y:auto}
.menu__list li{display:flex;align-items:baseline;gap:18px;border-bottom:1px solid var(--hairline)}
.menu__idx{color:var(--muted);flex:none}
.menu__list a{
  font-family:var(--display);font-weight:800;font-stretch:125%;text-transform:uppercase;
  letter-spacing:-.02em;font-size:clamp(1.75rem,5vw,3rem);line-height:1;
  padding:.34em 0;flex:1;color:var(--muted);
  transition:transform .35s var(--ease),color .35s var(--ease);
}
.menu__list a:hover{color:var(--white);transform:translateX(10px)}
.menu__foot{display:flex;flex-direction:column;gap:14px;margin-top:clamp(36px,8vh,88px)}
.menu__foot .label{margin-bottom:0}
.menu__mail{font-family:var(--display);font-weight:800;font-stretch:125%;text-transform:uppercase;
  letter-spacing:-.02em;font-size:clamp(1rem,1.9vw,1.375rem);line-height:1.1;
  border-bottom:1px solid var(--hairline);padding-bottom:8px;
  align-self:flex-start;transition:border-color .3s}
.menu__mail:hover{border-color:var(--white)}
.menu__media{display:none}

.social{display:flex;flex-wrap:wrap;gap:6px 0;color:var(--muted)}
.social li+li::before{content:"·";padding-inline:.75em;opacity:.5}
.social a{transition:color .3s var(--ease)}
.social a:hover{color:var(--white)}

/* ---------- hero ---------- */
/* 87svh, so the marquee band below shows above the fold — a moving slice of it
   is a stronger 'there is more' signal than any cue */
.hero{position:relative;min-height:87svh;
  padding:0 max(var(--pad),env(safe-area-inset-right,0px)) clamp(40px,7vh,80px)
            max(var(--pad),env(safe-area-inset-left,0px))}
.hero__bg{position:absolute;inset:0;overflow:hidden;background:var(--void)}
.hero__still{position:absolute;inset:0}
/* film grain. width/height are explicit because <canvas> is a replaced element —
   inset:0 alone leaves it at its intrinsic size. screen blend, so only the bright
   specks register and the photograph passes through otherwise untouched. */
.hero__grain{position:absolute;inset:0;width:100%;height:100%;opacity:0;
  mix-blend-mode:screen;image-rendering:pixelated;pointer-events:none;
  transition:opacity 1.4s var(--ease)}
.hero__grain.is-live{opacity:.20}
.hero__still img{width:100%;height:100%;object-fit:cover;object-position:50% 100%}

/* the photograph keeps its own slow drift underneath the shader */
@media (prefers-reduced-motion:no-preference){
  .hero__still img{animation:kenburns 34s ease-in-out infinite alternate;will-change:transform}
  .hero__still{animation:breathe 11s ease-in-out infinite alternate}
}
@keyframes breathe{from{filter:contrast(1) brightness(1)}to{filter:contrast(1.16) brightness(1.08)}}
@keyframes kenburns{from{transform:scale(1.03)}to{transform:scale(1.11) translate3d(-1.4%,-1%,0)}}

.hero__scrim{position:absolute;inset:0;
  background:
    radial-gradient(120% 80% at 50% 0%,rgba(0,0,0,0) 30%,rgba(6,7,10,.5) 100%),
    linear-gradient(180deg,rgba(6,7,10,.5) 0%,rgba(6,7,10,.12) 32%,rgba(6,7,10,.88) 100%);
}
.hero__scroll{position:absolute;left:50%;transform:translateX(-50%);bottom:clamp(40px,7vh,80px);z-index:2;
  display:flex;flex-direction:column;align-items:center;gap:12px;color:var(--muted);
  transition:color .3s var(--ease)}
.hero__scroll span{writing-mode:vertical-rl}
/* names the sections rather than pointing vaguely. Four links wrap badly on a
   phone, so below 760px the plain cue takes over instead. */
.hero__contents{display:none;position:absolute;left:50%;transform:translateX(-50%);
  bottom:calc(clamp(30px,5.5vh,60px) + env(safe-area-inset-bottom,0px));z-index:3;align-items:center;justify-content:center;flex-wrap:wrap}
.hero__contents a{color:var(--muted);padding:11px 16px;transition:color .3s var(--ease)}
.hero__contents a:hover,.hero__contents a:focus-visible{color:var(--white)}
.hero__contents a + a{border-left:1px solid var(--hairline)}
@media (min-width:760px){
  .hero__contents{display:flex}
  .hero__scroll{display:none}
}
.hero__scroll:hover{color:var(--white)}
.hero__scroll i{display:block;width:1px;height:44px;background:linear-gradient(180deg,currentColor,transparent);
  animation:drop 2.2s var(--ease) infinite}
@keyframes drop{0%{transform:scaleY(0);transform-origin:top}45%{transform:scaleY(1);transform-origin:top}
  55%{transform:scaleY(1);transform-origin:bottom}100%{transform:scaleY(0);transform-origin:bottom}}

/* ---------- marquee band ---------- */
.band{border-block:1px solid var(--hairline);overflow:hidden;padding-block:clamp(14px,2vw,22px);background:var(--ink)}
.hero + .band{position:relative;z-index:2;margin-top:-10px}
.band__track{display:flex;align-items:center;width:max-content;animation:slide 42s linear infinite;will-change:transform}
.band__track span{
  font-family:var(--display);font-weight:900;font-stretch:125%;text-transform:uppercase;
  font-size:clamp(2.5rem,7vw,6rem);line-height:1;letter-spacing:-.04em;color:var(--surface);
  -webkit-text-stroke:1px var(--hairline);
}
.band__cut{width:clamp(28px,4vw,64px);height:clamp(28px,4vw,64px);flex:none;
  background:var(--hairline);clip-path:polygon(50% 0,100% 50%,50% 100%,0 50%)}
.band:hover .band__track{animation-play-state:paused}
@keyframes slide{to{transform:translateX(-50%)}}

/* ---------- profile (bio + player, one section) ---------- */
.profile{background:var(--surface);border-block:1px solid var(--hairline)}
.profile__grid{display:grid;gap:var(--gap);grid-template-columns:1fr;align-items:start}
.profile__thesis{font-size:clamp(1.375rem,3vw,2.25rem);margin-bottom:.7em}
.profile__copy p{color:var(--muted);max-width:58ch;margin:0 0 1.15em;
  font-size:clamp(.875rem,.85rem + .12vw,.9375rem);line-height:1.68}
.profile__copy strong{color:var(--bone);font-weight:500}
.profile__copy a{color:var(--bone);border-bottom:1px solid rgba(233,234,237,.3);
  transition:border-color .3s var(--ease),color .3s var(--ease)}
.profile__copy a:hover{color:var(--white);border-bottom-color:var(--white)}
.stats{display:flex;flex-wrap:wrap;gap:0;margin:clamp(28px,4vw,40px) 0;border-top:1px solid var(--hairline)}
.stats > div{flex:1 1 108px;padding:14px 0;border-bottom:1px solid var(--hairline)}
.stats dt{color:var(--muted);margin-bottom:5px}
.stats dd{margin:0;color:var(--bone)}

/* only meaningful once the player sits in its own column beside the copy —
   stacked on mobile it reads as a second section label with nothing under it */
.profile__now{display:none;text-align:right}
.player__frame{--cham:20px;border:1px solid var(--hairline);background:#0b0d10;min-height:520px}
.player__frame iframe{width:100%;height:520px;border:0}

/* ---------- releases ---------- */
.releases{background:var(--ink)}
.releases__foot{margin:clamp(22px,3vw,34px) 0 0;text-align:right}
.grid{display:grid;gap:1px;background:var(--hairline);border:1px solid var(--hairline);grid-template-columns:1fr 1fr}
.grid > li{background:var(--ink)}
.tile{display:block;position:relative}
.tile__media{--cham:12px;aspect-ratio:1;display:block}
.tile__media img{width:100%;height:100%;object-fit:cover;filter:grayscale(.55) contrast(1.04);
  transition:transform .7s var(--ease),filter .5s var(--ease)}
.tile__go{position:absolute;right:10px;bottom:10px;width:34px;height:34px;display:grid;place-items:center;
  background:var(--white);color:#000;font-style:normal;font-size:.9rem;opacity:0;transform:translate(6px,6px);
  transition:opacity .35s var(--ease),transform .35s var(--ease);
  clip-path:polygon(0 0,100% 0,100% 100%,10px 100%,0 calc(100% - 10px))}
.tile__flag{position:absolute;left:0;top:0;background:var(--white);color:#000;padding:5px 10px 4px;font-size:.625rem;
  clip-path:polygon(0 0,100% 0,calc(100% - 9px) 100%,0 100%)}
.tile:hover .tile__media img{transform:scale(1.045);filter:grayscale(0) contrast(1.06)}
.tile:hover .tile__go{opacity:1;transform:translate(0,0)}
.tile__meta{display:flex;justify-content:space-between;align-items:baseline;gap:12px;padding:14px 14px 18px}
.tile__name{font-size:.9375rem;line-height:1.3}
.tile__year{color:var(--muted);flex:none}

/* ---------- video ---------- */
.video{background:var(--surface);border-block:1px solid var(--hairline)}
.video__grid{display:grid;gap:var(--gap);grid-template-columns:1fr}
.clip{margin:0}
.clip__btn{--cham:16px;display:block;width:100%;padding:0;aspect-ratio:16/9;position:relative}
.clip__btn img{width:100%;height:100%;object-fit:cover;filter:grayscale(.7) contrast(1.05);
  transition:filter .5s var(--ease),transform .8s var(--ease)}
.clip__btn:hover img{filter:grayscale(0);transform:scale(1.03)}
.clip__play{position:absolute;inset:0;margin:auto;width:74px;height:74px;
  background:rgba(255,255,255,.1);backdrop-filter:blur(6px);border:1px solid rgba(255,255,255,.55);
  clip-path:polygon(0 0,calc(100% - 14px) 0,100% 14px,100% 100%,14px 100%,0 calc(100% - 14px));
  transition:background .35s var(--ease)}
.clip__play::before{content:"";position:absolute;inset:0;margin:auto;width:0;height:0;
  border-left:15px solid var(--white);border-block:10px solid transparent;transform:translateX(3px)}
.clip__btn:hover .clip__play{background:rgba(255,255,255,.24)}
.clip figcaption{display:flex;flex-direction:column;gap:6px;padding-top:16px}
.clip__title{font-size:1.125rem}
.clip__meta{color:var(--muted)}

/* ---------- bookings ---------- */
.bookings{background:var(--ink)}
.bookings__note{color:var(--muted)}
.bookings__mail{display:block;font-size:clamp(1.25rem,4.2vw,3.25rem);
  letter-spacing:-.02em;word-break:break-word;transition:opacity .3s var(--ease)}
.bookings__mail:hover{opacity:.6}
.bookings__actions{display:flex;flex-wrap:wrap;align-items:center;gap:18px 28px;margin-top:clamp(28px,4vw,44px)}
.btn{border:1px solid var(--hairline);padding:13px 22px;color:var(--bone);
  clip-path:polygon(0 0,calc(100% - 11px) 0,100% 11px,100% 100%,11px 100%,0 calc(100% - 11px));
  transition:background .3s var(--ease),color .3s var(--ease),border-color .3s var(--ease)}
.btn:hover{background:var(--white);color:#000;border-color:var(--white)}

/* ---------- footer ---------- */
.foot{border-top:1px solid var(--hairline);background:var(--ink)}
.band--foot{border:0}
.band--foot .band__track{animation-duration:58s;animation-direction:reverse}
.band--foot .band__track span{color:transparent;-webkit-text-stroke:1px var(--hairline)}
.foot__inner{display:flex;flex-direction:column;gap:22px;
  padding-block:clamp(28px,4vw,44px) calc(clamp(28px,4vw,44px) + env(safe-area-inset-bottom,0px))}
.foot__legal{margin:0;color:var(--muted)}

/* ---------- reveal ---------- */
.reveal{opacity:0;transform:translateY(22px);transition:opacity .8s var(--ease),transform .8s var(--ease)}
.reveal.is-in{opacity:1;transform:none}

/* =========================================================
   BREAKPOINTS
   ========================================================= */
@media (min-width:700px){
  .grid{grid-template-columns:repeat(3,1fr)}
  .video__grid{grid-template-columns:1fr 1fr}
}
@media (min-width:900px){
  .profile__grid{grid-template-columns:minmax(0,1fr) minmax(0,1.05fr);column-gap:clamp(40px,6vw,96px)}
  .profile__now{display:block}
  .menu{grid-template-columns:1.15fr .85fr}
  .menu__nav{padding-inline:var(--pad) clamp(40px,5vw,80px)}
  .menu__media{display:block;position:relative;overflow:hidden;background:var(--void)}
  .menu__media img{width:100%;height:100%;object-fit:cover}
}
@media (min-width:1100px){
  .grid{grid-template-columns:repeat(4,1fr)}
}

/* =========================================================
   REDUCED MOTION — fully static, fully usable
   ========================================================= */
@media (prefers-reduced-motion:reduce){
  html{scroll-behavior:auto}
  *,*::before,*::after{animation:none!important;transition-duration:.01ms!important}
  .reveal{opacity:1;transform:none}
  .hero__still,
  .hero__still img{animation:none}

  /* The marquee is a duplicated string translated to -50%. Frozen at its start it
     sits flush left, so the first word is whole and the band reads as a deliberate
     static strip rather than an animation that has died mid-scroll. */
  .band__track{animation:none;transform:none}
  .band{overflow:hidden}

  /* Grain is texture, not motion — it is not what anyone means by reduce motion.
     grain.js paints a single frame and stops, so the hero keeps its tooth. */
  .hero__grain.is-live{opacity:.20}
}
