/* ==========================================================================
   Aaryan Goundry — Portfolio
   UAL Graphic & Media Design, 2025–27
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   0. Fonts (self-hosted variable · no external requests)
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  src: url("../fonts/archivo-var.woff2") format("woff2-variations");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "Archivo";
  font-style: normal;
  font-display: swap;
  font-weight: 100 900;
  font-stretch: 62% 125%;
  src: url("../fonts/archivo-var-ext.woff2") format("woff2-variations");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "Bricolage Grotesque";
  font-style: normal;
  font-display: swap;
  font-weight: 200 800;
  font-stretch: 75% 100%;
  src: url("../fonts/bricolage-var.woff2") format("woff2-variations");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+2000-206F, U+20AC, U+2122,
    U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-display: swap;
  font-weight: 100 800;
  src: url("../fonts/jetbrains-var.woff2") format("woff2-variations");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
    U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-display: swap;
  font-weight: 100 800;
  src: url("../fonts/jetbrains-var-ext.woff2") format("woff2-variations");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
    U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
    U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "JetBrains Mono";
  font-style: normal;
  font-display: swap;
  font-weight: 100 800;
  src: url("../fonts/jetbrains-var-cyr.woff2") format("woff2-variations");
  unicode-range: U+0301, U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
}

:root {
  /* ---- Palette -----------------------------------------------------------
     One field colour carries the whole site — a deep raspberry — against near
     black body copy on white, inverted for the dark theme. Four values of the
     raspberry: the field itself, a darker one for solid bands on dark, a
     tone-on-tone for motifs, and a lighter one that is the accent on dark
     grounds. Contrast ratios are noted against the ground each one is meant
     to sit on; all AA or better.
     ---------------------------------------------------------------------- */

  --rasp:       #AB185D;   /* the portfolio's field colour — 6.95:1 with white */
  --rasp-deep:  #7E1145;   /* darker raspberry, solid band on the dark theme */
  --rasp-tone:  #931651;   /* tone-on-tone motif only, never text (1.2:1) */
  --rasp-lift:  #FF7FB0;   /* accent on dark grounds — 6.3:1 on --bg dark */
  --ink:        #181818;   /* body copy, as in the portfolio */

  --bg: #FFFFFF;            /* plain white ground */
  --bg-2: color-mix(in srgb, var(--ink) 4%, var(--bg));
  --media-bg: #FFFFFF;      /* behind images: plain white, no cream cast */
  --fg: var(--ink);
  --fg-60: color-mix(in srgb, var(--ink) 64%, transparent);
  --fg-40: color-mix(in srgb, var(--ink) 44%, transparent);
  --rule: color-mix(in srgb, var(--ink) 20%, transparent);
  --tint: color-mix(in srgb, var(--ink) 7%, transparent);

  --accent: var(--rasp);    /* 6.95:1 on white */
  --accent-fg: #FFFFFF;     /* text that sits ON an accent fill */

  /* Solid colour bands (see .band) */
  --band-bg: var(--rasp);
  --band-fg: #FFFFFF;
  --band-accent: #FFFFFF;
  --band-accent-fg: var(--ink);

  /* Type */
  --font-display: "Bricolage Grotesque", "Archivo", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-body: "Archivo", "Helvetica Neue", Helvetica, Arial, sans-serif;
  --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;

  --step--1: clamp(0.72rem, 0.69rem + 0.15vw, 0.8rem);
  --step-0:  clamp(0.9rem, 0.86rem + 0.2vw, 1rem);
  --step-1:  clamp(1.1rem, 1rem + 0.5vw, 1.4rem);
  --step-2:  clamp(1.5rem, 1.2rem + 1.4vw, 2.4rem);
  --step-3:  clamp(2.1rem, 1.4rem + 3.4vw, 4.4rem);
  --step-4:  clamp(2.9rem, 1rem + 9vw, 9rem);
  --step-5:  clamp(3.4rem, -0.5rem + 17vw, 15rem);

  /* Layout */
  --gutter: clamp(1rem, 0.4rem + 2.4vw, 2.5rem);
  --rule-w: 1px;
  --radius: 0px;    /* square blocks, after the 2024–25 portfolio */
  --nav-h: 3.75rem;

  /* Motion */
  --ease: cubic-bezier(.22, 1, .36, 1);
  --dur: .6s;
}

