/* ==========================================================================
   Clique Studio - revamp.css
   Additive layer on top of site.css. Same tokens, same type, same colours -
   this file only adds the components the current site doesn't have yet:
   the 3D hero shell, the proof ticker, media-backed service cards, the work
   grid that plays, the pinned process, the configurator and the CTA.

   Nothing here redefines a token. If a value needs changing, change it in
   site.css section 1 and this file follows.
   ========================================================================== */

/* --------------------------------------------------------------------------
   R1. Extra tokens - derived only, no new brand colours
   -------------------------------------------------------------------------- */
:root {
  --glow-cyan:     34 211 238;          /* --c-secondary as channels */
  --hero-h:        100svh;
  --pin-len:       320vh;               /* scroll distance the hero scrubs over */
  --r-hairline:    1px solid var(--c-border);
}

/* --------------------------------------------------------------------------
   R2. 3D hero shell
   The pinned section is the scroll track. The canvas sits fixed inside it and
   the copy rides above. Without JS or WebGL the canvas never appears and the
   poster image behind it carries the section on its own.
   -------------------------------------------------------------------------- */
.stage {
  position: relative;
  /* the track the hero scrubs along; collapses to one screen when 3D is off */
  height: var(--pin-len);
}

.stage__sticky {
  position: sticky;
  top: 0;
  height: var(--hero-h);
  overflow: hidden;
  background: var(--c-bg);
}

/* Poster: the generated plate. Always present, always the bottom layer, so a
   failed canvas degrades to a still frame rather than a black box. */
.stage__poster {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.55;
  transition: opacity var(--t-slow) var(--ease);
}
/* Once the renderer has drawn its first frame it takes over from the poster. */
.stage.is-live .stage__poster { opacity: 0; }

.stage__canvas,
.stage__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  display: block;
  opacity: 0;
  transition: opacity 600ms var(--ease);
}
/* The clip is the subject, so it fills the pane and crops rather than
   letterboxing into black bars at whatever aspect the window happens to be. */
.stage__video { object-fit: cover; }
.stage.is-live .stage__canvas,
.stage.is-live .stage__video { opacity: 1; }

/* Vignette + floor fade, so the object always sits in the dark rather than
   ending at a hard canvas edge. */
.stage__veil {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(120% 80% at 50% 45%, transparent 35%, rgba(10,10,10,0.75) 100%),
    linear-gradient(to bottom, rgba(10,10,10,0.85) 0%, transparent 22%, transparent 64%, var(--c-bg) 98%);
}

.stage__copy {
  position: relative;
  z-index: 2;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  pointer-events: none;
}
.stage__copy > * { pointer-events: auto; }

.stage__title {
  font-family: var(--f-display);
  font-weight: 900;
  font-size: clamp(42px, 8.2vw, 104px);
  line-height: 0.92;
  letter-spacing: -0.045em;
  margin: var(--s-md) 0 0;
  max-width: 15ch;
  text-wrap: balance;
}
.stage__title em {
  font-style: normal;
  color: var(--c-accent);
  text-shadow: var(--glow-soft);
}

.stage__body {
  margin-top: var(--s-lg);
  max-width: 46ch;
  color: var(--c-muted-fg);
  font-size: clamp(16px, 1.5vw, 18.5px);
}

.stage__actions {
  margin-top: var(--s-xl);
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-md);
}

/* The running caption - changes as the scroll moves through the stages.
   aria-live is deliberately off: it is decorative narration of a visual, and
   announcing every change would be noise for a screen reader. */
.stage__readout {
  position: absolute;
  left: var(--gutter);
  bottom: clamp(20px, 4vh, 40px);
  z-index: 3;
  display: flex;
  align-items: baseline;
  gap: var(--s-md);
  font-family: var(--f-mono);
  font-size: 12.5px;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--c-secondary);
}
.stage__readout .num { color: var(--c-border-strong); }
.stage__readout .label { color: var(--c-fg); }

