/*
  Sheerss — single stylesheet.

  Design system referenced from apple.com/apple-watch-series-11 (mobile), measured
  directly from the live page:
    · 22px gutters, 980px max content width
    · section padding 96px mobile -> 160px desktop
    · sentence case, left-aligned at the gutter
    · pill buttons at 980px radius, 18px media radius
    · fixed frosted header, video bleeding through it
    · auto-advancing carousels with dot nav + play/pause
    · scroll-driven blur over the roster team photo
    · alternating pure-black / white / off-white full-bleed sections
    · scroll-triggered fade-up reveals

  Type is pitched off the hero rather than Apple's scale — see the size tokens.
  Titles are Helvetica Neue Bold; tracking is 0 (SPEC.md §1 specifies 50, but that
  read too airy on screen and was dialled back).

  Palette is unchanged from SPEC.md §1 — black, white and grey only, no accent.
  Motion lives here now (SPEC.md §10 steps 2-3); every effect is disabled under
  prefers-reduced-motion, and reveals only hide content when JS is running.
*/

/* ===== Design tokens ===== */
:root {
  --color-black: #111111;
  --color-true-black: #000000;
  --color-white: #ffffff;
  --color-grey-100: #f5f5f5;
  --color-grey-200: #e5e5e5;
  --color-grey-400: #b3b3b3;
  --color-grey-600: #757575;
  --color-grey-800: #333333;

  --color-bg: var(--color-white);
  --color-text: var(--color-black);
  --color-muted: var(--color-grey-600);
  --color-border: var(--color-grey-200);

  /* SPEC.md §1's pairing: Helvetica Neue for titles, Bodoni 72 for body. Neither is
     web-licensed, and both are absent on Windows/Android — which matters here, since
     Android dominates in Indonesia and the fallback is what most visitors see. So
     each has a real web equivalent behind it: Inter for HN, and Bodoni Moda (the
     spec's own recommendation) for Bodoni 72. */
  --font-display: 'Helvetica Neue', 'Inter', Helvetica, Arial, sans-serif;
  --font-body: 'Bodoni 72', 'Bodoni Moda', 'Libre Bodoni', Georgia, serif;
  --title-tracking: 0;

  /* Scale is pitched off the hero, which is the largest type on the page at
     26px mobile / 52px desktop. Everything else sits under it.
     Body holds at 16px minimum on mobile — SPEC.md §1's readability floor. */
  --size-hero: clamp(1.625rem, 1.2rem + 1.9vw, 3.25rem);       /* 26 -> 52 */
  --size-name: clamp(2.25rem, 1.6rem + 2.8vw, 4.5rem);         /* 36 -> 72, lead page name */
  --size-statement: clamp(1.5rem, 1.15rem + 1.5vw, 2.75rem);   /* 24 -> 44 */
  --size-headline: clamp(1.25rem, 1.05rem + 0.9vw, 2rem);      /* 20 -> 32 */
  --size-subhead: clamp(1.125rem, 1rem + 0.6vw, 1.625rem);     /* 18 -> 26 */
  --size-eyebrow: clamp(1rem, 0.92rem + 0.4vw, 1.375rem);      /* 16 -> 22 */
  --size-body: clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);       /* 16 -> 17 */
  --size-label: clamp(0.6875rem, 0.67rem + 0.08vw, 0.75rem);   /* 11 -> 12 */

  --gutter: 22px;
  --header-h: 56px;
  --section-pad: clamp(6rem, 3.4rem + 7vw, 10rem);          /* 96 -> 160 */
  --content-max: 980px;
  --measure: 34em;

  --radius-media: 18px;
  --radius-pill: 980px;

  --ease: cubic-bezier(0.28, 0.11, 0.32, 1);
}

/* Section themes — greyscale only, per SPEC.md §1 */
.theme-dark {
  --color-bg: var(--color-true-black);
  --color-text: var(--color-grey-100);
  --color-muted: var(--color-grey-400);
  --color-border: #2a2a2a;
}
.theme-alt {
  --color-bg: var(--color-grey-100);
  --color-text: var(--color-black);
}

/* ===== Reset ===== */
* { box-sizing: border-box; }
html { font-size: 16px; -webkit-text-size-adjust: 100%; }
body {
  margin: 0;
  padding-top: var(--header-h); /* the header is fixed; only .hero-bleed opts out */
  background: var(--color-white);
  color: var(--color-black);
  font-family: var(--font-body);
  font-size: var(--size-body);
  line-height: 1.55;
  /* Was -0.01em, which suited the sans. A Didone's hairlines need air, not
     tightening — SPEC.md §1 asks for 0.035em; 0 splits the difference and matches
     the tracking used on titles. */
  letter-spacing: 0;
  font-optical-sizing: auto;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: inherit; }
