/* ==========================================================================
   HATCH WEDDINGS — page composition
   Sections, film cards, the custom player.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. The reel HUD — fixed chrome that makes the page feel like equipment
   -------------------------------------------------------------------------- */

/* Running timecode, bottom-left. Advances with scroll depth, so it reads as
   your position in the film rather than as decoration. */
.hud {
  position: fixed;
  left: var(--gut);
  bottom: calc(var(--bar) + 1.5rem);
  z-index: 92;
  display: flex;
  align-items: center;
  gap: 0.7rem;
  pointer-events: none;
  transition: opacity var(--t-med) var(--ease), bottom var(--t-slow) var(--ease-io);
}
.hud.is-gone { opacity: 0; }

.hud__dot {
  width: 5px; height: 5px;
  border-radius: 50%;
  background: var(--accent);
  animation: rec 2.4s var(--ease-soft) infinite;
}
@keyframes rec { 0%, 100% { opacity: 1; } 50% { opacity: .18; } }

/* Chapter rail, right edge. Dots per section; the active one stretches into
   a bar. Click to travel. */
.rail {
  position: fixed;
  right: var(--gut);
  top: 50%;
  transform: translateY(-50%);
  z-index: 92;
  display: grid;
  gap: 1.1rem;
  justify-items: end;
}

.rail__item {
  display: flex;
  align-items: center;
  gap: 0.85rem;
  color: var(--bone-3);
  transition: color var(--t-fast) var(--ease);
}

.rail__num {
  font-family: var(--mono);
  font-size: 0.5625rem;
  letter-spacing: 0.1em;
  opacity: 0;
  transform: translateX(6px);
  transition: opacity var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
}

.rail__tick {
  display: block;
  width: 18px; height: 1px;
  background: currentColor;
  transition: width var(--t-med) var(--ease), background var(--t-fast) var(--ease);
}

.rail__item:hover { color: var(--bone); }
.rail__item:hover .rail__num { opacity: 1; transform: none; }

.rail__item.is-here { color: var(--accent); }
.rail__item.is-here .rail__tick { width: 38px; }
.rail__item.is-here .rail__num { opacity: 1; transform: none; }

@media (max-width: 68rem) { .rail { display: none; } }
@media (max-width: 40rem) { .hud { display: none; } }

/* --------------------------------------------------------------------------
   2. Hero
   -------------------------------------------------------------------------- */

.hero {
  position: relative;
  min-height: 100svh;
  display: grid;
  align-items: center;
  overflow: hidden;
  isolation: isolate;
}

.hero__media {
  position: absolute;
  inset: 0;
  z-index: -2;
}
.hero__media img,
.hero__media video {
  width: 100%; height: 100%;
  object-fit: cover;
  /* Desaturated and lifted slightly — stops stock footage looking like stock */
  filter: saturate(0.72) contrast(1.06) brightness(0.82);
  transform: scale(1.08);
  will-change: transform;
}

/* Two-stop scrim: heavier on the left where the type sits */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(100deg, rgba(7, 7, 7,.94) 0%, rgba(7, 7, 7,.72) 38%, rgba(7, 7, 7,.30) 68%, rgba(7, 7, 7,.55) 100%),
    linear-gradient(to top, var(--ink) 2%, transparent 30%);
}

.hero__inner {
  position: relative;
  padding-block: clamp(8rem, 18vh, 12rem) clamp(7rem, 14vh, 10rem);
}

.hero__eyebrow {
  display: flex;
  align-items: center;
  gap: 1.1rem;
  margin-bottom: clamp(1.5rem, 3vh, 2.4rem);
}
.hero__eyebrow::before {
  content: '';
  width: clamp(2rem, 5vw, 4rem);
  height: 1px;
  background: var(--accent);
  flex: none;
}

.hero__title { margin-bottom: 1.6rem; }

/* Two-line hero statement. The second line drops to secondary brightness so
   the pair reads as one thought with a beat in the middle, rather than as
   two competing headlines. */
.hero__h1 {
  font-family: var(--serif);
  font-weight: 200;
  font-size: clamp(2.4rem, 7vw, 6rem);
  line-height: 1.04;
  letter-spacing: 0.012em;
  margin-bottom: clamp(1.4rem, 3vh, 2rem);
}
.hero__h1 span { color: var(--bone-2); }

.hero__sub {
  font-family: var(--sans);
  font-size: clamp(1rem, 0.95rem + 0.3vw, 1.2rem);
  font-weight: 300;
  line-height: 1.6;
  letter-spacing: 0.01em;
  color: var(--bone-2);
  max-width: 32rem;
  margin-bottom: clamp(2rem, 5vh, 3.2rem);
}

.hero__cta { display: flex; flex-wrap: wrap; gap: 1rem 1.4rem; }

/* Inset frame with corner ticks — a viewfinder overlay rather than a border.

   Its inset has to clear the other hero chrome, not just the page gutter.
   At the gutter alone the top corners landed on the logo and the "Check your
   date" button, and the bottom ones on the timecode and the scroll cue. The
   vertical insets below sit below the nav and above that cue. */
.hero__frame {
  position: absolute;
  inset: clamp(6.5rem, 12vh, 8.5rem) var(--gut) clamp(6rem, 11vh, 7.5rem);
  z-index: 1;
  pointer-events: none;
  opacity: 0;
  transition: opacity 1.2s var(--ease) .6s;
}

/* Below the tablet breakpoint the chapter rail and timecode are hidden and
   the hero is tighter, so the frame would crowd the type. Drop it. */
@media (max-width: 48rem) { .hero__frame { display: none; } }
.hero__frame.is-in { opacity: 1; }

.hero__frame i {
  position: absolute;
  width: 18px; height: 18px;
  border: 0 solid var(--bone-4);
}
.hero__frame i:nth-child(1) { top: 0; left: 0; border-top-width: 1px; border-left-width: 1px; }
.hero__frame i:nth-child(2) { top: 0; right: 0; border-top-width: 1px; border-right-width: 1px; }
.hero__frame i:nth-child(3) { bottom: 0; left: 0; border-bottom-width: 1px; border-left-width: 1px; }
.hero__frame i:nth-child(4) { bottom: 0; right: 0; border-bottom-width: 1px; border-right-width: 1px; }

/* Scroll cue — a hairline that drains downward on a loop */
.cue {
  position: absolute;
  left: 50%;
  bottom: clamp(1.6rem, 4vh, 2.6rem);
  transform: translateX(-50%);
  z-index: 2;
  display: grid;
  justify-items: center;
  gap: 0.85rem;
}
.cue__rail {
  width: 1px; height: 46px;
  background: var(--hair);
  position: relative;
  overflow: hidden;
}
.cue__rail::after {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 40%;
  background: var(--accent);
  animation: drain 2.4s var(--ease-io) infinite;
}
@keyframes drain {
  0%   { transform: translateY(-100%); }
  100% { transform: translateY(250%); }
}

@media (max-width: 40rem) {
  .hero__scrim {
    background:
      linear-gradient(180deg, rgba(7, 7, 7,.82) 0%, rgba(7, 7, 7,.55) 45%, rgba(7, 7, 7,.92) 100%);
  }
  .cue { display: none; }
}

/* --------------------------------------------------------------------------
   3. Section furniture
   -------------------------------------------------------------------------- */

.sec { position: relative; }

.sec__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 2rem;
  flex-wrap: wrap;
  margin-bottom: clamp(2.5rem, 6vh, 4.5rem);
}

.sec__mark { display: flex; align-items: baseline; gap: 1.1rem; }
.sec__mark .tc { color: var(--accent); }

/* --------------------------------------------------------------------------
   4. Manifesto — words resolve out of the dark as you scroll
   -------------------------------------------------------------------------- */

.manifesto {
  font-family: var(--serif);
  font-weight: 200;
  font-size: clamp(1.7rem, 4.4vw, 3.6rem);
  line-height: 1.22;
  letter-spacing: 0.01em;
  max-width: 24ch;
  /* Even out the lines instead of leaving a one-word stub at the end */
  text-wrap: balance;
}
.manifesto em { color: var(--accent); }

/* The list of small moments. No bullet markers at all — each line is its own
   thought, separated by a hairline, and arrives on its own beat. */
.moments {
  list-style: none;
  margin-top: clamp(1.8rem, 4vh, 2.8rem);
  display: grid;
  max-width: 32rem;
}

.moments li {
  font-size: clamp(0.9375rem, 0.9rem + 0.25vw, 1.0625rem);
  line-height: 1.6;
  color: var(--bone-2);
  padding-block: 0.85rem;
  border-top: 1px solid var(--hair-soft);
}
.moments li:last-child { border-bottom: 1px solid var(--hair-soft); }

