/* Frosted Editor — editorial creator portfolio */
:root {
  --ink: #17151a;
  --ink-soft: #4a4550;
  --vermillion: #e8401f;
  --vermillion-deep: #b82c12;
  --mist: #e9e7ef;
  --mist-2: #f3f1f7;
  --paper: #ffffff;
  --line: rgba(23, 21, 26, .14);
  --glass: rgba(255, 255, 255, .55);
  --glass-strong: rgba(255, 255, 255, .76);
  --shadow: 0 24px 70px rgba(23, 21, 26, .12);
  --display: "Archivo", "Helvetica Neue", Arial, sans-serif;
  --mono: "JetBrains Mono", ui-monospace, SFMono-Regular, Menlo, monospace;
  --pad: clamp(1.25rem, 3.2vw, 2.5rem);
  --max: 1400px;

  /* aliases retained for the admin stylesheet */
  --grey: var(--mist);
  --grey-warm: var(--mist-2);
  --grey-deep: #d5d2dc;
  --red: var(--vermillion);
  --red-ink: var(--vermillion-deep);
  --white: var(--paper);
  --sans: var(--display);
}

* { box-sizing: border-box; }
html { scroll-behavior: smooth; }
html, body { margin: 0; padding: 0; }
body {
  min-width: 320px;
  background: var(--mist);
  color: var(--ink);
  font-family: var(--display);
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
}
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(circle at 0 5%, rgba(232,64,31,.14), transparent 28rem),
    radial-gradient(circle at 100% 18%, rgba(23,21,26,.08), transparent 30rem);
}
a { color: inherit; }
::selection { background: var(--vermillion); color: var(--paper); }
.skip { position: fixed; left: -9999px; top: 1rem; z-index: 100; }
.skip:focus { left: 1rem; background: var(--ink); color: var(--paper); padding: .75rem 1rem; border-radius: 999px; }

.display { margin: 0; color: var(--ink); font-weight: 900; line-height: .92; text-transform: uppercase; text-wrap: balance; }
.display--xl { font-size: clamp(3rem, 8vw, 7.5rem); }
.display--l { font-size: clamp(2.5rem, 6vw, 5rem); }
.display--m { font-size: clamp(1.55rem, 3vw, 2.5rem); }
.display--s { font-size: clamp(1.1rem, 1.6vw, 1.4rem); }
.micro { margin: 0; color: var(--ink-soft); font-family: var(--mono); font-size: 10px; font-weight: 500; line-height: 1.45; text-transform: uppercase; }
.micro--right { text-align: right; }
.micro--block { max-width: 18rem; }
.micro em { opacity: .6; font-style: normal; }
.marker { margin: 0; color: var(--vermillion); font-family: var(--mono); font-size: clamp(1.6rem, 3vw, 2.6rem); font-weight: 500; line-height: 1; }
.wordmark { color: var(--ink); font-size: .875rem; font-weight: 800; text-decoration: none; text-transform: uppercase; }
.wordmark::before { content: var(--mark, ""); display: inline-grid; width: 1.75rem; height: 1.75rem; margin-right: .55rem; place-items: center; background: var(--vermillion); color: var(--paper); font-size: .8rem; vertical-align: middle; }

.nav { display: flex; flex-wrap: wrap; align-items: center; gap: .55rem 1.6rem; }
.nav a { color: var(--ink-soft); font-size: .75rem; font-weight: 700; text-decoration: none; text-transform: uppercase; transition: color .2s ease; }
.nav a:hover, .nav a.is-active { color: var(--vermillion); }

/* Home: header, statement, then a fluid editorial work grid */
/* hidden, not clip: the reel below pins part of itself with position:sticky,
   and overflow:clip on an ancestor -- unlike hidden -- does not let a fixed
   or sticky descendant escape it, which throttles the very thing this rule
   sits above. hidden keeps the horizontal clipping this was written for
   without taking sticky down with it. */
body.is-home { overflow-x: hidden; }
.home { min-height: 100vh; }
/* One row, and it takes the slack. The rows used to be auto 1fr auto with
   the content in the first of them, so every pixel the rail was taller
   than its contents fell between the statement and the videos. With the
   slack above instead, the headline sits on the feed at a distance this
   sets rather than one the window decides, and the gap is the same
   whatever the screen is doing. */
.rail { min-height: clamp(30rem, 56vh, 42rem); max-width: var(--max); margin: 0 auto; padding: 7.5rem var(--pad) clamp(.75rem, 1.5vw, 1.25rem); display: grid; grid-template-columns: minmax(0, 1fr) auto; grid-template-rows: 1fr; gap: 2rem; align-items: end; }
.rail__top { position: fixed; inset: 0 0 auto; z-index: 30; display: flex; align-items: center; justify-content: space-between; gap: 2rem; padding: 1rem max(var(--pad), calc((100vw - var(--max)) / 2 + var(--pad))); background: rgba(233,231,239,.78); border-bottom: 1px solid rgba(255,255,255,.55); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); }
.rail__mid { grid-column: 1; align-self: end; max-width: 60rem; }
.statement { max-width: 13ch; touch-action: manipulation; }
/* The statement's last word on a wheel. Everything here hangs off spin--on,
   which static/js/statement.js adds once it has measured the words: with no
   JavaScript .spin is an untouched inline span and the headline is plain
   text. No transitions -- a wheel's speed changes inside a turn as well as
   between turns, so the script sets the position and the width every frame
   rather than handing either to CSS. */
.spin--on { display: inline-block; overflow: hidden; vertical-align: top; white-space: nowrap; }
.spin__reel { display: block; }
.spin__word { display: block; width: max-content; }
.spin__word[aria-hidden] { user-select: none; -webkit-user-select: none; }

/* The full stop, and the heart it beats into as the wheel drops home. Both
   are built by static/js/statement.js; the stop is a wrapper around the
   punctuation the sentence already had, so the page's text is unchanged. */
.stop { position: relative; display: inline-block; }
.stop__dot { display: inline-block; }
.stop__heart { position: absolute; left: 50%; bottom: .02em; width: .26em; height: .26em; color: var(--vermillion); fill: currentColor; opacity: 0; transform: translateX(-50%) scale(.2); pointer-events: none; }
.stop.is-hit .stop__dot { animation: stop-dot .9s ease both; }
.stop.is-hit .stop__heart { animation: stop-heart .9s cubic-bezier(.2,.7,0,1) both; }
@keyframes stop-dot {
  0% { opacity: 1; transform: scale(1); }
  14%, 74% { opacity: 0; transform: scale(.3); }
  100% { opacity: 1; transform: scale(1); }
}
@keyframes stop-heart {
  0% { opacity: 0; transform: translateX(-50%) scale(.2) rotate(-14deg); }
  18% { opacity: 1; transform: translateX(-50%) scale(1.3) rotate(6deg); }
  30% { opacity: 1; transform: translateX(-50%) scale(.88) rotate(-2deg); }
  42% { opacity: 1; transform: translateX(-50%) scale(1.08) rotate(0deg); }
  54%, 74% { opacity: 1; transform: translateX(-50%) scale(1); }
  100% { opacity: 0; transform: translateX(-50%) scale(1.5); }
}
/* The opening prompt: the brief's first question, asked as one line under
   the statement and again at the top of /enquire. The animated text is a
   ghost drawn over the field rather than its placeholder, so it can carry a
   cursor -- and so the input stays genuinely empty while it plays. */
.ask { display: grid; gap: .7rem; max-width: 34rem; margin-top: clamp(1.5rem, 3vw, 2.25rem); }
.ask__label { color: var(--ink-soft); font-family: var(--mono); font-size: 10px; font-weight: 500; line-height: 1.45; text-transform: uppercase; }
.ask__field { position: relative; display: block; }
/* the two share their metrics, so the ghost cannot drift off the text it is
   standing in for */
.ask__input, .ask__ghost { padding: .95rem 3.6rem .95rem 1.1rem; font: 500 1rem/1.5 var(--display); }
.ask__input { width: 100%; border: 1px solid var(--line); border-radius: 999px; background: var(--glass-strong); color: var(--ink); outline: 0; -webkit-backdrop-filter: blur(18px); backdrop-filter: blur(18px); transition: border-color .2s ease, box-shadow .2s ease; }
.ask__input:focus { border-color: var(--vermillion); box-shadow: 0 0 0 3px rgba(232,64,31,.14); }
.ask__input::placeholder { color: var(--ink-soft); opacity: .6; }
.ask__ghost { position: absolute; inset: 0; display: none; align-items: center; overflow: hidden; border: 1px solid transparent; color: var(--ink-soft); white-space: pre; pointer-events: none; }
.ask.is-prompting .ask__ghost { display: flex; }
.ask__caret { flex: none; width: 1px; height: 1.15em; margin-left: 1px; background: var(--vermillion); animation: ask-blink 1.05s steps(1, end) infinite; }
@keyframes ask-blink { 0%, 55% { opacity: 1; } 56%, 100% { opacity: 0; } }

