/* ============================================================
   Social Studio — every variant (/social, /social-1, /social-2, /social-3). Links after core.css.
   Split out of the former style.css on 2026-10-18; rules unchanged, in their
   original order. ============================================================ */

/* ============================================================
   Social Studio — LOADER VARIATION (/social-1/) — social-1.js · social-1-hero.js

   The hero is an intro in the manner of a loader. Everything in it is set on
   a 16:9 artboard (.sh-stage) in --u units: 1u is 1vw on a 16:9 window and
   less on a shorter one, so the composition contain-fits any viewport and
   nothing here is measured in JS. Sizes that animate are unitless custom
   properties (--w, --h, --gap, --tx) multiplied by --u; GSAP tweens the
   numbers and the layout follows, at any size, mid-flight included.

   THE RULES BELOW ARE THE SETTLED PAGE. The intro's opening state hangs off
   html.social-intro (stamped by the page's head script before first paint,
   removed by social-1-hero.js when the timeline hands over), so with JS off,
   under reduced motion, or on a return visit, the page is simply there.

   Black with white type on either theme (see the tokens).
   ============================================================ */

@font-face {
  font-family: 'PP Editorial New';
  src: url('../fonts/PPEditorialNew-UltralightItalic.woff2') format('woff2');
  font-weight: 200;
  font-style: italic;
  font-display: block;               /* the counter never flashes in a fallback face */
}

body.social-loader { background: var(--bg); color: var(--fg); }
.social-loader .site-header { position: absolute; }     /* stays with the hero; nothing below needs it */
.social-main { display: block; min-height: 0; padding-bottom: 0; position: relative; overflow-x: clip; }   /* the wordmark footer after it carries the bar clearance; nothing may widen the page (iOS pans a wider document) */

.social-hero {
  --u: min(1vw, calc(100svh / 56.25));
  --stage-h: 56.25;
  /* SOCIAL STUDIO: twelve characters set to the nav's margins (the ink is
     6.831em wide at this tracking), standing --pad-inline + --title-lift off
     the hero's foot. Here, not on the line, because the phone scroll cue
     centres itself between the still and this line. */
  --title-fs: calc((100vw - 2 * var(--pad-inline)) / 6.831);
  --title-lift: 10px;
  /* Where the title's baseline stands: the gutter + lift it always had, now
     measured from the TOP of the fixed furniture bar rather than the
     viewport's foot (the bar is back on this page — user, 2026-10-06). The
     still's centring and the phone EXPLORE cue read this same number. */
  --title-foot: calc(var(--furniture-h) + var(--pad-inline) + var(--title-lift));
  /* The settled still (desktop): the boards' 15.3 × 26.56 at 75% (user,
     2026-10-06). The EXPLORE cue is set from these same numbers. */
  --still-w: 11.475;
  --still-h: 19.92;
  /* The settled still's vertical offset from the row's centre (which is the
     viewport's): a lift that leaves even room above it and below it to the
     title's cap line (user, 2026-10-06) — half the distance from that cap
     line to the viewport's foot: (gutter + lift + cap height) / 2, Epilogue
     600's cap height being 0.748em and the baseline standing gutter + lift
     off the foot (see .sh-title-line). Pure px/vw. Here because the EXPLORE
     cue hangs off the still and needs the same number. */
  --title-cap: 0.748;
  --hero-lift: calc((var(--title-foot) + var(--title-cap) * var(--title-fs)) / -2);
  position: relative;
  height: 100svh;
  overflow: clip;
}
.sh-stage {
  position: absolute;
  left: 50%;
  top: 50%;
  width: calc(100 * var(--u));
  height: calc(var(--stage-h) * var(--u));
  translate: -50% -50%;
}
.sh-sr {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* The two display cuts, and the mask every moving line sits in: clipped top
   and bottom only, so italics and tight tracking are never shaved at the
   sides. */
.sh-serif { font-family: var(--ff-serif); font-weight: 400; letter-spacing: -0.03em; }
.sh-sans  { font-family: var(--ff-sans);  font-weight: 600; letter-spacing: -0.01em; }
.sh-mask {
  display: inline-block;
  overflow-x: visible;
  overflow-y: clip;
  line-height: 1;
  vertical-align: top;
}
.sh-in { display: inline-block; }

/* --- The settled line: SOCIAL STUDIO ------------------------------------- */
.sh-title {
  position: absolute;
  inset: 0;
  z-index: 1;                        /* under the still */
  margin: 0;
  font-size: inherit;
  font-weight: inherit;
  text-align: center;
}
.sh-title-line {
  position: absolute;
  left: 0;
  right: 0;
  font-size: calc(var(--fs) * var(--u));
  line-height: 1;
  white-space: nowrap;
}
/* Sized from the viewport (--title-fs), not the artboard. Flex, so the line
   centres even where its advance width (side bearings included) exceeds its
   box. */
.sh-title-line {
  font-size: var(--title-fs);
  translate: -0.01em 0;              /* the S's side bearing is wider than the O's */
  letter-spacing: -0.02em;
  display: flex;
  justify-content: center;
  gap: 0.2em;
}
/* …and it stands one --pad-inline plus 10px off the bottom of the viewport,
   not on the artboard: the offset is the artboard's distance from the hero's
   foot (negative on phones, whose artboard floats mid-screen), less the line
   box's room under the baseline, plus the margin. At every width — on phones
   too, since 2026-10-06 (the user's call); before that phones held it on the
   artboard under the still. */
.sh-title-line {
  bottom: calc((var(--stage-h) * var(--u) - 100svh) / 2 - 0.2225em + var(--title-foot));
}
/* …and when the title is the HERO's child rather than the stage's (social-3:
   so it can be fixed to the viewport while the card grows), the same foot
   measured from the hero's bottom — the stage's centring offset drops out. */
.sh-title--foot .sh-title-line {
  bottom: calc(var(--title-foot) - 0.2225em);
}
.sh-stage { z-index: 2; }            /* the stills stay over the title at rest (the title is z 1) */

/* --- The intro's statement: two lines above the row, two below ----------- */
.sh-head {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--top) * var(--u));
  display: flex;
  flex-direction: column;
  align-items: center;
  white-space: nowrap;
  visibility: hidden;                /* intro only */
}
.sh-head .sh-serif { font-size: calc(var(--fs-serif) * var(--u)); }
.sh-head .sh-sans  { font-size: calc(var(--fs-sans) * var(--u)); margin-top: calc(var(--lead) * var(--u)); }
/* Desktop text is the boards' × 0.73 (2026-10-06, the user's call): the
   headlines match the home hero's lockup (--hero-title-size, 8.6vw = 8.6u on
   a 16:9 stage) instead of the boards' 11.78u. Words and counter take the
   same factor below; the stills and the settled page do not. The top
   headline keeps its foot where the boards had it (23.54u, just over the
   row), so it moved down from 0 rather than floating up. */
