/* Mok Music stylesheet.
   Extracted from index.html once the site gained an articles section: three
   pages sharing one inline block meant three copies to keep in step. There is
   still no build step and no framework. This file is served as written.
   Brand tokens live in :root at the top and are the only place a hex belongs. */
/* Archivo ships as one variable file covering every weight we use:
   one 34KB request instead of three. */
@font-face {
  font-family: Archivo;
  src: url('assets/fonts/archivo-var.woff2') format('woff2');
  font-weight: 100 900;
  font-style: normal;
  font-display: swap;
}

:root {
  /* MOK'HOUSE brand guidelines v1.0 */
  --sand:     #F8F7EE;   /* Coastal Sand */
  --charcoal: #010101;   /* Charcoal Black */
  --red:      #DC4833;   /* Uluru Dirt, the action colour and nothing else */
  --spray:    #AAC5E6;   /* Sea Spray */
  --wattle:   #DAE050;   /* Wattle Bloom, sparing accent only */

  /* Archivo tracking: -50 on headings and labels, -25 on body */
  --track-tight: -0.05em;
  --track-body:  -0.025em;

  --gutter: clamp(1.5rem, 5vw, 6rem);
}

* { margin: 0; padding: 0; box-sizing: border-box; }

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

body {
  background: var(--charcoal);
  color: var(--sand);
  font-family: Archivo, system-ui, sans-serif;
  font-weight: 400;
  letter-spacing: var(--track-body);
  overflow-x: hidden;
}

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

/* ============================= HERO ============================= */

.hero {
  position: relative;
  isolation: isolate;
  min-height: 100svh;
  padding: var(--gutter);
  display: flex;
  flex-direction: column;
}

/* Hero imagery. One silent looping video, covering the section. */
.hero__imagery {
  position: absolute;
  inset: 0;
  z-index: -2;
  overflow: hidden;
  background: var(--charcoal);
}
.hero__imagery video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Scrim keeps type legible across every image in the rotation.
   Heavier than a single-image hero would need: type can't be placed in a
   dead zone when the dead zone moves between images. */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(to bottom,
      rgba(1,1,1,0.70) 0%,
      rgba(1,1,1,0.25) 30%,
      rgba(1,1,1,0.30) 55%,
      rgba(1,1,1,0.88) 100%),
    linear-gradient(to right,
      rgba(1,1,1,0.55) 0%,
      rgba(1,1,1,0.10) 55%,
      rgba(1,1,1,0.00) 100%);
}

.hero__top {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1.5rem;
  flex-wrap: wrap;
}
.hero__mid { flex: 1; }
.hero__bottom {
  margin-top: auto;
  display: flex;
  flex-direction: column;
  gap: clamp(1rem, 2.5vh, 1.75rem);
}

/* Weighted the way the MOK'HOUSE mark is: the first word set black, the second
   set light, so the two read as one lockup rather than two words. */
.wordmark {
  font-weight: 900;
  font-size: clamp(1.5rem, 2.9vw, 2.25rem);
  line-height: 1;
  letter-spacing: var(--track-tight);
  text-transform: uppercase;
}
/* MUSIC used to sit in a red box. The box is gone: red is the action colour,
   and spending it on the wordmark put a second red thing above the fold,
   competing with the email address. Weight alone carries the lockup, which
   is what the MOK'HOUSE mark does. */
.wordmark__light {
  font-weight: 500;
}
/* The byline sits under the wordmark rather than beside it: the mark is the
   brand, the name is the person behind it, and a producer scanning the top
   of the page should read them in that order. */
.wordmark__by {
  font-weight: 600;
  font-size: clamp(0.7rem, 1.15vw, 0.875rem);
  text-transform: uppercase;
  letter-spacing: 0.14em;
  opacity: 0.65;
  margin-top: 0.5em;
}
/* The apostrophe came out of the mark on 2026-08-07. Nothing in Archivo matches
   the drawn wedge in the MOK'HOUSE logo, and a near miss read worse than no
   apostrophe at all. The weight split now carries the family link on its own.
   Putting it back means drawing it, not picking a glyph. */

.label {
  font-size: clamp(0.625rem, 1.1vw, 0.75rem);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 0.5;
}
.hero__bottom .label { margin-bottom: -0.6rem; }

.headline {
  font-weight: 600;
  font-size: clamp(1.25rem, 2.4vw, 2rem);
  line-height: 1.15;
  letter-spacing: var(--track-tight);
  max-width: 22ch;
  text-wrap: balance;
}

/* The certification mark rides beside the line on phones and nowhere else.
   On a wide screen the acknowledgement is a short scroll away and the hero
   is deliberately one image and one line; on a phone that scroll is long
   enough that the mark is worth carrying up here. Hidden rather than
   absent from the markup, because the file is 60KB and shared with the
   acknowledgement, so a wide screen pays nothing extra for it. */
/* space-between, so the mark goes to the far edge rather than sitting
   against the end of the line. The headline keeps its 22ch measure and
   wraps as it always did: taking the measure away to make room was what
   pulled it onto one line. */
.hero__claim {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: clamp(1rem, 4vw, 1.5rem);
}
.hero__cert { display: none; }

/* 48rem, not the 34rem the work rows use. Those two numbers answer
   different questions: 34rem is where a still is wide enough to bleed off
   the gutter, and this is just whether there is room beside the line.
   There is, well past a phone, and pinning it to 34rem meant a handset
   reporting 560 or 600 CSS pixels showed nothing at all. */
@media (max-width: 48rem) {
  .hero__cert {
    display: block;
    width: clamp(3.5rem, 17vw, 5rem);
    height: auto;
    flex: none;
    /* Held back from full strength so the mark sits in the picture rather
       than on top of it. Opacity only: the artwork itself is untouched,
       never redrawn and never recoloured, and the acknowledgement carries
       it at full strength further down the page. */
    opacity: 0.82;
  }
}

/* Padding, not size. This is the primary call to action and it measured a
   19px-tall tap target on a phone, under the 24px minimum, and a fiddly
   thing to hit with a thumb. The negative margin keeps the surrounding
   rhythm unchanged while the hit area grows to ~43px. */