.ask__send { position: absolute; top: 50%; right: .42rem; display: grid; width: 2.6rem; height: 2.6rem; padding: 0; place-items: center; border: 0; border-radius: 999px; background: var(--ink); color: var(--paper); cursor: pointer; transform: translateY(-50%); transition: background .2s ease, transform .2s ease; }
.ask__send:hover, .ask__send:focus-visible { background: var(--vermillion); transform: translateY(-50%) scale(1.06); }
.ask__send svg { width: 1.05rem; height: 1.05rem; fill: none; stroke: currentColor; stroke-width: 1.7; }

.rail__mid::before { content: "(A) CREATOR — SHORT-FORM VIDEO"; display: block; margin-bottom: 1.25rem; color: var(--vermillion); font-family: var(--mono); font-size: .68rem; font-weight: 500; }
.enquire { display: inline-flex; align-items: center; margin-top: 2rem; padding: .85rem 1.3rem; border: 0; border-radius: 999px; background: var(--ink); color: var(--paper); font-size: .78rem; font-weight: 800; text-decoration: none; text-transform: uppercase; transition: background .2s ease, transform .2s ease; }
.enquire::after { content: "→"; margin-left: .75rem; }
.enquire:hover { background: var(--vermillion); transform: translateY(-2px); }

/* The enquiry form, always one tap away: an envelope parked bottom right on
   every page but the one it points at. Above the fixed masthead, and on a
   handset it drops its label and keeps the icon. */
.enquirecta { position: fixed; right: clamp(1rem,3vw,2rem); bottom: clamp(1rem,3vw,2rem); z-index: 40; display: inline-flex; align-items: center; gap: .6rem; padding: .85rem 1.3rem; border-radius: 999px; background: var(--ink); color: var(--paper); font-size: .78rem; font-weight: 800; text-decoration: none; text-transform: uppercase; box-shadow: 0 14px 34px rgba(23,21,26,.26); transition: background .2s ease, transform .2s ease, box-shadow .2s ease; }
.enquirecta:hover, .enquirecta:focus-visible { background: var(--vermillion); transform: translateY(-2px); box-shadow: 0 18px 40px rgba(232,64,31,.32); }
.enquirecta svg { flex: none; width: 1.15rem; height: 1.15rem; fill: none; stroke: currentColor; stroke-width: 1.7; }
.rail__bottom { grid-column: 2; display: grid; min-width: 12rem; gap: 1rem; justify-items: end; }