/* Progress rail down the right edge of the pinned hero. */
.stage__rail {
  position: absolute;
  right: var(--gutter);
  top: 50%;
  transform: translateY(-50%);
  z-index: 3;
  display: flex;
  flex-direction: column;
  gap: var(--s-sm);
}
.stage__rail span {
  display: block;
  width: 2px;
  height: 26px;
  background: var(--c-border);
  transition: background var(--t-base) var(--ease), box-shadow var(--t-base) var(--ease);
}
.stage__rail span.on {
  background: var(--c-accent);
  box-shadow: var(--glow-soft);
}

.scroll-hint {
  position: absolute;
  left: 50%;
  bottom: clamp(18px, 3.5vh, 34px);
  transform: translateX(-50%);
  z-index: 3;
  font-family: var(--f-mono);
  font-size: 11px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--c-border-strong);
  transition: opacity var(--t-base) var(--ease);
}
.stage.is-scrolled .scroll-hint { opacity: 0; }

/* --------------------------------------------------------------------------
   R3. Proof ticker
   -------------------------------------------------------------------------- */
.ticker {
  border-block: var(--r-hairline);
  background: var(--c-card);
  overflow: hidden;
}
.ticker__inner {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
}
.ticker__cell {
  padding: var(--s-xl) var(--s-lg);
  border-left: var(--r-hairline);
  text-align: center;
}
.ticker__cell:first-child { border-left: 0; }

.ticker__num {
  font-family: var(--f-display);
  font-weight: 800;
  font-size: clamp(30px, 4.4vw, 52px);
  line-height: 1;
  letter-spacing: -0.03em;
  font-variant-numeric: tabular-nums;
}
.ticker__label {
  margin-top: var(--s-sm);
  font-family: var(--f-mono);
  font-size: 11.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--c-muted-fg);
}

/* Three cells, not four: a 2x2 grid on phones would strand the third on a row
   of its own, so they stay on one row and give up some padding instead. */
@media (max-width: 860px) {
  .ticker__cell { padding: var(--s-lg) var(--s-sm); }
  .ticker__label { font-size: 10.5px; letter-spacing: 0.1em; }
}

/* --------------------------------------------------------------------------
   R4. Service cards, media-backed
   The existing .service card stays as it is for inner pages. This is the
   richer homepage variant: generated plate behind, type in front.
   -------------------------------------------------------------------------- */
.svc-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--s-md);
}
@media (max-width: 1000px) { .svc-grid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 640px)  { .svc-grid { grid-template-columns: 1fr; } }

.svc {
  position: relative;
  display: block;
  overflow: hidden;
  min-height: 340px;
  padding: var(--s-xl);
  border: var(--r-hairline);
  background: var(--c-card);
  color: inherit;
  text-decoration: none;
  isolation: isolate;
  transition: border-color var(--t-base) var(--ease), transform var(--t-base) var(--ease);
}
.svc:hover { border-color: var(--c-border-strong); transform: translateY(-3px); }
.svc:focus-visible { border-color: var(--c-ring); }

.svc__media {
  position: absolute;
  inset: 0;
  z-index: -1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.62;
  transform: scale(1.04);
  transition: opacity var(--t-slow) var(--ease), transform var(--t-slow) var(--ease);
}
.svc:hover .svc__media { opacity: 0.85; transform: scale(1.10); }

/* Keeps the type legible whatever the plate does underneath. */
.svc::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to top, rgba(10,10,10,0.95) 16%, rgba(10,10,10,0.55) 66%, rgba(10,10,10,0.25) 100%);
}

.svc__idx {
  font-family: var(--f-mono);
  font-size: 11.5px;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--c-secondary);
  margin: 0;
}
.svc__title {
  font-family: var(--f-display);
  font-weight: 800;
  font-size: clamp(24px, 2.6vw, 32px);
  letter-spacing: -0.03em;
  line-height: 1.05;
  margin: calc(var(--s-3xl) + var(--s-lg)) 0 0;
  display: flex;
  align-items: center;
  gap: var(--s-sm);
}
.svc__title svg { flex: none; color: var(--c-accent); transition: transform var(--t-base) var(--ease); }
.svc:hover .svc__title svg { transform: translate(3px, -3px); }