.sh-head { --fs-serif: 8.6; --fs-sans: 8.54; --lead: 0.044; }
.sh-head--top    { --top: 6.36; }         /* 23.54 − (8.6 + 0.044 + 8.54) */
.sh-head--bottom { --top: 33.29; --lead: 0.058; }

/* --- The row of stills --------------------------------------------------- */
.sh-row {
  position: absolute;
  left: 50%;
  top: 50%;
  z-index: 2;
  translate: -50% -50%;
  display: flex;
  align-items: center;
  gap: calc(var(--gap, 0.53) * var(--u));   /* the settled (portrait) gap: 10px on 1920, held from the gathered beat (social-1-hero.js) */
}
.sh-img {
  --w: 6.2;
  --h: 8.94;
  flex: none;
  width: calc(var(--w) * var(--u));
  height: calc(var(--h) * var(--u));
  overflow: hidden;
  clip-path: polygon(0% 0%, 100% 0%, 100% 100%, 0% 100%);
  visibility: hidden;                /* four of the five are the intro's only */
}
.sh-img img,
.sh-img video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The centre card's loops are stacked; the one playing shows (social-1.js
   .is-current). Same in the grow layer (.sv-grow video below). */
.sh-img video { position: absolute; inset: 0; opacity: 0; }
.sh-img video.is-current { opacity: 1; }
.sv-grow video { opacity: 0; }
.sv-grow video.is-current { opacity: 1; }
/* The centre still is the page's hero image: where the intro leaves it —
   lifted by --hero-lift (a length, so no --ty: the intro tweens `top` itself
   and reads this value from the computed style). */
.sh-img--hero {
  --w: var(--still-w);
  --h: var(--still-h);
  --tx: 0.2;
  /* Offsets, not `translate`: GSAP animates this element's transform during
     the intro and folds the translate property into it, in pixels. */
  position: relative;
  left: calc(var(--tx) * var(--u));
  top: var(--hero-lift);
  visibility: visible;
}

/* is / a— and in / 2026: a two-step ticker off each end of the row. */
.sh-word {
  --word-h: 5.1;                     /* boards' 7 × 0.73 — see .sh-head */
  --word-dy: 0.33;                   /* the italics sit a touch under the row's centre line */
  position: absolute;
  top: 50%;
  height: calc(var(--word-h) * var(--u));
  margin-top: calc((var(--word-h) / -2 + var(--word-dy, 0)) * var(--u));
  overflow-x: visible;
  overflow-y: clip;
  font-family: var(--ff-editorial);
  font-weight: 200;
  font-style: italic;
  letter-spacing: 0;
  white-space: nowrap;
  visibility: hidden;                /* intro only */
}
.sh-word--l { right: 100%; margin-right: calc(var(--gap-l, 4.4) * var(--u)); text-align: right; }
.sh-word--r { left: 100%;  margin-left:  calc(var(--gap-r, 2.1) * var(--u)); }
.sh-ticker { display: block; }
.sh-ticker > span {
  display: block;
  height: calc(var(--word-h) * var(--u));
  line-height: calc(var(--word-h) * var(--u));
  font-size: calc(var(--word-fs, 2.92) * var(--u));     /* boards' 4 × 0.73 */
}
.sh-ticker > .sh-word-lg { font-size: calc(var(--word-lg-fs, 3.65) * var(--u)); }   /* boards' 5 × 0.73 */

/* EXPLORE beside the still — the nav's 12px label, set vertically, at its top
   corner, and the arrow at its foot: the element is the still's height and
   rides on the still's lift and is sized from the still's tokens, so it
   tracks the still. Left: 0.85u off the still's right edge (50 + 0.2 +
   --still-w / 2). */
.sh-scroll {
  position: absolute;
  left: calc((50 + 0.2 + var(--still-w) / 2 + 0.85) * var(--u));
  top: calc((var(--stage-h) - var(--still-h)) / 2 * var(--u) + var(--hero-lift));
  height: calc(var(--still-h) * var(--u));
  z-index: 2;
  margin: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-between;
  font-size: 12px;
  font-weight: 500;
  line-height: 1;
}
.sh-scroll span { writing-mode: vertical-rl; }
.sh-scroll svg { width: calc(1.5 * var(--u)); height: calc(2.7 * var(--u)); overflow: visible; }
.sh-scroll svg path { vector-effect: non-scaling-stroke; }

/* The count, bottom right of the hero (not the artboard). HIDDEN for now
   (user, 2026-10-06) — markup and timeline kept so it can come back. */
.sh-counter {
  display: none;
  position: absolute;
  right: calc(2.4 * var(--u));
  bottom: calc(1.3 * var(--u));
  margin: 0;
  overflow-y: clip;
  font-family: var(--ff-editorial);
  font-weight: 200;
  font-style: italic;
  font-size: calc(2.34 * var(--u));  /* boards' 3.2 × 0.73 — see .sh-head */
  line-height: 1.1;
  letter-spacing: 0;
  visibility: hidden;                /* intro only */
}
.sh-counter-in { display: block; }

