:root{
  --bg:#ffffff;
  --ink:#111111;
  /* Hover pink. The brighter magenta reads better but only measures
     3.5:1 on white; this clears 4.5:1 and still reads as hot pink. */
  --pink:#e0006e;
  --mono:"Chivo Mono", ui-monospace, "SFMono-Regular", Menlo, monospace;
  /* Chivo Mono at 500 — the Medium cut. Declared explicitly so the
     fallback monospace matches weight before the webfont arrives. */
  --mono-weight:500;
  /* The system interface font — whatever the OS provides, not a bundled face. */
  --ui:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", sans-serif;
  /* One radius for every piece of media on the site. */
  --radius:14px;
  --pad:clamp(18px,3.5vw,34px);
  --col-gap:clamp(26px,5vw,80px);
}

*{ box-sizing:border-box; }
html,body{ margin:0; padding:0; }
html{ background:var(--bg); }

body{
  display:flex; flex-direction:column;
  min-height:100vh;
  min-height:100svh;
  min-height:100dvh;
  padding:var(--pad);
  background:var(--bg); color:var(--ink);
  font-family:var(--mono);
  font-weight:var(--mono-weight);
  font-size:12px;
  line-height:1.65;
  letter-spacing:.045em;
  text-transform:uppercase;
  -webkit-font-smoothing:antialiased;
}

a{ color:inherit; text-decoration:underline; text-underline-offset:3px; text-decoration-thickness:1px; }
a:hover, a:focus-visible{ color:var(--pink); text-decoration-color:var(--pink); outline:none; }

p{ margin:0; }

main{
  /* No flex:1 — it existed to push the old footer to the bottom, and
     with the footer gone it only stretched main and shoved the section
     below it (work grid, collage, deck) down the page. */
  display:grid; grid-template-columns:1fr 1fr; gap:var(--col-gap);
  align-content:start;
}
/* Homepage: the intro copy lives on the hero, so main carries only
   the link rows and does not need two columns. */
.main--rows{ grid-template-columns:1fr; padding-top:clamp(20px,4vh,40px); }
.main--rows .col{ max-width:78ch; }

.col{ min-width:0; }
.col p + p{ margin-top:1.65em; }

/* Result figures — same size and weight as body text, held back in tone.
   The number does the work; it should not shout. */
.fig{ opacity:.45; }

/* No nameplate — the opening sentence says the name itself. */
.head{
  display:flex; align-items:center; justify-content:space-between; gap:14px;
  margin:0 0 clamp(28px,5vh,48px);
}
/* Nav: the system interface font, no bar. Ink-coloured so it reads on
   the white pages and on the light glass of the hero alike, and it now
   takes the same pink hover as every other link — the grey bar was the
   only reason it could not. */
.nav{
  flex:1 1 auto;
  display:flex; justify-content:flex-end; align-items:center;
  gap:clamp(16px,2vw,26px);
  margin:0;
  font-family:var(--ui);
  font-size:13px; letter-spacing:-.01em; text-transform:none;
}
.nav a{ text-decoration:none; }
.nav a:hover, .nav a:focus-visible{ color:var(--pink); outline:none; }
.nav [aria-current]{ opacity:.4; }

/* On the homepage the header floats over the hero so the first screen
   stays a full viewport instead of being pushed down by it. */
.head--over{
  position:absolute; top:0; left:0; right:0; z-index:3;
  margin:0; padding:var(--pad);
}

/* ── Work index ───────────────────────────────────────────────────
   4:3 cards rather than the 16:9 a film studio would use — portrait
   and square source material survives that crop, and the shape does
   not pretend this is a reel. */
.worklist{
  margin-top:clamp(32px,6vh,64px);
  display:grid;
  grid-template-columns:repeat(auto-fit,minmax(300px,1fr));
  gap:clamp(20px,3vw,40px);
  max-width:1100px;
}
.workcard{ margin:0; }
.workcard a{ text-decoration:none; display:block; }
.workcard a:hover, .workcard a:focus-visible{ color:inherit; }
.workcard__img{
  display:block; width:100%; aspect-ratio:4/3;
  object-fit:cover; background:#f2f2f2;
}
.workcard__todo{
  display:flex; align-items:center; justify-content:center;
  width:100%; aspect-ratio:4/3; background:#f2f2f2; opacity:.5;
}
/* A card can carry a looping clip; the still sits underneath as the
   fallback for anything that will not play it. */