.canvas { max-width: var(--max); height: auto; margin: 0 auto; padding: 0 var(--pad) 6rem; overflow: visible; }
body.is-home .canvas { scroll-snap-type: none; }
.feed { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: clamp(1rem, 2vw, 1.4rem); }
.slide { display: block; height: auto; min-width: 0; padding: 0; scroll-snap-align: none; }
.slide__inner { display: flex; flex-direction: column; width: 100%; }
.phone { position: relative; width: 100%; padding: 0; border: 0; border-radius: min(1.2vw, 14px); overflow: hidden; background: var(--mist-2); box-shadow: 0 10px 35px rgba(23,21,26,.08); }
.phone::before, .phone::after, .phone__speaker, .phone__home { display: none; }
.frame { position: relative; width: 100%; aspect-ratio: var(--ar, 9/16); overflow: hidden; background: #d5d2dc; }
.phone .frame { border-radius: 0; }
.frame > div { position: absolute !important; inset: 0; width: 100%; height: 100%; padding: 0 !important; }
.frame iframe, .frame video { max-width: 100%; position: absolute; inset: 0; width: 100% !important; height: 100% !important; border: 0; }
.frame__idle { display: grid; place-items: center; background: linear-gradient(145deg, var(--mist-2), #d8d4df); }
.slide__meta { padding: .9rem .15rem 0; }
.slide__title { margin: .4rem 0 .25rem; font-size: clamp(1rem, 1.3vw, 1.2rem); }
.slide__caption { min-height: 2.8em; }
.statline { display: grid; grid-template-columns: repeat(3, 1fr); gap: .4rem; margin-top: .85rem; padding-top: .7rem; border-top: 1px solid var(--line); }
.statline__value { display: block; margin-top: .2rem; color: var(--ink); font-size: .95rem; font-weight: 700; }
.watch { display: inline-block; margin-top: 1rem; color: var(--vermillion); font-family: var(--mono); font-size: 10px; font-weight: 500; text-decoration: none; text-transform: uppercase; }
.watch:hover { color: var(--vermillion-deep); }

.slide--panel { grid-column: span 2; min-height: 0; margin-top: 2rem; padding: clamp(2rem,4vw,3rem); border: 1px solid rgba(255,255,255,.65); border-radius: min(1.5vw,18px); background: var(--glass); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); }
.panelblock { width: 100%; }
.panelblock__title { margin: .65rem 0 1.5rem; }
.quote { margin: 1.2rem 0 0; }
.quote__text { margin: 0; color: var(--ink); font-size: clamp(1.35rem, 2.5vw, 2.35rem); font-weight: 700; line-height: 1.14; text-transform: none; text-wrap: balance; }
.quote__by { margin-top: 1.25rem; color: var(--vermillion); }
.quote + .quote { margin-top: 2rem; padding-top: 2rem; border-top: 1px solid var(--line); }
/* The clients rail. One set of logos is laid out --sets times and slid a
   single set's width over and over, so the belt never shows its end. The
   window carries the fade and the clipping; the strip's border and paper sit
   outside it, so they stay solid all the way to the edge. */
.brands { border: 1px solid var(--line); background: rgba(255,255,255,.46); }
.brands__window { overflow: hidden; -webkit-mask: linear-gradient(90deg, transparent, #000 clamp(2rem,10%,5rem), #000 calc(100% - clamp(2rem,10%,5rem)), transparent); mask: linear-gradient(90deg, transparent, #000 clamp(2rem,10%,5rem), #000 calc(100% - clamp(2rem,10%,5rem)), transparent); }
.brands__rail { display: flex; width: max-content; animation: brands-roll var(--roll, 24s) linear infinite; }
/* :focus-within alone pauses on a plain mouse click too, and a click here
   opens a new tab (target="_blank") that never blurs the link behind it —
   so the belt stayed paused for good the moment someone returned to this
   tab. :focus-visible only matches real keyboard focus, which is the case
   this pause is actually for. */
.brands:hover .brands__rail, .brands:has(:focus-visible) .brands__rail { animation-play-state: paused; }
.brands__set { display: flex; margin: 0; padding: 0; list-style: none; }
.brand { flex: none; }
.brand > * { display: grid; min-height: 6rem; padding: 1rem clamp(1.5rem,3.5vw,3rem); place-items: center; text-align: center; text-decoration: none; opacity: .68; transition: opacity .25s ease; }
.brand > a:hover, .brand > a:focus-visible { opacity: 1; }
.brand__logo { display: block; height: 2.4rem; width: calc(2.4rem * var(--ratio, 3)); background: var(--ink-soft); -webkit-mask: var(--logo) center/contain no-repeat; mask: var(--logo) center/contain no-repeat; }
.brand__img { display: block; height: 3rem; width: auto; aspect-ratio: var(--ratio, auto); object-fit: contain; }
.brand__name { color: var(--ink-soft); font-size: 11px; font-weight: 800; text-transform: uppercase; white-space: nowrap; }
@keyframes brands-roll { to { transform: translateX(calc(-100% / var(--sets, 2))); } }
.feed--empty { min-height: 50vh; display: grid; place-content: center; text-align: center; }

/* Interior pages */
.masthead { position: sticky; top: 0; z-index: 20; display: flex; align-items: center; justify-content: space-between; gap: 2rem; padding: 1rem max(var(--pad), calc((100vw - var(--max))/2 + var(--pad))); background: rgba(233,231,239,.82); border-bottom: 1px solid rgba(255,255,255,.65); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); }
.sheet { max-width: var(--max); margin: 0 auto; padding: 0 var(--pad); }
.pagehead { display: grid; grid-template-columns: 1fr auto; gap: 2rem; align-items: end; padding: clamp(4rem,9vw,8rem) 0 clamp(2.5rem,5vw,4rem); border-bottom: 1px solid var(--line); }
.pagehead__title { max-width: 14ch; }
.pagehead__note { padding-bottom: .5rem; }
.pagehead--cta { padding: clamp(2.5rem,5vw,4rem) 0; border: 0; }
.lede { margin: 0; max-width: 22ch; color: var(--ink); font-size: clamp(1.5rem,3vw,2.7rem); font-weight: 700; line-height: 1.1; }
.split { display: grid; grid-template-columns: 1fr 1fr; gap: clamp(2rem,7vw,7rem); padding: clamp(3rem,6vw,5rem) 0; border-bottom: 1px solid var(--line); }
.split__lead { display: grid; align-content: start; gap: clamp(1.5rem,3vw,2.25rem); }
/* the profile picture: a 4:5 frame, so any upload is cropped to fit it and
   the layout never depends on the file's own shape */
.portrait { width: min(100%, 22rem); margin: 0; overflow: hidden; aspect-ratio: 4 / 5; border: 1px solid var(--line); border-radius: min(1.2vw,14px); background: var(--mist-2); box-shadow: 0 10px 35px rgba(23,21,26,.08); }
.portrait__img { display: block; width: 100%; height: 100%; object-fit: cover; }
.portrait__placeholder { display: block; width: 100%; height: 100%; fill: var(--grey-deep); }
.portrait--admin { width: 11rem; }
.split__notes { display: grid; gap: 1.15rem; justify-items: end; }
.split__notes .micro { max-width: 18rem; }
.body { max-width: 48ch; color: var(--ink-soft); font-size: 1rem; line-height: 1.7; }
.body p { margin: 0 0 1.2rem; }

/* /about: the picture beside the opening statement, then the argument itself
   as numbered chapters — the same editorial row as a service, given room for
   prose instead of a single line */
.split--about { align-items: start; }
.about__opening { display: grid; align-content: start; gap: clamp(1.5rem, 3vw, 2.25rem); }
.about__intro { max-width: 46ch; }
.about__intro p:last-child { margin-bottom: 0; }
.chapters { list-style: none; margin: 0; padding: clamp(2.5rem, 5vw, 4rem) 0 1rem; }
.chapter { display: grid; grid-template-columns: 4rem minmax(0, 1fr) minmax(0, 1.35fr); gap: clamp(1.5rem, 4vw, 3.5rem); align-items: start; padding: clamp(1.75rem, 3vw, 2.5rem) 0; border-bottom: 1px solid var(--line); }
.chapter:first-child { border-top: 1px solid var(--line); }
.chapter__n { color: var(--vermillion); }
.chapter__title { max-width: 14ch; }
.chapter__body { max-width: 46ch; }
.chapter__body p:last-child { margin-bottom: 0; }

.spec { border-top: 1px solid var(--line); }
.spec__row { display: grid; grid-template-columns: 1fr 1.35fr; gap: 1.5rem; align-items: end; padding: 1.15rem 0; border-bottom: 1px solid var(--line); }
.spec__row > :last-child { text-align: right; color: var(--ink); }
.spec--tight .spec__row { grid-template-columns: 1fr auto; padding: .55rem 0; }
.grid { display: grid; grid-template-columns: repeat(auto-fill,minmax(245px,1fr)); gap: clamp(1.25rem,2.5vw,2rem); padding: 3rem 0 6rem; }
.card { min-width: 0; display: flex; flex-direction: column; }
.frame--card { height: auto; border-radius: min(1.2vw,14px); box-shadow: 0 10px 35px rgba(23,21,26,.08); }
.card__meta { padding-top: .9rem; }
.card__title { margin: .4rem 0; font-size: 1.3rem; }
.card__caption { min-height: 2.8em; margin-bottom: .9rem; }
.services { list-style: none; margin: 0; padding: 2rem 0 5rem; }
.service { display: grid; grid-template-columns: 4rem 1fr 1fr; gap: 1.5rem; align-items: start; padding: 1.75rem 0; border-bottom: 1px solid var(--line); }
.service:first-child { border-top: 1px solid var(--line); }
.service__body { max-width: 30rem; justify-self: end; text-align: right; }

.form { display: grid; max-width: 58rem; padding: 2rem 0 5rem; }
.field { display: grid; grid-template-columns: 10rem 1fr; gap: 1.5rem; align-items: center; padding: 1rem 0; border-bottom: 1px solid var(--line); }
.field input, .field select, .field textarea { width: 100%; padding: .75rem 0; border: 0; border-radius: 0; outline: 0; background: transparent; color: var(--ink); font: 500 1rem var(--display); text-align: right; }
.field textarea { min-height: 9rem; padding: 1rem; border: 1px solid var(--line); border-radius: 8px; background: rgba(255,255,255,.35); line-height: 1.55; resize: vertical; text-align: left; }
.field input:focus, .field select:focus { box-shadow: 0 2px 0 var(--vermillion); }
.field textarea:focus { border-color: var(--vermillion); box-shadow: 0 0 0 3px rgba(232,64,31,.14); }
.field input::placeholder, .field textarea::placeholder { color: var(--ink-soft); opacity: .5; }
.field--stack { grid-template-columns: 1fr; gap: .5rem; }
.field--stack input, .field--stack select { text-align: left; }
.field .hint { grid-column: 1/-1; opacity: .7; }
.button { justify-self: start; margin-top: 1.5rem; padding: .9rem 1.35rem; border: 0; border-radius: 999px; background: var(--ink); color: var(--paper); cursor: pointer; font: 800 .72rem var(--display); text-transform: uppercase; transition: background .2s ease, transform .2s ease; }
.button:hover { background: var(--vermillion); transform: translateY(-2px); }
.button--ghost { border: 1px solid var(--line); background: transparent; color: var(--ink); }
.formerror { margin: 1.25rem 0 0; padding: 1rem; border-radius: 8px; background: var(--vermillion); color: var(--paper); font-size: .75rem; font-weight: 700; }

.siteend { margin-top: 2rem; padding: clamp(3rem,6vw,5rem) max(var(--pad),calc((100vw - var(--max))/2 + var(--pad))) 2rem; border-top: 1px solid var(--line); }
.contents { list-style: none; margin: 0 0 3rem; padding: 0; }
.contents li { border-bottom: 1px solid var(--line); }
.contents a { display: grid; grid-template-columns: 4.5rem 1fr auto; gap: 1.5rem; align-items: baseline; padding: 1rem 0; text-decoration: none; }
.contents a .display { transition: color .2s ease, transform .2s ease; }
.contents a:hover .display { color: var(--vermillion); transform: translateX(.35rem); }
.contents .micro { text-align: right; }
.siteend__foot { display: flex; align-items: flex-end; justify-content: space-between; gap: 2rem; }

/* ---------------------------------------------------------------------------
   Carried over from the pre-redesign stylesheet, restyled for Frosted Editor.
   The redesign dropped these rules; the markup that uses them is still live.
   --------------------------------------------------------------------------- */

/* landscape counterpart to the handset. The redesign already strips the phone
   down to a rounded well, so this is the same well for a film. */
.screen { position: relative; width: 100%; overflow: hidden; border-radius: min(1.2vw,14px); background: var(--mist-2); box-shadow: 0 10px 35px rgba(23,21,26,.08); }
.screen .frame { border-radius: 0; }
.slide--wide { grid-column: span 2; }

/* location line: pin + text between caption and stats */
.loc { display: flex; align-items: center; gap: .4rem; margin-top: .55rem; }
.loc__pin { flex-shrink: 0; width: 10px; height: 14px; color: var(--vermillion); }
.card__caption + .loc { margin-top: -.35rem; margin-bottom: .9rem; }

/* portfolio filters: label left, options right, hairline between */
.filter { margin-bottom: clamp(1.75rem,3.5vw,2.75rem); border-top: 1px solid var(--line); }
.filter__row { display: grid; grid-template-columns: 10rem 1fr; gap: 1.5rem; align-items: baseline; padding: .9rem 0; border-bottom: 1px solid var(--line); }
.filter__opts { display: flex; flex-wrap: wrap; gap: .5rem 1.1rem; justify-content: flex-end; }
.filter__opt { padding: 3px 0; border-radius: 999px; color: var(--ink-soft); font-family: var(--mono); font-size: 10.5px; font-weight: 500; text-decoration: none; text-transform: uppercase; transition: color .2s ease; }
.filter__opt:hover { color: var(--vermillion); }
.filter__opt.is-active { padding: 3px 10px; background: var(--vermillion); color: var(--paper); }
.filter__opt.is-active:hover { background: var(--vermillion-deep); color: var(--paper); }
.filter__opt.is-empty { opacity: .35; cursor: default; }
.filter__opt.is-empty:hover { color: var(--ink-soft); }
.filter__opt--clear { margin-left: .35rem; opacity: .55; }
.filter__opt--clear:hover { opacity: 1; }
.filter__count { margin-left: .35rem; opacity: .55; }
.grid__empty { padding: 2rem 0 4rem; }

/* category tags: a video's sectors as small pills, each a link to that filter */
.chips { display: flex; flex-wrap: wrap; gap: .35rem; margin: .15rem 0 .7rem; padding: 0; list-style: none; }
.chip { display: inline-block; padding: 3px 9px; border: 1px solid var(--line); border-radius: 999px; background: var(--glass); color: var(--ink-soft); font-family: var(--mono); font-size: 9.5px; font-weight: 500; line-height: 1.5; text-decoration: none; text-transform: uppercase; transition: background .2s ease, border-color .2s ease, color .2s ease; }
.chip__n { margin-left: .5em; opacity: .55; }
.chip:hover .chip__n, .chip:focus-visible .chip__n { opacity: .85; }
.summary .chips { margin: 0 0 .5rem; }
.summary .chip { padding: 5px 12px; font-size: 10.5px; }
.chip:hover, .chip:focus-visible { border-color: var(--vermillion); background: var(--vermillion); color: var(--paper); }

/* client marks: who a video was made for, as their own small round mark.
   They overlap by a third, the way a row of avatars does, so four of them
   still read as one credit rather than as four separate things; the one on
   the left is drawn on top, which is the order they are credited in. The
   mark lifts clear of its neighbours on hover, because the whole point is
   that each one is a link to that client's own site. */
.clients { display: flex; align-items: center; margin: .1rem 0 .55rem; padding: 0; list-style: none; }
.clients__item { display: flex; }
.clients__item + .clients__item { margin-left: -.42rem; }
.clientmark { display: grid; width: 1.45rem; height: 1.45rem; place-items: center; overflow: hidden; border: 1px solid var(--line); border-radius: 50%; background: var(--paper); box-shadow: 0 0 0 2px var(--mist); color: var(--ink-soft); text-decoration: none; transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease; }
.clients__item:hover .clientmark, .clientmark:focus-visible { z-index: 2; transform: translateY(-2px) scale(1.12); border-color: var(--vermillion); }
.clientmark__img { width: 100%; height: 100%; object-fit: contain; }
/* a favicon drawn for a dark browser chrome is a white mark on nothing, and
   on a white circle that is an empty circle. logo_facts() reads the ink, and
   the circle turns over under the ones that need it. */
.clientmark--dark { border-color: var(--ink); background: var(--ink); }
.clientmark__initial { font-size: 9px; font-weight: 800; letter-spacing: 0; line-height: 1; }
/* the row of items is drawn left to right, so without this the last one
   would sit on top of the first -- the reverse of the billing order */
.clients__item:nth-child(1) { z-index: 4; }
.clients__item:nth-child(2) { z-index: 3; }
.clients__item:nth-child(3) { z-index: 2; }
.clients__item:nth-child(4) { z-index: 1; }

/* under the title on a project page there is room for them to be bigger,
   and they are the first thing that says who the work was for */
.clients--lg { margin: .75rem 0 0; }
.clients--lg .clientmark { width: 2.4rem; height: 2.4rem; box-shadow: 0 0 0 3px var(--mist); }
.clients--lg .clients__item + .clients__item { margin-left: -.7rem; }
.clients--lg .clientmark__initial { font-size: 14px; }

.spec__row a { color: inherit; text-decoration: underline; text-decoration-color: var(--line); text-underline-offset: 3px; }
.spec__row a:hover { color: var(--vermillion); text-decoration-color: currentColor; }

@media (prefers-reduced-motion: reduce) {
  .clientmark { transition: none; }
  .clients__item:hover .clientmark { transform: none; }
}

/* The feed is a grid of everything now, so a list of jump links beside it
   has nothing left to navigate. The markup and feed.js hooks stay in place. */
.railindex { display: none; }

/* ---------------------------------------------------------------------------
   Project pages (/work/<id>-<slug>), FAQ, related work
   --------------------------------------------------------------------------- */

/* titles in the feed and the grid link through to the project; keep the
   display type looking like display type until it is hovered */
.slide__title a, .card__title a, .pagehead__title a { color: inherit; text-decoration: none; transition: color .2s ease; }
.slide__title a:hover, .card__title a:hover { color: var(--vermillion); }
.slide__more, .card__more { margin-top: .6rem; }
.slide__more a, .card__more a { color: var(--vermillion); text-decoration: none; }
.slide__more a:hover, .card__more a:hover { color: var(--vermillion-deep); }

.work { display: grid; grid-template-columns: minmax(0,.9fr) minmax(0,1.1fr); gap: clamp(2rem,5vw,4.5rem); align-items: start; padding: clamp(2.5rem,5vw,4rem) 0; }
.work__media { max-width: 26rem; }
.work__body { display: grid; gap: 1.25rem; }
.work__body .body { max-width: 52ch; }
.work__body .statline { margin-top: .5rem; }

.related { padding: clamp(3rem,6vw,4.5rem) 0 0; }
.related .contents { margin-top: 1rem; margin-bottom: 0; }

.faq { padding: clamp(3rem,6vw,5rem) 0 0; }
.faq__row { display: grid; grid-template-columns: minmax(0,1fr) minmax(0,1.1fr); gap: clamp(1.5rem,4vw,3.5rem); align-items: start; padding: 1.5rem 0; border-bottom: 1px solid var(--line); }
.faq__row:first-of-type { margin-top: 1rem; border-top: 1px solid var(--line); }
.faq__q { max-width: 18ch; margin: 0; }
.faq__a { max-width: 48ch; margin: 0; color: var(--ink-soft); font-size: 1rem; line-height: 1.7; }

@media (max-width: 950px) {
  .filter__row { grid-template-columns: 1fr; gap: .5rem; }
  .filter__opts { justify-content: flex-start; }
  .work { grid-template-columns: 1fr; }
  .work__media { max-width: 22rem; }
  .faq__row { grid-template-columns: 1fr; gap: .75rem; }
  .faq__q { max-width: none; }
}

@media (max-width: 600px) {
  .enquirecta { padding: .95rem; }
  .enquirecta__label { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }
  /* a single-column feed has no second column for a wide film to span */
  .slide--wide { grid-column: auto; }
}

@keyframes rise { from { opacity: 0; transform: translateY(16px); } to { opacity: 1; transform: none; } }
.rail__mid, .rail__bottom, .feed > *, .pagehead > * { animation: rise .65s cubic-bezier(.2,.7,0,1) both; }
.feed > :nth-child(2) { animation-delay: .08s; }
.feed > :nth-child(3) { animation-delay: .16s; }
.feed > :nth-child(4) { animation-delay: .24s; }

@media (max-width: 950px) {
  .rail { min-height: 58vh; grid-template-columns: 1fr; grid-template-rows: 1fr auto; padding-top: 8.5rem; }
  .rail__top { flex-wrap: wrap; row-gap: .6rem; padding: .85rem var(--pad) .75rem; }
  .rail__top .nav { flex-basis: 100%; gap: .4rem 1rem; }
  .rail__bottom { grid-column: 1; justify-items: start; }
  .feed { grid-template-columns: repeat(2,minmax(0,1fr)); }
  .slide--panel { grid-column: 1/-1; }
  .pagehead { grid-template-columns: 1fr; gap: 1.25rem; }
  .pagehead__note, .micro--right { text-align: left; }
  .split { grid-template-columns: 1fr; }
  .service { grid-template-columns: 3rem 1fr; }
  .service__body { grid-column: 2; justify-self: start; text-align: left; }
  .chapter { grid-template-columns: 3rem 1fr; gap: .75rem 1.5rem; }
  .chapter__body { grid-column: 2; }
  .chapter__title { max-width: none; }
  .contents a { grid-template-columns: 3rem 1fr; }
  .contents .micro:last-child { display: none; }
}

@media (max-width: 600px) {
  .rail { min-height: 34rem; min-width: 0; }
  .rail__mid, .rail__bottom { min-width: 0; width: 100%; }
  .statement { max-width: 100%; overflow-wrap: anywhere; }
  .feed { grid-template-columns: 1fr; }
  .rail__top .nav { gap: .5rem 1rem; }
  /* Sized with real slack, not just a fit: the spin word (statement.js)
     swaps in alternates of different widths as it animates, and the old
     clamp left ~5% of the line's width spare, so a wider alternate --
     or just a phone with a larger accessibility text size -- pushed a
     word past the edge and overflow-wrap broke it mid-word. ~15% smaller
     leaves room for that swing. */
  .statement { font-size: clamp(2.3rem,12vw,3.9rem); }
  .canvas { padding-bottom: 3rem; }
  .feed { gap: 2.5rem 1rem; }
  .slide__caption { min-height: 0; }
  .statline { grid-template-columns: 1fr; }
  .statline > div { display: flex; justify-content: space-between; }
  .slide--panel { padding: 1.5rem; }
  /* the rail's own sizes are fixed rems with nothing to shrink them, so a
     desktop-height logo cell — sized to sit comfortably in a wide belt —
     read as oversized against a phone's width */
  .brand > * { min-height: 4rem; padding: .65rem clamp(1rem,6vw,1.75rem); }
  .brand__logo { height: 1.6rem; width: calc(1.6rem * var(--ratio, 3)); }
  .brand__img { height: 2rem; }
  .masthead { align-items: flex-start; flex-direction: column; gap: 1rem; }
  .masthead .nav { gap: .5rem 1rem; }
  .grid { grid-template-columns: 1fr; }
  .field { grid-template-columns: 1fr; gap: .35rem; }
  .field input, .field select { text-align: left; }
  .contents a { grid-template-columns: 2.5rem 1fr; }
  .siteend__foot { align-items: flex-start; flex-direction: column; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation: none !important; transition: none !important; }
  /* a belt that cannot move would leave half its logos clipped off to the
     right, so the one set that is really there wraps and centres instead */
  .brands__window { -webkit-mask: none; mask: none; }
  .brands__rail { width: auto; }
  .brands__set { flex-wrap: wrap; justify-content: center; width: 100%; }
  .brands__set[aria-hidden] { display: none; }
}

/* ---------------------------------------------------------------------------
   /enquire — the six-step brief
   --------------------------------------------------------------------------- */

/* what to expect, before anyone starts typing */
.brief { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 1px; margin-top: clamp(2rem, 4vw, 3rem); overflow: hidden; border: 1px solid var(--line); border-radius: min(1.2vw, 14px); background: var(--line); }
.brief__row { display: grid; align-content: start; gap: .55rem; padding: 1.25rem 1.35rem; background: var(--glass); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); }
.brief__row .micro:first-child { color: var(--vermillion); }
.brief__row .micro:last-child { color: var(--ink); font-family: var(--display); font-size: .95rem; font-weight: 700; line-height: 1.45; text-transform: none; }

.wizard { display: block; padding: 0 0 5rem; }

/* the six steps as a contents bar: a table of contents with no script, a
   progress indicator with it */
.track { list-style: none; display: grid; grid-template-columns: repeat(6, minmax(0, 1fr)); gap: .6rem; margin: clamp(2.5rem, 5vw, 3.5rem) 0 0; padding: 0; }
.track__step { display: grid; align-content: start; gap: .4rem; padding-top: .65rem; border-top: 2px solid var(--line); transition: border-color .25s ease, opacity .25s ease; }
.track__n { color: var(--ink-soft); }
.track__step.is-done { border-top-color: var(--ink); }
.track__step.is-current { border-top-color: var(--vermillion); }
.track__step.is-current .track__n, .track__step.is-current .track__label { color: var(--vermillion); }
.track__step.is-skipped { opacity: .3; }

/* a step is a fieldset: with script off every one of them is on the page and
   the form is a single long brief, which posts identically */
/* a <legend> sits on the fieldset's border box rather than inside its
   padding, so the room above a step has to be asked for on the legend */
.step { min-width: 0; margin: 0; padding: 0; border: 0; }
.step__head { display: grid; gap: .55rem; width: 100%; padding: clamp(2.25rem, 4.5vw, 3.5rem) 0 clamp(1.4rem, 3vw, 2rem); }
/* moving to a step focuses its heading so a screen reader lands there; it
   is not in the tab order, so it does not need a ring drawn round it */
.step__head:focus { outline: 0; }
.step__count { color: var(--vermillion); }
.step__title { display: block; }
.step__blurb { max-width: 48ch; }

/* the fork step: three icons, not three paragraphs — WhatsApp, book a
   meeting, or type it out. Same hairline-grid trick as .brief, and
   align-items: start so opening the booking embed only grows its own
   cell rather than stretching its siblings to match. */
.fork { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); align-items: start; gap: 1px; overflow: hidden; border: 1px solid var(--line); border-radius: min(1.2vw, 14px); background: var(--line); }
.fork__opt { display: grid; place-items: center; gap: .6rem; padding: clamp(1.5rem, 3vw, 2rem) 1rem; border: 0; background: var(--glass); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); color: var(--ink); text-align: center; text-decoration: none; cursor: pointer; transition: background .2s ease; }
.fork__opt:hover, .fork__opt:focus-visible { background: rgba(255,255,255,.65); }
.fork__opt--booking { padding: 0; }
.fork__opt--booking summary { display: grid; place-items: center; gap: .6rem; padding: clamp(1.5rem, 3vw, 2rem) 1rem; list-style: none; }
.fork__opt--booking summary::-webkit-details-marker,
.fork__opt--booking summary::marker { display: none; content: ""; }
.fork__icon { display: grid; place-items: center; width: 2.75rem; height: 2.75rem; border-radius: 999px; background: var(--ink); color: var(--paper); }
.fork__icon svg { width: 1.3rem; height: 1.3rem; fill: none; stroke: currentColor; stroke-width: 1.6; }
.fork__label { font-family: var(--display); font-weight: 700; font-size: .95rem; }