/* DIVERGES FROM THE WEDDINGS SITE — do not restore by copying that file.

   The wedding site brightens the closing line of each list, because each of
   its lists builds to a payoff ("the little moments you were too busy
   enjoying to notice"). These lists are flat enumerations — disciplines,
   deliverables, capabilities — so lighting up the last item just made
   "Mastered for every platform it will live on" look like the important one.

   .moments li:last-child keeps only its border-bottom, set above. */

.manifesto-grid {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: clamp(2rem, 6vw, 6rem);
  align-items: end;
}
@media (max-width: 60rem) {
  .manifesto-grid { grid-template-columns: 1fr; align-items: start; }
  .manifesto { max-width: none; }
}

/* --------------------------------------------------------------------------
   5. Film cards
   -------------------------------------------------------------------------- */

.film {
  position: relative;
  display: block;
  isolation: isolate;
}

.film__frame {
  position: relative;
  overflow: hidden;
  background: var(--ink-2);
  aspect-ratio: 16 / 10;
}

.film__frame img,
.film__frame video {
  width: 100%; height: 100%;
  object-fit: cover;
  filter: saturate(.78) brightness(.86);
  transform: scale(1.02);
  transition: transform 1.4s var(--ease), filter var(--t-med) var(--ease);
}

/* Silent preview loop, revealed on hover where one exists */
.film__prev {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity .7s var(--ease);
}
.film:hover .film__prev { opacity: 1; }

.film__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, rgba(7, 7, 7,.88) 0%, rgba(7, 7, 7,.18) 48%, transparent 74%);
  opacity: .9;
  transition: opacity var(--t-med) var(--ease);
}

/* Corner ticks draw in on hover — the viewfinder locking on */
.film__ticks i {
  position: absolute;
  width: 14px; height: 14px;
  border: 0 solid var(--bone);
  opacity: 0;
  transition: opacity var(--t-med) var(--ease), transform var(--t-med) var(--ease);
}
.film__ticks i:nth-child(1) { top: 14px; left: 14px; border-top-width: 1px; border-left-width: 1px; transform: translate(-6px, -6px); }
.film__ticks i:nth-child(2) { top: 14px; right: 14px; border-top-width: 1px; border-right-width: 1px; transform: translate(6px, -6px); }
.film__ticks i:nth-child(3) { bottom: 14px; left: 14px; border-bottom-width: 1px; border-left-width: 1px; transform: translate(-6px, 6px); }
.film__ticks i:nth-child(4) { bottom: 14px; right: 14px; border-bottom-width: 1px; border-right-width: 1px; transform: translate(6px, 6px); }

.film:hover .film__ticks i { opacity: .6; transform: none; }
.film:hover .film__frame img { transform: scale(1.07); filter: saturate(.95) brightness(1); }
.film:hover .film__scrim { opacity: .65; }

.film__meta {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  padding: clamp(1.1rem, 2.4vw, 1.9rem);
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem;
}

.film__name {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(1.15rem, 1.9vw, 1.7rem);
  line-height: 1.1;
  letter-spacing: 0.01em;
  margin-bottom: 0.42rem;
}

.film__where {
  font-size: 0.625rem;
  font-weight: 500;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--bone-3);
}

.film__run {
  flex: none;
  font-family: var(--mono);
  font-size: 0.5625rem;
  letter-spacing: .1em;
  color: var(--bone-3);
  padding: .3rem .5rem;
  border: 1px solid var(--hair);
}

/* --- The reel: horizontal film strip on the home page ------------------- */

.reel { position: relative; }

.reel__track {
  display: flex;
  gap: clamp(1rem, 2vw, 2rem);
  will-change: transform;
}

.reel__cell {
  flex: none;
  width: clamp(19rem, 32vw, 32rem);
}

/* Sprocket rail above the strip — the detail that sells the metaphor */
.sprockets {
  display: flex;
  gap: 14px;
  margin-bottom: 1.4rem;
  opacity: .3;
  overflow: hidden;
}
.sprockets i {
  flex: none;
  width: 13px; height: 9px;
  border: 1px solid var(--bone-4);
  border-radius: 2px;
}

/* Desktop: vertical scroll drives the strip sideways inside a sticky stage */
@media (min-width: 62rem) {
  .reel__stage {
    position: sticky;
    top: 0;
    height: 100svh;
    display: grid;
    /* minmax(0, 1fr), not 1fr. A grid item's automatic minimum size is its
       content, so the full-width film track was stretching the shell wider
       than the viewport and dragging the section heading off the left edge
       with it. This lets the shell shrink and the track overflow instead,
       which the stage then clips. */
    grid-template-columns: minmax(0, 1fr);
    align-content: center;
    overflow: hidden;
  }
  .reel__stage > .shell { min-width: 0; }
  .reel__scroll { min-width: 0; }
}

/* Mobile: honest native swipe with snap — scroll-jacking on touch is awful */
@media (max-width: 61.99rem) {
  .reel__scroll {
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    -webkit-overflow-scrolling: touch;
    padding-inline: var(--gut);
    margin-inline: calc(var(--gut) * -1);
    scrollbar-width: none;
  }
  .reel__scroll::-webkit-scrollbar { display: none; }
  .reel__track { transform: none !important; }
  .reel__cell { width: min(78vw, 26rem); scroll-snap-align: center; }
}

/* --- Portfolio grid ------------------------------------------------------ */

/* A plain two-up grid on desktop.

   This used to be auto-fill with a 24rem minimum plus a rule making every
   7n+1 item span two columns. With a wide viewport that produced four
   tracks, and with only a handful of films the span left half-empty tracks
   mid-grid — large dead areas beside the cards rather than an editorial
   rhythm. Two equal columns fill the row at any count. */
.grid-films {
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(1.2rem, 2.6vw, 2.4rem);
}

@media (min-width: 48rem) {
  .grid-films { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

/* Filter row */
.filters {
  display: flex;
  flex-wrap: wrap;
  gap: .5rem 1.6rem;
  align-items: center;
  margin-bottom: clamp(2rem, 4vh, 3rem);
  padding-bottom: 1.2rem;
  border-bottom: 1px solid var(--hair-soft);
}
.filters button {
  font-size: .6875rem;
  font-weight: 500;
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--bone-3);
  padding: .4rem 0;
  position: relative;
  transition: color var(--t-fast) var(--ease);
}
.filters button::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0; height: 1px; width: 100%;
  background: var(--accent);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--t-fast) var(--ease);
}
.filters button:hover { color: var(--bone); }
.filters button.is-on { color: var(--bone); }
.filters button.is-on::after { transform: scaleX(1); }

/* --------------------------------------------------------------------------
   6. Split / editorial blocks
   -------------------------------------------------------------------------- */

.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2rem, 6vw, 6rem);
  align-items: center;
}
.split--wide { grid-template-columns: 1.15fr 1fr; }
.split__media { position: relative; overflow: hidden; aspect-ratio: 4 / 5; }
.split__media img { width: 100%; height: 100%; object-fit: cover; filter: saturate(.8) brightness(.9); }

@media (max-width: 60rem) {
  .split, .split--wide { grid-template-columns: 1fr; }
  .split__media { aspect-ratio: 16 / 11; }
}

/* A single frame lifted straight out of a film — used where a section is
   about the edit rather than about a product. Sized by the frame's own
   16:9 ratio instead of a fixed box, so none of Connor's framing is cropped
   away, and graded a touch down so it sits in the page rather than shouting
   over the type beside it. */
/* The picture takes the wider half — a wide frame shrunk to a text column
   loses the thing that makes it worth showing, which is the scale of the
   house against the people in front of it. */
.split--film { grid-template-columns: minmax(0, 1fr) minmax(0, 1.16fr); }
@media (max-width: 60rem) { .split--film { grid-template-columns: 1fr; } }

.still { margin: 0; display: grid; gap: 1rem; }
.still__frame { position: relative; overflow: hidden; }
.still__frame img {
  display: block;
  width: 100%;
  height: auto;
  filter: saturate(.86) brightness(.92);
}
.still__cap { color: var(--bone-3); }

@media (max-width: 60rem) {
  .still { margin-top: 1rem; }
}

/* --------------------------------------------------------------------------
   7. Process — sticky numerals, scrolling copy
   -------------------------------------------------------------------------- */

/* The section heading lives inside the left column, not above both, so it
   wraps against the steps instead of running the full width underneath the
   enquiry card. */
.process {
  display: grid;
  /* Equal halves: the steps' own reading measure caps their text, so a
     narrow fixed form column left a wide empty channel between the two. */
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(2rem, 4vw, 4rem);
  align-items: start;
}

.process__main { min-width: 0; }
.process__main .display-m { max-width: 22ch; }

/* The enquiry card rides alongside the steps and stays put while they
   scroll — the moment a couple is most likely to act is while they're
   reading how it works. */
