/* ── Tokens (see DESIGN.md) ─────────────────────────────────────────── */
:root {
  --bg: oklch(0.99 0 0);
  --surface: oklch(0.965 0.004 230);
  --surface-2: oklch(0.93 0.006 230);
  --ink: oklch(0.18 0.02 230);
  --muted: oklch(0.46 0.015 230);

  --teal: oklch(0.72 0.12 200);
  --teal-glow: oklch(0.72 0.12 200 / 0.35);
  --teal-ink: oklch(0.46 0.11 200);

  --navy: oklch(0.20 0.035 230);
  --navy-2: oklch(0.27 0.04 230);

  --yellow: oklch(0.875 0.179 92.7); /* #ffd100 */

  /* pale tinted surfaces — used as full-bleed section backgrounds to break
     up long runs of white, without introducing new saturated UI colors */
  --tint-teal: oklch(0.96 0.014 200);

  /* pink accent — used sparingly (tag pills, hover indicators), not as a
     fill. Same hue family as --pop-pink but lighter/more saturated so it
     reads as a clean, vivid hot pink rather than a muddy magenta. */
  --pink: oklch(0.65 0.24 355);
  --pink-ink: oklch(0.46 0.20 355);
  --pink-tint: oklch(0.95 0.035 355);

  /* decorative-only "pop" confetti hues — pulled from the showreel's own
     motion-graphics palette. Never used for text, buttons, or fills with
     text on top; squiggles + dots only. */
  --pop-red: oklch(0.6 0.21 28);
  --pop-blue: oklch(0.55 0.15 250);
  --pop-green: oklch(0.66 0.16 150);
  --pop-pink: oklch(0.6 0.22 350);

  --radius-sm: 6px;
  --radius-md: 10px;
  --radius-lg: 20px;

  --space-xs: 0.5rem;
  --space-sm: 1rem;
  --space-md: 2rem;
  --space-lg: 4rem;
  --space-xl: 8rem;

  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
  --ease-back: cubic-bezier(0.34, 1.56, 0.64, 1);
  --font-display: 'Inter', system-ui, sans-serif;
  --font-body: 'Inter', system-ui, sans-serif;
}

/* ── Reset ───────────────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }

html { scroll-behavior: smooth; }

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

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

img { max-width: 100%; display: block; }

a { color: inherit; text-decoration: none; }

ul { list-style: none; }

h1, h2, h3, h4 { font-family: var(--font-display); text-wrap: balance; }

p { text-wrap: pretty; }

:focus-visible {
  outline: 2px solid var(--teal-ink);
  outline-offset: 3px;
  border-radius: var(--radius-sm);
}

.skip-link {
  position: absolute;
  left: -9999px;
  top: auto;
}
.skip-link:focus {
  left: var(--space-sm);
  top: var(--space-sm);
  z-index: 100;
  background: var(--navy);
  color: #fff;
  padding: var(--space-xs) var(--space-sm);
  border-radius: var(--radius-sm);
}

/* ── Nav ─────────────────────────────────────────────────────────────── */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-sm) clamp(var(--space-sm), 5vw, var(--space-lg));
  color: #fff;
  transition: background-color 0.4s var(--ease-out-expo), backdrop-filter 0.4s var(--ease-out-expo), color 0.4s var(--ease-out-expo), border-color 0.4s var(--ease-out-expo);
  border-bottom: 1px solid transparent;
}

.nav.is-scrolled {
  background: oklch(0.99 0 0 / 0.85);
  backdrop-filter: blur(12px);
  color: var(--ink);
  border-color: oklch(0 0 0 / 0.06);
}

.nav-mark {
  font-weight: 800;
  font-size: clamp(0.95rem, 2.4vw, 1.1rem);
  letter-spacing: -0.01em;
  text-transform: lowercase;
  white-space: nowrap;
}
.nav-mark.cursor-hidden, .nav-mark.cursor-hidden * { cursor: none; }

/* Custom emoji "cursor" shown over the logo — see main.js. Fixed +
   tracked via mousemove since CSS can't restyle the native cursor. */
.cursor-emoji {
  position: fixed;
  top: 0;
  left: 0;
  font-size: 2.8rem;
  line-height: 1;
  pointer-events: none;
  z-index: 1000;
  transform: translate(-50%, -50%) scale(0);
  transition: transform 0.25s var(--ease-back);
}
.cursor-emoji.is-active { transform: translate(-50%, -50%) scale(1); }
.nav-mark .nav-dot {
  display: inline-block;
  color: #fff;
  transition: transform 0.35s var(--ease-back), color 0.3s var(--ease-out-expo);
}
.nav-mark:hover .nav-dot { color: var(--yellow); transform: translateY(-3px) scale(1.2); }
/* Yellow is hard to read on the scrolled nav's white background --
   use the brand teal there instead. */