/* the scheduler, revealed under its own icon when the <details> opens —
   a fixed height that fits a Google Calendar appointment page */
.fork__embed { padding: 0 1.25rem clamp(1.5rem, 3vw, 2rem); display: grid; gap: .65rem; }
.fork__embed iframe { width: 100%; height: clamp(24rem, 44vw, 30rem); border: 0; border-radius: 10px; background: rgba(255,255,255,.5); }
.fork__embed .micro { opacity: .75; }

.qs { list-style: none; margin: 0; padding: 0; border-top: 1px solid var(--line); }
.q { display: grid; grid-template-columns: 4rem minmax(0, 1fr); gap: clamp(1rem, 3vw, 2.5rem); padding: clamp(1.4rem, 2.5vw, 2rem) 0; border-bottom: 1px solid var(--line); }
.q__n { padding-top: .35rem; color: var(--vermillion); }
.q__main { display: grid; min-width: 0; gap: .65rem; }
.q__label { margin: 0; color: var(--ink); font-family: var(--display); font-size: clamp(1.05rem, 1.6vw, 1.25rem); font-weight: 700; line-height: 1.3; }
.q__req { margin-left: .6rem; color: var(--vermillion); font-family: var(--mono); font-size: 9.5px; font-weight: 500; text-transform: uppercase; vertical-align: middle; }
.q__hint { max-width: 54ch; opacity: .85; }
.q__control { margin-top: .15rem; }