.email {
  font-weight: 600;
  font-size: clamp(1rem, 1.7vw, 1.35rem);
  letter-spacing: var(--track-tight);
  color: var(--red);
  align-self: flex-start;
  word-break: break-word;
  border-bottom: 0.08em solid transparent;
  padding-block: 0.75rem;
  margin-block: -0.75rem;
}
.email:hover,
.email:focus-visible { border-bottom-color: var(--red); }

/* Client names as type, not logos. Official brand marks proved unreliable to
   source, and flattening logos to white destroys any built from knocked-out
   detail: it works on wordmarks and turns emblems into white shapes.

   Its own band below the hero, on flat charcoal rather than over the
   photograph. Grey, not sand: the names are supporting evidence for the
   headline above them, and at full sand they read as the loudest thing on
   the first screen, which is the work's job and not theirs. */
/* No bottom padding, for the reason .services has no top: .work below
   already opens with a full section's worth, and the two stacked left a
   dead run between the last client name and Selected Work. The band's own
   top padding still separates it from the hero. */
.clients-band {
  padding: clamp(2rem, 6vh, 3.5rem) var(--gutter) 0;
}
/* A grid, not a wrapping row. Flex-wrap put the eighth name alone on a second
   line, which reads as a layout accident rather than a client list. Two and
   four both divide eight, so the band is always a filled rectangle at every
   width. This is tied to there being eight names: see the note in the markup. */
.clients {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(0.6rem, 1.6vh, 1rem) clamp(1rem, 2.5vw, 2rem);
  font-weight: 600;
  font-size: clamp(0.75rem, 1.4vw, 1rem);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #8A8A85;   /* sand desaturated toward grey, 5.4:1 on charcoal */
}
@media (min-width: 40rem) {
  .clients { grid-template-columns: repeat(4, 1fr); }
}
/* The red slash separators are gone with the wrapping row that needed them.
   A grid separates its own items, and the house rule is that Uluru Red means
   "do something": a divider is not an action, so it had no claim on the
   colour. The email address is now the only red above or below the fold. */

:focus-visible { outline: 2px solid var(--red); outline-offset: 4px; }

/* Selecting text is something a producer actually does here: copying an
   email address, a credit line, a client name into a brief. Red is the
   action colour and selection is an action, so it stays consistent. */
::selection { background: var(--red); color: var(--charcoal); }

/* --- scroll reveal ------------------------------------------------
   Rows rise a little as they arrive. The hidden state is scoped to
   .js-reveal, which only exists once the inline script at the top of the
   body has run, so with JavaScript off, or if the script throws, every
   element is simply visible. Content is never hidden by default; that is
   the whole reason the class lives on <html> rather than the elements. */
.js-reveal [data-reveal] {
  opacity: 0;
  transform: translateY(1.5rem);
  transition:
    opacity   760ms cubic-bezier(0.22, 0.61, 0.36, 1) var(--reveal-delay, 0ms),
    transform 760ms cubic-bezier(0.22, 0.61, 0.36, 1) var(--reveal-delay, 0ms);
}
.js-reveal [data-reveal].is-in { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  .js-reveal [data-reveal] { opacity: 1; transform: none; transition: none; }
}

/* ============================= WORK ============================= */

.work { padding: clamp(4rem, 12vh, 9rem) var(--gutter); }

.section-label {
  font-size: clamp(0.625rem, 1.1vw, 0.75rem);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 0.5;
  padding-bottom: clamp(1.5rem, 4vh, 2.5rem);
  border-bottom: 1px solid #222;
}

/* A list with thumbnails, not a grid. YouTube supplies a still for every
   video, so the original "no thumbnails" argument for a list no longer
   applies. The list survives for a different reason: the context line and
   the credit are load-bearing, and text compressed under a grid tile gets
   skimmed past. The credit especially has to read as deliberate. */
.work__item {
  padding: clamp(1.75rem, 5vh, 3rem) 0;
  border-bottom: 1px solid #222;
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0 clamp(1rem, 2.5vw, 2rem);
  align-items: start;
}

/* --- video rows --------------------------------------------------
   Thumbnails are downloaded into assets/thumbs/, never hotlinked from
   ytimg.com. The site makes zero external requests until someone presses
   play, and a downloaded still cannot change underneath the page. */
.work__thumb {
  grid-column: 1;
  grid-row: 1 / span 4;
  width: clamp(11rem, 30vw, 28rem);
  aspect-ratio: 16 / 9;
  border: 0;
  padding: 0;
  background: #141414;
  position: relative;
  cursor: pointer;
  overflow: hidden;
}
/* The dimmed resting state only makes sense where a pointer can undim it.
   Scoped to hover-capable devices because it was previously unconditional:
   on a phone there is no hover, so every still sat at 75% permanently and the
   work looked washed out on the device most of these links get opened on. */
.work__thumb img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  transition: opacity 200ms ease, transform 400ms ease;
}
@media (hover: hover) and (pointer: fine) {
  .work__thumb img { opacity: 0.75; }
  .work__thumb:hover img,
  .work__thumb:focus-visible img { opacity: 1; transform: scale(1.03); }
}

/* Video affordance is a bare triangle: no plate, no circle, no outline. A
   still with a triangle on it already reads as a film, and the badge that
   used to carry the triangle covered the frame it was advertising.

   Sand, not red. Eight red play buttons stacked down the page is the same
   mistake the client separators made: red has to mean "act", and the only
   action on this page is getting in touch. A red mark sitting on every
   thumbnail quietly demotes the email.

   It sits at 80% so the image reads through it, and carries a drop shadow
   because a pale glyph over a pale frame otherwise disappears. */
.work__thumb::after {
  content: "";
  position: absolute;
  inset: 50% auto auto 50%;
  transform: translate(-50%, -50%);
  width: 3.5rem; height: 3.5rem;
  opacity: 0.8;
  background: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='%23F8F7EE'><path d='M8 5l11 7-11 7V5z'/></svg>") center / contain no-repeat;
  filter: drop-shadow(0 0 0.5rem rgba(1,1,1,0.55));
  transition: transform 200ms ease, opacity 200ms ease;
}
.work__thumb:hover::after {
  transform: translate(-50%, -50%) scale(1.08);
  opacity: 1;
}