.process__aside {
  position: sticky;
  top: 7.5rem;
  border: 1px solid var(--hair);
  background: var(--ink-1);
  padding: clamp(1.8rem, 2.4vw, 2.4rem);
  display: grid;
  gap: 1.5rem;
}
.process__aside .field input,
.process__aside .field textarea { font-size: .9375rem; }
.process__aside .field input { padding-top: 1rem; }
.process__aside .btn { justify-content: center; }

@media (max-width: 68rem) {
  .process { grid-template-columns: 1fr; }
  .process__main .display-m { max-width: none; }
  .process__aside { position: static; max-width: 34rem; }
}

.process__steps { display: grid; gap: clamp(2.5rem, 8vh, 6rem); }

.step { display: grid; grid-template-columns: auto 1fr; gap: clamp(1.2rem, 3vw, 2.6rem); align-items: start; }

.step__n {
  font-family: var(--mono);
  font-size: .625rem;
  letter-spacing: .16em;
  color: var(--accent);
  padding-top: .45rem;
}

.step__body h3 { margin-bottom: .8rem; }

@media (max-width: 52rem) {
  .process { grid-template-columns: 1fr; }
  .step { grid-template-columns: 1fr; gap: .7rem; }
}

/* --------------------------------------------------------------------------
   8. Values strip
   -------------------------------------------------------------------------- */

.values {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  border-top: 1px solid var(--hair-soft);
  border-bottom: 1px solid var(--hair-soft);
}

.value {
  padding: clamp(2rem, 4.5vh, 3.2rem) clamp(1rem, 2.4vw, 2rem);
  display: grid;
  gap: .85rem;
  align-content: start;
  position: relative;
  transition: background var(--t-med) var(--ease);
}
.value + .value { border-left: 1px solid var(--hair-soft); }
.value:hover { background: rgba(237, 237, 235, 0.022); }

.value__n { font-family: var(--mono); font-size: .5625rem; letter-spacing: .16em; color: var(--accent); }
.value h3 { font-size: 1.05rem; font-family: var(--sans); font-weight: 500; letter-spacing: .02em; line-height: 1.3; }
.value p { font-size: .8125rem; color: var(--bone-3); line-height: 1.65; }

@media (max-width: 60rem) { .values { grid-template-columns: repeat(2, 1fr); } .value:nth-child(3) { border-left: 0; } .value:nth-child(n+3) { border-top: 1px solid var(--hair-soft); } }
@media (max-width: 34rem) { .values { grid-template-columns: 1fr; } .value + .value { border-left: 0; border-top: 1px solid var(--hair-soft); } }

/* --------------------------------------------------------------------------
   8b. Reasons — a quieter, roomier grid than the values strip. Deliberately
   low-key: the work and the words carry the page, not a feature list.
   -------------------------------------------------------------------------- */

.reasons {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: clamp(2.4rem, 5vh, 4rem) clamp(2rem, 6vw, 6rem);
}
@media (max-width: 60rem) { .reasons { grid-template-columns: 1fr; } }

.reason { display: grid; gap: .85rem; align-content: start; }
.reason__n {
  font-family: var(--mono);
  font-size: .5625rem;
  letter-spacing: .16em;
  color: var(--accent);
}
.reason .body { max-width: 30rem; }

/* --------------------------------------------------------------------------
   9. Quote / testimonial
   -------------------------------------------------------------------------- */

.quote { max-width: 46rem; margin-inline: auto; text-align: center; }
.quote blockquote {
  font-family: var(--serif);
  font-weight: 200;
  font-style: normal;
  font-size: clamp(1.4rem, 3.2vw, 2.5rem);
  line-height: 1.32;
  letter-spacing: 0.012em;
  margin-bottom: 2rem;
}
.quote figcaption { display: grid; gap: .5rem; justify-items: center; }

/* --------------------------------------------------------------------------
   10. Pricing
   -------------------------------------------------------------------------- */

.packs {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 18rem), 1fr));
  gap: 1px;
  background: var(--hair-soft);
  border: 1px solid var(--hair-soft);
}

.pack {
  background: var(--ink);
  padding: clamp(1.8rem, 3.4vw, 2.8rem);
  display: grid;
  gap: 1.3rem;
  align-content: start;
  position: relative;
  transition: background var(--t-med) var(--ease);
}
.pack:hover { background: var(--ink-1); }

.pack.is-featured { background: var(--ink-1); }
.pack.is-featured::before {
  content: '';
  position: absolute;
  inset: 0 0 auto 0;
  height: 1px;
  background: var(--accent);
}

.pack__flag {
  position: absolute;
  top: 0; right: clamp(1.8rem, 3.4vw, 2.8rem);
  transform: translateY(-50%);
  background: var(--accent);
  color: var(--ink);
  font-size: .5625rem;
  font-weight: 700;
  letter-spacing: .2em;
  text-transform: uppercase;
  padding: .34rem .7rem;
}

.pack__name { font-family: var(--serif); font-weight: 300; font-size: clamp(1.5rem, 2.4vw, 2rem); line-height: 1.1; }
.pack__tag { font-size: .8125rem; color: var(--bone-3); line-height: 1.6; }

.pack__price {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(2rem, 3.4vw, 2.8rem);
  line-height: 1;
  letter-spacing: 0.015em;
}
.pack__price small {
  display: block;
  font-family: var(--sans);
  font-size: .5625rem;
  font-weight: 500;
  letter-spacing: .26em;
  text-transform: uppercase;
  color: var(--bone-3);
  margin-bottom: .55rem;
}

/* No bullet markers. Each inclusion is separated by a hairline instead,
   matching the .moments lists elsewhere. */
.pack__list { list-style: none; display: grid; }
.pack__list li {
  font-size: .84375rem;
  color: var(--bone-2);
  line-height: 1.6;
  padding-block: .6rem;
  border-top: 1px solid var(--hair-soft);
}
.pack__list li:last-child { border-bottom: 1px solid var(--hair-soft); }

/* --------------------------------------------------------------------------
   11. Closing CTA
   -------------------------------------------------------------------------- */

.closer { position: relative; overflow: hidden; isolation: isolate; }
.closer__media { position: absolute; inset: 0; z-index: -2; }
.closer__media img { width: 100%; height: 100%; object-fit: cover; filter: saturate(.55) brightness(.42); transform: scale(1.1); will-change: transform; }
.closer__scrim { position: absolute; inset: 0; z-index: -1; background: linear-gradient(to right, rgba(7, 7, 7,.92), rgba(7, 7, 7,.55) 60%, rgba(7, 7, 7,.8)); }
.closer__inner { padding-block: clamp(6rem, 18vh, 12rem); }

/* --------------------------------------------------------------------------
   12. The custom player
   Three very different backends (HTML5 / Vimeo / YouTube) wear exactly this
   one skin. No platform chrome is ever reachable — a click shield sits over
   the iframe and every control below is ours.
   -------------------------------------------------------------------------- */

.pl {
  position: fixed;
  inset: 0;
  z-index: 300;
  background: #000;
  display: grid;
  grid-template-rows: 1fr auto;
  opacity: 0;
  visibility: hidden;
  transition: opacity .6s var(--ease), visibility .6s;
}
.pl.is-open { opacity: 1; visibility: visible; }

.pl__stage {
  position: relative;
  display: grid;
  place-items: center;
  overflow: hidden;
  min-height: 0;
}

/* The frame the film plays inside — opens like a gate */
.pl__gate {
  position: relative;
  width: 100%;
  height: 100%;
  clip-path: inset(50% 0 50% 0);
  transition: clip-path 1s var(--ease-io) .1s;
}
.pl.is-open .pl__gate { clip-path: inset(0 0 0 0); }

.pl__gate iframe,
.pl__gate video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  border: 0;
  object-fit: contain;
  background: #000;
}

/* Click shield: swallows every pointer event before the embed sees it, so
   no Vimeo/YouTube UI can ever appear. Our controls live above it. */
.pl__shield { position: absolute; inset: 0; z-index: 2; }

/* Poster held over the embed until playback genuinely starts — hides the
   platform's own thumbnail and spinner */
.pl__poster {
  position: absolute;
  inset: 0;
  z-index: 3;
  background-size: cover;
  background-position: center;
  transition: opacity .8s var(--ease);
}
.pl__poster::after { content: ''; position: absolute; inset: 0; background: rgba(7, 7, 7,.42); }
.pl__poster.is-gone { opacity: 0; pointer-events: none; }

/* Our own spinner: a rotating hairline arc */
.pl__wait {
  position: absolute;
  z-index: 4;
  top: 50%; left: 50%;
  width: 46px; height: 46px;
  margin: -23px 0 0 -23px;
  border: 1px solid rgba(237, 237, 235,.16);
  border-top-color: var(--accent);
  border-radius: 50%;
  animation: spin 1s linear infinite;
  opacity: 0;
  transition: opacity .3s;
}
.pl__wait.is-on { opacity: 1; }
@keyframes spin { to { transform: rotate(360deg); } }

