/* ==========================================================================
   Tokens — change the whole look from here.
   ========================================================================== */
:root {
  --bg: #000;
  --fg: #fff;
  --muted: rgb(255 255 255 / 0.55);
  --line: rgb(255 255 255 / 0.28);
  --gold: #e9dd88;                                 /* project categories */
  --bar-line: 2px solid #fff;                      /* header + footer bar rules */
  --grain-opacity: 0.22;                           /* hero film grain, 0 = off */
  --scanline-opacity: 0.35;                        /* hero scanlines, 0 = off */

  --font-ui: "Inter", "Helvetica Neue", Arial, sans-serif;
  --font-display: "Helvetica Neue", Helvetica, Arial, sans-serif;

  --text-xs: 0.75rem;                              /* 12px labels */
  --text-header: clamp(0.8125rem, 1.15vw, 1.125rem); /* top bar, 13–18px */
  --text-display: clamp(2rem, 5.4vw, 5.25rem);     /* project titles */

  --gutter: clamp(1rem, 1.6vw, 1.75rem);
  --header-h: 4.5rem;                              /* updated live by main.js */
  --footer-h: 3rem;                                /* updated live by main.js */
  --ease: cubic-bezier(0.2, 0.7, 0.2, 1);
}

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

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; background: var(--bg); }

body {
  margin: 0;
  padding-bottom: var(--footer-h); /* room for the fixed footer bar */
  background: var(--bg);
  color: var(--fg);
  font: 400 var(--text-xs) / 1.35 var(--font-ui);
  letter-spacing: 0.01em;
  text-transform: uppercase;
  -webkit-font-smoothing: antialiased;
}

/* The header is filled in by main.js at the end of the page. Until then, keep
   the rest of the page hidden so it never shows up before the header does
   (which reads as a blink when moving between pages). Failsafe: show it
   anyway after 2s. */
.site-header:empty ~ * { visibility: hidden; animation: show-anyway 0s 2s forwards; }
@keyframes show-anyway { to { visibility: visible; } }

/* Desktop: highlighted (selected) text turns Polymarket blue with white type,
   like the header links on hover. */