/* The whole row acknowledges the cursor, not just the thumbnail it is over.
   A closed row is a thing you can open, so it leans very slightly toward the
   reader; an open one has nowhere left to go and stays put. The distance is
   deliberately small: this should register as responsiveness, not as an
   animation someone has to wait out. */
.work__client { transition: transform 420ms cubic-bezier(0.22, 0.61, 0.36, 1); }
.work__item:not(.is-open):hover .work__client { transform: translateX(0.3rem); }

/* Open rows swap the play glyph for a close one, so the button reads as the
   toggle it actually is rather than offering to play something already playing. */
.work__item.is-open .work__thumb::after {
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23F8F7EE' stroke-width='3' stroke-linecap='round'><path d='M6 6l12 12M18 6L6 18'/></svg>");
  background-size: 2rem;
}

/* --- the expanding panel -----------------------------------------
   Clicking a row opens it in place rather than swapping the thumbnail for a
   postage-stamp player. The thumbnail stays put, so the row keeps its
   identity while it is open and the list never reorders under the cursor.

   The height animation is the 0fr -> 1fr grid trick: it interpolates to the
   panel's natural height with no JavaScript measuring anything, which means
   it cannot desync from the content the way a cached scrollHeight does. The
   inner wrapper carries the overflow clip; min-height: 0 is what lets a grid
   child actually shrink below its content. */
.work__panel {
  grid-column: 1 / -1;
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 520ms cubic-bezier(0.22, 0.61, 0.36, 1),
              margin-top     520ms cubic-bezier(0.22, 0.61, 0.36, 1);
}
.work__panel > .work__panel-inner {
  overflow: hidden;
  min-height: 0;
}
.work__item.is-open .work__panel {
  grid-template-rows: 1fr;
  margin-top: clamp(1.5rem, 3.5vh, 2.5rem);
}

/* Opacity lags the height slightly on the way in so the content arrives
   after the space exists, not during. */
.work__panel-inner > * {
  opacity: 0;
  transition: opacity 200ms ease;
}
.work__item.is-open .work__panel-inner > * {
  opacity: 1;
  transition: opacity 320ms ease 180ms;
}

.work__video {
  width: 100%;
  max-width: 64rem;
  aspect-ratio: 16 / 9;
  background: #000;
}
.work__video iframe { width: 100%; height: 100%; border: 0; display: block; }

/* Below the campaign name, above the meta line. It was the most opaque of the
   three, which put the long read louder than the title it sits under. */
.work__detail {
  max-width: 58ch;
  font-size: clamp(0.9375rem, 1.5vw, 1.125rem);
  line-height: 1.5;
  opacity: 0.75;
  padding-top: clamp(1rem, 2.5vh, 1.5rem);
}
.work__meta {
  font-size: clamp(0.625rem, 1.1vw, 0.75rem);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 0.45;
  padding-top: clamp(0.75rem, 2vh, 1rem);
}

/* An expanding panel is exactly the motion someone with a vestibular
   disorder asked the media query for. Open and close still work; they
   just happen at once. */
@media (prefers-reduced-motion: reduce) {
  .work__panel,
  .work__panel-inner > * { transition: none; }
}

/* --- compact rows -------------------------------------------------
   Everything past the featured group. Same markup and the same expanding
   panel, scaled down: the description comes off because it is the longest
   element and the one a skimming producer is least likely to read this far
   down. What stays is the brand, the campaign and the credit, which is the
   minimum needed for the row to be a specific and attributable claim.
   Opening one still gives the full-size player and the long detail. */
.work__item--compact {
  padding: clamp(1rem, 2.5vh, 1.5rem) 0;
}
.work__item--compact .work__thumb {
  width: clamp(5rem, 13vw, 10rem);
  grid-row: 1 / span 3;
}
.work__item--compact .work__thumb::after {
  width: 2.25rem; height: 1.5rem;
  border-radius: 0.35rem;
  background-size: 0.65rem;
}
.work__item--compact .work__client {
  font-size: clamp(1.125rem, 2.2vw, 1.75rem);
}
.work__item--compact .work__campaign {
  font-size: clamp(0.8125rem, 1.3vw, 0.9375rem);
  padding-top: 0.2rem;
}
.work__item--compact .work__context { display: none; }
/* Direct child only. The credit inside an open panel is panel content and
   should read at full size like the detail paragraph beside it, not shrink
   just because the closed row it belongs to is a compact one. This selector
   now only catches the audio path, where the credit is still a row element. */
.work__item--compact > .work__credit {
  padding-top: clamp(0.4rem, 1.2vh, 0.6rem);
  font-size: clamp(0.5625rem, 0.95vw, 0.6875rem);
}

.work__client {
  grid-column: 2;
  font-weight: 600;
  font-size: clamp(1.5rem, 3.6vw, 3rem);
  line-height: 1.05;
  letter-spacing: var(--track-tight);
}
/* The campaign name above this used to sit at 0.72 and this at 0.7, which is
   no separation at all: title and description read as one grey block. The
   campaign is now full sand and this drops to 0.6, which composites to
   #95958F and 6.93:1 on charcoal. Checked composited, not raw, per the rule
   that caught four values last time. */
.work__context {
  grid-column: 2;
  font-size: clamp(0.875rem, 1.4vw, 1.0625rem);
  line-height: 1.5;
  opacity: 0.6;
  max-width: 46ch;
  padding-top: 0.5rem;
}
/* The campaign name, between the brand and the description. It earns its
   place beyond styling: there is a delivered and a lost or cancelled job
   under both Nintendo and GWM, so an unqualified brand name cannot be
   checked against the record. Naming the campaign makes each row a specific,
   verifiable claim rather than a logo drop. */
/* The campaign line is marked the way MUSIC is in the wordmark. The highlight
   is on an inner inline span, not the paragraph, so a title that wraps gets a
   block per line instead of one rectangle with a ragged line of empty red. */