/* Title block, top-left of the stage */
.pl__head {
  position: absolute;
  z-index: 5;
  top: 0; left: 0; right: 0;
  padding: clamp(1.2rem, 3vw, 2.2rem);
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 2rem;
  background: linear-gradient(to bottom, rgba(0,0,0,.62), transparent);
  transition: opacity var(--t-med) var(--ease);
}
.pl__title { font-family: var(--serif); font-weight: 300; font-size: clamp(1.15rem, 2.2vw, 1.7rem); line-height: 1.15; margin-bottom: .4rem; }

.pl__x {
  flex: none;
  width: 44px; height: 44px;
  display: grid; place-items: center;
  border: 1px solid var(--bone-4);
  border-radius: 50%;
  color: var(--bone);
  transition: background var(--t-fast) var(--ease), transform var(--t-fast) var(--ease), border-color var(--t-fast);
}
.pl__x:hover { background: var(--bone); color: var(--ink); transform: rotate(90deg); border-color: var(--bone); }

/* Big centre play/pause affordance */
.pl__big {
  position: absolute;
  z-index: 5;
  top: 50%; left: 50%;
  transform: translate(-50%, -50%);
  width: clamp(64px, 9vw, 88px);
  aspect-ratio: 1;
  display: grid; place-items: center;
  border: 1px solid var(--bone-4);
  border-radius: 50%;
  background: rgba(7, 7, 7,.3);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  color: var(--bone);
  transition: opacity .45s var(--ease), transform .45s var(--ease), background var(--t-fast);
}
.pl__big:hover { background: rgba(7, 7, 7,.55); transform: translate(-50%, -50%) scale(1.06); }
.pl__big.is-gone { opacity: 0; pointer-events: none; transform: translate(-50%, -50%) scale(.82); }

/* Control bar */
.pl__bar {
  position: relative;
  z-index: 6;
  padding: clamp(.9rem, 2vw, 1.5rem) clamp(1.2rem, 3vw, 2.2rem) clamp(1.2rem, 3vw, 2rem);
  display: grid;
  gap: .95rem;
  background: var(--ink);
  border-top: 1px solid var(--hair-soft);
  transition: opacity var(--t-med) var(--ease), transform var(--t-med) var(--ease);
}

/* Both head and bar retreat when idle during playback */
.pl.is-idle .pl__bar { opacity: 0; transform: translateY(100%); }
.pl.is-idle .pl__head { opacity: 0; }

/* Scrubber: a hairline that thickens on hover. Buffered range sits behind. */
.pl__scrub {
  position: relative;
  height: 18px;
  display: flex;
  align-items: center;
  cursor: pointer;
  touch-action: none;
}
.pl__track {
  position: relative;
  width: 100%;
  height: 2px;
  background: rgba(237, 237, 235,.14);
  overflow: hidden;
  transition: height var(--t-fast) var(--ease);
}
.pl__scrub:hover .pl__track { height: 5px; }

.pl__buf, .pl__prog {
  position: absolute;
  inset: 0 auto 0 0;
  width: 0;
  transform-origin: left;
}
.pl__buf { background: rgba(237, 237, 235,.2); }
.pl__prog { background: var(--accent); }

.pl__knob {
  position: absolute;
  top: 50%;
  left: 0;
  width: 11px; height: 11px;
  margin: -5.5px 0 0 -5.5px;
  border-radius: 50%;
  background: var(--accent);
  opacity: 0;
  transform: scale(.5);
  transition: opacity var(--t-fast), transform var(--t-fast) var(--ease);
  pointer-events: none;
}
.pl__scrub:hover .pl__knob { opacity: 1; transform: scale(1); }

/* Hover timecode bubble */
.pl__peek {
  position: absolute;
  bottom: 20px;
  transform: translateX(-50%);
  padding: .28rem .5rem;
  background: var(--ink-3);
  border: 1px solid var(--hair);
  opacity: 0;
  transition: opacity var(--t-fast);
  pointer-events: none;
  white-space: nowrap;
}
.pl__scrub:hover .pl__peek { opacity: 1; }

.pl__row { display: flex; align-items: center; gap: clamp(.9rem, 2vw, 1.5rem); }
.pl__row > .pl__spacer { margin-left: auto; }

.pl__btn {
  width: 34px; height: 34px;
  display: grid; place-items: center;
  color: var(--bone-2);
  border: 1px solid transparent;
  transition: color var(--t-fast) var(--ease), border-color var(--t-fast);
}
.pl__btn:hover { color: var(--bone); border-color: var(--hair); }
.pl__btn svg { width: 15px; height: 15px; }

.pl__time { font-family: var(--mono); font-size: .625rem; letter-spacing: .1em; color: var(--bone-2); font-variant-numeric: tabular-nums; }
.pl__time b { color: var(--bone); font-weight: 400; }
.pl__time span { color: var(--bone-4); margin-inline: .35rem; }

/* Volume: a hairline that expands on hover */
.pl__vol { display: flex; align-items: center; gap: .6rem; }
.pl__volrail {
  width: 0;
  height: 2px;
  background: rgba(237, 237, 235,.14);
  position: relative;
  cursor: pointer;
  overflow: hidden;
  transition: width var(--t-med) var(--ease);
}
.pl__vol:hover .pl__volrail, .pl__vol:focus-within .pl__volrail { width: 64px; }
.pl__volfill { position: absolute; inset: 0 auto 0 0; background: var(--bone-2); }

@media (max-width: 40rem) {
  .pl__vol { display: none; }
  .pl__time { font-size: .5625rem; }
}

/* --------------------------------------------------------------------------
   13. Utility
   -------------------------------------------------------------------------- */

.empty {
  padding: clamp(3rem, 8vh, 5rem) 0;
  text-align: center;
  color: var(--bone-3);
  border: 1px dashed var(--hair);
}

.note {
  padding: 1rem 1.2rem;
  border: 1px solid var(--hair);
  border-left: 2px solid var(--accent);
  background: rgba(255, 255, 255, 0.04);
  font-size: .84375rem;
  color: var(--bone-2);
  line-height: 1.6;
}

.toast {
  position: fixed;
  left: 50%;
  bottom: 2rem;
  transform: translate(-50%, 150%);
  z-index: 400;
  padding: .9rem 1.4rem;
  background: var(--ink-3);
  border: 1px solid var(--hair);
  font-size: .8125rem;
  transition: transform .55s var(--ease);
  max-width: calc(100vw - 2rem);
}
.toast.is-up { transform: translate(-50%, 0); }
.toast.is-bad { border-left: 2px solid var(--bone); }
.toast.is-good { border-left: 2px solid var(--accent); }

/* Skeleton shimmer while Firestore resolves */
.skel {
  position: relative;
  overflow: hidden;
  background: var(--ink-2);
}
.skel::after {
  content: '';
  position: absolute;
  inset: 0;
  transform: translateX(-100%);
  background: linear-gradient(90deg, transparent, rgba(237, 237, 235,.045), transparent);
  animation: shimmer 1.6s var(--ease-soft) infinite;
}
@keyframes shimmer { to { transform: translateX(100%); } }

/* Each memory is one or two lines, so balance them rather than letting the
   second line fall short. The non-breaking-space guard in app.js handles
   browsers without text-wrap support. */
.moments li { text-wrap: balance; }

/* The action cell in the "what you receive" grid. Bordered so it reads as a
   call to action rather than a sixth item in the list. */
.reason--cta {
  border: 1px solid var(--hair);
  background: var(--ink-1);
  padding: clamp(1.6rem, 2.4vw, 2.2rem);
  gap: 1rem;
  align-self: start;
}
.reason--cta .body { color: var(--bone-2); max-width: none; }

/* A short heading column beside a long column of copy: pin it so the heading
   stays with the text it introduces, instead of sitting at the top of a tall
   empty gap. align-self keeps it from being stretched by the grid, which
   would stop sticky working. */
/* The heading holds its place while the column beside it scrolls past.

   Worth knowing, because it has bitten once: Chrome gives every sticky
   element its own compositing layer, and on a page of flat #070707 that
   layer can come back a hair off the rest of the page — a faint rectangle
   whose bottom edge reads as a stray line. Painting it opaque in the page's
   own ink is the obvious fix and makes it worse, a clearly lighter box,
   because the layer still is not composited like the page around it. So it
   stays transparent, which is the quieter of the two. If the rectangle ever
   becomes the bigger annoyance, deleting the three properties below removes
   it completely and costs only the scroll effect. */
@media (min-width: 60rem) {
  .split__sticky {
    position: sticky;
    top: 8.5rem;
    align-self: start;
  }
}

