/* ============================================================
   Fonts, self-hosted
   Pulled from Google Fonts once and committed to assets/fonts/, so a
   page load talks to no server but ours. latin + latin-ext only: the
   te reo macrons (Tāmaki Makaurau) live in latin-ext, everything else
   we set is latin. Space Grotesk is one variable file covering 300-700,
   which is why four weights need only one @font-face per subset.
   To refresh: see README "Fonts are self-hosted".
   ============================================================ */
@font-face {
  font-family: 'Archivo Black';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/archivo-black-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Archivo Black';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("../assets/fonts/archivo-black-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("../assets/fonts/space-grotesk-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Space Grotesk';
  font-style: normal;
  font-weight: 300 700;
  font-display: swap;
  src: url("../assets/fonts/space-grotesk-latin-ext.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

/* ============================================================
   OYNK Creative — Design Tokens
   Brand source: OYNK_Creative_Brand_Colour_Guide.pdf v1.0
   ============================================================ */
:root {
  /* Brand colours — sampled from OYNK_Creative_Brand_Colour_Guide_Punchy_GenZ */
  --oynk-red: #DB322B;        /* hero red, unchanged */
  --oynk-red-hover: #C42720;
  --red-deep: #D02D26;        /* red section fills: cream copy clears 4.5:1 here, brand red is 4.31:1 */
  --ink: #2A1028;
  --cream: #FFF4EC;
  --cobalt: #4D55FF;          /* electric cobalt */
  --cobalt-deep: #3A41D9;     /* cobalt labels on light surfaces */
  --cobalt-wash: #EDEEFF;     /* derived light surface (guide has no light tint) */
  --lime: #D8FF3E;            /* acid lime — DARK BACKGROUNDS ONLY, 1.06:1 on cream */

  /* Semantic tokens */
  --color-bg: var(--cream);
  --color-bg-alt: var(--cobalt-wash);
  --color-bg-dark: var(--ink);
  --color-primary: var(--oynk-red);
  --color-primary-hover: var(--oynk-red-hover);
  --color-accent: var(--cobalt);
  --color-text: var(--ink);
  --color-text-on-dark: var(--cream);
  --color-text-muted: #6B5468;
  --color-border: rgba(42, 16, 40, 0.12);
  --color-border-dark: rgba(255, 244, 236, 0.16);

  /* Typography */
  --font-display: "Archivo Black", "Arial Black", sans-serif;
  --font-body: "Space Grotesk", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;

  --fs-xs: 0.8125rem;
  --fs-sm: 0.9375rem;
  --fs-base: 1rem;
  --fs-md: 1.125rem;
  --fs-lg: 1.375rem;
  --fs-xl: 1.75rem;
  --fs-2xl: 2.25rem;
  --fs-3xl: 3rem;
  --fs-4xl: 4rem;
  --fs-5xl: 5.5rem;

  /* Spacing (8px rhythm) */
  --space-1: 0.5rem;
  --space-2: 1rem;
  --space-3: 1.5rem;
  --space-4: 2rem;
  --space-5: 3rem;
  --space-6: 4rem;
  --space-7: 6rem;
  --space-8: 8rem;

  --radius-sm: 12px;
  --radius-md: 20px;
  --radius-lg: 32px;
  --radius-pill: 999px;

  --shadow-soft: 0 4px 24px rgba(42, 16, 40, 0.08);
  --shadow-lift: 0 16px 40px rgba(42, 16, 40, 0.16);

  --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
  --dur-fast: 150ms;
  --dur-base: 250ms;
  --dur-slow: 400ms;

  --container: 1200px;
}

/* ============================================================
   Reset
   ============================================================ */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; padding: 0; }
html { -webkit-text-size-adjust: 100%; overflow-x: hidden; }
html, body { min-height: 100dvh; }
body {
  font-family: var(--font-body);
  font-size: var(--fs-base);
  line-height: 1.55;
  letter-spacing: -0.005em;
  color: var(--color-text);
  background: var(--color-bg);
  overflow-x: hidden;
  width: 100%;
}
img, picture, svg { display: block; max-width: 100%; }
a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: none; cursor: pointer; }
ul { list-style: none; }
input, textarea, select { font: inherit; color: inherit; }

:focus-visible {
  outline: 3px solid var(--cobalt);
  outline-offset: 3px;
  border-radius: 4px;
}

a, button, .btn, [role="button"] { cursor: pointer; touch-action: manipulation; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

/* ============================================================
   Cross-document view transitions
   Pure CSS, no JS. The browser crossfades the old document into the
   new one instead of flashing white between them. Firefox ignores the
   whole block, which is exactly the fallback we want.
   ============================================================ */
@media (prefers-reduced-motion: no-preference) {
  @view-transition { navigation: auto; }

  ::view-transition-old(root),
  ::view-transition-new(root) {
    animation-duration: 220ms;
    animation-timing-function: var(--ease-out);
  }

  /* The header is byte-identical on every page, so exclude it from the
     crossfade: it should read as chrome that stayed put, not as something
     that re-rendered. Naming it gives it its own snapshot; killing the
     animation on that snapshot holds it still. */
  .nav { view-transition-name: site-nav; }
  ::view-transition-old(site-nav),
  ::view-transition-new(site-nav) { animation: none; opacity: 1; }
}

/* ============================================================
   Typography
   ============================================================ */
h1, h2, h3 {
  font-family: var(--font-display);
  line-height: 1.02;
  letter-spacing: -0.01em;
  text-transform: uppercase;
  color: var(--ink);
}
h1 { font-size: clamp(2.75rem, 5vw + 1rem, var(--fs-5xl)); }
h2 { font-size: clamp(2rem, 3.4vw + 1rem, var(--fs-3xl)); }
h3 { font-size: clamp(1.375rem, 1.6vw + 1rem, var(--fs-xl)); }
h4 { font-family: var(--font-body); font-weight: 700; font-size: var(--fs-md); }
p { max-width: 62ch; }
p.lede { font-size: var(--fs-lg); color: var(--color-text-muted); max-width: 46ch; }
.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  font-family: var(--font-body);
  font-weight: 700;
  font-size: var(--fs-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--oynk-red);
}
.section-dark h1, .section-dark h2, .section-dark h3 {
  color: var(--cream);
}
/* Acid lime is deliberately rationed: 15.2:1 on ink but only 1.06:1 on
   cream, so it appears on dark surfaces only, and only as a small accent. */
.section-dark .eyebrow { color: var(--lime); }
.section-dark p, .section-dark p.lede { color: rgba(255, 244, 236, 0.72); }

/* ============================================================
   Layout helpers
   ============================================================ */
.wrap {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--space-3);
}
@media (min-width: 768px) { .wrap { padding-inline: var(--space-4); } }

section { padding-block: var(--space-6); position: relative; }
@media (min-width: 1024px) { section { padding-block: var(--space-7); } }

.section-alt { background: var(--cobalt-wash); }
.section-dark { background: var(--ink); }
.section-red { background: var(--red-deep); }

.grid { display: grid; gap: var(--space-3); }
.grid > * { min-width: 0; }
.grid-2 { grid-template-columns: 1fr; }
.grid-3 { grid-template-columns: 1fr; }
.grid-4 { grid-template-columns: 1fr; }
@media (min-width: 640px) {
  .grid-2 { grid-template-columns: repeat(2, 1fr); }
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
}
@media (min-width: 1024px) {
  .grid-3 { grid-template-columns: repeat(3, 1fr); }
  .grid-4 { grid-template-columns: repeat(4, 1fr); }
}

.text-center { text-align: center; }
.mx-auto { margin-inline: auto; }

.section-head { max-width: 46rem; margin-bottom: var(--space-5); }
.section-head.center { margin-inline: auto; text-align: center; }