ul { list-style: none; margin: 0; padding: 0; }
p { margin: 0 0 1.2em; }
main { display: block; }

/* All titles: Helvetica Neue Bold, 50 tracking (SPEC.md §1). Tracking is set once
   here — per-element letter-spacing was removed rather than overridden, so nothing
   silently wins on source order. */
h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: 700;
  letter-spacing: var(--title-tracking);
  margin: 0 0 0.5em;
  line-height: 1.08;
}
h1 { font-size: var(--size-statement); }
h2 { font-size: var(--size-headline); line-height: 1.12; }
h3 { font-size: var(--size-eyebrow); line-height: 1.2; }

/* ===== Layout primitives ===== */
.section {
  background: var(--color-bg);
  color: var(--color-text);
  padding: var(--section-pad) var(--gutter);
}
.section-flush { padding-left: 0; padding-right: 0; }
.section-tight { padding-top: 0; }
.wrap { max-width: var(--content-max); margin: 0 auto; }
.measure { max-width: var(--measure); }

.eyebrow {
  font-family: var(--font-display);
  font-size: var(--size-label);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-muted);
  margin: 0 0 0.9em;
}
.statement { font-size: var(--size-statement); }
.lede {
  font-size: clamp(1.0625rem, 1rem + 0.3vw, 1.25rem); /* 17 -> 20 */
  font-weight: 400;
  letter-spacing: -0.005em;
  line-height: 1.4;
  color: var(--color-muted);
}

/* ===== Buttons ===== */
.btn {
  display: inline-block;
  padding: 0.72em 1.35em;
  background: var(--color-black);
  color: var(--color-white);
  text-decoration: none;
  font-family: var(--font-display);
  font-size: 0.9375rem;
  font-weight: 700;
  letter-spacing: var(--title-tracking);
  line-height: 1.2;
  border: 1px solid transparent;
  border-radius: var(--radius-pill);
}
.btn:hover { opacity: 0.85; }
.btn-outline {
  background: transparent;
  color: var(--color-text);
  border-color: currentColor;
}
.btn-small { font-size: 0.875rem; padding: 0.6em 1.15em; }
.theme-dark .btn { background: var(--color-white); color: var(--color-black); }
.theme-dark .btn-outline { background: transparent; color: var(--color-grey-100); }
.btn-row { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 1.75rem; }

/* ===== Fixed frosted nav =====
   Fixed rather than sticky so the hero video runs underneath it and shows through
   the frosted glass. body has a matching padding-top; only .hero-bleed opts out. */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  min-height: var(--header-h);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  padding: 0.6rem var(--gutter);
  color: var(--color-white);
  background: rgba(0, 0, 0, 0.3);
  -webkit-backdrop-filter: saturate(180%) blur(20px);
  backdrop-filter: saturate(180%) blur(20px);
  border-bottom: 1px solid rgba(255, 255, 255, 0.12);
}
/* Half-strength tint for when a hero video is behind the bar and carries the
   contrast: always on the homepage, and on lead pages only while the hero is under
   it (scripts/ui.js toggles it). White pages behind the bar need the full tint. */
.site-header-over-media { background: rgba(0, 0, 0, 0.15); }
.logo {
  display: block;
  margin-right: auto;
  line-height: 0;
}
.logo img { height: 7px; width: auto; }
/* Icon buttons. Both icons share a 24 viewBox at stroke-width 1.5 AND render at the
   same box size — both halves matter, since a smaller box scales the stroke down
   with it and the two would no longer match in weight. */
.site-header .icon { width: 20px; height: 20px; display: block; }

.nav-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  background: none;
  border: none;
  cursor: pointer;
  color: inherit;
}
.nav-cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  padding: 0;
  border-radius: 50%;
  background: var(--color-white);
  color: var(--color-black);
  text-decoration: none;
}
.nav-cta:hover { opacity: 0.85; }
.site-nav { display: none; width: 100%; }
.site-nav.is-open { display: block; }
.site-nav > ul { display: flex; flex-direction: column; padding: 0.5rem 0 0.75rem; }
.site-nav a {
  display: block;
  text-decoration: none;
  font-family: var(--font-display);
  font-size: 1.25rem;
  font-weight: 700;
  letter-spacing: var(--title-tracking);
  padding: 0.45rem 0;
}
.site-nav a[aria-current="page"] { color: rgba(255, 255, 255, 0.6); }
.submenu { padding: 0 0 0.5rem 0.9rem; }
.submenu a { font-size: 0.9375rem; font-weight: 400; padding: 0.3rem 0; }