.work__mark {
  background: var(--red);
  color: var(--charcoal);
  padding: 0.08em 0.22em;
  -webkit-box-decoration-break: clone;
  box-decoration-break: clone;
}
.work__campaign {
  grid-column: 2;
  font-size: clamp(0.9375rem, 1.7vw, 1.25rem);
  font-weight: 600;
  line-height: 1.2;
  letter-spacing: var(--track-tight);
  opacity: 1;
  padding-top: 0.35rem;
}

/* Grey, not red and not sand. #8A8A85 measures 5.4:1 on charcoal at full
   opacity. Set as a colour rather than as opacity on sand, because the old
   0.62 opacity made the real contrast invisible to a plain colour check.

   On video rows this now lives inside the expanding panel, so it appears
   with the film rather than on the closed row. That is a deliberate change
   to a documented convention: the spec asked for it on every row because it
   is the Kate and Glenn arrangement made visible. It is still on every
   piece and still not fine print, but it is now seen by people who watch
   the work rather than by everyone who scrolls past. Audio rows keep it
   visible, because they have no panel to put it in.

   grid-column is still set for the audio path, where the credit remains a
   direct child of the row grid. Inside the panel it is inert. */
.work__credit {
  grid-column: 2;
  font-size: clamp(0.625rem, 1.1vw, 0.75rem);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #8A8A85;
  opacity: 1;
  padding-top: clamp(0.75rem, 2vh, 1.25rem);
}
/* Inside the panel it follows the detail paragraph, so it needs the measure
   of body copy rather than the tighter spacing of a row element. */
.work__panel-inner .work__credit { max-width: 58ch; }

/* Play control. The browser's own audio engine does the playing; only the
   controls are ours, because default controls differ per browser and none of
   them belong on this page. */
.work__play {
  grid-column: 1;
  grid-row: 1 / span 2;
  width: clamp(2.75rem, 5vw, 3.75rem);
  height: clamp(2.75rem, 5vw, 3.75rem);
  border: 1px solid var(--red);
  border-radius: 50%;
  background: none;
  color: var(--red);
  cursor: pointer;
  display: grid;
  place-items: center;
  flex: none;
  transition: background-color 150ms ease, color 150ms ease;
}
.work__play:hover { background: var(--red); color: var(--charcoal); }
.work__play svg { width: 38%; height: 38%; fill: currentColor; }
.work__play .icon-pause { display: none; }
.work__item.is-playing .work__play { background: var(--red); color: var(--charcoal); }
.work__item.is-playing .icon-play  { display: none; }
.work__item.is-playing .icon-pause { display: block; }

.work__progress {
  grid-column: 2;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding-top: clamp(0.75rem, 2vh, 1.25rem);
  max-width: 34rem;
}
.work__bar {
  flex: 1;
  height: 2px;
  background: #2e2e2e;
  position: relative;
}
.work__bar span {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  background: var(--red);
}
.work__time {
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  opacity: 0.5;
  min-width: 4.5ch;
}

.work__error {
  grid-column: 2;
  padding-top: 0.75rem;
  font-size: 0.8125rem;
  color: var(--red);
}

/* --- phones ------------------------------------------------------
   The row stacks and the still goes edge to edge, past the section gutter.
   On a phone the image is the fastest credibility signal on the page, and
   an inset one reads as a card in a list; a full-bleed one reads as the
   work itself. The negative margin is what escapes .work's padding, and it
   has to be paired with a matching width or the row overflows sideways.

   48rem, raised from 34rem. 34rem is 544 CSS pixels, and a large handset
   in portrait reports more than that: a Galaxy S25+ lands in the 560 to
   600 range. Every rule in here was written for "a phone" and none of it
   was reaching the phones it was written for, which is a worse outcome
   than a 700px window getting the stacked layout. */
@media (max-width: 48rem) {
  /* No rule between items. The divider earns its place on a wide screen,
     where the rows are short and the thumbnails are small enough that the
     list needs help separating them. Once every still runs the full width
     of the phone, the pictures do that job on their own and the line is
     just a mark across the page. */
  .work__item { grid-template-columns: 1fr; border-bottom: 0; }
  .work__play { grid-column: 1; grid-row: auto; justify-self: start; margin-top: 1rem; }
  .work__client, .work__campaign, .work__context, .work__credit,
  .work__progress, .work__error { grid-column: 1; }

  .work__thumb, .work__video {
    grid-column: 1;
    grid-row: auto;
    width: 100%;
    margin-bottom: clamp(1rem, 3vh, 1.5rem);
  }
  /* Only the still bleeds. The player sits inside .work__panel-inner, which
     clips its overflow to make the open/close height animation work, so a
     negative margin there would be cut off rather than escaping. */
  /* Taller than the film is. At this width 16:9 gives about 210px of
     picture, and the still is the fastest credibility signal on the page,
     so it is worth 40 more. The still is already object-fit: cover, so
     this crops the sides of the frame rather than squashing it.
     Only the still: .work__video stays 16:9, because cropping the actual
     film is a different thing from cropping a thumbnail of it. */
  .work__thumb,
  .work__item--compact .work__thumb { aspect-ratio: 3 / 2; }

  .work__thumb,
  .work__item--compact .work__thumb {
    width: calc(100% + (var(--gutter) * 2));
    margin-left: calc(var(--gutter) * -1);
  }

  /* Corner badge rather than the centred pill. Small enough to leave the
     still readable, still present because touch has no hover state to fall
     back on: with nothing here, a thumbnail is just a picture and the one
     thing a producer wants to do on this row becomes invisible. The
     gradient is what keeps it legible over a bright frame. */
  .work__thumb::before {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 40%;
    background: linear-gradient(to top, rgba(1,1,1,0.55), transparent);
    pointer-events: none;
  }
  .work__thumb::after {
    inset: auto auto 0.6rem 0.6rem;
    transform: none;
    width: 2.5rem; height: 1.75rem;
    border-radius: 0.4rem;
    background-size: 0.7rem;
  }
  .work__item.is-open .work__thumb::after { background-size: 0.7rem; }
  .work__thumb:hover::after { transform: none; }

  /* Belt and braces on the dimming fix. The hover-capability query above is
     the correct rule, but it is worth not depending on capability detection
     alone for something this visible: at this width the still is the page,
     and it should never be sitting at 75% waiting for a hover that a finger
     cannot produce. */
  .work__thumb img { opacity: 1; }

  /* Title and campaign carry the row on a phone. The description is the
     longest element and the first thing a thumb-scrolling reader skips, so
     it comes off; the credit stays, because it always does. */
  .work__context { display: none; }
}

