/* =============================================================================
   PIBITOS MEDIA — style.css
   Stage (b): tokens, reset, layout, typography, components.

   NO animation, transition or transform-on-interaction lives here. All of that
   is motion.css at stage (c), which is why this file can be reviewed on its own.

   Ordering: tokens → reset → utilities → layout → components → footer/dialog.
   ============================================================================= */


/* -----------------------------------------------------------------------------
   1. FONTS

   Archivo Variable (SIL OFL) carries both a weight and a width axis in one
   90kb file — the page sets 100% for text and 112–118% for display, so the
   width axis is load-bearing, not decoration. DM Mono is used only for
   timecodes, metrics and platform badges.

   The metric-matched fallback below is measured, not guessed: at 100px/400,
   the lede string sets 5202px in Archivo against 5312px in Helvetica Neue, so
   the fallback is scaled to 97.9% and the line breaks in the same places
   before and after the swap.

   No `ascent-override` / `descent-override`, deliberately. Every line-height
   on this page is an explicit unitless number, so line boxes are sized by the
   CSS, not by the font's vertical metrics — only advance widths can shift
   anything, and `size-adjust` is what fixes those.
   -------------------------------------------------------------------------- */

@font-face {
  font-family: "Archivo";
  src: url("/assets/fonts/archivo-var.woff2") format("woff2-variations");
  font-weight: 100 900;
  font-stretch: 62% 125%;
  font-style: normal;
  font-display: swap;
}

/* Stands in for Archivo during the swap, scaled so nothing reflows. */
@font-face {
  font-family: "Archivo Fallback";
  src: local("Helvetica Neue"), local("Arial"), local("Liberation Sans");
  size-adjust: 97.9%;
  font-display: swap;
}

@font-face {
  font-family: "DM Mono";
  src: url("/assets/fonts/dm-mono-subset.woff2") format("woff2");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}


/* -----------------------------------------------------------------------------
   2. TOKENS
   -------------------------------------------------------------------------- */

:root {
  /* --- colour (SPEC §5). Contrast vs --ink: bone 17.4:1, bone-dim 8.6:1,
         amber 8.9:1, ink-on-amber 8.9:1. All AAA.
         HARD RULE: never bone on amber (1.9:1). Amber takes near-black. --- */
  --ink:         #0F0F0E;
  --ink-raised:  #1A1A18;
  --ink-line:    #2A2A26;
  /* --ink-line is a decorative hairline: at 1.33:1 against --ink it is below the
     3:1 WCAG 1.4.11 floor, which is fine for a divider and NOT fine for anything
     whose border is the only thing marking it as a control. Interactive edges use
     the token below: 3.45:1 on --ink, 3.02:1 on --ink-raised, both measured. */
  --ink-line-strong: #6B6860;
  --bone:        #F5F0E0;
  --bone-dim:    #AEAAA0;
  --amber:       #EFA01E;
  --amber-soft:  rgb(239 160 30 / .16);
  --focus:       #F5F0E0;

  /* --- type --- */
  --font-display: "Archivo", "Archivo Fallback", "Helvetica Neue", Arial, sans-serif;
  --font-mono:    "DM Mono", ui-monospace, "SF Mono", Menlo, monospace;

  /* fluid 320 → 1440, computed linearly (SPEC §4.2) */
  --fs-xs:      clamp(0.75rem,  0.732rem + 0.09vw, 0.8125rem);
  --fs-sm:      clamp(0.875rem, 0.857rem + 0.09vw, 0.9375rem);
  --fs-base:    clamp(1rem,     0.964rem + 0.18vw, 1.125rem);
  --fs-md:      clamp(1.25rem,  1.179rem + 0.36vw, 1.5rem);
  --fs-lg:      clamp(1.75rem,  1.464rem + 1.43vw, 2.75rem);
  --fs-xl:      clamp(2.5rem,   1.786rem + 3.57vw, 5rem);
  --fs-display: clamp(3rem,     1.286rem + 8.57vw, 9rem);

  /* --- space --- */
  --sp-2xs: .25rem;
  --sp-xs:  .5rem;
  --sp-sm:  .75rem;
  --sp-md:  1rem;
  --sp-lg:  1.5rem;
  --sp-xl:  2.5rem;
  --sp-2xl: 4rem;
  --sp-section: clamp(3.5rem, 9.5vw, 9rem);

  /* --- layout --- */
  --measure: 62ch;
  --pad-x: clamp(1.25rem, 4vw, 4rem);
  --rail:  clamp(1.75rem, 5vw, 6rem);   /* left channel the timeline lives in */
  --gap:   clamp(1rem, 2.2vw, 2rem);
  /* Space under the h1. One token so the lede and the showreel — which sit
     in the same grid row but are separate items — start on the same line. */
  --hero-gap: clamp(2.5rem, 5vw, 4.5rem);
  /* Floating nav bar. --nav-p is the container's own padding, and the inner
     radius is derived from it: concentric radii (outer − padding) is why a
     button inside a rounded box looks seated rather than pasted on. */
  --nav-r: clamp(1rem, 1.6vw, 1.5rem);
  --nav-p: .5rem;
  --nav-top: clamp(.5rem, 1.2vw, .875rem);
  --max:   100rem;

  /* ONE hand-drawn wobble in two orientations — the same path transposed, so
     the rail down the page and the underline beneath a word are literally the
     same gesture. Stroke 2.6 across a 60-unit run is the logo's own ratio
     (stroke 7 across a 670-unit curve), landing at ~3px at display size.

     These TILE rather than stretch. A single stretched sweep is what made the
     underline read flat and thin: ~600px wide against a 112-unit-tall source
     flattens the curvature roughly five-fold and thins the stroke with it.
     Repeating a fixed-aspect tile holds curvature and weight at every width. */
  --rail-w: 12px;

  --curve-x: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 60 12'%3E%3Cpath d='M0 7 C 12 2.5, 26 2.5, 38 6 C 47 8.6, 53 9, 60 7' fill='none' stroke='%23EFA01E' stroke-width='2.6' stroke-linecap='round'/%3E%3C/svg%3E");
  --rail-amber: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 60'%3E%3Cpath d='M7 0 C 2.5 12, 2.5 26, 6 38 C 8.6 47, 9 53, 7 60' fill='none' stroke='%23EFA01E' stroke-width='2.6' stroke-linecap='round'/%3E%3C/svg%3E");
  --rail-dim: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 60'%3E%3Cpath d='M7 0 C 2.5 12, 2.5 26, 6 38 C 8.6 47, 9 53, 7 60' fill='none' stroke='%232A2A26' stroke-width='2.6' stroke-linecap='round'/%3E%3C/svg%3E");

  /* --- timeline progress, 0–1.
         Base value is 1: with no JS and no scroll-driven animation the line is
         simply drawn in full, which is also the reduced-motion behaviour
         (SPEC §6.1). motion.css is what makes it start at 0 and track scroll. */
  --play-mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 16'%3E%3Cpath d='M2.5 2 L12 8 L2.5 14 Z' fill='%230F0F0E' stroke='%230F0F0E' stroke-width='2.4' stroke-linejoin='round'/%3E%3C/svg%3E");
  --tl: 1;

  color-scheme: dark;
}