@media (min-width: 834px) {
  :root { --header-h: 64px; }
  .nav-toggle { display: none; }
  .logo img { height: 8px; }
  .site-nav { display: block; width: auto; order: 2; margin-left: auto; }
  .nav-cta { order: 3; }
  .site-nav > ul { flex-direction: row; gap: 2.25rem; padding: 0; align-items: center; }
  .site-nav a { font-size: 0.8125rem; font-weight: 700; padding: 0; }
  .has-submenu { position: relative; }
  .submenu {
    display: none;
    position: absolute;
    top: calc(100% + 0.65rem);
    left: 50%;
    transform: translateX(-50%);
    background: rgba(0, 0, 0, 0.72);
    -webkit-backdrop-filter: saturate(180%) blur(20px);
    backdrop-filter: saturate(180%) blur(20px);
    border: 1px solid rgba(255, 255, 255, 0.12);
    border-radius: 12px;
    padding: 0.5rem 0;
    min-width: 190px;
  }
  .has-submenu:hover .submenu,
  .has-submenu:focus-within .submenu { display: block; }
  .submenu a { padding: 0.45rem 1rem; white-space: nowrap; }
}

/* ===== Hero =====
   The container aspect ratio changes per breakpoint and the video is object-fit:
   cover, so one 16:9 encode is cropped differently at each size. Per-breakpoint
   encodes can be dropped into the <source media> list later with no markup change. */
.hero {
  position: relative;
  background: var(--color-true-black);
  color: var(--color-grey-100);
}
.hero-bleed { margin-top: calc(var(--header-h) * -1); }
/* Tall portrait crop on phones — a viewport height rather than a fixed ratio, so it
   fills the screen the way the reference does. dvh keeps it stable while Safari's
   toolbar collapses; the vh line is the fallback for older browsers. */
.hero-media {
  position: relative;
  height: 82vh;
  height: 82dvh;
  overflow: hidden;
}
.hero-video,
.hero-media img { width: 100%; height: 100%; object-fit: cover; }
/* When iOS won't autoplay (Low Power Mode, or the browser's auto-play setting) it
   draws its own big play button over the video. Hide it, so a blocked video reads
   as an intentional still of the poster; scripts/ui.js starts playback on the
   first touch anywhere on the page. */
.hero-video::-webkit-media-controls-start-playback-button {
  display: none !important;
  -webkit-appearance: none;
}
.hero-video::-webkit-media-controls { display: none !important; }

/* Copy sits over the video, low in the frame. */
.hero-copy {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: 0 max(var(--gutter), calc((100% - var(--content-max)) / 2)) 2.75rem;
}
.hero-copy .eyebrow { color: rgba(255, 255, 255, 0.85); margin-bottom: 0.6em; }
.hero-copy h1 {
  font-size: var(--size-hero);
  line-height: 1.14;
  margin-bottom: 0;
  text-shadow: 0 1px 24px rgba(0, 0, 0, 0.45);
}

/* Apple's .marquee-scrim: blurs the bottom of the video and fades it into the
   black copy area below, so the two blocks read as one surface. */
.marquee-scrim {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 140px;
  background: var(--color-true-black);
  -webkit-backdrop-filter: blur(20px) saturate(1);
  backdrop-filter: blur(20px) saturate(1);
  -webkit-mask-image: linear-gradient(rgba(0, 0, 0, 0), #000 100%);
  mask-image: linear-gradient(rgba(0, 0, 0, 0), #000 100%);
  pointer-events: none;
}

@media (min-width: 700px) {
  .hero-media { height: 86vh; height: 86dvh; }
}
@media (min-width: 1024px) {
  /* A 16:9 container against 16:9 footage crops almost nothing, so the subject just
     sits small in the middle of a wide frame. Zoom in deliberately: at 1440px wide
     this samples ~1835px of the 1920px source, so it costs no sharpness. */
  .hero-media { height: 94vh; height: 94dvh; }
  .hero-video {
    object-position: center 42%;
    transform: scale(1.22);
    transform-origin: center 45%;
  }
  .marquee-scrim { height: 180px; }
  .hero-copy { padding-bottom: 4.5rem; }
  .hero-copy h1 { max-width: 20ch; }
}

/* Black breathing room between the hero and the block below it */
.block-gap {
  height: 10vh;
  height: 10dvh;
  background: var(--color-true-black);
}

/* ===== Sections with a photo background (blocks 2 and 5) ===== */
.has-bg-image { position: relative; overflow: hidden; }
.has-bg-image > .section-bg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.has-bg-image > .bg-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.7));
}
/* Black fade over the top fifth, so the black above melts into the photo instead of
   meeting it on a hard line. Layered over the existing legibility scrim. */
.has-bg-image.fade-top > .bg-scrim {
  background-image:
    linear-gradient(to bottom, var(--color-true-black), transparent 20%),
    linear-gradient(rgba(0, 0, 0, 0.4), rgba(0, 0, 0, 0.7));
}
.has-bg-image > .wrap { position: relative; }