.nav.is-scrolled .nav-mark:hover .nav-dot { color: var(--teal); }

/* ── Letter-by-letter wave (nav links, logo, headings) — see main.js
   splitLetters(). Each .letter gets a staggered transition-delay from
   its --i custom property, alternating tilt direction so the effect
   reads as a little wave. Nav links & the logo trigger on hover; section
   titles instead trigger off .wave-active, set by main.js on whichever
   section is dominating the scroll view, so the wave reads as "this
   section is active" as you scroll rather than requiring a mouseover. */
.letters { display: inline-block; }
.letter {
  display: inline-block;
  transition: transform 0.4s var(--ease-back), color 0.3s var(--ease-out-expo);
  transition-delay: calc(var(--i, 0) * 25ms);
}
.nav-links a:hover .letter,
.nav-mark:hover .letter,
main section.wave-active h2 .letter {
  transform: translateY(-0.12em);
}
main section.wave-active h2 .letter {
  color: var(--pop-blue);
}

/* Contact's teal background needs a dark letter color to read -- black
   instead of the shared blue. */
.contact.wave-active h2 .letter {
  color: var(--ink);
}

.nav-links {
  display: flex;
  gap: clamp(var(--space-xs), 3vw, var(--space-md));
  font-size: clamp(0.78rem, 1.6vw, 0.85rem);
  font-weight: 600;
}

.nav-links a {
  position: relative;
  color: currentColor;
  opacity: 0.75;
  transition: opacity 0.25s var(--ease-out-expo), color 0.25s var(--ease-out-expo);
}
.nav-links a:hover,
.nav-links a:focus-visible { opacity: 1; color: var(--yellow); }
.nav.is-scrolled .nav-links a:hover,
.nav.is-scrolled .nav-links a:focus-visible { color: var(--teal); }
.nav-links a::after {
  content: '';
  position: absolute;
  left: 0; bottom: -6px;
  width: 0; height: 2px;
  background: currentColor;
  transition: width 0.3s var(--ease-out-expo);
}
.nav-links a:hover::after,
.nav-links a:focus-visible::after { width: 100%; }

/* ── Buttons ─────────────────────────────────────────────────────────── */
.btn {
  display: inline-flex;
  align-items: center;
  gap: 0.5em;
  padding: 0.85em 1.6em;
  border-radius: var(--radius-lg);
  border: none;
  font-family: inherit;
  font-weight: 700;
  font-size: 0.95rem;
  cursor: pointer;
  transition: transform 0.3s var(--ease-out-expo), box-shadow 0.3s var(--ease-out-expo), background-color 0.3s var(--ease-out-expo);
}
.btn:hover { transform: translateY(-2px); }

.btn-primary {
  background: var(--navy);
  color: #fff;
}
.btn-primary:hover { box-shadow: 0 8px 24px -6px oklch(0 0 0 / 0.35); }