/* ====================== ACKNOWLEDGEMENT ======================= */

/* Sits between the dark footer panel and the red enquiry panel, on its own
   charcoal band. Deliberately not shrunk to colophon size: this is the
   statement the business is built on, and setting it as fine print would
   undercut the thing it says. Measure is held at 62ch so it reads as prose
   rather than a legal footer. */
.acknowledgement {
  padding: clamp(2.5rem, 7vh, 4.5rem) var(--gutter);
  border-top: 1px solid #222;
}
.acknowledgement__body {
  max-width: 62ch;
  font-size: clamp(0.9375rem, 1.5vw, 1.125rem);
  line-height: 1.6;
  opacity: 0.85;
}
/* The apostrophe that used to sit in MOK'MUSIC here went when it went from the
   masthead, so the brand spells one way on the page. */

.acknowledgement__cert {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.5vw, 1.75rem);
  flex-wrap: wrap;
  padding-top: clamp(1.75rem, 4vh, 2.5rem);
}
/* No panel added here. Supply Nation publish three variants, and the one
   named "white" already carries its own white rounded panel with the mark
   knocked out in black, which is the variant drawn for dark backgrounds.
   The badge that appears on the charcoal is theirs, unaltered. Recolouring
   a certification mark is not permitted, and picking the right variant
   means there is no reason to. */
.acknowledgement__cert img {
  width: clamp(5.5rem, 9vw, 7rem);
  height: auto;
}
.acknowledgement__cert-note {
  font-size: clamp(0.6875rem, 1.1vw, 0.8125rem);
  line-height: 1.5;
  opacity: 0.55;
  max-width: 34ch;
}

/* The full-bleed red statement page from the brand guide sat here, between the
   work list and this section, carrying one line at the largest type on the
   site. Removed 2026-08-07. It was the rhythm break between two charcoal
   sections, so its absence is felt: work now runs straight into who. If that
   needs filling, the gap is a pacing problem, not a missing sentence. */

/* ============================= WHO ============================= */

.who {
  padding: clamp(4rem, 12vh, 9rem) var(--gutter);
  position: relative;
  isolation: isolate;
}
/* The section is read at a skim, so the paragraph drops back and the three
   things worth catching in that skim stay at full strength: the name, the
   credential, the band. Done with colour rather than weight, because Archivo
   at this size goes noticeably wider when it gets heavier and the 34ch measure
   would reflow. Composited over charcoal the muted value lands at 6:1, so the
   quiet text is still past AA on its own, not just next to the loud text. */
.who__body {
  color: rgba(248, 247, 238, 0.55);
  font-size: clamp(1.125rem, 2.2vw, 1.875rem);
  line-height: 1.4;
  letter-spacing: var(--track-tight);
  max-width: 34ch;
  padding-top: clamp(1.5rem, 4vh, 2.5rem);
}
.who__body + .who__body { padding-top: 1em; }
.who__key { color: var(--sand); }

/* --- the portrait -------------------------------------------------
   The copy runs to 34ch, so the right of this section was dead space on any
   wide screen. The portrait fills it without becoming a second column: it
   sits behind the type, dissolving at every edge, so there is no hard frame
   to align to and the text never has to wrap around it.

   Colour. The photograph is shot against pale ice-blue, which lands within a
   few degrees of Sea Spray in the MOK'HOUSE palette, so it is treated
   toward that token rather than away from it. Brightness is pulled well
   down so it reads as a cool glow surfacing out of the charcoal instead of
   a bright rectangle punched into it, and saturation is eased back so the
   blue never competes with the one red element on the page. Nothing here is
   cultural imagery, which is the only material the untreated-image rule
   covers: this is a portrait, and treating it is a design choice. */
