/* ScrollSplitCard — Bizverse build.
   1:1 port of the Componentry `scroll-split-card` (componentry.dev) from
   React + framer-motion to the vanilla GSAP/ScrollTrigger + Lenis stack this
   site already runs. The layout/box model below mirrors the original Tailwind
   classes exactly; scripts/scroll-split-card.js drives the same transforms.
   Palette is Bizverse (no Componentry blue) and comes from the tailwind config:
   #0F1115 background · #1C1F26 surface · #DAA06D primary · #FDDAB8 on-surface. */

.ssc {                       /* was: relative h-[500vh] w-full */
  position: relative;
  width: 100%;
  /* Upstream is 500vh, but it runs inside its own 100dvh scroll box on native
     (Lenis-prevented) scrolling. Here the page scrolls, and Lenis' easing means
     the same 400vh of travel takes noticeably longer in wall-clock time.
     320vh leaves 220vh of travel. Paired with the tightened flip window in
     scripts/scroll-split-card.js (SPLIT_END / FLIP_END / LIFT_END) that puts the
     rotation at ~62vh, against ~104vh on upstream's even 0.4 / 0.8 timing. */
  height: 320vh;
}

.ssc__sticky {               /* was: sticky top-0 flex h-screen ... [perspective:1200px] */
  position: sticky;
  top: 0;
  display: flex;
  height: 100vh;
  height: 100dvh;
  width: 100%;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  perspective: 1200px;
  /* The trio is centred, so any extra height is taken equally off the top and
     the bottom — but only the top is constrained (fixed 92px navbar, then the
     closing line), while measurement showed 130-280px of unused room below on
     every viewport. This nudge shifts the centring down so that spare bottom
     room becomes card height instead. It is exactly the height added to the
     stage below, which keeps the resting top edge — and therefore every
     clearance above it — unchanged. box-sizing is border-box (Tailwind
     preflight), so the sticky box is still one viewport tall, and the cue and
     closing line are absolutely positioned against the padding box, so neither
     of them moves. */
  padding-top: 105px;
}

/* --- scroll cue + closing line (absolute in the sticky viewport) --- */
.ssc__cue,
.ssc__outro {
  position: absolute;
  left: 0;
  right: 0;
  text-align: center;
  padding: 0 24px;
  pointer-events: none;
  will-change: transform, opacity;
}

/* ONE headline slot. The opening line and the post-flip line occupy exactly the
   same place and are never on screen together — the opener has faded by p=0.1,
   the second arrives at p=0.68 — so the effect reads as a single headline
   changing its words rather than as two separate captions.
   max(): the percentage alone drops the line onto the navbar on short
   viewports, where 15% is barely past the navbar's fixed 92px. */
.ssc__cue,
.ssc__outro { top: max(112px, 15%); }

/* Shared typography: the opening line is a statement now, not a scroll hint,
   so it is set at the same scale as the line that replaces it. */
.ssc__cue p,
.ssc__outro p {
  font-family: "Outfit", ui-sans-serif, system-ui, sans-serif;
  font-size: clamp(20px, 4vw, 32px);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.2;
  color: rgba(253, 218, 184, 0.85);
}

.ssc__cue em,
.ssc__outro em {
  font-style: italic;
  color: #DAA06D;
}

/* --- the three-panel rig --- */
.ssc__stage {                /* was: flex h-[400px] w-full max-w-4xl px-4 relative */
  position: relative;
  display: flex;
  /* Sized to sit at one scale with the tilted grid's tiles below: ~373x504
     here against the gallery's ~356x504. Width is unchanged from before; this
     is the "longer" pass, so only the height grew (438 -> 504).
     The subtracted constant matters more than the dvh fraction: the navbar
     above is a fixed 92px whatever the viewport height, so a pure fraction
     leaves proportionally less room for the closing line on short screens.
     The pair below is solved from two real constraints — 504px tall on a
     900px viewport, and no more than ~288px on a 640px one, where the line
     and the navbar between them only leave ~176px of headroom. */
  height: clamp(325px, calc(83dvh - 139px), 665px);
  width: 100%;
  max-width: 72rem;
  padding: 0 16px;
  transform-style: preserve-3d;
  will-change: transform;
}

.ssc__panel {                /* was: relative h-full flex-1 */
  position: relative;
  height: 100%;
  flex: 1 1 0%;
  transform-style: preserve-3d;
  will-change: transform;
}