/* -----------------------------------------------------------------------------
   3. RESET
   -------------------------------------------------------------------------- */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;      /* motion.css disables under reduced-motion */
  scroll-padding-top: 6rem;     /* sticky header clearance for #anchors */
}

html { overflow-x: clip; }

/* Ember gradient. A fixed pseudo-element rather than a background on <body>,
   so it never repaints while scrolling — one composited layer, no scroll cost.
   Alphas are the preview's Ember at 190% baked in (.14 and .09, x1.9).
   Worst case this lifts the ground to #4b3612, where bone still reads 10.0:1
   and bone-dim 4.9:1 — AA holds, but bone-dim has only 0.4 of margin, so don't
   push these higher without re-checking. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    radial-gradient(70% 55% at 8% 4%,  rgb(239 160 30 / .266) 0%, transparent 60%),
    radial-gradient(60% 50% at 96% 42%, rgb(239 160 30 / .171) 0%, transparent 62%);
}

body {
  margin: 0;
  background: var(--ink);
  color: var(--bone);
  font-family: var(--font-display);
  font-size: var(--fs-base);
  font-weight: 400;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  overflow-x: clip;
}

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

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

a { color: inherit; text-decoration: none; }
button, input, textarea { font: inherit; color: inherit; }

/* Focus is never removed, only restyled. Bone rather than amber so it stays
   visible on top of amber fills and amber hover washes. */
:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 2px;
  border-radius: 1px;
}


/* -----------------------------------------------------------------------------
   4. UTILITIES
   -------------------------------------------------------------------------- */

