/* Adzvibe — typographic poster, hard-light graphic world.
   The page hard-cuts between an ink ground and a paper ground, so the accent
   carries two lightnesses of one hue: bright teal on ink, deep teal on paper.
   Nothing here restyles .sc-stage, .sc-copy or a [data-sc-*] hook. */

:root {
  --sc-canvas:     #05100d;
  --sc-surface:    #0c1a17;
  --sc-ink:        #f4f8f6;
  --sc-ink-soft:   #93aaa4;
  --sc-accent:     #15fdc6;
  --sc-accent-ink: #05100d;

  --sc-font-display: "Archivo", system-ui, sans-serif;
  --sc-font-text:    "Geist", system-ui, sans-serif;

  /* the light is fixed and high left, the way a single hard source is */
  --shx: 5px; --shy: 8px; --shb: 0px;
  --field-ink: #15fdc6;
  --field-op: 0.045;
  --rule: color-mix(in oklab, var(--sc-ink) 16%, transparent);
}

/* the paper ground. `color` is restated because it resolves as a computed
   value on body and would otherwise ignore the token change entirely. */
.on-paper {
  --sc-ink:      #0a0f0e;
  --sc-ink-soft: #45564f;
  --sc-accent:   #00786a;
  --field-ink:   #00786a;
  --field-op:    0.07;
  --rule: color-mix(in oklab, #0a0f0e 18%, transparent);
  color: var(--sc-ink);
  background: #f4f7f5;
}
.on-ink { color: var(--sc-ink); background: var(--sc-canvas); }

.lit  { text-shadow: var(--shx) var(--shy) var(--shb) rgb(0 0 0 / 0.35); }
.on-paper .lit { text-shadow: 3px 4px 0 rgb(10 15 14 / 0.10); }

/* ── the neon rays ──────────────────────────────────────────
   The signature. The mark is stroked, not filled, in four passes: a wide
   bloom, a mid halo, a crisp core, and a sweep that travels the outline when
   the journey hands over from one brand to the next. Behind it a fan of soft
   beams turns slowly. Every value below reads a custom property that page.js
   eases toward its target, which is why the state changes never pop: nothing
   here carries its own duration, and nothing switches on a boolean. */
.rays {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  display: grid;
  place-items: center;
  /* solid black under the marks. The only colour it ever carries is the wash
     that comes up as a brand takes the middle, and goes again. */
  background:
    radial-gradient(120% 80% at 50% 46%,
      rgb(12 62 50 / calc(0.55 * var(--e, 0))) 0%,
      rgb(2 8 7 / 0.96) 58%,
      #000000 100%),
    #000000;
  opacity: var(--on, 0);
  transition: opacity 620ms var(--sc-ease-out);
  /* its own compositing layer, and nothing inside it can affect the page's
     layout or paint, so the band above it is not repainted when it changes */
  will-change: opacity;
  contain: strict;
}
.rays__svg {
  width: min(126vw, 1580px);
  height: auto;
  overflow: visible;
  /* Deliberately NOT scaled from --e any more. Scaling a filtered subtree
     re-runs the blur every frame, and that alone was holding the band at 30fps
     while the same page ran at 60 with this layer hidden. The intensity is
     carried by opacity instead, which is a composite-time property. */
  transform: translateY(2%);
}

.ray-pass { fill: none; stroke-linejoin: round; stroke-linecap: round; }

/* Every stroke width below is a constant. A width that reads a custom property
   changes the filter's input on every frame, which invalidates the cached blur;
   holding the geometry still lets Chrome rasterise each pass once and then just
   composite it at whatever opacity the loop asks for. */
.ray-pass--bloom {
  stroke: #0ce0b4;
  stroke-width: 40px;
  opacity: calc(var(--e, 0) * 0.5);
}
.ray-pass--halo {
  stroke: #15fdc6;
  stroke-width: 11px;
  opacity: calc(var(--e, 0) * 0.62);
}
.ray-pass--core {
  stroke: #d8fff4;
  stroke-width: 2.3px;
  opacity: calc(0.06 + var(--e, 0) * 0.82);
}
/* the sweep carries no filter of its own, so its dash offset is free to move */
.ray-pass--sweep {
  stroke: #ffffff;
  stroke-width: 3.4px;
  stroke-dasharray: 120 880;
  stroke-dashoffset: calc((1 - var(--sweep, 0)) * -1000);
  opacity: calc(var(--sweepop, 0) * 0.85);
}

/* The fan behind the mark used to be fourteen SVG rectangles inside a
   34px Gaussian blur, rotating. Rotating a filtered group re-runs the blur
   every frame. It is one repeating conic gradient now: the softness is drawn
   into the gradient rather than filtered onto it, so the whole fan rasterises
   once and the rotation is a composited transform. */
.rays__fan {
  position: absolute;
  width: 190vmax;
  height: 190vmax;
  border-radius: 50%;
  pointer-events: none;
  background: repeating-conic-gradient(from 0deg at 50% 50%,
    rgb(16 200 164 / 0.20) 0deg,
    rgb(16 200 164 / 0.06) 7deg,
    rgb(16 200 164 / 0) 13deg,
    rgb(16 200 164 / 0) 25.7deg);
  -webkit-mask-image: radial-gradient(closest-side, #000 0%, rgb(0 0 0 / 0.35) 55%, transparent 100%);
          mask-image: radial-gradient(closest-side, #000 0%, rgb(0 0 0 / 0.35) 55%, transparent 100%);
  transform: rotate(calc(var(--spin, 0) * 1deg));
  opacity: calc(var(--e, 0) * 0.9);
  will-change: transform, opacity;
}

@media (prefers-reduced-motion: reduce) {
  .rays__fan { transform: none; }
  .ray-pass--sweep { opacity: 0; }
}

section { position: relative; }
section > :not([data-sc-stage]) { position: relative; z-index: 1; }
[data-sc-stage] > * { position: relative; z-index: 1; }

/* ── chrome ────────────────────────────────────────────────────────────────*/
.skip {
  position: absolute; left: -9999px; top: 0;
  background: var(--sc-accent); color: var(--sc-accent-ink);
  padding: var(--sc-3) var(--sc-4); z-index: 90;
}
.skip:focus-visible { left: var(--sc-4); top: var(--sc-4); }

.pin-link {
  position: fixed;
  right: var(--sc-gutter); bottom: var(--sc-5);
  z-index: 60;
  font: 600 var(--sc-t-sm) / 1 var(--sc-font-text);
  letter-spacing: var(--sc-track-wide);
  text-transform: uppercase;
  color: #05100d;
  background: #15fdc6;
  text-decoration: none;
  padding: var(--sc-3) var(--sc-4);
  opacity: 0;
  transform: translateY(6px);
  transition: opacity var(--sc-d-slow) var(--sc-ease-out),
              transform var(--sc-d-slow) var(--sc-ease-out);
}
.pin-link.is-shown { opacity: 1; transform: none; }
.pin-link.is-hushed { opacity: 0; pointer-events: none; }
.pin-link:hover { background: #f4f8f6; }
.pin-link:active { transform: translateY(1px); }

/* ── act 1 · the wordmark, and proof on the first screen ───────────────────*/
.hero {
  min-height: 100svh;
  display: grid;
  grid-template-rows: 1fr auto auto;
  padding: calc(var(--sc-8) + env(safe-area-inset-top)) var(--sc-gutter) var(--sc-5);
  gap: var(--sc-6);
}
.hero__mark {
  align-self: end;
  margin: 0;
  font-family: var(--sc-font-display);
  font-weight: 800;
  font-size: clamp(3.6rem, 19vw, 20rem);
  line-height: 0.8;
  letter-spacing: -0.05em;
  text-transform: lowercase;
}
.hero__mark .dot { color: var(--sc-accent); }
.hero__foot {
  display: grid;
  grid-template-columns: minmax(0, 40ch) auto;
  gap: var(--sc-4) var(--sc-7);
  align-items: end;
  justify-content: space-between;
  border-top: 1px solid var(--rule);
  padding-top: var(--sc-4);
}
.hero__say { margin: 0; font-size: var(--sc-t-lg); line-height: 1.35; color: var(--sc-ink-soft); text-wrap: pretty; }
.hero__say b { color: var(--sc-ink); font-weight: 500; }
.hero__where {
  margin: 0;
  font: 500 var(--sc-t-xs) / 1.5 var(--sc-font-text);
  letter-spacing: var(--sc-track-wide);
  text-transform: uppercase;
  color: var(--sc-ink-soft);
  text-align: right;
}
@media (max-width: 700px) {
  .hero__foot { grid-template-columns: 1fr; }
  .hero__where { text-align: left; }
}

/* the proof strip on screen one. It is a slot, not a run: the marks are in the
   band layer, which parks over this slot at strip scale. Keeping the slot in
   flow is what gives the band something to measure, so the two states share one
   position and the growth reads as the same marks getting bigger. */
.strip {
  border-top: 1px solid var(--rule);
  padding-top: var(--sc-4);
  overflow: hidden;
}
.strip__track { overflow: hidden; }
.strip__label {
  margin: 0 0 var(--sc-3);
  font: 500 var(--sc-t-xs) / 1 var(--sc-font-text);
  letter-spacing: var(--sc-track-wide);
  text-transform: uppercase;
  color: var(--sc-ink-soft);
}
/* taller than the old 38px: several of the client marks are detailed, and
   below about this height their detail collapses into a smudge no amount of
   contrast recovers */
.strip__slot { height: 54px; }

html.lenis { scroll-behavior: auto; }

/* ── the band ──────────────────────────────────────────────────────────────
   One run of client marks for the whole page, fixed, and sized entirely from
   two custom properties that page.js walks:

     --g   the growth. 0 is the hero strip, 1 is the maximised showcase.
     --f   per item: how close that brand is to the middle of the screen.

   At --g 1 the brand at the middle is the showcase — its mark rises to the
   top of the band and its work slides along underneath it — while the brands
   either side of it stay on screen as marks alone, which is what says there
   are more of them to travel to. Nothing here carries its own duration except
   the work slide, so no state change can cut. */
.band {
  position: fixed;
  left: 0; right: 0; top: 0;
  z-index: 2;
  pointer-events: none;
  overflow: hidden;
  height: calc(54px + var(--g) * (62vh - 54px));
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 5%, #000 95%, transparent);
  will-change: transform;
}
.band__track { height: 100%; width: max-content; will-change: transform; }
.band__run {
  display: flex;
  align-items: center;
  height: 100%;
  width: max-content;
  padding-inline: var(--sc-gutter);
  gap: calc(72px + var(--g) * 26px);
}

.band__item {
  /* Layout and style containment only. `paint` would be the stronger hint, but
     it clips: the marks are laid out in a box sized for full focus and the
     taller ones ride above it, so painting them outside the box is the design,
     not an accident. */
  contain: layout style;
  --fg: calc(var(--f, 0) * var(--g));      /* focus, but only once maximised */
  /* the brand being shown gets the bigger mark; the ones either side stay
     smaller, which is the difference between a showcase and a row */
  /* Unitless, because the scale below has to divide one by the other and CSS
     cannot divide lengths. --markh is the box the mark would want right now;
     --markh-max is the box it would want at full focus. */
  --mark-base: calc(54 + var(--g) * 140);
  --markh: calc((var(--mark-base) + var(--fg) * 70) * 1px * var(--fit, 1));
  --markh-max: calc((var(--mark-base) + 70) * 1px * var(--fit, 1));
  /* what the mark is scaled to, relative to the box it is laid out in */
  --marks: calc((var(--mark-base) + var(--fg) * 70) / (var(--mark-base) + 70));
  position: relative;
  flex: 0 0 auto;
  height: 100%;
  /* the showcase is wide enough to carry a row of work and still leave the
     neighbouring marks on screen at either edge */
  width: calc(150px + var(--g) * (min(46vw, 680px) - 150px));
  display: grid;
  grid-template-rows: auto calc(var(--g) * 210px);
  align-content: center;
  justify-items: center;
  gap: calc(var(--g) * 30px);
}

/* The mark's box is scaled by --fit rather than the image overflowing it:
   letting the image exceed its box is what cropped the taller marks against
   the band's own edge. --fit is per mark, written by build.mjs from the ink
   each mask actually carries, so a fine wordmark is given more of the box and
   a solid roundel less and twelve very different files read as one set. */
/* A client with nothing supplied yet shows the mark and the counter. The work
   row is not laid out at all, so the mark sits centred rather than floating
   above a strip of empty cards. */
/* The counter still belongs to a bare brand: it is where the reader is in the
   run, not a work panel. Only the sliding row of cards goes. */
.band__item--bare { grid-template-rows: auto auto; }

.band__item .card__mark {
  width: 100%;
  /* The box is laid out at the size the mark would take at full focus and then
     left alone. Driving its height from --f meant every frame of travel
     relaid out the run and re-rastered up to twenty-four images, which is
     where the moving band's long frames were coming from. The size the reader
     sees is a transform on the image instead: composited, no layout, no
     re-raster. */
  height: var(--markh-max);
  display: grid;
  place-items: center;
}
.band__item .card__mark img {
  /* the marks are the only thing in the run that actually moves on screen */
  will-change: transform, opacity;
  transform: translate3d(0, calc(var(--fg) * -10px), 0) scale(var(--marks, 1));
  max-height: var(--markh-max);
  max-width: 100%;
  /* the brands either side are present, not competing */
  opacity: calc(0.95 + var(--g) * 0.05 - (var(--g) - var(--fg)) * 0.45);
}

.band__item .card__work {
  width: 100%;
  overflow: hidden;
  display: grid;
  gap: var(--sc-3);
  justify-items: center;
  align-content: start;
  opacity: var(--fg);
  transform: translate3d(0, calc((1 - var(--fg)) * 16px), 0);
}
.card__idx {
  margin: 0;
  font: 600 var(--sc-t-xs) / 1 var(--sc-font-text);
  letter-spacing: var(--sc-track-wide);
  color: var(--sc-accent);
}

/* the work itself: a row of cards that travels right to left on its own,
   slowly, for as long as the brand is the one being shown */
.work__slide {
  width: 100%;
  overflow: hidden;
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent);
}
.work__track {
  display: flex;
  width: max-content;
  gap: var(--sc-4);
  animation: work-slide 34s linear infinite;
}
@keyframes work-slide {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-50%, 0, 0); }
}
@media (prefers-reduced-motion: reduce) { .work__track { animation: none; } }

/* A supplied piece of work, in the same sliding row as the labelled cards.
   Fixed box, image contained: the client's photos are square phone shots and a
   row that changed height per picture would move the marks above it. */
.work__shot {
  flex: 0 0 auto;
  margin: 0;
  width: clamp(150px, 15vw, 210px);
  height: 100%;
  max-height: 210px;
  overflow: hidden;
  border: 1px solid var(--rule);
  background: #0a1310;
}
.work__shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  opacity: 0.92;
}

.work__card {
  flex: 0 0 auto;
  min-width: 15rem;
  display: grid;
  gap: var(--sc-2);
  padding: var(--sc-4) var(--sc-5);
  border: 1px solid var(--rule);
  border-radius: 14px;
  background: color-mix(in oklab, var(--sc-ink) 4%, transparent);
}
.work__label {
  margin: 0;
  font: 500 var(--sc-t-xs) / 1 var(--sc-font-text);
  letter-spacing: var(--sc-track-wide);
  text-transform: uppercase;
  color: var(--sc-ink-soft);
}
.work__value {
  margin: 0;
  font-family: var(--sc-font-display);
  font-weight: 700;
  font-size: var(--sc-t-xl);
  line-height: 1.1;
  letter-spacing: var(--sc-track-tight);
}

@media (max-width: 860px) {
  .band { height: calc(54px + var(--g) * (66vh - 54px)); }
  .band__item {
    width: calc(120px + var(--g) * (68vw - 120px));
    grid-template-rows: auto calc(var(--g) * 190px);
  }
  .band__item { --markh: calc((54px + var(--g) * 104px + var(--fg) * 46px) * var(--fit, 1)); }
  .band__run { gap: calc(56px + var(--g) * 28px); }
}

@media (max-width: 560px) {
  .band__item {
    width: calc(104px + var(--g) * (78vw - 104px));
    grid-template-rows: auto calc(var(--g) * 176px);
  }
  .band__item { --markh: calc((54px + var(--g) * 86px + var(--fg) * 34px) * var(--fit, 1)); }
  .work__card { min-width: 12rem; padding: var(--sc-3) var(--sc-4); }
  .work__value { font-size: var(--sc-t-lg); }
  .band__run { gap: calc(48px + var(--g) * 20px); }
}

/* ── act 2 · the argument ──────────────────────────────────────────────────*/
.argue {
  height: 100%;
  display: grid;
  align-content: center;
  padding: var(--sc-7) var(--sc-gutter);
  max-width: 48rem;
  margin-inline: auto;
}
.argue p {
  grid-area: 1 / 1;
  margin: 0;
  font-family: var(--sc-font-display);
  font-weight: 600;
  font-size: var(--sc-t-2xl);
  line-height: 1.08;
  letter-spacing: var(--sc-track-snug);
  text-wrap: balance;
}
.argue p em { font-style: normal; color: var(--sc-accent); }

/* ── act 2 · the work ────────────────────────────────────────────
   The section holds the copy and an empty slot. The marks that used to be
   rendered again here are gone: the band layer parks over the slot at brand
   scale, so screen one and screen two are one run of logos at two sizes.

   The lead and the tail are the regions where a scroll is a page scroll. Over
   the slot itself, page.js turns the same gesture into lateral travel through
   the brands, and hands vertical scroll back at either end of the run. */
.reel {
  background: transparent;
  display: grid;
  align-content: center;
  gap: var(--sc-7);
  padding-block: var(--sc-8);
}
.reel__inner { display: grid; gap: var(--sc-6); }
.reel__lead, .reel__tail {
  width: min(78vw, 34rem);
  padding-inline: var(--sc-gutter);
  display: grid;
  gap: var(--sc-3);
}
.reel__lead h2 { margin: 0; }
.reel__note, .reel__tailline {
  margin: 0;
  color: var(--sc-ink-soft);
  font-size: var(--sc-t-base);
  line-height: var(--sc-leading-body);
  max-width: 34ch;
}
.reel__tailline {
  font-family: var(--sc-font-display);
  font-size: var(--sc-t-xl);
  color: var(--sc-ink);
  letter-spacing: var(--sc-track-snug);
}
/* The band's rest position, reserved in the layout rather than floated over
   it. The slot is sticky inside a hold that is a screen and a bit tall, so it
   parks in the middle of the screen and stays there while that hold passes —
   which is the window the roster is cycled in — and the copy above and below
   it is never underneath the marks. */
.reel__hold { height: 130vh; }
.reel__slot {
  position: sticky;
  top: calc(50vh - 31vh);
  height: 62vh;
}

/* the mark and the work beside it, shared with the band */
.card__mark {
  display: grid;
  place-items: center;
}
.card__mark img {
  max-width: 100%;
  max-height: 100%;
  width: auto;
  height: auto;
  object-fit: contain;
  /* the same greyed mark the strip shows, at size. No colour version. */
}
.card__work { display: grid; gap: var(--sc-3); align-content: center; }
.card__idx {
  margin: 0;
  font: 600 var(--sc-t-xs) / 1 var(--sc-font-text);
  letter-spacing: var(--sc-track-wide);
  color: var(--sc-accent);
}
.card__name {
  margin: 0;
  font-family: var(--sc-font-display);
  font-weight: 700;
  font-size: var(--sc-t-2xl);
  line-height: 1;
  letter-spacing: var(--sc-track-tight);
}
.card__sector {
  margin: 0;
  font: 500 var(--sc-t-xs) / 1.4 var(--sc-font-text);
  letter-spacing: var(--sc-track-wide);
  text-transform: uppercase;
  color: var(--sc-ink-soft);
}

@media (max-width: 860px) {
  .reel__lead, .reel__tail { width: 82vw; }
  .reel__slot { height: 66vh; top: calc(50vh - 33vh); }
}

/* ── act 4 · the intro film ────────────────────────────────────────────────
   A scrub act: the engine owns the playhead, this only places the copy. The
   film is a lockup building on black, so the copy sits at the bottom where the
   frame stays dark for the whole ten seconds and never competes with it. */
.intro [data-sc-stage] { background: #000; }
/* the held final frame. It fades in once, over the film, and never leaves */
.intro__locked {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: 1;
  opacity: 0;
  pointer-events: none;
  transition: opacity 220ms var(--sc-ease-out);
}
.intro__locked.is-locked { opacity: 1; }
.intro__say {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 36ch);
  gap: var(--sc-4) var(--sc-7);
  align-items: end;
  padding: var(--sc-6) var(--sc-gutter) var(--sc-7);
  max-width: 82rem;
  margin-inline: auto;
}
.intro__say h2 { margin: 0; font-size: var(--sc-t-2xl); line-height: 1.04; letter-spacing: var(--sc-track-tight); max-width: 16ch; }
.intro__say p { margin: 0; color: var(--sc-ink-soft); font-size: var(--sc-t-base); line-height: var(--sc-leading-body); }
@media (max-width: 860px) { .intro__say { grid-template-columns: 1fr; } }

/* Portrait. The clip is 16:9 and the copy is long, and covering one with the
   other is what the phone screenshot showed: the lockup cropped to a giant
   fragment with the paragraph sitting on top of it. So on a narrow screen the
   stage becomes two rows — the film in its own aspect, contained rather than
   cropped, and the copy underneath it on the black. */
@media (max-width: 760px) {
  .intro [data-sc-stage] {
    display: grid;
    grid-template-rows: minmax(0, 1fr) auto;
    align-content: center;
  }
  .intro [data-sc-stage] video[data-sc-scrub],
  .intro [data-sc-stage] .sc-stage__poster {
    position: relative;
    inset: auto;
    grid-area: 1 / 1;
    align-self: center;
    width: 100%;
    height: auto;
    max-height: 100%;
    object-fit: contain;
  }
  .intro .sc-scrim { display: none; }
  .intro__locked {
    position: relative;
    grid-area: 1 / 1;
    align-self: center;
    height: auto;
    max-height: 100%;
    object-fit: contain;
  }
  .intro__say {
    position: relative;
    grid-area: 2 / 1;
    padding: var(--sc-5) var(--sc-gutter) var(--sc-7);
    gap: var(--sc-3);
  }
  .intro__say h2 { font-size: var(--sc-t-xl); max-width: 20ch; }
  .intro__say p { font-size: var(--sc-t-sm); }
}

/* ── act 3 · services ─────────────────────────────────────────
   Concrete deliverables, and beside each one a framed picture whose image
   drifts between real examples. */
.svcs { padding-block: var(--sc-8); }
.svcs__head { max-width: 46ch; margin-bottom: var(--sc-7); display: grid; gap: var(--sc-3); }
.svcs__head h2 { margin: 0; }
.svcs__note { margin: 0; color: var(--sc-ink-soft); font-size: var(--sc-t-base); line-height: var(--sc-leading-body); }

.svc {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 22rem);
  gap: var(--sc-5) var(--sc-8);
  align-items: start;
  padding-block: var(--sc-6);
  border-top: 1px solid var(--rule);
}
.svc:last-of-type { border-bottom: 1px solid var(--rule); }
/* no examples supplied for this discipline yet: the copy takes the full measure
   rather than sitting beside an empty frame */