[data-theme="dark"] {
  --bg: #000000;            /* black ground */
  --bg-2: #141414;
  --media-bg: var(--bg);
  --fg: #F8F1F4;
  --fg-60: color-mix(in srgb, #F8F1F4 64%, transparent);
  --fg-40: color-mix(in srgb, #F8F1F4 44%, transparent);
  --rule: color-mix(in srgb, #F8F1F4 20%, transparent);
  --tint: color-mix(in srgb, #F8F1F4 8%, transparent);

  --accent: var(--rasp-lift);      /* 6.3:1 on --bg */
  --accent-fg: #1A0710;

  --band-bg: var(--rasp);          /* the field colour holds on dark too */
  --band-fg: #FFFFFF;
  --band-accent: #FFFFFF;
  --band-accent-fg: var(--ink);
}

/* --------------------------------------------------------------------------
   1b. Solid colour bands
   A .band re-declares the theme tokens for its subtree, so every component
   inside it adapts without needing its own override.
   -------------------------------------------------------------------------- */

.band {
  --bg: var(--band-bg);
  --bg-2: color-mix(in srgb, var(--band-fg) 12%, var(--band-bg));
  --fg: var(--band-fg);
  --fg-60: color-mix(in srgb, var(--band-fg) 66%, transparent);
  --fg-40: color-mix(in srgb, var(--band-fg) 46%, transparent);
  --rule: color-mix(in srgb, var(--band-fg) 24%, transparent);
  --tint: color-mix(in srgb, var(--band-fg) 12%, transparent);
  --accent: var(--band-accent);
  --accent-fg: var(--band-accent-fg);

  background: var(--band-bg);
  color: var(--band-fg);
}

.band--green {                     /* class name kept; now the plain band */
  --band-bg: var(--bg);            /* white in light, black in dark */
  --band-fg: var(--fg);
  /* named outright, not var(--accent): .band sets --accent from --band-accent,
     so pointing back at it makes a cycle and the token resolves to nothing. */
  --band-accent: var(--rasp);
  --band-accent-fg: #FFFFFF;
  border-top: var(--rule-w) solid var(--rule);
}
[data-theme="dark"] .band--green {
  --band-accent: var(--rasp-lift);
  --band-accent-fg: #1A0710;
}


/* --------------------------------------------------------------------------
   1c. Motifs
   The portfolio decorates its colour fields with tone-on-tone geometry: a
   stack of dots down one edge, short bars, and a cross. Drawn here in CSS on
   the bands themselves, so no markup carries decoration.
   -------------------------------------------------------------------------- */

.band { position: relative; overflow: hidden; }

.band::before,
.band::after {
  content: "";
  position: absolute;
  pointer-events: none;
  opacity: .55;
}

/* three dots, stacked at the right edge */
.band::before {
  top: clamp(1.5rem, 5vw, 4rem);
  right: clamp(1rem, 4vw, 4rem);
  width: 2.25rem;
  height: 9rem;
  background-image: radial-gradient(circle, var(--band-motif) 46%, transparent 47%);
  background-size: 2.25rem 3.4rem;
  background-repeat: repeat-y;
}

/* the cross */
.band::after {
  bottom: clamp(1.5rem, 5vw, 4rem);
  right: clamp(2.5rem, 7vw, 7rem);
  width: 6rem;
  height: 6rem;
  background-image:
    linear-gradient(var(--band-motif), var(--band-motif)),
    linear-gradient(var(--band-motif), var(--band-motif));
  background-size: 1.35rem 6rem, 6rem 1.35rem;
  background-position: center, center;
  background-repeat: no-repeat;
}

.band { --band-motif: color-mix(in srgb, var(--band-fg) 16%, transparent); }
.band--green { --band-motif: color-mix(in srgb, var(--fg) 12%, transparent); }

@media (max-width: 40rem) {
  .band::before, .band::after { display: none; }
}

@media (prefers-reduced-motion: no-preference) {
  .band::before, .band::after { transition: opacity var(--dur) var(--ease); }
}


/* --------------------------------------------------------------------------
   1d. Atmosphere
   A drifting field of the house motifs behind the hero, and a page tint that
   blends from one project to the next as the work scrolls past. Both are
   built from the same two colours as everything else, held far enough back
   that body copy keeps its contrast.
   -------------------------------------------------------------------------- */

/* The tint layer sits under the content and above the ground. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background: var(--tint-now, transparent);
  transition: background 900ms var(--ease);
}

:root {
  /* The per-project wash is off: the work sits on the plain ground. Swap any
     of these for a color-mix to bring the tint back for that category. */
  --tint-editorial:  transparent;
  --tint-identity:   transparent;
  --tint-type:       transparent;
  --tint-motion:     transparent;
  --tint-digital:    transparent;
  --tint-image:      transparent;
  --tint-photography:transparent;
}
[data-theme="dark"] {
  --tint-editorial:  transparent;
  --tint-identity:   transparent;
  --tint-type:       transparent;
  --tint-motion:     transparent;
  --tint-digital:    transparent;
  --tint-image:      transparent;
  --tint-photography:transparent;
}

/* Hero motif field — three layers of dots, bars and a cross, drifting at
   different speeds so the hero is never quite still. */
.hero-field {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  z-index: 0;
}
.hero-field i {
  position: absolute;
  inset: -20% -10%;
  display: block;
  opacity: .66;
  /* Thinned out towards the middle so the type never fights the pattern. */
  -webkit-mask-image: radial-gradient(115% 80% at 42% 52%, transparent 34%, #000 78%);
  mask-image: radial-gradient(115% 80% at 42% 52%, transparent 34%, #000 78%);
}
.hero-field i:nth-child(1) {
  background-image: radial-gradient(circle, color-mix(in srgb, var(--rasp) 16%, transparent) 22%, transparent 23%);
  background-size: 8.5rem 8.5rem;
  animation: drift-a 54s linear infinite;
}
.hero-field i:nth-child(2) {
  background-image:
    linear-gradient(color-mix(in srgb, var(--rasp) 12%, transparent), color-mix(in srgb, var(--rasp) 12%, transparent)),
    linear-gradient(color-mix(in srgb, var(--rasp) 12%, transparent), color-mix(in srgb, var(--rasp) 12%, transparent));
  background-size: 1.1rem 7rem, 7rem 1.1rem;
  background-position: 22% 30%, 22% 30%;
  background-repeat: no-repeat;
  animation: drift-b 38s ease-in-out infinite alternate;
}
.hero-field i:nth-child(3) {
  background-image: linear-gradient(color-mix(in srgb, var(--ink) 7%, transparent), color-mix(in srgb, var(--ink) 7%, transparent));
  background-size: 14rem .9rem;
  background-position: 78% 68%;
  background-repeat: no-repeat;
  animation: drift-c 46s ease-in-out infinite alternate;
}
.hero > * { position: relative; z-index: 1; }

@keyframes drift-a { to { transform: translate3d(8.5rem, 8.5rem, 0); } }
@keyframes drift-b { to { transform: translate3d(9vw, -4vh, 0) rotate(8deg); } }
@keyframes drift-c { to { transform: translate3d(-11vw, 5vh, 0); } }

@media (prefers-reduced-motion: reduce) {
  .hero-field i { animation: none; }
  body::before { transition: none; }
}



/* --------------------------------------------------------------------------
   1f. Sketch selection
   Drag across the headline and the covered letters redraw as a pencil study
   of themselves: the same letterforms, hatched fill, drawn outline, edges
   knocked off-true by a displacement map. Hero only — every other selection
   on the site behaves normally.
   -------------------------------------------------------------------------- */

.sketch-defs { position: absolute; width: 0; height: 0; overflow: hidden; }

.hero__type .ch { display: inline-block; }

/* The browser's own highlight sits on top of the drawing, and while text is
   selected Chromium paints the glyph with the selection colour, which defeats
   background-clip: text. Both are turned off here so the hatch shows. */
.hero__type h1 ::selection {
  background: color-mix(in srgb, var(--accent) 24%, transparent);
  color: transparent;
  -webkit-text-fill-color: transparent;
}
.hero__type h1::selection {
  background: color-mix(in srgb, var(--accent) 24%, transparent);
  color: transparent;
  -webkit-text-fill-color: transparent;
}
/* on the raspberry bar the tint has to go the other way to be seen */
.hero__type .outline ::selection,
.hero__type .outline::selection {
  background: color-mix(in srgb, #FFFFFF 26%, transparent);
}

.hero__type .ch.is-sketch {
  color: transparent;
  -webkit-text-fill-color: transparent;
  -webkit-text-stroke: 1.4px var(--fg);
  background-image: repeating-linear-gradient(
    48deg,
    var(--fg) 0 1.5px,
    transparent 1.5px 6px
  );
  -webkit-background-clip: text;
  background-clip: text;
}

/* the full stop keeps its colour, hatched like everything else */
.hero__type .ch.accent.is-sketch {
  -webkit-text-stroke-color: var(--accent);
  background-image: repeating-linear-gradient(
    48deg,
    var(--accent) 0 1.5px,
    transparent 1.5px 5px
  );
}

/* inside the raspberry bar the pencil is white, as it would be on a dark board */
.hero__type .outline .ch.is-sketch {
  -webkit-text-stroke-color: #FFFFFF;
  background-image: repeating-linear-gradient(
    48deg,
    #FFFFFF 0 1.5px,
    transparent 1.5px 6px
  );
}

/* a hand doesn't draw two letters at the same angle */
.hero__type .ch.is-sketch:nth-child(3n)   { transform: rotate(-0.7deg) translateY(0.4px); }
.hero__type .ch.is-sketch:nth-child(3n+1) { transform: rotate(0.5deg); }
.hero__type .ch.is-sketch:nth-child(3n+2) { transform: rotate(-0.3deg) translateY(-0.3px); }

@media (prefers-reduced-motion: reduce) {
  .hero__type .ch.is-sketch { filter: none; transform: none; }
}


/* --------------------------------------------------------------------------
   Mosaic
   Justified rows: each tile's flex-grow is its own aspect ratio, so widths
   land in proportion to the pictures and every tile in a row ends up exactly
   the same height. Nothing is cropped and nothing is letterboxed. Greyscale
   until hovered.
   -------------------------------------------------------------------------- */

.mosaic { padding: 0 clamp(.5rem, 1.5vw, 1rem); }

.mosaic__row {
  display: flex;
  gap: 3px;
  margin-bottom: 3px;
}

.tile {
  position: relative;
  display: block;
  /* grow in proportion to the picture, from a zero basis: within a row the
     widths land in proportion and every tile ends the same height */
  flex: var(--r, 1) 1 0;
  min-width: 0;
  overflow: hidden;
  background: var(--media-bg);
  /* A hairline drawn inside the box, so work shot on white still reads as a
     picture. Inset shadow rather than a border: a border would eat into the
     content width and the row heights would stop matching to the pixel. */
  box-shadow: inset 0 0 0 1px var(--rule);
  transition: box-shadow .35s var(--ease);
  container-type: inline-size;
}

.tile:hover, .tile:focus-visible { box-shadow: inset 0 0 0 1px var(--accent); }

.tile img {
  display: block;
  width: 100%;
  height: auto;
  filter: grayscale(1) contrast(1.02);
  transition: filter .45s var(--ease), transform .8s var(--ease);
}

.tile:hover img,
.tile:focus-visible img {
  filter: grayscale(0);
  transform: scale(1.02);
}

.tile__label {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  display: grid;
  gap: .15rem;
  padding: .9rem 1rem 1rem;
  background: var(--accent);
  color: var(--accent-fg);
  opacity: 0;
  transform: translateY(100%);
  transition: opacity .35s var(--ease), transform .45s var(--ease);
  pointer-events: none;
}

.tile__title {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-1);
  line-height: 1.05;
  letter-spacing: -0.01em;
  text-transform: uppercase;
}

.tile__meta,
.tile__go {
  font-size: var(--step--1);
  letter-spacing: .06em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--accent-fg) 78%, transparent);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.tile__go { margin-top: .35rem; }

.tile:hover .tile__label,
.tile:focus-visible .tile__label { opacity: 1; transform: none; }

/* Narrow tiles can't carry three lines — drop to the title alone. */
@container (max-width: 15rem) {
  .tile__meta, .tile__go { display: none; }
}

.tile.is-hidden { display: none; }

/* Filtered down to a handful, the justified rows fall apart — a single
   survivor would stretch the width of the page. So a filtered view drops the
   packing and lays every tile out at one shared height, widths still in
   proportion to the pictures. */
.mosaic.is-filtered {
  display: flex;
  flex-wrap: wrap;
  gap: 3px;
  align-items: flex-start;
}
.mosaic.is-filtered .mosaic__row { display: contents; }
.mosaic.is-filtered .tile {
  flex: 0 1 auto;
  width: calc(var(--r, 1) * clamp(11rem, 17vw, 17rem));
  max-width: 100%;
}

/* A phone is too narrow for rows packed to a wide screen: they wrap, the
   leftovers stretch, and the wall stops looking level. So below this width the
   rows dissolve and every cover re-flows into a two-column collage. Each tile
   keeps its own proportions, nothing is stretched to fill a gap, and the whole
   thing reads as one piece of work rather than a broken grid. */
@media (max-width: 52rem) {
  .mosaic {
    columns: 2;
    column-gap: 3px;
    padding: 0 3px;
  }
  .mosaic__row { display: contents; }
  .tile {
    display: block;
    width: 100%;
    margin: 0 0 3px;
    break-inside: avoid;
    -webkit-column-break-inside: avoid;
    container-type: normal;   /* the tile names itself here, whatever its width */
  }
  /* The hairline belongs to the picture, not to the picture-plus-caption —
     otherwise every tile reads as a boxed card. */
  .tile,
  .tile:hover,
  .tile:focus-visible { box-shadow: none; }
  .tile img { outline: 1px solid var(--rule); outline-offset: -1px; }

  /* Nothing hovers on a phone, so the pictures carry their own colour and
     every tile wears its title instead of hiding it behind a cursor. */
  .tile img { filter: none; }
  .tile:hover img, .tile:focus-visible img { transform: none; }
  .tile__label {
    position: static;
    opacity: 1;
    transform: none;
    background: transparent;
    color: var(--fg);
    padding: .55rem .15rem 1rem;
    gap: .1rem;
  }
  .mosaic .tile__title { font-size: var(--step-0); }
  .mosaic .tile__meta {
    display: block;
    color: var(--fg-60);
    white-space: normal;     /* room to wrap rather than trail off in a column */
    overflow: visible;
  }
  .mosaic .tile__go { display: none; }

  /* Filtered views use the same collage — no lone tile stretched wide. */
  .mosaic.is-filtered { display: block; }
  .mosaic.is-filtered .tile { width: 100%; flex: none; max-width: none; }
}

/* One line of instructions for a cursor, another for a thumb. */
.on-touch { display: none; }
@media (hover: none), (max-width: 52rem) {
  .on-pointer { display: none; }
  .on-touch { display: inline; }
}

/* Room for a third column on a small tablet, so the tiles don't blow up. */
@media (min-width: 34rem) and (max-width: 52rem) {
  .mosaic { columns: 3; }
}

/* One column on the smallest phones, where two would be postage stamps. */
@media (max-width: 20rem) {
  .mosaic { columns: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .tile img { transition: filter .45s linear; }
  .tile:hover img { transform: none; }
}

/* --------------------------------------------------------------------------
   2. Reset & base
   -------------------------------------------------------------------------- */

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

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--step-0);
  line-height: 1.5;
  font-variation-settings: "wght" 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
  transition: background-color .4s var(--ease), color .4s var(--ease);
}

body.is-locked { overflow: hidden; }

img, svg, video { display: block; max-width: 100%; height: auto; }

h1, h2, h3, h4, p, figure, blockquote, ul, ol, dl, dd { margin: 0; }
ul, ol { padding: 0; list-style: none; }

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
}

::selection { background: var(--accent); color: var(--accent-fg); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* --------------------------------------------------------------------------
   3. Utility type
   -------------------------------------------------------------------------- */

.mono {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: .06em;
  text-transform: uppercase;
  font-weight: 400;
}

.display {
  font-family: var(--font-display);
  font-variation-settings: "wght" 800, "wdth" 92, "opsz" 96;
  line-height: .88;
  letter-spacing: -0.025em;
  text-transform: uppercase;
}

.h-xl { font-size: var(--step-5); }
.h-lg { font-size: var(--step-4); }
.h-md { font-size: var(--step-3); }
.h-sm { font-size: var(--step-2); }

.lead {
  font-size: var(--step-1);
  line-height: 1.35;
  letter-spacing: -0.015em;
  max-width: 34ch;
}

.muted { color: var(--fg-60); }
.accent { color: var(--accent); }
.center { text-align: center; }

.link-u {
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 100% 1px;
  background-repeat: no-repeat;
  background-position: 0 100%;
  transition: background-size .4s var(--ease);
  padding-bottom: 2px;
}
.link-u:hover { background-size: 0% 1px; background-position: 100% 100%; }

/* --------------------------------------------------------------------------
   4. Layout primitives
   -------------------------------------------------------------------------- */

.wrap {
  padding-inline: var(--gutter);
  width: 100%;
  margin-inline: auto;
}

.grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--gutter);
}

.rule {
  border: 0;
  border-top: var(--rule-w) solid var(--rule);
  margin: 0;
}

section { position: relative; }

.section-pad { padding-block: clamp(3.5rem, 2rem + 7vw, 9rem); }

.section-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-block: .9rem;
  border-top: 3px solid var(--accent);
}

/* --------------------------------------------------------------------------
   5. The curtain
   A raspberry panel that covers the page while it loads and wipes upward when
   it is ready. Leaving a page slides the same panel up from below, so a click
   and the arrival that follows read as one continuous movement rather than two
   unrelated animations. It holds for as long as the page actually takes, which
   is the point — a slow page gets something to look at instead of a white
   screen.
   -------------------------------------------------------------------------- */

.curtain {
  position: fixed;
  inset: 0;
  z-index: 999;
  background: var(--rasp);
  color: #FFFFFF;
  display: grid;
  padding: var(--gutter);
  transform: translateY(0);
  will-change: transform;
}

/* Arriving: the panel leaves through the top. */
.curtain.is-up {
  transform: translateY(-101%);
  transition: transform .66s cubic-bezier(.76, 0, .24, 1);
}

/* Parked below the fold, ready to be pulled up on the way out. */
.curtain.is-below {
  transform: translateY(101%);
  transition: none;
}

/* Leaving: the same panel climbs back into place. */
.curtain.is-down {
  transform: translateY(0);
  transition: transform .46s cubic-bezier(.76, 0, .24, 1);
}

.curtain.is-off { visibility: hidden; pointer-events: none; }

.curtain__inner {
  display: grid;
  grid-template-rows: auto 1fr auto;
  width: 100%;
  height: 100%;
}