/* ===== Statement / promise ===== */
.promise { text-align: left; }
.promise .statement { margin: 0 0 0.5em; max-width: 18ch; }

/* ===== Segmented tab control (roster + person genres) ===== */
.tabs {
  display: inline-flex;
  gap: 0.25rem;
  padding: 0.25rem;
  margin: 0 0 2.5rem;
  background: rgba(120, 120, 128, 0.16);
  border-radius: var(--radius-pill);
}
.tabs button {
  appearance: none;
  background: none;
  border: none;
  border-radius: var(--radius-pill);
  padding: 0.5rem 1.1rem;
  font-family: var(--font-display);
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: var(--title-tracking);
  color: inherit;
  cursor: pointer;
  white-space: nowrap;
}
.tabs button[aria-selected="true"] {
  background: var(--color-white);
  color: var(--color-black);
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
}

/* ===== Carousel (Selected Works + roster) =====
   The track is a native scroll-snap container, so with JS off it stays a working
   swipe gallery. .carousel-controls are hidden until the .js class is present. */
.carousel { position: relative; }
.carousel-track {
  display: flex;
  gap: 0.75rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-inline: var(--gutter);
}
.carousel-track::-webkit-scrollbar { display: none; }
.carousel-slide { flex: 0 0 auto; scroll-snap-align: center; }

[data-carousel-variant="peek"] .carousel-slide {
  width: 82%;
  max-width: 420px;
  aspect-ratio: 308 / 480;
  overflow: hidden;
  border-radius: var(--radius-media);
}
[data-carousel-variant="peek"] .carousel-slide img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
[data-carousel-variant="single"] .carousel-slide {
  width: 100%;
  scroll-snap-align: start;
}
/* One lead at a time, no sliver of the next. The section's own gutter provides the
   inset, so the track adds none — the slide then fills the full track width and the
   next one starts exactly at its right edge. */
[data-carousel-variant="single"] .carousel-track {
  padding-inline: 0;
  gap: var(--gutter);
}

.carousel-controls { display: none; }
.js .carousel-controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 1rem;
  margin-top: 2rem;
  padding-inline: var(--gutter);
}
/* Selected Works: controls float over the lower part of the photos rather than
   sitting under them, on a frosted pill so they read against any image. */
.js [data-carousel-variant="peek"] .carousel-controls {
  position: absolute;
  left: 50%;
  bottom: 1.25rem;
  transform: translateX(-50%);
  margin-top: 0;
  padding: 0.5rem 0.9rem;
  gap: 0.9rem;
  border-radius: var(--radius-pill);
  background: rgba(0, 0, 0, 0.35);
  -webkit-backdrop-filter: blur(40px);
  backdrop-filter: blur(40px);
}
.dotnav-items { display: flex; align-items: center; gap: 16px; }
.dotnav-item {
  display: block;
  width: 8px;
  height: 8px;
  padding: 0;
  border: none;
  border-radius: var(--radius-pill);
  background: rgba(255, 255, 255, 0.4);
  cursor: pointer;
  transition: width 0.25s var(--ease), background-color 0.25s linear;
}
/* Apple's active dot is a wider pill, not a colour swap */
.dotnav-item[aria-selected="true"] { width: 32px; background: var(--color-white); }
.dotnav-item:focus-visible,
.play-pause-button:focus-visible { outline: 2px solid var(--color-white); outline-offset: 3px; }

.play-pause-button {
  width: 32px;
  height: 32px;
  padding: 0;
  border: none;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(40px);
  backdrop-filter: blur(40px);
  cursor: pointer;
  position: relative;
}
/* Play triangle by default; swaps to a pause bar pair while running */
.play-pause-button::before {
  content: "";
  position: absolute;
  top: 50%;
  left: 50%;
  transform: translate(-40%, -50%);
  border-style: solid;
  border-width: 5px 0 5px 8px;
  border-color: transparent transparent transparent var(--color-white);
}
.playing .play-pause-button::before {
  transform: translate(-50%, -50%);
  border: none;
  width: 8px;
  height: 10px;
  background: linear-gradient(
    to right,
    var(--color-white) 0 3px,
    transparent 3px 5px,
    var(--color-white) 5px 8px
  );
}

/* ===== Roster: pinned scroll sequence (block 4) =====
   The section is taller than the viewport with a sticky stage inside, so scrolling
   runs a sequence: team photo sharp edge-to-edge -> photo blurs -> lead cards rise
   -> the whole block unpins and the next block follows.

   Progress properties default to their FINISHED state so that with no JS (or under
   reduced motion) the cards are visible and nothing is stranded off-screen;
   scripts/scroll-blur.js drives them from 0 instead. */
.roster { padding-left: 0; padding-right: 0; }
.roster > .wrap { padding-inline: var(--gutter); }