/* ============================================================
   Buttons
   ============================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5em;
  padding: 0.95em 1.7em;
  border-radius: var(--radius-pill);
  font-weight: 700;
  font-size: var(--fs-sm);
  letter-spacing: 0.01em;
  min-height: 48px;
  transition: transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              background-color var(--dur-base) var(--ease-out);
  white-space: nowrap;
}
.btn:active { transform: scale(0.97); }

.btn-primary {
  background: var(--oynk-red);
  color: var(--cream);
  box-shadow: var(--shadow-soft);
}
.btn-primary:hover { background: var(--oynk-red-hover); transform: translateY(-2px); box-shadow: var(--shadow-lift); }

.btn-dark {
  background: var(--ink);
  color: var(--cream);
}
.btn-dark:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); }

.btn-outline {
  background: transparent;
  border: 2px solid currentColor;
  color: var(--ink);
}
.section-dark .btn-outline, .section-red .btn-outline { color: var(--cream); }
.btn-outline:hover { background: rgba(42, 16, 40, 0.06); transform: translateY(-2px); }
.section-dark .btn-outline:hover, .section-red .btn-outline:hover { background: rgba(255, 244, 236, 0.12); }

.btn-ghost {
  padding: 0.6em 0.2em;
  font-weight: 700;
  color: var(--ink);
  border-bottom: 2px solid transparent;
}
.btn-ghost:hover { border-color: currentColor; }

.icon { width: 1.15em; height: 1.15em; flex: none; stroke: currentColor; fill: none; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; }

/* ============================================================
   Nav
   ============================================================ */
.nav {
  position: sticky;
  top: 0;
  z-index: 100;
  background: rgba(255, 244, 236, 0.85);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--color-border);
}
.nav-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-block: var(--space-2);
  gap: var(--space-3);
}
.brand { display: flex; align-items: center; gap: 0.6rem; font-family: var(--font-display); font-size: var(--fs-md); letter-spacing: -0.01em; }
.brand img.wordmark { height: 22px; width: auto; }

.nav-links {
  display: none;
  align-items: center;
  gap: var(--space-4);
  font-weight: 700;
  font-size: var(--fs-sm);
}
.nav-links a { position: relative; padding-block: 0.3rem; }
.nav-links a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -2px;
  height: 2px;
  background: var(--oynk-red);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-base) var(--ease-out);
}
.nav-links a:hover::after, .nav-links a[aria-current="page"]::after { transform: scaleX(1); }
.nav-links a[aria-current="page"] { color: var(--oynk-red); }

.nav-cta { display: none; }
.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  border-radius: var(--radius-pill);
}
.nav-toggle:hover { background: rgba(42, 16, 40, 0.06); }

@media (min-width: 900px) {
  .nav-links { display: flex; }
  .nav-cta { display: inline-flex; }
  .nav-toggle { display: none; }
}

.mobile-menu {
  position: fixed;
  inset: 0;
  z-index: 200;
  background: var(--ink);
  color: var(--cream);
  display: flex;
  flex-direction: column;
  padding: var(--space-3);
  transform: translateY(-100%);
  transition: transform var(--dur-slow) var(--ease-out);
}
.mobile-menu.is-open { transform: translateY(0); }
.mobile-menu-head { display: flex; justify-content: space-between; align-items: center; }
.mobile-menu-links { display: flex; flex-direction: column; gap: var(--space-2); margin-top: var(--space-6); font-family: var(--font-display); font-size: var(--fs-2xl); text-transform: uppercase; }
.mobile-menu-links a { display: block; }
.mobile-menu-foot { margin-top: auto; display: flex; flex-direction: column; gap: var(--space-2); }
body.menu-open { overflow: hidden; }

/* ============================================================
   Hero
   ============================================================ */
.hero {
  padding-block: var(--space-6) var(--space-7);
  overflow: hidden;
  position: relative;
}
.hero .wrap { position: relative; z-index: 1; }
.hero-copy { max-width: 40rem; }
.hero-copy p.lede { margin-top: var(--space-3); }
.hero-actions { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-4); }
 { font-size: var(--fs-xs); font-weight: 700; letter-spacing: 0.04em; text-transform: uppercase; color: var(--color-text-muted); }

/* ============================================================
   Cards
   ============================================================ */
.card {
  background: var(--cream);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  box-shadow: var(--shadow-soft);
  transition: transform var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out);
}
.card:hover { transform: translateY(-4px) scale(1.01); box-shadow: var(--shadow-lift); }
.card-plum { background: var(--ink); color: var(--cream); }
.card-red { background: var(--oynk-red); color: var(--cream); }
/* Uses --cobalt-deep, not --cobalt. Body copy on coloured cards is cream
   at 86%, which measures 3.96:1 on #4D55FF and fails AA. The deeper tone
   takes it to 5.30:1 and looks near-identical at card size. */
.card-cobalt { background: var(--cobalt-deep); color: var(--cream); }

/* Plain-language comparison under each headline figure. Written by
   scripts/render-dashboard.mjs, never by hand: the numbers change daily and
   a hardcoded comparison would go from stale to false. */
.stat-note {
  margin-top: 0.4rem;
  font-size: var(--fs-xs);
  line-height: 1.35;
  color: var(--color-text-muted);
  max-width: 20ch;
  margin-inline: auto;
}
/* Cream at 62% measures 6.77:1 on ink, comfortably past AA. */
.section-dark .stat-note { color: rgba(255, 244, 236, 0.62); }

.card-icon {
  width: 52px; height: 52px;
  border-radius: var(--radius-sm);
  display: flex; align-items: center; justify-content: center;
  background: rgba(219, 50, 43, 0.1);
  color: var(--oynk-red);
  margin-bottom: var(--space-3);
}
.card-plum .card-icon, .card-red .card-icon, .card-cobalt .card-icon {
  background: rgba(255, 244, 236, 0.16);
  color: var(--cream);
}
.card h3 { margin-bottom: 0.5rem; }
.card p { color: var(--color-text-muted); }
.card-plum p, .card-red p, .card-cobalt p { color: rgba(255, 244, 236, 0.86); }
/* Headings inherit --ink globally, which is invisible on .card-plum and
   muddy on .card-red. Coloured cards need light headings. */
.card-plum h2,  .card-plum h3,  .card-plum h4,
.card-red h2,   .card-red h3,   .card-red h4,
.card-cobalt h2, .card-cobalt h3, .card-cobalt h4 { color: var(--cream); }

.tag {
  display: inline-flex;
  align-items: center;
  gap: 0.35em;
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 0.35em 0.8em;
  border-radius: var(--radius-pill);
  background: rgba(77, 85, 255, 0.16);
  color: var(--cobalt-deep);
}

/* ============================================================
   Receipts wall
   The portfolio as a feed. Each card IS the reel frame; the view
   count is the headline and the still is the substrate. Four columns
   because the source stills are 440px wide: at three columns each
   card asks for ~726 source px at 2x DPR and there aren't any.
   ============================================================ */
.receipts {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-2);
}
@media (min-width: 560px)  { .receipts { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1024px) {
  .receipts { grid-template-columns: repeat(4, 1fr); padding-bottom: var(--space-5); }
  /* Knock every second card off the baseline so the wall reads as a feed
     rather than a spreadsheet. transform, not margin, so it costs no row
     height and leaves no gap between the two rows. */
  .receipt:nth-child(even) { transform: translateY(var(--space-5)); }
}

.receipt {
  position: relative;
  display: block;
  aspect-ratio: 9 / 16;
  overflow: hidden;
  /* Deliberately not the 32px used on every other card. Things you read
     get a tighter corner than things you press. */
  border-radius: var(--radius-sm);
  background: var(--ink);
  color: var(--cream);
}
.receipt img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform var(--dur-slow) var(--ease-out);
}
/* The only motion on the card. No lift, no tilt, no glow. */
.receipt:hover img, .receipt:focus-visible img { transform: scale(1.04); }