.curtain__mark {
  color: color-mix(in srgb, #FFFFFF 68%, transparent);
}

.curtain__word {
  align-self: center;
  font-family: var(--font-display);
  font-variation-settings: "wght" 800;
  font-weight: 800;
  font-size: clamp(2.6rem, 11vw, 9rem);
  line-height: .88;
  letter-spacing: -0.035em;
  text-transform: uppercase;
  text-wrap: balance;
  max-width: 14ch;
  /* rises into place as the panel settles */
  opacity: 0;
  transform: translateY(0.35em);
  animation: curtain-word .5s cubic-bezier(.22, 1, .36, 1) .06s forwards;
}

@keyframes curtain-word {
  to { opacity: 1; transform: none; }
}

.curtain.is-continued .curtain__word {
  animation: none;
  opacity: 1;
  transform: none;
}

.curtain__foot {
  display: flex;
  align-items: end;
  justify-content: space-between;
  gap: 1rem;
}

.curtain__rule {
  flex: 1;
  height: 1px;
  background: color-mix(in srgb, #FFFFFF 32%, transparent);
  margin-bottom: .45em;
}

.curtain__rule i {
  display: block;
  height: 100%;
  width: 0;
  background: #FFFFFF;
}

/* The panel is the one place the raspberry stays raspberry in both themes —
   it is the site's colour, not a surface tint. */
[data-theme="dark"] .curtain { background: var(--rasp-deep); }

@media (prefers-reduced-motion: reduce) {
  .curtain,
  .curtain.is-up,
  .curtain.is-down { transition: opacity .2s linear; }
  .curtain.is-up { transform: none; opacity: 0; }
  .curtain__word { animation: none; opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   6. Navigation
   -------------------------------------------------------------------------- */

.nav {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 120;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-inline: var(--gutter);
  background: color-mix(in srgb, var(--bg) 82%, transparent);
  backdrop-filter: blur(14px) saturate(1.2);
  -webkit-backdrop-filter: blur(14px) saturate(1.2);
  border-bottom: var(--rule-w) solid var(--rule);
  transition: transform .5s var(--ease), background-color .4s var(--ease);
}
.nav.is-hidden { transform: translateY(-101%); }

.nav__mark {
  display: flex;
  align-items: center;
  gap: .55rem;
  font-family: var(--font-display);
  font-variation-settings: "wght" 700;
  font-size: var(--step-0);
  letter-spacing: -0.02em;
  text-transform: uppercase;
  white-space: nowrap;
}
.nav__mark .dot {
  width: .52rem; height: .52rem;
  border-radius: 50%;
  background: var(--accent);
  flex: none;
  animation: pulse 2.4s var(--ease) infinite;
}
@keyframes pulse {
  0%, 100% { transform: scale(1); opacity: 1; }
  50% { transform: scale(.6); opacity: .5; }
}

.nav__mid {
  display: flex;
  gap: 1.6rem;
  align-items: center;
}
.nav__mid a { position: relative; }
.nav__mid a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -.35em;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform .4s var(--ease);
}
.nav__mid a:hover::after,
.nav__mid a[aria-current="page"]::after { transform: scaleX(1); }
.nav__mid a[aria-current="page"] { color: var(--accent); }

.nav__end {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.clock { font-variant-numeric: tabular-nums; }

.theme-toggle {
  width: 2.1rem; height: 2.1rem;
  border: var(--rule-w) solid var(--rule);
  border-radius: 50%;
  display: grid;
  place-items: center;
  transition: background-color .3s var(--ease), border-color .3s var(--ease), transform .3s var(--ease);
}
.theme-toggle:hover { background: var(--tint); transform: rotate(35deg); }
.theme-toggle svg { width: 1rem; height: 1rem; }
.theme-toggle .i-moon { display: none; }
[data-theme="dark"] .theme-toggle .i-sun { display: none; }
[data-theme="dark"] .theme-toggle .i-moon { display: block; }

/* Burger */
.burger {
  display: none;
  width: 2.4rem; height: 2.1rem;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  gap: .35rem;
}
.burger span {
  display: block;
  width: 1.4rem; height: 1.5px;
  background: currentColor;
  transition: transform .4s var(--ease), opacity .3s var(--ease);
}
.burger.is-open span:nth-child(1) { transform: translateY(4.75px) rotate(45deg); }
.burger.is-open span:nth-child(2) { opacity: 0; }
.burger.is-open span:nth-child(3) { transform: translateY(-4.75px) rotate(-45deg); }

/* Overlay menu */
.menu {
  position: fixed;
  inset: 0;
  z-index: 110;
  background: var(--bg);
  padding: calc(var(--nav-h) + 2rem) var(--gutter) var(--gutter);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  clip-path: inset(0 0 100% 0);
  transition: clip-path .7s var(--ease);
  pointer-events: none;
}
.menu.is-open { clip-path: inset(0 0 0 0); pointer-events: auto; }
.menu__list li {
  overflow: hidden;
  border-bottom: var(--rule-w) solid var(--rule);
}
.menu__list a {
  display: flex;
  align-items: baseline;
  gap: 1rem;
  padding-block: .35em;
  font-family: var(--font-display);
  font-variation-settings: "wght" 700;
  font-size: var(--step-3);
  line-height: 1.05;
  letter-spacing: -0.035em;
  text-transform: uppercase;
  transform: translateY(105%);
  transition: transform .6s var(--ease), color .3s var(--ease);
}
.menu.is-open .menu__list a { transform: translateY(0); }
.menu__list li:nth-child(1) a { transition-delay: .08s; }
.menu__list li:nth-child(2) a { transition-delay: .14s; }
.menu__list li:nth-child(3) a { transition-delay: .20s; }
.menu__list li:nth-child(4) a { transition-delay: .26s; }
.menu__list li:nth-child(5) a { transition-delay: .32s; }
.menu__list a:hover { color: var(--accent); }
.menu__list .idx {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: .06em;
  color: var(--fg-40);
  text-transform: none;
}
.menu__foot {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 2rem;
  justify-content: space-between;
  padding-top: 1.5rem;
  border-top: var(--rule-w) solid var(--rule);
}

/* --------------------------------------------------------------------------
   7. Hero
   -------------------------------------------------------------------------- */

.hero {
  min-height: 100svh;
  padding-top: var(--nav-h);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  position: relative;
}

.hero__top {
  padding: clamp(1.5rem, 5vh, 3.5rem) var(--gutter) 0;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
}

.greeting {
  display: inline-flex;
  align-items: baseline;
  gap: .4rem;
  min-height: 1.4em;
}
.greeting__word {
  display: inline-block;
  min-width: 7ch;
  transition: opacity .3s var(--ease), transform .3s var(--ease);
}
.greeting__word.is-out { opacity: 0; transform: translateY(-.4em); }

.hero__type {
  padding-inline: var(--gutter);
  padding-block: clamp(1rem, 4vh, 2rem);
  flex: 1 1 auto;
  display: flex;
  align-items: center;
}
.hero__type h1 {
  width: 100%;
  font-family: var(--font-display);
  font-variation-settings: "wght" 800, "wdth" 100;
  font-size: var(--step-5);
  line-height: .82;
  letter-spacing: -0.045em;
  text-transform: uppercase;
}
.hero__type .line {
  display: block;
  overflow: hidden;
  white-space: nowrap;
}
.hero__type .line > span {
  display: inline-block;
  transform: translateY(105%);
  transition: transform 1s var(--ease);
}
.is-ready .hero__type .line > span { transform: translateY(0); }
.is-ready .hero__type .line:nth-child(2) > span { transition-delay: .09s; }
.is-ready .hero__type .line:nth-child(3) > span { transition-delay: .18s; }
/* The middle line is knocked out of a raspberry highlight — the letters take
   the page ground, so the bar reads as a marker stroke through the type. */
.hero__type .outline {
  -webkit-text-stroke: 1.5px color-mix(in srgb, var(--fg) 32%, transparent);
  color: var(--bg);
  background: var(--rasp);   /* the deep field colour in both themes, not the
                                lighter dark-mode accent */
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
  padding: .02em .1em .05em;
  margin-left: -.1em;
}

.hero__bottom {
  padding: 0 var(--gutter) clamp(1.25rem, 3vh, 2.5rem);
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--gutter);
  align-items: end;
  border-top: var(--rule-w) solid var(--rule);
  padding-top: 1rem;
}
.hero__bottom .col-a { grid-column: 1 / span 5; }
.hero__bottom .col-b { grid-column: 7 / span 3; }
.hero__bottom .col-c { grid-column: 11 / span 2; text-align: right; }

.scroll-cue {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
}
.scroll-cue .arrow {
  display: inline-block;
  animation: bob 1.8s var(--ease) infinite;
}
@keyframes bob {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(.28em); }
}

/* --------------------------------------------------------------------------
   8. Marquee
   -------------------------------------------------------------------------- */

.marquee {
  overflow: hidden;
  border-block: 0;
  padding-block: .8rem;
  background: var(--rasp);
  color: #FFFFFF;
}
.marquee__track {
  display: flex;
  width: max-content;
  animation: slide 38s linear infinite;
}
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee__track span {
  font-family: var(--font-display);
  font-variation-settings: "wght" 700, "wdth" 95, "opsz" 96;
  font-size: var(--step-2);
  text-transform: uppercase;
  letter-spacing: -0.02em;
  padding-inline: 1.2rem;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 1.2rem;
}
.marquee__track span::after {
  content: "";
  width: .4rem; height: .4rem;
  border-radius: 50%;
  background: var(--accent);
  display: inline-block;
}
@keyframes slide { to { transform: translateX(-50%); } }

/* --------------------------------------------------------------------------
   9. Work index — hover-preview list
   -------------------------------------------------------------------------- */

.index-list { border-top: var(--rule-w) solid var(--rule); }

.index-row {
  border-bottom: var(--rule-w) solid var(--rule);
  position: relative;
}
.index-row > a {
  display: grid;
  grid-template-columns: 4rem minmax(0, 1fr) auto auto;
  gap: var(--gutter);
  align-items: center;
  padding-block: clamp(.85rem, 2vw, 1.6rem);
  padding-inline: var(--gutter);
  position: relative;
  z-index: 1;
  transition: color .35s var(--ease), padding-left .45s var(--ease);
}
.index-row > a::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--accent);
  transform: scaleY(0);
  transform-origin: 50% 100%;
  transition: transform .45s var(--ease);
  z-index: -1;
}
.index-row > a:hover { color: var(--accent-fg); padding-left: calc(var(--gutter) + .75rem); }
.index-row > a:hover::before { transform: scaleY(1); }

.index-row .num {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  color: var(--fg-40);
  transition: color .35s var(--ease);
}
.index-row > a:hover .num { color: color-mix(in srgb, var(--accent-fg) 72%, transparent); }
.index-row .title {
  font-family: var(--font-display);
  font-variation-settings: "wght" 700;
  font-size: var(--step-2);
  line-height: 1;
  letter-spacing: -0.03em;
  text-transform: uppercase;
}
.index-row .meta { color: var(--fg-60); transition: color .35s var(--ease); }
.index-row > a:hover .meta { color: color-mix(in srgb, var(--accent-fg) 78%, transparent); }
.index-row .go {
  font-family: var(--font-mono);
  font-size: var(--step--1);
  opacity: 0;
  transform: translateX(-.5rem);
  transition: opacity .35s var(--ease), transform .35s var(--ease);
}
.index-row > a:hover .go { opacity: 1; transform: translateX(0); }