.u-sr {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.u-hidden { display: none; }

.skip-link {
  position: absolute;
  top: var(--sp-xs);
  left: var(--sp-xs);
  z-index: 100;
  padding: var(--sp-sm) var(--sp-md);
  background: var(--amber);
  color: var(--ink);
  font-weight: 600;
  transform: translateY(-200%);
}
.skip-link:focus { transform: translateY(0); }

/* Timecodes. Decorative (aria-hidden in the markup) — they mark keyframes on
   the timeline, they don't carry information the headings lack. */
.tc {
  position: absolute;
  top: var(--tc-top, var(--sp-section));
  left: var(--pad-x);
  width: var(--rail);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  letter-spacing: .04em;
  text-align: center;

  color: var(--amber);

  /* The playhead. --at is this keyframe's own position on the rail, written by
     timeline.js; --tl is where the amber fill has reached. The label clears out
     as the fill arrives, so the line runs through open gutter instead of over
     its own timecodes. The x60 resolves it across ~1.7% of the scroll: a cut,
     not a dissolve -- an edit timeline does not cross-fade its keyframes.

     --at defaults to 0 and timeline.js caps it at 1 - 1/60 so the last mark can
     still finish. With no JS, no scroll-driven animation, or reduced motion
     (--tl pinned at 1) --at stays 0 for every label, --passed resolves to 1 and
     the gutter is simply clear -- no half-faded text in the fallback. */
  --at: 0;
  --passed: clamp(0, (var(--tl) - var(--at)) * 60, 1);
  opacity: calc(1 - var(--passed));
}
/* On mobile the timecode leaves the gutter and sits inline above its heading.
   Absolute positioning in a gutter narrower than the label is what clipped
   "00:00" to "0:00" — reserving enough gutter would cost more width than the
   content can spare at 375px. */
@media (max-width: 40rem) {
  .tc {
    position: static;
    width: auto;
    padding-left: 0;
    margin-bottom: var(--sp-xs);
    text-align: left;
  }
}


/* -----------------------------------------------------------------------------
   5. BUTTONS

   Sharp rectangles, not pills: the logo already supplies every curve on the
   page, so the interface stays straight and lets the drawn line be the only
   soft shape. One amber button per screen — see .hero__actions.
   -------------------------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-xs);
  padding: .85em 1.4em;
  border: 1px solid transparent;
  border-radius: 2px;
  font-size: var(--fs-sm);
  font-weight: 600;
  letter-spacing: .01em;
  cursor: pointer;
}

.btn--amber   { background: var(--amber); color: var(--ink); border-color: var(--amber); }
.btn--outline { background: none; color: var(--amber); border-color: var(--amber); }
.btn--ghost   { background: none; color: var(--bone); border-color: var(--ink-line-strong); }


/* -----------------------------------------------------------------------------
   6. HEADER — a floating bar, not a full-width slab

   Against a flat ground an opaque edge-to-edge bar is invisible furniture.
   Against the gradient it reads as a black plate laid over the artwork, so it
   detaches instead: inset from every edge, rounded, translucent, and blurred
   so the gradient shows through it rather than being covered by it.

   Sticky rather than fixed. It stays in flow, so nothing below needs padding
   to compensate for its height — which matters because that height changes
   when the bar wraps to two rows on mobile.
   -------------------------------------------------------------------------- */

.site-header {
  position: sticky;
  top: var(--nav-top);
  z-index: 20;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-sm) var(--sp-lg);
  width: min(calc(100% - 2 * var(--pad-x)), calc(var(--max) - 2 * var(--pad-x)));
  margin: var(--nav-top) auto 0;
  padding: var(--nav-p) var(--nav-p) var(--nav-p) var(--sp-lg);
  border: 1px solid rgb(245 240 224 / .1);
  border-color: rgb(from var(--bone) r g b / .1);
  border-radius: var(--nav-r);
  background: rgb(15 15 14 / .58);
  background: rgb(from var(--ink) r g b / .58);
  -webkit-backdrop-filter: blur(16px) saturate(150%);
  backdrop-filter: blur(16px) saturate(150%);
}

/* No backdrop-filter (older Firefox, and anything with it disabled): fall back
   to a near-opaque ground so the bar never becomes unreadable over the gradient. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .site-header { background: rgb(15 15 14 / .93); background: rgb(from var(--ink) r g b / .93); }
}

/* Sized by height, not width — the wordmark has to sit inside a fixed-height
   bar, and its 2.33:1 ratio means width-based sizing overflows it vertically. */
.brand { flex: 0 0 auto; display: flex; }
.brand img { height: clamp(1.75rem, 3.2vw, 2.5rem); width: auto; }

/* Concentric: inner radius = outer radius − the padding between them. */
.site-header .btn { border-radius: calc(var(--nav-r) - var(--nav-p)); }

.site-nav { flex: 1 1 auto; }
.site-nav ul {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-md) var(--sp-lg);
  font-size: var(--fs-sm);
  color: var(--bone-dim);
}

.lang-switch { font-size: var(--fs-sm); color: var(--bone-dim); }
.header-cta  { margin-left: auto; }

/* Mobile header: brand left, CTA right, nav + language on the row beneath.
   The CTA stays in the header rather than becoming a sticky bottom bar — the
   header is already sticky, so it persists either way, and one bar costs no
   vertical space and needs no body padding compensation. It is the page's
   primary conversion action and must never leave the screen. */
@media (max-width: 48rem) {
  /* Two rows inside one rounded container: brand and CTA on top, nav and
     language beneath. The bar becomes a rounded rectangle rather than a
     shorter pill — same object, more of it. */
  .site-header {
    row-gap: var(--sp-xs);
    padding: var(--nav-p) var(--nav-p) var(--sp-sm) var(--sp-md);
  }
  .brand       { order: 1; }
  .header-cta  { order: 2; margin-left: auto; }
  .header-cta .btn { padding: .6em 1em; font-size: var(--fs-xs); }
  .site-nav    { order: 3; flex: 1 1 60%; }
  .site-nav ul { gap: var(--sp-sm) var(--sp-md); font-size: var(--fs-xs); }
  .lang-switch { order: 4; flex: 0 0 auto; margin-left: auto; font-size: var(--fs-xs); }
}