/* The number has to clear 4.5:1 over a photograph nobody has vetted, so
   the scrim is a contrast floor, not a mood. */
.receipt::after {
  content: "";
  position: absolute;
  inset: 30% 0 0 0;
  background: linear-gradient(rgba(20, 6, 16, 0), rgba(20, 6, 16, 0.5) 52%, rgba(20, 6, 16, 0.88));
  pointer-events: none;
}

.receipt-tag {
  position: absolute;
  z-index: 2;
  top: var(--space-2);
  left: -5px;
  max-width: calc(100% - 10px);
  padding: 0.4em 0.75em;
  background: var(--oynk-red);
  color: var(--cream);
  border-radius: 0;          /* the one hard rectangle on a page of pills */
  transform: rotate(-1.5deg);
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.receipt-count {
  /* -0.06em kills Archivo Black's sidebearing so the numeral sits flush
     to the card's optical edge instead of looking indented. */
  margin-left: -0.06em;
  margin-bottom: 0.1em;
  font-family: var(--font-display);
  font-size: clamp(2.6rem, 3.2vw + 0.8rem, 3.5rem);
  line-height: 0.78;
  letter-spacing: -0.035em;
  color: var(--cream);
  font-variant-numeric: tabular-nums;
}

.receipt-foot {
  position: absolute;
  z-index: 2;
  left: var(--space-2);
  right: var(--space-2);
  bottom: var(--space-2);
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
.receipt-subject {
  font-weight: 700;
  font-size: var(--fs-sm);
  color: var(--cream);
}
.receipt-meta {
  display: flex;
  gap: 0.6rem;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: rgba(255, 244, 236, 0.62);
}

@media (prefers-reduced-motion: reduce) {
  .receipt img { transition: none; }
  .receipt:hover img, .receipt:focus-visible img { transform: none; }
}

/* ============================================================
   Marquee
   ============================================================ */
.marquee {
  overflow: hidden;
  background: var(--ink);
  color: var(--cream);
  padding-block: var(--space-2);
}
.marquee-track {
  display: flex;
  width: max-content;
  gap: var(--space-4);
  animation: marquee 42s linear infinite;
}
@media (prefers-reduced-motion: reduce) { .marquee-track { animation: none; } }
.marquee-track span {
  font-family: var(--font-display);
  font-size: var(--fs-md);
  text-transform: uppercase;
  white-space: nowrap;
  display: flex; align-items: center; gap: var(--space-4);
}
/* Separator is a drawn shape, not a glyph: a text bullet reads as list
   punctuation dropped into a headline. Rotated square, brand red. */
.marquee-track span::after {
  content: "";
  flex: none;
  width: 9px;
  height: 9px;
  background: var(--oynk-red);
  transform: rotate(45deg);
}
@keyframes marquee {
  from { transform: translateX(0); }
  to { transform: translateX(-50%); }
}

/* ============================================================
   Reveal-on-scroll
   ============================================================ */
[data-reveal] {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}
[data-reveal].is-visible { opacity: 1; transform: translateY(0); }
@media (prefers-reduced-motion: reduce) {
  [data-reveal] { opacity: 1; transform: none; transition: none; }
}

/* ============================================================
   Footer
   ============================================================ */
.footer { background: var(--ink); color: var(--cream); padding-block: var(--space-6) var(--space-4); }
.footer-top { display: grid; gap: var(--space-5); grid-template-columns: 1fr; margin-bottom: var(--space-5); }
@media (min-width: 768px) { .footer-top { grid-template-columns: 1.3fr 1fr 1fr 1fr; } }
.footer h4 { color: var(--cream); margin-bottom: var(--space-2); text-transform: uppercase; font-size: var(--fs-xs); letter-spacing: 0.08em; opacity: 0.6; }
.footer-links { display: flex; flex-direction: column; gap: 0.6rem; font-weight: 600; }
.footer-links a:hover { color: var(--cobalt); }
.footer-bottom {
  border-top: 1px solid var(--color-border-dark);
  padding-top: var(--space-3);
  display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--space-2);
  font-size: var(--fs-xs);
  color: rgba(255,244,236,0.6);
}
.social-row { display: flex; gap: var(--space-2); }
.social-row a { width: 40px; height: 40px; border-radius: 50%; border: 1px solid var(--color-border-dark); display: flex; align-items: center; justify-content: center; transition: background var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out); }
.social-row a:hover { background: var(--oynk-red); border-color: var(--oynk-red); transform: translateY(-2px); }

/* ============================================================
   Misc
   ============================================================ */
.skip-link {
  position: absolute; left: -9999px; top: 0; z-index: 999;
  background: var(--oynk-red); color: var(--cream); padding: 1em 1.5em; border-radius: 0 0 var(--radius-sm) 0; font-weight: 700;
}
.skip-link:focus { left: 0; }
.price-card {
  border: 2px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  background: var(--cream);
  transition: transform var(--dur-base) var(--ease-out), box-shadow var(--dur-base) var(--ease-out), border-color var(--dur-base) var(--ease-out);
}
.price-card:hover { transform: translateY(-4px); box-shadow: var(--shadow-lift); }
.price-card.featured { background: var(--ink); color: var(--cream); border-color: var(--ink); }
.price-card.featured .price-features li { color: rgba(255,244,236,0.85); }
.price-features { display: flex; flex-direction: column; gap: 0.7rem; }
.price-features li { display: flex; gap: 0.6rem; align-items: flex-start; color: var(--color-text-muted); font-size: var(--fs-sm); }
.price-features li .icon { color: var(--oynk-red); flex: none; margin-top: 0.15em; }
.featured .price-features li .icon { color: var(--cobalt); }