/* The enquiry card now occupies half the row, so its short fields sit in
   pairs rather than running as one tall narrow column. */
.aside__pair { display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; }
@media (max-width: 46rem) { .aside__pair { grid-template-columns: 1fr; } }

/* --------------------------------------------------------------------------
   12. Phones

   Almost every couple meets a wedding filmmaker on a phone, in the evening,
   half-scrolling. Everything below serves that: nothing to pinch, nothing to
   aim at, and the enquiry never more than a thumb away.
   -------------------------------------------------------------------------- */

/* iOS zooms the entire page in whenever a focused input is under 16px, and
   then leaves you to pinch your way back out. On the one form that has to
   work, that is the difference between an enquiry and an abandoned tab.
   16px is the floor, not a preference. */
@media (max-width: 60rem) {
  .field input,
  .field textarea,
  .field select,
  .process__aside .field input,
  .process__aside .field textarea { font-size: 1rem; }
  .field input,
  .field select { padding-block: 1.3rem 1rem; }
  .field textarea { padding-block: 1.3rem 1rem; min-height: 8rem; }
}

/* Tap targets sized for a cursor. 44px is Apple's documented minimum and
   48px is Google's; these clear both. The burger keeps its 26x14 drawing and
   gains the area as an invisible pad, so the bars stay optically aligned. */
@media (max-width: 60rem) {
  .burger::after { content: ''; position: absolute; inset: -17px -12px; }

  .socials { gap: .8rem; }
  .socials a { width: 46px; height: 46px; }

  .foot__col a { padding-block: .4rem; }

  .film__meta { padding: 1.1rem; }
}

@media (max-width: 48rem) {
  /* Calls to action take the full measure. Width is the affordance on a
     touch screen: a full-width bar is unmissable and needs no aim, where a
     240px pill floating in a column reads as a footnote. */
  .btn {
    --pad: 1.2rem;
    width: 100%;
    justify-content: space-between;
    min-height: 3.5rem;
    font-size: .75rem;
    letter-spacing: .2em;
    padding-inline: 1.5rem;
  }

  /* Bare buttons are text links wearing a button's clothes. They keep their
     own width and gain the height as padding instead. */
  .btn--bare {
    width: auto;
    min-height: 2.75rem;
    padding-block: .75rem;
    justify-content: flex-start;
    gap: 1.1rem;
  }

  .hero__cta { flex-direction: column; align-items: stretch; gap: .9rem; }

  /* In the hero specifically, the second action gets its box back. A bare
     text link under a full-width button reads as an afterthought, and on a
     phone this pair is the whole of the first screen's navigation. */
  .hero__cta .btn--bare {
    width: 100%;
    justify-content: space-between;
    min-height: 3.5rem;
    padding-inline: 1.5rem;
    border-color: var(--bone-4);
    background-image: linear-gradient(var(--bone), var(--bone));
  }
  /* :hover sticks after a tap on touch, and the bare variant's hover hides
     the border — which would look like the button breaking on press. */
  .hero__cta .btn--bare:hover {
    color: var(--ink);
    border-color: var(--bone);
    background-size: 100% 100%;
  }

  /* Display type is proportionally far larger on a phone, so gaps tuned on a
     desktop read as a collision here. */
  .sec__head { margin-bottom: clamp(2.2rem, 8vw, 3.2rem); }
  .step__body h3 { margin-bottom: 1rem; }
  .process__steps { gap: 3.4rem; }
  .pack { gap: 1.5rem; }
  .moments li { padding-block: .9rem; }
}

/* --- The standing enquiry bar -------------------------------------------- */

/* Below 60rem the nav's "Check your date" is hidden and the homepage runs to
   roughly 8,000px. A couple convinced a third of the way down had no way to
   act without scrolling to the very end or opening the menu. This rides the
   bottom of the screen from the moment the hero leaves, and steps aside when
   the page's own closing call to action arrives so the two never compete.

   The bar stays dark so the page keeps its blackness; the contrast is spent
   on the button, which is the part that has to be pressed. */
.cta-bar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 94;
  display: none;
  padding: .85rem var(--gut);
  padding-bottom: calc(.85rem + env(safe-area-inset-bottom, 0px));
  background: rgba(7, 7, 7, .88);
  -webkit-backdrop-filter: blur(16px) saturate(1.15);
          backdrop-filter: blur(16px) saturate(1.15);
  border-top: 1px solid var(--hair-soft);
  transform: translateY(102%);
  transition: transform .55s var(--ease-io);
}

@media (max-width: 60rem) {
  html.js .cta-bar { display: flex; align-items: center; gap: .7rem; }
}

.cta-bar.is-up { transform: none; }
.cta-bar__main { flex: 1; justify-content: space-between; min-height: 3.5rem; }

/* WhatsApp.

   Deliberately not the green bubble every plugin drops on a site: on a black
   cinematic page that reads as a bolt-on, and the point of the widget is to
   look like it belongs to the brand. Same bone as everything else, and it
   only ever sits beside the primary action rather than replacing it — the
   form captures the date, the venue and what they want, which a chat thread
   does not. */
.cta-bar__wa {
  flex: none;
  width: 3.5rem; height: 3.5rem;
  display: grid; place-items: center;
  border: 1px solid var(--bone-4);
  color: var(--bone);
  transition: color var(--t-fast) var(--ease),
              border-color var(--t-fast) var(--ease),
              background-color var(--t-fast) var(--ease);
}
.cta-bar__wa svg { width: 22px; height: 22px; }
.cta-bar__wa:hover { background: var(--bone); border-color: var(--bone); color: var(--ink); }

/* Above 60rem there is no bar — the nav already carries "Check your date" —
   so what is left is a single button resting in the corner. */
@media (min-width: 60.01rem) {
  html.js .cta-bar {
    display: block;
    left: auto;
    right: clamp(1.2rem, 2.4vw, 2.2rem);
    bottom: clamp(1.2rem, 2.4vw, 2.2rem);
    padding: 0;
    background: none;
    border: 0;
    -webkit-backdrop-filter: none;
            backdrop-filter: none;
    transform: translateY(180%);
  }
  html.js .cta-bar.is-up { transform: none; }
  .cta-bar__main { display: none; }
  .cta-bar__wa {
    width: 3.25rem; height: 3.25rem;
    border-radius: 50%;
    background: rgba(7, 7, 7, .8);
    -webkit-backdrop-filter: blur(14px);
            backdrop-filter: blur(14px);
    border-color: var(--hair);
  }
}

/* The enquiry page has no primary action to sit beside — it is the page — so
   the element is the button itself.

   All of this sizing used to sit inside the phone-only query, which left the
   button 0x0 and invisible on every desktop: the wide-screen .cta-bar rules
   strip the padding, background and border, and unlike the other pages there
   is no .cta-bar__wa child to carry a size, because here the bar IS the
   button. It must come after the min-width block above — same specificity,
   so source order decides. */
.cta-bar--solo { padding: 0; background: none; border: 0; }

html.js .cta-bar--solo {
  display: grid; place-items: center;
  left: auto;
  right: var(--gut);
  bottom: calc(var(--gut) + env(safe-area-inset-bottom, 0px));
  width: 3.5rem; height: 3.5rem;
  border: 1px solid var(--bone-4);
  background: rgba(7, 7, 7, .86);
  -webkit-backdrop-filter: blur(14px);
          backdrop-filter: blur(14px);
  color: var(--bone);
}
.cta-bar--solo svg { width: 22px; height: 22px; }

@media (min-width: 60.01rem) {
  /* Matches the circle the other pages park in the same corner. */
  html.js .cta-bar--solo {
    right: clamp(1.2rem, 2.4vw, 2.2rem);
    bottom: clamp(1.2rem, 2.4vw, 2.2rem);
    width: 3.25rem; height: 3.25rem;
    border-radius: 50%;
    border-color: var(--hair);
  }
}

.cta-bar--solo:hover { background: var(--bone); border-color: var(--bone); color: var(--ink); }

@media (prefers-reduced-motion: reduce) {
  .cta-bar { transition: none; }
}

/* The price line inside the enquiry card. Quiet, but present at the exact
   moment a couple is deciding whether to fill the form in. */
.aside__from {
  margin-top: .85rem;
  padding-top: .85rem;
  border-top: 1px solid var(--hair-soft);
  font-size: .8125rem;
  color: var(--bone-3);
}
.aside__from strong { color: var(--bone); font-weight: 500; }
.aside__from .tlink { font-size: inherit; }

/* Remaining sub-44px targets, and the one CTA that was still a text link.

   The Enquire inside a priced package is the closest thing this site has to
   an "add to basket": it is the moment a couple has read the price and
   decided. On a phone it was a bare text link sitting under £1,800. */