.ssc__face {                 /* shared by front + back: absolute inset-0 */
  position: absolute;
  inset: 0;
  overflow: hidden;
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
  /* No will-change here on purpose. The faces never move under their own
     transform — .ssc__panel is the animated layer — and listing border-radius /
     box-shadow promoted six extra composited layers whose only effect was more
     paint work per frame. */
}

/* Front — one image sliced into three. Each panel shows its own third of a
   300%-wide backdrop, so at rest the trio reads as a single flat photograph. */
.ssc__front { z-index: 2; }

.ssc__front-img {
  position: absolute;
  inset: 0;
  height: 100%;
  width: 300%;
  background-size: 100% 100%;
  background-position: center;
}

/* Back — the organiser content card, pre-flipped so it faces away at rest. */
.ssc__back {
  z-index: 1;
  transform: rotateY(180deg);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 32px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  background-image: linear-gradient(to bottom right, rgba(255, 255, 255, 0.1), transparent);
}

/* Grain, drawn in CSS so the component pulls no external asset.
   Deliberately NOT mix-blend-mode: overlay. Blending forces the compositor to
   read back the backdrop every frame, and with three of these rotating in 3D it
   was the single source of dropped frames in the reveal — profiling put the
   worst frame at 149ms with it on and 18ms with it off. The gradients already
   carry both white and black dots, so a plain low-opacity overlay reads the
   same and stays on the fast path. */
.ssc__grain {
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.1;
  background-image:
    repeating-radial-gradient(circle at 17% 32%, rgba(255,255,255,0.5) 0 1px, transparent 1px 3px),
    repeating-radial-gradient(circle at 71% 66%, rgba(0,0,0,0.5) 0 1px, transparent 1px 4px);
  background-size: 96px 96px, 128px 128px;
}

.ssc__eyebrow {
  position: relative;
  z-index: 10;
  margin-bottom: auto;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  opacity: 0.65;
}

.ssc__title {
  position: relative;
  z-index: 10;
  margin-bottom: 12px;
  font-family: "Outfit", ui-sans-serif, system-ui, sans-serif;
  font-size: 24px;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.01em;
}

.ssc__desc {
  position: relative;
  z-index: 10;
  font-family: "Inter", ui-sans-serif, system-ui, sans-serif;
  font-size: 13px;
  line-height: 1.5;
  opacity: 0.8;
}

/* ---------- tablet ---------- */
@media (max-width: 1023px) {
  .ssc__stage { height: clamp(345px, calc(50dvh + 65px), 605px); max-width: 54rem; }
  .ssc__back { padding: 24px; }
  .ssc__title { font-size: 20px; }
}

/* ---------- mobile: same split/reveal idea, flatter perspective ---------- */
@media (max-width: 767px) {
  .ssc { height: 280vh; }
  .ssc__sticky { perspective: 800px; }
  /* Each panel is 1/3 of this box's width, and the old height (a dvh-based
     clamp meant for a landscape 3-across layout) ignored that — at 375px wide
     each panel was ~117px across against a 345px+ tall card, a skinny 1:3
     sliver instead of the desktop trio's ~1:1.6. Height as a function of width
     keeps that ratio at any phone size: (100vw - 24px) is the stage's content
     width (padding already subtracted), /3 for one panel, *1.5 for the target
     aspect. */
  .ssc__stage { height: clamp(150px, calc((100vw - 24px) / 3 * 1.5), 400px); padding: 0 12px; }
  .ssc__back { padding: 16px 14px; }
  .ssc__eyebrow { font-size: 9px; letter-spacing: 0.12em; }
  .ssc__title { font-size: 15px; margin-bottom: 6px; }
  .ssc__desc { font-size: 10.5px; line-height: 1.45; }
  .ssc__cue,
  .ssc__outro { top: max(84px, 14%); }
}

@media (max-width: 400px) {
  .ssc__desc { font-size: 10px; }
}

/* ---------- reduced motion ----------
   No sticky scroll rig at all: the rotation/translation stack is dropped and
   the three content cards are laid out as a plain, fully readable grid. */
.ssc.is-static { height: auto; }

.ssc.is-static .ssc__sticky {
  position: static;
  height: auto;
  display: flex;
  flex-direction: column;
  /* Without the sticky 100vh viewport there is nothing centring the cards, so
     clear the fixed navbar explicitly. */
  padding: 150px 0 8px;
  perspective: none;
}

@media (max-width: 767px) {
  .ssc.is-static .ssc__sticky { padding-top: 110px; }
}

.ssc.is-static .ssc__cue { display: none; }