.roster-sequence {
  --blur-progress: 1;
  --cards-progress: 1;
  position: relative;
}
/* Height is tuned with the phase thresholds in scripts/scroll-blur.js. */
.js .roster-sequence { height: 226vh; }

.roster-pin { position: relative; }
.js .roster-pin {
  position: sticky;
  top: var(--header-h);
  height: calc(100vh - var(--header-h));
  overflow: hidden;
}

/* Edge to edge — the section's horizontal padding is removed above so nothing insets it */
.roster-bg {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
}
/* Black fade over the top and bottom fifths of the team photo only, so it blends
   into the black above and below. Pseudo-elements rather than extra background
   layers because the photo itself is set via an inline style, which would win. */
.roster-bg::before,
.roster-bg::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  height: 20%;
  pointer-events: none;
}
.roster-bg::before {
  top: 0;
  background: linear-gradient(to bottom, var(--color-true-black), transparent);
}
.roster-bg::after {
  bottom: 0;
  background: linear-gradient(to top, var(--color-true-black), transparent);
}
/* Apple's blur-after: opacity is scroll-driven, so the team photo recedes as the
   names come up and white text stays legible. */
.blur-after {
  position: absolute;
  inset: 0;
  -webkit-backdrop-filter: blur(40px) brightness(0.6);
  backdrop-filter: blur(40px) brightness(0.6);
  opacity: var(--blur-progress);
  pointer-events: none;
}

/* The section's own padding is removed so the photo can go edge to edge, so the
   card layer supplies the gutter instead. */
.roster-cards {
  position: relative;
  height: 100%;
  display: flex;
  align-items: center;
  padding-inline: var(--gutter);
  opacity: var(--cards-progress);
}
.js .roster-cards {
  transform: translateY(calc((1 - var(--cards-progress)) * 40px));
  visibility: visible;
}
.roster-cards .carousel { width: 100%; }
.roster-cards .roster-card,
.roster-cards .roster-card h3 a,
.roster-cards .roster-bio,
.roster-cards .role-tags { color: var(--color-white); }
/* Portrait at roughly half size and centred on the page; the text below it stays
   left-aligned at the gutter. */
.roster-cards .roster-card-portrait { max-width: 50%; margin-inline: auto; }
/* Name, tags, bio and button share one column 20% narrower than the card, centred on
   the page. The column is what's centred — the text inside it stays left-aligned.
   They're wrapped rather than centred individually because the button is
   inline-level, so margin:auto wouldn't move it and it would fall out of line. */
.roster-cards .roster-card-body { max-width: 80%; margin-inline: auto; }

/* ===== Roster cards ===== */
.roster-card-portrait {
  display: block;
  aspect-ratio: 4 / 5;
  overflow: hidden;
  border-radius: var(--radius-media);
  margin-bottom: 1.25rem;
}
.roster-card-portrait img { width: 100%; height: 100%; object-fit: cover; }
.roster-card h3 { margin: 0 0 0.35rem; }
.roster-card h3 a { text-decoration: none; }
.roster-bio { color: var(--color-muted); margin-bottom: 1.1rem; }
.role-tags {
  font-family: var(--font-display);
  font-size: var(--size-label);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-muted);
  margin: 0 0 0.9rem;
}

/* ===== Genre pages ===== */
.genre-frames { padding-left: 0; padding-right: 0; }
.frame-grid { display: grid; gap: 0.5rem; }
@media (min-width: 700px) { .frame-grid { grid-template-columns: repeat(2, 1fr); } }
.frame-grid figure { margin: 0; aspect-ratio: 2 / 3; overflow: hidden; }
.frame-grid img { width: 100%; height: 100%; object-fit: cover; }

.lead-list { display: grid; grid-template-columns: repeat(2, 1fr); gap: 2rem 1rem; }
@media (min-width: 834px) { .lead-list { grid-template-columns: repeat(5, 1fr); } }
.lead-list a { text-decoration: none; display: block; }
.lead-list img {
  aspect-ratio: 4 / 5;
  object-fit: cover;
  width: 100%;
  border-radius: var(--radius-media);
  margin-bottom: 0.75rem;
}
.lead-name {
  display: block;
  font-family: var(--font-display);
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: -0.01em;
}
.lead-list .role-tags { margin: 0.3rem 0 0; }

/* ===== Person pages ===== */
/* Lead hero: the lead's 9:16 video edge to edge under the frosted header, name set
   big in black over a white scrim at the foot of the frame, so it runs straight on
   into the white bio. Every rule is scoped under .hero-lead, which out-specifies the
   homepage hero rules without depending on source order. */