@media (max-width: 60rem) {
  .menu a { min-height: 3rem; display: flex; align-items: center; }
  .foot__col a { min-height: 2.75rem; display: flex; align-items: center; }
  .socials { margin-top: .2rem; }
  .socials a { display: grid; }

  .pack .btn--bare {
    width: 100%;
    justify-content: space-between;
    min-height: 3.5rem;
    padding-inline: 1.4rem;
    border-color: var(--bone-4);
    background-image: linear-gradient(var(--bone), var(--bone));
  }
  .pack .btn--bare:hover {
    color: var(--ink);
    border-color: var(--bone);
    background-size: 100% 100%;
  }
}

/* A lede that reads as the second half of the line above it rather than as a
   new paragraph. This used to be an inline margin-top of -.6rem, which was
   tuned against a single-line heading on a desktop. On a phone the heading
   above wraps to two or three lines of much larger type and the pull upward
   closed the gap completely — the paragraph sat right on the heading's
   baseline. The tightening is now desktop-only, and phones fall back to the
   stack's own rhythm. */
@media (min-width: 48.01rem) {
  /* Tighter than the stack's own rhythm, because this line belongs to the
     one above it — but positive. At -.6rem the two sat about six pixels
     apart, which reads as a wrap rather than as a second thought. */
  .stack > .lede--tight { margin-top: 1rem; }
}

/* A floor under every display heading on a phone. Display type here is two
   to three times the body size, so a gap that looks considered on a desktop
   reads as crushed at this scale. */
@media (max-width: 48rem) {
  .stack > .display-s + *,
  .stack > .display-m + *,
  .stack > .display-l + *,
  .stack > h2 + *,
  .stack > h3 + * { margin-top: max(var(--s, 1.5rem), 1.5rem); }
}

/* iOS refuses to autoplay video in Low Power Mode and paints its own start
   button over the frame. On a decorative hero loop that is a grey triangle
   sitting in the middle of the headline shot. Hiding it lets the poster
   still stand in, which is what the fallback was always meant to look like.
   Scoped to the hero: the film player needs its own surface untouched. */
.hero__media video::-webkit-media-controls,
.hero__media video::-webkit-media-controls-start-playback-button,
.hero__media video::-webkit-media-controls-panel {
  display: none !important;
  -webkit-appearance: none;
}

/* The closing image on a phone.

   The scrim is a left-to-right gradient, heaviest on the left where the type
   sits on a wide screen. Narrow the viewport to 375px and the whole of it
   falls inside the .92 end of that gradient — the picture was underneath the
   entire time, blacked out. On top of a brightness of .42 it read as a plain
   black panel.

   On a phone the copy stacks down the middle instead of sitting to one side,
   so the gradient turns vertical: the image is allowed to show at the top and
   darkens into the text below it. */
@media (max-width: 48rem) {
  .closer__media img { filter: saturate(.6) brightness(.6); transform: scale(1.06); }
  .closer__scrim {
    background: linear-gradient(to bottom,
      rgba(7, 7, 7, .42) 0%,
      rgba(7, 7, 7, .62) 38%,
      rgba(7, 7, 7, .86) 72%,
      rgba(7, 7, 7, .94) 100%);
  }
  .closer__media::after {
    background: radial-gradient(140% 80% at 50% 34%,
      transparent 45%, rgba(0, 0, 0, .28) 88%, rgba(0, 0, 0, .5) 100%);
  }
}

/* The month arrows are 30px, which is fine for a cursor and too small for a
   thumb. On touch they get the 44px they need. */
@media (max-width: 60rem) {
  .dp__nav { width: 44px; height: 44px; }
  .dp__clear { min-height: 2.75rem; display: inline-flex; align-items: center; }
}

/* --------------------------------------------------------------------------
   13. The phone edit

   A desktop visitor is often researching; a phone visitor is usually
   deciding, frequently in the evening, with a thumb. The full page ran to
   ten and a half screens and 765 words on a 375px phone, and the worst of
   the imbalance was that the process copy took 29% of the height while the
   films — the actual product — took 6%.

   So the phone gets an edit, not a different site: see the work, see the
   price, book it. Nothing is rewritten and nothing is duplicated; the
   supporting prose is simply not shown at this width, and every word of it
   is still one tap away on About, Pricing and Enquire, where someone who
   wants the detail will go looking for it.
   -------------------------------------------------------------------------- */

.mprice { display: none; }

/* Section rhythm on a phone.

   --bay is clamp(5rem, 13vh, 10rem), which on an 812px phone resolves to
   about 105px top and bottom — 210px of empty page at every boundary. That
   reads as generous on a desktop, where sections are wide; stacked in a
   375px column on a page we have just cut in half it reads as a void, and
   it was adding roughly four hundred pixels of pure scrolling. Still a
   clear beat between sections, just not a chasm. */
@media (max-width: 48rem) {
  :root { --bay: clamp(3.5rem, 9vh, 5.5rem); }
}

@media (max-width: 48rem) {
  .mprice { display: block; padding-block: var(--bay); }
  .mprice h2 { margin-block: .6rem 1.8rem; }

  .mprice__list { display: grid; margin-bottom: 1.6rem; }

  .mprice__row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    padding-block: 1.15rem;
    border-top: 1px solid var(--hair-soft);
    min-height: 3.25rem;
  }
  .mprice__row:last-child { border-bottom: 1px solid var(--hair-soft); }

  .mprice__name {
    font-family: var(--serif);
    font-weight: 300;
    font-size: 1.375rem;
    color: var(--bone);
  }
  /* The figure is the reason the section exists, so it is not the quietest
     thing on the row. "From" stays small and dim; the number does not. */
  .mprice__fig {
    font-family: var(--serif);
    font-weight: 300;
    font-size: 1.375rem;
    color: var(--bone);
    white-space: nowrap;
  }
  .mprice__fig em {
    font-style: normal;
    font-family: var(--sans);
    font-size: .5625rem;
    font-weight: 500;
    letter-spacing: .22em;
    text-transform: uppercase;
    color: var(--bone-3);
    margin-right: .45rem;
  }

  /* --- What the phone does not show ------------------------------------

     #ch-keep is the "we don't just film weddings" manifesto. Its job is the
     emotional opening, and on a phone the hero and the closing section
     already do that job twice over.

     The reason and step bodies go, but their headings stay: four short
     lines each still say what Hatch is and how it works, without the three
     hundred words underneath.

     The filmmaker section keeps its heading and its button through to the
     About page, and loses the biography in between. */
  #ch-keep { display: none; }

  #ch-why .reason .body,
  #ch-how .step__body .body { display: none; }

  #ch-why .reasons { gap: 1.4rem; }
  #ch-why .reason { display: flex; align-items: baseline; gap: .9rem; }
  #ch-why .reason h3 { font-size: 1.0625rem; }

  #ch-how .process__steps { gap: 1.1rem; }
  #ch-how .step__body h3 { font-size: 1.0625rem; margin-bottom: 0; }

  #ch-who .body p:not(:first-child) { display: none; }
  #ch-who .moments { display: none; }

  /* The films speak for themselves here; the paragraph introducing them is
     sixty-five words standing between a phone and the work. */
  #ch-films .body { display: none; }

  /* The films are the product and get the room the prose gave back. */
  .reel__cell { width: min(86vw, 28rem); }
}

/* --------------------------------------------------------------------------
   14. The odd-one-out card on the films grid

   Two columns means an odd number of films leaves the last cell empty. That
   cell is prime space — someone who has just watched the work is as warm as
   they will ever be — so it asks the obvious question instead of sitting
   blank. films.html only renders it when the count is odd: at six films
   there is no gap, and a form would be taking room the work has earned.
   -------------------------------------------------------------------------- */

.film-cta {
  aspect-ratio: 16 / 10;
  border: 1px solid var(--hair);
  background: var(--ink-1);
  display: grid;
  align-content: center;
  padding: clamp(1.4rem, 2.6vw, 2.2rem);
  container-type: inline-size;
}

.film-cta__in { display: grid; gap: .5rem; }

.film-cta__lede {
  font-size: .875rem;
  line-height: 1.45;
  color: var(--bone-2);
  max-width: 34ch;
  /* Without this it broke after "if I'm", leaving "free." alone on line two. */
  text-wrap: balance;
}

.film-cta__form { display: grid; gap: .1rem; }
.film-cta__pair { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; }

/* Everything here is tuned to one constraint: the card has to fit the same
   16:10 cell a film occupies. If it grows, it drags the row taller and the
   film beside it ends up floating in empty space — which is the hole this
   was built to close. So the fields lose the generous padding they get on
   the enquiry page, and the button loses its big minimum height. */
.film-cta .field input { padding-block: .55rem .4rem; font-size: .875rem; }