@media (min-width: 761px) {
  ::selection { background: #1652f0; color: #fff; }
}

/* Oversized pixel-hand cursor from the original site (hotspot = fingertip). */
html { cursor: url("assets/cursor/hand.png") 23 2, auto; }
a, button, [role="button"] { cursor: url("assets/cursor/hand-hover.png") 23 2, pointer; }

a { color: inherit; text-decoration: none; }
img, video { display: block; max-width: 100%; }
ul, ol { list-style: none; margin: 0; padding: 0; }
h1, h2, h3, p { margin: 0; font: inherit; }

.u-link { text-decoration: underline; text-underline-offset: 0.2em; text-decoration-thickness: 1px; }
.u-link:hover { color: var(--muted); }

/* Desktop header links (name, "Polymarket", Info, Awards): Polymarket-blue
   highlight with white type on hover. The padding is cancelled by the
   margin so nothing shifts. */
.site-header__brand-link {
  padding: 0 0.15em;
  margin: 0 -0.15em;
  transition: background-color 0.2s, color 0.2s;
}
@media (min-width: 761px) {
  .site-header .site-header__brand-link:hover { background: #1652f0; color: #fff; }
}

/* Header name + description. Desktop: only the name links home. Phones: the
   name, emoji and description are one link home, with the name and
   description underlined (never the emoji). */
.site-header__id--mobile { display: none; }
.site-header__home { transition: color 0.2s; }
.site-header__home:hover { color: var(--muted); }
.site-header__name,
.site-header__role--mobile {
  text-decoration: underline;
  text-underline-offset: 0.2em;
  text-decoration-thickness: 1px;
}

.label { color: var(--muted); margin-bottom: 1.25rem; }

/* ==========================================================================
   Header
   ========================================================================== */
.site-header {
  position: sticky;
  top: 0;
  z-index: 10;
  display: grid;
  /* "Currently at" starts at the page's halfway line (the Info page's Clients
     column lines up with it): column 1 + gap + left padding = 50%. */
  grid-template-columns: calc(50% - var(--gutter)) 1fr auto;
  gap: var(--gutter);
  align-items: start;
  padding: 1.25rem var(--gutter) 1rem;
  background: var(--bg);
  border-bottom: var(--bar-line);
  font-size: var(--text-header);
  line-height: 1.25;
}

.site-header__role { display: block; line-height: 1.05; letter-spacing: -0.02em; }
.site-header__role--mobile { display: none; line-height: 1.2; letter-spacing: -0.02em; } /* a bit more air now that it is underlined */
.site-header__currently { line-height: 1.05; }

.site-header__nav { display: flex; gap: 1.25rem; }
.site-header__nav a[aria-current="page"] { color: var(--muted); }

/* ==========================================================================
   Hero — fills the viewport under the header at every size.
   object-fit: cover crops instead of stretching, so it never goes wonky.
   ========================================================================== */
.hero {
  position: relative;
  height: calc(var(--screen-h, 100svh) - var(--header-h) - var(--footer-h));
  min-height: 20rem;
  overflow: hidden;
  background: var(--bg); /* white in light mode, black in dark, until the video shows */
  opacity: var(--hero-fade, 1); /* set by main.js as you scroll */
}

/* Hide iOS's big play button that appears before an autoplaying video starts. */
.hero__video::-webkit-media-controls-start-playback-button,
.hero__video::-webkit-media-controls { display: none !important; -webkit-appearance: none; }

/* Home page-load intro, desktop and phones (main.js adds .is-loading-in): the home video
   (with its scanlines) fades up from the page background over 1.25s as soon
   as the page loads, while the header's three blocks fade up left to right
   (0.1s, 0.25s, 0.4s). */
.is-loading-in .site-header > :nth-child(1) { animation: intro-fade-up 0.6s ease-out 0.1s both; }
.is-loading-in .site-header > :nth-child(2) { animation: intro-fade-up 0.6s ease-out 0.25s both; }
.is-loading-in .site-header > :nth-child(3) { animation: intro-fade-up 0.6s ease-out 0.4s both; }
.is-loading-in .hero__video { animation: intro-fade 1.25s ease-out both; }
.is-loading-in .hero::before { animation: intro-scanlines 1.25s ease-out both; }
@keyframes intro-fade {
  from { opacity: 0; }
  to   { opacity: 1; }
}
@keyframes intro-scanlines {
  from { opacity: 0; }
  to   { opacity: var(--scanline-opacity); }
}
@keyframes intro-fade-up {
  from { opacity: 0; translate: 0 0.5rem; }
  to   { opacity: 1; translate: 0 0; }
}

.hero__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}
/* Phones: the opening walking shot plays 15% closer (main.js sets the class
   for that shot only). */
.hero__video.is-zoomed { scale: 1.15; }

/* Scanlines: thin dark horizontal lines every 3px. */
.c-item.has-screen-fx::before,
.about__photo::before,
.hero::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background: repeating-linear-gradient(to bottom, #000 0 1px, transparent 1px 3px);
  opacity: var(--scanline-opacity);
}

/* Film grain: a tiny tile of noise, jittered a few times a second. */
.c-item.has-screen-fx::after,
.about__photo::after,
.hero::after {
  content: "";
  z-index: 1;
  position: absolute;
  inset: -50%;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='220' height='220'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3CfeColorMatrix values='0.33 0.33 0.33 0 0 0.33 0.33 0.33 0 0 0.33 0.33 0.33 0 0 0 0 0 0 1'/%3E%3CfeComponentTransfer%3E%3CfeFuncR type='linear' slope='3' intercept='-1'/%3E%3CfeFuncG type='linear' slope='3' intercept='-1'/%3E%3CfeFuncB type='linear' slope='3' intercept='-1'/%3E%3C/feComponentTransfer%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  background-size: 220px;
  opacity: var(--grain-opacity);
  mix-blend-mode: overlay;
  animation: grain 0.9s steps(6) infinite;
}

@keyframes grain {
  0%   { transform: translate(0, 0); }
  20%  { transform: translate(-7%, 4%); }
  40%  { transform: translate(5%, -6%); }
  60%  { transform: translate(-4%, -3%); }
  80%  { transform: translate(6%, 5%); }
  100% { transform: translate(0, 0); }
}

.hero__scroll {
  z-index: 2;
  position: absolute;
  left: 50%;
  bottom: 1.5rem;
  translate: -50% 0;
  display: grid;
  justify-items: center;
  gap: 0.5rem;
  font-size: 0.625rem;
  letter-spacing: 0.08em;
  mix-blend-mode: difference;
}

/* On the home page the cue's line runs right down to the footer bar's rule. */
.hero > .hero__scroll { bottom: 0; }

.hero__scroll::after {
  content: "";
  width: 1px;
  height: 2rem;
  background: currentColor;
  transform-origin: top;
  animation: scroll-line 2s var(--ease) infinite;
}

@keyframes scroll-line {
  0%   { transform: scaleY(0); }
  50%  { transform: scaleY(1); transform-origin: top; }
  51%  { transform-origin: bottom; }
  100% { transform: scaleY(0); transform-origin: bottom; }
}

/* ==========================================================================
   Work list
   ========================================================================== */
.work { padding: 0 var(--gutter); }

.project { position: relative; border-bottom: 1px solid var(--line); }

.project__link {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(9rem, 16rem);
  gap: var(--gutter);
  padding: clamp(1.5rem, 3vw, 3rem) 0;
  transition: opacity 0.4s var(--ease);
}

.project__title {
  max-width: 19ch;
  font: 400 var(--text-display) / 0.95 var(--font-display);
  letter-spacing: -0.025em;
  text-transform: none;
  text-wrap: balance;
  text-decoration: underline 1px transparent;
  text-underline-offset: 0.1em;
}

.project__link:hover .project__title,
.project__link:focus-visible .project__title { text-decoration-color: currentColor; }

.project__meta { display: grid; align-content: start; gap: 1rem; }
.project__year { text-decoration: underline; text-underline-offset: 0.2em; }

/* Desktop: the client sits just above the headline, the category gets its
   own gold column, and the year + badges stay on the right. (Phones put
   client and category on one line instead; see the small-screens block.) */
.project__link {
  grid-template-columns: minmax(0, 1fr) minmax(7rem, 12rem) minmax(9rem, 16rem);
  grid-template-rows: auto 1fr;
  grid-template-areas: "client category meta" "title category meta";
  row-gap: 1.1rem;
}
.project__labels { display: contents; } /* same size as the year */
.project__client { grid-area: client; justify-self: start; text-decoration: underline; text-underline-offset: 0.2em; }
.project__category { grid-area: category; color: var(--gold); }
.project__title { grid-area: title; }
.project__meta { grid-area: meta; }

.badges { display: flex; flex-wrap: wrap; align-items: center; gap: 0.6rem 0.75rem; }
.badge-group { display: flex; align-items: center; gap: 0.75rem; }

.badges img {
  width: auto;
  height: auto;
  max-height: 2.5rem;
  max-width: 5rem;
}

/* Dim the rest of the list while one project is hovered. */
@media (hover: hover) {
  .work__list:has(.project__link:hover) .project__link:not(:hover) { opacity: 0.3; }
}

/* ==========================================================================
   Case-study pages (drawn by canvas.js from data/<page>.json)
   Every number in the data is in "canvas units": 1024 across on desktop,
   320 on phones. --u converts one unit to real pixels for the current width.
   ========================================================================== */
.plain { display: grid; gap: 0.35em; }

.canvas {
  container-type: inline-size;
  /* Readymag measured from the top of the window, under the header. */
  margin-top: calc(-1 * var(--header-h));
  overflow: hidden;
  text-transform: none;
  letter-spacing: normal;
  font-family: Roboto, Arial, sans-serif;
  line-height: normal;
}

.canvas__stage {
  --u: calc(100cqw / var(--cw));
  position: relative;
  height: calc(var(--ch) * var(--u) + var(--after-shift, 0px));
}

.c-item {
  position: absolute;
  left: calc(var(--x) * var(--u));
  top: calc(var(--y) * var(--u) + var(--shift, 0px));
  width: calc(var(--w) * var(--u));
  height: calc(var(--h) * var(--u));
  rotate: var(--angle, 0deg);
  opacity: var(--opacity, 1);
}

/* Text */
.c-text { white-space: pre-wrap; overflow-wrap: break-word; }
.c-text, .c-text * { font-size: calc(var(--fs, 16) * var(--u)); }
.c-text p, .c-text li { margin: 0; }
.c-text [style*="--lh"] { line-height: calc(var(--lh) * var(--u)); }
.c-text [style*="--ls"] { letter-spacing: calc(var(--ls) * var(--u)); }
.c-text [style*="--ti"] { text-indent: calc(var(--ti) * var(--u)); }
.c-text [style*="--pt"] { padding-top: calc(var(--pt) * var(--u)); }
.c-text [style*="--pb"] { padding-bottom: calc(var(--pb) * var(--u)); }
.c-text a { color: inherit; transition: opacity 0.3s var(--ease); }
.c-text a:hover { opacity: 0.6; }

/* Images */
.c-image { display: block; overflow: hidden; border-radius: calc(var(--radius, 0) * var(--u)); }
.c-image img { width: 100%; height: 100%; object-fit: cover; }
.c-image.is-cropped img { position: absolute; max-width: none; object-fit: fill; }

/* Video */
.c-video { overflow: hidden; background: #000; }
.c-video iframe, .c-video video { width: 100%; height: 100%; border: 0; object-fit: cover; }
/* Vimeo film with a cover image: a play button over the still. */
.c-video--poster { cursor: pointer; }
.c-video--poster img { width: 100%; height: 100%; object-fit: cover; }
.c-video__play {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 4.5rem;
  aspect-ratio: 1;
  translate: -50% -50%;
  border: 0;
  border-radius: 50%;
  background: rgb(0 0 0 / 0.45);
  cursor: inherit;
  transition: background-color 0.25s var(--ease), scale 0.25s var(--ease);
}
.c-video__play::after {
  content: "";
  position: absolute;
  left: 54%;
  top: 50%;
  translate: -50% -50%;
  border-style: solid;
  border-width: 0.8rem 0 0.8rem 1.35rem;
  border-color: transparent transparent transparent #fff;
}
.c-video--poster:hover .c-video__play { background: #1652f0; scale: 1.06; }
/* Full screen: show the whole frame (letterboxed), never crop it to the screen. */
.c-video video:fullscreen { object-fit: contain; }
.c-video video:-webkit-full-screen { object-fit: contain; }

/* Lines and shapes */
.c-line::before {
  content: "";
  position: absolute;
  inset: 50% 0 auto;
  height: max(1px, calc(var(--weight) * var(--u)));
  translate: 0 -50%;
  background: var(--color);
}

.c-shape {
  background: var(--fill, transparent);
  border: calc(var(--border, 0) * var(--u)) solid var(--color, transparent);
  border-radius: calc(var(--radius, 0) * var(--u));
}
.c-shape.is-ellipse { border-radius: 50%; }

/* ==========================================================================
   Info page, desktop (deets/index.html). Phones use the canvas layout.
   First screen: About / Clients / CV between the header and the footer bar,
   with the Scroll cue running down to the footer. Second screen: the photo,
   exactly one screen tall, with the home video's scanlines and grain.
   ========================================================================== */
.about { display: none; }
@media (min-width: 601px) {
  .about { display: block; }
  .about ~ .canvas { display: none; }
}

.about__intro {
  position: relative;
  min-height: calc(var(--screen-h, 100svh) - var(--header-h) - var(--footer-h));
  display: grid;
  /* Clients starts at the halfway line (like "Currently at" in the header),
     CV at about five-sixths across. */
  grid-template-columns: calc(50% - var(--gutter)) calc(33.5% - var(--gutter) / 3) 1fr;
  column-gap: var(--gutter);
  /* Headings start 23.5% of the way down the first screen (the Awards page
     uses this same grid, so it starts at the same height). */
  align-content: start;
  padding: calc((var(--screen-h, 100svh) - var(--header-h) - var(--footer-h)) * 0.235) var(--gutter) 4.5rem;
  font: 400 clamp(0.9375rem, 1.17vw, 1.375rem) / 1.15 Roboto, var(--font-ui);
}
.about__title {
  margin: 0 0 1.6em;
  font: inherit;
  font-size: 1.8em;
  line-height: normal; /* box top = text top, so Awards can line up with it */
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.12em;
}
.about__text { max-width: 28em; text-transform: none; }
.about__text p { margin: 0 0 1.15em; }
.about__text p:last-child { margin-bottom: 0; }
.about__text a { color: inherit; text-decoration: none; }
.about__text a:hover { text-decoration: underline; }
.about__list + .about__list { margin-top: 1.15em; }
.about__list strong { font-weight: 700; }
.about__list em { font-style: italic; }

/* Awards page, desktop: the same three columns and start height as Info,
   one year per column, three years per row. */
.about__intro.awards { row-gap: 4.5em; padding-bottom: 6rem; }
.awards__list { text-transform: none; }
.awards__list p { margin: 0; }
.awards__gap { height: 1.15em; }
.awards__list a { color: inherit; }
.awards__list a:hover { opacity: 0.6; }

/* The cue: in the page's text colour, its line running down to the footer bar. */
.about__intro > .hero__scroll {
  bottom: 0;
  color: var(--fg);
  mix-blend-mode: normal;
  font-family: var(--font-ui);
  font-size: 0.625rem;
}
.about__photo {
  position: relative;
  overflow: hidden;
  height: calc(var(--screen-h, 100svh) - var(--header-h) - var(--footer-h));
  scroll-margin-top: var(--header-h); /* "Scroll" lands it right under the header */
}
.about__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

/* The home page's animated "Scroll" cue, at the bottom of a project's first screen. */
.page-scroll {
  position: absolute;
  inset: 0 0 auto;
  height: calc(var(--screen-h, 100svh) - var(--footer-h));
  z-index: 1;
  pointer-events: none;
}
.page-scroll .hero__scroll {
  pointer-events: auto;
  font-family: var(--font-ui);
  text-transform: uppercase;
}

/* Intro screen (pages with "introScreen": true): the intro is centred on an
   all-black first screen, everything after it starts below the fold, and the
   Scroll cue's line runs down to the footer bar like on the home page. */
.has-intro-screen .is-intro { --shift: var(--intro-shift, 0px); }
.has-intro-screen .is-after { --shift: var(--after-shift, 0px); }
.has-intro-screen .page-scroll .hero__scroll { bottom: 0; }
/* Without an intro screen the cue would land on top of the copy: leave it out. */
.canvas:not(.has-intro-screen) .page-scroll { display: none; }

/* Smooth reveal: each item fades and rises as it scrolls into view. */
.canvas.is-animated .c-item {
  opacity: 0;
  translate: 0 1.25rem;
  transition:
    opacity 1.1s cubic-bezier(0.16, 1, 0.3, 1),
    translate 1.1s cubic-bezier(0.16, 1, 0.3, 1);
}
.canvas.is-animated .c-item.is-visible { opacity: var(--opacity, 1); translate: 0 0; }

/* Pages with "introFade" (data file): the opening block fades up 0–100% on its
   own, a set time after the page loads (canvas.js sets the timings). */
.canvas.has-intro-fade .c-item.is-intro {
  transition: none;
  animation: intro-block-fade var(--intro-fade-duration, 0.9s) cubic-bezier(0.16, 1, 0.3, 1)
    var(--intro-fade-wait, var(--intro-fade-delay, 0.5s)) both;
}
/* A soft reveal: each piece rises a little and comes into focus as it fades in. */
@keyframes intro-block-fade {
  from { opacity: 0; translate: 0 1.5rem; filter: blur(8px); }
  to   { opacity: var(--opacity, 1); translate: 0 0; filter: blur(0); }
}

/* ==========================================================================
   Footer
   ========================================================================== */
.site-footer { padding: 0 var(--gutter); }

.site-footer__grid {
  display: grid;
  gap: var(--gutter);
  padding: clamp(3rem, 8vw, 7rem) 0;
}

/* The closing thank-you, in the headline typeface at half the headline size (no underline). */
.site-footer__note,
.site-footer__note--mobile {
  max-width: 19ch;
  font: 400 calc(var(--text-display) * 0.5) / 1 var(--font-display);
  letter-spacing: -0.025em;
  text-transform: none;
  text-wrap: balance;
}
.site-footer__note--mobile { display: none; }

/* Phones: an up arrow at the end of the home page that scrolls back to the top. */
.site-footer__top { display: none; }

/* Always visible, pinned to the bottom of the screen on every page. */
.site-footer__bar {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: 10;
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 0.85rem var(--gutter) calc(0.85rem + env(safe-area-inset-bottom));
  background: var(--bg);
  border-top: var(--bar-line);
  font-size: var(--text-header);
  letter-spacing: -0.01em;
}

/* A solid strip below the bar, so nothing (video or projects) ever shows
   underneath it, even through a see-through phone toolbar. */
.site-footer__bar::after {
  content: "";
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  height: 100vh;
  background: var(--bg);
}

.social { display: flex; gap: 1rem; }
.social svg { width: 1.125rem; height: 1.125rem; fill: currentColor; }
.social a:hover { color: var(--muted); }

/* ==========================================================================
   Reveal on scroll (only once JS has marked the page ready)
   ========================================================================== */
.js .reveal { opacity: 0; transform: translateY(1.5rem); }
.js .reveal.is-visible {
  opacity: 1;
  transform: none;
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}

/* The header's crystal ball: a translucent swirl turns inside the ball (an
   emoji can't be animated itself, so this sits over its glass). */
.site-header__orb { position: relative; display: inline-block; text-decoration: none; }
.site-header__orb::after {
  content: "";
  position: absolute;
  left: 52%;
  top: 0.5%;
  width: 83%;
  aspect-ratio: 1;
  translate: -50% 0;
  border-radius: 50%;
  background: conic-gradient(
    rgb(255 255 255 / 0.75), rgb(255 255 255 / 0) 22%,
    rgb(255 120 230 / 0.55) 45%, rgb(255 255 255 / 0) 68%,
    rgb(150 200 255 / 0.6) 85%, rgb(255 255 255 / 0.75));
  mix-blend-mode: overlay;
  animation: orb-swirl 3s linear infinite;
  pointer-events: none;
}
@keyframes orb-swirl { to { rotate: 1turn; } }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .js .reveal { opacity: 1; transform: none; }
  .hero__scroll::after, .hero::after, .about__photo::after, .c-item.has-screen-fx::after, .site-header__orb::after { animation: none; }
}

/* ==========================================================================
   Light mode (desktop and phones): toggled by the pill switch in the
   header, which sets <html data-theme="light"> (or "dark"). Light is the default.
   ========================================================================== */
.site-header__nav { align-items: center; }

.theme-toggle {
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  line-height: 0;
}

/* Pill-shaped track with a sun on the left and a moon on the right. The knob
   sits over the moon in dark mode and slides over to the sun in light mode. */
.theme-toggle__track {
  --pill-h: 1.35em;
  --knob: calc(var(--pill-h) - 6px);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: space-between;
  width: 2.7em;
  height: var(--pill-h);
  padding: 0 0.3em;
  border: 1.5px solid currentColor;
  border-radius: 999px;
}

.theme-toggle__icon {
  width: 0.72em;
  height: 0.72em;
  fill: currentColor;
  stroke: currentColor;
  stroke-width: 1.4;
  stroke-linecap: round;
}

.theme-toggle__knob {
  position: absolute;
  top: 50%;
  left: calc(100% - var(--knob) - 2px);
  width: var(--knob);
  height: var(--knob);
  border-radius: 50%;
  background: currentColor;
  translate: 0 -50%;
  transition: left 0.3s var(--ease);
}

:root[data-theme="light"] .theme-toggle__knob { left: 2px; }
.theme-toggle:hover .theme-toggle__track { opacity: 0.7; }

/* The home video's Scroll cue always sits on footage, so it stays white. */
.hero > .hero__scroll { color: #fff; }

:root[data-theme="light"] {
  --bg: #fff;
  --fg: #000;
  --muted: rgb(0 0 0 / 0.55);
  --line: rgb(0 0 0 / 0.28);
  --bar-line: 2px solid #000;
  --gold: #857414; /* deeper gold so phone category labels read on white */
}

/* Case-study pages: white page, white type and rules become black.
   Coloured accents and black type on images are left as they are. */
:root[data-theme="light"] .canvas { background: var(--bg) !important; }
:root[data-theme="light"] .c-text.is-white,
:root[data-theme="light"] .c-text [style*="color:#fff"] { color: #000 !important; }
/* White-on-transparent award/press logos (tagged by canvas.js) flip to black. */
:root[data-theme="light"] .c-image.is-white-logo img { filter: invert(1); }
:root[data-theme="light"] .c-line { --color: #000 !important; }
:root[data-theme="light"] .page-scroll .hero__scroll { color: #000; mix-blend-mode: normal; }
/* Phones: the cue's line runs over the top of the first image, so it inverts
   whatever is behind it to stay readable in both themes. */
@media (max-width: 600px) {
  .page-scroll .hero__scroll,
  :root[data-theme="light"] .page-scroll .hero__scroll { color: #fff; mix-blend-mode: difference; }
}

/* ==========================================================================
   Small screens
   ========================================================================== */
@media (max-width: 760px) {
  .site-header { grid-template-columns: 1fr auto; }
  .site-header__currently { display: none; }
  .site-header__nav .hide-mobile { display: none; }

  /* On phones the Scroll cue's line runs down to the footer bar's rule on every page. */
  .hero__scroll { bottom: 0; }

  /* Phones swap the two-line title for the shorter mobile one. */
  .site-header__role { display: none; }
  .site-header__role--mobile { display: block; }
  .site-header__id--desktop { display: none; }
  .site-header__id--mobile { display: inline-block; }

  .project__link { grid-template-columns: 1fr; grid-template-rows: none; grid-template-areas: none; gap: 1rem; }
  .project__title { max-width: none; }
  .project__title, .project__meta { grid-area: auto; }
  .project__category { text-align: right; }

  /* Client (left) and category (right) replace the year above each headline. */
  .project__labels {
    display: flex;
    justify-content: space-between;
    gap: 1rem;
    font-size: var(--text-header);
  }
  .project__year { display: none; }

  .info { grid-template-columns: 1fr; }
  .info__col { padding: 0 0 2.5rem; border-left: 0; }

  /* Footer on phones: just the thank-you text. */
  /* Same proportions as desktop: about one headline-height of space above it
     and two below (counting the footer bar). */
  .site-footer__grid {
    grid-template-columns: 1fr auto;
    align-items: end;
    padding: calc(var(--text-display) * 1.13 + 1.5rem) 0 6rem; /* extra room so the browser's own toolbar never covers the text or arrow */
  }
  .site-footer__grid > * { display: none; }
  .site-footer__grid > .site-footer__note--mobile { display: block; }
  .site-footer__grid > .site-footer__top {
    display: grid;
    place-items: center;
    width: 2.75rem;
    height: 2.75rem;
    margin: 0 -0.6rem -0.6rem 0; /* the arrow lines up with the text edge; the tap area is bigger */
    padding: 0;
    border: 0;
    background: none;
    color: inherit;
    cursor: pointer;
  }
  .site-footer__top svg {
    width: 1.6rem;
    height: 1.6rem;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.5;
  }
}

/* Phones held sideways: browsers don't let a website lock the screen's
   rotation, so cover the page and ask for portrait instead. Full-screen
   video sits above this, so films can still be watched sideways. */
@media (orientation: landscape) and (max-height: 500px) and (pointer: coarse) {
  html { overflow: hidden; }
  html::after {
    content: "Please turn your phone upright";
    position: fixed;
    inset: 0;
    z-index: 2147483647;
    display: grid;
    place-items: center;
    padding: 1rem;
    background: var(--bg);
    color: var(--fg);
    font: 500 0.875rem / 1.3 var(--font-ui);
    letter-spacing: 0.01em;
    text-align: center;
    text-transform: uppercase;
  }
}

/* ==========================================================================
   Liquid glass (light and dark mode)
   Frosted, floating header and footer bars with bright edge highlights,
   glass cards for each project, and a soft colour wash behind everything
   for the glass to pick up.
   ========================================================================== */
:root[data-theme] {
  --glass-blur: blur(22px) saturate(185%);
  --float: 0.625rem;                            /* gap around the floating bars */
}
:root[data-theme="light"] {
  --bg: #f3f5fa;
  --line: rgb(0 0 0 / 0.12);
  --glass: rgb(255 255 255 / 0.42);
  --glass-header: rgb(255 255 255 / 0.58);      /* a touch whiter so black type reads over the video */
  --glass-strong: rgb(255 255 255 / 0.62);
  --glass-sheen: linear-gradient(160deg, rgb(255 255 255 / 0.55), rgb(255 255 255 / 0.12) 45%, rgb(255 255 255 / 0.3));
  --glass-sheen-hover: linear-gradient(160deg, rgb(255 255 255 / 0.75), rgb(255 255 255 / 0.25) 45%, rgb(255 255 255 / 0.45));
  --glass-edge: rgb(255 255 255 / 0.85);
  --glass-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.9),       /* top highlight */
    inset 0 -1px 0 rgb(255 255 255 / 0.35),     /* bottom rim */
    inset 0 0 0 1px rgb(255 255 255 / 0.25),    /* inner rim */
    0 10px 30px -8px rgb(20 30 60 / 0.18),      /* lift */
    0 2px 6px rgb(20 30 60 / 0.06);
  --lift-shadow: rgb(20 30 60 / 0.3);
  --wash:
    radial-gradient(42vmax 42vmax at 12% 8%, rgb(120 165 255 / 0.85), transparent 70%),
    radial-gradient(38vmax 38vmax at 92% 22%, rgb(255 150 200 / 0.75), transparent 70%),
    radial-gradient(46vmax 46vmax at 70% 92%, rgb(120 235 200 / 0.75), transparent 70%),
    radial-gradient(32vmax 32vmax at 8% 80%, rgb(255 205 120 / 0.7), transparent 70%);
}
/* Dark: smoked glass over a deep, glowing wash. */
:root[data-theme="dark"] {
  --bg: #06070b;
  --line: rgb(255 255 255 / 0.1);
  --glass: rgb(28 30 40 / 0.42);
  --glass-header: rgb(18 20 28 / 0.55);         /* a touch darker so white type reads over the video */
  --glass-strong: rgb(255 255 255 / 0.14);
  --glass-sheen: linear-gradient(160deg, rgb(255 255 255 / 0.14), rgb(255 255 255 / 0.02) 45%, rgb(255 255 255 / 0.06));
  --glass-sheen-hover: linear-gradient(160deg, rgb(255 255 255 / 0.22), rgb(255 255 255 / 0.05) 45%, rgb(255 255 255 / 0.1));
  --glass-edge: rgb(255 255 255 / 0.16);
  --glass-shadow:
    inset 0 1px 0 rgb(255 255 255 / 0.22),      /* top highlight */
    inset 0 -1px 0 rgb(255 255 255 / 0.06),     /* bottom rim */
    inset 0 0 0 1px rgb(255 255 255 / 0.05),    /* inner rim */
    0 14px 40px -10px rgb(0 0 0 / 0.7),         /* lift */
    0 2px 6px rgb(0 0 0 / 0.4);
  --lift-shadow: rgb(0 0 0 / 0.8);
  --wash:
    radial-gradient(42vmax 42vmax at 12% 8%, rgb(60 90 255 / 0.45), transparent 70%),
    radial-gradient(38vmax 38vmax at 92% 22%, rgb(220 40 140 / 0.35), transparent 70%),
    radial-gradient(46vmax 46vmax at 70% 92%, rgb(0 190 160 / 0.32), transparent 70%),
    radial-gradient(32vmax 32vmax at 8% 80%, rgb(255 140 40 / 0.28), transparent 70%);
}

/* A soft colour wash behind the page, so the glass has something to bend. */
:root[data-theme] body { background: transparent; }
:root[data-theme] body::before {
  content: "";
  position: fixed;
  inset: -10vmax;
  z-index: -1;
  background: var(--wash), var(--bg);
  animation: glass-drift 40s ease-in-out infinite alternate;
}
@keyframes glass-drift {
  to { transform: translate3d(3vmax, -2vmax, 0) rotate(4deg) scale(1.05); }
}
@media (prefers-reduced-motion: reduce) {
  :root[data-theme] body::before { animation: none; }
}

/* The glass recipe. */
:root[data-theme] .site-header,
:root[data-theme] .site-footer__bar,
:root[data-theme] .project,
:root[data-theme] .site-footer__top,
:root[data-theme] .theme-toggle__track {
  background: var(--glass-sheen), var(--glass);
  -webkit-backdrop-filter: var(--glass-blur);
  backdrop-filter: var(--glass-blur);
  border: 1px solid var(--glass-edge);
  box-shadow: var(--glass-shadow);
}

/* Header: a floating glass panel; the page (and the home video) slides under it. */
:root[data-theme] .site-header {
  top: var(--float);
  margin: var(--float) var(--float) 0;
  border-radius: 1.5rem;
  padding: 1rem calc(var(--gutter) - var(--float)) 0.85rem;
  --glass: var(--glass-header);
}
:root[data-theme] .site-header__nav a {
  padding: 0.3em 0.75em;
  margin: -0.3em -0.2em;
  border-radius: 999px;
  text-decoration: none;
  transition: background 0.25s var(--ease), box-shadow 0.25s var(--ease);
}
:root[data-theme] .site-header__nav a:hover {
  background: var(--glass-strong);
  box-shadow: inset 0 1px 0 var(--glass-edge), 0 4px 12px -4px var(--lift-shadow);
}
:root[data-theme] .theme-toggle__track { border-radius: 999px; }
:root[data-theme="light"] .theme-toggle__knob {
  background: #fff;
  box-shadow: 0 1px 3px rgb(0 0 0 / 0.25), inset 0 -1px 1px rgb(0 0 0 / 0.06);
}

/* Footer bar: a floating glass pill above the bottom edge. */
:root[data-theme] .site-footer__bar {
  left: var(--float);
  right: var(--float);
  bottom: calc(var(--float) + env(safe-area-inset-bottom));
  padding: 0.7rem calc(var(--gutter) - var(--float));
  border-radius: 999px;
}
:root[data-theme] .site-footer__bar::after { content: none; }
:root[data-theme] body { padding-bottom: calc(var(--footer-h) + var(--float) * 2); }

/* Home video: its own rounded module, floating over the colour wash like the
   project cards, with a glass rim and a soft lift. */
:root[data-theme] .hero {
  margin: var(--float) var(--float) 0;
  height: calc(var(--screen-h, 100svh) - var(--header-h) - var(--footer-h) - var(--float) * 4);
  background: #000;
  border-radius: 1.75rem;
  isolation: isolate; /* keeps the video clipped to the rounded corners */
  box-shadow:
    0 0 0 1px var(--glass-edge),
    0 30px 60px -20px var(--lift-shadow),
    0 12px 24px -12px var(--lift-shadow);
}
/* Round the video itself too: iPhone Safari can ignore the module's rounded
   clipping on a playing video. */
:root[data-theme] .hero__video {
  border-radius: inherit;
  --r: 1.75rem;
  clip-path: inset(0 round var(--r));
}
/* The phone's zoomed opening shot is scaled 1.15x, so clip it in by the same
   amount (in the video's own, scaled-up space) to keep the corners round. */
:root[data-theme] .hero__video.is-zoomed {
  clip-path: inset(calc((1 - 1 / 1.15) / 2 * 100%) round calc(var(--r) / 1.15));
}

/* Work list: each project is a glass card. */
:root[data-theme] .work {
  /* The first card starts just below the footer bar, so it never peeks
     through the glass on the opening screen. */
  padding: calc(var(--footer-h) + var(--float) * 2) var(--float) 0;
}
:root[data-theme] .work__list { display: grid; gap: var(--float); padding: 0; margin: 0; }
:root[data-theme] .project {
  border-radius: 1.75rem;
  border-bottom: 1px solid var(--glass-edge);
  padding: 0 clamp(1.25rem, 2.5vw, 2.5rem);
  transition: transform 0.4s var(--ease), box-shadow 0.4s var(--ease), background 0.4s var(--ease);
}
:root[data-theme] .project:hover {
  transform: translateY(-2px);
  background: var(--glass-sheen-hover), var(--glass);
}

/* Back-to-top arrow: a small glass button. */
:root[data-theme] .site-footer__grid > .site-footer__top { border-radius: 999px; margin: 0; }

/* Case-study pages: let the colour wash through behind the content. */
:root[data-theme] .canvas { background: transparent !important; }

/* Phones, project pages: every image and video is its own rounded module
   floating over the wash, and the divider rules turn into soft hairlines. */
@media (max-width: 760px) {
  :root[data-theme] .canvas .c-image,
  :root[data-theme] .canvas .c-video {
    border-radius: 1.25rem;
    overflow: hidden;
    isolation: isolate;
    box-shadow:
      0 0 0 1px var(--glass-edge),
      0 18px 36px -14px var(--lift-shadow),
      0 6px 12px -8px var(--lift-shadow);
  }
  :root[data-theme] .canvas :is(.c-image, .c-video) :is(img, video, iframe) { border-radius: inherit; }
  /* Edge-to-edge media gets the same side gap as the cards. */
  :root[data-theme] .canvas :is(.c-image, .c-video):is([style*="--x:0;"], [style*="--x: 0;"]) {
    left: var(--float) !important;
    width: calc(100% - var(--float) * 2) !important;
  }
  :root[data-theme] .c-line { --color: var(--line) !important; }
}

@media (max-width: 760px) {
  :root[data-theme] { --float: 0.5rem; }
  :root[data-theme] .site-header { border-radius: 1.25rem; }
  :root[data-theme] .project { border-radius: 1.25rem; }
  :root[data-theme] .hero { border-radius: 1.25rem; }
  :root[data-theme] .hero__video { --r: 1.25rem; }
}