.hero-lead .hero-media { height: auto; }
.hero-lead .lead-frame { position: relative; width: 100%; aspect-ratio: 9 / 16; }
.hero-lead .lead-fill { display: none; }
/* Undo the homepage's desktop zoom — that exists to crop wide footage, and this
   footage is already framed for portrait. */
.hero-lead .hero-video { transform: none; object-position: center; }

/* White scrim over the bottom 40% of the frame. That 40% is mirrored in
   scripts/ui.js, which treats this zone as white when deciding the header tint. */
.lead-scrim {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 40%;
  background: linear-gradient(
    to top,
    var(--color-white) 0%,
    rgba(255, 255, 255, 0.85) 30%,
    rgba(255, 255, 255, 0) 100%
  );
  pointer-events: none;
}
.hero-lead .hero-copy h1 {
  font-size: var(--size-name);
  line-height: 1.05;
  color: var(--color-black);
  text-shadow: none;
  max-width: none;
}

/* Wider than a phone, a 9:16 frame can't span the width without an absurd height,
   so it's centred at full height and a blurred copy of the portrait fills the sides. */
@media (min-width: 700px) {
  .hero-lead .hero-media { height: 92vh; height: 92dvh; }
  .hero-lead .lead-frame { width: auto; height: 100%; margin-inline: auto; }
  .hero-lead .lead-fill {
    display: block;
    position: absolute;
    inset: -40px; /* overshoot so the blur doesn't reveal soft edges */
    background-size: cover;
    background-position: center;
    filter: blur(40px) brightness(0.75);
  }
}

/* Bio follows straight on from the white scrim, so it needs little top padding.
   It also overlaps the hero by 1px and paints above it: 9:16 of most phone widths
   isn't a whole pixel (375px -> 666.67px), and on 2x screens that shared last row
   blends the hero's layers into a visible grey seam. Covering it with the bio's
   solid white hides it — the scrim is pure white at that edge anyway. */
.lead-bio {
  position: relative;
  margin-top: -1px;
  padding-top: 1.5rem;
}
.lead-bio p:not(.role-tags) { color: var(--color-muted); }

/* Horizontal snap gallery — Apple's mobile gallery pattern, and SPEC.md §10 step 3 */
.gallery-strip {
  display: flex;
  gap: 0.75rem;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding: 0 var(--gutter) 1.5rem;
  margin: 0 calc(var(--gutter) * -1);
}
.gallery-strip::-webkit-scrollbar { display: none; }
.gallery-strip figure {
  margin: 0;
  flex: 0 0 78%;
  scroll-snap-align: center;
  aspect-ratio: 2 / 3;
  overflow: hidden;
  border-radius: var(--radius-media);
}
@media (min-width: 834px) { .gallery-strip figure { flex-basis: 42%; } }
.gallery-strip img { width: 100%; height: 100%; object-fit: cover; }

.gallery-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0.5rem; margin-top: 1rem; }
@media (min-width: 834px) { .gallery-grid { grid-template-columns: repeat(3, 1fr); gap: 0.75rem; } }
.gallery-grid figure { margin: 0; aspect-ratio: 2 / 3; overflow: hidden; border-radius: var(--radius-media); }
.gallery-grid img { width: 100%; height: 100%; object-fit: cover; }

/* ===== Videographer film tiles ===== */
.film-grid { display: grid; gap: 1.5rem; }
@media (min-width: 700px) { .film-grid { grid-template-columns: repeat(2, 1fr); } }
.film-tile-frame {
  aspect-ratio: 16 / 9;
  overflow: hidden;
  position: relative;
  border-radius: var(--radius-media);
  background: var(--color-grey-800);
}
.film-tile-frame img,
.film-tile-frame video { width: 100%; height: 100%; object-fit: cover; }
.person-films .btn-row { margin-top: 2rem; }
.film-caption {
  margin: 0.75rem 0 0;
  font-family: var(--font-display);
  font-size: var(--size-label);
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-muted);
}

/* ===== Services ===== */
/* Banner: the homepage hero's frosted-header-over-media treatment, but a photo and
   shorter. The page below is white, so the scrim only darkens for legibility rather
   than fading to black the way the homepage hero does. */
.hero-banner .hero-media { height: 70vh; height: 70dvh; }
@media (min-width: 1024px) {
  .hero-banner .hero-media { height: 78vh; height: 78dvh; }
}
.banner-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.25) 0%,
    rgba(0, 0, 0, 0) 35%,
    rgba(0, 0, 0, 0.6) 100%
  );
  pointer-events: none;
}
.banner-tagline { margin: 0.6em 0 0; max-width: 32ch; color: rgba(255, 255, 255, 0.9); }

.services-intro h2 { margin-bottom: 0.4em; }