.svc--noframe { grid-template-columns: minmax(0, 1fr); }
.svc__name {
  margin: 0 0 var(--sc-4);
  font-family: var(--sc-font-display);
  font-weight: 600;
  font-size: var(--sc-t-xl);
  letter-spacing: var(--sc-track-snug);
}
.svc__list { margin: 0; padding: 0; list-style: none; display: grid; gap: var(--sc-3); max-width: 56ch; }
.svc__list li {
  position: relative;
  padding-left: var(--sc-5);
  color: var(--sc-ink-soft);
  font-size: var(--sc-t-base);
  line-height: 1.5;
}
.svc__list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.62em;
  width: 12px;
  height: 1px;
  background: var(--sc-accent);
}
@media (max-width: 900px) {
  .svc { grid-template-columns: 1fr; }
}

/* the frame ───────────────────────────────────────────────────────────────
   A picture in a frame whose subject changes while you are not looking
   straight at it. Every example is stacked in the same glass and the cycle
   crossfades over a third of each hold, with a slow scale and drift on the
   plate underneath, so the change reads as the picture thinking rather than
   as a slide arriving. Nothing translates in from outside the frame, and
   nothing cuts. */
.frame {
  margin: 0;
  display: grid;
  gap: var(--sc-3);
  justify-self: end;
  width: 100%;
  max-width: 22rem;
}
.frame__glass {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  background: #0a1310;
  border: 1px solid color-mix(in oklab, var(--sc-accent) 30%, transparent);
  box-shadow:
    inset 0 1px 0 rgb(244 248 246 / 0.06),
    0 24px 50px rgb(2 7 6 / 0.5);
}
/* the glass itself: a soft sheen that keeps the frame from reading as a div */
.frame__glass::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(122deg, rgb(244 248 246 / 0.07) 0%, rgb(244 248 246 / 0) 42%);
}
/* a plate is a still or a loop; both are framed the same way */
video.frame__plate { object-fit: cover; }
.frame__plate {
  position: absolute;
  inset: 10%;
  margin: auto;
  width: 80%;
  height: 80%;
  object-fit: contain;
  opacity: 0;
  /* keyframe offsets cannot be calc()d, and the visible window is 1/n of the
     cycle, so build.mjs emits one keyframe set per plate count and each frame
     names the one it needs. Negative delays stagger the plates through it. */
  animation-name: var(--anim);
  animation-duration: var(--cycle, 24s);
  animation-timing-function: ease-in-out;
  animation-iteration-count: infinite;
  animation-delay: calc(-1 * var(--cycle, 24s) / var(--n, 1) * var(--k, 0));
  will-change: opacity, transform;
}
/* Media is shot for phones, so it is framed like one: the same glass, in 9:16,
   narrower so it sits at the same height as the square frames rather than
   towering over its own copy. Nothing else about the frame changes. */