.q__line, .q__text, .q__select select { width: 100%; border: 1px solid var(--line); border-radius: 10px; background: rgba(255,255,255,.5); color: var(--ink); font: 500 1rem var(--display); outline: 0; transition: border-color .2s ease, box-shadow .2s ease; }
.q__line { max-width: 30rem; padding: .7rem .9rem; }
.q__text { min-height: 10rem; padding: 1rem; border-radius: 12px; line-height: 1.6; resize: vertical; }
.q__line:focus, .q__text:focus, .q__select select:focus { border-color: var(--vermillion); box-shadow: 0 0 0 3px rgba(232,64,31,.14); }
.q__line::placeholder, .q__text::placeholder { color: var(--ink-soft); opacity: .55; }

/* the brief's opening question, carried over from the home page — it leads
   the form, so it gets a little more room and a little more weight */
.q--lede .q__label { font-size: clamp(1.25rem, 2.1vw, 1.6rem); }
.q--lede .q__text { min-height: 13rem; }

.q__select { position: relative; display: block; max-width: 24rem; }
.q__select select { appearance: none; -webkit-appearance: none; padding: .7rem 2.4rem .7rem .9rem; cursor: pointer; }
.q__select::after { content: "▾"; position: absolute; top: 50%; right: 1rem; color: var(--vermillion); font-size: .75rem; pointer-events: none; transform: translateY(-50%); }

/* choices as pills — the same language as the portfolio's category chips */
.opts { display: flex; flex-wrap: wrap; gap: .5rem; }
.opt { position: relative; display: inline-flex; align-items: center; padding: .55rem .95rem; border: 1px solid var(--line); border-radius: 999px; background: var(--glass); color: var(--ink-soft); cursor: pointer; font-family: var(--mono); font-size: 10.5px; font-weight: 500; line-height: 1.5; text-transform: uppercase; transition: background .18s ease, border-color .18s ease, color .18s ease; }
.opt input { position: absolute; width: 1px; height: 1px; margin: 0; opacity: 0; pointer-events: none; }
.opt:hover { border-color: var(--vermillion); color: var(--ink); }
.opt:has(input:checked) { border-color: var(--vermillion); background: var(--vermillion); color: var(--paper); }
.opt:has(input:focus-visible) { box-shadow: 0 0 0 3px rgba(232,64,31,.25); }

