/* ============================================================================
   MAIN — layout, surfaces, components and motion for Day Bespoke Carpentry.
   Type lives in type.css and nowhere else: this file sets no font-size,
   font-weight, font-family, letter-spacing or text-transform, and every text
   colour is a var() so a dark section swaps tokens instead of adding treatments.

   The look (Joseph, 9 Oct 2026, from the hero concepts he chose and the UK
   makers they echo — Smallbone, Artichoke, Humphrey Munson): evening light.
   Cinematic full-screen photographs with the words on their dark side, a large
   serif headline, small spaced capitals for labels, thin bronze lines, dark
   feature sections alternating with white reading sections, and slow motion
   (a settling zoom, sections rising into view, a silent workshop reel).
   The client's colours throughout: black, white, a little bronze.
   ========================================================================== */

:root {
  /* Jack's colours, strictly: black, white, a little bronze. Greys are only
     tints between the black and the white; there is one bronze. */
  --dark  : #0D0D0D;             /* black: every dark section                  */
  --rule  : #E5E5E5;             /* hairline on white                          */
  --bronze: #B08D57;             /* the one bronze                             */
  --edge  : #8A8A8A;             /* form borders, 3.5:1 on white (UI 3:1)      */

  --btn-bg : var(--ink);
  --btn-ink: #FFFFFF;
  --on-bronze: #121212;          /* text on the bronze button: 6.1:1 */

  --gutter: clamp(1.25rem, 4.5vw, 4rem);
  --wrap  : 80rem;
  --band  : clamp(5rem, 10vw, 9rem);
  --header: 5.25rem;
  --ease  : cubic-bezier(0.2, 0.7, 0.2, 1);
}

/* ---- One continuous page colour -------------------------------------------
   Every section says whether it is dark or light (data-theme, set by
   build_pages.py). js/motion.js moves the page to that colour as the section
   reaches the middle of the screen, and the colours glide over ~0.7s: the
   background, every text colour and every hairline together, so there is never
   a hard line where one section stops and the next starts. The colour tokens
   are registered so the browser can animate them. Without JavaScript each dark
   section simply paints its own background. Reduced motion: instant.
   The background, cards and hairlines glide (linear, so the midpoint is
   predictable) over 0.7s; the text and button colours change in one step, at
   the moment the #121212 ink and white are equally readable on it (#7A7A7A,
   4.3:1 either way). Gliding both made text meet its background at 1.04:1. */