.ssc.is-static .ssc__outro {
  position: static;
  order: -1;                 /* above the cards, matching the animated layout */
  margin: 0 auto 32px;
  opacity: 1 !important;
  transform: none !important;
}

.ssc.is-static .ssc__stage {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 16px;
  height: auto;
  margin: 0 auto;
  transform: none !important;
  transform-style: flat;
}

.ssc.is-static .ssc__panel {
  height: 545px;
  transform: none !important;
  transform-style: flat;
}

.ssc.is-static .ssc__front { display: none; }

.ssc.is-static .ssc__back {
  transform: none;
  backface-visibility: visible;
  border-radius: 16px;
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.2),
              inset 0 -24px 48px rgba(0, 0, 0, 0.4);
}

@media (max-width: 767px) {
  .ssc.is-static .ssc__stage { grid-template-columns: 1fr; }
  .ssc.is-static .ssc__panel { height: auto; min-height: 200px; }
}

/* ==========================================================================
   OrganisingHeadsDeck — the mobile-only Organising Heads presentation.
   Pairs with scripts/oh-deck.js, which mounts it into the same host element
   the split card uses, but only under 768px. Everything above this comment is
   the desktop composition and is untouched.

   Sizing is solved against ONE constraint: the Cores gallery directly below.
   There a poster tile is min(380px, 100vw - 40px) wide at 1054/1492. The card
   here is 34px narrower per side (that is the neighbour peek — it has to come
   from somewhere) and buys the difference back in height: a taller 5:8
   portrait frame plus a caption block the gallery tiles do not carry. Net, a
   taller card than a Cores tile at every width from 320 to 414px, and the
   largest single visual in its own section.
   ========================================================================== */

.ohd {
  /* 52px of the viewport goes to the two 26px slivers of the neighbouring
     cards; every remaining pixel is the active portrait. That 26px is the
     smallest sliver that still reads as a card edge rather than as a shadow,
     and it is deliberately mean: it is the only thing this layout spends that
     a full-width Cores tile below does not. Capped so the deck does not become
     one enormous card on a large phone or a small tablet. */
  --ohd-card-w: min(calc(100vw - 52px), 380px);
  --ohd-peek: max(26px, calc((100vw - var(--ohd-card-w)) / 2));
  position: relative;
  /* Clears the fixed navbar. The split card needed 280vh of scroll to run its
     reveal; this needs none, which is where the section's dead vertical space
     went — the content simply starts. */
  padding: 116px 0 8px;
}

.ohd__intro {
  padding: 0 24px;
  text-align: center;
}

/* Same slot, same words and same type as the split card's opening line, so the
   two versions of the section read as one design. */
.ohd__cue {
  font-family: "Outfit", ui-sans-serif, system-ui, sans-serif;
  font-size: clamp(21px, 5.6vw, 30px);
  font-weight: 500;
  letter-spacing: -0.02em;
  line-height: 1.2;
  color: rgba(253, 218, 184, 0.85);
}

.ohd__cue em {
  font-style: italic;
  color: #DAA06D;
}

/* The section's real heading, set as the same eyebrow the Cores gallery uses
   for its own labels rather than as a competing headline. */
.ohd__title {
  margin-top: 14px;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: rgba(218, 160, 109, 0.85);
}

/* --- the deck ---
   A scroll-snap track, not a carousel widget: horizontal swipe, momentum and
   snapping are the browser's own, and a vertical drag still scrolls the page
   because this container only ever consumes the horizontal axis. */
.ohd__deck {
  position: relative;          /* offsetLeft in oh-deck.js is measured off this */
  display: flex;
  align-items: center;
  gap: 10px;
  /* The padding is what lets the first and last card reach the centre. */
  padding: 30px var(--ohd-peek) 26px;
  overflow-x: auto;
  overflow-y: hidden;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;   /* no accidental browser back-swipe */
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}

.ohd__deck::-webkit-scrollbar { display: none; }

.ohd__deck:focus-visible {
  outline: 2px solid #DAA06D;
  outline-offset: -2px;
  border-radius: 20px;
}

.ohd__card {
  position: relative;
  flex: 0 0 var(--ohd-card-w);
  scroll-snap-align: center;
  border-radius: 14px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  background: #1C1F26;
  overflow: hidden;
  /* Paper, lit from above — the same inset highlight and dropped shadow the
     split card's panels carry at the end of their reveal. */
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.12),
              0 26px 46px -20px rgba(0, 0, 0, 0.62);
  transform: translateX(var(--ohd-x, 0)) scale(0.925) rotate(var(--ohd-rot, 0deg));
  opacity: 0.62;
  transition: transform 0.45s cubic-bezier(0.22, 1, 0.36, 1),
              opacity 0.45s ease,
              box-shadow 0.45s ease;
  will-change: transform;
}