.svc__desc {
  margin: var(--s-md) 0 0;
  color: var(--c-muted-fg);
  font-size: 15.5px;
  line-height: 1.6;
}
.svc__tags {
  margin: var(--s-lg) 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-xs);
  list-style: none;
  padding: 0;
}
.svc__tags li {
  font-family: var(--f-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-muted-fg);
  border: var(--r-hairline);
  padding: 3px 8px;
}

/* --------------------------------------------------------------------------
   R5. Work that plays
   Poster image by default; the clip is only fetched and played on intent
   (hover, focus, or in-view on touch), so the grid costs one image per card
   on first paint.
   -------------------------------------------------------------------------- */
.work-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--s-md);
}
@media (max-width: 760px) { .work-grid { grid-template-columns: 1fr; } }

.wk {
  position: relative;
  display: block;
  border: var(--r-hairline);
  background: var(--c-card);
  color: inherit;
  text-decoration: none;
  overflow: hidden;
  transition: border-color var(--t-base) var(--ease);
}
.wk:hover, .wk:focus-visible { border-color: var(--c-border-strong); }

.wk__frame {
  position: relative;
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--c-muted);
}
.wk__frame img,
.wk__frame video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.wk__frame video { opacity: 0; transition: opacity var(--t-slow) var(--ease); }
.wk.is-playing .wk__frame video { opacity: 1; }
.wk__frame img { transform: scale(1.01); transition: transform var(--t-slow) var(--ease); }
.wk:hover .wk__frame img { transform: scale(1.05); }

.wk__badge {
  position: absolute;
  z-index: 2;
  left: var(--s-md);
  top: var(--s-md);
  font-family: var(--f-mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-on-accent);
  background: var(--c-accent);
  padding: 4px 9px;
}

.wk__body { padding: var(--s-lg) var(--s-xl) var(--s-xl); }
.wk__title {
  font-family: var(--f-display);
  font-weight: 800;
  font-size: clamp(20px, 2.2vw, 26px);
  letter-spacing: -0.025em;
  margin: 0;
}
.wk__desc { margin: var(--s-sm) 0 0; color: var(--c-muted-fg); font-size: 15px; }

/* Result chips. Only ever populated with a number the studio can evidence -
   the markup carries no placeholder figures. */
.wk__metrics {
  margin: var(--s-lg) 0 0;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-lg);
  list-style: none;
  padding: 0;
}
.wk__metrics li { display: flex; flex-direction: column; gap: 2px; }
.wk__metrics b {
  font-family: var(--f-display);
  font-weight: 800;
  font-size: 22px;
  letter-spacing: -0.02em;
  color: var(--c-fg);
  font-variant-numeric: tabular-nums;
}
.wk__metrics span {
  font-family: var(--f-mono);
  font-size: 10.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-muted-fg);
}

/* --------------------------------------------------------------------------
   R6. Pinned process
   Left column pins, right column scrolls its steps past it. On narrow screens
   and under reduced motion the pin is dropped and it reads as a plain list.
   -------------------------------------------------------------------------- */
.proc { display: grid; grid-template-columns: 0.85fr 1.15fr; gap: var(--s-4xl); }
@media (max-width: 900px) { .proc { grid-template-columns: 1fr; gap: var(--s-2xl); } }

.proc__pin {
  position: sticky;
  top: calc(var(--header-h) + var(--s-3xl));
  align-self: start;
}
@media (max-width: 900px) { .proc__pin { position: static; } }

.proc__count {
  margin-top: var(--s-xl);
  font-family: var(--f-mono);
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--c-secondary);
}
.proc__bar {
  margin-top: var(--s-md);
  height: 2px;
  background: var(--c-border);
  position: relative;
  overflow: hidden;
}
.proc__bar i {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0%;
  background: var(--c-accent);
  box-shadow: var(--glow-soft);
  transition: width var(--t-slow) var(--ease);
}