/* --- After the hero: the still grows to full-bleed, then a message --------
   (v1, 2026-10-06; Figma 2253:2050). The growth is in flow, in the manner of
   adcker.com — no pin: the hero scrolls away as normal while .sv-grow (the
   still again, absolutely placed against .sv-settle, which has no overflow
   clip so the layer can reach up into the hero) is tweened by scroll from
   the settled still's box to the frame's full face (social-1-expand.js).
   There is ONE image, and it lives INSIDE the sticky frame (.sv-media), so
   once grown it holds on screen with the frame for the rest of .sv-settle's
   height — no counter-scroll — while the message types itself in letter by
   letter with the scroll (user, 2026-10-06); then both scroll on with the
   page. Everything here is the SETTLED state; JS only toggles the layer's
   visibility and drives the numbers. */
.sv-grow {                           /* the WINDOW: the frame's full face at rest … */
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
  z-index: 3;                        /* over the hero's row (2) and title (1); under the message (4) */
  transform-origin: 50% 50%;         /* … scaled (non-uniformly — it is only a mask) and moved onto the growing box
  will-change: transform;               (social-1-expand.js): transforms only, nothing laid out or re-rastered per frame */
  visibility: hidden;                /* hidden at the top of the page, where the still itself is the image */
  pointer-events: none;
}
.sv-grow img,
.sv-grow video {                     /* the IMAGE: the frame's cover-fitted size (JS), centred in the window, counter-scaled
                                        so it stays undistorted at the box's own cover scale. No clip-path anywhere: iOS
                                        Safari misdrew a clipped, transformed <img> (a wide strip mid-growth). */
  position: absolute;
  left: 50%;
  top: 50%;
  translate: -50% -50%;
  transform-origin: 50% 50%;
  max-width: none;                   /* the site caps images at 100%; this one is wider than its window on a portrait screen */
  max-height: none;
  object-fit: cover;
  will-change: transform;
}
.sv-settle {
  --settle-runway: 100svh;           /* the one knob: the growth landing (the typed message went, client 2026-10-14) */
  position: relative;                /* the layer's containing block; no z-index, no clip */
  height: var(--settle-runway);
  background: var(--bg);
}
.sv-media {
  position: sticky;                  /* the message stays centred on screen for the whole block */
  top: 0;
  z-index: 60;                       /* sticky makes a stacking context, so the box itself goes over the layer (3); over the nav (50) too, so the growing frame covers it (user, 2026-10-14) */
  height: 100lvh;                    /* the LARGE viewport: on iOS the toolbar collapses on scroll and 100svh left a black band under the full-bleed */
  display: grid;
  place-items: center;
  pointer-events: none;
}
/* The message: the site's italic body copy (Libre Caslon Text Italic 14/140,
   −2%, as the work credits — user, 2026-10-06), sentence case, on one narrow
   measure (459px on the board). Visible by default — JS splits it into
   letters and types them in with the scroll, so with no JS or reduced motion
   it is simply there. z-index 4 against the grow layer's 3, inside the
   frame's stacking context, so it reads OVER the full-bleed image. */
.sv-message {
  position: relative;
  z-index: 4;
  margin: 0;
  max-width: 460px;
  padding: 0 var(--pad-inline);
  text-align: center;
  font-family: var(--ff-serif-italic);
  font-style: italic;
  font-weight: 400;
  font-size: 14px;
  line-height: var(--lh-body);
  letter-spacing: -0.02em;
  color: #fff;
}
.sv-message .sv-char { opacity: 0; }   /* typed in by social-1-expand.js; the split keeps spaces as text */
/* Layer promotion for the hero's text. The title line is promoted ALWAYS,
   not only mid-growth: toggling will-change on it with .is-growing promoted
   a fresh layer while the title sat at opacity 0 (the way back up), and
   Safari paints a newly promoted layer for a frame before its opacity
   applies — SOCIAL STUDIO flashed (user, 2026-10-14). One text line is a
   cheap permanent layer. The scroll cue keeps the on-demand promotion. */
.sh-title-line { will-change: opacity, filter; }
.social-hero.is-growing .sh-scroll { will-change: opacity, filter; }
/* While the card grows, SOCIAL STUDIO stays where it settled — pinned to the
   viewport's foot over the growing frame — and fades out (social-1-expand.js
   scrubs the opacity; back up the hero, it fades in again). The hero has no
   transform and no z-index, so fixed here is the viewport, and 5 clears the
   settle frame's 4 (user, 2026-10-14). The pinned box is the HERO's box —
   top 0, 100svh tall — not the visual viewport (inset: 0), which grows as
   iOS's toolbar collapses and had the title shift at the switch. */
.social-hero.is-growing .sh-title { position: fixed; top: 0; left: 0; right: 0; bottom: auto; height: 100svh; z-index: 70; pointer-events: none; }   /* over the frame (60) while it fades; never in the way of a touch */
@media (prefers-reduced-motion: reduce) {
  .sv-grow { visibility: visible; }   /* no morph: the hero, then the full-bleed image, as blocks */
  .sv-message .sv-char { opacity: 1; }
}

/* --- The intro's opening state ------------------------------------------- */
html.social-intro { overflow: hidden; }
html.social-intro :is(.site-header, .furniture-bar) { opacity: 0; pointer-events: none; }
html.social-intro :is(.sh-head, .sh-word, .sh-counter, .sh-img) { visibility: visible; }
html.social-intro .sh-img { opacity: 0; }
html.social-intro .sh-scroll { opacity: 0; }
html.social-intro :is(.sh-head, .sh-title) .sh-in { transform: translateY(105%); }
html.social-intro .sh-ticker { transform: translateY(50%); }
html.social-intro :is(.sh-in, .sh-ticker, .sh-img, .sh-counter-in) { will-change: transform; }