.who__portrait {
  position: absolute;
  z-index: -1;
  inset: 0 0 0 auto;
  width: min(50%, 44rem);
  pointer-events: none;

  /* Two gradients intersected, not one radial. A radial mask on a bright
     photograph produces a glowing oval sitting on top of the black: the
     shape of the mask becomes the thing you see. Fading along the axes that
     actually meet something (left, into the type; top and bottom, into the
     sections either side) leaves no closed shape to read as an object. */
  /* Long, multi-stop ramps rather than two-stop ones. A two-stop gradient
     ramps at a constant rate, and the eye finds the point where it stops
     ramping, which is what reads as an edge. Extra stops bunched toward the
     opaque end approximate an ease, so the falloff never lands on a
     detectable line. Nothing reaches full opacity except the deepest part of
     the right-hand side. */
  -webkit-mask-image:
    linear-gradient(to right,
      transparent 0%, rgba(0,0,0,0.04) 18%, rgba(0,0,0,0.14) 32%,
      rgba(0,0,0,0.30) 46%, rgba(0,0,0,0.52) 60%, rgba(0,0,0,0.76) 76%, #000 100%),
    linear-gradient(to bottom,
      transparent 0%, rgba(0,0,0,0.10) 10%, rgba(0,0,0,0.38) 22%,
      rgba(0,0,0,0.78) 36%, #000 50%, rgba(0,0,0,0.78) 68%,
      rgba(0,0,0,0.34) 84%, transparent 100%);
          mask-image:
    linear-gradient(to right,
      transparent 0%, rgba(0,0,0,0.04) 18%, rgba(0,0,0,0.14) 32%,
      rgba(0,0,0,0.30) 46%, rgba(0,0,0,0.52) 60%, rgba(0,0,0,0.76) 76%, #000 100%),
    linear-gradient(to bottom,
      transparent 0%, rgba(0,0,0,0.10) 10%, rgba(0,0,0,0.38) 22%,
      rgba(0,0,0,0.78) 36%, #000 50%, rgba(0,0,0,0.78) 68%,
      rgba(0,0,0,0.34) 84%, transparent 100%);
  -webkit-mask-composite: source-in;
          mask-composite: intersect;
}

/* The photograph is lit on pale ice-blue, a few degrees off Sea Spray in the
   MOK'HOUSE palette. Rather than fight that blue or drain it out, the
   exposure is pulled right down until the backdrop sits within a shade of
   the page's charcoal, so what survives is a cool rim light and a
   silhouette, and the fade has nothing bright left to give away. Saturation
   comes back slightly so the remaining blue reads as Sea Spray and not as
   grey. Nothing here is cultural imagery, which is the only material the
   untreated-image rule covers; this is a portrait, and grading it is a
   design decision. */
.who__portrait img {
  width: 100%; height: 100%;
  object-fit: cover;
  /* Was 50%, which centred him in a box narrow enough that the mask ramp was
     still climbing where his face is. Pulling the window left across the
     source moves him right in the frame, so there is a run of empty wall
     between the fade and his face rather than the fade landing on it. */
  object-position: 42% 42%;
  display: block;
  /* Was brightness 0.52 with contrast 1.16, which buried the face. The two
     compounded: the contrast lift deepened the shadows the brightness cut had
     already made, and the side of the face turned away from the light went to
     black. Lifted together, so the exposure comes up without flattening. */
  filter: brightness(0.75) contrast(1.08) saturate(0.72);
}

/* Below the wide breakpoint the copy already fills the measure, and a
   portrait sitting under 34ch of type is just noise behind the words. */
/* Narrow screens used to hide this outright, which meant the portrait
   silently disappeared on any window under 880px: a split-screen laptop, a
   narrow preview pane, every phone. It looked like a broken image rather
   than a design decision.

   It now stops being a background and becomes a normal block. The original
   objection still holds, that type sitting on top of a photograph at this
   width is unreadable, so nothing sits on top of it any more. The fade
   drops the vertical ramp, since the top and bottom edges now meet the
   sections above and below rather than the middle of a paragraph.

   It carried a margin-top here, written as though it sat under the copy.
   It does not: it is the section's first child, so it lands above the
   heading, and that margin stacked straight onto the section's own top
   padding. Removed, because the padding is already the gap. */
@media (max-width: 55rem) {
  /* No top padding, for the reason .services has none: the section above
     already ends with a full one, and two stacked read as a hole rather
     than as rhythm. It matters more here than anywhere else on the page,
     because the portrait leads the section and its mask fades the top
     edge out, so the black runs on well past where the image starts. */
  .who { padding-top: 0; }

  .who__portrait {
    position: static;
    width: 100%;
    aspect-ratio: 16 / 10;
    -webkit-mask-image:
      linear-gradient(to bottom,
        transparent 0%, rgba(0,0,0,0.35) 14%, #000 42%, #000 72%,
        rgba(0,0,0,0.30) 90%, transparent 100%);
            mask-image:
      linear-gradient(to bottom,
        transparent 0%, rgba(0,0,0,0.35) 14%, #000 42%, #000 72%,
        rgba(0,0,0,0.30) 90%, transparent 100%);
    -webkit-mask-composite: source-over;
            mask-composite: add;
  }
  .who__portrait img {
    /* Stays centred: this layout has no left-to-right ramp to clear, so the
       reason the wide one is offset does not apply here. Exposure lifted to
       match it though, since the photograph is the same photograph. */
    object-position: 50% 38%;
    filter: brightness(0.78) contrast(1.06) saturate(0.72);
  }
}

/* ========================== SERVICES ========================== */

.services { padding: 0 var(--gutter) clamp(4rem, 12vh, 9rem); }

.services__list {
  display: flex;
  flex-wrap: wrap;
  gap: clamp(1rem, 3vw, 3rem);
  /* No bottom padding: it existed to hold the list off the packages grid,
     and the grid is gone. The section's own bottom padding carries the
     rhythm into the footer, same as .work and .who. */
  padding: clamp(1.5rem, 4vh, 2.5rem) 0 0;
  font-weight: 600;
  font-size: clamp(1.25rem, 3vw, 2.5rem);
  letter-spacing: var(--track-tight);
}
.services__list span:not(:last-child)::after {
  content: "/";
  color: var(--sand);
  opacity: 0.3;
  margin-left: clamp(1rem, 3vw, 3rem);
}

/* The .packages and .package rules that sat here went out with the markup
   they styled. Restoring the section means restoring both:
   git show 814d381:index.html and git show 814d381:styles.css */

/* ============================ FOOTER ============================ */

/* Direction B from hero-variants.html, rejected as a hero because it fought
   the wordmark, reused here where its bias toward the call to action is
   exactly what a closing section wants. */
/* The footer is a column: the charcoal/red split field on top, then the
   Acknowledgement as one full-width band beneath it. The split has to be its
   own wrapper. Left as direct children of a flex .footer, the Acknowledgement
   became a third column and crushed both panels. */
.footer { display: flex; flex-direction: column; }
.footer__split { display: flex; min-height: 70svh; }

.footer__dark {
  flex: 1.1;
  background: var(--charcoal);
  padding: var(--gutter);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: clamp(2rem, 6vh, 4rem);
}
.footer__red {
  flex: 1;
  background: var(--red);
  color: var(--charcoal);
  padding: var(--gutter);
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: clamp(1rem, 3vh, 1.5rem);
}

.footer__pitch {
  font-weight: 600;
  font-size: clamp(1.5rem, 3.2vw, 2.75rem);
  line-height: 1.1;
  letter-spacing: var(--track-tight);
  max-width: 16ch;
}
.footer__email {
  display: inline-block;
  font-weight: 600;
  font-size: clamp(1.125rem, 2vw, 1.75rem);
  letter-spacing: var(--track-tight);
  color: var(--red);
  align-self: flex-start;
  word-break: break-word;
  border-bottom: 0.08em solid transparent;
  padding-block: 0.7rem;      /* 21px tap target, same fix as the hero */
  margin-block: -0.4rem;
}
.footer__email:hover,
.footer__email:focus-visible { border-bottom-color: var(--red); }
.footer__legal { font-size: 0.75rem; opacity: 0.6; }   /* 0.4 measured 3.47:1 */

.footer__red .label { opacity: 1; }   /* charcoal on red at 0.7 measured 3.69:1 */

/* Form. Underlines rather than boxes: fewer lines on the page, and it keeps
   the red field feeling like a statement rather than an admin screen. */
.form { display: flex; flex-direction: column; gap: clamp(0.75rem, 2vh, 1.25rem); }
.form label {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  opacity: 1;
  display: block;
  margin-bottom: 0.35rem;
}
.form input,
.form textarea {
  width: 100%;
  background: none;
  border: 0;
  border-bottom: 1px solid rgba(1,1,1,0.35);
  color: var(--charcoal);
  font: inherit;
  font-weight: 600;
  letter-spacing: var(--track-body);
  padding: 0.4rem 0;
  border-radius: 0;
}
.form textarea { resize: vertical; min-height: 4.5rem; }
.form input:focus,
.form textarea:focus { outline: none; border-bottom-color: var(--charcoal); }
.form input::placeholder,
.form textarea::placeholder { color: rgba(1,1,1,0.4); }

.form button {
  align-self: flex-start;
  margin-top: 0.5rem;
  background: var(--charcoal);
  color: var(--sand);
  border: 0;
  font: inherit;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-size: 0.8125rem;
  padding: 0.85rem 1.75rem;
  cursor: pointer;
}
.form button:disabled { opacity: 0.5; cursor: default; }

/* A real person never sees this. Anything in it is a bot. */
.form .hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.form__status { font-size: 0.875rem; font-weight: 600; line-height: 1.4; }
.form__status a { text-decoration: underline; }

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

@media (max-width: 50rem) {
  .footer__split { flex-direction: column; min-height: 0; }
  .footer__dark, .footer__red { flex: none; }
}

/* ========================== ARTICLES ==========================
   The articles section exists for search traffic, which is a different
   buyer from the one the front page is built for. A producer arriving from
   a cold email is skimming; someone arriving from a search result is
   reading. So these pages are set for reading: a single measure, larger
   body type, and no motion competing with the text.

   They share this stylesheet with index.html so a brand change lands
   everywhere at once. That is the whole reason the CSS came out of
   index.html. */

.site-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 1.5rem;
  flex-wrap: wrap;
  padding: clamp(1.5rem, 4vh, 2.5rem) var(--gutter);
  border-bottom: 1px solid #222;
}
.site-head .wordmark { font-size: clamp(1.125rem, 2vw, 1.5rem); }
.site-head__nav {
  display: flex;
  gap: clamp(1rem, 2.5vw, 2rem);
  font-size: clamp(0.625rem, 1.1vw, 0.75rem);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.site-head__nav a { opacity: 0.6; }
.site-head__nav a:hover,
.site-head__nav a:focus-visible { opacity: 1; }
.site-head__nav a[aria-current="page"] { opacity: 1; color: var(--red); }

/* One column, one measure. 68ch is the reading measure; the front page uses
   much shorter measures because nothing there is meant to be read in full. */
.article {
  padding: clamp(3rem, 9vh, 6rem) var(--gutter);
  max-width: 68ch;
}
.article__meta {
  font-size: clamp(0.625rem, 1.1vw, 0.75rem);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #8A8A85;
}
.article__title {
  font-weight: 700;
  font-size: clamp(1.875rem, 5vw, 3.25rem);
  line-height: 1.05;
  letter-spacing: var(--track-tight);
  text-wrap: balance;
  padding-top: clamp(0.75rem, 2vh, 1.25rem);
}
.article__standfirst {
  font-size: clamp(1.0625rem, 2vw, 1.375rem);
  line-height: 1.45;
  letter-spacing: var(--track-tight);
  opacity: 0.8;
  padding-top: clamp(1rem, 3vh, 1.75rem);
}
.article__body { padding-top: clamp(1.75rem, 5vh, 3rem); }
.article__body > * + * { margin-top: 1.15em; }
.article__body p,
.article__body li {
  font-size: clamp(1rem, 1.6vw, 1.125rem);
  line-height: 1.65;
  opacity: 0.86;
}
.article__body h2 {
  font-weight: 600;
  font-size: clamp(1.25rem, 2.6vw, 1.75rem);
  line-height: 1.15;
  letter-spacing: var(--track-tight);
  margin-top: 2em;
  opacity: 1;
}
.article__body h3 {
  font-weight: 600;
  font-size: clamp(1.0625rem, 2vw, 1.3125rem);
  letter-spacing: var(--track-tight);
  margin-top: 1.6em;
}
.article__body ul, .article__body ol { padding-left: 1.1rem; }
.article__body li + li { margin-top: 0.4em; }

/* A four-column comparison will not fit a phone, and shrinking the type until
   it does makes it unreadable. The wrapper scrolls on its own axis instead,
   so the table gets its width and the page body still never scrolls
   sideways. tabindex makes the scroll region reachable from the keyboard,
   which is the part that is easy to leave out. */
.article__table-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}
.article__table {
  border-collapse: collapse;
  min-width: 34rem;
  width: 100%;
  font-size: clamp(0.8125rem, 1.4vw, 0.9375rem);
  line-height: 1.4;
}
.article__table th,
.article__table td {
  text-align: left;
  vertical-align: top;
  padding: 0.7rem 1rem 0.7rem 0;
  border-bottom: 1px solid rgba(248, 247, 238, 0.12);
}
/* The header row and the row labels carry full sand. The cells sit at 0.78,
   which composites to 11.4:1 on charcoal, so the grid reads as label against
   content rather than as one flat block. */
.article__table thead th,
.article__table tbody th { font-weight: 600; }
.article__table td { opacity: 0.78; }
.article__table thead th { border-bottom-color: rgba(248, 247, 238, 0.3); }

/* Selectors carry the section prefix so they outrank .article__body h2 and
   .article__body li on specificity. The alternative was !important, which
   would then have to be beaten by another !important the next time. */
.article__body h2.article__sources-head {
  font-size: clamp(0.625rem, 1.1vw, 0.75rem);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: #8A8A85;
  margin-top: 3em;
}
.article__body ul.article__sources { padding-left: 1.1rem; }
.article__body ul.article__sources li {
  font-size: clamp(0.8125rem, 1.3vw, 0.875rem);
}
.article__body a {
  color: var(--red);
  border-bottom: 0.08em solid transparent;
}
.article__body a:hover,
.article__body a:focus-visible { border-bottom-color: var(--red); }
.article__body blockquote {
  border-left: 2px solid var(--red);
  padding-left: 1rem;
  opacity: 0.9;
}

/* Index of articles. A list, for the same reason the work section is a
   list: the titles are the content, and a grid would shrink them. */
.article-list { padding: 0 var(--gutter) clamp(4rem, 12vh, 9rem); }
.article-list__item {
  display: block;
  padding: clamp(1.5rem, 4vh, 2.25rem) 0;
  border-bottom: 1px solid #222;
}
.article-list__item h2 {
  font-weight: 600;
  font-size: clamp(1.25rem, 3vw, 2rem);
  line-height: 1.1;
  letter-spacing: var(--track-tight);
  text-wrap: balance;
}
.article-list__item p {
  font-size: clamp(0.9375rem, 1.5vw, 1.0625rem);
  line-height: 1.5;
  opacity: 0.7;
  max-width: 60ch;
  padding-top: 0.5rem;
}
.article-list__item:hover h2 { color: var(--red); }

/* The one action on an article page. Search traffic that reads to the end
   is the only traffic here worth converting, so it gets a real block
   rather than a link in the footer. */
.article-cta {
  margin: clamp(2.5rem, 7vh, 4rem) 0 0;
  padding: clamp(1.75rem, 5vh, 2.75rem);
  border: 1px solid #222;
}
.article-cta p {
  font-weight: 600;
  font-size: clamp(1.125rem, 2.2vw, 1.5rem);
  line-height: 1.2;
  letter-spacing: var(--track-tight);
  opacity: 1;
}
.article-cta a {
  display: inline-block;
  color: var(--red);
  font-weight: 600;
  font-size: clamp(1rem, 1.7vw, 1.25rem);
  letter-spacing: var(--track-tight);
  border-bottom: 0.08em solid transparent;
  padding-block: 0.75rem;
  margin-block: -0.25rem;
  word-break: break-word;
}
.article-cta a:hover,
.article-cta a:focus-visible { border-bottom-color: var(--red); }

.site-foot {
  padding: clamp(2rem, 6vh, 3.5rem) var(--gutter);
  border-top: 1px solid #222;
  display: flex;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  font-size: 0.75rem;
  opacity: 0.6;
}

/* The one outbound link in the footer of the conversion page. Underlined so
   it reads as a link at colophon size, where a colour change alone would
   not be obvious enough. */
.footer__link {
  border-bottom: 1px solid currentColor;
  padding-bottom: 0.1em;
}
.footer__link:hover,
.footer__link:focus-visible { color: var(--red); border-bottom-color: var(--red); }

/* --- the screen reel page ----------------------------------------
   /screen/ is a link pasted into an email to one director. It borrows the
   article measure for its prose, but the pieces themselves break out of
   68ch, because a producer is watching them rather than reading them.

   .work__thumb comes across from the front page unchanged, triangle and
   all, so a piece here reads as the same object it does there. */
.reel-section { padding: clamp(1rem, 3vh, 2rem) var(--gutter) clamp(2rem, 6vh, 4rem); }
.reel__head { max-width: 68ch; }
.reel__head h2 {
  font-weight: 600;
  font-size: clamp(1.25rem, 2.6vw, 1.75rem);
  line-height: 1.15;
  letter-spacing: var(--track-tight);
}
.reel__head p {
  font-size: clamp(1rem, 1.6vw, 1.125rem);
  line-height: 1.65;
  opacity: 0.86;
  margin-top: 0.6em;
}
/* The second .article picks up the top padding a page-opening article wants,
   which is far too much when it is following a section. */
.article__body > h2.reel__how-head { margin-top: 0; }
.reel {
  max-width: 78rem;
  padding-top: clamp(1.25rem, 4vh, 2.5rem);
}
.reel__item {
  display: grid;
  grid-template-columns: minmax(0, 22rem) 1fr;
  gap: clamp(1rem, 2.5vw, 2rem);
  align-items: start;
  padding: clamp(1.5rem, 4vh, 2.5rem) 0;
  border-bottom: 1px solid rgba(248, 247, 238, 0.12);
}
.reel__item h3 {
  font-weight: 600;
  font-size: clamp(1.0625rem, 2vw, 1.3125rem);
  letter-spacing: var(--track-tight);
}
.reel__item figcaption > * + * { margin-top: 0.6em; }
.reel__item figcaption p {
  font-size: clamp(1rem, 1.6vw, 1.125rem);
  line-height: 1.65;
  opacity: 0.86;
}
/* Same rule as the front page: the credit is the arrangement made visible,
   so it renders on every piece and is never dropped. 0.62 composites to
   7.4:1 on charcoal, which is the floor this project holds credits to. */
.reel__item .reel__credit {
  font-size: clamp(0.75rem, 1.3vw, 0.8125rem);
  line-height: 1.5;
  opacity: 0.62;
}
/* The reel slot before the reel exists. Dashed, so it is obvious to Harry
   that the page is unfinished and never reads as a design choice. */
.reel__pending {
  border: 1px dashed rgba(248, 247, 238, 0.3);
  padding: clamp(1.25rem, 4vh, 2rem);
  border-radius: 0.5rem;
}
/* The built iframe takes the slot the thumbnail button was holding, at the
   same 16:9, so nothing below it moves when a piece opens. */
.reel__video {
  aspect-ratio: 16 / 9;
  background: #141414;
  overflow: hidden;
}
.reel__video iframe { width: 100%; height: 100%; border: 0; display: block; }

@media (max-width: 46rem) {
  .reel__item { grid-template-columns: 1fr; }
}
/* The thumbnail carries its own clamped width on the front page, where it
   sits in an auto grid column. Here the column already sets the width, so
   the clamp only fights it and the stills come out at different sizes.

   The margin reset is the important half. Under 34rem the front page bleeds
   the still past both gutters on purpose, using a width calc and a matching
   negative margin. Overriding only the width leaves the negative margin
   behind, and the still hangs off the left edge while stopping short on the
   right. Both have to go together or neither. */
.reel .work__thumb { width: 100%; margin-left: 0; }