.proc__steps { display: flex; flex-direction: column; gap: var(--s-md); }

.proc__step {
  border: var(--r-hairline);
  background: var(--c-card);
  padding: var(--s-xl);
  transition: border-color var(--t-base) var(--ease), background var(--t-base) var(--ease);
}
.proc__step.is-current { border-color: var(--c-accent); background: var(--c-muted); }

.proc__step h3 {
  font-family: var(--f-display);
  font-weight: 800;
  font-size: clamp(21px, 2.3vw, 28px);
  letter-spacing: -0.025em;
  margin: var(--s-sm) 0 0;
}
.proc__step p { margin: var(--s-md) 0 0; color: var(--c-muted-fg); font-size: 15.5px; }
.proc__wk {
  font-family: var(--f-mono);
  font-size: 11.5px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--c-secondary);
}
.proc__step ul {
  margin: var(--s-lg) 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-xs);
}
.proc__step ul li {
  font-family: var(--f-mono);
  font-size: 10.5px;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--c-muted-fg);
  border: var(--r-hairline);
  padding: 3px 8px;
}

/* --------------------------------------------------------------------------
   R7. Configurator
   A real form, not a toy: it posts the same shape the contact form does, so a
   quote request arrives with the selection attached.
   -------------------------------------------------------------------------- */
.cfg {
  border: var(--r-hairline);
  background: var(--c-card);
  display: grid;
  grid-template-columns: 1.25fr 0.75fr;
}
@media (max-width: 900px) { .cfg { grid-template-columns: 1fr; } }

.cfg__pick { padding: var(--s-2xl); border-right: var(--r-hairline); }
@media (max-width: 900px) { .cfg__pick { border-right: 0; border-bottom: var(--r-hairline); } }

.cfg__legend {
  font-family: var(--f-mono);
  font-size: 11.5px;
  letter-spacing: 0.15em;
  text-transform: uppercase;
  color: var(--c-secondary);
  padding: 0;
  margin: 0 0 var(--s-lg);
}
.cfg fieldset { border: 0; padding: 0; margin: 0 0 var(--s-2xl); }
.cfg fieldset:last-of-type { margin-bottom: 0; }

.cfg__opts { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-sm); }
@media (max-width: 700px) { .cfg__opts { grid-template-columns: repeat(2, 1fr); } }

/* The native control stays in the accessibility tree and keeps keyboard and
   screen-reader behaviour; the label is what gets styled. */
.cfg__opt { position: relative; }
.cfg__opt input {
  position: absolute;
  opacity: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  cursor: pointer;
}
.cfg__opt span {
  display: block;
  border: 1px solid var(--c-border-strong);
  padding: var(--s-md);
  font-size: 14.5px;
  font-weight: 500;
  text-align: center;
  transition: border-color var(--t-fast) var(--ease), color var(--t-fast) var(--ease), background var(--t-fast) var(--ease);
}
.cfg__opt input:hover + span { border-color: var(--c-fg); }
.cfg__opt input:checked + span {
  border-color: var(--c-accent);
  background: rgb(var(--glow-pink) / 0.10);
  color: var(--c-fg);
}
.cfg__opt input:focus-visible + span { outline: 2px solid var(--c-ring); outline-offset: 2px; }