.frame--media { max-width: 13rem; }
.frame--media .frame__glass { aspect-ratio: 9 / 16; }

.frame__cap {
  font: 500 var(--sc-t-xs) / 1.4 var(--sc-font-text);
  letter-spacing: var(--sc-track-wide);
  text-transform: uppercase;
  color: var(--sc-ink-soft);
}
@media (prefers-reduced-motion: reduce) {
  /* the frame stops thinking and simply shows one example */
  .frame__plate { animation: none; opacity: 0; }
  .frame__plate[style*="--k:0"] { opacity: 1; }
}

/* ── act 5 · the peak. the mark puts itself together ───────────────────────*/
.forge {
  height: 100%;
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: var(--sc-5);
  padding: var(--sc-7) var(--sc-gutter) var(--sc-8);
  align-items: center;
}
.forge__cue { margin: 0; }
.forge__stage { align-self: center; width: min(92vw, 60rem); margin-inline: auto; }
.forge__stage svg { width: 100%; height: auto; display: block; overflow: visible; }
.shard {
  /* the lockup finishes at 78% of the act, so the last fifth of the peak holds
     the assembled mark instead of ending on the moment it arrives */
  --k: clamp(0, calc((0.78 - var(--sc-p, 0)) / 0.78), 1);
  transform-box: fill-box;
  transform-origin: center;
  transform:
    translate(calc(var(--dx, 0) * var(--k) * 1px),
              calc(var(--dy, 0) * var(--k) * 1px))
    rotate(calc(var(--r, 0deg) * var(--k)));
  opacity: clamp(0.45, calc((var(--sc-p, 0) - var(--t, 0)) * 4 + 0.62), 1);
}
.forge__say {
  /* the assembled mark travels through this band, so the copy sits on its own
     opaque ground rather than on a shard. Hard edge, no gradient: the world is
     hard light. */
  position: relative;
  z-index: 2;
  background: var(--sc-canvas);
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 36ch);
  gap: var(--sc-4) var(--sc-7);
  align-items: end;
  border-top: 1px solid var(--rule);
  padding: var(--sc-4) 0 var(--sc-2);
}
.forge__say h2 { margin: 0; font-size: var(--sc-t-2xl); line-height: 1.04; letter-spacing: var(--sc-track-tight); max-width: 16ch; }
.forge__say p { margin: 0; color: var(--sc-ink-soft); font-size: var(--sc-t-base); line-height: var(--sc-leading-body); }
@media (max-width: 860px) { .forge__say { grid-template-columns: 1fr; } }