/* Narrowest phones only — 22rem is 352px, below the 375–430px band nearly
   every handset sits in. An earlier cut at 26rem fired at 416px and hid the
   language switch on almost all of them, which is the wrong thing to lose on
   a bilingual site. The nav scrolls sideways instead; the switch stays. */
@media (max-width: 22rem) {
  .site-header { padding-inline: var(--sp-sm) var(--nav-p); }
  .site-nav    { overflow-x: auto; overscroll-behavior-x: contain; }
  .site-nav ul { flex-wrap: nowrap; }
}


/* -----------------------------------------------------------------------------
   7. MAIN + THE TIMELINE
   -------------------------------------------------------------------------- */

main {
  position: relative;
  max-width: var(--max);
  margin-inline: auto;
}

/* Padding-block is asymmetric on purpose. With equal top and bottom the space
   between two sections is TWO section paddings — which is what produced the
   ~250px voids. One section's worth is pacing; two reads as a bug. */
main > section {
  position: relative;
  padding: var(--sp-section) var(--pad-x) 0
           calc(var(--pad-x) + var(--rail));
}
main > section:last-of-type { padding-bottom: var(--sp-section); }

/* The logo's curve continued vertically down the whole page — not a border.
   Same wobble tile as the underline, repeated, at the logo's stroke weight.

   Progress is revealed by clip-path rather than scaleY: scaling a drawn curve
   squashes its wobble, so the played and unplayed halves would stop being the
   same line. SPEC §6 is amended for this. */
/* Runs past </main> so the line ends on the footer's 01:00 label. The concept is
   a one-minute cut: stopping short of the minute leaves it without an ending. */
.timeline {
  position: absolute;
  top: 0;
  bottom: calc(-1 * (var(--sp-2xl) + var(--sp-xs)));
  left: calc(var(--pad-x) + var(--rail) / 2 - var(--rail-w) / 2);
  width: var(--rail-w);
  pointer-events: none;
}
.timeline__track,
.timeline__progress {
  position: absolute;
  inset: 0;
  background-repeat: repeat-y;
  background-size: var(--rail-w) 60px;
}
.timeline__track    { background-image: var(--rail-dim); }
.timeline__progress {
  background-image: var(--rail-amber);
  clip-path: inset(0 0 calc((1 - var(--tl)) * 100%) 0);
}

@media (max-width: 40rem) {
  .timeline { left: 2px; }
  main > section { padding-left: calc(var(--pad-x) + .5rem); }
}


/* -----------------------------------------------------------------------------
   8. SECTION HEADS + SHARED TYPE
   -------------------------------------------------------------------------- */

h1, h2, h3 {
  font-weight: 800;
  line-height: 1.05;
  letter-spacing: -.03em;
  text-wrap: balance;
}

.section-head { margin-bottom: var(--sp-xl); }
.section-head h2 { font-size: var(--fs-lg); font-stretch: 112%; }

.section-lede {
  margin-top: var(--sp-sm);
  max-width: var(--measure);
  font-size: var(--fs-md);
  color: var(--bone-dim);
  line-height: 1.4;
}

/* The one-line breaks between blocks — the whole reason the page has pacing. */
.statement {
  margin-block: clamp(3rem, 6vw, 5.5rem);
  max-width: 20ch;
  font-size: var(--fs-xl);
  font-weight: 800;
  font-stretch: 118%;
  line-height: 1.02;
  letter-spacing: -.035em;
  text-wrap: balance;
}


/* -----------------------------------------------------------------------------
   9. HERO

   h1 spans both columns. At --fs-display's 144px ceiling a two-column headline
   would break to four or five characters a line, which is why the showreel sits
   beside the lede rather than beside the heading.
   -------------------------------------------------------------------------- */

.hero { padding-top: clamp(1.75rem, 3.5vw, 3rem); --tc-top: clamp(1.75rem, 3.5vw, 3rem); }

.hero__title {
  font-size: var(--fs-display);
  font-stretch: 118%;
  letter-spacing: -.04em;
  line-height: .92;
}
/* The headline wraps to four or five lines on a phone, so the underlined
   phrase almost always spans two of them. A touch more leading there buys the
   curve its clearance; at desktop sizes the phrase fits one line and the tight
   .92 setting stands. */
@media (max-width: 48rem) {
  /* 1.02 left the wrapped phrase's curve ~10px inside the ascenders of the line
     beneath it -- on the LCP element, screen one, in both locales. Spanish wraps
     one line deeper than English, so this is verified against the longer string. */
  .hero__title { line-height: 1.16; }
}