.cfg__out {
  padding: var(--s-2xl);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: var(--s-xl);
}
.cfg__price {
  font-family: var(--f-display);
  font-weight: 900;
  font-size: clamp(38px, 5.5vw, 62px);
  line-height: 1;
  letter-spacing: -0.04em;
  font-variant-numeric: tabular-nums;
}
.cfg__price small {
  display: block;
  font-family: var(--f-mono);
  font-weight: 400;
  font-size: 11.5px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--c-secondary);
  margin-bottom: var(--s-sm);
}
.cfg__note { color: var(--c-muted-fg); font-size: 13.5px; line-height: 1.6; }
.cfg__lines { list-style: none; margin: var(--s-lg) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--s-sm); }
.cfg__lines li {
  display: flex;
  justify-content: space-between;
  gap: var(--s-md);
  font-size: 14px;
  color: var(--c-muted-fg);
  border-bottom: var(--r-hairline);
  padding-bottom: var(--s-sm);
}
.cfg__lines li b { color: var(--c-fg); font-weight: 600; font-variant-numeric: tabular-nums; }

/* --------------------------------------------------------------------------
   R8. Marquee + CTA
   -------------------------------------------------------------------------- */
.marquee {
  overflow: hidden;
  border-block: var(--r-hairline);
  padding: var(--s-lg) 0;
  /* fade the ends so words don't clip at the viewport edge */
  mask-image: linear-gradient(to right, transparent, #000 8%, #000 92%, transparent);
}
.marquee__track {
  display: flex;
  gap: var(--s-3xl);
  width: max-content;
  animation: marquee 38s linear infinite;
}
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee span {
  font-family: var(--f-display);
  font-weight: 800;
  font-size: clamp(22px, 3vw, 38px);
  letter-spacing: -0.03em;
  color: var(--c-border-strong);
  white-space: nowrap;
}
.marquee span em { font-style: normal; color: var(--c-accent); }
@keyframes marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }

.cta {
  position: relative;
  overflow: hidden;
  border: var(--r-hairline);
  padding: clamp(40px, 7vw, 96px) var(--gutter);
  text-align: center;
  isolation: isolate;
}
.cta__media {
  position: absolute;
  inset: 0;
  z-index: -1;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.38;
}
.cta::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: radial-gradient(70% 90% at 50% 50%, rgba(10,10,10,0.55), rgba(10,10,10,0.92));
}
.cta h2 {
  font-family: var(--f-display);
  font-weight: 900;
  font-size: clamp(32px, 5.6vw, 66px);
  line-height: 0.98;
  letter-spacing: -0.04em;
  margin: 0;
  text-wrap: balance;
}
.cta p { margin: var(--s-lg) auto 0; max-width: 52ch; color: var(--c-muted-fg); }
.cta .btn { margin-top: var(--s-2xl); }

/* --------------------------------------------------------------------------
   R9. Testimonials
   Markup is here; the page only renders cards the studio has a real, approved
   quote for. No invented names ship in this file.
   -------------------------------------------------------------------------- */
.quotes { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-md); }
@media (max-width: 950px) { .quotes { grid-template-columns: 1fr; } }

.quote {
  border: var(--r-hairline);
  background: var(--c-card);
  padding: var(--s-xl);
  display: flex;
  flex-direction: column;
  gap: var(--s-lg);
}
.quote blockquote {
  margin: 0;
  font-size: 17px;
  line-height: 1.6;
  color: var(--c-fg);
}
.quote figcaption {
  font-family: var(--f-mono);
  font-size: 11.5px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--c-muted-fg);
  margin-top: auto;
}
.quote figcaption b { display: block; color: var(--c-fg); font-weight: 500; letter-spacing: 0.08em; }

/* A card that openly marks itself as awaiting real content, so an unfinished
   section can never be mistaken for a shipped claim. */
.quote--empty { border-style: dashed; }
.quote--empty blockquote { color: var(--c-border-strong); }

/* --------------------------------------------------------------------------
   R10. Section header helper
   -------------------------------------------------------------------------- */
.shead { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--s-2xl); flex-wrap: wrap; }
.shead__lede { max-width: 46ch; color: var(--c-muted-fg); }