/* a browser without :has() cannot paint the pill from its input, so it shows
   the input instead — plainer, but never ambiguous about what is ticked */
@supports not selector(:has(*)) {
  .opt { gap: .5rem; }
  .opt input { position: static; width: auto; height: auto; opacity: 1; pointer-events: auto; accent-color: var(--vermillion); }
}

/* the date behind "a specific date" */
.q__extra { display: grid; gap: .45rem; margin-top: .9rem; padding-left: .9rem; border-left: 2px solid var(--vermillion); }
.q__extra .q__line { max-width: 14rem; }

.q.is-missing .q__label { color: var(--vermillion); }
.q.is-missing .q__line, .q.is-missing .q__text, .q.is-missing .q__select select { border-color: var(--vermillion); }

/* the specific reason, right under the field it belongs to — rendered
   server-side when a no-JS submission bounces, filled in by enquiry.js
   the moment a script-driven check catches the same problem earlier */
.q__error { display: none; margin: .5rem 0 0; color: var(--vermillion); font-weight: 600; }
.q.is-missing .q__error { display: block; }

/* the field a person never sees */
.hp { display: none; }

.consent { max-width: 56ch; margin-top: clamp(2rem, 4vw, 3rem); }
.consent .micro { line-height: 1.65; }
.consent a { color: var(--vermillion); }

.wizard__nav { display: flex; flex-wrap: wrap; align-items: center; gap: .75rem; margin-top: clamp(1.5rem, 3vw, 2.25rem); }
.wizard__nav .button { margin-top: 0; }
.wizard__status { margin-top: 1rem; color: var(--vermillion); }
.wizard__status:empty { display: none; }

/* one step at a time, once the script is running */
.wizard.is-live .step { display: none; }
.wizard.is-live .step.is-current { display: block; animation: rise .45s cubic-bezier(.2,.7,0,1) both; }

/* what happens next, on the thank-you page */
.next { list-style: none; margin: 0; padding: clamp(2rem, 4vw, 3rem) 0 1rem; }
.next__step { display: grid; grid-template-columns: 4rem minmax(0, 1fr); gap: clamp(1rem, 3vw, 2.5rem); padding: 1.35rem 0; border-bottom: 1px solid var(--line); }
.next__step:first-child { border-top: 1px solid var(--line); }
.next__n { padding-top: .2rem; color: var(--vermillion); }
.next__step h2 { margin: 0 0 .45rem; }
.next__step .micro { max-width: 48ch; }

@media (max-width: 950px) {
  .brief, .fork { grid-template-columns: 1fr; }
  .track__label { display: none; }
}

@media (max-width: 600px) {
  .q, .next__step { grid-template-columns: 2.5rem minmax(0, 1fr); gap: .5rem 1rem; }
  .q__n, .next__n { padding-top: .5rem; }
  .wizard__nav .button { flex: 1 1 auto; text-align: center; justify-content: center; }
  /* the scheduler's own page is a wide, desktop-shaped layout — the old
     24rem floor (from a clamp meant for wider screens) squeezed it into a
     box shorter than its content needs, so it read as squished rather than
     just narrow. Taller, and closer to the card's full width, gives its
     calendar and time-slot list room to lay out properly. */
  .fork__embed { padding: 0 .5rem 1.25rem; }
  .fork__embed iframe { height: 34rem; }
}

/* ---------------------------------------------------------------------------
   Home page — the reel (featured work)

   One control underneath both readings of it: the index the scroll
   position rounds to, set by static/js/reel.js as --pos (and --dist, its
   absolute value) on every .reelcard. Below 951px that index steps a
   single phone up through the deck and the words sit over the picture, the
   way they do in the apps this work is cut for; at 951px and up the same
   index slides the deck sideways with its neighbours showing either side
   and the words take the column beside it.

   The handset is always whole. --phone-w is capped by the height left
   after the masthead, the chin and the hint have taken theirs, so the
   frame shrinks to fit a short laptop in landscape rather than running off
   the bottom of it -- a reel you have to scroll to see the end of the
   frame is not a reel.

   Before the script runs (or with none at all) .reel carries no .is-live
   class and everything below is a plain stack of the same work in reading
   order, the way .wizard falls back to every step at once.
   --------------------------------------------------------------------------- */

.reel {
  position: relative;
  margin: clamp(.25rem,1vw,.75rem) 0 clamp(2.5rem,5vw,4rem);
  /* where the phone comes to rest under the fixed masthead, the white
     chin it stands on, and the radius all three of its corners share */
  --reel-top: clamp(5.75rem, 15vw, 7rem);
  --phone-chin: clamp(2.5rem, 7.5vw, 3rem);
  --phone-r: 1.9rem;
  /* the strip along the bottom of the picture left clear for the embed's
     own controls: no shield over it, no words, no heart */
  --reel-controls: 2.75rem;
  --phone-w: min(72vw, 16.5rem, calc((100vh - var(--reel-top) - var(--phone-chin) - 6rem) * 9 / 16));
  --phone-screen: calc(var(--phone-w) * 16 / 9);
}
/* svh, where it is understood: a handset's address bar sliding away must
   not restate the frame's size mid-scroll */
@supports (height: 100svh) {
  .reel { --phone-w: min(72vw, 16.5rem, calc((100svh - var(--reel-top) - var(--phone-chin) - 6rem) * 9 / 16)); }
}
@media (max-width: 950px) {
  .reel { --phone-w: min(72vw, 16.5rem, calc((100vh - var(--reel-top) - var(--phone-chin) - 9.5rem) * 9 / 16)); }
  @supports (height: 100svh) {
    .reel { --phone-w: min(72vw, 16.5rem, calc((100svh - var(--reel-top) - var(--phone-chin) - 9.5rem) * 9 / 16)); }
  }
}

.reel__stage { display: flex; flex-direction: column; gap: clamp(1.25rem,3vw,2rem); }
.reel__lede { order: 2; display: grid; gap: 1.5rem; }
.reel__viewer { order: 1; display: block; }
.reel__hint { order: 3; }

.reelmeta { padding-top: 1.5rem; border-top: 1px solid var(--line); }
.reelmeta:first-child { padding-top: 0; border-top: 0; }
.reelmeta__n { color: var(--vermillion); }
.reelmeta__title { margin: .5rem 0 .35rem; font-size: clamp(1.3rem, 2.6vw, 1.9rem); }
.reelmeta__title a { color: inherit; text-decoration: none; transition: color .2s ease; }
.reelmeta__title a:hover { color: var(--vermillion); }
.reelmeta__caption { max-width: 36ch; margin-top: .35rem; }
.reelmeta__more { margin-top: .85rem; }
.reelmeta__more a { color: var(--vermillion); text-decoration: none; }
.reelmeta__more a:hover { color: var(--vermillion-deep); }

.reel__phone { width: 100%; margin: 0; }
.reel__window { overflow: visible; }
.reel__deck { display: grid; gap: 1.75rem; }
.reelcard { position: static; width: 100%; }
.reelcard .frame { border-radius: min(1.2vw,14px); box-shadow: 0 10px 35px rgba(23,21,26,.08); }
.reelcard__shield, .reelcard--cta, .reel__arrow, .reel__chin,
.reel__hint, .heart, .reel__outline { display: none; }