/* The pencil curve from the logo, drawn under the words it belongs to.

   Painted as a background rather than an inline <svg><use>. An absolutely
   positioned mark is sized against the union of an inline element's line
   boxes, so the moment "actually retains" wraps to two lines — which it does
   on any narrow viewport — the curve stretches across both as one flat sweep.
   A background with box-decoration-break:clone gives every line box its own
   curve, which is what a hand-drawn underline actually does.

   It also means the motif is one CSS token reusable by the stage (c) link
   hovers, instead of a sprite each locale has to carry. */
.curve-under {
  background-image: var(--curve-x);
  background-repeat: repeat-x;
  /* Lifted off the very bottom of the line box. The curve paints ~16px tall,
     and display leading of .92 makes consecutive line boxes overlap by about
     the same amount — so sitting flush at 100% put the underline straight
     through the ascenders of the line beneath whenever the phrase wrapped. */
  background-position: 0 calc(100% - .18em);
  background-size: 1.5em .3em;   /* holds the tile's own 5:1 aspect */
  padding-bottom: .22em;
  box-decoration-break: clone;
  -webkit-box-decoration-break: clone;
}

/* Keeps "short-form" whole. At display size a break after the hyphen reads as
   a rendering fault rather than a line break. A span rather than U+2011 so the
   text stays the literal string "short-form" for search. */
.nb { white-space: nowrap; }

.hero__lede {
  margin-top: var(--hero-gap);
  max-width: 46ch;
  font-size: var(--fs-md);
  color: var(--bone-dim);
  line-height: 1.4;
}

.hero__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-sm);
  margin-top: var(--sp-lg);
}

.showreel { position: relative; margin-top: var(--hero-gap); }

/* The whole figure is the hit area -- facade.js maps a click anywhere inside
   .showreel onto the caption link. A stretched ::after cannot do it here: the
   hover underline in motion.css makes .showreel__play position:relative, so its
   own ::after is trapped inside the link. Hence the pointer cue lives here and
   the mapping lives in the module. */
.showreel { cursor: pointer; }

.reel__media::after,
.showreel::after {
  content: "";
  position: absolute;
  left: var(--sp-sm);
  bottom: var(--sp-sm);
  width: 2.25rem;
  aspect-ratio: 1;
  border-radius: 50%;
  background: var(--amber) var(--play-mark) no-repeat 54% center;
  background-size: 36%;
  pointer-events: none;
}
.showreel::after {
  left: var(--sp-lg);
  bottom: var(--sp-lg);
  width: clamp(3rem, 5.5vw, 4.5rem);
}
.showreel__poster {
  width: 100%;
  aspect-ratio: 16 / 9;
  object-fit: cover;
  background: var(--ink-raised);
  border: 1px solid var(--ink-line);
}
.showreel__caption {
  display: flex;
  justify-content: space-between;
  gap: var(--sp-md);
  margin-top: var(--sp-sm);
  font-size: var(--fs-sm);
}
.showreel__play { color: var(--amber); font-weight: 600; }
.showreel__dur {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  color: var(--bone-dim);
}

@media (min-width: 60rem) {
  .hero {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    column-gap: var(--sp-2xl);
    align-items: start;
  }
  .hero__title   { grid-column: 1 / -1; }
  .hero__lede,
  .hero__actions { grid-column: 1; }
  .showreel      { grid-column: 2; grid-row: 2 / span 2; margin-top: var(--hero-gap); }
}


/* -----------------------------------------------------------------------------
   10. CLIENTS MARQUEE

   Static and wrapping here. motion.css turns it into a loop and unhides the
   duplicate row — so with CSS only, or under reduced motion, the names simply
   sit there once instead of appearing twice.
   -------------------------------------------------------------------------- */

.clients { padding-block: var(--sp-2xl) 0; }
.clients__title {
  font-size: var(--fs-xs);
  font-weight: 500;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--bone-dim);
  margin-bottom: var(--sp-lg);
}

.marquee { overflow: hidden; }
.marquee__row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-sm) var(--sp-md);
}
.marquee__row[aria-hidden="true"] { display: none; }

/* Each client is a pill: avatar, handle, follower count. Sized off the type
   ramp rather than fixed pixels, so it stays legible at 390px and doesn't look
   undersized at 1440. */
.client__link {
  display: flex;
  align-items: center;
  gap: var(--sp-sm);
  padding: .4rem .95rem .4rem .4rem;
  border: 1px solid var(--ink-line);
  border-radius: 999px;
  background: var(--ink-raised);
}
.client__avatar {
  width: clamp(2.5rem, 4.5vw, 3.25rem);
  height: clamp(2.5rem, 4.5vw, 3.25rem);
  border-radius: 50%;
  object-fit: cover;
  flex: 0 0 auto;
  background: var(--ink-line);   /* holds the circle before the file decodes */
}
.client__meta { display: grid; gap: .1em; min-width: 0; }
.client__handle {
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: -.01em;
  color: var(--bone);
  white-space: nowrap;
}
.client__count {
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-variant-numeric: tabular-nums;
  color: var(--bone-dim);
  white-space: nowrap;
}