/* --- Phones: the same beats on a taller artboard ------------------------- */
@media (max-width: 48em) {
  .social-hero {
    --u: min(1vw, calc(100svh / 140));
    --stage-h: 140;
  }
  .sh-head { --fs-serif: 19; --fs-sans: 17; --lead: 0; }
  .sh-head--top    { --top: 26.9; }
  .sh-head--bottom { --top: 77.77; --lead: 0; }

  /* Three cards, not five (QA 2026-10-08): the outer two never render on
     phones, and the three that remain are 25% larger (gaps unchanged; the
     JS beat sizes in social-1-hero.js PHONE match). */
  .sh-row > div.sh-img:first-of-type,
  .sh-row > div.sh-img:last-of-type { display: none; }
  .sh-img { --w: 11.875; --h: 17.125; }
  .sh-img--hero { --w: 40; --h: 66; --tx: 0; top: calc(-8 * var(--u)); }

  /* SOCIAL STUDIO stands off the furniture bar, and the bar is fixed to the
     viewport's foot — it moves with Safari's toolbar. At rest the title was
     absolute in the 100svh hero, so when the toolbar collapsed the bar went
     down with it and the title stayed where the small viewport put it: the
     two drifted apart by the toolbar's height (user, 2026-10-18). Fixed to
     the same foot, with the same safe-area pad as the bar, the title keeps
     its distance from the bar wherever the toolbar is — and the growth no
     longer switches it from one box to another, so nothing jumps at the
     switch either. Its z-index stays the hero's (1, under the still); the
     growth raises it (above). Once the hero has scrolled away the scrubbed
     fade has it at 0, so a fixed box leaves nothing on screen. */
  .sh-title--foot { position: fixed; top: auto; bottom: 0; left: 0; right: 0; height: auto; pointer-events: none; }
  .sh-title--foot .sh-title-line { bottom: calc(var(--title-foot) + var(--safe-bottom) - 0.2225em); }
  .social-hero.is-growing .sh-title--foot { top: auto; bottom: 0; height: auto; }

  /* Pinned growth (QA 2026-10-08: the still "glitched and jumped" while it
     scaled, because its frame was still in flow and lagging the finger). The
     settle block now starts UNDER the hero, so its sticky frame — and the
     growing layer in it — is fixed to the viewport from the first pixel of
     scroll: the image grows in place at the centre of the screen while the
     hero scrolls away beneath it (social-1-expand.js measures the still in
     document coordinates for this). The growth takes half a screen; then a
     half-screen hold while the hero finishes leaving; then the typing. */
  /* The runway, and the one sum that makes the full screen SET on a real
     phone: the frame (100lvh, sticky) can only stick for (runway − its own
     height). The growth is 0.5 × 100svh (the hero's height; the small
     viewport, toolbar expanded). So the runway is the frame plus the growth,
     in THOSE units — not 150svh, which with the toolbar expanded (svh < lvh)
     unstuck the frame ~100px before the growth ended. Then the frame scrolls
     off with the page and the stories arrive at its bottom (user, 2026-10-14). */
  .sv-settle { margin-top: -100svh; --settle-runway: calc(100lvh + 50svh); background: transparent; }   /* transparent: it overlaps the hero */

  /* No CSS scroll snap on this page any more (user, 2026-10-14): the hero
     travel is JS, the services are one screen the reader scrolls onto, and
     the page's old `proximity` snap pulled the scroll back on leaving them. */

  .sh-word { --word-h: 10; --word-fs: 6; --word-lg-fs: 7; --gap-l: 5.5; --gap-r: 4.5; }   /* 10px more air between the words and the cards (2.5u @ 390; user, 2026-10-14) */
  /* EXPLORE under the still, not beside it: centred in the gap between the
     still's foot (artboard: 95u) and the title's line box (viewport: --title-fs
     above its bottom offset). Both edges in stage coordinates, halved; the
     cue's own translate centres it on that point. Arrow in px — the artboard
     unit is too small here to size it. */
  .sh-scroll {
    left: 50%;
    top: calc((165 * var(--u) + 50svh - 0.7775 * var(--title-fs) - var(--title-foot)) / 2);
    height: auto;
    translate: -50% -50%;
    gap: 14px;
  }
  .sh-scroll svg { width: 10px; height: 18px; }
  .sh-counter { right: 16px; bottom: 16px; font-size: 32px; }
}

/* --- Social Studio services (/social-1/, v1 2026-10-06) --------------------
   Figma 2268:2144 / hover 2253:2082. The home page's mission statement, on
   this copy, under a small SOCIAL STUDIO SERVICES label (the site's .t-label,
   40px above — user, 2026-10-06, replacing the "Our services include" lead-in):
   Libre Caslon Display at 125px on 1920 (6.51vw), −0.03em, solid leading,
   120 × 335 padding; the inline images are three portraits at one size
   (--media-w / --media-ratio below). The reveal is mission.js
   (initMissionReveal); the hover is team-roster.js initBleed with its layer
   mounted INSIDE the section — behind the paragraph, over the black — rather
   than on body. The hover is the site's one hover-reveal (HOVER REVEAL block,
   hover-bleed.js) in its portrait cut: everything else drops to 10%, the
   still comes up over the dimmed copy and the hovered phrase alone rises above
   it, underlined. The phrases' boxes never overlap (1em inline-block leaves on
   solid leading), so the paint-order change under the pointer cannot strobe
   the way the roster once did. */
.services {
  --hover-still-w: 18.75vw;          /* the portrait hover still: 360 ÷ 1920 — 400 less 10% (user, 2026-10-14); read by .hover-bleed, a child of this section */
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;           /* at least a screen tall, its content centred (user, 2026-10-14) */
  min-height: 100svh;
  gap: 40px;                         /* label → copy → link, as the mission's */
  background: var(--bg);
  color: var(--fg);
  padding: 8.33vw 17.45vw;           /* 160 / 335 ÷ 1920 */
}
.svc-stage { display: contents; }   /* desktop: the flex gap runs through it; phones make it the sticky stage (below) */
.svc-label { margin: 0; text-align: center; }   /* type from .t-label; stays as it is under hover, like the roster's column labels */
/* The label and the link read OVER the hover portrait (user, 2026-10-14): the
   portrait layer is z 1 inside the section, the active phrase rises to 2 —
   these two sit with it, and are never dimmed (they are not [data-still]). */
.svc-label, .sl-cta { position: relative; z-index: 2; }
/* The sections' link — WORK WITH THE STUDIO, SEE THE WORK: the label type,
   underlined at rest (Figma link/Underline), 30% on hover like every link. */