/* iOS zooms the whole page when a focused input is under 16px, and this rule
   sat at a higher specificity than the 16px floor in the phones section. The
   card is hidden on phones today, so this was latent rather than live. */
@media (max-width: 48rem) {
  .film-cta .field input { font-size: 1rem; }
}
.film-cta .field__label { font-size: .5rem; letter-spacing: .2em; }
.film-cta .field__err { font-size: .625rem; }
.film-cta .btn {
  margin-top: .7rem;
  justify-content: space-between;
  --pad: .8rem;
  min-height: 0;
  font-size: .625rem;
}

.film-cta__thanks { display: grid; align-content: center; min-height: 8rem; }

/* Below 48rem the grid is a single column, so there is no gap to fill and no
   reason to put a second form on a page that already carries a standing
   enquiry bar. */
@media (max-width: 48rem) {
  .film-cta { display: none; }
}

/* A narrow card stacks the name and email rather than squeezing them. */
@container (max-width: 30rem) {
  .film-cta__pair { grid-template-columns: 1fr; gap: .35rem; }
}

/* The line naming Connor above the headline.

   At label size and 58% bone it read as a caption — technically present,
   easy to miss. It is the only place on the page that says who he is, so it
   gets full bone, a size up, tighter tracking so the eye takes it in as a
   phrase rather than letter by letter, and a gold rule to anchor it. */
.hero__kicker {
  font-size: .8125rem;
  letter-spacing: .18em;
  color: var(--bone);
  /* It sits above the sub-text rather than above the headline, so the space
     it needs is the gap inside a block, not between two. */
  margin-bottom: .75rem;
}

@media (max-width: 48rem) {
  .hero__kicker {
    font-size: .6875rem;
    letter-spacing: .14em;
    margin-bottom: .6rem;
  }
}

/* --------------------------------------------------------------------------
   The opening still

   The hero holds on Connor for five seconds, then dissolves into the film:
   the person before the work. The pause doubles as buffer time for the video
   underneath, which is already loading.

   He sits to the right with the type to his left, the way the mockup had it.
   The panel behind him is solid ink so the video does not show through and
   fight the portrait, and the photograph's left edge is masked into that ink
   rather than butting against it — this is an outdoor shot of a bright manor,
   so a hard vertical seam would read as a pasted-on cutout.
   -------------------------------------------------------------------------- */

.hero__intro {
  position: absolute;
  inset: 0;
  z-index: 1;
  background: var(--ink);
  transition: opacity 1.5s var(--ease);
}