/* -----------------------------------------------------------------------------
   11. WORK GRID

   Column counts are set by breakpoint rather than auto-fill so the 3-up desktop
   rhythm is exact. Card count is therefore free to change without touching CSS.
   -------------------------------------------------------------------------- */

.reel-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--gap) var(--gap);
}
@media (min-width: 40rem) { .reel-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64rem) { .reel-grid { grid-template-columns: repeat(3, 1fr); } }
/* 4-up past 90rem. At 1440 a 3-up card is ~384px wide, which at 9:16 makes it
   683px tall before its caption — taller than the viewport, so the most
   important section became several screens of scrolling with nothing to fix
   on. Four columns puts the natural height back under the cap below. */
@media (min-width: 90rem) { .reel-grid { grid-template-columns: repeat(4, 1fr); } }

/* Mobile: a peeking scroll-snap rail. Twelve full-width 9:16 cards stacked is
   an unscrollable wall; the peek also signals there's more sideways.
   Keyboard users reach every card by tabbing its link, which scrolls it in. */
/* Mobile: scroll-snap carousel, overflow contained to the grid itself.
   The previous version pulled the grid past the section's padding box with a
   negative margin to get a full-bleed peek; that escaped `body { overflow-x }`
   and gave the PAGE a horizontal scrollbar. The peek now comes from the column
   width alone, so nothing crosses the section boundary. */
@media (max-width: 39.999rem) {
  .reel-grid {
    grid-auto-flow: column;
    grid-auto-columns: 82%;
    grid-template-columns: none;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    overscroll-behavior-x: contain;
    padding-bottom: var(--sp-md);
  }
  .reel { scroll-snap-align: start; }
  .reel__media { max-height: none; }
}

.reel { position: relative; }

/* 9:16 is the native shape and stays the default, but it is capped so a whole
   card always fits on screen. Where the cap bites, object-fit crops rather than
   distorts — losing a little of the frame beats losing the section. */
.reel__media {
  position: relative;
  aspect-ratio: 1080 / 1920;
  max-height: min(56vh, 34rem);
  overflow: hidden;
  background: var(--ink-raised);
  border: 1px solid var(--ink-line);
}
.reel__poster {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.reel__client {
  margin-top: var(--sp-md);
  font-size: var(--fs-base);
  font-weight: 700;
  letter-spacing: -.01em;
}

/* One link, whole-card hit area. Accessible name stays just the client name
   plus its visually-hidden suffix — the overlay adds no extra tab stop. */
.reel__link::after { content: ""; position: absolute; inset: 0; }

.reel__desc {
  margin-top: var(--sp-2xs);
  font-size: var(--fs-sm);
  color: var(--bone-dim);
  line-height: 1.45;
}

.reel__meta {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-xs) var(--sp-md);
  margin-top: var(--sp-xs);
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  letter-spacing: .04em;
  text-transform: uppercase;
}
.reel__platform { color: var(--bone-dim); }
.reel__metric   { color: var(--amber); font-variant-numeric: tabular-nums; }


/* -----------------------------------------------------------------------------
   12. NUMBERS
   -------------------------------------------------------------------------- */

/* Hairlines top and bottom make this read as a beat between blocks rather than
   another section — it has no visible heading of its own. */
.numbers {
  border-block: 1px solid var(--ink-line);
  padding-block: clamp(2.5rem, 5vw, 4rem);
  margin-top: var(--sp-section);
}

.stats {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-xl);
}
/* Three equal columns gave 61.8M -- the best piece of evidence on the page --
   exactly the weight of "+25", and the section settled for --fs-xl while the
   hero next door commits to --fs-display. Nothing here is new: the lead figure
   simply takes the hero's own setting and the logo's own curve. The other two
   hold their scale, because a hierarchy needs something to be quieter. */
@media (min-width: 48rem) {
  .stats {
    grid-template-columns: 1.35fr 1fr;
    align-items: start;
    column-gap: var(--sp-2xl);
  }
  .stat:first-child { grid-row: span 2; }
}

.stat:first-child .stat__value {
  font-family: var(--font-display);
  font-size: var(--fs-display);
  font-stretch: 118%;
  letter-spacing: -.04em;
  line-height: .92;
  /* The motif, painted as it is under the h1 -- but a <dd> is a block, so it
     filled the whole grid column and the curve ran 188px past the end of the
     number. fit-content makes it hug the figure the way the hero's inline span
     does, and the deeper padding drops it clear of the digits. */
  width: fit-content;
  background-image: var(--curve-x);
  background-repeat: repeat-x;
  background-position: 0 100%;
  background-size: 1.5em .3em;
  padding-bottom: .3em;
}
.stat:first-child .stat__label { font-size: var(--fs-base); color: var(--bone); }