.badge-featured {
  align-self: flex-start;
  background: var(--oynk-red);
  color: var(--cream);
  font-size: var(--fs-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 0.3em 0.8em;
  border-radius: var(--radius-pill);
}

.step {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}
.step-num {
  font-family: var(--font-display);
  font-size: var(--fs-4xl);
  color: var(--cobalt);
}
.section-alt .step-num,
.section-red .step-num {
  color: transparent;
  -webkit-text-stroke: 2px var(--ink);
}

/* ============================================================
   Phone mockup carousel
   ============================================================ */
.phone-carousel {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: 480px;
  margin-inline: auto;
  max-width: 560px;
  touch-action: pan-y;
}
.phone-carousel-track {
  position: relative;
  width: 100%;
  height: 100%;
  perspective: 1200px;
}
.phone-slide {
  position: absolute;
  top: 50%;
  left: 50%;
  width: 200px;
  will-change: transform, opacity;
  transition: transform var(--dur-slow) var(--ease-out), opacity var(--dur-slow) var(--ease-out);
}
.phone-frame {
  position: relative;
  aspect-ratio: 9 / 16;
  background: var(--ink);
  border-radius: 28px;
  padding: 8px;
  box-shadow: var(--shadow-lift);
}
.phone-frame::before {
  content: "";
  position: absolute;
  top: 14px;
  left: 50%;
  transform: translateX(-50%);
  width: 46px;
  height: 14px;
  border-radius: var(--radius-pill);
  background: #000;
  z-index: 2;
}
.phone-screen {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: 20px;
  background: var(--cobalt-wash);
}
.phone-carousel-arrow {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 5;
  width: 44px;
  height: 44px;
  border-radius: 50%;
  background: var(--cream);
  color: var(--ink);
  display: flex;
  align-items: center;
  justify-content: center;
  box-shadow: var(--shadow-soft);
  transition: background var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.phone-carousel-arrow:hover { background: var(--oynk-red); color: var(--cream); transform: translateY(-50%) scale(1.06); }
.phone-carousel-arrow.prev { left: -4px; }
.phone-carousel-arrow.next { right: -4px; }
@media (min-width: 640px) {
  .phone-carousel-arrow.prev { left: -22px; }
  .phone-carousel-arrow.next { right: -22px; }
}
.phone-carousel-dots {
  position: absolute;
  bottom: -8px;
  left: 50%;
  transform: translateX(-50%);
  display: flex;
  gap: 0.5rem;
  z-index: 5;
}
.phone-carousel-dots .dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--color-border);
  transition: background var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.phone-carousel-dots .dot[aria-current="true"] {
  background: var(--oynk-red);
  transform: scale(1.3);
}
@media (prefers-reduced-motion: reduce) {
  .phone-slide { transition: none; }
}

/* ============================================================
   Motion layer: hero depth, parallax, cursor, micro-interactions
   ============================================================ */

/* ---- Scroll progress bar ---- */
.scroll-progress {
  position: fixed;
  inset: 0 0 auto 0;
  height: 3px;
  z-index: 300;
  background: linear-gradient(90deg, var(--oynk-red), var(--cobalt));
  transform: scaleX(0);
  transform-origin: 0 50%;
  pointer-events: none;
}

/* ---- Nav condense on scroll ---- */
.nav-inner { transition: padding-block var(--dur-base) var(--ease-out); }
.nav { transition: box-shadow var(--dur-base) var(--ease-out); }
.nav.is-scrolled { box-shadow: 0 6px 24px rgba(42, 16, 40, 0.09); }
.nav.is-scrolled .nav-inner { padding-block: 0.6rem; }

/* ---- Hero layout ---- */
.hero-grid { display: grid; gap: var(--space-5); align-items: center; }
@media (min-width: 900px) {
  .hero-grid { grid-template-columns: 1.05fr 0.95fr; gap: var(--space-4); }
}
.hero-main { position: relative; z-index: 2; }

/* ============================================================
   Cut hero (index)
   One hard-edged red field with a straight diagonal edge, replacing the
   four ambient blur-and-opacity effects that used to sit here. The cut
   passes behind phone 1 and in front of phones 2 and 3, so it reads as a
   plane through the composition instead of a shape on the backdrop.
   ============================================================ */
.hero-cut { padding-bottom: var(--space-7); }
.hero-cut-field {
  position: absolute;
  inset: 0;
  z-index: 2;
  background: var(--red-deep);
  /* Deliberately stops short of the headline. Ink on --red-deep is 3.06:1,
     which scrapes large-text AA and fails everything else, and the fix the
     audit wanted (a cream copy of the h1 clipped to the same polygon) needs
     two headlines kept pixel-identical through a JS word-split. Not a trade
     worth making on a site edited by hand. So the type stays ink on cream
     and the field owns the lower band, where the only things crossing it
     are the phone cluster and the stat cards, both of which carry their
     own surface.

     The field is a right-side plane rather than a band across the whole
     hero, and that is a measurement, not a preference: the copy column
     stacks headline, lede, buttons and stat cards with 30-50px between
     them, so any diagonal crossing it lands on one of them at some
     viewport. Keeping the left edge right of the copy column at every
     width means the type never has to change colour to survive. */
  clip-path: polygon(64% 0, 100% 0, 100% 100%, 56% 100%);
}

/* .wrap and .hero-phones both carry z-index, which would trap the phones in
   their own stacking contexts and leave them unable to clear the field.
   Opting them out lets the whole hero stack in one context.

   All three phones sit ABOVE .hero-cut-field (z-index 2). The field used to
   slice between them, with phone 1 behind it for depth, but that read as the
   left phone being clipped by the diagonal rather than layered under it. The
   three values stay adjacent so the phones keep their own overlap order:
   1 behind 3 behind 2. .hero-main clears all of them so the headline still
   wins where the copy column and the cluster meet at narrow widths. */
.hero-cut .wrap,
.hero-cut .hero-phones { z-index: auto; }
.hero-cut .hero-phone-1 { z-index: 3; }
.hero-cut .hero-phone-3 { z-index: 4; }
.hero-cut .hero-phone-2 { z-index: 5; }
.hero-cut .hero-main    { z-index: 6; }

/* The thesis of the page, set as type. Every word in this headline used to
   be the same size, which is what made it read as generated. */
.hero-cut h1 .hero-payload {
  font-style: normal;
  font-size: 1.26em;
  letter-spacing: -0.035em;
}

/* These cards sit on the red now. At 72% white over --red-deep the card
   goes pink and the red numeral inside it drops to 3.15:1; opaque cream
   puts it back to the 4.4:1 it has everywhere else on the site. */
.hero-cut .hero-proof-item {
  background: var(--cream);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}

/* Below 900px the hero stacks into a tall column and the diagonal would
   land somewhere arbitrary in the middle of the copy. Flatten it to a band
   under the stat cards instead. */
@media (max-width: 899px) {
  /* Stacked layout: the phones are mid-column, so the plane lies down
     into a band under the stat cards. */
  .hero-cut-field { clip-path: polygon(0 calc(100% - 150px), 100% calc(100% - 210px), 100% 100%, 0 100%); }
  .hero-cut h1 .hero-payload { font-size: 1.18em; }
}

/* ---- Headline word stagger ---- */
.hero h1 .word {
  display: inline-block;
  opacity: 0;
  transform: translate3d(0, 0.5em, 0) rotate(3deg);
  transition: opacity 520ms var(--ease-out), transform 620ms var(--ease-out);
}
.hero h1.is-in .word { opacity: 1; transform: none; }

/* ---- Floating phone cluster ---- */
.hero-phones {
  position: relative;
  height: 300px;
  display: block;
  margin-top: var(--space-3);
  z-index: 1;
}
@media (min-width: 900px)  { .hero-phones { height: 440px; margin-top: 0; } }
@media (min-width: 1200px) { .hero-phones { height: 500px; } }

/* .hero-phone carries JS scroll-parallax; .phone-float carries the idle CSS
   animation. Separate elements so the two never fight over `transform`. */
.hero-phone {
  position: absolute;
  width: 118px;
  will-change: transform;
}
@media (min-width: 900px) { .hero-phone { width: 168px; } }
.hero-phone .phone-float {
  animation: phone-float 7s ease-in-out infinite alternate;
}
@media (min-width: 1200px) { .hero-phone { width: 190px; } }
.hero-phone-1 { top: 4%;  left: 0;    z-index: 1; }
.hero-phone-2 { top: 16%; left: 33%;  z-index: 3; }
.hero-phone-3 { top: 2%;  right: -2%; z-index: 2; }
.hero-phone-1 .phone-float { animation-delay: -1.2s; }
.hero-phone-3 .phone-float { animation-delay: -3.4s; }
.hero-phone-2 .phone-frame { box-shadow: 0 26px 60px rgba(42, 16, 40, 0.28); }

@keyframes phone-float {
  from { transform: translate3d(0, 0, 0) rotate(-2deg); }
  to   { transform: translate3d(0, -18px, 0) rotate(2deg); }
}

/* Crossfading screen (reads as playing video) */
.phone-cycle { position: relative; width: 100%; height: 100%; border-radius: 20px; overflow: hidden; }
.phone-cycle .phone-screen {
  position: absolute;
  inset: 0;
  border-radius: 20px;
  transition: opacity 700ms var(--ease-out);
}
.phone-cycle .phone-screen.is-out { opacity: 0; }

/* Live dot so the cluster reads as active content */
.hero-phone-2 .phone-frame::after {
  content: "";
  position: absolute;
  right: 16px;
  bottom: 16px;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--lime);
  box-shadow: 0 0 0 0 rgba(216, 255, 62, 0.7);
  animation: live-pulse 2s ease-out infinite;
  z-index: 3;
}
@keyframes live-pulse {
  0%   { box-shadow: 0 0 0 0 rgba(216, 255, 62, 0.6); }
  70%  { box-shadow: 0 0 0 12px rgba(216, 255, 62, 0); }
  100% { box-shadow: 0 0 0 0 rgba(216, 255, 62, 0); }
}