/* Cursor-following preview */
.preview {
  border-radius: var(--radius);
  position: fixed;
  top: 0; left: 0;
  width: clamp(180px, 18vw, 300px);
  aspect-ratio: 4 / 5;
  pointer-events: none;
  z-index: 90;
  opacity: 0;
  transform: translate(-50%, -50%) scale(.9);
  transition: opacity .35s var(--ease), transform .45s var(--ease);
  overflow: hidden;
  background: var(--media-bg);
}
.preview.is-visible { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.preview img {
  width: 100%; height: 100%;
  object-fit: contain;
  background: var(--media-bg);
}
@media (hover: none), (max-width: 860px) { .preview { display: none; } }

/* --------------------------------------------------------------------------
   9b. Highlights — staggered editorial grid, one per discipline
   -------------------------------------------------------------------------- */

.disc-strip {
  display: flex;
  flex-wrap: wrap;
  gap: .4rem 1.4rem;
  padding: .9rem var(--gutter);
  border-bottom: var(--rule-w) solid var(--rule);
}
.disc-strip a {
  display: inline-flex;
  align-items: baseline;
  gap: .45rem;
  transition: color .3s var(--ease);
}
.disc-strip a:hover { color: var(--accent); }
.disc-strip .n { color: var(--fg-40); }

.hl-grid {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  column-gap: var(--gutter);
  row-gap: clamp(2.5rem, 7vw, 7rem);
  padding: clamp(2.5rem, 6vw, 5rem) var(--gutter) 0;
}

.hl {
  display: block;
  grid-column: span 6;
  scroll-margin-top: calc(var(--nav-h) + 1.5rem);
}

/* Width follows the cover's proportions (set in build.js). */
.hl--w3 { grid-column: span 3; }
.hl--w4 { grid-column: span 4; }
.hl--w5 { grid-column: span 5; }
.hl--w6 { grid-column: span 6; }
.hl--w7 { grid-column: span 7; }
.hl--w8 { grid-column: span 8; }
.hl--w9 { grid-column: span 9; }

/* Moving covers fill their box exactly as the still would. */
.hl__media .cover-loop {
  display: block;
  width: 100%;
  height: auto;
  object-fit: cover;
  filter: grayscale(1);
  transition: transform 1s var(--ease), filter .45s var(--ease);
}
.hl:hover .hl__media .cover-loop,
.hl:focus-visible .hl__media .cover-loop { transform: scale(1.05); filter: grayscale(0); }
.card__media .cover-loop {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
@media (prefers-reduced-motion: reduce) {
  .cover-loop { display: none; }
}

/* Gentle stagger, alternating down the rows. */
.hl--low { align-self: end; }

/* A project alone on its row is centred — left-aligned it leaves the right
   half of the page empty and the grid looks unfinished. */
.hl--solo { justify-self: center; }
@media (min-width: 60rem) {
  .hl--solo.hl--w5 { grid-column: 5 / span 5; }
  .hl--solo.hl--w6 { grid-column: 4 / span 6; }
  .hl--solo.hl--w8 { grid-column: 3 / span 8; }
  .hl--solo.hl--w9 { grid-column: 2 / span 9; }
}

.hl__media {
  position: relative;
  overflow: hidden;
  background: var(--media-bg);
  border-radius: var(--radius);
}
.hl__media img {
  width: 100%;
  height: auto;
  display: block;
  /* Grey until you point at it, same as the gallery wall. */
  filter: grayscale(1);
  transition: transform 1s var(--ease), filter .45s var(--ease);
}
.hl:hover .hl__media img,
.hl:focus-visible .hl__media img { transform: scale(1.05); filter: grayscale(0); }

.hl__view {
  position: absolute;
  border-radius: 100px;
  right: .8rem; bottom: .8rem;
  background: var(--accent);
  color: var(--accent-fg);
  padding: .4rem .75rem;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: .06em;
  text-transform: uppercase;
  opacity: 0;
  transform: translateY(.5rem);
  transition: opacity .4s var(--ease), transform .4s var(--ease);
}
.hl:hover .hl__view { opacity: 1; transform: translateY(0); }

.hl__eyebrow {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: .55rem;
}
.hl__cat {
  font-family: var(--font-display);
  font-variation-settings: "wght" 700;
  font-size: var(--step-1);
  letter-spacing: -0.025em;
  text-transform: uppercase;
  color: var(--accent);
  line-height: 1;
}
.hl__body {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-top: .7rem;
  margin-top: .7rem;
  border-top: var(--rule-w) solid var(--rule);
}
.hl__title {
  font-family: var(--font-display);
  font-variation-settings: "wght" 700;
  font-size: var(--step-2);
  line-height: 1;
  letter-spacing: -0.03em;
  text-transform: uppercase;
  transition: color .3s var(--ease);
}
.hl:hover .hl__title { color: var(--accent); }
.hl__summary {
  margin-top: .5rem;
  max-width: 46ch;
  font-size: var(--step--1);
  line-height: 1.45;
  color: var(--fg-60);
}

@media (max-width: 900px) {
  .hl-grid { row-gap: clamp(2rem, 8vw, 3.5rem); }
  .hl,
  .hl--w3, .hl--w4, .hl--w5, .hl--w6, .hl--w7, .hl--w8, .hl--w9 {
    grid-column: 1 / -1;
    align-self: auto;
  }
}

/* --------------------------------------------------------------------------
   10. Work grid (cards)
   -------------------------------------------------------------------------- */

.work-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
  gap: var(--gutter) var(--gutter);
  padding-inline: var(--gutter);
}
.work-grid.is-wide { grid-template-columns: repeat(auto-fill, minmax(min(100%, 30rem), 1fr)); }

.card { display: block; }
.card__media {
  position: relative;
  overflow: hidden;
  background: var(--media-bg);
  aspect-ratio: 4 / 3;
  border-radius: var(--radius);
}
.card--tall .card__media { aspect-ratio: 3 / 4; }
.card__media img {
  width: 100%; height: 100%;
  object-fit: contain;
  padding: 4%;
  transition: transform .8s var(--ease), filter .6s var(--ease);
}
.card:hover .card__media img { transform: scale(1.045); }
.card__tag {
  position: absolute;
  border-radius: 100px;
  left: .7rem; top: .7rem;
  background: var(--bg);
  color: var(--fg);
  padding: .25rem .5rem;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: .06em;
  text-transform: uppercase;
}
.card__body {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-top: .7rem;
  border-top: var(--rule-w) solid var(--rule);
  margin-top: .7rem;
}
.card__title {
  font-family: var(--font-display);
  font-variation-settings: "wght" 700;
  font-size: var(--step-1);
  letter-spacing: -0.025em;
  text-transform: uppercase;
  line-height: 1.05;
}
.card:hover .card__title { color: var(--accent); }

/* Filters */
.filters {
  display: flex;
  flex-wrap: wrap;
  gap: .45rem;
  padding-inline: var(--gutter);
  padding-block: 1rem;
}
.filter {
  border: var(--rule-w) solid var(--rule);
  border-radius: 100px;
  padding: .4rem .85rem;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: .06em;
  text-transform: uppercase;
  transition: background-color .3s var(--ease), color .3s var(--ease), border-color .3s var(--ease);
}
.filter:hover { background: var(--tint); }
.filter.is-active {
  background: var(--fg);
  color: var(--bg);
  border-color: var(--fg);
}
.card.is-hidden { display: none; }
.index-row.is-hidden { display: none; }

/* --------------------------------------------------------------------------
   11. About / text blocks
   -------------------------------------------------------------------------- */

.split {
  display: grid;
  grid-template-columns: repeat(12, minmax(0, 1fr));
  gap: var(--gutter);
  padding-inline: var(--gutter);
}
.split__label { grid-column: 1 / span 3; }
.split__body { grid-column: 5 / span 7; }
.split__body p + p { margin-top: 1em; }
.split__body p { max-width: 60ch; }

/* About: portrait on the left, copy on the right. */
.split--portrait .about-portrait { grid-column: 1 / span 4; margin: 0; }
.split--portrait .split__body { grid-column: 6 / span 7; }
.about-portrait img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--media-bg);
  border-radius: var(--radius);
}

@media (max-width: 60rem) {
  .split--portrait .about-portrait { grid-column: 1 / -1; max-width: 22rem; }
  .split--portrait .split__body { grid-column: 1 / -1; }
}

.stat-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: var(--gutter);
  padding-inline: var(--gutter);
}
.stat {
  border-top: var(--rule-w) solid var(--rule);
  padding-top: .75rem;
}
.stat__n {
  font-family: var(--font-display);
  font-variation-settings: "wght" 700;
  font-size: var(--step-3);
  line-height: 1;
  letter-spacing: -0.035em;
}

.def-list { border-top: var(--rule-w) solid var(--rule); }
.def-list__row {
  display: grid;
  grid-template-columns: minmax(6rem, 1fr) 2.2fr;
  gap: var(--gutter);
  padding-block: .75rem;
  border-bottom: var(--rule-w) solid var(--rule);
}

/* --------------------------------------------------------------------------
   12. Project pages
   -------------------------------------------------------------------------- */

.proj-hero {
  padding: calc(var(--nav-h) + clamp(2rem, 6vh, 4.5rem)) var(--gutter) 0;
}
.proj-hero h1 {
  font-family: var(--font-display);
  font-variation-settings: "wght" 800;
  font-size: var(--step-4);
  line-height: .86;
  letter-spacing: -0.04em;
  text-transform: uppercase;
  margin-block: .35em .5em;
}

.proj-cover {
  margin-top: clamp(1.5rem, 4vw, 3rem);
  background: var(--media-bg);
  overflow: hidden;
  border-radius: var(--radius);
}
.proj-cover img { width: 100%; object-fit: cover; }

.media-stack {
  display: grid;
  gap: var(--gutter);
  padding-inline: var(--gutter);
}
.media-stack .pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--gutter);
}
/* --------------------------------------------------------------------------
   Small figures
   A contact-sheet grid for the supporting shots. Screens and details don't
   need half the page each — at this size a run of them reads as one exhibit
   and the page stops being a scroll.
   -------------------------------------------------------------------------- */

.small-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(clamp(10rem, 17vw, 15rem), 1fr));
  gap: clamp(.6rem, 1.4vw, 1.1rem);
  align-items: start;
  margin-bottom: clamp(1.5rem, 4vw, 2.5rem);
}

.small-grid figure.fig { margin: 0; }
.small-grid figure.fig img { box-shadow: inset 0 0 0 1px var(--rule); }
.small-grid figcaption { font-size: var(--step--1); }

/* --------------------------------------------------------------------------
   Live prototypes
   A working thing embedded in the page rather than a picture of one.

   Two rules keep it out of the way. It is inert until you click it, so the
   wheel goes to the page and not into the frame — an embed that swallows
   scrolling is worse than no embed. And its height is capped, because a
   prototype that sets its own height while measuring its own height grows
   forever if anything inside it is sized in vh, which is exactly what happened
   to the channel toolkit.
   -------------------------------------------------------------------------- */

.live { margin: 0 0 clamp(1.5rem, 4vw, 2.5rem); }

.live__frame { position: relative; }

.live__stage {
  position: relative;
  min-width: 0;
}

.live iframe {
  display: block;
  width: 100%;
  max-height: min(80vh, 58rem);
  border: 0;
  background: var(--media-bg);
  outline: 1px solid var(--rule);
  outline-offset: -1px;
}

/* The frame keeps its pointer events from the first paint. Turning them off
   and on again looks tidier in the stylesheet, but a browser that has already
   ruled a frame out of hit-testing does not reliably let the wheel or a drag
   back in when the property flips — the page scrolled and the prototype sat
   there. The veil below is a real element on top of it, so while the embed is
   inert everything lands on the veil and the page keeps the wheel; once it is
   live the veil goes and the prototype gets the lot. */

/* The veil is transparent: you see the prototype, the page keeps the wheel. */
.live__veil {
  position: absolute;
  inset: 0;
  appearance: none;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
  display: block;
}
.live.is-live .live__veil { display: none; }