/* dt precedes dd in the markup because a dl requires it; column-reverse puts
   the figure back on top visually. */
.stat { display: flex; flex-direction: column-reverse; gap: var(--sp-2xs); }

.stat__value {
  font-family: var(--font-mono);
  font-size: var(--fs-xl);
  font-weight: 800;
  font-stretch: 115%;
  line-height: 1;
  letter-spacing: -.035em;
  font-variant-numeric: tabular-nums;
  color: var(--amber);
}
.stat__label {
  font-size: var(--fs-sm);
  color: var(--bone-dim);
  text-transform: uppercase;
  letter-spacing: .1em;
}


/* -----------------------------------------------------------------------------
   13. SERVICES
   -------------------------------------------------------------------------- */

.service-list { border-top: 1px solid var(--ink-line); max-width: 54rem; }
.service-list li {
  padding: var(--sp-md) 0;
  border-bottom: 1px solid var(--ink-line);
  font-size: var(--fs-md);
  font-weight: 500;
}

.services__aside {
  margin-top: var(--sp-md);
  font-size: var(--fs-sm);
  color: var(--bone-dim);
}

.services__anchor {
  margin-top: var(--sp-xl);
  max-width: var(--measure);
  font-size: var(--fs-base);
  color: var(--bone-dim);
}
.services__anchor strong { color: var(--bone); font-weight: 700; }


/* -----------------------------------------------------------------------------
   14. STUDIO
   -------------------------------------------------------------------------- */

.studio__copy {
  max-width: var(--measure);
  font-size: var(--fs-md);
  color: var(--bone-dim);
  line-height: 1.45;
}

.founders {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-xl);
  margin-top: var(--sp-xl);
}
@media (min-width: 48rem) { .founders { grid-template-columns: repeat(2, 1fr); } }

/* The figures are the founders' portraits. currentColor lets them take bone
   here and any other colour later without a second file. */
.founder__figure {
  width: auto;
  height: clamp(7rem, 14vw, 11rem);
  color: var(--bone);
  margin-bottom: var(--sp-md);
}
.founder__name { font-size: var(--fs-md); font-weight: 700; }
.founder__nick { color: var(--amber); font-weight: 500; }
.founder__role {
  margin-top: var(--sp-2xs);
  font-size: var(--fs-sm);
  color: var(--bone-dim);
  text-transform: uppercase;
  letter-spacing: .1em;
}


/* -----------------------------------------------------------------------------
   15. TESTIMONIALS
   -------------------------------------------------------------------------- */

.quotes {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--sp-xl);
}
@media (min-width: 56rem) { .quotes { grid-template-columns: repeat(3, 1fr); } }

.quote {
  height: 100%;
  padding: var(--sp-lg);
  background: var(--ink-raised);
  border: 1px solid var(--ink-line);
}
.quote blockquote p {
  font-size: var(--fs-base);
  line-height: 1.5;
}
.quote figcaption {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-xs);
  margin-top: var(--sp-md);
  font-size: var(--fs-sm);
}
.quote__name   { font-weight: 700; }
.quote__handle { color: var(--amber); font-family: var(--font-mono); }


/* -----------------------------------------------------------------------------
   16. CONTACT
   -------------------------------------------------------------------------- */

/* No `hidden` attribute on this in the markup — the HTML spec says authors must
   not override `hidden` with CSS, and with it present AT would keep ignoring
   the element even once :target revealed it, leaving the no-JS path with no
   confirmation at all. Visibility is entirely CSS's job. */
.form-sent { display: none; }
/* :target is the no-JS path (Web3Forms redirects to #sent); .is-sent is the
   fetch path. Same presentation either way. */
.form-sent:target,
.form-sent.is-sent {
  display: block;
  max-width: 40rem;
  margin-bottom: var(--sp-lg);
  padding: var(--sp-md) var(--sp-lg) calc(var(--sp-md) + .35em);
  background: var(--amber-soft) var(--curve-x) repeat-x left bottom var(--sp-md);
  background-size: 3em .5em;
  border-radius: 2px;
  font-weight: 600;
}

.enquiry { max-width: 40rem; }

/* Three of four fields are required. Marking the one optional field is quieter
   than three badges and still satisfies WCAG 3.3.2, which asks that required
   fields be identified -- not that each carry its own marker. */
.enquiry__note {
  max-width: 40rem;
  margin: 0 0 var(--sp-lg);
  font-size: var(--fs-sm);
  color: var(--bone-dim);
}
.field__opt {
  margin-left: .5ch;
  font-family: var(--font-mono);
  font-size: var(--fs-xs);
  font-weight: 400;
  letter-spacing: .04em;
  color: var(--bone-dim);
  text-transform: lowercase;
}