/* ---- Custom cursor ----
   Both layers blend with `difference`, so brand red inverts against whatever
   it sits on instead of vanishing. Red (219,50,43) against each section fill:
     cream    #FFF4EC -> ( 36,194,193) teal
     cobalt   #EDEEFF -> ( 18,188,212) cyan
     ink      #2A1028 -> (177, 34,  3) orange-red
     red-deep #D02D26 -> ( 11,  5,  5) near-black  <- the case that was invisible
   The ring border is opaque now: at 45% alpha the blended result washed back
   toward the backdrop and lost the contrast the blend just bought. */
.cursor-dot, .cursor-ring {
  position: fixed;
  left: 0; top: 0;
  z-index: 9999;
  border-radius: 50%;
  pointer-events: none;
  will-change: transform;
  opacity: 0;
  mix-blend-mode: difference;
  transition: opacity var(--dur-base) linear;
}
.cursor-dot {
  width: 7px; height: 7px;
  margin: -3.5px 0 0 -3.5px;
  background: var(--oynk-red);
}
.cursor-ring {
  width: 36px; height: 36px;
  margin: -18px 0 0 -18px;
  border: 2px solid var(--oynk-red);
  transition: opacity var(--dur-base) linear,
              background-color var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.cursor-dot.is-live, .cursor-ring.is-live { opacity: 1; }
.cursor-ring.is-active {
  background: rgba(219, 50, 43, 0.12);
  border-color: var(--oynk-red);
}
/* Only hide the native cursor once JS has actually drawn a replacement */
html.has-cursor, html.has-cursor a, html.has-cursor button { cursor: none; }
html.has-cursor input, html.has-cursor textarea, html.has-cursor select { cursor: auto; }

/* ---- Magnetic + tilt ---- */
[data-magnetic] { will-change: transform; }

/* ---- Motion opt-out: everything above resets to static ---- */
@media (prefers-reduced-motion: reduce) {
  .hero-phone .phone-float,
  .hero-phone-2 .phone-frame::after { animation: none; }
  .hero h1 .word { opacity: 1; transform: none; transition: none; }
  .phone-cycle .phone-screen { transition: none; }
  .cursor-dot, .cursor-ring { display: none; }
  .scroll-progress { display: none; }
}

/* ============================================================
   Reel strip: full-bleed auto-scrolling wall of real content
   ============================================================ */
.reel-strip {
  overflow: hidden;
  padding-block: var(--space-2);
  -webkit-mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
          mask-image: linear-gradient(90deg, transparent, #000 6%, #000 94%, transparent);
}
.reel-strip-track {
  display: flex;
  gap: var(--space-2);
  width: max-content;
  animation: marquee 46s linear infinite;
}
.reel-strip:hover .reel-strip-track { animation-play-state: paused; }
.reel-strip-item {
  flex: none;
  width: 132px;
  aspect-ratio: 9 / 16;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--cobalt-wash);
  box-shadow: var(--shadow-soft);
  transition: transform var(--dur-base) var(--ease-out);
}
.reel-strip-item:hover { transform: translateY(-6px) scale(1.03); }
.reel-strip-item img { width: 100%; height: 100%; object-fit: cover; }
@media (min-width: 768px)  { .reel-strip-item { width: 176px; } }
@media (min-width: 1200px) { .reel-strip-item { width: 200px; } }

@media (prefers-reduced-motion: reduce) {
  .reel-strip-track { animation: none; }
  .reel-strip { overflow-x: auto; }
}

/* ============================================================
   Mobile hero order: lead with the headline, then the work,
   then the CTAs. Images sit high instead of buried under stats.
   ============================================================ */
@media (max-width: 899px) {
  .hero { padding-block: var(--space-4) var(--space-5); }
  .hero-grid { display: flex; flex-direction: column; gap: var(--space-3); }
  /* display:contents dissolves the wrapper so its children can be ordered
     alongside .hero-phones in the same flex context. */
  .hero-main { display: contents; }
  .hero .eyebrow { order: 1; }
  .hero-copy     { order: 2; }
  .hero-phones   { order: 3; margin-top: var(--space-1); }
  .hero-actions  { order: 4; margin-top: 0; }
  .hero-proof    { order: 5; margin-top: var(--space-1); gap: var(--space-3); }

  .hero .eyebrow, .hero-copy, .hero-actions, .hero-proof {
    position: relative;
    z-index: 2;
  }
  .hero-proof-item strong { font-size: var(--fs-xl); }
}

/* Mobile: the absolute-positioned desktop fan overlaps badly at narrow widths,
   so lay the three phones out as an even row instead. Tilt goes on
   .phone-frame because .hero-phone (parallax) and .phone-float (idle drift)
   are both already driving `transform`. */
@media (max-width: 899px) {
  .hero-phones {
    display: flex;
    justify-content: center;
    align-items: flex-start;
    gap: var(--space-2);
    height: auto;
  }
  .hero-phone {
    position: static;
    flex: 1 1 0;
    min-width: 0;
    max-width: 108px;
  }
  .hero-phone-2 { margin-top: var(--space-3); }
  .hero-phone-3 { margin-top: var(--space-1); }
  .hero-phone-1 .phone-frame { transform: rotate(-4deg); }
  .hero-phone-2 .phone-frame { transform: rotate(2deg);  }
  .hero-phone-3 .phone-frame { transform: rotate(5deg);  }
  .hero-phone .phone-frame { border-radius: 20px; padding: 5px; }
  .hero-phone .phone-frame::before { width: 30px; height: 9px; top: 9px; }
  .hero-phone .phone-screen, .hero-phone .phone-cycle { border-radius: 15px; }
}

/* ============================================================
   Hero stat cards: give the metrics a surface so they carry
   weight instead of floating loose under the headline.
   ============================================================ */
.hero-proof {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-2);
  margin-top: var(--space-4);
}
.hero-proof-item {
  background: rgba(255, 255, 255, 0.72);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-2) calc(var(--space-2) - 2px);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  transition: transform var(--dur-base) var(--ease-out),
              box-shadow var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
}
.hero-proof-item:hover {
  transform: translateY(-3px);
  box-shadow: var(--shadow-soft);
  border-color: rgba(219, 50, 43, 0.35);
}
.hero-proof-item strong {
  display: block;
  font-size: clamp(1.5rem, 5.2vw, 2.5rem);
  line-height: 1;
  color: var(--oynk-red);
  font-variant-numeric: tabular-nums;
}
.hero-proof-item span {
  display: block;
  margin-top: 0.45rem;
  font-size: 0.6875rem;
  letter-spacing: 0.07em;
}

/* ============================================================
   Stats chart: real per-reel view counts, bars grow on scroll
   ============================================================ */
.chart { margin-top: var(--space-5); }
.chart-plot {
  display: flex;
  align-items: flex-end;
  gap: 4px;
  height: 190px;
}
@media (min-width: 640px) { .chart-plot { gap: 10px; height: 230px; } }

.chart-col {
  flex: 1 1 0;
  min-width: 0;
  height: 100%;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 0.4rem;
  border-radius: 10px 10px 0 0;
}
.chart-val {
  font-family: var(--font-display);
  font-size: 0.625rem;
  text-align: center;
  color: var(--cream);
  font-variant-numeric: tabular-nums;
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-out) calc(var(--delay, 0ms) + 260ms);
}
@media (min-width: 640px) { .chart-val { font-size: 0.8125rem; } }
.chart.is-in .chart-val { opacity: 1; }