.live__chip {
  position: absolute;
  right: .75rem;
  bottom: .75rem;
  padding: .4rem .7rem;
  background: var(--accent);
  color: var(--accent-fg);
  font-size: var(--step--1);
  letter-spacing: .06em;
  text-transform: uppercase;
  pointer-events: none;
  transition: opacity .25s var(--ease);
}

.live__veil:hover .live__chip,
.live__veil:focus-visible .live__chip { opacity: .82; }

.live__chip--on { opacity: 0; }
.live.is-live .live__chip--on { opacity: 1; }

.live__veil:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.live figcaption {
  margin-top: .75rem;
  color: var(--fg-60);
  font-size: var(--step--1);
}

@media (max-width: 40rem) {
  .live iframe { min-height: 26rem; max-height: 92vh; }
  .live__chip { right: .5rem; bottom: .5rem; }
}

/* --------------------------------------------------------------------------
   The hang
   Photographs go up small and in clusters, each one stemming off a labelled
   hairline, so a whole archive is a screen or two rather than eight. Prints sit
   at four different heights and hang off different points, which is what stops
   a wall of pictures reading as a spreadsheet. Point at one and it comes
   forward far enough to actually look at.
   -------------------------------------------------------------------------- */

/* Room at the top for a print to be lifted into without covering the writing
   above it — a picture at 1.7 reaches about 70px past where it hangs. */
/* Captions are absolutely positioned and can be wider than the print they
   belong to, so one hanging near the right edge used to push the whole page
   sideways. Clipped on that axis only: a print still lifts and grows upward
   when you point at it, and nothing drags a scrollbar across the page. */
.hang {
  display: grid;
  gap: clamp(1.75rem, 4vw, 3rem);
  padding-top: clamp(1rem, 4vw, 3.5rem);
  overflow-x: clip;
  overflow-y: visible;
}

.cluster {
  display: grid;
  grid-template-columns: minmax(7rem, 11rem) 1px 1fr;
  gap: clamp(.9rem, 2.4vw, 1.75rem);
  align-items: start;
}

.cluster__head { display: grid; gap: .3rem; position: relative; padding-top: .15rem; }

.cluster__label { color: var(--accent); white-space: normal; }

/* the branch from the label across to the spine */
.cluster__head::after {
  content: "";
  position: absolute;
  top: .55rem;
  left: 100%;
  width: clamp(.9rem, 2.4vw, 1.75rem);
  height: 1px;
  background: var(--rule);
}

.cluster__note { font-size: var(--step--1); color: var(--fg-40); max-width: 22ch; }

.cluster__spine { display: block; width: 1px; height: 100%; background: var(--rule); }

.cluster__shots {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: clamp(.5rem, 1.2vw, .9rem);
}

.shot {
  --h: clamp(7rem, 11vw, 11.5rem);
  position: relative;
  margin: 0;
  flex: 0 0 auto;
  height: var(--h);
  transition: transform .35s var(--ease);
  transform-origin: center bottom;
}

/* four heights and four hanging points, cycled — a wall, not a row */
.shot--0 { --h: clamp(7rem, 11vw, 11.5rem); margin-top: 0; }
.shot--1 { --h: clamp(5.6rem, 8.6vw, 9rem);  margin-top: clamp(.8rem, 2vw, 1.6rem); }
.shot--2 { --h: clamp(8.2rem, 13vw, 13.5rem); margin-top: 0; }
.shot--3 { --h: clamp(6.4rem, 9.8vw, 10.2rem); margin-top: clamp(.4rem, 1.2vw, 1rem); }

/* The picture is a button now. Everything a button brings with it goes, and
   the cursor says what it does. */
.shot__open {
  display: block;
  height: 100%;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  cursor: zoom-in;
  -webkit-tap-highlight-color: transparent;
}
.shot__open:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.shot img {
  display: block;
  height: 100%;
  width: auto;
  max-width: 100%;
  background: var(--media-bg);
  box-shadow: inset 0 0 0 1px var(--rule);
}

.shot figcaption {
  position: absolute;
  left: 0;
  top: calc(100% + .4rem);
  width: max-content;
  max-width: 14rem;
  font-size: var(--step--1);
  color: var(--fg-60);
  opacity: 0;
  transition: opacity .25s var(--ease);
  pointer-events: none;
}

/* Forward far enough to read, and above its neighbours while it is there. */
.shot:hover,
.shot:focus-within { transform: scale(1.7); z-index: 3; }
.shot:hover { z-index: 4; }
.shot:hover figcaption,
.shot:focus-within figcaption { opacity: 1; }

.shot:hover img,
.shot:focus-within img { box-shadow: 0 1.2rem 2.5rem -1rem rgba(0, 0, 0, .45); }

.cluster:hover .shot:not(:hover) { opacity: .55; }
.shot { transition: transform .35s var(--ease), opacity .35s var(--ease); }

@media (max-width: 52rem) {
  .cluster { grid-template-columns: 1fr; gap: .75rem; }
  .cluster__spine { width: 100%; height: 1px; }
  .cluster__head::after { display: none; }
  .shot { --h: clamp(6rem, 26vw, 9rem) !important; margin-top: 0 !important; }
  .shot:hover, .shot:focus-within { transform: scale(1.35); }
  /* No hover on a phone, and an absolutely positioned caption still counts
     towards the page's scrollable width — which is a sideways scrollbar for
     something nobody can see. */
  .shot figcaption { display: none; }
}

@media (prefers-reduced-motion: reduce) {
  .shot, .shot:hover, .shot:focus-within { transition: opacity .2s linear; transform: none; }
}

figure.fig { background: var(--media-bg); border-radius: var(--radius); overflow: hidden; }
figure.fig img { width: 100%; }
figure.fig video {
  width: 100%;
  height: auto;
  display: block;
  background: var(--media-bg);
}

figure.fig { background: transparent; }
figure.fig > img, figure.fig > video { border-radius: var(--radius); background: var(--media-bg); }
figure.fig figcaption {
  padding-top: .55rem;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: .05em;
  color: var(--fg-60);
  text-transform: uppercase;
}

.outbound {
  display: grid;
  grid-template-columns: minmax(6rem, 10rem) minmax(0, 1fr) auto;
  align-items: baseline;
  gap: .4rem 1.5rem;
  padding: clamp(1.1rem, 2.5vw, 1.6rem) clamp(.75rem, 2vw, 1.25rem);
  border-top: var(--rule-w) solid var(--rule);
  border-bottom: var(--rule-w) solid var(--rule);
  transition: background-color .35s var(--ease), color .35s var(--ease);
}

.outbound:hover,
.outbound:focus-visible { background: var(--accent); color: var(--accent-fg); }

.outbound__label { color: var(--fg-60); }
.outbound:hover .outbound__label,
.outbound:hover .outbound__note { color: color-mix(in srgb, var(--accent-fg) 78%, transparent); }

.outbound__name {
  font-family: var(--font-display);
  font-weight: 700;
  font-size: var(--step-1);
  letter-spacing: -0.01em;
}

.outbound__note {
  grid-column: 2;
  color: var(--fg-60);
  font-size: var(--step--1);
}

.outbound__go {
  grid-row: 1 / span 2;
  grid-column: 3;
  align-self: center;
  font-size: var(--step-1);
}

@media (max-width: 40rem) {
  .outbound {
    grid-template-columns: minmax(0, 1fr) auto;
    gap: .3rem 1rem;
  }
  .outbound__label { grid-column: 1; }
  .outbound__name { grid-column: 1; }
  .outbound__note { grid-column: 1; }
  .outbound__go { grid-row: 1 / span 3; grid-column: 2; }
}

.next-project {
  display: block;
  border-top: var(--rule-w) solid var(--rule);
  padding: clamp(2.5rem, 6vw, 5rem) var(--gutter);
  transition: background-color .4s var(--ease), color .4s var(--ease);
}
.next-project:hover { background: var(--accent); color: var(--accent-fg); }
.next-project h2 {
  font-family: var(--font-display);
  font-variation-settings: "wght" 800;
  font-size: var(--step-3);
  line-height: .9;
  letter-spacing: -0.04em;
  text-transform: uppercase;
  margin-top: .3em;
}

/* --------------------------------------------------------------------------
   13. Footer
   -------------------------------------------------------------------------- */

.footer {
  border-top: var(--rule-w) solid var(--rule);
  padding: clamp(2.5rem, 6vw, 5rem) var(--gutter) 1.25rem;
}
.footer__cta {
  font-family: var(--font-display);
  font-variation-settings: "wght" 800;
  font-size: var(--step-4);
  line-height: .86;
  letter-spacing: -0.04em;
  text-transform: uppercase;
  display: inline-block;
}
.footer__cta:hover { color: var(--accent); }
.footer__cols {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: var(--gutter);
  margin-top: clamp(2rem, 5vw, 4rem);
  padding-top: 1rem;
  border-top: var(--rule-w) solid var(--rule);
}
.footer__cols li + li { margin-top: .3rem; }
.footer__base {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.5rem;
  justify-content: space-between;
  margin-top: clamp(2rem, 5vw, 3.5rem);
  padding-top: .85rem;
  border-top: var(--rule-w) solid var(--rule);
}

/* --------------------------------------------------------------------------
   14. Reveal on scroll
   -------------------------------------------------------------------------- */

.reveal {
  opacity: 0;
  transform: translateY(1.6rem);
  transition: opacity .8s var(--ease), transform .8s var(--ease);
}
.reveal.is-in { opacity: 1; transform: none; }

/* --------------------------------------------------------------------------
   15. Contact form
   -------------------------------------------------------------------------- */

.form { display: grid; gap: 1.25rem; max-width: 42rem; }
.field { display: grid; gap: .35rem; }
.field input, .field textarea {
  font: inherit;
  color: inherit;
  background: transparent;
  border: 0;
  border-bottom: var(--rule-w) solid var(--rule);
  padding: .6rem 0;
  transition: border-color .3s var(--ease);
}
.field input:focus, .field textarea:focus {
  outline: none;
  border-color: var(--accent);
}
.field textarea { resize: vertical; min-height: 7rem; }

.btn {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  border: var(--rule-w) solid var(--fg);
  border-radius: 100px;
  padding: .7rem 1.4rem;
  font-family: var(--font-mono);
  font-size: var(--step--1);
  letter-spacing: .07em;
  text-transform: uppercase;
  justify-self: start;
  transition: background-color .35s var(--ease), color .35s var(--ease), border-color .35s var(--ease);
}
.btn:hover { background: var(--fg); color: var(--bg); }
.btn--accent { border-color: var(--accent); color: var(--accent); }
.btn--accent:hover { background: var(--accent); color: var(--accent-fg); }

/* --------------------------------------------------------------------------
   16. Responsive
   -------------------------------------------------------------------------- */

@media (max-width: 900px) {
  .nav__mid { display: none; }
  .burger { display: flex; }
  .hero__bottom .col-a { grid-column: 1 / -1; }
  .hero__bottom .col-b { grid-column: 1 / span 6; }
  .hero__bottom .col-c { grid-column: 7 / span 6; }
  .split__label { grid-column: 1 / -1; }
  .split__body { grid-column: 1 / -1; }
  .index-row > a { grid-template-columns: 2.5rem minmax(0, 1fr) auto; }
  .index-row .meta { display: none; }
  .media-stack .pair { grid-template-columns: 1fr; }
}

@media (max-width: 560px) {
  .nav__end .clock { display: none; }
  .index-row > a { grid-template-columns: 2rem minmax(0, 1fr); }
  .index-row .go { display: none; }
  .hero__type .outline { -webkit-text-stroke-width: 1px; }
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
  .reveal { opacity: 1; transform: none; }
  .hero__type .line > span { transform: none; }
}

@media print {
  .nav, .menu, .curtain, .marquee, .preview { display: none !important; }
}