.field { margin-bottom: var(--sp-lg); }
.field label {
  display: block;
  margin-bottom: var(--sp-2xs);
  font-size: var(--fs-sm);
  color: var(--bone-dim);
  text-transform: uppercase;
  letter-spacing: .1em;
}
.field input,
.field textarea {
  width: 100%;
  padding: var(--sp-sm) var(--sp-md);
  background: var(--ink-raised);
  border: 1px solid var(--ink-line-strong);
  border-radius: 2px;
  font-size: var(--fs-base);
}
.field textarea { resize: vertical; min-height: 7rem; }

.enquiry__submit { margin-top: var(--sp-lg); }

.enquiry__status:empty { display: none; }
.enquiry__status {
  margin-top: var(--sp-md);
  font-size: var(--fs-sm);
  color: var(--amber);
}

.contact__alt {
  margin-top: var(--sp-lg);
  font-size: var(--fs-sm);
  color: var(--bone-dim);
}
.contact__alt a { color: var(--bone); text-decoration: underline; text-underline-offset: .25em; }


/* -----------------------------------------------------------------------------
   16b. LOADER

   Layout only — the draw, the exit and the failsafe are all in motion.css.
   Present in the markup rather than injected by JS so it covers the page from
   first paint; hidden outright without JS by nojs.css.
   -------------------------------------------------------------------------- */

.loader {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: grid;
  place-items: center;
  background: var(--ink);
}
.loader__art   { width: min(74vw, 620px); overflow: visible; }
.loader__curve { fill: none; stroke: var(--amber); stroke-width: 7; stroke-linecap: round; }
.loader__tc {
  margin-top: var(--sp-lg);
  text-align: center;
  font-family: var(--font-mono);
  font-size: var(--fs-sm);
  letter-spacing: .18em;
  color: var(--amber);
  font-variant-numeric: tabular-nums;
}


/* -----------------------------------------------------------------------------
   17. FOOTER + DIALOG
   -------------------------------------------------------------------------- */

.site-footer {
  position: relative;
  display: grid;
  gap: var(--sp-lg);
  max-width: var(--max);
  margin-inline: auto;
  padding: var(--sp-2xl) var(--pad-x) var(--sp-xl)
           calc(var(--pad-x) + var(--rail));
  --tc-top: var(--sp-2xl);
  border-top: 1px solid var(--ink-line);
  font-size: var(--fs-sm);
  color: var(--bone-dim);
}
/* The footer carries the 01:00 that ends the cut, so on mobile its gutter has to
   match the sections' -- declared after the padding shorthand above, which would
   otherwise win on source order at equal specificity. */
@media (max-width: 40rem) {
  .site-footer { padding-left: calc(var(--pad-x) + .5rem); }
}

@media (min-width: 56rem) {
  .site-footer {
    grid-template-columns: 2fr 1fr 1fr auto;
    align-items: start;
  }
  .site-footer__mark { grid-row: span 2; }
}

.site-footer__mark { width: clamp(9rem, 18vw, 15rem); height: auto; }
.site-footer__contact { font-style: normal; line-height: 1.7; }
.site-footer__contact a,
.site-footer__social a { color: var(--bone); }
.site-footer__social ul { display: grid; gap: var(--sp-xs); }
.site-footer__lang a { color: var(--amber); font-weight: 600; }
.site-footer__legal { font-size: var(--fs-xs); }

.player {
  position: relative;
  width: min(90vw, 60rem);
  padding: 0;
  background: var(--ink-raised);
  border: 1px solid var(--ink-line);
  color: var(--bone);
}

/* Loading and error copy live in the markup, not in CSS `content`, so each
   locale ships its own string. Both share the frame's 16/9 and are anchored to
   the same bottom edge, so they cover it exactly without hard-coding a height. */
.player__status {
  display: none;
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  aspect-ratio: 16 / 9;
  place-items: center;
  align-content: center;
  gap: var(--sp-sm);
  margin: 0;
  padding: var(--sp-lg);
  background: var(--ink-raised);
  color: var(--bone-dim);
  font-size: var(--fs-sm);
  text-align: center;
}
.player.is-loading [data-status="loading"],
.player.is-error   [data-status="error"] { display: grid; }
.player__status a { color: var(--amber); }

/* The bar is the curve again, drawn once across the wait. Static by default: the
   sweep is added in motion.css, so reduced motion gets a legible amber rule
   rather than nothing. */
.player__status[data-status="loading"]::after {
  content: "";
  width: min(14rem, 60%);
  height: .5em;
  background: var(--curve-x) repeat-x;
  background-size: 3em .5em;
}
.player::backdrop { background: rgb(15 15 14 / .88); background: rgb(from var(--ink) r g b / .88); }
.player__frame { aspect-ratio: 16 / 9; }
.player__frame iframe { width: 100%; height: 100%; border: 0; }
.player__close {
  display: block;
  margin-left: auto;
  padding: var(--sp-sm) var(--sp-md);
  background: none;
  border: 0;
  font-size: var(--fs-sm);
  cursor: pointer;
}