.workcard__frame{ display:block; position:relative; aspect-ratio:4/3; overflow:hidden;
  background:#f2f2f2; border-radius:var(--radius); }
.workcard__frame .workcard__img, .workcard__vid{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover; display:block;
}
.workcard__vid{ z-index:1; }

.workcard__meta{ margin-top:10px; }
.workcard a:hover .workcard__title{ color:var(--pink); }
.workcard__sub{ opacity:.45; }

.mark{ display:block; width:34px; height:auto; margin:0; flex:0 0 auto; }
/* Inline SVG sits on a text baseline and carries descender space below
   it, which made the mark's box taller than the circle and left the
   sphere riding high against the nav bar. */
.mark svg{ display:block; width:100%; height:auto; }
.mark a{ display:block; text-decoration:none; }
.mark a:hover, .mark a:focus-visible{ opacity:.6; }

/* ── Case study pages ─────────────────────────────────────────────
   Narrative body drops out of uppercase. All-caps at 12px is right
   for two sentences and a link list; it fights the reader across
   three paragraphs. Caps stays on titles, labels and nav so the
   page still reads as part of the same site.
   To revert: delete the text-transform and letter-spacing below. */
.prose{ text-transform:none; letter-spacing:.02em; margin-top:1.65em; }
.prose p{ max-width:56ch; }
.prose p + p{ margin-top:1.65em; }

.label{ opacity:.45; }

/* The case-study right column starts level with the first line of prose,
   not with the title sitting above it. Two line-boxes: the title's own
   line, plus the prose block's top margin. Expressed in line-height units
   so it survives a change to font-size. */
.col--offset{ margin-top:calc(1.65em * 2); }

/* ── Deck ─────────────────────────────────────────────────────────
   Slides sit as black plates on the white page — no inverted band,
   no frame, no chrome. The page stays what it is and the work
   supplies all the contrast it needs. */
.deck{ margin-top:clamp(48px,10vh,96px); }
.deck__inner{ max-width:940px; }
/* The video row needs room for four 325px tiles side by side. */
.deck__inner--wide{ max-width:none; }

/* Native <details> — works with no JavaScript, is keyboard operable
   and screen-reader correct for free. Kept instant rather than
   animated: nothing else on this site transitions except links. */
.deck summary{
  cursor:pointer;
  display:inline-block;
  list-style:none;
  text-decoration:underline;
  text-underline-offset:3px;
  text-decoration-thickness:1px;
}
.deck summary::-webkit-details-marker{ display:none; }
.deck summary:hover, .deck summary:focus-visible{
  color:var(--pink); text-decoration-color:var(--pink); outline:none;
}
.deck .sign::after{ content:"+"; }
.deck details[open] .sign::after{ content:"\2212"; }
.deck details[open] summary{ margin-bottom:clamp(28px,5vh,48px); }

.deck__pdf{ margin-top:clamp(28px,5vh,48px); }

/* ── Hero ─────────────────────────────────────────────────────────
   A first screen, not a gate: no timer, nothing blocks, scrolling
   works from the first frame. The video supplies what frosted glass
   needs to actually read as glass — over a white page there is
   nothing to blur. */
.hero{
  position:relative;
  margin:calc(var(--pad) * -1) calc(var(--pad) * -1) 0;
  min-height:100vh; min-height:100svh; min-height:100dvh;
  display:flex; align-items:center; justify-content:center;
  overflow:hidden;
}
/* The still frame sits underneath as a background, so a video that is
   slow, blocked by autoplay policy, or missing never leaves a blank
   screen — there is always something for the glass to work on. */
.hero__media{
  position:absolute; inset:0;
  background-size:cover; background-position:center;
}
.hero__media video{ width:100%; height:100%; object-fit:cover; display:block; }

/* Stand-in until there is a video file — approximates heavily
   defocused footage so the glass can be judged now. */
.hero__standin{
  width:100%; height:100%;
  background:
    radial-gradient(60% 50% at 25% 20%, #d8dade 0%, transparent 60%),
    radial-gradient(50% 60% at 80% 35%, #c9ccd2 0%, transparent 65%),
    radial-gradient(70% 60% at 50% 90%, #9fa3ab 0%, transparent 70%),
    linear-gradient(160deg, #b9bcc2 0%, #8f939b 100%);
}

.hero__glass{
  position:absolute; inset:0;
  background:rgba(244,244,246,.42);
  -webkit-backdrop-filter:blur(46px) saturate(1.35);
  backdrop-filter:blur(46px) saturate(1.35);
}
@supports not ((backdrop-filter:blur(1px)) or (-webkit-backdrop-filter:blur(1px))){
  .hero__glass{ background:rgba(240,240,242,.9); }
}

/* Whole hero fades up on load; the centred block drifts a little
   behind it so the two arrivals are not simultaneous. */
@keyframes heroIn{ from{ opacity:0 } to{ opacity:1 } }
@keyframes heroUp{ from{ opacity:0; transform:translateY(10px) } to{ opacity:1; transform:none } }
.hero{ animation:heroIn .9s ease both; }
.hero__inner{ animation:heroUp 1s .25s ease both; }

/* The footage dissolves into the page instead of being cut off at a
   hard line. The glass clears as you scroll, so without this the video
   is at its most vivid exactly where it meets the white — the worst
   possible place for a straight edge. */
.hero__fade{
  position:absolute; left:0; right:0; bottom:0; height:30%;
  z-index:1; pointer-events:none;
  background:linear-gradient(to bottom, rgba(255,255,255,0) 0%, rgba(255,255,255,.75) 62%, #fff 100%);
}

/* Scroll hint, bottom of the first screen. Fades out with everything
   else once you start moving. */
.hero__hint{
  position:absolute; left:0; right:0; bottom:var(--pad);
  z-index:2; text-align:center; opacity:.45; margin:0;
}

.hero__inner{
  position:relative; z-index:2;
  max-width:44ch;
  padding:var(--pad);
  text-align:center;
}
.hero__inner .mark{ margin:0 auto clamp(20px,4vh,34px); }
.hero__copy + .hero__copy{ margin-top:1.65em; }

/* Static first frame for anyone who asked for less motion. */
@media (prefers-reduced-motion:reduce){
  .hero__media video{ display:none; }
  .hero, .hero__inner{ animation:none; }
}

/* ── Grid ─────────────────────────────────────────────────────────
   Three across with a 2px gutter: an Instagram profile grid. The work
   was made for that surface, so it is shown on it. Square crops are
   not a compromise here — they are how these appeared in profile. */
.grid{
  margin-top:clamp(28px,5vh,48px);
  display:grid;
  grid-template-columns:repeat(3,1fr);
  gap:2px;
}
.grid img{
  display:block; width:100%;
  aspect-ratio:1;
  object-fit:cover;
  background:#f2f2f2;
}

/* Embeds. loading="lazy" plus a closed <details> means YouTube's
   player is usually never fetched at all unless someone opens the
   section — a facade without writing any JavaScript for one. */
.embed{ margin:0; aspect-ratio:16/9; width:100%; background:#f2f2f2;
  overflow:hidden; border-radius:var(--radius); }
.embed iframe{ width:100%; height:100%; border:0; display:block; }
.embed figcaption{ opacity:.45; margin-bottom:10px; }
.embed + .embed{ margin-top:clamp(32px,6vh,64px); }

/* TikTok's iframe carries its own caption, audio credit and buttons,
   so it needs far more height than the 9:16 video inside it. 325x740
   matches what their own embed reserves. */
/* One row, never a 2x2 block. Tiles hold 325px — TikTok's embed gets
   cramped below that — so on a narrow screen the row scrolls sideways
   instead of wrapping into pairs. */
.tiktoks{
  margin-top:clamp(32px,6vh,64px);
  display:flex;
  gap:clamp(16px,2vw,28px);
  overflow-x:auto;
  padding-bottom:10px;
  scroll-snap-type:x proximity;
}
.tiktok{ margin:0; flex:0 0 325px; scroll-snap-align:start; }
.tiktok iframe{ width:100%; height:740px; border:0; display:block; background:#f2f2f2; }
.tiktok figcaption{ opacity:.45; margin-bottom:10px; }
.tiktok__todo{
  display:flex; align-items:center; justify-content:center;
  width:100%; height:740px;
  background:#f2f2f2; text-decoration:none; opacity:.5;
}

.slide{ margin:0; }
.slide + .slide{ margin-top:clamp(32px,6vh,64px); }
.slide figcaption{ opacity:.45; margin-bottom:10px; }
.slide img{ display:block; width:100%; height:auto; border-radius:var(--radius); }

footer{
  margin-top:auto;
  display:flex; justify-content:space-between; align-items:flex-end;
  gap:var(--col-gap); margin-top:clamp(48px,12vh,120px);
}
footer .right{ text-align:right; }

@media (max-width:820px){
  main{ grid-template-columns:1fr; gap:clamp(30px,6vh,48px); }
  /* Single column — nothing to align to, so drop the offset. */
  .col--offset{ margin-top:0; }
  footer{ flex-direction:column; align-items:flex-start; gap:14px; margin-top:clamp(40px,8vh,64px); }
  footer .right{ text-align:left; }
}

/* ── Reel ─────────────────────────────────────────────────────────
   Four tiles sized to fit the row, so the page only ever scrolls the
   normal way. Two across on a phone. Clips run continuously; the
   still sits underneath purely as a fallback if a video cannot play.
   Each tile links straight to the post. */
.reel{
  margin-top:clamp(28px,5vh,48px);
  display:grid; grid-template-columns:repeat(4,1fr);
  gap:clamp(12px,1.6vw,22px); max-width:1100px;
}
@media (max-width:820px){ .reel{ grid-template-columns:repeat(2,1fr); } }
.reel figure{ margin:0; }
.reel figcaption{ margin-bottom:8px; }
.reel .frame{
  display:block; position:relative;
  aspect-ratio:9/16; background:#f2f2f2; overflow:hidden;
  border-radius:var(--radius);
  text-decoration:none;
}
/* The global link hover inverts colours — wrong over an image. */
.reel .frame:hover, .reel .frame:focus-visible{ color:inherit; outline:none; }
.reel img, .reel video{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; display:block;
}
.reel video{ z-index:1; transition:opacity .25s ease; }
.reel .frame:hover video{ opacity:.88; }

/* ── Collage ──────────────────────────────────────────────────────
   Twelve-column bed. Each piece keeps its native aspect ratio and is
   given a span by what it is, not at random — a series title leads,
   the numbered entries follow it narrower. Nothing is cropped, so
   tall pieces take narrow spans and wide ones take broad spans;
   dense flow backfills what that leaves. */
.collage{
  margin-top:clamp(28px,5vh,48px);
  /* No width cap: the section fills the page. Column count climbs with
     the viewport instead, so wide screens get more columns rather than
     a narrow block of work with dead space beside it. */
  display:flex;
  gap:clamp(10px,1.4vw,20px);
  align-items:flex-start;
}
.collage__col{ flex:1 1 0; min-width:0; display:flex; flex-direction:column; gap:clamp(10px,1.4vw,20px); }
.collage figure{ margin:0; }
.collage .piece{ display:block; position:relative; width:100%; background:#f0f0f0;
  overflow:hidden; border-radius:var(--radius); }
.collage img, .collage video{
  position:absolute; inset:0; width:100%; height:100%;
  object-fit:cover; display:block;
}
.collage video{ z-index:1; }
.collage figcaption{ margin-top:8px; opacity:.45; }