/* ------------------------------------------------------------------ the lightbox
   A picture opened off the wall. It is the same small file to begin with,
   scaled up from exactly where it was hanging, so nothing has to be downloaded
   before it moves; the full-size file is swapped into the same box as soon as
   it arrives. Closing puts it back where it came from. */
.lb {
  position: fixed;
  inset: 0;
  z-index: 200;
  touch-action: none;
}
.lb[hidden] { display: none; }

.lb__veil {
  position: absolute;
  inset: 0;
  background: var(--bg);
  opacity: 0;
  transition: opacity .32s var(--ease);
}
.lb.is-open .lb__veil { opacity: .985; }

.lb__img {
  position: fixed;
  margin: 0;
  display: block;
  background: var(--media-bg);
  box-shadow: 0 2rem 5rem -1.5rem rgba(0, 0, 0, .55);
  transform-origin: top left;
  will-change: transform;
}

.lb__cap,
.lb__btn,
.lb__count {
  position: fixed;
  opacity: 0;
  transition: opacity .25s var(--ease);
}
.lb.is-open .lb__cap,
.lb.is-open .lb__btn,
.lb.is-open .lb__count { opacity: 1; }

.lb__cap {
  left: 50%;
  transform: translateX(-50%);
  bottom: 1.4rem;
  margin: 0;
  max-width: min(46rem, 88vw);
  text-align: center;
  font-size: var(--step--1);
  color: var(--fg-60);
}

.lb__btn {
  appearance: none;
  border: 0;
  background: none;
  font: inherit;
  font-size: var(--step--1);
  color: var(--fg-60);
  cursor: pointer;
  padding: .7rem .9rem;
  line-height: 1;
}
.lb__btn:hover { color: var(--fg); }
.lb__btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.lb__btn--close { top: 1rem; right: 1rem; }
.lb__btn--prev  { top: 50%; left: .2rem;  transform: translateY(-50%); font-size: 1.4rem; }
.lb__btn--next  { top: 50%; right: .2rem; transform: translateY(-50%); font-size: 1.4rem; }
.lb__count      { top: 1.15rem; left: 1.2rem; font-size: var(--step--1); color: var(--fg-40); }

/* While it is loading the big one, the small one is what you are looking at,
   so say so rather than leaving the corner silent. */
.lb.is-loading .lb__count::after { content: " \00b7 loading"; }

@media (max-width: 52rem) {
  .lb__btn--prev, .lb__btn--next { display: none; }
  .lb__cap { bottom: .9rem; font-size: var(--step--2, .72rem); }
}

@media (prefers-reduced-motion: reduce) {
  .lb__veil, .lb__img, .lb__cap, .lb__btn, .lb__count { transition: none !important; }
}

/* ------------------------------------------------------------- summer work
   A hidden, temporary section. Everything made over one summer and everything
   gone to look at, laid out as a contact sheet rather than a portfolio: the
   pictures keep their own shape instead of being cropped into a grid, so a
   panorama stays a panorama and a phone screenshot stays tall. Columns do the
   packing, which means the order reads down a column rather than across a row
   — right for a pile of work, wrong for anything that has to be sequential. */

.sw-head { padding-top: clamp(5rem, 12vw, 9rem); }
.sw-head .wrap { display: grid; gap: .9rem; }
.sw-head .lead { max-width: 46ch; }

.sw-section { padding-top: clamp(2.5rem, 6vw, 4.5rem); padding-bottom: clamp(1rem, 3vw, 2rem); }
.sw-section__head { margin-bottom: clamp(1.4rem, 3vw, 2.4rem); }
.sw-section__head h2 { font-size: var(--step-3); }

/* The half that is other people's work says so before you reach any of it. */
.sw-section__intro {
  margin-top: .7rem;
  max-width: 54ch;
  color: var(--fg-60);
  border-left: 2px solid var(--accent);
  padding-left: .9rem;
}

.sw-band { margin-top: clamp(2rem, 5vw, 3.2rem); }

/* The writing above a band's pictures. Held to a readable measure rather than
   run across the full width of the grid under it. */
.sw-text {
  position: relative;
  max-width: 58ch;
  margin: 0 0 clamp(1.1rem, 2.5vw, 1.8rem);
}
.sw-text p { margin: 0 0 .7rem; }
.sw-text p:last-child { margin-bottom: 0; }

/* What was seen, where and when. The brief asks for it, and a claim about
   somebody else's work is worth a link to the thing itself. */
.sw-links { margin: 0 0 clamp(1.1rem, 2.5vw, 1.8rem); padding: 0; list-style: none; max-width: 58ch; }
.sw-links li { padding: .4rem 0; border-top: 1px solid var(--rule); }
.sw-links li:last-child { border-bottom: 1px solid var(--rule); }
.sw-links a { font-size: var(--step-0); }
.sw-links .when { display: block; font-size: var(--step--1); color: var(--fg-40); }

.sw-draft {
  display: inline-block;
  margin-bottom: .6rem;
  padding: .12rem .45rem;
  font-size: var(--step--1);
  color: var(--accent-fg);
  background: var(--accent);
}

/* A band's own reference sits under its work, indented off a rule, so it reads
   as belonging to that band rather than standing beside it as equal. */
.sw-research {
  margin-top: clamp(1.4rem, 3vw, 2.2rem);
  padding-left: clamp(.8rem, 2vw, 1.4rem);
  border-left: 2px solid var(--rule);
}
.sw-band__head--sub { border-bottom-style: dashed; }
.sw-research .sw-grid { columns: 5; }
@media (max-width: 68rem) { .sw-research .sw-grid { columns: 4; } }
@media (max-width: 40rem) { .sw-research .sw-grid { columns: 2; } }

.sw-band__head {
  display: flex;
  align-items: baseline;
  gap: .9rem;
  padding-bottom: .55rem;
  margin-bottom: .9rem;
  border-bottom: 1px solid var(--rule);
}
.sw-band__label { color: var(--accent); font-size: var(--step--1); margin: 0; }
.sw-band__note  { color: var(--fg-40); font-size: var(--step--1); margin: 0; }
.sw-band__count { margin-left: auto; flex: none; }

.sw-grid {
  columns: 4;
  column-gap: clamp(.5rem, 1.1vw, .8rem);
}

.sw-shot {
  break-inside: avoid;
  margin: 0 0 clamp(.5rem, 1.1vw, .8rem);
  position: relative;
}

/* On this page the button wraps a picture that sets its own height from its
   own proportions, so the fixed height the wall gives it has to come off. */
.sw-shot .shot__open {
  height: auto;
  width: 100%;
  cursor: zoom-in;
}

.sw-shot img {
  display: block;
  width: 100%;
  height: auto;
  background: var(--media-bg);
  box-shadow: inset 0 0 0 1px var(--rule);
}

.sw-shot figcaption {
  position: absolute;
  inset: auto 0 0 0;
  padding: .45rem .55rem;
  font-size: 0.7rem;
  line-height: 1.25;
  color: #fff;
  background: linear-gradient(transparent, rgba(0, 0, 0, .72));
  opacity: 0;
  transition: opacity .2s var(--ease);
  pointer-events: none;
}
.sw-shot:hover figcaption,
.sw-shot:focus-within figcaption { opacity: 1; }

@media (max-width: 68rem) { .sw-grid { columns: 3; } }
@media (max-width: 40rem) {
  .sw-grid { columns: 2; }
  /* The note is too long to sit on the label's line here, so it drops under
     it and the count stays up with the label where it can be read. */
  .sw-band__head { display: grid; grid-template-columns: 1fr auto; gap: .15rem .9rem; }
  .sw-band__label { grid-row: 1; grid-column: 1; }
  .sw-band__count { grid-row: 1; grid-column: 2; margin-left: 0; }
  .sw-band__note { grid-row: 2; grid-column: 1 / -1; }
  /* no hover on a phone, so the caption would never be readable anyway */
  .sw-shot figcaption { display: none; }
}

/* --------------------------------------------------------------- the board
   The way in, at the very bottom of every page under the copyright line.
   Quiet on purpose: it is not navigation, and nobody is meant to go looking
   for it. Full size and full contrast the moment it is pointed at or tabbed
   to, so it is a real control rather than a hidden one. */
.board {
  display: block;
  width: 16px;
  margin: clamp(1.6rem, 4vw, 2.6rem) auto 0;
  color: var(--fg);
  opacity: .12;
  transform: rotate(-14deg);
  transition: opacity .25s var(--ease), transform .35s var(--ease);
}
.board:hover { opacity: .85; transform: rotate(0deg) translateY(-2px); }
.board:focus-visible { opacity: .9; outline: 2px solid var(--accent); outline-offset: 6px; }
.board svg { display: block; }

/* On a touch screen there is no pointing at it first, so it has to be findable
   as it sits — still quiet, just not invisible. */
@media (hover: none) { .board { opacity: .3; } }

@media (prefers-reduced-motion: reduce) {
  .board, .sw-shot figcaption { transition: none; }
}

/* ---------------------------------------------------------- the deck mockup
   Five pages at 16:9, drawn empty. Each page is a real 16:9 frame that scales
   with the column, and everything inside it is sized in cqw — container query
   units — so the whole layout shrinks as one piece and the proportions you see
   here are the proportions you get on a projector. Type set in px would grow
   relative to the page as the page got smaller, which is exactly the mistake a
   layout mockup exists to prevent. */

.sw-deck { padding-bottom: clamp(2rem, 5vw, 4rem); }

.pg { margin: 0 0 clamp(1.6rem, 4vw, 2.8rem); }

.pg-cap {
  display: flex;
  align-items: baseline;
  gap: .7rem;
  padding-bottom: .45rem;
  margin-bottom: .55rem;
  border-bottom: 1px solid var(--rule);
}
.pg-cap__n { color: var(--accent); }
.pg-cap__name { font-size: var(--step-0); }
.pg-cap__ratio { margin-left: auto; }

.pg-page {
  container-type: inline-size;
  aspect-ratio: 16 / 9;
  background: var(--bg-2);
  box-shadow: inset 0 0 0 1px var(--rule);
}

.pg-grid {
  display: grid;
  gap: 1.6cqw;
  padding: 3cqw 3.2cqw;
  height: 100%;
}

/* ---- writing already in place, so the proportions are honest */
.pg-txt { display: flex; flex-direction: column; justify-content: flex-start; min-width: 0; }
.pg-txt p { margin: 0 0 .9cqw; font-size: calc(1.32cqw * var(--pg-tx, 1)); line-height: 1.45; color: var(--fg-60); }
.pg-txt p:last-child { margin-bottom: 0; }

.pg-h {
  margin: 0 0 1cqw;
  font-family: var(--font-display, inherit);
  font-size: calc(3cqw * var(--pg-tx, 1));
  line-height: 1.02;
  letter-spacing: -0.01em;
  color: var(--fg);
}
.pg-sub { font-size: calc(1.7cqw * var(--pg-tx, 1)) !important; color: var(--fg) !important; margin-bottom: 1.4cqw !important; }
.pg-foot { margin-top: auto !important; padding-top: 1cqw; font-size: calc(1.05cqw * var(--pg-tx, 1)) !important; color: var(--fg-40) !important; }

/* the half that is somebody else's work stays marked, even at this size */
.pg-txt--ref .pg-h { color: var(--accent); font-size: calc(1.7cqw * var(--pg-tx, 1)); }
.pg-txt--note p { font-size: calc(1.22cqw * var(--pg-tx, 1)); color: var(--fg); }

/* ---- the empty boxes */
.pg-box { display: flex; align-items: center; justify-content: center; min-width: 0; min-height: 0; }