/* Wedding and Prewedding: photo blocks with the copy over the lower part. */
.service-blocks { display: grid; gap: 1.25rem; }
@media (min-width: 834px) {
  .service-blocks { grid-template-columns: 1fr 1fr; }
}
.service-block {
  position: relative;
  overflow: hidden;
  border-radius: var(--radius-media);
  min-height: 36rem;
  display: flex;
  align-items: flex-end;
  color: var(--color-white);
  background: var(--color-grey-800);
}
.service-block > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.service-block::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(to bottom, rgba(0, 0, 0, 0) 30%, rgba(0, 0, 0, 0.78) 100%);
}
.service-block-body { position: relative; z-index: 1; padding: 1.75rem; }
.service-block h2 { margin-bottom: 0.15em; }
.service-block .price { color: rgba(255, 255, 255, 0.8); margin-bottom: 1rem; }
.service-block p { color: rgba(255, 255, 255, 0.92); }
.service-block .btn { background: var(--color-white); color: var(--color-black); }

/* Junior: a manual side-slider of package cards. */
.section-flush > .wrap { padding-inline: var(--gutter); }
.junior-packages h2 { margin-bottom: 1rem; }
[data-carousel-variant="cards"] .carousel-slide { width: 82%; max-width: 380px; }
.package-card {
  display: flex;
  flex-direction: column;
  background: var(--color-grey-100);
  border-radius: var(--radius-media);
  overflow: hidden;
}
.package-card figure { margin: 0; aspect-ratio: 4 / 3; overflow: hidden; }
.package-card figure img { width: 100%; height: 100%; object-fit: cover; }
.package-card-body {
  display: flex;
  flex-direction: column;
  flex: 1;
  padding: 1.25rem 1.25rem 1.5rem;
}
.package-card h3 { margin-bottom: 0.4em; }
.package-card-body > p:not(.eyebrow):not(.price) { color: var(--color-muted); }
/* margin-top:auto pins price + button to the foot, so they line up across cards
   whose copy runs to different lengths. */
.package-card .price { color: var(--color-text); margin-top: auto; margin-bottom: 1rem; }
.package-card .btn { align-self: flex-start; }
/* The shared dots are white, for dark sections; these sit on white. */
[data-carousel-variant="cards"] .dotnav-item { background: rgba(0, 0, 0, 0.2); }
[data-carousel-variant="cards"] .dotnav-item[aria-selected="true"] { background: var(--color-black); }
[data-carousel-variant="cards"] .dotnav-item:focus-visible { outline-color: var(--color-black); }

/* Portfolio: closing photo block linking into the work pages. */
.portfolio-block {
  display: flex;
  align-items: flex-end;
  min-height: 70vh;
  min-height: 70dvh;
}
.portfolio-block > .wrap { width: 100%; }
.portfolio-subtitle {
  margin: 0;
  font-family: var(--font-display);
  font-size: var(--size-label);
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.85);
}

/* ===== Enquire ===== */
/* The questions open WhatsApp rather than posting anywhere (templates/enquire.js).
   Apple-style: filled grey fields, no borders, pill choices instead of radios. */
.enquire-form { max-width: 36rem; }
.form-field { margin: 0 0 2rem; }
fieldset.form-field { border: none; padding: 0; margin-inline: 0; min-width: 0; }
.form-field > label,
.form-field legend {
  display: block;
  font-family: var(--font-display);
  font-size: 1.0625rem;
  font-weight: 700;
  margin-bottom: 0.6rem;
  padding: 0;
}
.field-hint { font-size: 0.875rem; color: var(--color-muted); margin: -0.35rem 0 0.6rem; }
.form-field input[type="text"],
.form-field input[type="date"],
.form-field select,
.form-field textarea {
  display: block;
  width: 100%;
  min-height: 3.25rem;
  padding: 0.9rem 1rem;
  border: 0;
  border-radius: 12px;
  background: var(--color-grey-100);
  font-family: var(--font-body);
  font-size: 1rem; /* 16px, or iOS zooms the page on focus */
  line-height: 1.4;
  color: var(--color-black);
  -webkit-appearance: none;
  appearance: none;
}
.form-field textarea { resize: vertical; }
.form-field input:focus,
.form-field select:focus,
.form-field textarea:focus { outline: 2px solid var(--color-black); outline-offset: 2px; }
/* iOS centres an empty date's placeholder and shrinks it; keep it a normal field. */
.form-field input[type="date"]::-webkit-date-and-time-value { text-align: left; }
.form-field input:disabled { opacity: 0.4; }
.form-field select {
  padding-right: 2.75rem;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath d='M1 1.5l5 5 5-5' fill='none' stroke='%23111' stroke-width='1.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 1.1rem center;
}

/* Pill choices: the input covers its pill invisibly, so it's what gets tapped and
   focused, and the browser's "please select" prompt points at the right place. */