.hero__intro img {
  position: absolute;
  top: 0; right: 0;
  height: 100%;
  /* Narrower than the hero, so the whole frame is not filled with one face —
     "zoomed out" is really about how much of the hero he occupies. */
  width: clamp(20rem, 44vw, 44rem);
  object-fit: cover;
  object-position: 50% 16%;
  filter: saturate(.72) contrast(1.06) brightness(.82);
  -webkit-mask-image: linear-gradient(90deg, transparent 0%, #000 34%);
          mask-image: linear-gradient(90deg, transparent 0%, #000 34%);
}

/* Set by app.js once the five seconds are up. */
.hero__intro.is-gone { opacity: 0; pointer-events: none; }

/* On a phone the type stacks over the whole width, so there is no left-hand
   column to keep clear: he takes the frame, pushed right and up so his face
   clears the headline. */
@media (max-width: 48rem) {
  .hero__intro img {
    width: 100%;
    object-position: 72% 12%;
    -webkit-mask-image: linear-gradient(180deg, #000 42%, transparent 96%);
            mask-image: linear-gradient(180deg, #000 42%, transparent 96%);
  }
}

@media (prefers-reduced-motion: reduce) {
  .hero__intro { transition: opacity .4s linear; }
}

/* --------------------------------------------------------------------------
   The mark on displayed photographs

   Only on pictures that are presented as pictures — the editorial stills in
   a split, and the framed still on About. Deliberately not on the hero or
   the closing image: those are backgrounds with type sitting over them, and
   a second piece of text in the corner would be competing with the headline.
   The film cards are also left alone, since they already carry the couple's
   name and a running time.

   A drop shadow rather than a scrim, because these photographs vary from a
   dark manor to a bright confetti throw and a panel would show as a box on
   the light ones.
   -------------------------------------------------------------------------- */

.split__media,
.still__frame { position: relative; }

.split__media::after,
.still__frame::after {
  content: 'Hatch Videography';
  position: absolute;
  right: clamp(.85rem, 1.6vw, 1.4rem);
  bottom: clamp(.7rem, 1.4vw, 1.15rem);
  font-size: .5625rem;
  font-weight: 500;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: rgba(237, 237, 235, .82);
  text-shadow: 0 1px 6px rgba(0, 0, 0, .55);
  pointer-events: none;
}

@media (max-width: 48rem) {
  .split__media::after,
  .still__frame::after { font-size: .5rem; letter-spacing: .18em; }
}

/* ==========================================================================
   The enquiry form

   `.form` and `.form__pair` carried no rules whatsoever, so the contact page
   fell back to bare block layout. Two things were wrong with that. The only
   thing separating one field's underline from the next field's label was the
   input's own top padding, so the label sat on the line above it. And the
   pair wrapper never formed two columns at any width — name and company,
   email and phone, date and location all ran full width in a single stack.

   `.aside__pair` on the homepage card always had these rules; the contact
   page's equivalents were simply never written.
   ========================================================================== */

.form {
  display: grid;
  gap: clamp(1.6rem, 3.4vh, 2.3rem);
}

.form__pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(1.25rem, 3vw, 2rem);
}

/* One column before the two halves get too narrow to read. Matches the
   breakpoint `.aside__pair` uses so the two forms collapse together. */
@media (max-width: 46rem) {
  .form__pair { grid-template-columns: 1fr; }
}

/* The grid gap already spaces every direct child, so the fieldset and the
   closing note need no margins of their own. */
.form fieldset { margin: 0; padding: 0; }

/* ==========================================================================
   15 · The light table  (VIDEOGRAPHY ONLY — not in the weddings stylesheet)

   The work browser on /work. Disciplines down the left, one big framed still
   in the middle, a strip of thumbnails down the right.

   Three of Elliot's references collapsed into one component, because they
   are all the same idea — a dark room where a light picks out one frame at a
   time. The contact-sheet layout, the slight perspective on the strip, and a
   spotlight that follows the cursor. The last one earns its place here more
   than anywhere else on the site: lighting is the job.
   ========================================================================== */

.lt {
  display: grid;
  grid-template-columns: minmax(8rem, 11rem) minmax(0, 1fr) 6.5rem;
  gap: clamp(1.5rem, 3.5vw, 3.5rem);
  align-items: center;
}

/* ---- disciplines ---------------------------------------------------- */

.lt__cats { display: grid; align-content: center; gap: .1rem; }

.lt__cat {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: .6rem;
  width: 100%;
  padding: .55rem 0;
  background: none;
  border: 0;
  text-align: left;
  color: var(--bone-3);
  font: 400 .9375rem/1.4 var(--sans);
  cursor: pointer;
  transition: color var(--t-fast) var(--ease);
}
.lt__cat em {
  font: 500 .5625rem/1 var(--mono);
  font-style: normal;
  letter-spacing: .1em;
  color: var(--bone-4);
  transition: color var(--t-fast) var(--ease);
}
.lt__cat:hover { color: var(--bone-2); }
.lt__cat.is-on { color: var(--bone); }
.lt__cat.is-on em { color: var(--accent); }

/* ---- the stage ------------------------------------------------------ */

.lt__stage { display: grid; gap: 1.1rem; min-width: 0; }

.lt__frame {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  padding: 0;
  background: #000;
  border: 0;
  overflow: hidden;
  cursor: pointer;

  /* Where the light is. Set by lighttable.js on pointermove; only ever read
     by the mask below, so moving it costs a composite and not a reflow. */
  --mx: 50%;
  --my: 50%;
}

.lt__frame img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  transition: opacity .32s var(--ease);
}

/* The underlit copy. Always there, so the frame is never black. */
.lt__dim { filter: brightness(.38) saturate(.55) contrast(1.05); }

/* The lit copy, revealed only where the light falls. */
.lt__lit {
  opacity: 0;
  -webkit-mask-image: radial-gradient(circle 15rem at var(--mx) var(--my),
                        #000 0%, rgba(0,0,0,.85) 34%, transparent 70%);
          mask-image: radial-gradient(circle 15rem at var(--mx) var(--my),
                        #000 0%, rgba(0,0,0,.85) 34%, transparent 70%);
  -webkit-mask-repeat: no-repeat;
          mask-repeat: no-repeat;
}
.lt__frame.is-lit .lt__lit { opacity: 1; }

/* No cursor to follow — a phone, a tablet, or reduced motion. The still is
   simply lit, with no mask and nothing to chase. */
.lt__frame.is-flat .lt__dim { filter: none; }
.lt__frame.is-flat .lt__lit { display: none; }

/* The cross-fade between films. */
.lt__frame.is-out img { opacity: 0; }
.lt__frame.is-out .lt__dim { opacity: .25; }

/* Corner brackets, the same marks the hero and the film cards carry. */
.lt__brackets i {
  position: absolute;
  width: 14px; height: 14px;
  border: 1px solid rgba(237, 237, 235, .5);
  transition: border-color var(--t-fast) var(--ease);
}
.lt__brackets i:nth-child(1) { top: 12px; left: 12px;  border-right: 0; border-bottom: 0; }
.lt__brackets i:nth-child(2) { top: 12px; right: 12px; border-left: 0;  border-bottom: 0; }
.lt__brackets i:nth-child(3) { bottom: 12px; left: 12px;  border-right: 0; border-top: 0; }
.lt__brackets i:nth-child(4) { bottom: 12px; right: 12px; border-left: 0;  border-top: 0; }
.lt__frame:hover .lt__brackets i { border-color: var(--accent); }

.lt__play {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%) scale(.92);
  width: 4.25rem; height: 4.25rem;
  display: grid; place-items: center;
  border: 1px solid rgba(237, 237, 235, .6);
  border-radius: 50%;
  color: var(--bone);
  background: rgba(7, 7, 7, .3);
  -webkit-backdrop-filter: blur(6px);
          backdrop-filter: blur(6px);
  opacity: 0;
  transition: opacity var(--t-fast) var(--ease), transform var(--t-fast) var(--ease);
  pointer-events: none;
}
.lt__play svg { width: 20px; height: 20px; margin-left: 3px; }
.lt__frame:hover .lt__play,
.lt__frame:focus-visible .lt__play { opacity: 1; transform: translate(-50%, -50%) scale(1); }

.lt__meta {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1.5rem;
}
.lt__title { font-size: clamp(1.05rem, 1.6vw, 1.35rem); color: var(--bone); line-height: 1.25; }
.lt__sub   { font-size: .75rem; letter-spacing: .16em; text-transform: uppercase; color: var(--bone-4); margin-top: .4rem; }
.lt__count { display: flex; align-items: center; gap: .5rem; white-space: nowrap; color: var(--bone-3); }
.lt__count i { display: block; width: 1.6rem; height: 1px; background: var(--hair); }
.lt__run { color: var(--bone-4); }

/* ---- the filmstrip -------------------------------------------------- */

.lt__strip {
  display: grid;
  gap: .55rem;
  max-height: 32rem;
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: none;
  /* The conveyor, borrowed from the "every : second" reference: the strip
     sits on a slight perspective and each thumbnail is turned a few degrees
     away until it is the selected one, which swings flat. */
  perspective: 800px;
}
.lt__strip::-webkit-scrollbar { display: none; }

.lt__thumb {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 16 / 9;
  padding: 0;
  background: #0b0b0b;
  border: 1px solid transparent;
  overflow: hidden;
  cursor: pointer;
  transform: rotateY(-14deg) scale(.94);
  transform-origin: right center;
  opacity: .5;
  transition: transform .45s var(--ease), opacity .45s var(--ease),
              border-color var(--t-fast) var(--ease);
}
.lt__thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lt__thumb:hover { opacity: .85; transform: rotateY(-7deg) scale(.97); }
.lt__thumb.is-on {
  transform: none;
  opacity: 1;
  border-color: var(--accent);
}

/* Nothing renders until the data lands, so the skeleton never flashes. */
.lt { opacity: 0; transition: opacity .5s var(--ease); }
.lt.is-ready { opacity: 1; }

/* ---- phones and tablets --------------------------------------------- */

@media (max-width: 62rem) {
  /* Stage first, then the strip laid on its side under it, then the
     disciplines. A vertical rail beside a full-width still does not fit, and
     a horizontal one is the thing a thumb can actually flick through. */
  .lt {
    grid-template-columns: 1fr;
    gap: 1.25rem;
  }
  .lt__stage { order: 1; }
  .lt__strip { order: 2; }
  .lt__cats  { order: 3; }

  .lt__strip {
    grid-auto-flow: column;
    grid-auto-columns: 42vw;
    max-height: none;
    overflow-x: auto;
    overflow-y: hidden;
    perspective: none;
    padding-bottom: .35rem;
    scroll-snap-type: x mandatory;
  }
  .lt__thumb {
    transform: none;
    opacity: .55;
    scroll-snap-align: start;
  }
  .lt__thumb:hover { transform: none; }

  /* Disciplines become a row of chips, sized for a thumb. */
  .lt__cats {
    grid-auto-flow: column;
    grid-auto-columns: max-content;
    justify-content: start;
    gap: .5rem;
    overflow-x: auto;
    scrollbar-width: none;
  }
  .lt__cats::-webkit-scrollbar { display: none; }
  .lt__cat {
    min-height: 2.75rem;
    align-items: center;
    padding: .5rem .9rem;
    border: 1px solid var(--hair);
    font-size: .8125rem;
    white-space: nowrap;
  }
  .lt__cat.is-on { border-color: var(--bone-4); }

  .lt__play { opacity: 1; transform: translate(-50%, -50%) scale(1); width: 3.5rem; height: 3.5rem; }
}

/* ==========================================================================
   16 · The phone edit, continued  (VIDEOGRAPHY ONLY)

   Section 13 above is inherited from the wedding site and only half applies
   here, because it targets that site's section ids. #ch-why, #ch-how and
   #ch-who match and are already trimmed; #ch-keep and #ch-films do not exist
   on this site — the equivalents are #ch-range and #ch-work.

   It also only ever covered the homepage. Measured on a 375px phone, the
   inner pages were carrying more prose than the homepage ever did:
   services 547 words over 7.3 screens, about 434 over 7.1. Someone deciding
   on a sofa reads none of it.

   The rule throughout is the same as Section 13: nothing is rewritten and
   nothing is duplicated. Headings, lists and every call to action stay —
   they carry the argument on their own — and the supporting prose is simply
   not shown at this width. Every word is still there on a wider screen.
   ========================================================================== */

@media (max-width: 48rem) {

  /* --- Home ------------------------------------------------------------

     #ch-range lists the disciplines. The hero directly above it already
     says "brands, sport, events and screen", and /work now opens on chips
     that name the same five. Three statements of the same fact inside one
     thumb-scroll, and this was the longest of them at 721px. */
  #ch-range { display: none; }

  /* Thirty words standing between a phone and the films. */
  #ch-work .sec__head .body { display: none; }

  /* --- Work ------------------------------------------------------------

     The craft list stays: five short lines that say what is in every job.
     The two paragraphs introducing them do not. */
  #w-craft .body { display: none; }
  #w-craft .split__sticky { position: static; }

  /* --- Services --------------------------------------------------------

     The package cards are the product and are untouched. What goes is the
     prose under each of the five "what you get" headings — the headings
     themselves already read as a feature list — and the argument about
     cheap films, which keeps its list and its closing line. */
  #s-included .reason .body { display: none; }
  #s-included .reasons { gap: 1.4rem; }
  #s-included .reason { display: flex; align-items: baseline; gap: .9rem; }
  #s-included .reason h3 { font-size: 1.0625rem; }
  /* The card that ends the grid is a call to action, so it keeps its copy. */
  #s-included .reason--cta { display: grid; }
  #s-included .reason--cta .body { display: block; }

  #s-value .body { display: none; }
  #s-value .split__sticky { position: static; }

  /* --- About -----------------------------------------------------------

     Connor's own page, so the opening stays whole. Below it the headings
     and the disciplines list carry the story; the three long prose blocks
     underneath them are the ones nobody thumbs through. */
  #a-how .body { display: none; }
  #a-how .split__sticky { position: static; }

  #a-range .body p:not(:first-child) { display: none; }
  #a-post .body p:not(:first-child) { display: none; }
}

/* ==========================================================================
   The thank-you panel

   Spacing only. The reveal — a clip-path wipe — is owned by the inline style
   in contact.html on both sites, and stays there.

   The panel is a grid with a single uniform gap, which gave the heading, the
   line under it, the paragraph and the button the same distance apart and
   made the whole thing read as one block instead of a hierarchy. The gap is
   dropped to zero here and the rhythm comes from the children, so each step
   down the panel is a different size.
   ========================================================================== */

.thanks {
  display: grid;
  justify-items: start;
  max-width: 32rem;
  gap: 0;
}
.thanks .display-m { margin-bottom: .55rem; }
.thanks .lede      { margin-bottom: 1.15rem; color: var(--bone-2); }
.thanks .body      { margin-bottom: 2rem; max-width: 27rem; }