.pg-box__inner {
  width: 100%;
  height: 100%;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .5cqw;
  padding: .8cqw;
  text-align: center;
  background: var(--tint);
  outline: 1px dashed var(--rule);
  outline-offset: -1px;
}
/* a box given a shape keeps it, and takes its width from its height */
.pg-box__inner[style*="aspect-ratio"] { width: auto; }

.pg-box__label { font-size: 1.1cqw; color: var(--fg-60); }
.pg-box__hint  { font-size: 1cqw; line-height: 1.3; color: var(--fg-40); max-width: 22ch; }

.pg-box--ref .pg-box__inner { outline-color: var(--accent); }
.pg-box--ref .pg-box__label { color: var(--accent); }

/* Below about a tablet the type inside a 16:9 page is too small to read at all,
   so the page stops pretending and lets itself get taller than 16:9. The boxes
   keep their relative sizes; only the aspect gives. */
@media (max-width: 46rem) {
  .pg-page { aspect-ratio: auto; }
  .pg-grid { grid-template-columns: 1fr !important; grid-template-rows: none !important; grid-template-areas: none !important; height: auto; padding: 5vw; gap: 3vw; }
  .pg-grid > * { grid-area: auto !important; }
  .pg-box__inner { aspect-ratio: 16 / 10 !important; width: 100% !important; }
  .pg-txt p { font-size: var(--step--1); }
  .pg-h { font-size: var(--step-1); }
  .pg-sub { font-size: var(--step-0) !important; }
  .pg-foot { font-size: var(--step--1) !important; }
  .pg-box__label { font-size: var(--step--1); }
  .pg-box__hint { font-size: var(--step--1); }
}

/* ------------------------------------------------------------ design boards
   One per app, built out of the components the rest of the site already uses:
   section-head for the title bar, split for label-and-body rows, def-list for
   the specs and small-grid for the screens. The only new thing is the swatch
   itself, because nothing on the site shows a colour as a colour — and a
   palette is only useful if you can see the colour and read what it is for in
   the same glance, so it is a block of the colour with its name, its hex and
   its definition set against it rather than a grid of squares and a key. */

.sw-boards { padding-bottom: clamp(2rem, 5vw, 4rem); }

/* a lighter section rhythm than section-pad: these stack several to a board */
.section-pad-s { padding-block: clamp(1rem, 2.2vw, 1.6rem); }

.bd { margin-top: clamp(2rem, 5vw, 3.5rem); }
.bd .def-list { margin-bottom: clamp(.5rem, 1.5vw, 1rem); }

.bd__chips {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11.5rem, 1fr));
  gap: .5rem;
}

.sw-chip { display: flex; align-items: stretch; gap: .55rem; min-width: 0; }

/* A ring rather than a border, so a swatch the same colour as the page still
   reads as a swatch — white on white and near-black on black both happen. */
.sw-chip__col {
  flex: none;
  width: 2.5rem;
  border-radius: 2px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--fg) 22%, transparent);
}

.sw-chip__meta { display: grid; gap: .05rem; min-width: 0; align-content: center; }
.sw-chip__name { font-size: var(--step--1); line-height: 1.25; }
.sw-chip__hex  { font-size: var(--step--1); color: var(--fg-60); }
.sw-chip__def  { font-size: var(--step--1); color: var(--fg-40); line-height: 1.3; }

/* A screen in a small-grid opens full size, so the picture is a button — the
   button has to disappear and leave the figure exactly as it was. */
.small-grid .fig .shot__open,
.pair .fig .shot__open { display: block; width: 100%; height: auto; cursor: zoom-in; }

/* A board's screens are the evidence for everything above them, so they run
   larger than a contact sheet — three or four to a row rather than six. The
   hairline matters more here than elsewhere: half of these screenshots are a
   white interface, and without an edge they dissolve into the page. */
.bd .small-grid {
  grid-template-columns: repeat(auto-fit, minmax(clamp(11rem, 20vw, 17rem), 1fr));
  gap: clamp(.7rem, 1.6vw, 1.2rem);
}
.bd .small-grid .fig { background: none; }
.bd .small-grid .fig img {
  box-shadow: inset 0 0 0 1px var(--rule), 0 .4rem 1.4rem -.8rem rgba(0, 0, 0, .3);
}
.bd .small-grid figcaption { margin-top: .4rem; }

@media (max-width: 52rem) {
  .bd__chips { grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); }
}

/* ---------------------------------------------------------- the deck mockup
   Five pages at 16:9, drawn empty. Each page is a real 16:9 frame that scales
   with the column, and everything inside it is sized in cqw — container query
   units — so the whole layout shrinks as one piece and the proportions you see
   here are the proportions you get on a projector. Type set in px would grow
   relative to the page as the page got smaller, which is exactly the mistake a
   layout mockup exists to prevent. */

.sw-deck { padding-bottom: clamp(2rem, 5vw, 4rem); }

.pg { margin: 0 0 clamp(1.6rem, 4vw, 2.8rem); }

.pg-cap {
  display: flex;
  align-items: baseline;
  gap: .7rem;
  padding-bottom: .45rem;
  margin-bottom: .55rem;
  border-bottom: 1px solid var(--rule);
}
.pg-cap__n { color: var(--accent); }
.pg-cap__name { font-size: var(--step-0); }
.pg-cap__ratio { margin-left: auto; }

.pg-page {
  container-type: inline-size;
  aspect-ratio: 16 / 9;
  background: var(--bg-2);
  box-shadow: inset 0 0 0 1px var(--rule);
}

.pg-grid {
  display: grid;
  gap: 1.6cqw;
  padding: 3cqw 3.2cqw;
  height: 100%;
}

/* ---- writing already in place, so the proportions are honest */
.pg-txt { display: flex; flex-direction: column; justify-content: flex-start; min-width: 0; }
.pg-txt p { margin: 0 0 .9cqw; font-size: calc(1.32cqw * var(--pg-tx, 1)); line-height: 1.45; color: var(--fg-60); }
.pg-txt p:last-child { margin-bottom: 0; }

.pg-h {
  margin: 0 0 1cqw;
  font-family: var(--font-display, inherit);
  font-size: calc(3cqw * var(--pg-tx, 1));
  line-height: 1.02;
  letter-spacing: -0.01em;
  color: var(--fg);
}
.pg-sub { font-size: calc(1.7cqw * var(--pg-tx, 1)) !important; color: var(--fg) !important; margin-bottom: 1.4cqw !important; }
.pg-foot { margin-top: auto !important; padding-top: 1cqw; font-size: calc(1.05cqw * var(--pg-tx, 1)) !important; color: var(--fg-40) !important; }

/* the half that is somebody else's work stays marked, even at this size */
.pg-txt--ref .pg-h { color: var(--accent); font-size: calc(1.7cqw * var(--pg-tx, 1)); }
.pg-txt--note p { font-size: calc(1.22cqw * var(--pg-tx, 1)); color: var(--fg); }

/* ---- the empty boxes */
.pg-box { display: flex; align-items: center; justify-content: center; min-width: 0; min-height: 0; }

.pg-box__inner {
  width: 100%;
  height: 100%;
  max-height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: .5cqw;
  padding: .8cqw;
  text-align: center;
  background: var(--tint);
  outline: 1px dashed var(--rule);
  outline-offset: -1px;
}
/* a box given a shape keeps it, and takes its width from its height */
.pg-box__inner[style*="aspect-ratio"] { width: auto; }

.pg-box__label { font-size: 1.1cqw; color: var(--fg-60); }
.pg-box__hint  { font-size: 1cqw; line-height: 1.3; color: var(--fg-40); max-width: 22ch; }

.pg-box--ref .pg-box__inner { outline-color: var(--accent); }
.pg-box--ref .pg-box__label { color: var(--accent); }

/* Below about a tablet the type inside a 16:9 page is too small to read at all,
   so the page stops pretending and lets itself get taller than 16:9. The boxes
   keep their relative sizes; only the aspect gives. */
@media (max-width: 46rem) {
  .pg-page { aspect-ratio: auto; }
  .pg-grid { grid-template-columns: 1fr !important; grid-template-rows: none !important; grid-template-areas: none !important; height: auto; padding: 5vw; gap: 3vw; }
  .pg-grid > * { grid-area: auto !important; }
  .pg-box__inner { aspect-ratio: 16 / 10 !important; width: 100% !important; }
  .pg-txt p { font-size: var(--step--1); }
  .pg-h { font-size: var(--step-1); }
  .pg-sub { font-size: var(--step-0) !important; }
  .pg-foot { font-size: var(--step--1) !important; }
  .pg-box__label { font-size: var(--step--1); }
  .pg-box__hint { font-size: var(--step--1); }
}

/* ------------------------------------------------------------ design boards
   One per app. A palette is only useful if you can see the colour and read
   what it is for at the same time, so a swatch is a block of the colour with
   its name, its hex and its definition set against it rather than a grid of
   squares you then have to decode from a key underneath. */

.sw-boards { padding-bottom: clamp(2rem, 5vw, 4rem); }

.bd {
  margin-top: clamp(2.2rem, 5vw, 3.6rem);
  padding-top: clamp(1.2rem, 3vw, 2rem);
  border-top: 2px solid var(--rule);
}

.bd__head { max-width: 60ch; margin-bottom: clamp(1.2rem, 3vw, 2rem); }
.bd__name { font-size: var(--step-2); margin: 0 0 .4rem; }
.bd__line { margin: 0 0 .45rem; color: var(--fg-60); }
.bd__src  { font-size: var(--step--1); }

.bd__body {
  display: grid;
  grid-template-columns: minmax(0, 1.55fr) minmax(0, 1fr);
  gap: clamp(1.2rem, 3vw, 2.4rem);
  align-items: start;
}

.bd__cols { display: grid; gap: clamp(1rem, 2.2vw, 1.5rem); }

.bd__glabel {
  margin: 0 0 .5rem;
  font-size: var(--step--1);
  color: var(--accent);
}

.bd__chips {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(11.5rem, 1fr));
  gap: .5rem;
}

.sw-chip { display: flex; align-items: stretch; gap: .55rem; min-width: 0; }

/* A ring rather than a border, so a swatch the same colour as the page still
   reads as a swatch — white on white and near-black on black both happen here. */
.sw-chip__col {
  flex: none;
  width: 2.5rem;
  border-radius: 2px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--fg) 22%, transparent);
}

.sw-chip__meta { display: grid; gap: .05rem; min-width: 0; align-content: center; }
.sw-chip__name { font-size: var(--step--1); line-height: 1.25; }
.sw-chip__hex  { font-size: var(--step--1); color: var(--fg-60); }
.sw-chip__def  { font-size: var(--step--1); color: var(--fg-40); line-height: 1.3; }

.bd__specs { margin: 0; display: grid; gap: .6rem; }
.bd__specs div { display: grid; gap: .1rem; padding-bottom: .55rem; border-bottom: 1px solid var(--rule); }
.bd__specs dt { font-size: var(--step--1); color: var(--accent); }
.bd__specs dd { margin: 0; font-size: var(--step--1); color: var(--fg-60); }

.bd__notes { grid-column: 1 / -1; display: grid; gap: .6rem; max-width: 68ch; }
.bd__notes p { margin: 0; color: var(--fg-60); }

.bd__shots {
  margin-top: clamp(1rem, 2.4vw, 1.6rem);
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: clamp(.5rem, 1.1vw, .8rem);
  align-items: start;
}

@media (max-width: 52rem) {
  .bd__body { grid-template-columns: 1fr; }
  .bd__chips { grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr)); }
}

/* -------------------------------------------------------------------- reel
   The homepage opens on the work now, one piece at a time, where the words
   Graphic & Media Design used to be. It fills whatever the hero has left
   between the greeting and the footer row, so it keeps the page's shape. */

.reel {
  position: relative;
  z-index: 1;
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  padding: clamp(1rem, 3vh, 2rem) var(--gutter) 1rem;
  gap: .7rem;
}