/* ── act 6 · how it runs, on paper ─────────────────────────────────────────*/
.how { padding-block: var(--sc-8); }
.how__head { max-width: 36ch; margin-bottom: var(--sc-6); }
.how__steps {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: var(--sc-6) var(--sc-7);
  border-top: 1px solid var(--rule);
  padding-top: var(--sc-5);
}
.how__steps h3 {
  margin: 0 0 var(--sc-2);
  font-family: var(--sc-font-display);
  font-size: var(--sc-t-lg);
  letter-spacing: var(--sc-track-snug);
}
.how__steps p { margin: 0; color: var(--sc-ink-soft); font-size: var(--sc-t-sm); line-height: 1.6; }
.how__steps .n {
  display: block;
  margin-bottom: var(--sc-3);
  font: 600 var(--sc-t-xs) / 1 var(--sc-font-text);
  letter-spacing: var(--sc-track-wide);
  color: var(--sc-accent);
}
@media (max-width: 860px) { .how__steps { grid-template-columns: 1fr; } }

.gets {
  margin-top: var(--sc-6);
  padding-top: var(--sc-5);
  border-top: 1px solid var(--rule);
  display: grid;
  grid-template-columns: minmax(0, 18rem) minmax(0, 1fr);
  gap: var(--sc-4) var(--sc-7);
}
.gets h3 { margin: 0; font-family: var(--sc-font-display); font-size: var(--sc-t-lg); letter-spacing: var(--sc-track-snug); }
.gets ul {
  margin: 0; padding: 0; list-style: none;
  columns: 2; column-gap: var(--sc-7);
  font-size: var(--sc-t-sm); line-height: 1.5; color: var(--sc-ink-soft);
}
.gets li { break-inside: avoid; padding-block: var(--sc-2); border-top: 1px solid var(--rule); }
@media (max-width: 860px) { .gets { grid-template-columns: 1fr; } .gets ul { columns: 1; } }