/* The track is a full-height rounded column; the fill rises inside it via
   transform. Avoids percentage-height-in-flex resolution issues, keeps the
   animation on the compositor, and the empty track doubles as a max reference. */
.chart-bar {
  flex: 1 1 auto;
  display: flex;
  align-items: stretch;
  border-radius: 8px;
  overflow: hidden;
  background: rgba(255, 244, 236, 0.09);
}
.chart-fill {
  width: 100%;
  background: var(--cobalt);
  transform: scaleY(0);
  transform-origin: 50% 100%;
  transition: transform 850ms var(--ease-out) var(--delay, 0ms),
              background var(--dur-base) var(--ease-out);
}
.chart.is-in .chart-fill { transform: scaleY(var(--pct, 1)); }

.chart-col:hover .chart-fill,
.chart-col:focus-visible .chart-fill {
  background: var(--oynk-red);
}
.chart-x {
  font-size: 0.625rem;
  text-align: center;
  color: rgba(255, 244, 236, 0.6);
  letter-spacing: 0.02em;
  display: none;
}
@media (min-width: 640px) { .chart-x { display: block; } }
.chart-col:hover .chart-x, .chart-col:focus-visible .chart-x { color: var(--cream); }

/* Label under a dashboard figure. Was the same 90-char inline style pasted
   four times, which is the least editable thing on the page. */
.stat-label {
  color: rgba(255, 244, 236, 0.7);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

.chart-caption {
  margin-top: var(--space-3);
  font-size: var(--fs-xs);
  color: rgba(255, 244, 236, 0.55);
  text-align: center;
  max-width: none;
}

@media (prefers-reduced-motion: reduce) {
  .chart-fill { transform: scaleY(var(--pct, 1)); transition: none; }
  .chart-val { opacity: 1; transition: none; }
}

/* Mobile: reclaim a little of the wrap padding and tighten gaps so the
   eight bars stay as wide as possible for tapping. */
@media (max-width: 639px) {
  .chart { margin-inline: -10px; }
  .chart-plot { gap: 3px; }
}

/* ============================================================
   Service cards: directional entrance + cursor spotlight
   ============================================================ */
.service-card {
  position: relative;
  overflow: hidden;
}
/* Keep real content above the spotlight layer */
.service-card > * { position: relative; z-index: 1; }

/* Three cards converge: the outer two swing in from their own side, the
   middle rises. More travel and a longer curve than the site's general
   reveal, because this is the section that has to stop the scroll. The
   overshoot in the easing is what makes them land rather than glide.
   Horizontal travel is safe: html carries overflow-x: hidden. */
.service-card[data-reveal] {
  transform: translate3d(-72px, 30px, 0) scale(0.93);
  transition: opacity 640ms var(--ease-out),
              transform 880ms cubic-bezier(0.22, 1.2, 0.36, 1);
}
.service-card[data-reveal]:nth-of-type(2) { transform: translate3d(0, 84px, 0) scale(0.93); }
.service-card[data-reveal]:nth-of-type(3) { transform: translate3d(72px, 30px, 0) scale(0.93); }
.service-card[data-reveal].is-visible { transform: none; }

/* Icon pops, arrow slides */
.service-card .card-icon {
  transition: transform var(--dur-base) var(--ease-out),
              background-color var(--dur-base) var(--ease-out);
}
.service-card:hover .card-icon {
  transform: translateY(-3px) scale(1.09) rotate(-7deg);
  background: rgba(255, 244, 236, 0.3);
}
.service-card .arrow {
  display: inline-block;
  transition: transform var(--dur-base) var(--ease-out);
}
.service-card:hover .arrow,
.service-card a:focus-visible .arrow { transform: translateX(6px); }

@media (prefers-reduced-motion: reduce) {
  .service-card[data-reveal] { transform: none; transition: none; }
  .service-card:hover .card-icon,
  .service-card:hover .arrow { transform: none; }
}

/* ============================================================
   Service card reel backdrops
   Real reels crossfade behind each card behind a brand-tinted
   scrim. Scrim density is set by contrast, not taste: white body
   copy must clear 4.5:1 even against a pure-white image pixel.
   ============================================================ */
.card-media {
  position: absolute;
  inset: 0;
  z-index: 0;              /* below .service-card > * (z-index 1) */
  overflow: hidden;
  border-radius: inherit;
}
.card-media-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Held blur keeps copy legible and hides the upscale edge. Only
     transform/opacity animate, so no per-frame filter repaint. */
  filter: blur(2px) saturate(1.06);
  transform: scale(1.06);
  transition: transform 800ms var(--ease-out), opacity 700ms var(--ease-out);
}
.card-media-img.is-out { opacity: 0; }

/* Hover reveal is the push-in, NOT a lighter scrim: thinning the
   scrim measured 3.43:1 against a white image pixel. */
.service-card:hover .card-media-img,
.service-card:focus-within .card-media-img { transform: scale(1.15); }

.card-red .card-media::after,
.card-plum .card-media::after,
.card-cobalt .card-media::after {
  content: "";
  position: absolute;
  inset: 0;
}
.card-red .card-media::after {
  background: linear-gradient(165deg, rgba(194, 42, 36, 0.90), rgba(150, 30, 25, 0.96));
}
.card-plum .card-media::after {
  background: linear-gradient(165deg, rgba(38, 14, 36, 0.90), rgba(20, 6, 16, 0.96));
}
/* Added when the third card appeared. Without it the reel showed at full
   strength behind white copy and was unreadable. Measured against a pure
   white image pixel at the gradient's lightest end: 6.40:1, which is
   better than the red card's own 4.59:1. */
.card-cobalt .card-media::after {
  background: linear-gradient(165deg, rgba(48, 54, 190, 0.90), rgba(32, 36, 140, 0.96));
}

/* Copy goes full-strength white now that it sits over photography. */
.service-card p { color: var(--cream); }

@media (prefers-reduced-motion: reduce) {
  .card-media-img { transition: none; }
  .service-card:hover .card-media-img,
  .service-card:focus-within .card-media-img { transform: scale(1.06); }
}

/* ============================================================
   Pricing: tier differentiators
   ============================================================ */
/* Not-included row: cross icon + muted tone (6.27:1 on cream),
   no strikethrough so it stays readable. */
.price-features li.is-excluded { color: var(--color-text-muted); opacity: 0.75; }
.price-features li.is-excluded .icon { color: var(--color-text-muted); }

/* The row that justifies the upgrade */
.price-features li.is-key { color: var(--cream); font-weight: 700; }
.featured .price-features li.is-key .icon { color: var(--lime); }

/* Growth value callout. Lime only as the accent mark and rule: cobalt is
   3.37:1 on ink and this is small text, lime is 15.2:1. */
.price-save {
  align-self: stretch;
  margin-top: -0.3rem;
  padding: 0.7em 0.95em;
  border-radius: var(--radius-sm);
  border-left: 3px solid var(--lime);
  background: rgba(216, 255, 62, 0.12);
  color: var(--cream);
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: 1.4;
  max-width: none;
}
.price-save-lead {
  display: block;
  font-family: var(--font-display);
  font-size: var(--fs-sm);
  line-height: 1.15;
  text-transform: uppercase;
  color: var(--lime);
  margin-bottom: 0.3em;
}
.price-save-lead:last-child { margin-bottom: 0; }

/* "From" as a small label above the figure: at three columns the display
   face pushed "From $1,699 / month" onto three ragged lines. */