.reelend { display: grid; align-content: center; justify-items: center; gap: .55rem; width: 100%; height: 100%; padding: clamp(1.5rem,5vw,2.5rem) 1.25rem; text-align: center; background: linear-gradient(160deg, var(--mist-2), #d8d4df); }
.reelend__title { max-width: 11ch; font-size: clamp(1.3rem, 5.5vw, 1.75rem); }
.reelend__note { max-width: 20ch; }
.reelend .enquire { margin-top: 1rem; }

/* ----------------------------------------------------------- is-live: on */

.reel.is-live .reel__phone { position: relative; width: var(--phone-w); flex: none; margin: 0 auto; }
.reel.is-live .reel__window {
  position: relative; width: 100%; height: var(--phone-screen);
  overflow: hidden; background: var(--mist-2);
  border-radius: var(--phone-r) var(--phone-r) 0 0;
}
/* the chin: the one piece of furniture saying how much there is to see,
   kept inside the frame so it can never be the bit below the fold */
.reel.is-live .reel__chin {
  display: grid; place-items: center; height: var(--phone-chin);
  background: var(--paper);
  border-radius: 0 0 var(--phone-r) var(--phone-r);
}
.reel.is-live .reel__deck { position: relative; display: block; width: 100%; height: 100%; }
.reel.is-live .reelcard { position: absolute; inset: 0; display: flex; align-items: center; justify-content: center; }
.reel.is-live .reelcard .frame { position: absolute; inset: 0; width: 100%; height: 100%; aspect-ratio: auto; border-radius: 0; box-shadow: none; }
.reel.is-live .reelcard__shield { display: block; position: absolute; inset: 0; z-index: 2; }
.reel.is-live .reelcard.is-far { display: none; }
.reel.is-live .reelcard--cta { display: flex; }
.reel.is-live .reelmeta { display: none; }
.reel.is-live .reelmeta.is-active { display: block; animation: rise .5s cubic-bezier(.2,.7,0,1) both; }

/* the outline, and the four marks that make a rounded rectangle read as a
   handset: the island and the right button on the outline itself, the two
   left buttons on the frame under it */
.reel.is-live .reel__outline {
  display: block; position: absolute; inset: -.5rem; z-index: 6;
  border: 2px solid var(--vermillion);
  border-radius: calc(var(--phone-r) + .5rem);
  pointer-events: none;
}
.reel.is-live .reel__outline::before {
  content: ""; position: absolute; top: .62rem; left: 50%;
  width: 32%; height: .7rem; transform: translateX(-50%);
  border: 1.7px solid var(--vermillion); border-radius: 999px;
}
.reel.is-live .reel__outline::after {
  content: ""; position: absolute; right: -2px; top: 27%;
  width: 2px; height: 13%;
  background: var(--vermillion); border-radius: 2px;
}
.reel.is-live .reel__phone::before, .reel.is-live .reel__phone::after {
  content: ""; position: absolute; left: calc(-.5rem - 2px); z-index: 6;
  width: 2px; background: var(--vermillion); border-radius: 2px;
}
.reel.is-live .reel__phone::before { top: 21%; height: 6.5%; }
.reel.is-live .reel__phone::after { top: 31%; height: 11%; }

.reel.is-live .reel__arrow {
  display: grid; place-items: center; width: 2.6rem; height: 2.6rem;
  flex: none; border: 1px solid var(--line); border-radius: 999px;
  background: var(--glass-strong); color: var(--ink); cursor: pointer;
  -webkit-backdrop-filter: blur(12px); backdrop-filter: blur(12px);
  transition: background .2s ease, color .2s ease, opacity .2s ease;
}
.reel.is-live .reel__arrow svg { width: 1.1rem; height: 1.1rem; fill: none; stroke: currentColor; stroke-width: 2; }
.reel.is-live .reel__arrow--prev svg { transform: rotate(180deg); }
.reel.is-live .reel__arrow:hover { background: var(--ink); color: var(--paper); }
.reel.is-live .reel__arrow:disabled { opacity: .3; cursor: default; pointer-events: none; }

.reel.is-live .reel__ticks { display: flex; gap: .25rem; list-style: none; margin: 0; padding: 0; }
.tick { padding: .3rem .5rem; border: 0; border-radius: 999px; background: transparent; color: var(--ink-soft); font-family: var(--mono); line-height: 1; cursor: pointer; transition: background .2s ease, color .2s ease; }
.tick:hover { color: var(--ink); }
.tick.is-active { background: var(--ink); color: var(--paper); }
.tick--end { color: var(--vermillion); }
.tick--end.is-active { background: var(--vermillion); color: var(--paper); }

.reel.is-live .reel__hint { display: flex; align-items: center; justify-content: center; gap: .5rem; color: var(--ink-soft); opacity: 1; transition: opacity .3s ease; }
.reel.is-live .reel__hint.is-gone { opacity: 0; pointer-events: none; }
.reel__chev { display: inline-flex; width: 1rem; height: 1rem; }
.reel__chev svg { width: 100%; height: 100%; fill: none; stroke: currentColor; stroke-width: 1.8; }
.reel__hinttext--d { display: none; }

/* the like button and its burst, over the picture rather than under it --
   a sibling of the window, so the window's own overflow never clips it */
.reel.is-live .heart {
  display: grid; position: absolute; right: .7rem; z-index: 7;
  /* up the right-hand side rather than down in the corner, where the
     caption is: the same rail the apps put their own buttons on */
  bottom: calc(var(--phone-chin) + var(--reel-controls) + .4rem);
  width: 2.4rem; height: 2.4rem; place-items: center; border: 0;
  border-radius: 999px; background: rgba(23,21,26,.4); color: var(--paper);
  cursor: pointer; -webkit-backdrop-filter: blur(8px); backdrop-filter: blur(8px);
}
/* nothing to like on the way out */
.reel.is-live.is-end .heart { display: none; }
.heart__mark { width: 1.3rem; height: 1.3rem; fill: none; stroke: currentColor; stroke-width: 1.8; transform-origin: center; animation: heart-idle 2.6s ease-in-out infinite; transition: fill .2s ease, stroke .2s ease; }
.heart.is-liked .heart__mark { fill: var(--vermillion); stroke: var(--vermillion); }
.heart.is-beat .heart__mark { animation: heart-beat .55s cubic-bezier(.2,.7,0,1); }
@keyframes heart-idle { 0%, 100% { transform: scale(1); } 50% { transform: scale(1.08); } }
@keyframes heart-beat { 0% { transform: scale(1); } 30% { transform: scale(1.4); } 50% { transform: scale(.88); } 70% { transform: scale(1.15); } 100% { transform: scale(1); } }
.heart__burst { position: absolute; inset: 0; overflow: visible; pointer-events: none; }
.heart__pip { position: absolute; right: .95rem; bottom: 1.1rem; width: .95rem; height: .95rem; color: var(--vermillion); opacity: 0; transform: translate(0,0) rotate(0) scale(.3); animation: heart-pip .95s cubic-bezier(.15,.7,.3,1) both; animation-delay: var(--wait, 0ms); }
.heart__pip svg { width: 100%; height: 100%; fill: currentColor; stroke: none; }
@keyframes heart-pip {
  0% { opacity: 0; transform: translate(0,0) rotate(0) scale(.3); }
  12% { opacity: 1; transform: translate(calc(var(--dx,0px) * .15), calc(var(--dy,0px) * .15)) rotate(calc(var(--r,0deg) * .3)) scale(var(--s,1)); }
  75% { opacity: .9; }
  100% { opacity: 0; transform: translate(var(--dx,0px), var(--dy,0px)) rotate(var(--r,0deg)) scale(calc(var(--s,1) * .8)); }
}

/* ----------------------------------------------------- is-live: <= 950px
   One phone, and the words inside it. Each card slides in from below and
   the last one out above, so the frame itself never moves -- only what is
   behind it does, which is the "lock". */

@media (max-width: 950px) {
  .reel.is-live .reel__stage { position: sticky; top: var(--reel-top); z-index: 5; gap: .75rem; }
  /* on a handset the ask rides in the pinned stage, just above the phone,
     so it stays on screen the whole way through the reel (reel.js moves it) */
  .reel__stage > .ask { order: 0; width: 100%; max-width: 30rem; margin: 0 auto; padding: .25rem 0; }
  .reel__stage > .ask .ask__label { display: none; }
  .reel__stage > .ask .ask__input, .reel__stage > .ask .ask__ghost { padding-top: .7rem; padding-bottom: .7rem; font-size: .95rem; }
  .reel__stage > .ask .ask__send { width: 2.25rem; height: 2.25rem; }
  /* the shield stops short of the controls strip, so play / pause and the
     scrubber can be reached */
  .reel.is-live .reelcard__shield { bottom: var(--reel-controls); }
  .reel.is-live .reel__viewer { display: flex; justify-content: center; }
  .reel.is-live .reel__arrow { display: none; }

  /* the words over the picture: a scrim up from the bottom of the frame,
     so the type holds whatever is playing under it */
  .reel.is-live .reel__lede {
    position: absolute; top: var(--ask-h, 0px); left: 50%; z-index: 5;
    width: var(--phone-w); height: var(--phone-screen);
    transform: translateX(-50%);
    display: flex; align-items: flex-end;
    padding: 2.5rem 3.4rem calc(var(--reel-controls) + .35rem) .95rem;
    overflow: hidden;
    border-radius: var(--phone-r) var(--phone-r) 0 0;
    background: linear-gradient(to top, rgba(14,12,17,.88), rgba(14,12,17,.5) 34%, rgba(14,12,17,0) 68%);
    pointer-events: none;
  }
  /* the end card says all of this itself, in the frame */
  .reel.is-live.is-end .reel__lede { display: none; }
  .reel.is-live .reelmeta { min-width: 0; color: var(--paper); }
  .reel.is-live .reelmeta .micro { color: rgba(255,255,255,.82); font-size: 9.5px; }
  /* the type inside the frame is measured against the frame, not the
     window: the handset stops growing at 16.5rem however wide the screen
     around it gets, and a title set in vw would carry on past it */
  .reel.is-live .reelmeta__title {
    margin: .25rem 0 .2rem; color: var(--paper); line-height: 1.02;
    font-size: clamp(.78rem, calc(var(--phone-w) * .072), 1rem);
    overflow-wrap: anywhere; text-wrap: balance;
  }
  .reel.is-live .reelmeta__caption { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; max-width: none; }
  /* a caption, not a data sheet: the tags and the counts have the column
     on a wide screen and the project page everywhere, and neither is worth
     a line of a handset's screen */
  .reel.is-live .reelmeta .chips, .reel.is-live .reelmeta .statline,
  .reel.is-live .reelmeta .watch { display: none; }
  .reel.is-live .reelmeta .clients { margin: .3rem 0 .4rem; }
  .reel.is-live .reelmeta .clientmark { border-color: rgba(255,255,255,.3); box-shadow: 0 0 0 2px rgba(14,12,17,.35); }
  .reel.is-live .reelmeta a { pointer-events: auto; }
  .reel.is-live .reelmeta__more { margin-top: .5rem; }
  .reel.is-live .reelmeta__more a { color: var(--paper); }

  .reel.is-live .reelcard {
    transform: translateY(calc(var(--pos, 0) * 104%));
    transition: transform .85s cubic-bezier(.45,.05,.2,1);
    pointer-events: none;
  }
  .reel.is-live .reelcard.is-active { pointer-events: auto; }
  .reel__chev { transform: rotate(90deg); }
  .reel.is-live .reel__hint:not(.is-gone) .reel__chev { animation: hint-bounce-y 1.6s ease-in-out infinite; }
}

@keyframes hint-bounce-y { 0%, 100% { transform: rotate(90deg) translateY(0); } 50% { transform: rotate(90deg) translateY(4px); } }

/* ----------------------------------------------------- is-live: >= 951px
   Coverflow. The same --pos/--dist slide the deck sideways instead of up:
   the centre card sits under the outline, its neighbours peek in at either
   edge, scaled back and faded, everything beyond them culled. The wheel
   pages it exactly as the arrows do -- one control, two ways at it. */

@media (min-width: 951px) {
  .reel {
    --reel-top: clamp(6rem, 10vh, 7.5rem);
    --phone-w: min(21vw, 15rem, calc((100vh - var(--reel-top) - var(--phone-chin) - 8rem) * 9 / 16));
  }
  @supports (height: 100svh) {
    .reel { --phone-w: min(21vw, 15rem, calc((100svh - var(--reel-top) - var(--phone-chin) - 8rem) * 9 / 16)); }
  }
  .reel.is-live .reel__stage {
    position: sticky; top: var(--reel-top);
    display: grid; grid-template-columns: minmax(0,.9fr) minmax(0,1.1fr);
    align-items: center; gap: clamp(2.5rem, 5vw, 4.5rem);
  }
  .reel.is-live .reel__lede { order: 1; }
  .reel.is-live .reel__viewer {
    order: 2; display: flex; align-items: center; justify-content: center;
    gap: clamp(1.25rem, 2.5vw, 2.25rem);
  }
  .reel.is-live .reel__hint { grid-column: 1 / -1; justify-self: center; }
  /* the neighbours have to be able to lean out past the frame */
  .reel.is-live .reel__window { overflow: visible; }
  .reel.is-live .reel__deck { perspective: 1600px; }
  .reel.is-live .reelcard {
    overflow: hidden; background: var(--mist-2);
    border-radius: var(--phone-r) var(--phone-r) 0 0;
    box-shadow: 0 20px 50px rgba(23,21,26,.16);
    transform:
      translateX(calc(var(--pos, 0) * 74%))
      rotateY(calc(var(--pos, 0) * -12deg))
      scale(calc(1 - min(2, var(--dist, 0)) * .22));
    opacity: calc(1 - min(2, var(--dist, 0)) * .5);
    z-index: calc(3 - min(3, var(--dist, 0)));
    transition: transform .85s cubic-bezier(.45,.05,.2,1), opacity .85s ease;
    pointer-events: none;
  }
  /* only the one under the outline has a chin to meet */
  .reel.is-live .reelcard:not(.is-active) { border-radius: var(--phone-r); }
  .reel.is-live .reelcard.is-active { pointer-events: auto; box-shadow: none; }
  .reel.is-live .reel__hinttext--m { display: none; }
  .reel.is-live .reel__hinttext--d { display: inline; }
  .reel.is-live .reel__hint:not(.is-gone) .reel__chev { animation: hint-bounce-x 1.6s ease-in-out infinite; }
}

@keyframes hint-bounce-x { 0%, 100% { transform: translateX(0); } 50% { transform: translateX(4px); } }

/* Done: the reader has reached the last card, so the pin is let go for
   good. The reel becomes an ordinary block the page scrolls past in both
   directions, and the only thing that changes the video after this is the
   numbers in the chin. Later than the media blocks above on purpose --
   this has to outrank the sticky in both of them. */
.reel.is-live.is-done .reel__stage { position: static; }

/* the closing panels: the logos straight after the work, the quotes last */
.panel { max-width: var(--max); margin: clamp(1.5rem,3vw,2.5rem) auto 0; padding: clamp(2rem,4vw,3rem); border: 1px solid rgba(255,255,255,.65); border-radius: min(1.5vw,18px); background: var(--glass); backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px); }
@media (max-width: 600px) {
  .panel { padding: 1.5rem; }
}

@media (prefers-reduced-motion: reduce) {
  .reel.is-live .reelcard { transition: none; }
  .reel__chev, .heart__mark { animation: none !important; }
}

/* ---------------------------------------------------------------------------
   The wand — the pointer, on anything that has one

   The cursor is replaced by a wand drawn in the same line as the envelope
   and the pin, trailing sparks as it moves and casting a handful on click.
   static/js/wand.js builds it and only runs on a fine pointer, so a touch
   screen is left with the nothing it already had; the class it sets is
   what turns the native cursor off, so a script that never runs cannot
   leave the page without one. Typing is the exception -- a field still
   gets the caret the operating system draws.
   --------------------------------------------------------------------------- */

body.has-wand, body.has-wand * { cursor: none; }
body.has-wand input, body.has-wand textarea, body.has-wand select,
body.has-wand [contenteditable] { cursor: auto; }

.wand { position: fixed; left: 0; top: 0; z-index: 9998; pointer-events: none; will-change: transform; }
.wand__svg {
  position: absolute; left: -1.87rem; top: -.78rem;
  width: 2.6rem; height: 2.6rem; overflow: visible;
  transform-origin: 72% 30%;
  transition: transform .22s cubic-bezier(.2,.7,0,1);
  animation: wand-bob 3.4s ease-in-out infinite;
}
.wand__shaft { stroke: var(--ink); stroke-width: 2.6; stroke-linecap: round; }
.wand__grip { stroke: var(--vermillion); stroke-width: 2.6; stroke-linecap: round; }
.wand__star { fill: var(--vermillion); transform-origin: 23px 9.6px; animation: wand-twinkle 2.1s ease-in-out infinite; }
/* over anything that answers to a click, the wand cocks back to cast */
.wand.is-hot .wand__svg { transform: rotate(-14deg) scale(1.12); }
.wand.is-cast .wand__svg { animation: wand-cast .45s cubic-bezier(.2,.7,0,1); }
.wand.is-away { opacity: 0; }
@keyframes wand-bob { 0%, 100% { transform: rotate(0deg); } 50% { transform: rotate(-5deg); } }
@keyframes wand-twinkle { 0%, 100% { transform: scale(1) rotate(0deg); } 50% { transform: scale(1.18) rotate(20deg); } }
@keyframes wand-cast {
  0% { transform: rotate(0) scale(1); }
  35% { transform: rotate(-24deg) scale(.92); }
  70% { transform: rotate(8deg) scale(1.16); }
  100% { transform: rotate(0) scale(1); }
}

.spark { position: fixed; z-index: 9997; width: .55rem; height: .55rem; margin: -.275rem 0 0 -.275rem; color: var(--vermillion); pointer-events: none; animation: spark-out var(--life, .75s) cubic-bezier(.2,.7,0,1) forwards; }
.spark svg { width: 100%; height: 100%; fill: currentColor; }
.spark--ink { color: var(--ink-soft); }
@keyframes spark-out {
  0% { opacity: 1; transform: translate(0,0) scale(var(--s, 1)) rotate(0deg); }
  100% { opacity: 0; transform: translate(var(--dx, 0), var(--dy, 0)) scale(.15) rotate(var(--r, 90deg)); }
}

@media (prefers-reduced-motion: reduce) {
  .wand__svg, .wand__star { animation: none !important; }
}