/* --------------------------------------------------------------------------
   R11. Reduced motion and no-3D fallbacks
   The hero must be a finished-looking section when nothing animates, not a
   broken one. Collapsing the track to a single screen is what does that.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .stage { height: auto; }
  .stage__readout { display: none; }
  .stage__sticky { position: static; height: auto; min-height: 88svh; }
  .stage__poster { opacity: 0.30; }
  .stage.is-live .stage__poster { opacity: 0.30; }   /* keep the still, drop the scrub */
  .stage__canvas,
  .stage__video { display: none; }
  .marquee__track { animation: none; }
  .scroll-hint { display: none; }
  .svc:hover { transform: none; }
  .svc:hover .svc__media { transform: scale(1.04); }
}

/* Set by the script when WebGL is unavailable or the device is too small to
   carry a real-time scene. Same collapse, same finished look. */
.stage.no3d { height: auto; }
.stage.no3d .stage__sticky { position: static; height: auto; min-height: 88svh; }
.stage.no3d .stage__canvas,
.stage.no3d .stage__video,
.stage.no3d .stage__rail,
.stage.no3d .scroll-hint { display: none; }
.stage.no3d .stage__poster { opacity: 0.30; }
/* The readout narrates a scrub that isn't running here, so it goes. */
.stage.no3d .stage__readout { display: none; }

@media (max-width: 700px) {
  .stage__veil {
    background:
      radial-gradient(130% 70% at 50% 40%, transparent 20%, rgba(10,10,10,0.80) 100%),
      linear-gradient(to bottom, rgba(10,10,10,0.90) 0%, rgba(10,10,10,0.55) 40%, var(--c-bg) 96%);
  }
  .stage__readout { font-size: 11px; gap: var(--s-sm); }
  .stage__rail { display: none; }
  .svc { min-height: 280px; padding: var(--s-lg); }
  .svc__title { margin-top: var(--s-3xl); }
  .cfg__pick, .cfg__out { padding: var(--s-xl); }
}

/* --------------------------------------------------------------------------
   R12. Print - the 3D hero is screen-only
   -------------------------------------------------------------------------- */
@media print {
  .stage { height: auto; }
  .stage__sticky { position: static; height: auto; }
  .stage__canvas, .stage__video, .stage__rail, .scroll-hint, .marquee { display: none; }
}

/* --------------------------------------------------------------------------
   R13. Finale
   Set by a hero when its last beat needs the whole frame. The copy has been
   on screen for the length of the scroll by then, so standing it down is what
   lets the reveal land instead of fighting the headline.
   -------------------------------------------------------------------------- */
.stage.is-finale .stage__copy {
  opacity: 0;
  transform: translateY(-14px);
  transition: opacity var(--t-slow) var(--ease), transform var(--t-slow) var(--ease);
}
.stage__copy { transition: opacity var(--t-slow) var(--ease), transform var(--t-slow) var(--ease); }
@media (prefers-reduced-motion: reduce) {
  .stage.is-finale .stage__copy { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   R14. Hero clip framing
   Full-bleed, the clip ran under the fixed header and its first frame crowded
   the headline. The footage is lit on black, so instead of regenerating it the
   picture is drawn smaller and anchored right, and its edges are feathered so
   no rectangle shows where the video's black meets the page's.
   -------------------------------------------------------------------------- */
@media (min-width: 821px) {
  .stage__video {
    transform: scale(0.82);
    transform-origin: 88% 58%;
    -webkit-mask-image:
      linear-gradient(to right, transparent 0%, #000 14%, #000 92%, transparent 100%),
      linear-gradient(to bottom, transparent 0%, #000 10%, #000 88%, transparent 100%);
    -webkit-mask-composite: source-in;
    mask-image:
      linear-gradient(to right, transparent 0%, #000 14%, #000 92%, transparent 100%),
      linear-gradient(to bottom, transparent 0%, #000 10%, #000 88%, transparent 100%);
    mask-composite: intersect;
  }
}

/* On a portrait phone, cover crops to a vertical slice of the middle of the
   frame - but the subject lives right of centre. Aim the slice at it. */
@media (max-width: 820px) {
  .stage__video { object-position: 66% 50%; }
}