.price-from {
  display: block;
  font-family: var(--font-body);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  opacity: 0.65;
  margin-bottom: 0.2em;
}
.price-amount { font-family: var(--font-display); font-size: var(--fs-3xl); line-height: 1.05; }
.price-amount small {
  font-family: var(--font-body);
  font-size: var(--fs-sm);
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  opacity: 0.7;
  white-space: nowrap;
}

/* ============================================================
   Pricing grid alignment
   The three tiers differ structurally (only Growth has a badge
   and a value callout), which staggered the price and feature
   rows. Subgrid makes all cards share one row track set, so
   every row lines up across columns regardless of content.
   ============================================================ */
.price-card { position: relative; }

/* The badge stays in flow and gets its own row; the other two tiers hold
   that row open with an empty slot. Absolute-positioning it collided with
   the "GROWTH" heading at three columns. */

/* Empty placeholder holding the callout row open on non-featured cards */
.price-slot { display: block; }

@media (min-width: 1024px) {
  @supports (grid-template-rows: subgrid) {
    /* badge | title | price | callout | features | button */
    .pricing-grid { grid-template-rows: auto auto auto auto 1fr auto; }
    .price-card {
      display: grid;
      grid-template-rows: subgrid;
      grid-row: span 6;
    }
    /* features row already absorbs slack via 1fr */
    .price-card > .btn { margin-top: 0 !important; }
  }
}

/* Grid items stretch by default; the badge should hug its label */
.price-card .badge-featured { justify-self: start; }

/* The reserved rows only matter where the cards sit side by side. Below the
   subgrid breakpoint they'd just be dead gaps in the stacked flex layout. */
@media (max-width: 1023px) {
  .price-slot { display: none; }
}

/* Group label separating the management block from the base deliverables */
.price-features li.price-group {
  display: block;
  margin-top: 0.35rem;
  padding-top: 0.8rem;
  border-top: 1px solid var(--color-border);
  font-size: var(--fs-xs);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.07em;
  color: var(--color-text-muted);
}
.featured .price-features li.price-group {
  border-top-color: var(--color-border-dark);
  color: rgba(255, 244, 236, 0.6);
}
/* Included management rows: lime tick marks the differentiator */
.featured .price-features li.is-mgmt { color: var(--cream); }
.featured .price-features li.is-mgmt .icon { color: var(--lime); }

/* Optional extra: plus icon rather than a tick, with a tag pushed to the
   row's trailing edge so it reads as available, not included. */
.price-features li.is-addon { color: rgba(255, 244, 236, 0.82); }
.featured .price-features li.is-addon .icon { color: rgba(255, 244, 236, 0.5); }
.addon-tag {
  margin-left: auto;
  flex: none;
  align-self: center;
  font-size: 0.625rem;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 0.2em 0.55em;
  border-radius: var(--radius-pill);
  border: 1px solid var(--color-border-dark);
  color: rgba(255, 244, 236, 0.72);
  white-space: nowrap;
}

/* ============================================================
   Services: numbered capability rows
   ============================================================ */
.caps-head { max-width: 44rem; margin-bottom: var(--space-5); }
.caps-head h2 { margin-top: var(--space-2); }

.cap-row {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-3);
  padding-block: var(--space-5);
  border-top: 1px dashed var(--color-border);
  align-items: start;
}
.cap-row:last-child { border-bottom: 1px dashed var(--color-border); }
@media (min-width: 900px) {
  .cap-row {
    grid-template-columns: 96px minmax(240px, 320px) 1fr;
    gap: var(--space-4);
    padding-block: var(--space-6);
  }
}

.cap-num {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  color: var(--ink);
  line-height: 1;
}
@media (min-width: 900px) { .cap-num { font-size: var(--fs-2xl); } }

.cap-media {
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--cobalt-wash);
  /* Source reels are 9:16. A 4:3 box cropped so hard it cut the title
     cards off; 4:5 keeps the on-screen text intact on narrow screens. */
  aspect-ratio: 4 / 5;
}
@media (min-width: 900px) { .cap-media { aspect-ratio: 3 / 4; } }
.cap-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* over-scaled so the scroll shift never exposes an edge */
  transform: scale(1.06);
  will-change: transform;
}

.cap-plan {
  display: inline-block;
  font-size: var(--fs-xs);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--cobalt-deep);
  background: rgba(77, 85, 255, 0.12);
  padding: 0.3em 0.8em;
  border-radius: var(--radius-pill);
  margin-bottom: var(--space-2);
}
.cap-title {
  font-size: clamp(2rem, 4.4vw, 3.25rem);
  line-height: 0.98;
}
.cap-sub {
  color: var(--color-text-muted);
  font-size: var(--fs-md);
  margin-top: 0.5rem;
  max-width: 34ch;
}
.cap-list {
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  margin-top: var(--space-4);
}
.cap-list li {
  display: flex;
  align-items: center;
  gap: 0.6rem;
  font-weight: 800;
  font-size: var(--fs-xs);
  letter-spacing: 0.05em;
  text-transform: uppercase;
}
.cap-list .icon {
  color: var(--cobalt);
  flex: none;
  transition: transform var(--dur-base) var(--ease-out);
}
.cap-list li:hover .icon { transform: translate(3px, -3px); }

@media (prefers-reduced-motion: reduce) {
  .cap-media img { transform: scale(1.06) !important; }
}

/* Below the 3-column breakpoint the capability media would stretch to the
   full wrap width (up to ~836px, i.e. 1672 device px), far past the source.
   Cap it so the 720w asset is always >= what the screen asks for. */
@media (max-width: 899px) {
  .cap-media { max-width: 336px; }
}

.btn:disabled { opacity: 0.55; cursor: not-allowed; pointer-events: none; }

/* ============================================================
   Footer brand lockup
   The source PNG carried 86% transparent padding, which shrank the
   wordmark and forced dead space around it. Now cropped to content,
   so it can be sized and spaced honestly.
   ============================================================ */
.footer-brand {
  display: inline-block;
  width: 180px;
  max-width: 60%;
  margin-bottom: var(--space-3);
  transition: opacity var(--dur-base) var(--ease-out), transform var(--dur-base) var(--ease-out);
}
.footer-brand img {
  width: 100%;
  height: auto;
  /* single-colour wordmark, so a filter beats shipping a second asset */
  filter: brightness(0) invert(1);
}
.footer-brand:hover { opacity: 0.82; transform: translateY(-2px); }

/* Oversized mark bleeding off the footer edge: ties the brand into the
   layout instead of leaving a small logo floating in space. */
.footer { position: relative; overflow: hidden; }
.footer::after {
  content: "";
  position: absolute;
  right: -4%;
  bottom: -14%;
  width: min(46vw, 560px);
  aspect-ratio: 442 / 153;
  background: url("../assets/img/oynk-logo.webp") no-repeat center / contain;
  filter: brightness(0) invert(1);
  opacity: 0.055;
  pointer-events: none;
  z-index: 0;
}
.footer .wrap { position: relative; z-index: 1; }
@media (max-width: 767px) {
  .footer::after { width: 78vw; right: -12%; bottom: -6%; opacity: 0.045; }
}

/* ============================================================
   Lead forms  --  contact.html
   Two audiences, one stage. The business panel is red, the creator
   panel cobalt, so switching feels like changing room rather than
   changing a field. Every animation is transform/opacity only.
   ============================================================ */

.form-switch {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
  max-width: 26rem;
  margin: var(--space-4) auto var(--space-4);
  padding: 4px;
  background: var(--cream);
  border: 1px solid var(--color-border);
  border-radius: 999px;
}
.form-switch-btn {
  position: relative;
  z-index: 1;
  padding: 0.7rem 1rem;
  border: 0;
  background: none;
  cursor: pointer;
  font-family: inherit;
  font-size: var(--fs-sm);
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--color-text-muted);
  border-radius: 999px;
  transition: color var(--dur-base) var(--ease-out);
}
.form-switch-btn.is-on { color: var(--cream); }
.form-switch-btn:focus-visible { outline: 2px solid var(--ink); outline-offset: 2px; }