.sl-cta {
  text-decoration: underline;
  text-underline-offset: var(--ul-offset);
  text-decoration-thickness: var(--ul-thickness);
}
.svc-text,
.cs-text {
  --media-w: .592em;                 /* one size for all three inline portraits (user, 2026-10-06): the first one's, the board's 74 × 100 at 125px */
  --media-ratio: 74 / 100;
  --media-gap: .18em;
  position: relative;                /* no z-index: the phrases stack against the still themselves */
  max-width: 100%;
  overflow-wrap: anywhere;           /* a phrase may break inside a word before it widens the page (phones) */
  font-family: var(--ff-serif);
  font-size: 6.51vw;                 /* 125 ÷ 1920 */
  letter-spacing: -0.03em;
  line-height: 1;
}
/* Case Studies By Request (Figma 2283:164): the services' type and rhythm,
   then the clients as a marquee — 957 × 162 on 1920, the set laid twice for
   the −50% loop — fading to nothing at both ends (a mask, so the ground shows
   through whatever the theme). Logos carry their board widths in --w, drawn
   at 120% (user, 2026-10-06), on cells half again wider than the board's 199.6
   for the extra air between them; American Airlines' --w is itself 75% of
   the board's 92.71 (69.53) — it read heavy at full size. */
.case-studies {
  position: relative;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 40px;
  background: var(--bg);
  color: var(--fg);
  padding: 8.33vw 17.45vw;           /* 160 / 335 ÷ 1920 */
}
.cs-marquee {
  width: 49.84vw;                    /* 957 ÷ 1920 */
  height: 8.44vw;                    /* 162 ÷ 1920 */
  overflow: clip;
  -webkit-mask-image: linear-gradient(to right, transparent, #000 18%, #000 82%, transparent);
  mask-image: linear-gradient(to right, transparent, #000 18%, #000 82%, transparent);
}
.cs-marquee-track {
  display: flex;
  width: max-content;
  height: 100%;
  will-change: transform;
}
.cs-logo {
  display: grid;
  place-items: center;
  flex: none;
  width: 15.6vw;                     /* 199.6 × 1.5 ÷ 1920 */
  height: 100%;
}
.cs-logo img {
  display: block;
  width: calc(var(--w) * 1.2 * 100vw / 1920);
  height: auto;
}
@media (max-width: 48em) {
  .services,
  .case-studies { padding: 15.38vw var(--pad-inline) 160px; }   /* 160px under the logos (user, 2026-10-14) */
  /* The logo marquee runs edge to edge on phones (user, 2026-10-14): it
     bleeds out of the section's side padding, and its fade masks the ends. */
  .cs-marquee { width: calc(100% + 2 * var(--pad-inline)); margin-inline: calc(-1 * var(--pad-inline)); }
  .svc-text,
  .cs-text { font-size: 12vw; }      /* as .mission-text on phones */
  .case-studies .sl-cta { margin-top: -20px; }   /* SEE THE WORK 20px under the line (user, 2026-10-14); the logos keep the 40 */
  .cs-marquee { height: 20vw; }
  .cs-logo { width: 37.5vw; }
  .cs-logo img { width: calc(var(--w) * 1.2 * 100vw / 800); }
}

/* Phones: the services as a STEPPER (user, 2026-10-06). No hover here, so the
   section becomes a runway five screens tall with the stage stuck at the top
   for 100svh; one scrubbed trigger (social-1-services.js initServicesStepper,
   snapping a screen at a time) makes one service active per step. The first
   step is the section as it is on arrival — black, the whole sentence at the
   phone paragraph's 12vw, no image (user, 2026-10-06); each step after it
   is one service: its portrait (a video when one exists) fills the screen IN
   FRONT of the dimmed copy and its phrase alone rises above it — the desktop
   hover, stepped by scroll, with the still at full screen. (Superseded 2026-10-14 by the STORIES below — kept for the record.) `.is-stepping` was set by the module
   only while its media query matches, so a widened window falls back to the
   desktop layout untouched. */
@media (max-width: 48em) {
  /* STORIES (user, 2026-10-14): one screen; the story's loop full screen (z 2),
     the label, the name, the indicator and the link in front (z 3), the tap
     halves over everything but the link (z 4 / 5). */
  .services.is-stories {
    display: block;
    position: relative;
    height: 100lvh;                  /* the full screen once iOS's toolbar collapses (100svh left the next section showing beneath) */
    padding: 0;
    overflow: clip;
    background: #000;                /* black first, the text alone — the stories only once the section is the view (.is-live) */
    color: #fff;                     /* over media, on either theme */
  }
  .services.is-stories .svc-stage {
    position: relative;
    height: 100svh;                  /* the fallback… */
    height: 100dvh;                  /* …and the dynamic viewport: the centred text follows Safari's toolbar (user, 2026-10-14); the 100lvh section and media cover what the toolbar uncovers */
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 40px;                       /* as the section's */
    padding: calc(10px + var(--safe-top)) var(--pad-inline) calc(10px + var(--safe-bottom));   /* 10px top and bottom (user, 2026-10-14), plus the phone's safe areas */
  }
  .services.is-stories .svc-text { display: none; }   /* the five lines are desktop's; a story shows one name */
  .svc-media {
    position: absolute;
    inset: 0;
    height: 100lvh;                  /* taller than the 100svh stage: fills to the bar once iOS's toolbar collapses */
    z-index: 2;
    pointer-events: none;
    opacity: 0;                      /* up only while the section is the view (social-1-services.js .is-live) */
    transition: opacity 0.6s ease;
  }
  .services.is-live .svc-media { opacity: 0.9; }   /* the story's still and loop at 90% over the black (user, 2026-10-14): the text reads */
  .svc-slide {
    position: absolute;
    inset: 0;
    margin: 0;
    opacity: 0;
    transition: opacity 0.4s ease;   /* stories cross-fade */
  }
  .svc-slide.is-active { opacity: 1; }
  .svc-slide :is(img, video) { display: block; width: 100%; height: 100%; object-fit: cover; }
  .svc-slide video { position: absolute; inset: 0; opacity: 0; transition: opacity 0.4s ease; }   /* fades in over its still once playing */
  .svc-slide video.is-playing { opacity: 1; }
  .services.is-stories :is(.svc-label, .svc-name) { position: relative; z-index: 3; }
  .services.is-stories .svc-name { margin-top: -20px; }   /* 20px under the label (user, 2026-10-14); the link keeps the 40 */
  .services.is-stories .sl-cta { position: relative; z-index: 5; margin-top: -20px; }   /* above the tap halves: still a link; 20px under the story's name (user, 2026-10-14) */
  .svc-name {
    margin: 0;
    text-align: center;
    font-family: var(--ff-serif);
    font-weight: 400;
    font-size: 12vw;                 /* as .svc-text on phones */
    line-height: 1;
    letter-spacing: -0.03em;
    text-wrap: balance;
  }
  /* The indicator, along the top under the nav. */
  .svc-indicator {
    position: absolute;
    left: var(--pad-inline);
    right: var(--pad-inline);
    top: calc(10px + var(--safe-top));   /* 10px from the stage's top (user, 2026-10-14), under the status bar */
    z-index: 3;
  }
  .svc-bars { display: flex; gap: 6px; }
  .svc-bar {
    flex: 1;
    height: 2px;
    background: rgba(255, 255, 255, 0.25);
    overflow: hidden;
  }
  .svc-bar-fill {
    display: block;
    height: 100%;
    background: #fff;
    transform-origin: left center;
    transform: scaleX(0);            /* the current one is tweened 0 → 1 over the story (social-1-services.js) */
  }
  .svc-count-row { display: flex; justify-content: space-between; align-items: baseline; margin-top: 10px; }   /* 10px under the bars (user, 2026-10-14) */
  .svc-count { font-variant-numeric: tabular-nums; }
  .svc-tap { position: absolute; inset: 0; z-index: 4; display: flex; }
  .svc-tap-half {
    flex: 1;
    touch-action: manipulation;      /* no 300ms tap delay; pinch off, pan free */
    appearance: none;
    background: none;
    border: 0;
    margin: 0;
    padding: 0;
    cursor: pointer;
    -webkit-tap-highlight-color: transparent;
  }
}

/* --- /social-3/ — services one per line (2026-10-06) -----------------------
   Figma 2268:2144 as revised: each service on its own line with its portrait
   inline, on a wider measure (the text runs 1491 wide on 1920, so the side
   padding is 214 rather than 335). The lines are forced by <br class="br-d">
   in the markup and held with nowrap on desktop; phones drop the breaks and
   wrap as the mission does. Everything else is /social-1/. */
.social-lines .services { padding-inline: 11.15vw; }   /* 214 ÷ 1920 */
@media (min-width: 48.0625em) {
  .social-lines .svc-text { white-space: nowrap; }
}
@media (max-width: 48em) {
  .social-lines .services { padding-inline: var(--pad-inline); }
  .social-lines .services.is-stories { padding: 0; }   /* the stories run full-bleed: this variation's padding must not win over its rule */
}

/* --- /social-2/ — the split-title variation (2026-10-06) ------------------
   The same intro, settling on the user's reference layouts (desktop-layout.jpg
   / mobile-layout.jpg, measured): the still centred on the viewport — so the
   settle is a pure resize, no lift — SOCIAL and STUDIO flanking it on desktop
   with their caps centred on it, the line under it on phones. No EXPLORE cue,
   no counter. Everything here only overrides the settled state above; the
   intro's frames are untouched. */
.social-split .sh-scroll { display: none; }
.social-split .sh-img--hero { --tx: 0; top: 0; }

@media (min-width: 48.0625em) {
  .social-split .sh-img--hero { --w: 17.97; --h: 33.41; }   /* reference: 17.97% × 59.4% of a 16:9 frame */
  .social-split .sh-title-line {
    --title-fs: 9.5vw;               /* cap height 7.1% of the frame width ÷ 0.748 */
    --margin: 5.62;                  /* ink to frame edge, both sides (the gaps to the still fall out: ~4.8 / 4.4u) */
    inset: 0;                        /* the full stage, so the masks' 50% is the still's centre */
    display: block;
    translate: none;
    letter-spacing: -0.03em;         /* the reference's tracking (the hero lockup's), not the line's −0.02em */
  }
  /* Each word is its own absolutely placed mask, set to the frame's edge —
     SOCIAL from the left, STUDIO from the right — with its cap line centred
     on the still's centre (caps sit 0.4035em under the box top: 0.7775em to
     the baseline less half of 0.748em). */
  .social-split .sh-title .sh-mask {
    position: absolute;
    top: calc(50% - 0.4035em);
  }
  .social-split .sh-title .sh-mask:first-of-type { left:  calc(var(--margin) * var(--u) - 0.044em); }   /* less the S's left side bearing */
  .social-split .sh-title .sh-mask:last-of-type  { right: calc(var(--margin) * var(--u) - 0.014em); }   /* less the O's right side bearing */
}

@media (max-width: 48em) {
  .social-split .sh-img--hero { --w: 48.21; --h: 89.4; }    /* reference: 48.2% of the width, 0.539 aspect */
  /* One line under the still: cap top 5u below its foot (the box top is
     0.0295em above the cap top), 7.03vw type set loose (the reference's
     phone words measure 5% wider than at −0.03em: tracking 0), 0.32em apart
     (ink gap 2.7% of the width). */
  .social-split .sh-title-line {
    --title-fs: 7.03vw;
    bottom: auto;
    top: calc(50% + (89.4 / 2 + 5) * var(--u) - 0.0295em);
    gap: 0.32em;
    letter-spacing: 0;
  }
}



/* ============================================================
   Social Studio (/social/) — social.js · social-intro.js ·
   social-services.js   (Figma 2168:2684; displays 2235:2005)

   Every measure is the board's own pixel value × --px, where 1px on the
   1920 board is 1/1920 of the viewport (1/390 on phones) — the site's
   vw-proportional rule, written so the numbers can be read against Figma.

   THE RULES BELOW ARE THE SETTLED PAGE; the hero's opening state hangs off
   html.ss-intro (stamped by the head script, lifted by social-intro.js).
   Black with white type on either theme (see the tokens).
   ============================================================ */

body.social-page { background: var(--bg); color: var(--fg); }
.social-page .site-header { position: absolute; }
.social-page .social-main { --px: calc(100vw / 1920); --ss-ease: cubic-bezier(0.75, 0, 0.25, 1); }

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

/* --- Hero: the lockup, on the board's 1920 × 1080 --------------------------- */
.ss-hero {
  position: relative;
  height: calc(1080 * var(--px));
}
.ss-lockup { position: absolute; inset: 0; }

/* Each word sits in a mask its own size and rises out of it. The artwork is
   white; it follows the ground like the logo does. */
.ss-word {
  position: absolute;
  left: calc(var(--x) * var(--px));
  top: calc(var(--y) * var(--px));
  width: calc(var(--w) * var(--px));
  height: calc(var(--h) * var(--px));
  overflow-x: visible;
  overflow-y: clip;
}
.ss-word img {
  display: block;
  width: 100%;
  height: 100%;
  filter: invert(var(--art-invert));
}
.ss-word--every      { --x: 686.14; --y: 125.31; --w: 550.28; --h: 132.03; }
.ss-word--campaign-1 { --x: 495.01; --y: 290.9;  --w: 917.2;  --h: 142.22; }
.ss-word--social     { --x: 653.37; --y: 695.27; --w: 610.77; --h: 134.05; }
.ss-word--campaign-2 { --x: 498.3;  --y: 863.49; --w: 910.64; --h: 141.2; }

.ss-stills {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(484.51 * var(--px));
  display: flex;
  justify-content: center;
  gap: calc(46.25 * var(--px));
}
.ss-still {
  width: calc(92.25 * var(--px));
  height: calc(159.13 * var(--px));
  overflow: hidden;
}
.ss-still img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.ss-still:nth-child(3) img { object-position: 62% 40%; }   /* keeps the face in the portrait crop */

/* "is a" · "in 2026." — small serif notes hanging beside their lines. */
.ss-note {
  position: absolute;
  left: calc(var(--x) * var(--px));
  top: calc(var(--y) * var(--px));
  margin: 0;
  font-family: var(--ff-serif-text);
  font-size: max(12px, calc(16.14 * var(--px)));
  line-height: 1.4;
  letter-spacing: -0.02em;
  white-space: nowrap;
}
.ss-note--is { --x: 593.94;  --y: 743.43; }
.ss-note--in { --x: 1442.93; --y: 926.44; }

/* --- Services: the strip ---------------------------------------------------- */
.ss-strip {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  padding: calc(40 * var(--px)) 20px 0;
}
.ss-card {
  appearance: none;
  background: none;
  border: 0;
  margin: 0;
  padding: 0;
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  flex: none;
  width: calc(227 * var(--px));
  height: calc(300 * var(--px));
  min-height: 200px;
  display: flex;
  flex-direction: column;
  gap: 16px;
  font-size: 12px;
  font-weight: 500;
  letter-spacing: -0.01em;
  text-transform: uppercase;
}
.ss-card-num { line-height: 1.6; white-space: pre; }
.ss-card-label { line-height: 1.4; white-space: nowrap; }
.ss-card-media {
  flex: 1 0 0;
  min-height: 1px;
  width: 100%;
  overflow: hidden;
  /* Rest: greyscale, the full box. Hover draws it in and brings its colour
     back; the selected card is in colour at full size. One transition for all. */
  filter: grayscale(1);
  clip-path: inset(0);
  transition: clip-path 0.5s var(--ss-ease), filter 0.5s var(--ss-ease);
}
.ss-card-media img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
@media (hover: hover) {
  .ss-card:hover .ss-card-media { clip-path: inset(8.5%); filter: grayscale(0); }
}
.ss-card:focus-visible { outline: none; }
.ss-card:focus-visible .ss-card-media { clip-path: inset(8.5%); filter: grayscale(0); }
.ss-card[aria-selected="true"] .ss-card-media { filter: grayscale(0); clip-path: inset(0); }

/* --- Services: the showcase ---------------------------------------------------
   ONE set of three frames (left · middle · right) that every service shares, so
   a change of service is the frames changing shape, not one row replacing
   another. Each frame sits on the 12-column, 10px-gap row by two unitless
   numbers — --l, the column it starts on (0-based), and --s, how many it spans
   — which social-services.js tweens; the row's height never changes, so
   selecting a service never moves the page.

     portrait trio   3·2   5·2   7·2
     square trio     0·4   4·4   8·4      (edge to edge)
     wide            the middle frame alone, 2·8; the outer two tucked under
                     it, faded out

   Inside a frame, one layer per service is stacked; the selected service's is
   shown, and they change with the site's blur fade. */
.ss-stage {
  display: grid;
  padding: calc(120 * var(--px)) 0 calc(40 * var(--px));
}
.ss-showcase,
.ss-title { grid-area: 1 / 1; }
.ss-showcase {
  --pitch: calc((100% + 10px) / 12);
  position: relative;
  height: calc(632 * var(--px));
}
.ss-frame {
  position: absolute;
  top: 0;
  bottom: 0;
  left: calc(var(--l) * var(--pitch));
  width: calc(var(--s) * var(--pitch) - 10px);
  margin: 0;
  overflow: hidden;
  background: var(--bg);
}
.ss-frame:nth-child(2) { z-index: 1; }   /* the one that grows over the others */

[data-layout="portrait-trio"] .ss-frame:nth-child(1) { --l: 3; --s: 2; }
[data-layout="portrait-trio"] .ss-frame:nth-child(2) { --l: 5; --s: 2; }
[data-layout="portrait-trio"] .ss-frame:nth-child(3) { --l: 7; --s: 2; }
[data-layout="square-trio"] .ss-frame:nth-child(1) { --l: 0; --s: 4; }
[data-layout="square-trio"] .ss-frame:nth-child(2) { --l: 4; --s: 4; }
[data-layout="square-trio"] .ss-frame:nth-child(3) { --l: 8; --s: 4; }
[data-layout="wide"] .ss-frame:nth-child(1) { --l: 3; --s: 2; opacity: 0; }
[data-layout="wide"] .ss-frame:nth-child(2) { --l: 2; --s: 8; }
[data-layout="wide"] .ss-frame:nth-child(3) { --l: 7; --s: 2; opacity: 0; }

.ss-layer {
  position: absolute;
  inset: 0;
  opacity: 0;
  visibility: hidden;
}
.ss-layer.is-active { opacity: 1; visibility: visible; z-index: 1; }
.ss-layer.is-leaving { visibility: visible; }
.ss-layer :is(img, video) {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
/* The loop fades up over its poster only once it is actually playing. */
.ss-layer video { opacity: 0; transition: opacity 0.4s ease; }
.ss-layer video.is-playing { opacity: 1; }

/* The service's name across the row. Every name is stacked in the same cell;
   the selected one shows, and they change with the same blur fade. */
.ss-title {
  place-self: center;
  z-index: 2;
  margin: 0;
  display: grid;
  pointer-events: none;
  color: #fff;
  font-family: var(--ff-serif);
  font-size: calc(72 * var(--px));
  line-height: 1;
  letter-spacing: -0.03em;
  text-align: center;
  white-space: nowrap;
}
.ss-title span {
  grid-area: 1 / 1;
  opacity: 0;
  visibility: hidden;
}
.ss-title span.is-active { opacity: 1; visibility: visible; }
.ss-title span.is-leaving { visibility: visible; }

.ss-cta {
  margin: 0;
  padding-bottom: calc(80 * var(--px));
  text-align: center;
}
.ss-link {
  font-size: 12px;
  font-weight: 500;
  line-height: 1.4;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  text-decoration: underline;
  text-decoration-thickness: 0.075em;
  text-underline-offset: var(--ul-offset);
  text-decoration-skip-ink: none;
  color: inherit;
}

/* --- Clients -------------------------------------------------------------- */
.ss-clients {
  background: var(--bg);
  color: var(--fg);
  padding-bottom: calc(40 * var(--px));
}
.ss-clients-head {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 40px;
  padding: calc(120 * var(--px)) var(--pad-inline) calc(80 * var(--px));
}
.ss-clients-title {
  margin: 0;
  font-family: var(--ff-serif);
  font-weight: 400;
  font-size: max(28px, calc(72 * var(--px)));
  line-height: 1;
  letter-spacing: -0.03em;
}
.ss-logos {
  list-style: none;
  margin: 0;
  padding: 0 calc(160 * var(--px));
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px;
}
.ss-logos li {
  height: calc(244 * var(--px));
  display: flex;
  align-items: center;
  justify-content: center;
}
.ss-logos img {
  width: calc(var(--w) * var(--px));
  height: calc(var(--h) * var(--px));
  object-fit: contain;
}

/* --- The hero's opening state --------------------------------------------- */
html.ss-intro .site-header { opacity: 0; pointer-events: none; }
html.ss-intro .ss-word img { transform: translateY(105%); }
html.ss-intro .ss-still { clip-path: inset(100% 0 0 0); }
html.ss-intro .ss-note { opacity: 0; }
html.ss-intro :is(.ss-word img, .ss-still, .ss-still img) { will-change: transform, clip-path; }

/* --- Phones: the same pieces, stacked ---------------------------------------- */
@media (max-width: 48em) {
  .social-page .social-main { --px: calc(100vw / 390); }

  .ss-hero { height: auto; min-height: 100svh; display: flex; }
  .ss-lockup {
    position: static;
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: calc(14 * var(--px));
    padding: var(--nav-h) 0 calc(40 * var(--px));
  }
  .ss-word { position: static; width: calc(var(--w) * 0.4 * var(--px)); height: calc(var(--h) * 0.4 * var(--px)); }
  .ss-stills { position: static; gap: calc(20 * var(--px)); margin: calc(6 * var(--px)) 0; }
  .ss-still { width: calc(60 * var(--px)); height: calc(103.5 * var(--px)); }
  .ss-note { position: static; font-size: 14px; }
  /* "is a" comes before SOCIAL here (it hangs beside it on the board). */
  .ss-word--every      { order: 1; }
  .ss-word--campaign-1 { order: 2; }
  .ss-stills           { order: 3; }
  .ss-note--is         { order: 4; }
  .ss-word--social     { order: 5; }
  .ss-word--campaign-2 { order: 6; }
  .ss-note--in         { order: 7; }

  /* The strip scrolls sideways, a card and a bit at a time. */
  .ss-strip {
    justify-content: flex-start;
    gap: 10px;
    padding: 0 var(--pad-inline);
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: var(--pad-inline);
    scrollbar-width: none;
  }
  .ss-strip::-webkit-scrollbar { display: none; }
  .ss-card { width: 56vw; height: 68vw; scroll-snap-align: start; }

  /* The frames become a row to swipe — no morph here, the layout just sets
     their widths — all the same height; the name sits beneath it rather than
     across. */
  .ss-stage { padding: calc(40 * var(--px)) 0 calc(20 * var(--px)); }
  .ss-showcase {
    display: flex;
    gap: 10px;
    height: 100vw;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    scrollbar-width: none;
  }
  .ss-showcase::-webkit-scrollbar { display: none; }
  .ss-frame { position: relative; left: auto; flex: none; scroll-snap-align: center; }
  [data-layout="portrait-trio"] { padding-inline: var(--pad-inline); }
  [data-layout="portrait-trio"] .ss-frame { width: 56vw; }
  [data-layout="square-trio"] .ss-frame,
  [data-layout="wide"] .ss-frame { width: 100vw; }
  [data-layout="wide"] .ss-frame:not(:nth-child(2)) { display: none; }
  .ss-title {
    grid-area: 2 / 1;
    color: inherit;
    font-size: 28px;
    padding-top: 16px;
  }
  .ss-cta { padding: 20px 0 calc(60 * var(--px)); }

  .ss-clients-head { gap: 20px; padding: calc(60 * var(--px)) var(--pad-inline) calc(40 * var(--px)); }
  .ss-logos { padding: 0 var(--pad-inline); grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .ss-logos li { height: 120px; }
  .ss-logos img { width: calc(var(--w) * 0.52 * var(--px)); height: calc(var(--h) * 0.52 * var(--px)); }
}