/* ── act 7 · the close ─────────────────────────────────────────────────────*/
.close {
  height: 100%;
  display: grid;
  grid-template-rows: 1fr auto;
  padding: var(--sc-7) var(--sc-gutter) var(--sc-5);
}
.close__mark {
  grid-area: 1 / 1;
  align-self: center;
  justify-self: end;
  width: min(36vw, 24rem);
  opacity: 0.92;
}
.close__mark svg { width: 100%; height: auto; display: block; }
@media (max-width: 860px) { .close__mark { display: none; } }

.close__inner {
  grid-area: 1 / 1;
  align-self: center;
  display: grid;
  gap: var(--sc-4);
  justify-items: start;
  max-width: 36rem;
}
.close__inner h2 { margin: 0; font-size: var(--sc-t-2xl); line-height: 1.06; letter-spacing: var(--sc-track-tight); text-wrap: balance; }
.close__inner p { margin: 0; color: var(--sc-ink-soft); font-size: var(--sc-t-sm); line-height: var(--sc-leading-body); max-width: 46ch; }
.cta {
  justify-self: start;
  font: 600 var(--sc-t-base) / 1 var(--sc-font-text);
  color: var(--sc-accent-ink);
  background: var(--sc-accent);
  text-decoration: none;
  padding: var(--sc-4) var(--sc-6);
  transition: background var(--sc-d-fast) var(--sc-ease-out), color var(--sc-d-fast) var(--sc-ease-out);
}
.cta:hover { background: #f4f8f6; color: #05100d; }
.cta:active { transform: translateY(1px); }
.close__alt { font-size: var(--sc-t-sm); color: var(--sc-ink-soft); }
.close__alt a { color: var(--sc-ink); }

.foot {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sc-2) var(--sc-6);
  align-items: center;
  justify-content: space-between;
  border-top: 1px solid var(--rule);
  padding-top: var(--sc-4);
  font: 400 var(--sc-t-xs) / 1.5 var(--sc-font-text);
  color: var(--sc-ink-soft);
}
.foot svg { height: 18px; width: auto; color: var(--sc-ink-soft); }
.foot__links { display: flex; flex-wrap: wrap; gap: var(--sc-2) var(--sc-4); }
.foot__links a { text-decoration: none; border-bottom: 1px solid var(--rule); padding-bottom: 1px; }
.foot__links a:hover { color: var(--sc-accent); border-bottom-color: var(--sc-accent); }
.close__alt a { border-bottom: 1px solid var(--rule); text-decoration: none; }
.close__alt a:hover { color: var(--sc-accent); border-bottom-color: var(--sc-accent); }
.foot a { color: inherit; }
.foot a:hover { color: var(--sc-ink); }

/* ── shared ────────────────────────────────────────────────────────────────*/
.eyebrow {
  margin: 0 0 var(--sc-3);
  font: 500 var(--sc-t-xs) / 1 var(--sc-font-text);
  letter-spacing: var(--sc-track-wide);
  text-transform: uppercase;
  color: var(--sc-accent);
}
.wipe { display: block; line-height: 1; padding-block: 0.1em; }