.reel__stage {
  position: relative;
  flex: 1;
  min-height: 0;
  background: var(--bg-2);
  overflow: hidden;
}

/* Every frame is stacked in the same box and cross-faded. Opacity is the only
   thing that moves, which the compositor can do on its own. */
.reel__slide {
  position: absolute;
  inset: 0;
  display: block;
  opacity: 0;
  visibility: hidden;
  transition: opacity .35s var(--ease), visibility 0s linear .35s;
}
.reel__slide.is-on { opacity: 1; visibility: visible; transition: opacity .35s var(--ease); }
.reel__slide:focus-visible { outline: 2px solid var(--accent); outline-offset: -4px; }

.reel__slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* The label has to stay readable over a photograph and over a white poster,
   so it sits on a scrim of its own rather than trusting the picture. */
.reel__meta {
  position: absolute;
  left: 0;
  bottom: 0;
  right: 0;
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: .3rem .9rem;
  padding: clamp(2.5rem, 8vh, 5rem) clamp(.9rem, 2vw, 1.4rem) clamp(.8rem, 2vh, 1.2rem);
  color: #fff;
  background: linear-gradient(transparent, rgba(0, 0, 0, .78));
  pointer-events: none;
}
.reel__num   { color: rgba(255, 255, 255, .6); font-size: var(--step--1); }
.reel__title { font-family: var(--font-display); font-size: var(--step-2); line-height: 1; }
.reel__disc  { color: rgba(255, 255, 255, .72); font-size: var(--step--1); margin-left: auto; }

.reel__bar { display: flex; align-items: center; gap: 1rem; }
.reel__ticks { display: flex; gap: .4rem; flex: 1; }

.reel__tick {
  appearance: none; border: 0; background: none; cursor: pointer;
  padding: .45rem .1rem; flex: 1; max-width: 4.5rem;
}
.reel__tick i {
  display: block; height: 2px; background: var(--rule);
  transition: background .3s var(--ease);
}
.reel__tick.is-on i { background: var(--accent); }
.reel__tick:hover i { background: var(--fg-60); }
.reel__tick:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.reel__pause {
  appearance: none; border: 0; background: none; cursor: pointer;
  font-size: var(--step--1); color: var(--fg-40); padding: .35rem 0;
}
.reel__pause:hover { color: var(--fg); }
.reel__pause:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

@media (max-width: 40rem) {
  .reel__title { font-size: var(--step-1); }
  .reel__disc { margin-left: 0; width: 100%; }
}

/* Asked not to be moved: the first piece stays put and the ticks become the
   way through. The control says so rather than lying about being paused. */
@media (prefers-reduced-motion: reduce) {
  .reel__slide { transition: none; }
}

/* ------------------------------------------------------- pictures on a slide
   A slide is a 16:9 container query, so everything inside it is sized in cqw
   and the whole page shrinks as one piece. A picture fills its cell and is
   cropped to it — a slide is a fixed frame, and the alternative is letterboxed
   grey down the sides of half the grid. The cutouts (the app mockups) are the
   exception: they have no background left, so they are fitted, not cropped,
   and they sit on whatever the page is. */

.pg-shot {
  margin: 0;
  min-width: 0;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: .5cqw;
}

.pg-shot .shot__open {
  flex: 1 1 auto;
  display: block;
  min-height: 0;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
  position: relative;
}

.pg-shot img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: color-mix(in srgb, var(--fg) 6%, transparent);
}

/* Half of these are white interfaces on a white page; without an edge they
   dissolve into it. */
.pg-shot:not(.pg-shot--cut) img { box-shadow: inset 0 0 0 1px var(--rule); }

.pg-shot figcaption {
  font-size: calc(.95cqw * var(--pg-tx, 1));
  line-height: 1.3;
  color: var(--fg-40);
  flex: 0 0 auto;
}

/* Other people's work is marked where it sits, not in a key underneath. */
.pg-shot--ref img { box-shadow: inset 0 0 0 1px var(--accent); }
.pg-shot--ref figcaption { color: var(--accent); }

/* A cutout has had its background removed, so cropping it would cut the
   phones in half. */
.pg-shot--cut img { object-fit: contain; background: none; }

.pg-shot .shot__open:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

@media (max-width: 52rem) {
  .pg-shot img { height: auto; aspect-ratio: 4 / 3; }
  .pg-shot--cut img { aspect-ratio: 16 / 9; }
  .pg-shot figcaption { font-size: var(--step--1); }
  .pg-shot { gap: .4rem; }
}

/* ------------------------------------------------------ the deck, version 2
   The plain one, and the one that carries his own pages. The frame is 16:9,
   because that is what a projector gives you. The words take a column on the
   left, where they are on every one of his pages, and each picture is placed
   absolutely at the frame it had in InDesign, mapped into the slide by
   pdfplace.py. Everything is in cqw against the page's own container query,
   so the whole composition scales as one piece — on screen, and at whatever
   size the PDF export asks for. */

.sw-deck--plain { padding-bottom: clamp(2rem, 5vw, 4rem); }

.pg2 { margin: 0 0 clamp(1.6rem, 4vw, 2.8rem); }

.pg2-cap {
  display: flex;
  align-items: baseline;
  gap: .7rem;
  padding-bottom: .45rem;
}
.pg2-cap__n { color: var(--accent); }
.pg2-cap__name { font-size: var(--step-0); }

.pg2-page {
  container-type: inline-size;
  aspect-ratio: 16 / 9;
  background: var(--bg-2);
  box-shadow: inset 0 0 0 1px var(--rule);
  /* The pictures run past the edges so they can be bigger; the slide is
     where they stop. */
  overflow: hidden;
}

.pg2-in { position: relative; height: 100%; }

/* The words sit where they sit on his pages: a column down the left, set
   optically in the middle of it rather than pinned to the top. */
.pg2-words {
  position: absolute;
  left: 5cqw;
  top: 50%;
  transform: translateY(-50%);
  width: 28cqw;
}
/* A slide with no pictures on it — his page five had none — gives the words
   the room the pictures would have had, rather than leaving them small in a
   third of an empty screen. */
.pg2-words--wide { width: 58cqw; }
.pg2-words--wide .pg2-h { font-size: 5.2cqw; margin-bottom: 2.2cqw; }
.pg2-words--wide p { font-size: 2cqw; margin-bottom: 1.6cqw; }

.pg2-h {
  margin: 0 0 1.6cqw;
  font-family: var(--font-display, inherit);
  font-size: 4cqw;
  line-height: 1.02;
  letter-spacing: -0.015em;
  color: var(--fg);
}

.pg2-words p {
  margin: 0 0 1.2cqw;
  font-size: 1.55cqw;
  line-height: 1.5;
  color: var(--fg-60);
}
.pg2-words p:last-child { margin-bottom: 0; }

/* The seven questions are a list he reads down, so they are set as one. */
.pg2-ask {
  margin: 0 0 1.3cqw;
  padding-left: 1.5cqw;
  list-style: disc;
}
.pg2-ask li {
  font-size: 1.4cqw;
  line-height: 1.45;
  margin-bottom: .35cqw;
  color: var(--fg);
}

/* One picture, at its frame. Stacking follows source order, which is his
   draw order, so what he put on top stays on top. */
.pg2-shot { position: absolute; margin: 0; }

.pg2-shot .shot__open {
  display: block;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
}

.pg2-shot img { display: block; width: 100%; height: 100%; object-fit: cover; }

/* A credit rides inside its own picture. Under it, it would either fall off
   the bottom of the slide or be covered by whatever overlaps it — these
   arrangements overlap on purpose — and a credit that cannot be read is not
   a credit. */
.pg2-shot figcaption {
  position: absolute;
  left: 0;
  /* --cap-shift pulls the line back inside the slide when its own frame
     bleeds off the edge. */
  bottom: var(--cap-shift, 0cqw);
  max-width: 100%;
  padding: .5cqw .7cqw;
  font-size: 1cqw;
  line-height: 1.25;
  color: #fff;
  background: rgba(0, 0, 0, .62);
  /* The frames overlap on purpose, so a later picture would paint over this.
     Only the line is lifted, not the picture it belongs to. */
  z-index: 3;
}

.pg2-shot--captop figcaption { bottom: auto; top: var(--cap-shift, 0cqw); }

.pg2-shot .shot__open:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

/* Too narrow for a projector layout: the slide stops pretending to be one
   and becomes the words followed by the pictures, in order. */
@media (max-width: 52rem) {
  .pg2-page { aspect-ratio: auto; }
  .pg2-in { display: flex; flex-direction: column; gap: 4vw; padding: 5vw; }
  .pg2-words, .pg2-shot {
    position: static;
    transform: none;
    width: 100% !important;
    height: auto !important;
  }
  .pg2-h { font-size: var(--step-2); margin-bottom: .8rem; }
  .pg2-words p { font-size: var(--step--1); margin-bottom: .7rem; }
  .pg2-ask li { font-size: var(--step--1); }
  .pg2-shot img { height: auto; }
  .pg2-shot figcaption { position: static; width: auto; font-size: var(--step--1); margin-top: .4rem; }
}


/* ------------------------------------------------- the reel, on a phone
   On a small screen the work takes the whole screen. The band stops being
   a band inside a page and becomes the page: edge to edge, from under the
   nav to the bottom of the viewport, with the greeting over the top of it
   and the project's name over the bottom. The tagline and the rest of the
   hero fall below the fold, which is the right place for them — you arrive
   looking at the work, and you scroll to read about it.

   Desktop is untouched. */

@media (max-width: 640px) {
  .hero {
    min-height: 0;
    padding-top: 0;
    display: block;
    position: relative;
  }

  .hero .reel {
    display: block;
    height: calc(100svh - var(--nav-h));
    padding: 0;
    margin: 0;
  }
  .hero .reel__stage { height: 100%; }

  /* The picture fills the screen. A landscape frame on a portrait screen
     means a crop, and the crop is taken from the middle. */
  .hero .reel__slide img { object-fit: cover; }
  .hero .reel__meta { z-index: 2; }

  /* Drag the band sideways by hand; up and down still scrolls the page. */
  .hero .reel__stage { touch-action: pan-y; }
  .hero .reel__slide img { -webkit-user-drag: none; user-select: none; }

  /* Enough darkness at the top for white type to sit on any photograph,
     and none of it where the picture is doing the talking. */
  .hero .reel__stage::after {
    content: "";
    position: absolute;
    inset: 0 0 auto;
    height: 7rem;
    background: linear-gradient(rgb(0 0 0 / .55), transparent);
    pointer-events: none;
    z-index: 1;
  }

  .hero__top {
    position: absolute;
    top: var(--nav-h);
    left: 0;
    right: 0;
    z-index: 5;
    padding: .7rem var(--gutter) 0;
    color: #fff;
    pointer-events: none;
  }
  .hero__top .muted { color: rgb(255 255 255 / .72); }

  /* The ticks ride on the picture, under the project's name. */
  .hero .reel__bar {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 5;
    padding: 0 var(--gutter) 1rem;
    gap: .9rem;
  }
  .hero .reel__tick i { background: rgb(255 255 255 / .38); }
  .hero .reel__tick.is-on i { background: #fff; }
  .hero .reel__pause { color: rgb(255 255 255 / .75); }
  .hero .reel__pause:hover { color: #fff; }

  /* The project's name sits higher, clear of the ticks. */
  .hero .reel__meta { padding-bottom: 2.6rem; }

  /* Everything else in the hero is below the fold now. */
  .hero__bottom {
    padding-top: clamp(1.5rem, 5vw, 2rem);
    padding-bottom: clamp(1.5rem, 5vw, 2rem);
    border-top: 0;
  }
}