.btn-ghost {
  border: 1.5px solid oklch(1 0 0 / 0.5);
  color: #fff;
}
.btn-ghost:hover { border-color: #fff; background: oklch(1 0 0 / 0.08); }

.btn-accent {
  background: var(--yellow);
  color: var(--navy);
  font-size: 1.1rem;
  margin: var(--space-md) 0;
  box-shadow: 0 6px 16px -6px oklch(0.5 0.12 90 / 0.4);
}
.btn-accent:hover {
  box-shadow: 0 10px 24px -6px oklch(0.4 0.1 90 / 0.4);
}

/* ── Hero ────────────────────────────────────────────────────────────── */
.hero {
  position: relative;
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: clamp(6rem, 12vh, 8rem) clamp(var(--space-sm), 6vw, var(--space-xl)) var(--space-lg);
  overflow: hidden;
  background: linear-gradient(338deg, oklch(0.59 0.12 200), var(--teal) 70%);
  color: #fff;
}

.hero-glow {
  position: absolute;
  top: -25%;
  right: -15%;
  width: 55vw;
  height: 55vw;
  max-width: 640px;
  max-height: 640px;
  background: radial-gradient(circle, oklch(1 0 0 / 0.16), transparent 70%);
  filter: blur(20px);
  pointer-events: none;
}

.hero-kicker {
  color: oklch(1 0 0 / 0.85);
  font-weight: 700;
  font-size: 0.85rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  margin-bottom: var(--space-sm);
  opacity: 0;
  animation: rise 0.7s var(--ease-out-expo) 0.1s forwards;
}

/* Full width of the hero's padded content box at every viewport size --
   tracking the container via % rather than vw means it scales
   continuously with the available space instead of jumping at a
   breakpoint, and stays full-width on small screens too. Capped so it
   doesn't get absurd on ultrawide monitors. */
.hero-name-wrap {
  position: relative;
  width: min(100%, 1200px);
  aspect-ratio: 1009 / 484;
  opacity: 0;
  animation: rise 0.8s var(--ease-out-expo) 0.2s forwards;
}

.hero-name {
  font-size: clamp(3.2rem, 11vw, 7.2rem);
  font-weight: 900;
  line-height: 0.96;
  letter-spacing: -0.03em;
  text-transform: lowercase;
}

.hero-name-wrap .hero-name {
  position: absolute;
  inset: 0;
}

.hero-name .dot-accent { color: var(--yellow); }

.hero-name-wrap.cursor-hidden, .hero-name-wrap.cursor-hidden * { cursor: none; }

/* Letter-shaped cutout window onto a mouse-tracking emoji — see main.js.
   A plain overlay carrying the emoji as a background-image, masked to
   assets/lottie/dansondigital-mask.svg -- letterform paths extracted
   directly from the Lottie JSON, so they align pixel-for-pixel with
   the actual animated logo (the HTML fallback text is a different
   font and doesn't line up closely enough for a text-clip trick). */
.hero-name-mask {
  position: absolute;
  inset: 0;
  pointer-events: none;
  mask-image: url('../assets/lottie/dansondigital-mask.svg');
  mask-size: 100% 100%;
  mask-repeat: no-repeat;
  -webkit-mask-image: url('../assets/lottie/dansondigital-mask.svg');
  -webkit-mask-size: 100% 100%;
  -webkit-mask-repeat: no-repeat;
  opacity: 0;
  transition: opacity 0.2s var(--ease-out-expo);
}
.hero-name-mask.is-active { opacity: 1; }

/* ── Animated-asset crossfade (Lottie over a static fallback) ───────────
   The fallback starts hidden so the animated version doesn't get shown
   twice (once static, once animated). It's only revealed if the
   animation genuinely can't run: prefers-reduced-motion (media query
   below), no JS (.noscript style in <head>), or a failed load
   (lottie-init.js sets opacity inline in that case). */
.anim-wrap .anim-fallback {
  opacity: 0;
}

.anim-wrap .lottie-anim {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity 0.4s var(--ease-out-expo);
}

.anim-wrap.lottie-ready .lottie-anim { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .anim-wrap .anim-fallback { opacity: 1; }
}

/* ── Firework field — scattered, looping firework bursts in the back of
   the hero/contact bookends. Decorative, behind all content. ─────────── */
.firework-field {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}

.firework-dot {
  position: absolute;
  width: clamp(70px, 9vw, 120px);
  height: clamp(70px, 9vw, 120px);
}

/* Hero fireworks run bigger and spread across the full header (left and
   right of the logo, not just the right-hand gap) -- they sit behind
   the wordmark in z-order (earlier in the DOM, no z-index), so it's
   fine for them to pass behind it. */
.firework-field--hero .firework-dot {
  width: clamp(140px, 17vw, 240px);
  height: clamp(140px, 17vw, 240px);
}
.firework-field--hero .firework-dot--1 { top: 8%; left: 6%; }
.firework-field--hero .firework-dot--2 { top: 58%; left: 22%; }
.firework-field--hero .firework-dot--3 { top: 12%; right: 8%; }
.firework-field--hero .firework-dot--4 { top: 66%; right: 16%; }

.firework-field--contact .firework-dot--1 { top: 6%; left: 6%; }
.firework-field--contact .firework-dot--2 { top: 58%; left: 16%; }
.firework-field--contact .firework-dot--3 { top: 12%; right: 8%; }
.firework-field--contact .firework-dot--4 { top: 62%; right: 24%; }

@media (max-width: 760px) {
  .firework-dot { width: 60px; height: 60px; }
}

/* Click-anywhere firework burst, spawned at the click point by main.js */
.click-firework {
  position: fixed;
  top: 0;
  left: 0;
  width: clamp(70px, 9vw, 120px);
  height: clamp(70px, 9vw, 120px);
  transform: translate(-50%, -50%);
  pointer-events: none;
  z-index: 999;
}

/* Fireworks burst hooked to the contact CTA — empty/invisible until a
   fireworks.json is exported and loads successfully; harmless either way. */
.cta-wrap {
  position: relative;
  display: inline-flex;
}

.cta-wrap .btn-accent {
  position: relative;
  z-index: 1;
}

.lottie-anim--burst {
  position: absolute;
  inset: -70px;
  z-index: 0;
  pointer-events: none;
}

.hero-role {
  margin-top: var(--space-md);
  font-size: clamp(1rem, 1.6vw, 1.25rem);
  color: oklch(1 0 0 / 0.92);
  font-weight: 600;
  opacity: 0;
  animation: rise 0.8s var(--ease-out-expo) 0.32s forwards;
}

.hero-lede {
  margin-top: var(--space-sm);
  max-width: 56ch;
  color: oklch(1 0 0 / 0.92);
  font-size: 1.05rem;
  font-weight: 500;
  opacity: 0;
  animation: rise 0.8s var(--ease-out-expo) 0.42s forwards;
}

.hero-actions {
  display: flex;
  gap: var(--space-sm);
  margin-top: var(--space-md);
  flex-wrap: wrap;
  opacity: 0;
  animation: rise 0.8s var(--ease-out-expo) 0.52s forwards;
}

.hero-stats {
  display: flex;
  gap: clamp(var(--space-md), 5vw, var(--space-lg));
  margin-top: var(--space-xl);
  flex-wrap: wrap;
  opacity: 0;
  animation: rise 0.8s var(--ease-out-expo) 0.64s forwards;
}

.stat { display: flex; flex-direction: column; gap: 0.2em; }

.stat-num {
  font-size: clamp(1.8rem, 3vw, 2.4rem);
  font-weight: 900;
  color: #fff;
}

.stat-label {
  font-size: 0.8rem;
  color: oklch(1 0 0 / 0.78);
  max-width: 18ch;
  font-weight: 500;
}

@keyframes rise {
  from { opacity: 0; transform: translateY(18px); }
  to { opacity: 1; transform: translateY(0); }
}

.scroll-cue {
  position: absolute;
  bottom: var(--space-md);
  left: clamp(var(--space-sm), 6vw, var(--space-xl));
  width: 26px;
  height: 42px;
  border: 1.5px solid oklch(1 0 0 / 0.4);
  border-radius: 20px;
  display: flex;
  justify-content: center;
  padding-top: 8px;
}

.scroll-cue span {
  width: 4px;
  height: 8px;
  border-radius: 2px;
  background: #fff;
  animation: scroll-pulse 1.8s ease-in-out infinite;
}

@keyframes scroll-pulse {
  0% { transform: translateY(0); opacity: 1; }
  60% { opacity: 0.2; }
  100% { transform: translateY(14px); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .hero-kicker, .hero-name, .hero-role, .hero-lede, .hero-actions, .hero-stats {
    opacity: 1; animation: none;
  }
  .scroll-cue span { animation: none; opacity: 0.6; }
}

/* ── Sections ────────────────────────────────────────────────────────── */
section { padding: var(--space-xl) clamp(var(--space-sm), 6vw, var(--space-xl)); }

/* Background rhythm down the page: teal (hero), navy (reel), white
   (services, work — neutral; pink is an accent only), pale teal (about),
   teal (contact). */
.reel {
  position: relative;
  overflow: hidden;
  background-color: var(--navy);
  color: #fff;
}

/* Colour changes wipe in from the top rather than crossfading or
   snapping in place -- two full-cover layers alternate, each starting
   translated fully off past the top edge and sliding down to
   translateY(0) to sweep over whichever one is currently showing (see
   wipeBgTo() in main.js, which also swaps z-index on every call --
   position:0 here is just the shared starting point). Sits below the
   section's actual content, which all needs position+z-index to paint
   above a positioned, same-stacking-context sibling regardless of DOM
   order. */
.reel-bg-layer {
  position: absolute;
  inset: 0;
  z-index: 0;
  background-color: var(--navy);
  pointer-events: none;
}

.reel > .section-head {
  position: relative;
  z-index: 1;
  /* Bigger than the default section-head gap -- deliberately not a
     margin-top on .reel-stage instead, since adjacent sibling margins
     collapse to their max rather than summing, which silently ate an
     earlier attempt at this. Needs to comfortably clear the tab bar's
     full height once .reel-tabs.is-playing slides it up above the
     frame. */
  margin-bottom: var(--space-xl);
}
.reel .section-head h2 { color: #fff; }
.reel .section-head p { color: oklch(1 0 0 / 0.7); }


.about { background: var(--tint-teal); }

.section-head { max-width: 60ch; margin-bottom: var(--space-lg); }

.section-head h2 {
  font-size: clamp(2rem, 4vw, 3rem);
  font-weight: 800;
  letter-spacing: -0.02em;
  margin-bottom: var(--space-xs);
}

.section-head p { color: var(--muted); font-size: 1.05rem; }

/* ── Scroll reveals ──────────────────────────────────────────────────────
   A default fade-up, plus a few bouncier variants (.reveal-pop,
   .reveal-zoom, .reveal-slide) so different section types get a
   distinct, springier entrance rather than one uniform fade. */
.reveal {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity 0.7s var(--ease-out-expo), transform 0.7s var(--ease-out-expo);
}
.reveal.is-visible { opacity: 1; transform: translateY(0); }

.reveal-pop {
  opacity: 0;
  transform: translateY(36px) scale(0.85) rotate(-3deg);
  transition: opacity 0.6s var(--ease-out-expo), transform 0.7s var(--ease-back);
}
.reveal-pop:nth-child(even) { transform: translateY(36px) scale(0.85) rotate(3deg); }
.reveal-pop.is-visible { opacity: 1; transform: translateY(0) scale(1) rotate(0); }

.reveal-zoom {
  opacity: 0;
  transform: scale(0.88);
  transition: opacity 0.6s var(--ease-out-expo), transform 0.6s var(--ease-back);
}
.reveal-zoom.is-visible { opacity: 1; transform: scale(1); }

.reveal-slide {
  opacity: 0;
  transform: translateX(-44px);
  transition: opacity 0.6s var(--ease-out-expo), transform 0.6s var(--ease-back);
}
.reveal-slide.is-visible { opacity: 1; transform: translateX(0); }

@media (prefers-reduced-motion: reduce) {
  .reveal, .reveal-pop, .reveal-zoom, .reveal-slide {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ── Work grid ───────────────────────────────────────────────────────── */
.work-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: var(--space-md);
}

.work-card {
  background: var(--surface);
  border-radius: var(--radius-lg);
  overflow: hidden;
  display: flex;
  flex-direction: column;
  transition: transform 0.4s var(--ease-out-expo), box-shadow 0.4s var(--ease-out-expo);
}

.work-card:hover {
  transform: translateY(-6px);
  box-shadow: 0 20px 40px -20px oklch(0.2 0.03 230 / 0.25), 0 0 0 2px var(--teal);
}

.work-card--feature { grid-column: span 2; }

@media (max-width: 760px) {
  .work-card--feature { grid-column: span 1; }
}

.work-media {
  aspect-ratio: 16 / 10;
  overflow: hidden;
  background: var(--surface-2);
}

.work-card--feature .work-media { aspect-ratio: 16 / 7; }

.work-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.6s var(--ease-out-expo);
}

.work-card:hover .work-media img { transform: scale(1.05); }

.work-body { padding: var(--space-md); flex: 1; display: flex; flex-direction: column; gap: 0.4em; }

.work-date {
  font-size: 0.78rem;
  color: var(--teal-ink);
  font-weight: 700;
  letter-spacing: 0.03em;
}

.work-body h3 {
  font-size: 1.3rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.work-body p:not(.work-date) {
  color: var(--muted);
  font-size: 0.92rem;
  max-width: 65ch;
}

.work-body strong { color: var(--ink); font-weight: 700; }

.tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4em;
  margin-top: 0.4em;
}

.tags li {
  font-size: 0.75rem;
  color: var(--muted);
  background: var(--surface-2);
  padding: 0.3em 0.8em;
  border-radius: 999px;
  font-weight: 600;
}

/* ── About ───────────────────────────────────────────────────────────── */
.about-lede {
  font-size: 1.15rem;
  line-height: 1.75;
  max-width: 70ch;
}
.about-lede strong { color: var(--teal-ink); font-weight: 700; }

/* ── Showreel ────────────────────────────────────────────────────────── */
/* Floats as a control bar over the vertical center of the slideshow
   rather than sitting in its own row above the frame -- each tab
   doubles as that category's play button (see js/main.js), so a dark,
   blurred backdrop keeps the pill legible over whatever photo happens
   to be showing. top:50% + translateY(-50%) centers it regardless of
   its own height; on play, .is-playing swaps both to slide the whole
   bar up and out above the frame instead, clearing it so the Vimeo
   iframe underneath is unobstructed -- .reel-stage has no
   overflow:hidden of its own, so the bar is free to travel past the
   frame's top edge rather than getting clipped. */
.reel-tabs {
  position: absolute;
  left: var(--space-sm);
  right: var(--space-sm);
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  display: flex;
  justify-content: center;
  gap: 0.6em;
  flex-wrap: wrap;
  transition: top 0.5s var(--ease-out-expo), transform 0.5s var(--ease-out-expo);
}

.reel-tabs.is-playing {
  top: 0;
  transform: translateY(calc(-100% - var(--space-md) - var(--space-sm)));
}

.reel-tab {
  display: inline-flex;
  align-items: center;
  gap: 0.6em;
  font-family: inherit;
  font-size: 1.3rem;
  font-weight: 700;
  padding: 0.75em 1.5em 0.75em 1.1em;
  border-radius: 999px;
  border: 1.5px solid oklch(1 0 0 / 0.25);
  background: oklch(0.2 0.03 230 / 0.55);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: #fff;
  cursor: pointer;
  transition: transform 0.25s var(--ease-back), background-color 0.2s var(--ease-out-expo), border-color 0.2s var(--ease-out-expo), color 0.2s var(--ease-out-expo), font-size 0.5s var(--ease-out-expo), padding 0.5s var(--ease-out-expo);
}

.reel-tab-play {
  flex-shrink: 0;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: oklch(1 0 0 / 0.18);
  padding: 6px;
  box-sizing: content-box;
  transition: background-color 0.2s var(--ease-out-expo), width 0.5s var(--ease-out-expo), height 0.5s var(--ease-out-expo), padding 0.5s var(--ease-out-expo);
}

.reel-tab:hover { transform: scale(1.06); border-color: oklch(1 0 0 / 0.5); }

.reel-tab.is-active {
  background: var(--teal);
  border-color: var(--teal);
  color: var(--navy);
}

.reel-tab.is-active .reel-tab-play { background: oklch(1 0 0 / 0.3); }

/* Once playback moves the bar up out of the frame (.is-playing above),
   shrink the tabs -- they're no longer the focal control, just a small
   category switcher sitting above the video. */
.reel-tabs.is-playing .reel-tab {
  font-size: 1rem;
  padding: 0.55em 1.1em 0.55em 0.85em;
}
.reel-tabs.is-playing .reel-tab-play {
  width: 14px;
  height: 14px;
  padding: 5px;
}

.reel-stage {
  position: relative;
  z-index: 1;
}

.reel-frame {
  position: relative;
  z-index: 1;
  aspect-ratio: 16 / 9;
  border-radius: var(--radius-lg);
  overflow: hidden;
  background: var(--navy);
  border: 1px solid oklch(1 0 0 / 0.12);
}

.reel-poster {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Fading slideshow filling the frame, with the tab bar (play buttons)
   floating on top -- built from whatever images main.js finds listed
   in the current tab's data-slideshow folder manifest (see
   scripts/build-slideshow-manifest.js; falls back to the plain static
   poster above if no manifest loads). Each .reel-slide is a full .reel-
   poster image absolutely stacked on the others; .is-active crossfades
   it in via opacity while the rest sit at 0. */
.reel-slideshow {
  position: relative;
  width: 100%;
  height: 100%;
}

.reel-slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  z-index: 0;
  transition: opacity 1.2s ease;
}

.reel-slide.is-active {
  opacity: 1;
  z-index: 1;
}

.reel-play {
  position: absolute;
  inset: 0;
  margin: auto;
  width: 84px;
  height: 84px;
  border-radius: 50%;
  border: none;
  background: var(--teal);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: transform 0.3s var(--ease-out-expo), box-shadow 0.3s var(--ease-out-expo);
}

.reel-frame:hover .reel-play {
  transform: scale(1.08);
  box-shadow: 0 0 0 12px oklch(0.72 0.12 200 / 0.25);
}

.reel-frame iframe {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
}

.reel-link {
  position: relative;
  z-index: 1;
  display: inline-block;
  margin-top: var(--space-sm);
  color: var(--teal);
  font-size: 0.9rem;
  font-weight: 700;
  transition: opacity 0.25s var(--ease-out-expo);
}
.reel-link:hover, .reel-link:focus-visible { opacity: 0.7; }

/* Copies a deep link to whichever reel is current (see main.js) --
   sits below the frame, centred as its own pill so it reads as a
   deliberate call to action rather than a footnote link. */
.reel-share {
  position: relative;
  z-index: 1;
  display: flex;
  width: fit-content;
  align-items: center;
  gap: 0.6em;
  margin: var(--space-md) auto 0;
  padding: 0.85em 1.75em;
  border: none;
  border-radius: 999px;
  background: var(--yellow);
  font-family: inherit;
  color: var(--navy);
  font-size: 1.05rem;
  font-weight: 700;
  cursor: pointer;
  box-shadow: 0 6px 16px -6px oklch(0 0 0 / 0.4);
  transition: transform 0.25s var(--ease-back), box-shadow 0.25s var(--ease-out-expo);
}
.reel-share:hover, .reel-share:focus-visible {
  transform: scale(1.06);
  box-shadow: 0 10px 24px -6px oklch(0 0 0 / 0.5);
}

/* ── Services ────────────────────────────────────────────────────────── */
.service-list {
  border-top: 1px solid var(--surface-2);
}

.service-row {
  display: grid;
  grid-template-columns: 1.1fr 1.4fr auto;
  gap: var(--space-md);
  align-items: center;
  padding: var(--space-md) 0;
  border-bottom: 1px solid var(--surface-2);
  border-left: 3px solid transparent;
  transition: padding-left 0.3s var(--ease-out-expo), background-color 0.3s var(--ease-out-expo), border-left-color 0.3s var(--ease-out-expo);
}

.service-row:hover {
  padding-left: var(--space-sm);
  background: var(--surface);
  border-left-color: var(--pink);
}

.service-row h3 {
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.service-row p {
  color: var(--muted);
  font-size: 0.95rem;
  max-width: 50ch;
}

.service-row .tags { justify-content: flex-end; margin-top: 0; }
.service-row .tags li { color: var(--pink-ink); background: var(--pink-tint); }

@media (max-width: 760px) {
  .service-row {
    grid-template-columns: 1fr;
    gap: 0.6em;
  }
  .service-row .tags { justify-content: flex-start; }
}

/* ── Contact ─────────────────────────────────────────────────────────── */
.contact {
  position: relative;
  overflow: hidden;
  text-align: center;
  background: linear-gradient(180deg, oklch(0.69 0.12 200), var(--teal) 45%);
  color: #fff;
  border-radius: var(--radius-lg);
  margin: 0 clamp(var(--space-sm), 6vw, var(--space-xl)) var(--space-xl);
}

.contact h2 {
  font-size: clamp(2rem, 5vw, 3.4rem);
  font-weight: 800;
  letter-spacing: -0.02em;
}

.contact p {
  color: oklch(1 0 0 / 0.9);
  max-width: 46ch;
  margin: var(--space-sm) auto 0;
  font-weight: 500;
}

.contact-links {
  display: flex;
  gap: var(--space-md);
  justify-content: center;
  font-size: 0.9rem;
  font-weight: 600;
  color: oklch(1 0 0 / 0.85);
}

.contact-links a { transition: opacity 0.25s var(--ease-out-expo); }
.contact-links a:hover, .contact-links a:focus-visible { opacity: 0.7; }

/* ── Contact form ────────────────────────────────────────────────────── */
/* Flip card: front face is the live form, back face is a "Posted"
   thank-you revealed by rotating the whole card 180° on submit (see
   main.js flipToThankYou()). JS sets an explicit height on
   .contact-flip before flipping so the card doesn't jump (the back
   face is absolutely positioned, so it can't otherwise size itself). */
.contact-flip {
  position: relative;
  z-index: 1;
  margin: var(--space-lg) auto 0;
  max-width: 560px;
  perspective: 1800px;
}

.contact-flip-inner {
  position: relative;
  width: 100%;
  height: 100%;
  transform-style: preserve-3d;
  transition: transform 0.9s var(--ease-back);
}
.contact-flip.is-sent .contact-flip-inner { transform: rotateY(180deg); }

.contact-flip-front,
.contact-flip-back {
  backface-visibility: hidden;
  -webkit-backface-visibility: hidden;
}

.contact-flip-back {
  position: absolute;
  inset: 0;
  transform: rotateY(180deg);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  gap: 0.4em;
  background: var(--bg);
  color: var(--ink);
  border-radius: var(--radius-lg);
  padding: var(--space-md);
  box-shadow: 0 30px 60px -20px oklch(0.2 0.03 230 / 0.35);
}

.postmark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 84px;
  height: 84px;
  border: 3px dashed var(--teal);
  border-radius: 50%;
  color: var(--teal-ink);
  font-weight: 800;
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  transform: rotate(-12deg) scale(0);
  margin-bottom: 0.2em;
}
.contact-flip.is-sent .postmark {
  animation: postmark-stamp 0.5s var(--ease-back) 0.55s forwards;
}
@keyframes postmark-stamp {
  to { transform: rotate(-12deg) scale(1); }
}

.flip-thanks {
  font-size: clamp(1.8rem, 4vw, 2.4rem);
  font-weight: 800;
}
.flip-thanks .letter {
  display: inline-block;
  opacity: 0;
  transform: translateY(14px);
}
.contact-flip.is-sent .flip-thanks .letter {
  animation: letter-rise 0.5s var(--ease-back) forwards;
  animation-delay: calc(0.75s + var(--i, 0) * 45ms);
}
@keyframes letter-rise {
  to { opacity: 1; transform: translateY(0); }
}

.contact-flip-back p {
  color: var(--muted);
  max-width: 32ch;
}

.contact-form {
  position: relative;
  text-align: left;
  background: var(--bg);
  color: var(--ink);
  border-radius: var(--radius-lg);
  padding: var(--space-md);
  display: flex;
  flex-direction: column;
  gap: var(--space-sm);
  box-shadow: 0 30px 60px -20px oklch(0.2 0.03 230 / 0.35);
}

.form-row {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-sm);
}

@media (max-width: 540px) {
  .form-row { grid-template-columns: 1fr; }
}

.form-field {
  display: flex;
  flex-direction: column;
  gap: 0.35em;
  font-size: 0.85rem;
  font-weight: 700;
  text-align: left;
}

.form-field span { color: var(--muted); }

.form-field input,
.form-field select,
.form-field textarea {
  font-family: inherit;
  font-size: 0.95rem;
  font-weight: 500;
  color: var(--ink);
  background: var(--surface);
  border: 1.5px solid var(--surface-2);
  border-radius: var(--radius-sm);
  padding: 0.7em 0.9em;
  transition: border-color 0.2s var(--ease-out-expo);
}

.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus {
  outline: none;
  border-color: var(--teal);
}

.form-field textarea { resize: vertical; }

.form-field input.is-invalid,
.form-field textarea.is-invalid {
  border-color: var(--pop-red);
}

.field-error {
  font-size: 0.8rem;
  font-weight: 600;
  color: var(--pop-red);
}
.field-error:empty { display: none; }

.iron-triangle {
  border: 1.5px dashed var(--surface-2);
  border-radius: var(--radius-md);
  padding: var(--space-sm);
  text-align: center;
}

.iron-triangle-body {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.iron-triangle legend {
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--muted);
  padding: 0 0.3em;
  margin: 0 auto;
}

.iron-triangle-options {
  display: flex;
  gap: 0.6em;
  flex-wrap: wrap;
  justify-content: center;
}

.iron-toggle {
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 700;
  padding: 0.55em 1.1em;
  border-radius: 999px;
  border: 1.5px solid var(--surface-2);
  background: var(--surface);
  color: var(--ink);
  cursor: pointer;
  transition: transform 0.25s var(--ease-back), background-color 0.2s var(--ease-out-expo), border-color 0.2s var(--ease-out-expo), color 0.2s var(--ease-out-expo);
}

/* scale(), not translateY() -- a vertical lift physically moves the
   button away from a cursor parked near its bottom edge, un-hovering
   it, which drops it back onto the cursor and re-triggers hover in a
   jittering loop. Scaling from center never moves an edge away from
   the pointer that's already over it. */
.iron-toggle:hover { transform: scale(1.06); }

.iron-toggle.is-selected {
  background: var(--pink);
  border-color: var(--pink);
  color: #fff;
}

.iron-toggle.is-bounce { animation: iron-bounce 0.35s var(--ease-back); }

@keyframes iron-bounce {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-6px) rotate(-4deg); }
}

/* Hidden until a third pick is attempted, then slides open -- see
   main.js (adds .is-revealed on the bump path). The grid-template-rows
   0fr -> 1fr trick animates smoothly to the content's natural height
   without needing to measure it in JS. */
.iron-triangle-note-wrap {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows 0.5s var(--ease-back);
}
.iron-triangle-note-wrap.is-revealed { grid-template-rows: 1fr; }

.iron-triangle-note-wrap .iron-triangle-note {
  overflow: hidden;
  min-height: 0;
  margin: 0.6em 0 0;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--muted);
}

.form-submit { align-self: center; }

.form-status {
  text-align: center;
  font-size: 0.9rem;
  font-weight: 600;
}
.form-status.is-success { color: var(--teal-ink); }
.form-status.is-error { color: var(--pop-red); }

/* ── Footer ──────────────────────────────────────────────────────────── */
.site-footer {
  text-align: center;
  padding: var(--space-md) var(--space-sm) var(--space-lg);
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 500;
}