/* The pill is the only moving part; the labels just change colour. */
.form-switch-pill {
  position: absolute;
  z-index: 0;
  top: 4px;
  left: 4px;
  width: calc(50% - 4px);
  height: calc(100% - 8px);
  border-radius: 999px;
  background: var(--oynk-red);
  transition: transform var(--dur-base) var(--ease-out),
              background-color var(--dur-base) var(--ease-out);
}
.form-switch[data-on="creator"] .form-switch-pill {
  transform: translateX(100%);
  background: var(--cobalt-deep);
}

.form-stage { position: relative; max-width: 34rem; margin-inline: auto; }

.lead-form {
  --accent: var(--oynk-red);
  display: grid;
  gap: var(--space-3);
}
.lead-form[data-form-panel="creator"] { --accent: var(--cobalt-deep); }
.lead-form[hidden] { display: none; }

/* Panels arrive from below rather than simply appearing. */
.lead-form.is-entering { animation: leadIn var(--dur-base) var(--ease-out) both; }
@keyframes leadIn {
  from { opacity: 0; transform: translate3d(0, 10px, 0); }
  to   { opacity: 1; transform: none; }
}

/* ---- fields ---- */
.field { position: relative; }
.field input {
  width: 100%;
  padding: 1.45rem 0.9rem 0.55rem;
  font: inherit;
  color: var(--ink);
  background: var(--cream);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm, 10px);
  transition: border-color var(--dur-base) var(--ease-out);
}
.field input:focus { outline: none; border-color: var(--accent); }
.field label {
  position: absolute;
  left: 0.95rem;
  top: 1rem;
  font-size: var(--fs-sm);
  color: var(--color-text-muted);
  pointer-events: none;
  transform-origin: left top;
  transition: transform var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out);
}
/* Floats when focused or filled. :placeholder-shown is why every input
   carries placeholder=" " -- without it the label never lifts. */
.field input:focus + label,
.field input:not(:placeholder-shown) + label {
  transform: translateY(-0.72rem) scale(0.78);
  color: var(--accent);
}
.field-opt { font-weight: 400; opacity: 0.7; }

/* Underline that draws in from the left on focus. */
.field-line {
  position: absolute;
  left: 0.9rem;
  right: 0.9rem;
  bottom: 0;
  height: 2px;
  background: var(--accent);
  border-radius: 2px;
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform var(--dur-base) var(--ease-out);
}
.field input:focus ~ .field-line { transform: scaleX(1); }

.field-prefixed input { padding-left: 1.9rem; }
.field-prefixed .field-prefix {
  position: absolute;
  left: 0.95rem;
  top: 1.42rem;
  font-weight: 700;
  color: var(--accent);
  opacity: 0;
  transition: opacity var(--dur-base) var(--ease-out);
}
.field-prefixed input:focus ~ .field-prefix,
.field-prefixed input:not(:placeholder-shown) ~ .field-prefix { opacity: 1; }

.field input[aria-invalid="true"] { border-color: var(--oynk-red); }
.field-error {
  margin-top: 0.3rem;
  font-size: var(--fs-xs);
  color: var(--red-deep);
}

/* Never rendered, never seen. A bot fills it, a person cannot. */
.lead-honey {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

/* ---- submit ---- */
.lead-submit {
  position: relative;
  justify-self: start;
  background: var(--accent);
  border-color: var(--accent);
  min-width: 12rem;
  justify-content: center;
}
.lead-submit .lead-submit-arrow { transition: transform var(--dur-base) var(--ease-out); }
.lead-submit:hover .lead-submit-arrow { transform: translate3d(3px, -3px, 0); }

.lead-spinner {
  position: absolute;
  width: 1.05rem;
  height: 1.05rem;
  border: 2px solid rgba(255, 244, 236, 0.35);
  border-top-color: var(--cream);
  border-radius: 50%;
  opacity: 0;
  animation: leadSpin 0.7s linear infinite;
}
@keyframes leadSpin { to { transform: rotate(360deg); } }

.lead-form.is-sending .lead-submit { pointer-events: none; }
.lead-form.is-sending .lead-submit-text,
.lead-form.is-sending .lead-submit-arrow { opacity: 0; }
.lead-form.is-sending .lead-spinner { opacity: 1; }

.lead-status { margin: 0; font-size: var(--fs-sm); color: var(--red-deep); min-height: 1.2em; }

/* ---- success ---- */
.lead-done {
  text-align: center;
  padding: var(--space-5) var(--space-3);
  animation: leadIn var(--dur-base) var(--ease-out) both;
}
.lead-done[hidden] { display: none; }
.lead-tick { display: inline-block; color: var(--accent-done, var(--oynk-red)); }
.lead-tick svg { overflow: visible; }
.lead-tick-ring, .lead-tick-mark {
  fill: none;
  stroke: currentColor;
  stroke-width: 3;
  stroke-linecap: round;
  stroke-linejoin: round;
}
/* Drawn rather than faded in: the ring closes, then the tick lands. */
.lead-tick-ring { stroke-dasharray: 151; stroke-dashoffset: 151; animation: leadDraw 0.5s var(--ease-out) 0.05s forwards; }
.lead-tick-mark { stroke-dasharray: 40; stroke-dashoffset: 40; animation: leadDraw 0.3s var(--ease-out) 0.45s forwards; }
@keyframes leadDraw { to { stroke-dashoffset: 0; } }

.lead-done h3 { margin-top: var(--space-2); }
.lead-done p { color: var(--color-text-muted); }

@media (prefers-reduced-motion: reduce) {
  .form-switch-pill { transition: background-color 1ms linear; }
  .lead-form.is-entering { animation: none; }
  .lead-done { animation: none; }
  .field label, .field-line, .field-prefix,
  .lead-submit .lead-submit-arrow { transition: none; }
  .field input:focus ~ .field-line { transform: scaleX(1); }
  .lead-spinner { animation: none; }
  .lead-tick-ring, .lead-tick-mark { animation: none; stroke-dashoffset: 0; }
}

/* ============================================================
   Process flow  --  the four steps
   Four columns read as four unrelated tiles, and a bare zigzag on the
   cream section had nothing to sit on. Each step is now a box: the
   section background is --cream and so is .card, so these take the
   cobalt wash instead, which also ties them to the cobalt numerals.
   ============================================================ */
.steps-flow {
  display: grid;
  gap: var(--space-2);
  max-width: 46rem;
  margin-inline: auto;
}
.step-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  background: var(--cobalt-wash);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  text-align: left;
}
.step-row .step-num {
  flex: none;
  line-height: 1;
  min-width: 3.2ch;
}
.step-body h4 { margin: 0; }
.step-body p { margin-top: 0.3rem; }

/* Boxes alternate the side they arrive from, which keeps the sequence
   feeling like a sequence without mirroring the text inside them. */
.step-row[data-reveal] {
  transform: translate3d(-110px, 0, 0);
  transition: opacity 560ms var(--ease-out),
              transform 780ms cubic-bezier(0.22, 1.15, 0.36, 1),
              box-shadow var(--dur-base) var(--ease-out);
}
.step-row[data-reveal]:nth-child(even) { transform: translate3d(110px, 0, 0); }
.step-row[data-reveal].is-visible { transform: none; }

.step-row:hover { box-shadow: var(--shadow-soft); }

@media (prefers-reduced-motion: reduce) {
  .step-row[data-reveal],
  .step-row[data-reveal]:nth-child(even) { transform: none; transition: none; }
}