/* Neighbours are pulled IN, under the active card's edge, rather than left to
   float at arm's length: their inner edge disappears behind the portrait and
   what is left at the viewport edge is a clean ~26px sliver of card. Pulling
   them in is also what keeps that sliver constant — scaling a neighbour down
   about its own centre would have hidden it, not revealed it. */
.ohd__card.is-before { --ohd-x: 26px; --ohd-rot: -1.1deg; }
.ohd__card.is-after { --ohd-x: -26px; --ohd-rot: 1.1deg; }

.ohd__card.is-active {
  transform: none;
  opacity: 1;
  box-shadow: inset 0 1px 1px rgba(255, 255, 255, 0.16),
              0 34px 60px -22px rgba(0, 0, 0, 0.7),
              0 2px 10px -4px rgba(0, 0, 0, 0.45);
}

/* --- portrait frame ---
   5:8 and object-fit: cover, so a real portrait fills it without distortion at
   any source aspect. The crop is biased slightly above centre: on a standard
   portrait that keeps the face in the frame's upper third rather than in the
   dead middle, and keeps chins clear of the caption edge. */
.ohd__frame {
  position: relative;
  width: 100%;
  aspect-ratio: 5 / 8;
  overflow: hidden;
  background: #14171d;
}

.ohd__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 32%;
  user-select: none;
  -webkit-user-drag: none;
}

/* Awaiting-portrait slot, deliberately quiet — the same reserved-space
   language as the Cores gallery's empty tiles, so the two read as one system. */
.ohd__frame--empty {
  display: flex;
  align-items: center;
  justify-content: center;
  background:
    linear-gradient(150deg, rgba(218, 160, 109, 0.06), rgba(255, 255, 255, 0.015) 55%, transparent),
    #14171d;
  box-shadow: inset 0 0 0 1px rgba(218, 160, 109, 0.14);
}

.ohd__slot {
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: clamp(38px, 12vw, 56px);
  font-weight: 500;
  letter-spacing: 0.06em;
  color: rgba(253, 218, 184, 0.13);
  user-select: none;
}

/* --- caption --- */
.ohd__cap {
  padding: 15px 18px 17px;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
  background: linear-gradient(to bottom right, rgba(255, 255, 255, 0.05), transparent);
}

.ohd__name {
  font-family: "Outfit", ui-sans-serif, system-ui, sans-serif;
  font-size: 19px;
  font-weight: 600;
  letter-spacing: -0.01em;
  line-height: 1.2;
  color: #315D72;
}

.ohd__role {
  margin-top: 5px;
  font-family: "JetBrains Mono", ui-monospace, monospace;
  font-size: 10.5px;
  font-weight: 500;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(253, 218, 184, 0.8);
}

.ohd__note {
  margin-top: 10px;
  font-family: "Inter", ui-sans-serif, system-ui, sans-serif;
  font-size: 12.5px;
  line-height: 1.5;
  color: rgba(49, 93, 114, 0.8);
  /* Two lines, so a long blurb can never push one card taller than its
     neighbours and make the deck jump as it snaps. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* --- position indicator --- */
.ohd__dots {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 4px;
}

.ohd__dot {
  /* 44px of target around a 7px dot. */
  width: 34px;
  height: 44px;
  padding: 0;
  border: 0;
  background: none;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
}

.ohd__dot::before {
  content: "";
  width: 7px;
  height: 7px;
  border-radius: 999px;
  background: rgba(253, 218, 184, 0.28);
  transition: background 0.3s ease, transform 0.3s ease;
}

.ohd__dot[aria-current="true"]::before {
  background: #DAA06D;
  transform: scale(1.28);
}

.ohd__dot:focus-visible {
  outline: 2px solid #DAA06D;
  outline-offset: -6px;
  border-radius: 8px;
}

/* Reduced motion: the deck is a scroll container, so it keeps working exactly
   as it does above — only the settling animation between cards is dropped. */
@media (prefers-reduced-motion: reduce) {
  .ohd__card { transition: none; }
  .ohd__dot::before { transition: none; }
}

/* Very small phones (320px): the caption is what gives way, never the
   photograph. */
@media (max-width: 350px) {
  .ohd { --ohd-card-w: min(calc(100vw - 44px), 380px); }
  .ohd__cap { padding: 13px 14px 15px; }
  .ohd__name { font-size: 17px; }
}