@property --page     { syntax: "<color>"; inherits: true; initial-value: #FFFFFF; }
@property --ink      { syntax: "<color>"; inherits: true; initial-value: #121212; }
@property --ink-soft { syntax: "<color>"; inherits: true; initial-value: #4A4A4A; }
@property --link     { syntax: "<color>"; inherits: true; initial-value: #121212; }
@property --rule     { syntax: "<color>"; inherits: true; initial-value: #E5E5E5; }
@property --btn-bg   { syntax: "<color>"; inherits: true; initial-value: #121212; }
@property --btn-ink  { syntax: "<color>"; inherits: true; initial-value: #FFFFFF; }
@property --card     { syntax: "<color>"; inherits: true; initial-value: #F6F6F6; }
html {                           /* towards white: text flips at 45% (0.32s) */
  --page: #FFFFFF;
  --card: #F6F6F6;               /* a review card on white */
  transition: --page 0.7s linear, --card 0.7s linear, --rule 0.7s linear,
              --ink 0s linear 0.32s, --ink-soft 0s linear 0.32s, --link 0s linear 0.32s,
              --btn-bg 0s linear 0.32s, --btn-ink 0s linear 0.32s;
}
html[data-theme="dark"] {        /* towards black: text flips at 55% (0.39s) */
  transition: --page 0.7s linear, --card 0.7s linear, --rule 0.7s linear,
              --ink 0s linear 0.39s, --ink-soft 0s linear 0.39s, --link 0s linear 0.39s,
              --btn-bg 0s linear 0.39s, --btn-ink 0s linear 0.39s;
}

/* A dark section swaps the ink tokens. On #0D0D0D: white 19.4:1 · soft grey 10.7:1. */
html[data-theme="dark"], html:not(.js) [data-theme="dark"], .on-dark {
  --ink     : #FFFFFF;
  --ink-soft: #BDBDBD;
  --link    : #FFFFFF;
  --rule    : rgba(255, 255, 255, 0.14);
  --btn-bg  : #FFFFFF;
  --btn-ink : #0D0D0D;
  --card    : #161616;           /* a review card on black */
  --page    : #0D0D0D;
  color: var(--ink);
}
.on-dark, html:not(.js) [data-theme="dark"] { background: var(--dark); }

*, *::before, *::after { box-sizing: border-box; }
html { scroll-padding-top: calc(var(--header) + 1rem); background: var(--page); }
/* on a phone the call bar covers the foot of the screen: focus and jumps stop above it */
@media (max-width: 47.4375em) { html { scroll-padding-bottom: 5.75rem; } }
/* lock the root only: hiding body's overflow made body the scroll box, and the
   sticky header (with the menu in it) jumped to the top of the document */
html.menu-locked { overflow: hidden; }
body { margin: 0; background: transparent; color: var(--ink); }
img, video { display: block; max-width: 100%; height: auto; }
ul, ol { padding: 0; list-style: none; }

:focus-visible { outline: 2px solid var(--bronze); outline-offset: 3px; }
::selection { background: var(--bronze); color: #121212; }
/* the parts the browser draws, in Jack's colours too: a bronze caret, and a
   scrollbar of the page's own black or white with a grey thumb */
input, textarea, select { caret-color: var(--bronze); }
html { scrollbar-color: var(--ink-soft) var(--page); }

.skip-link {
  position: absolute; left: 1rem; top: -4rem; z-index: 100;
  padding: 0.75rem 1rem; background: var(--dark); color: #FFFFFF;
}
.skip-link:focus { top: 1rem; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ---- Layout ------------------------------------------------------------- */
.wrap   { width: min(100% - 2 * var(--gutter), var(--wrap)); margin-inline: auto; }
/* One left edge for the whole site: reading sections start where the hero copy
   starts, and keep a comfortable 46rem line inside it. */
.narrow { width: min(100% - 2 * var(--gutter), var(--wrap)); margin-inline: auto; }
.narrow > * { max-width: 46rem; }
.band   { padding-block: var(--band); }
.band--tight { padding-block: calc(var(--band) * 0.6); }

.intro + .band { padding-top: 0; }
.stack > * + * { margin-top: 1.25rem; }
.mt-lg { margin-top: 2.25rem; }
.stack > h2:not(:first-child) { margin-top: 3.5rem; }
.stack > .eyebrow + h2 { margin-top: 1.1rem; }

/* The eyebrow: small spaced capitals in bronze, led by a thin bronze line. */
.eyebrow { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1rem; max-width: 100%; color: var(--ink-soft); }
.eyebrow::before { content: ""; width: 2.5rem; height: 1px; background: var(--bronze); flex: none; }
.hero .eyebrow, .feature .eyebrow { color: var(--ink); }   /* on a photograph: white */

/* Intro: a large lead on its own, the opening of a page's reading part. */
.intro p { max-width: 46rem; }

/* ---- Wood grain ---------------------------------------------------------------
   The grain inside the logo's lettering, drawn as line art by tools/build_grain.py,
   laid very faintly behind a few white sections (Joseph, 12 Oct 2026). Black lines
   at 7% read as a pale grey texture that melts away at the top and bottom; text
   on it keeps its contrast (body text above 15:1). One switch removes it:
   WOOD_GRAIN in tools/build_pages.py. */
.grain { position: relative; isolation: isolate; }
.grain::before {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: url("/images/grain.svg?v=40d2146b") center / cover no-repeat;
  opacity: 0.07;
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 20%, #000 80%, transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0, #000 20%, #000 80%, transparent 100%);
}

/* ---- Links and buttons -------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.75rem;
  min-height: 52px; padding: 0.8rem 1.75rem; border-radius: 2px;
  background: var(--btn-bg); color: var(--btn-ink);
  border: 1px solid var(--btn-bg); text-decoration: none; cursor: pointer;
  transition: background-color 0.4s var(--ease), color 0.4s var(--ease), border-color 0.4s var(--ease);
}
.btn:hover { background: transparent; color: var(--ink); }
.btn--quiet { background: transparent; color: var(--ink); border-color: var(--ink); }
.btn--quiet:hover { background: var(--ink); color: var(--btn-ink); }
.btn--bronze { background: var(--bronze); border-color: var(--bronze); color: var(--on-bronze); }
.btn--bronze:hover { background: transparent; color: var(--ink); }
.btn svg { width: 1.1em; height: 1.1em; flex: none; }
.actions { display: flex; flex-wrap: wrap; gap: 0.85rem; align-items: center; margin-top: 2.25rem; }
@media (max-width: 399px) { .actions .btn { padding-inline: 1.1rem; } }

/* A text link in the label style, with a line that draws out on hover. */
.more {
  display: inline-flex; align-items: center; gap: 0.9rem; margin-top: 1.5rem; min-height: 44px;
  color: var(--ink); text-decoration: none;
}
.more::after { content: ""; width: 2.25rem; height: 1px; background: var(--bronze); transition: width 0.5s var(--ease); }
.more:hover::after { width: 3.5rem; }

/* ---- Header ------------------------------------------------------------- */
/* A clean bar after the reference Joseph chose (12 Oct 2026): the logo on the
   left, the six main pages centred in plain text, the call on the right as plain
   text with a bronze phone mark, no boxes. Clear over every page's photograph;
   nav.js adds .is-scrolled (and .menu-open) to bring the dark bar back. No
   backdrop blur: it made image-heavy pages stutter while scrolling. */
.site-header {
  position: sticky; top: 0; z-index: 50;
  background: rgba(13, 13, 13, 0.96);
  transition: background-color 0.5s var(--ease);
}
.site-header.over-photo { background: transparent; }
.site-header.over-photo.is-scrolled, .site-header.over-photo.menu-open { background: rgba(13, 13, 13, 0.96); }
.header-inner {
  display: flex; align-items: center; justify-content: space-between; gap: 2rem;
  width: min(100% - 2 * var(--gutter), var(--wrap)); margin-inline: auto; min-height: var(--header);
}
.site-logo img { width: auto; height: 60px; }   /* bigger (Joseph, 12 Oct 2026); 72px on desktop */
.site-nav { display: none; }
.site-nav a, .mobile-menu a { text-decoration: none; }
.site-nav a { position: relative; padding-block: 0.6rem; color: var(--ink); white-space: nowrap; }
.site-nav a::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0.2rem; height: 1px;
  background: var(--bronze); transform: scaleX(0); transform-origin: left; transition: transform 0.5s var(--ease);
}
.site-nav a:hover::after, .site-nav a[aria-current]::after { transform: scaleX(1); }
.header-actions { display: flex; align-items: center; gap: 1.25rem; }
/* the call: plain text, a small bronze phone mark, a bronze line on hover */
.header-call { display: none; align-items: center; gap: 0.55rem; min-height: 44px; color: var(--ink); text-decoration: none; white-space: nowrap; }
.header-call svg { width: 1.05em; height: 1.05em; flex: none; color: var(--bronze); }
.header-call:hover { text-decoration: underline; text-decoration-color: var(--bronze); text-underline-offset: 0.3em; }
/* Instagram and Facebook in the top right corner (Jack, 31 Oct 2026): small white
   marks in the phone mark's thin line, bronze on hover. Each a 40 x 44 target. On
   phones they sit beside the menu button; below 360px there is no room, and the
   footer carries both links. */
.header-social { display: flex; align-items: center; }
.header-social a { display: inline-grid; place-items: center; width: 40px; height: 44px; color: var(--ink); transition: color 0.3s; }
.header-social svg { width: 18px; height: 18px; }
.header-social a:hover { color: var(--bronze); }
@media (max-width: 47.49em) { .header-actions { gap: 0.25rem; } }
@media (max-width: 22.49em) { .header-social { display: none; } }
.btn { text-align: center; overflow-wrap: normal; }
a[href^="tel:"]:not(.btn), a[href^="https://wa.me/"]:not(.btn) { white-space: nowrap; }   /* a number never splits over two lines */

.hamburger {
  display: inline-flex; flex-direction: column; justify-content: center; gap: 6px;
  width: 48px; height: 48px; padding: 0 11px; background: none; border: 0; cursor: pointer;
}
.hamburger span { display: block; height: 1px; background: var(--ink); transition: transform 0.3s var(--ease), opacity 0.3s; }
.hamburger[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.hamburger[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.hamburger[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

/* Phone menu: the links in the display serif, large, one per line. */
.mobile-menu { display: none; padding: 1rem var(--gutter) 2.5rem; background: transparent; }
/* the open menu hangs below the bar, out of the page flow, so opening and
   closing it never moves the page the visitor was reading */
.mobile-menu.is-open {
  display: block; position: absolute; inset: 100% 0 auto 0;
  max-height: calc(100dvh - var(--header)); overflow-y: auto; overscroll-behavior: contain;
  background: rgba(13, 13, 13, 0.96);
}
.mobile-menu .menu-link { display: block; padding-block: 0.45rem; color: var(--ink); }

/* The call shows from tablet width (phones have it in the bar at the foot).
   From 1200px the six links sit in the middle of the bar, centred on the page:
   the logo and the call take equal outer columns. Breakpoints in em, so a larger
   browser text size gets the phone layout sooner instead of a crowded bar. */
@media (min-width: 47.5em) { .header-call { display: inline-flex; } }
@media (min-width: 75em) {
  :root { --header: 6.25rem; }
  .header-inner { display: grid; grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); gap: 0 2rem; }
  .site-logo { justify-self: start; }
  .site-nav { display: flex; gap: 0 2.25rem; }
  .header-actions { justify-self: end; }
  .hamburger, .mobile-menu, .mobile-menu.is-open { display: none; }
  .site-logo img { height: 72px; }
}

/* ---- Breadcrumbs -------------------------------------------------------- */
.breadcrumbs ol { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.breadcrumbs li + li::before {
  content: ""; display: inline-block; width: 0.35rem; height: 0.35rem; margin-right: 0.6rem;
  border-top: 1px solid var(--ink-soft); border-right: 1px solid var(--ink-soft);
  transform: translateY(-0.1rem) rotate(45deg);
}
.breadcrumbs a { text-decoration: none; }
.breadcrumbs a:hover { text-decoration: underline; }

figcaption { margin-top: 0.75rem; }

/* ---- Hero: the photograph fills the first screen ---------------------------
   Every page opens the same way: the graded photograph to every edge, the
   header clear over it, and the words on its dark side (bottom on phones, left
   on desktop). The photo settles from a slow zoom as the page opens and the
   words rise into place. tools/contrast_on_photos.py measures every line on
   the rendered pages at six widths. */
.hero, .feature, .hero.on-dark, .feature.on-dark {
  position: relative; isolation: isolate; overflow: hidden;
  display: grid; align-content: end; background: transparent;
  --ink-soft: #E6E6E6;
}
.hero { min-height: 100svh; margin-top: calc(-1 * var(--header)); }
/* Edges that melt: the hero into what follows, every feature into both
   neighbours, so photographs flow into one another and into the page. */
.feature {
  -webkit-mask-image: linear-gradient(180deg, transparent 0%, #000 6%, #000 94%, transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0%, #000 6%, #000 94%, transparent 100%);
}
.hero--inner { min-height: min(92svh, 62rem); }
.hero--compact { min-height: min(78svh, 48rem); }
.hero--compact .hero__copy { padding-bottom: clamp(6.5rem, 10vh, 8rem); }
.hero__media, .feature__media { position: absolute; inset: 0; z-index: -2; overflow: hidden; margin: 0; }
.hero__media picture, .feature__media picture { display: block; width: 100%; height: 100%; }
.hero__media img, .feature__media img {
  width: 100%; height: 100%; max-width: none; object-fit: cover;
}
.hero::before {                 /* shade under the clear header */
  content: ""; position: absolute; inset: 0 0 auto 0; height: max(13rem, calc(var(--header) + 6rem)); z-index: -1; pointer-events: none;
  background: linear-gradient(180deg, rgba(13, 13, 13, 0.5) 0%, rgba(13, 13, 13, 0.2) 50%, rgba(13, 13, 13, 0) 100%);
}
@media (min-width: 47.5em) {     /* the call and the menu sit on the photograph: a deeper shade behind them */
  .hero::before { background: linear-gradient(180deg, rgba(13, 13, 13, 0.8) 0%, rgba(13, 13, 13, 0.66) 45%, rgba(13, 13, 13, 0.28) 72%, rgba(13, 13, 13, 0) 100%); }
}
.hero::after, .feature::after {
  content: ""; position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: linear-gradient(0deg, rgba(13, 13, 13, 0.9) 0%, rgba(13, 13, 13, 0.82) 32%, rgba(13, 13, 13, 0.5) 52%, rgba(13, 13, 13, 0.12) 68%, rgba(13, 13, 13, 0) 78%);
}
.hero--short::after, .feature--short::after {
  background: linear-gradient(0deg, rgba(13, 13, 13, 0.88) 0%, rgba(13, 13, 13, 0.78) 28%, rgba(13, 13, 13, 0.48) 46%, rgba(13, 13, 13, 0.1) 62%, rgba(13, 13, 13, 0) 72%);
}
/* where the words start: the same left edge as every section */
.hero, .feature { --edge-x: max(var(--gutter), (100% - var(--wrap)) / 2); }
/* an outlined button over a photograph carries a little smoked glass, so it
   reads on any photo without darkening the photo itself */
.hero .btn--quiet, .feature .btn--quiet { background: rgba(13, 13, 13, 0.74); }
.hero .btn--quiet:hover, .feature .btn--quiet:hover { background: var(--ink); }
.hero__copy, .feature__copy {
  width: min(100% - 2 * var(--gutter), var(--wrap)); margin-inline: auto;
  padding-block: calc(var(--header) + 4rem) clamp(6rem, 17vh, 11rem);
}
.hero__copy > * + *, .feature__copy > * + * { margin-top: 1.5rem; }
.hero__copy > * { max-width: 36rem; }
.hero__copy > h1 { max-width: 10em; }
.hero__copy > .actions, .hero__copy > .breadcrumbs { max-width: none; }
.hero__copy > .lead { max-width: 38rem; }
.hero__copy > .eyebrow + h1 { margin-top: 1.75rem; }
.hero__copy .lead { margin-top: 2rem; }
.accent { display: block; color: var(--bronze); }   /* bronze on black: 6.9:1 */
.phrase { white-space: nowrap; }
.hero .breadcrumbs { margin-bottom: 0.25rem; }
@media (max-width: 759px) {
  .hero .breadcrumbs { display: none; }   /* the trail stays in the schema */
  /* inner pages on a phone: the call alone; the bar at the foot carries the rest */
  .hero--inner .actions .btn--quiet { display: none; }
}

@media (min-width: 860px) {
  .hero, .feature { align-content: center; }
  .hero { align-content: end; }
  /* Desktop (11 Oct 2026, "brightened up to match" the services opening): the
     shade sits behind the words only, from just above the first line, fading out
     to the side and at the ends; the rest of the photograph keeps its daylight.
     The section itself keeps only a light floor where it melts into the page. */
  .hero::after, .feature::after, .hero--short::after, .feature--short::after,
  .feature--right::after, .feature--right.feature--short::after {
    background: linear-gradient(0deg, rgba(13, 13, 13, 0.3) 0%, rgba(13, 13, 13, 0) 22%);
  }
  .hero:not(.hero--plain) .hero__copy, .feature__copy { position: relative; }
  .hero__copy { --copy-top: calc(var(--header) + 4rem); }
  .feature__copy { --copy-top: clamp(5rem, 9vw, 8rem); }
  .hero:not(.hero--plain) .hero__copy::before, .feature__copy::before {
    content: ""; position: absolute; z-index: -1; pointer-events: none;
    top: calc(var(--copy-top) - 6rem); bottom: 0;
    left: calc(50% - 50vw); width: calc(50vw - 50% + 54rem);    /* screen edge to past the words */
    background: linear-gradient(90deg, rgba(13, 13, 13, 0.84) 0, rgba(13, 13, 13, 0.76) calc(100% - 14rem), rgba(13, 13, 13, 0) 100%);
    -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 6rem, #000 calc(100% - 3rem), transparent 100%);
            mask-image: linear-gradient(180deg, transparent 0, #000 6rem, #000 calc(100% - 3rem), transparent 100%);
  }
  .hero:not(.hero--plain) .hero__copy::before {      /* an opening's words run to its foot: no fade there */
    -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 6rem);
            mask-image: linear-gradient(180deg, transparent 0, #000 6rem);
  }
  .hero--short .hero__copy::before, .feature--short .feature__copy::before {
    background: linear-gradient(90deg, rgba(13, 13, 13, 0.76) 0, rgba(13, 13, 13, 0.66) calc(100% - 14rem), rgba(13, 13, 13, 0) 100%);
  }
  .feature--right .feature__copy::before {
    left: auto; right: calc(50% - 50vw); width: calc(50vw - 50% + 52rem);
    background: linear-gradient(270deg, rgba(13, 13, 13, 0.84) 0, rgba(13, 13, 13, 0.76) calc(100% - 14rem), rgba(13, 13, 13, 0) 100%);
  }
  .feature--right.feature--short .feature__copy::before {
    background: linear-gradient(270deg, rgba(13, 13, 13, 0.72) 0, rgba(13, 13, 13, 0.62) calc(100% - 14rem), rgba(13, 13, 13, 0) 100%);
  }
  /* the homepage's display headline runs wider than an inner page's: its shade
     reaches past the end of "Bespoke by nature." (bronze needs a dark ground) */
  .hero:not(.hero--inner):not(.hero--plain) .hero__copy::before {
    width: calc(50vw - 50% + 62rem);
    background: linear-gradient(90deg, rgba(13, 13, 13, 0.86) 0, rgba(13, 13, 13, 0.8) calc(100% - 14rem), rgba(13, 13, 13, 0) 100%);
  }
  .feature--right .feature__copy { display: grid; grid-template-columns: 1fr minmax(0, 34rem); }
  .feature--right .feature__copy > * { grid-column: 2; }
}

/* Phones (11 Oct 2026, brighter photos): the shade follows the words. It
   starts 6rem above the first line, wherever that falls, so the photograph
   above the words stays at full brightness and every line keeps its contrast. */
@media (max-width: 859px) {
  .hero:not(.hero--plain) .hero__copy, .feature__copy { position: relative; }
  .hero__copy { --copy-top: calc(var(--header) + 4rem); }
  .feature__copy { --copy-top: clamp(14rem, 40vh, 22rem); }
  .hero:not(.hero--plain) .hero__copy::before, .feature__copy::before {
    content: ""; position: absolute; z-index: -1; pointer-events: none;
    left: calc(-1 * var(--gutter)); right: calc(-1 * var(--gutter)); bottom: 0; top: calc(var(--copy-top) - 6rem);
    background: linear-gradient(0deg, rgba(13, 13, 13, 0.88) 0%, rgba(13, 13, 13, 0.8) calc(100% - 5rem), rgba(13, 13, 13, 0) 100%);
  }
  .hero::after, .feature::after, .hero--short::after, .feature--short::after {
    background: linear-gradient(0deg, rgba(13, 13, 13, 0.5) 0%, rgba(13, 13, 13, 0) 30%);
  }
}

/* The foot of every photo opening fades into the white page below it, under
   the buttons, never under the words (Joseph, 12 Oct 2026: "blending into a
   white page instead of black, throughout the site"). The words keep their own
   dark shade; every opening leaves at least 6.5rem below its last button. */
.hero:not(.hero--plain)::after, .hero.hero--short:not(.hero--plain)::after {
  background: linear-gradient(0deg, #FFFFFF 0, rgba(255, 255, 255, 0.9) 1.5rem, rgba(255, 255, 255, 0.4) 3.75rem, rgba(255, 255, 255, 0) 6rem) !important;
}

/* Openings without a photograph (privacy, AI facts, thank-you, 404) keep their
   own black, so their light type never meets a white page mid-glide; the
   bottom edge melts away under the last line. */
.hero.hero--plain {
  background: var(--dark);
  -webkit-mask-image: linear-gradient(180deg, #000 calc(100% - 3.5rem), transparent 100%);
          mask-image: linear-gradient(180deg, #000 calc(100% - 3.5rem), transparent 100%);
}

/* No photograph (privacy, AI facts, thank-you, 404): the same black first screen. */
.hero--plain { min-height: 0; }
.hero--plain::after { content: none; }
.hero--plain .hero__copy { padding-block: calc(var(--header) + clamp(4rem, 10vw, 7rem)) clamp(3rem, 7vw, 5rem); }

/* ---- Feature: a full-screen photograph with its words on the dark side ----- */
.feature { min-height: min(100svh, 58rem); }
.feature__copy { padding-block: clamp(14rem, 40vh, 22rem) clamp(6rem, 15vh, 10rem); }
.feature__copy > * { max-width: 34rem; }
.feature__copy > .actions { max-width: none; }
@media (min-width: 860px) { .feature__copy { padding-block: clamp(5rem, 9vw, 8rem); } }

/* ---- The workshop reel: Jack's own video, silent, looping in view ---------- */
/* The video fills its own half of the screen, edge to edge and the full height
   of the window, the words beside it (Joseph, 11 Oct 2026: "make the video
   bigger", twice). On phones it runs the full width, the words below. */
.reel-band { padding-block: 0; }
.reel { display: grid; align-items: center; }
.reel__copy { width: min(100% - 2 * var(--gutter), var(--wrap)); margin-inline: auto; padding-block: 3rem var(--band); }
.reel__media { position: relative; margin: 0; }
/* its edges melt into the black, top and bottom (and towards the words on desktop) */
.reel__media video {
  -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 9%, #000 91%, transparent 100%);
          mask-image: linear-gradient(180deg, transparent 0, #000 9%, #000 91%, transparent 100%);
}
@media (min-width: 53.75em) {
  .reel__media video {
    -webkit-mask-image: linear-gradient(180deg, transparent 0, #000 9%, #000 91%, transparent 100%),
                        linear-gradient(90deg, #000 82%, transparent 100%);
    -webkit-mask-composite: source-in;
            mask-image: linear-gradient(180deg, transparent 0, #000 9%, #000 91%, transparent 100%),
                        linear-gradient(90deg, #000 82%, transparent 100%);
            mask-composite: intersect;
  }
}
.reel__media video { width: 100%; aspect-ratio: 4 / 5; object-fit: cover; background: var(--dark); }
.reel__toggle, .hero__pause {
  position: absolute; right: 1rem; bottom: 1rem; width: 48px; height: 48px; padding: 0;
  display: grid; place-items: center; border-radius: 50%; cursor: pointer;
  border: 1px solid rgba(255, 255, 255, 0.7); background: rgba(13, 13, 13, 0.55); color: #FFFFFF;
}
.reel__toggle svg, .hero__pause svg { width: 16px; height: 16px; }
.hero__pause .icon-play { display: none; }
.hero__pause[aria-pressed="true"] .icon-play { display: block; }
.hero__pause[aria-pressed="true"] .icon-pause { display: none; }
.hero__pause[hidden] { display: none; }
/* Joseph, 12 Oct 2026: no pause button on the photo. It stays for keyboard and
   screen-reader users (WCAG 2.2.2 asks for a way to stop moving content) and
   appears only when reached with the Tab key. */
.hero .hero__pause:not(:focus-visible) { clip-path: inset(50%); opacity: 0; pointer-events: none; }

/* ---- The homepage opening: a slow crossfade between photos of one job ------
   (Joseph, 11 Oct 2026). One photo shows at a time; the next fades in over 1.5s
   every 4s with a barely-there zoom. Later slides load after the page; reduced
   motion keeps the first photo still and hides the pause button. */
.hero__slide { position: absolute; inset: 0; opacity: 0; transition: opacity 1.5s ease; }
.hero__slide.is-active { opacity: 1; }
.hero__slide picture { display: block; width: 100%; height: 100%; }
.hero .hero__pause { right: var(--gutter); bottom: clamp(1.25rem, 4vh, 2.5rem); z-index: 2; }
@media (prefers-reduced-motion: no-preference) {
  .hero__slide img { transition: transform 6s linear; }
  .hero__slide.is-active img { transform: scale(1.05); }
}
.reel__toggle .icon-play { display: none; }
.reel__toggle[aria-pressed="true"] .icon-play { display: block; }
.reel__toggle[aria-pressed="true"] .icon-pause { display: none; }
.reel__copy > * + * { margin-top: 1.5rem; }
.reel__copy > * { max-width: 34rem; }
/* The Which? Trusted Trader mark (Jack, 31 Oct 2026), in Which?'s own colours as
   its rules ask, 128px wide (Which?'s minimum is 105 x 85): under the video's
   words on the homepage, beside the profile link on About. */
.which-badge { display: inline-block; line-height: 0; }
.which-badge img { width: 8rem; height: auto; }
.reel__copy > .which-badge { display: block; width: max-content; margin-top: 2.75rem; }
.which-mark { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 2.25rem; }
.which-mark .more { margin-top: 0; }
@media (min-width: 53.75em) {   /* em: large text keeps one column */
  .reel { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .reel__media video { aspect-ratio: auto; height: min(100svh, 72rem); }
  .reel__copy { width: auto; margin: 0; padding: var(--band) var(--gutter) var(--band) clamp(3rem, 7vw, 8rem); }
}

/* ---- Pull quote: one review, large, centred on black ---------------------- */
.pull { max-width: 46rem; margin-inline: auto; text-align: center; }
.pull > svg { width: 2.75rem; height: auto; color: var(--bronze); margin: 0 auto 2rem; }
.pull .lead { margin-inline: auto; }
.pull blockquote p { color: var(--ink); }
.pull blockquote p + p { margin-top: 1rem; }
.pull figcaption { margin-top: 1.75rem; }

/* ---- Duo: the editorial two-column section — label and heading on the left,
   the content on the right (one column on phones). */
.duo { display: grid; gap: 2rem 5rem; }
.duo__head > * + * { margin-top: 1.1rem; }
.duo__body > * + * { margin-top: 1.25rem; }
.duo__body { max-width: 46rem; }
@media (min-width: 960px) {
  .duo { grid-template-columns: minmax(0, 5fr) minmax(0, 7fr); align-items: start; }
  .duo__head { position: sticky; top: calc(var(--header) + 2rem); }
}

/* ---- Media split: photograph to the page edge beside the copy, on white --- */
.media-split { display: grid; }
.media-split__photo { margin: 0; min-height: min(80vh, 46rem); max-height: 56rem; overflow: hidden; }
.media-split__photo img { width: 100%; height: 100%; object-fit: cover; }
.media-split__copy { padding: clamp(3.5rem, 8vw, 7rem) var(--gutter); align-self: center; max-width: 42rem; }
@media (min-width: 860px) {
  .media-split { grid-template-columns: 1fr 1fr; }
  .media-split--flip .media-split__photo { order: 2; }
  .media-split__copy { padding-inline: clamp(3rem, 7vw, 7rem); }
}

/* ---- Founder quote ---------------------------------------------------------- */
.statement--second { display: block; margin-top: 0.35rem; }
.founder blockquote { max-width: var(--col); }
.founder figcaption { margin-top: 1.25rem; }

/* ---- Trust line: four credentials between hairlines ------------------------ */
.trust-band { border-top: 0; }   /* no line: the opening dissolves straight into the trust points */
/* Desktop: the homepage opening leaves room for the four trust points, so they
   show on the first screen, and the headline sits higher (Joseph, 11 Oct 2026) */
@media (min-width: 860px) {
  .trust-band { padding-block: 1.5rem; }
  .hero:not(.hero--inner):not(.hero--plain) { min-height: calc(100svh - 10rem); }
  .hero:not(.hero--inner):not(.hero--plain) .hero__copy { padding-bottom: clamp(6.5rem, 10vh, 8rem); }
}
@media (min-width: 1100px) {
  .hero:not(.hero--inner):not(.hero--plain) { min-height: calc(100svh - 6rem); }
}
/* Phones: the homepage photos zoomed out (Joseph, 12 Oct 2026: "zoomed out a
   little to see more of the image"). Covering a tall, narrow screen showed only
   58% of each photo's width; the photo now sits at its own 4:5 shape across the
   full width (all of it shows) and melts into black behind the words. Phones
   only: from 760px the wide cut is used and fills the screen as before. */
@media (max-width: 759px) {
  .hero:not(.hero--inner):not(.hero--plain) { background: #0D0D0D; }
  .hero:not(.hero--inner):not(.hero--plain) .hero__media {
    bottom: auto; height: auto; aspect-ratio: 4 / 5;
    -webkit-mask-image: linear-gradient(180deg, #000 0, #000 70%, transparent 100%);
            mask-image: linear-gradient(180deg, #000 0, #000 70%, transparent 100%);
  }
  .hero:not(.hero--inner):not(.hero--plain) .hero__copy { padding-bottom: 6.75rem; }
}
.trust { display: grid; gap: 1.25rem; }
.trust li { display: flex; align-items: center; gap: 0.9rem; color: var(--ink-soft); }
.trust li::before { content: ""; width: 5px; height: 5px; transform: rotate(45deg); background: var(--bronze); flex: none; }
.trust a { display: inline-flex; align-items: center; min-height: 44px; color: inherit; text-decoration: none; }
.trust a:hover { color: var(--ink); }
@media (min-width: 760px) { .trust { grid-template-columns: repeat(2, auto); justify-content: space-between; } }
@media (min-width: 1100px) { .trust { grid-template-columns: repeat(4, auto); } }

/* ---- Towns: a quiet list between hairlines ------------------------------- */
.towns { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); column-gap: 2.5rem; border-top: 1px solid var(--rule); }
.towns li { padding-block: 0.85rem; border-bottom: 1px solid var(--rule); }
@media (min-width: 760px) { .towns { grid-template-columns: repeat(3, minmax(0, 1fr)); } }

/* ---- Reviews: cards with five bronze stars (the layout Joseph chose) ------ */
.stars { display: flex; gap: 0.3rem; color: var(--bronze); margin-bottom: 1.25rem; }
.stars svg { width: 17px; height: 17px; }
.review {
  max-width: var(--measure); padding: clamp(1.75rem, 3vw, 2.5rem);
  background: var(--card); border: 1px solid var(--rule); border-radius: 2px;
}
.review blockquote, .review p { text-wrap: pretty; }
.review blockquote p + p { margin-top: 0.75rem; }
.review figcaption { display: grid; gap: 0.15rem; margin-top: 1.5rem; padding-top: 1.25rem; border-top: 1px solid var(--rule); }
.reviews { display: grid; gap: 1.5rem; }
.reviews.reviews--wall { display: block; columns: 1; column-gap: 1.5rem; }
.reviews--wall .review { max-width: none; break-inside: avoid; margin-bottom: 1.5rem; }
@media (min-width: 760px) { .reviews.reviews--wall { columns: 2; } }
@media (min-width: 1100px) { .reviews.reviews--wall { columns: 3; } }
.reviews-band + .reviews-band { padding-top: 0; }
.pull .stars { justify-content: center; margin-bottom: 1.75rem; }
/* one review on its own between sections: centred, no card */
.review.review--feature { max-width: 40rem; margin-inline: auto; padding: 0; text-align: center; background: none; border: 0; }
.review--feature .stars { justify-content: center; }
.review--feature figcaption { justify-items: center; padding-top: 0; border-top: 0; }
.review--feature figcaption::before { content: ""; width: 2.5rem; height: 1px; margin-bottom: 1rem; background: var(--bronze); }
.reviews-band .more { margin-top: 1rem; }

/* ---- Services: alternating editorial rows (the layout Joseph chose) ------- */
.svc { display: grid; gap: clamp(2.5rem, 5vw, 6rem); align-items: center; }
@media (min-width: 960px) {
  .svc { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .svc--flip .svc__photo { order: 2; }
}
.svc__photo { position: relative; margin: 0; overflow: hidden; }
/* the photo, the title and the listed services lead to the service's own page */
.svc__photo-link { display: block; }
.svc__copy h2 a { color: inherit; text-decoration: none; }
.svc__copy h2 a:hover { text-decoration: underline; text-decoration-color: var(--bronze); text-decoration-thickness: 1px; text-underline-offset: 0.15em; }
.svc__list a { color: inherit; text-decoration: underline; text-decoration-color: var(--bronze); text-underline-offset: 0.2em; }
@media (hover: hover) { .svc__photo-link:hover img { transform: scale(1.03); } }
.svc__photo img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; transition: transform 1.4s var(--ease); }
.svc__photo:hover img { transform: scale(1.03); }
.svc__num {
  position: absolute; left: 1.5rem; bottom: 1.5rem; display: grid; place-items: center;
  width: 3.5rem; height: 3.5rem; background: var(--bronze); color: var(--on-bronze);
}
.svc__copy > * + * { margin-top: 1.5rem; }
.svc__copy > .eyebrow + h2 { margin-top: 1.1rem; }
.svc__copy > p { max-width: 36rem; }
.svc__list { display: grid; gap: 0.9rem 2.5rem; padding-block: 1.5rem; border-block: 1px solid var(--rule); }
@media (min-width: 600px) { .svc__list { grid-template-columns: 1fr 1fr; } }
.svc__list li { display: flex; gap: 0.85rem; align-items: baseline; }
.svc__list li::before { content: ""; width: 7px; height: 7px; flex: none; border: 1px solid var(--bronze); transform: translateY(-2px) rotate(45deg); }
.svc__links { display: flex; flex-wrap: wrap; gap: 0.5rem 2.25rem; }
.svc__links .more { margin-top: 0; }
.svc-band + .svc-band { padding-top: 0; }

/* ---- Two photographs as an editorial pair (process page) ----------------- */
.duet { display: grid; gap: 3rem; }
.duet figure { margin: 0; }
.duet__frame { overflow: hidden; }
.duet__frame img { width: 100%; height: auto; }
.duet figcaption { margin-top: 1.25rem; }
@media (min-width: 860px) {
  .duet { grid-template-columns: repeat(12, minmax(0, 1fr)); gap: 0 2rem; align-items: start; }
  .duet__a { grid-column: 1 / span 7; }
  .duet__b { grid-column: 9 / span 4; margin-top: 38%; }
}

/* ---- Process steps: large bronze numbers, a hairline above each ------------ */
.steps { counter-reset: step; display: grid; gap: 0; }
.steps > li { counter-increment: step; display: grid; gap: 0.75rem; padding-block: 2.25rem; border-top: 1px solid var(--rule); }
.steps > li::before { content: counter(step, decimal-leading-zero); color: var(--ink-soft); }
@media (min-width: 860px) {
  .steps { grid-template-columns: 1fr 1fr; column-gap: 5rem; }
}

/* ---- FAQ: questions between hairlines, a thin plus that turns ------------- */
.faq { display: grid; gap: 0; border-top: 1px solid var(--rule); }
.faq details { border-bottom: 1px solid var(--rule); }
.faq summary {
  position: relative; display: flex; justify-content: space-between; gap: 1.5rem; align-items: center;
  padding-block: 1.5rem; cursor: pointer; list-style: none;
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after {
  content: ""; flex: none; width: 14px; height: 14px;
  background: linear-gradient(var(--bronze), var(--bronze)) center / 14px 1px no-repeat,
              linear-gradient(var(--bronze), var(--bronze)) center / 1px 14px no-repeat;
  transition: transform 0.4s var(--ease);
}
.faq details[open] summary::after { transform: rotate(45deg); }
.faq-answer { padding-bottom: 1.75rem; max-width: var(--measure); }

/* ---- Gallery: justified rows, every photograph whole ---------------------
   The gallery layout chosen for this site: each row fills the width and every
   photo in it is the same height, because each one's flex-grow is its own
   width-to-height ratio (build_images.py trims every photo to the nearest of
   these ratios). Phones: one photo per line, full width. */
.mosaic { display: grid; gap: 0.5rem; margin-top: 2.5rem; }
.gallery-band > .wrap > p { max-width: var(--measure); }
.mosaic-row { display: grid; gap: 0.5rem; }
.mosaic a { display: block; min-width: 0; overflow: hidden; text-decoration: none; }
.mosaic img { width: 100%; height: auto; transition: transform 1.4s var(--ease); }
.mosaic a:hover img { transform: scale(1.03); }
/* each photo's share of its row (used wherever the row is a flex line) */
.mosaic .ar-4x3  { flex: 1.3333 1 0; }
.mosaic .ar-3x4  { flex: 0.75 1 0; }
.mosaic .ar-1x1  { flex: 1 1 0; }
.mosaic .ar-3x2  { flex: 1.5 1 0; }
.mosaic .ar-2x3  { flex: 0.6667 1 0; }
.mosaic .ar-16x9 { flex: 1.7778 1 0; }
@media (min-width: 600px) { .mosaic-row { display: flex; } }
/* a strip of three between sections stays one line, even on a phone */
.mosaic--strip { margin-top: 0; }
.mosaic--strip .mosaic-row { display: flex; }
.lightbox { padding: 0; border: 0; background: transparent; max-width: 92vw; max-height: 92vh; }
.lightbox::backdrop { background: rgba(13, 13, 13, 0.94); }
.lightbox img { max-width: 92vw; max-height: 86vh; width: auto; }
.lightbox button { position: fixed; top: 1rem; right: 1rem; }

/* ---- Contact details and address ---------------------------------------- */
.contact-grid { display: grid; gap: 3rem; }
.contact-grid > div { padding-top: 1.75rem; border-top: 1px solid var(--rule); }
@media (min-width: 860px) { .contact-grid { grid-template-columns: 1fr 1fr; gap: 3.5rem 5rem; } }

/* ---- Forms --------------------------------------------------------------- */
.form { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.75rem; max-width: 42rem; }
.form > *, .form fieldset > *, .field > * { min-width: 0; max-width: 100%; }
.form fieldset { border: 0; padding: 0; margin: 0; min-width: 0; display: grid; gap: 1.5rem; }
.form legend { padding: 0; }
/* groups in the form: a hairline and room above each, the heading in the strong
   voice (body size), so nine fields never read as four page sections */
.form h3 { padding-top: 2rem; border-top: 1px solid var(--rule); }
.form > h3:first-of-type, .error-summary + h3 { padding-top: 0; border-top: 0; }
.form legend h3 { margin-bottom: 0.75rem; }
/* the list of problems at the top of the form: framed in ink, one bronze line */
.error-summary { padding: 1.25rem 1.5rem; border: 2px solid var(--ink); }
.error-summary:focus { outline-offset: 4px; }
.error-summary ul { margin-top: 0.75rem; display: grid; gap: 0.5rem; }
.error-summary a { color: var(--ink); text-decoration: underline; text-decoration-color: var(--bronze); text-underline-offset: 0.15em; }
.form h2:not(:first-of-type) { margin-top: 1.5rem; }
.field { display: grid; gap: 0.45rem; }
.field .hint { margin-top: -0.15rem; }
.form input[type="text"], .form input[type="email"], .form input[type="tel"],
.form select, .form textarea {
  width: 100%; min-height: 52px; padding: 0.8rem 0.95rem;
  border: 1px solid var(--edge); border-radius: 2px; background: transparent; color: var(--ink);
  transition: border-color 0.3s var(--ease);
}
.form input:focus, .form select:focus, .form textarea:focus { border-color: var(--ink); }
/* a problem is named above its field, marked with a bronze diamond, and the
   field's edge thickens: never colour alone */
.field-error { display: flex; align-items: baseline; gap: 0.6rem; color: var(--ink); }
.field-error::before { content: ""; width: 0.5rem; height: 0.5rem; flex: none; background: var(--bronze); transform: translateY(-0.1rem) rotate(45deg); }
.form .field [aria-invalid="true"] { border-color: var(--ink); border-width: 2px; padding: calc(0.8rem - 1px) calc(0.95rem - 1px); }
.choice + .field-error, .field-error + .choice { margin-top: 0.5rem; }
.btn[aria-disabled="true"] { cursor: progress; }
.form textarea { min-height: 9rem; resize: vertical; }
/* the job menu drawn like the other fields in every browser (Safari drew a
   27px system control): no native chrome, a small ink chevron */
.form select {
  -webkit-appearance: none; appearance: none; padding-right: 2.75rem;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%), linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 1.45rem) 52%, calc(100% - 1.05rem) 52%;
  background-size: 0.4rem 0.4rem; background-repeat: no-repeat;
}
.form .field select[aria-invalid="true"] { padding-right: calc(2.75rem - 1px); }
.form input[type="file"] { padding: 0.6rem 0; }
.form input[type="file"]::file-selector-button {
  font: inherit; margin-right: 1rem; padding: 0.6rem 1rem; min-height: 44px;
  background: transparent; color: var(--ink); border: 1px solid var(--ink); border-radius: 2px; cursor: pointer;
}
.choice { display: flex; gap: 0.7rem; align-items: flex-start; }
.choice input { width: 1.5rem; height: 1.5rem; margin-top: 0.05rem; flex: none; accent-color: var(--ink); }
.choices { display: flex; flex-wrap: wrap; gap: 0.75rem 1.75rem; }
.honey { display: none; }

/* ---- Closing: the call to action over a photograph ------------------------ */
.closing { min-height: min(86svh, 50rem); }
.closing__copy > * { max-width: 36rem; }
.closing__copy > .actions { max-width: none; }
.closing__copy > * + * { margin-top: 1.5rem; }
.closing__copy .actions { margin-top: 2.25rem; }

/* ---- Footer -------------------------------------------------------------- */
.site-footer { position: relative; padding-block: clamp(4.5rem, 8vw, 7rem) 2.25rem; }
.site-footer::before {
  content: ""; position: absolute; left: 0; right: 0; bottom: 100%; height: clamp(4.5rem, 9vw, 7.5rem); pointer-events: none;
  background: linear-gradient(180deg, rgba(13, 13, 13, 0) 0%, rgba(13, 13, 13, 0.55) 55%, #0D0D0D 100%);
}
.footer-grid { display: grid; gap: 3rem; }
@media (min-width: 860px) { .footer-grid { grid-template-columns: 1.5fr 1fr 1fr; gap: 4rem; } }
.footer-brand img { width: 200px; height: auto; }
.footer-brand > * + * { margin-top: 1.25rem; }
.footer-brand .label { color: var(--bronze); }
.footer-links { display: grid; justify-items: start; margin-top: 0.75rem; }
.footer-links a, .footer-contact a, .social a, .footer-bottom a { display: inline-flex; align-items: center; min-height: 44px; }
.footer-contact { display: grid; justify-items: start; margin-top: 0.5rem; }
.footer-links a, .footer-bottom a, .site-footer address a { text-decoration: none; transition: color 0.3s; }
.footer-links a:hover, .footer-bottom a:hover, .site-footer address a:hover { color: var(--ink); text-decoration: underline; }
.social { display: flex; flex-wrap: wrap; gap: 0 1.5rem; }
.footer-bottom {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem 1.5rem; justify-content: space-between;
  margin-top: 4rem; padding-top: 1.75rem; border-top: 1px solid var(--rule);
}
.footer-bottom nav { display: flex; flex-wrap: wrap; gap: 0 1.5rem; }
.site-credit a { text-decoration: none; }
.site-credit a:hover { color: var(--ink); text-decoration: underline; text-decoration-color: var(--bronze); }
/* Phones (Joseph, 12 Oct 2026): a short footer. The logo, tagline, address,
   number and email step aside (they are on the Contact page and the call bar);
   the order is the three profile links, the services, the company links, the
   company number and VAT with the legal links, and the Staie credit last. */
@media (max-width: 859px) {
  .footer-brand > a:first-child, .footer-brand > .label, .footer-brand > address { display: none; }
  .footer-brand > * + * { margin-top: 0; }
  .footer-grid { gap: 2.25rem; }
  .footer-bottom { flex-direction: column; align-items: flex-start; justify-content: flex-start; gap: 0.25rem; margin-top: 2.5rem; }
  .footer-bottom > nav { order: 1; }
  .footer-bottom > .site-credit { order: 2; margin-top: 0.75rem; }
}

/* ---- Sticky call bar (mobile only): calling is the one thing Jack wants --- */
.sticky-cta {
  position: fixed; inset: auto 0 0 0; z-index: 40;
  display: grid; grid-template-columns: 1fr 1.15fr; gap: 0.5rem;
  padding: 0.6rem var(--gutter) calc(0.6rem + env(safe-area-inset-bottom));
}
.sticky-cta .btn { padding: 0.5rem 0.75rem; min-height: 52px; white-space: normal; text-align: center; line-height: 1.25; }
.sticky-cta--call-only { grid-template-columns: 1fr; }
/* the narrowest phones: the call keeps its own width, the quote takes the rest (two lines, not four) */
@media (max-width: 23.6875em) {
  .sticky-cta { grid-template-columns: 1fr auto; }
  .sticky-cta--call-only { grid-template-columns: 1fr; }
  .sticky-cta .btn { padding-inline: 0.6rem; }
}
body { padding-bottom: 4.75rem; }
/* very narrow in text terms (a 320px phone, or any phone at 125% text and up):
   the bar is the call alone, on one line; the quote stays in the menu */
@media (max-width: 21em) {
  .sticky-cta, .sticky-cta--call-only { grid-template-columns: 1fr; }
  .sticky-cta .sticky-quote { display: none; }
}
@media (min-width: 47.5em) { .sticky-cta { display: none; } body { padding-bottom: 0; } }

/* ---- Cookie banner (only rendered when analytics is switched on) --------- */
.cookie-banner {
  position: fixed; inset: auto 0 4.75rem 0; z-index: 60;
  padding: 1rem var(--gutter); background: var(--dark); color: var(--ink);
}
.cookie-banner .actions { margin-top: 0.75rem; }
@media (min-width: 760px) { .cookie-banner { bottom: 0; } }

/* ---- Motion ----------------------------------------------------------------
   Slow and quiet, transforms and opacity only (no layout work while scrolling).
   The .js class is set before first paint, so without JavaScript nothing is
   ever hidden. Visitors who ask for reduced motion get a still page. */
@media (prefers-reduced-motion: no-preference) {
  .js .hero__media img { animation: settle 2.8s var(--ease) both; }
  .js .hero__copy > * { animation: rise 1.2s var(--ease) both; }
  .js .hero__copy > :nth-child(1) { animation-delay: 0.25s; }
  .js .hero__copy > :nth-child(2) { animation-delay: 0.4s; }
  .js .hero__copy > :nth-child(3) { animation-delay: 0.6s; }
  .js .hero__copy > :nth-child(4) { animation-delay: 0.8s; }
  .js .hero__copy > :nth-child(5) { animation-delay: 1s; }
  .js .hero__copy > .actions { animation: none; }   /* the call is there from the first frame */

  .js [data-reveal] { opacity: 0; transform: translateY(2rem); transition: opacity 1.1s var(--ease), transform 1.3s var(--ease); }
  .js [data-reveal].is-in { opacity: 1; transform: none; }
  .js [data-reveal]:nth-child(2) { transition-delay: 0.12s; }
  .js [data-reveal]:nth-child(3) { transition-delay: 0.24s; }

  /* Feature photographs drift a little against the scroll: compositor-only,
     in browsers that support scroll-driven animation. */
  @supports (animation-timeline: view()) {
    .feature__media img, .closing .feature__media img, .duet__frame img {
      animation: drift linear both; animation-timeline: view(); animation-range: entry 0% exit 100%;
    }
    .duet__b .duet__frame img { animation-name: drift-more; }
  }
}
@keyframes settle { from { transform: scale(1.1); } to { transform: scale(1); } }
@keyframes rise   { from { opacity: 0; transform: translateY(1.75rem); } to { opacity: 1; transform: none; } }
@keyframes drift  { from { transform: translateY(-5%) scale(1.12); } to { transform: translateY(5%) scale(1.12); } }
@keyframes drift-more { from { transform: translateY(-9%) scale(1.2); } to { transform: translateY(9%) scale(1.2); } }

@media (prefers-reduced-motion: reduce) {
  /* a still page: no glide (html[data-theme] included), no drift, no rise */
  *, *::before, *::after, html, html[data-theme] { transition: none !important; animation: none !important; scroll-behavior: auto !important; }
}

@media print {
  .site-header, .sticky-cta, .site-footer, .cookie-banner, .reel__toggle { display: none; }
  body { padding-bottom: 0; }
}