.chip-row { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.chip { position: relative; display: inline-flex; }
.chip input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}
.chip span {
  padding: 0.75em 1.3em;
  border: 1px solid var(--color-grey-400);
  border-radius: var(--radius-pill);
  font-family: var(--font-display);
  font-size: 0.9375rem;
  font-weight: 700;
  line-height: 1.2;
  transition: background-color 0.2s var(--ease), color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.chip input:checked + span {
  background: var(--color-black);
  border-color: var(--color-black);
  color: var(--color-white);
}
.chip input:focus-visible + span { outline: 2px solid var(--color-black); outline-offset: 2px; }
.date-row { display: flex; flex-wrap: wrap; gap: 0.5rem; align-items: center; }
.date-row input[type="date"] { flex: 1 1 9rem; width: auto; }

.enquire-submit { margin-top: 2.75rem; }
button.btn { cursor: pointer; }
.btn-large { font-size: 1.0625rem; padding: 1em 1.75em; }
.enquire-submit .btn,
.enquire-fallback .btn { width: 100%; text-align: center; }
@media (min-width: 700px) {
  .enquire-submit .btn,
  .enquire-fallback .btn { width: auto; }
}
.send-note { font-size: 0.875rem; color: var(--color-muted); margin: 0.9rem 0 0; }
/* The message is composed by JS; without it the plain WhatsApp link stands in. */
.enquire-fallback { margin-top: 2.75rem; }
.js .enquire-fallback { display: none; }
html:not(.js) .enquire-submit { display: none; }

.steps { display: grid; gap: 2.25rem; margin: 2.5rem 0 0; padding: 0; list-style: none; }
@media (min-width: 834px) { .steps { grid-template-columns: repeat(3, 1fr); gap: 2.5rem; } }
.step-num {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  margin-bottom: 1rem;
  border: 1px solid currentColor;
  border-radius: 50%;
  font-family: var(--font-display);
  font-size: 0.9375rem;
  font-weight: 700;
}
.steps h3 { font-size: var(--size-subhead); margin-bottom: 0.35em; }
.steps p { color: var(--color-muted); margin: 0; }

.enquire-studio .btn-row { margin-top: 1.25rem; }

.trust-grid { display: grid; gap: 2.5rem; }
@media (min-width: 834px) { .trust-grid { grid-template-columns: 1fr 1fr; align-items: start; } }
.trust-block p { margin-bottom: 0.4em; }
.map-embed { aspect-ratio: 4 / 3; border-radius: var(--radius-media); overflow: hidden; }
.map-embed iframe { width: 100%; height: 100%; border: 0; }

/* ===== Footer ===== */
.site-footer {
  background: var(--color-grey-100);
  padding: 3.5rem var(--gutter);
  font-size: 0.8125rem;
  color: var(--color-grey-600);
}
.footer-inner { max-width: var(--content-max); margin: 0 auto; display: grid; gap: 2.5rem; }
@media (min-width: 834px) { .footer-inner { grid-template-columns: 1.5fr 1fr; } }
.footer-name { font-family: var(--font-display); font-weight: 600; color: var(--color-black); margin-bottom: 0.6em; }
.footer-contact p { margin-bottom: 0.35em; }
.footer-nav ul { display: flex; flex-direction: column; gap: 0.6rem; }
.footer-nav a { text-decoration: none; }
.footer-nav a:hover { text-decoration: underline; }

/* ===== Scroll reveals =====
   Scoped to .js so content is always visible if JavaScript never runs. */
.js [data-reveal] { opacity: 0; transform: translateY(28px); }
.js [data-reveal].is-visible {
  opacity: 1;
  transform: none;
  transition: opacity 0.9s var(--ease), transform 0.9s var(--ease);
}
.js [data-reveal-media] img { transform: scale(1.08); }
.js [data-reveal-media].is-visible img {
  transform: none;
  transition: transform 1.4s var(--ease);
}

@media (prefers-reduced-motion: reduce) {
  .js [data-reveal],
  .js [data-reveal].is-visible { opacity: 1; transform: none; transition: none; }
  .js [data-reveal-media] img,
  .js [data-reveal-media].is-visible img { transform: none; transition: none; }
  .gallery-strip,
  .carousel-track { scroll-behavior: auto; }
  .dotnav-item { transition: none; }
  /* Nothing auto-advances, so there's nothing to pause */
  .play-pause-button { display: none; }
  /* The pinned sequence is scroll-driven, so collapse it to a normal block rather
     than leaving several viewports of dead scroll. Progress properties keep their
     finished defaults, so the photo is blurred and the cards are visible. */
  .js .roster-sequence { height: auto; }
  .js .roster-pin { position: relative; top: auto; height: auto; padding: 3rem 0; }
  .js .roster-cards { transform: none; }
  .btn:hover { opacity: 1; }
}
