/* ==========================================================================
   TALYA TAKES STORY — site styles
   Colours, fonts and spacing all live in the :root block below.
   Change a value there and it updates everywhere on the site.
   ========================================================================== */

:root {
  --bg:            #f8f7f4;
  --bg-soft:       #efede7;
  --ink:           #111111;
  --ink-soft:      #666666;
  --line:          #e3dfd8;
  --accent:        #00558c;
  --accent-dark:   #004d7e;   /* accent darkened ~10%, for hover states */
  /* The brand blue is only legible on the light background: #00558c on the dark
     --ink used by the homepage chapters and the opener is 2.41:1, well under the
     4.5:1 minimum. This pale tint of the same hue reads as the same "it turned
     blue" gesture but sits at 14.7:1 on --ink and behaves like white over a
     mid-tone photograph. Use it anywhere the accent lands on a dark surface. */
  --accent-on-dark: #cfe6f7;
  --error:         #a4241d;   /* 6.9:1 on the cream background */

  --font-display: "Cormorant Garamond", "Times New Roman", serif;
  --font-body:    "Inter", "Helvetica Neue", Arial, sans-serif;

  --wide:   1440px;
  --narrow: 780px;

  --nav-h: 78px;
  --radius: 4px;
}

/* ---------- reset ---------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; font-size: calc(100% * var(--a11y-scale, 1)); }
body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 16px;
  line-height: 1.75;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
}
img { max-width: 100%; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; }

/* ---------- typography ---------- */
h1, h2, h3, h4 {
  font-family: var(--font-display);
  font-weight: 300;
  line-height: 1.15;
  color: var(--ink);
  margin: 0 0 .85em;
  letter-spacing: .01em;
}
/* Hero h1 scale — reduced (2026 pass) from clamp(2.4rem,6vw,4.6rem) to read as a
   quieter, more editorial mark instead of an oversized display headline. h1 is
   used exclusively for each page's single hero headline, so this one rule keeps
   every hero across the site (and every project page) on the same reduced scale. */
h1 { font-size: clamp(1.5rem, 3.2vw, 2.4rem); }
h2 { font-size: clamp(1.9rem, 4vw, 3.1rem); }
h3 { font-size: clamp(1.3rem, 2.4vw, 1.8rem); }
p  { margin: 0 0 1.3em; line-height: 1.8; }

.eyebrow {
  font-size: .72rem;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--ink-soft);
  font-weight: 400;
  margin-bottom: 1.4em;
  display: block;
}
.lede { font-size: 1.06rem; color: var(--ink); }
.muted { color: var(--ink-soft); }
.center { text-align: center; }

.service-list { margin: 0 0 1.3em; padding: 0; list-style: none; color: var(--ink); }
.service-list li { padding-left: 1.2em; position: relative; margin-bottom: .5em; }
.service-list li::before { content: "—"; position: absolute; left: 0; color: var(--accent); }
.service-sub { margin-top: 2em; }
.service-sub h3 { margin-bottom: .5em; }

/* ---------- layout ---------- */
.wrap    { max-width: var(--wide);   margin: 0 auto; padding: 0 clamp(20px, 5vw, 64px); }
.wrap-sm { max-width: var(--narrow); margin: 0 auto; padding: 0 clamp(20px, 5vw, 40px); }
.section { padding: clamp(70px, 11vw, 150px) 0; }
/* Used to be a slightly darker off-cream (var(--bg-soft)) for a bit of alternation between
   sections — now flattened to the same cream as everything else so the whole site reads as
   one continuous canvas, not a checkerboard of near-identical tones. */
.section--soft { background: var(--bg); }

/* ---------- buttons / links ----------
   One consistent treatment everywhere per the brand system: solid accent
   background, white text, 4px radius, no shadow, hover darkens the accent
   ~10%. .btn--light/.btn--solid are kept as classes (still referenced in
   HTML) but no longer need their own overrides — the base .btn already IS
   the "use brand color consistently for buttons" treatment. */
.btn {
  display: inline-block;
  border: 0;
  border-radius: var(--radius);
  padding: 15px 40px;
  font-size: .74rem;
  font-weight: 500;
  letter-spacing: .26em;
  text-transform: uppercase;
  background: var(--accent);
  color: #fff;
  box-shadow: none;
  transition: background .35s ease;
}
.btn:hover { background: var(--accent-dark); }
/* Used while the contact form is mid-send, to stop a second submission. */
.btn[disabled] { opacity: .6; cursor: progress; }
.btn[disabled]:hover { background: var(--accent); }

.link-underline {
  font-size: .74rem;
  font-weight: 500;
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--accent);
  border-bottom: 1px solid var(--accent);
  padding-bottom: 3px;
  transition: opacity .3s ease;
}
.link-underline:hover { opacity: .6; }

/* ---------- nav ---------- */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  height: var(--nav-h);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 clamp(20px, 5vw, 64px);
  z-index: 100;
  transition: background .4s ease, box-shadow .4s ease;
}
.nav--over { color: #fff; }                       /* transparent over hero */
.nav--solid {
  background: rgba(248,247,244,.97);
  color: var(--ink);
  box-shadow: 0 1px 0 var(--line);
  backdrop-filter: blur(6px);
}
.nav__logo {
  font-family: var(--font-display);
  font-size: 1.18rem;
  letter-spacing: .32em;
  text-transform: uppercase;
  white-space: nowrap;
}
.nav__links { display: flex; gap: clamp(20px, 3vw, 46px); align-items: center; }
.nav__links a {
  font-size: .73rem;
  font-weight: 500;
  letter-spacing: .22em;
  text-transform: uppercase;
  position: relative;
  padding-bottom: 3px;
  transition: color .3s ease;
}
.nav__links a:hover,
.nav__links a[aria-current="page"] { color: var(--accent); }
.nav__links a::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 0; height: 1px;
  background: currentColor;
  transition: width .35s ease;
}
.nav__links a:hover::after,
.nav__links a[aria-current="page"]::after { width: 100%; }

.nav__toggle { display: none; background: none; border: 0; color: inherit; letter-spacing: .2em; font-size: .73rem; text-transform: uppercase; }
/* keeps the accessibility toggle grouped with the hamburger so the pair stays anchored at the
   end of the bar instead of drifting to the middle once .nav__links (which used to hold the
   a11y button) disappears at the mobile breakpoint — the button must stay reachable at every
   viewport width, not just desktop. */
.nav__actions { display: flex; align-items: center; gap: .9em; }

/* mobile drawer */
.drawer {
  position: fixed;
  inset: 0;
  background: var(--bg);
  z-index: 200;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 26px;
  opacity: 0;
  visibility: hidden;
  transition: opacity .35s ease, visibility .35s ease;
}
.drawer.is-open { opacity: 1; visibility: visible; }
.drawer a {
  font-family: var(--font-display);
  font-size: 1.9rem;
  transition: color .3s ease;
}
.drawer a:hover,
.drawer a[aria-current="page"] { color: var(--accent); }
.drawer__close {
  position: absolute;
  top: 26px; right: clamp(20px, 5vw, 64px);
  background: none; border: 0;
  font-size: .74rem; letter-spacing: .24em; text-transform: uppercase;
}

/* ---------- hero ---------- */
.hero {
  position: relative;
  height: 100svh;
  min-height: 560px;
  display: grid;
  place-items: center;
  overflow: hidden;
  color: #fff;
  text-align: center;
}
.hero__img,
.hero__video {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
}
.hero::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(0,0,0,.34) 0%, rgba(0,0,0,.16) 45%, rgba(0,0,0,.42) 100%);
}
.hero__inner { position: relative; z-index: 2; padding: 0 24px; }
.hero h1 { color: #fff; margin-bottom: .5em; }
.hero__sub {
  font-size: .78rem;
  letter-spacing: .34em;
  text-transform: uppercase;
  margin-bottom: 6.5em;
}
/* hero CTA: white/minimal so it supports the photo rather than competing
   with it — distinct from the site's standard blue .btn, used only here */
.btn--light {
  background: #fff;
  color: var(--ink);
  border: 1px solid rgba(255,255,255,.6);
}
.btn--light:hover { background: #e9e9e9; }
.hero__scroll {
  position: absolute;
  bottom: 34px; left: 50%;
  transform: translateX(-50%);
  z-index: 2;
  font-size: .68rem;
  letter-spacing: .26em;
  text-transform: uppercase;
  opacity: .85;
}

/* ---------- homepage slideshow (full-screen, 5 images, cinematic crossfade) ---------- */
.slideshow {
  position: relative; height: 100svh; min-height: 560px;
  overflow: hidden; color: #fff;
}
.slideshow__slide {
  position: absolute; inset: 0; opacity: 0; z-index: 0;
  transition: opacity 2.4s ease-in-out;
}
.slideshow__slide img,
.slideshow__slide video {
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1); transition: transform 7s ease-out;
}
.slideshow__slide.is-on { opacity: 1; z-index: 1; }
.slideshow__slide.is-on img,
.slideshow__slide.is-on video { transform: scale(1.06); }
.slideshow::after {
  content: ""; position: absolute; inset: 0; z-index: 2; pointer-events: none;
  background: linear-gradient(180deg, rgba(0,0,0,.34) 0%, rgba(0,0,0,.12) 45%, rgba(0,0,0,.42) 100%);
}
.slideshow__inner {
  position: relative; z-index: 3; height: 100%;
  display: grid; place-items: center; text-align: center; padding: 0 24px;
}
.slideshow__inner .eyebrow { color: rgba(255,255,255,.75); }
.slideshow__inner h1 { color: #fff; margin-bottom: .5em; }
.slideshow__sub { font-size: .78rem; letter-spacing: .34em; text-transform: uppercase; color: rgba(255,255,255,.85); }
/* a thin, minimal divider marking the boundary between the hero and the chapters below —
   understated, not a heavy full-width rule */
.slideshow::before {
  content: ""; position: absolute; z-index: 3; bottom: 0; left: 50%; transform: translateX(-50%);
  width: 64px; height: 1px; background: rgba(255, 255, 255, .55); pointer-events: none;
}
.slideshow__scroll {
  position: absolute; bottom: 34px; left: 50%; transform: translateX(-50%); z-index: 3;
  font-size: .68rem; letter-spacing: .26em; text-transform: uppercase; opacity: .85;
}

/* hero content (and the scroll cue) stay hidden until the opening sequence hands off to this exact
   frame — see the "opening sequence" block below and in main.js — then rise in as a continuation
   of the same shot, never a separate page load. */
.slideshow__inner > * {
  opacity: 0; transform: translateY(18px);
  transition: opacity .9s ease, transform .9s cubic-bezier(.16,1,.3,1);
}
.slideshow__inner .eyebrow { transition-delay: .1s; }
.slideshow__inner h1 { transition-delay: .26s; }
.slideshow__inner .slideshow__sub { transition-delay: .44s; }
.slideshow.is-revealed .slideshow__inner > * { opacity: 1; transform: none; }
.slideshow__scroll {
  opacity: 0; transform: translate(-50%, 10px);
  transition: opacity 1s ease .62s, transform 1s cubic-bezier(.16,1,.3,1) .62s;
}
.slideshow.is-revealed .slideshow__scroll { opacity: .85; transform: translate(-50%, 0); }

@media (prefers-reduced-motion: reduce) {
  .slideshow__slide, .slideshow__slide img { transition: none; }
  .slideshow__inner > *, .slideshow__scroll { transition: none; }
}

/* ---------- homepage journey: one continuous scroll, full-screen chapters ----------
   Replaces the old editorial-mosaic gateway. Every category is its own 100vh
   chapter (full-bleed photo + overlay text), same scale as the slideshow above
   it — nothing here is sized or treated like a card/thumbnail. Chapters fade
   in and their background does a slow parallax drift as they cross the
   viewport (see the "homepage journey" block in main.js). */
.chapter {
  position: relative; min-height: 100vh; display: flex; align-items: center;
  overflow: hidden; background: var(--ink); color: #fff;
}
.chapter__bg {
  position: absolute; inset: -6% -6%; width: 112%; height: 112%; object-fit: cover;
  will-change: transform; filter: brightness(.6) saturate(1.03);
}
.chapter::before {
  content: ""; position: absolute; inset: 0; z-index: 1; pointer-events: none;
  background: linear-gradient(180deg, var(--ink) 0%, rgba(0,0,0,0) 16%, rgba(0,0,0,.18) 62%, var(--ink) 100%);
}
/* the whole card is clickable — a full-cover link sits just above the photo/gradient (z-index 1)
   and below the text (z-index 2). .chapter__body itself ignores pointer events so clicks in its
   padding/gaps fall through to this link, while the visible "View X →" text re-enables pointer
   events on itself, keeping its own hover animation exactly as it was. */
.chapter__hit {
  position: absolute; inset: 0; z-index: 1; cursor: pointer;
}
.chapter__body {
  position: relative; z-index: 2; max-width: 640px; padding: 0 clamp(24px, 8vw, 110px);
  opacity: 0; transform: translateY(30px); pointer-events: none;
  transition: opacity .9s ease, transform .9s cubic-bezier(.16,1,.3,1);
}
.chapter__body a, .chapter__body button { pointer-events: auto; }
.chapter.is-active .chapter__body { opacity: 1; transform: translateY(0); }
.chapter__num {
  font-family: var(--font-display); font-size: 1rem; color: var(--accent);
  letter-spacing: .1em; display: block; margin-bottom: .9em;
}
.chapter__title { color: #fff; font-size: clamp(2.4rem, 6vw, 4.6rem); margin: 0 0 .35em; line-height: 1.05; }
.chapter__line { font-size: 1rem; color: rgba(255,255,255,.82); max-width: 46ch; margin: 0 0 1.6em; line-height: 1.75; }
.chapter__link {
  display: inline-flex; align-items: center; gap: .6em; font-size: .78rem; letter-spacing: .16em; text-transform: uppercase;
  border-bottom: 1px solid rgba(255,255,255,.5); padding-bottom: .3em;
  transition: border-color .3s ease, gap .3s ease, color .3s ease;
}
.chapter__link:hover,
.chapter__link:focus-visible { border-color: var(--accent-on-dark); color: var(--accent-on-dark); gap: 1em; }

.chapter--right .chapter__body { margin-left: auto; text-align: right; }
.chapter--right .chapter__link { flex-direction: row-reverse; }

/* ---------- the three homepage chapters on a phone ----------
   Wedding, Family Photography and Documentary are each a full-bleed photograph at
   min-height:100vh with the copy laid over it. Two things made that feel oversized
   on a phone in a way it never does on a desktop:

     - `vh` on a phone means the TALLEST the viewport ever gets — it counts the
       space behind the browser's own retracting address bar. A 100vh section is
       therefore taller than the screen actually showing it, so a chapter could not
       be taken in at once however you scrolled;
     - the title's clamp bottoms out at its 2.4rem floor below roughly 640px, so the
       type held its full 38px beside an image that had shrunk to phone size. That
       balance only works at desktop proportions.

   So: `svh` (the *small* viewport height — the visible one, address bar showing)
   instead of `vh`, 68% of it, and the type stepped down to match. The three read as
   compact cards while scrolling rather than three full screens, and the top of the
   next one is always in view.

   Scoped to .chapter[data-chapter], which is exactly these three: .chapter--card
   and .chapter--split reuse the same base class for the About and Contact blocks
   and must keep their own sizing — an unscoped .chapter rule here would stretch
   those cards to 68svh too. Nothing below touches the photographs, the crop, the
   parallax, the fade-in, the colours or the type styles. Scale only. */
@media (max-width: 700px) {
  .chapter[data-chapter] { min-height: 68svh; }
  .chapter[data-chapter] .chapter__num { font-size: .85rem; margin-bottom: .7em; }
  .chapter[data-chapter] .chapter__title { font-size: clamp(1.7rem, 8vw, 2.3rem); }
  .chapter[data-chapter] .chapter__line { font-size: .92rem; margin-bottom: 1.3em; }
  .chapter[data-chapter] .chapter__link { font-size: .72rem; }
}

.chapter--close { justify-content: center; text-align: center; }
.chapter--close .chapter__body { max-width: 640px; margin: 0 auto; }
.chapter--close .chapter__title { font-size: clamp(2.6rem, 6.5vw, 5rem); }
.chapter--close .chapter__cta { display: inline-block; margin-top: .6em; }

/* chapters V & VI: a smaller framed photo beside the text, on the same
   baseline as the copy, instead of a full-bleed background image */
.chapter--split { background: var(--ink); }
.chapter--split .chapter__frame {
  position: relative; z-index: 2; width: 100%; max-width: 1120px; margin: 0 auto;
  display: flex; align-items: center; gap: clamp(32px, 6vw, 90px);
  padding: 0 clamp(24px, 8vw, 110px);
  pointer-events: none;
}
.chapter--split .chapter__media {
  flex: 0 0 clamp(200px, 32vw, 400px);
  height: clamp(260px, 40vw, 460px);
  overflow: hidden; border-radius: 3px;
}
.chapter--split .chapter__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.chapter--split .chapter__body { padding: 0; max-width: 480px; }
.chapter--close.chapter--split.chapter { justify-content: center; }
.chapter--close.chapter--split .chapter__body { max-width: 480px; margin: 0; text-align: left; }
.chapter--close.chapter--split .chapter__cta { margin-top: .6em; }

@media (max-width: 760px) {
  .chapter--split .chapter__frame { flex-direction: column; text-align: center; }
  .chapter--split .chapter__body,
  .chapter--close.chapter--split .chapter__body { text-align: center; max-width: 100%; }
}

/* finale: Behind the Camera + Contact paired as two compact white cards on
   one row, instead of two full-screen dark chapters */
.finale-row {
  background: var(--bg);
  display: flex; flex-wrap: wrap; justify-content: center; align-items: stretch;
  gap: clamp(24px, 4vw, 48px);
  padding: clamp(90px, 12vw, 160px) clamp(24px, 6vw, 60px);
}
.chapter--card {
  min-height: 0; background: #fff; color: var(--ink);
  border-radius: 6px; box-shadow: 0 14px 44px rgba(17, 17, 17, .08);
  flex: 1 1 380px; max-width: 440px;
  padding: clamp(28px, 4vw, 40px);
}
.chapter--card::before { display: none; }
.chapter--card.chapter--split .chapter__frame { padding: 0; max-width: none; margin: 0; gap: clamp(20px, 3vw, 28px); }
/* the About/Contact cards carry finished logo artwork, not a photo crop — contain (not cover)
   so the full mark is always visible, with the card's own white padding acting as a mat/frame
   around it rather than a hard crop */
.chapter--card.chapter--split .chapter__media {
  flex: 0 0 clamp(120px, 17vw, 170px); height: clamp(120px, 17vw, 170px);
  display: flex; align-items: center; justify-content: center;
  background: #fff; padding: 10px; border-radius: 6px;
}
.chapter--card.chapter--split .chapter__media img { width: 100%; height: 100%; object-fit: contain; }
.chapter--card.chapter--split .chapter__body { max-width: none; }
.chapter--card .chapter__title,
.chapter--card.chapter--close .chapter__title { color: var(--ink); font-size: clamp(1.5rem, 2.6vw, 1.9rem); }
.chapter--card .chapter__line { color: var(--ink-soft); }
.chapter--card .chapter__link { border-bottom-color: rgba(17, 17, 17, .3); color: var(--ink); }
.chapter--card .chapter__link:hover { border-color: var(--accent); color: var(--accent); }
.chapter--card.chapter--close.chapter--split .chapter__body { text-align: left; margin: 0; }

@media (max-width: 640px) {
  .chapter--card.chapter--split .chapter__frame { flex-direction: column; text-align: center; }
  .chapter--card.chapter--split .chapter__body,
  .chapter--card.chapter--close.chapter--split .chapter__body { text-align: center; }
}

/* interlude: the quote as a cinematic pause before the closing signature — full-width solid
   black, deliberately its own moment rather than a continuation of the page's cream. Vertical
   padding gives it comfortable presence without going back to the old 70vh full-screen block;
   the 16px above/below still comes purely from the .section-spacer divs around it, not from
   this padding, so the spacer stays the single source of truth for that gap. */
.interlude {
  position: relative; display: flex; align-items: center; justify-content: center;
  text-align: center; padding: clamp(64px, 9vw, 120px) 24px; overflow: hidden;
  background: #0d0d0d; color: #fff;
}
.interlude blockquote {
  position: relative; z-index: 2;
  font-family: var(--font-display); font-weight: 300; font-style: italic;
  font-size: clamp(1.15rem, 1.8vw, 1.5rem); line-height: 1.4; max-width: 820px; margin: 0;
}
.interlude cite { display: block; margin-top: 1.2em; font-style: normal; font-size: .7rem; letter-spacing: .28em; text-transform: uppercase; color: rgba(255, 255, 255, .55); }

/* ==========================================================================
   SIGNATURE — the homepage's closing mark: wordmark + WhatsApp/Email/Instagram,
   replacing the old About/Contact CTA cards. Deliberately quiet — no button,
   no card, no shadow — reads as a signature at the bottom of a letter rather
   than a conventional page footer.
   ========================================================================== */
.signature {
  padding: 0 0 clamp(60px, 9vw, 100px);
  text-align: center;
  background: var(--bg);
}
.signature__mark {
  display: block;
  font-family: var(--font-display);
  font-size: clamp(1rem, 1.8vw, 1.3rem);
  letter-spacing: .32em;
  text-transform: uppercase;
  color: var(--ink);
  margin-bottom: 16px;
}
.signature__icons {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(34px, 5.5vw, 56px);
}
.signature__icon {
  display: flex; align-items: center; justify-content: center;
  width: clamp(26px, 3.2vw, 32px); height: clamp(26px, 3.2vw, 32px);
  color: var(--ink-soft);
  transition: color .35s ease, transform .35s ease;
}
.signature__icon svg { width: 100%; height: 100%; }
.signature__icon:hover,
.signature__icon:focus-visible { color: var(--accent); transform: translateY(-3px); }
@media (prefers-reduced-motion: reduce) {
  .signature__icon { transition: color .35s ease; }
  .signature__icon:hover, .signature__icon:focus-visible { transform: none; }
}

/* a clean beat of solid warm-cream space between the hero video and the chapters below — full
   width, just enough to let the hero breathe before the journey continues. Same cream as the
   site's own base background, not a separate color. */
.hero-spacer { width: 100%; height: 16px; background: var(--bg); }

/* the same cream pause, repeated between every section on the page — a consistent 16px beat
   so each section gets an identical breath before the next begins */
.section-spacer { width: 100%; height: 16px; background: var(--bg); }

/* thread: persistent chapter nav down the right edge, doubles as a progress indicator.
   Pure white marks, no background panel — reads as a light, minimal indicator rather than a
   heavy floating pill, at its best against the site's mostly-dark full-bleed sections. */
.thread {
  position: fixed; right: clamp(18px, 3vw, 34px); top: 50%; transform: translateY(-50%); z-index: 90;
  display: flex; flex-direction: column; align-items: center;
}
.thread__line { width: 1px; background: rgba(255, 255, 255, .4); position: absolute; top: 0; bottom: 0; left: 50%; transform: translateX(-50%); z-index: 0; }
.thread__fill { width: 1px; background: var(--accent); position: absolute; top: 0; left: 50%; transform: translateX(-50%); z-index: 1; height: 0%; transition: height .2s linear; }
.thread__item { position: relative; z-index: 2; padding: 15px 0; }
.thread__dot {
  display: block; width: 7px; height: 7px; border-radius: 50%; background: #fff;
  border: 0; padding: 0; cursor: pointer; transition: background .3s ease, transform .3s ease;
}
.thread__item.is-active .thread__dot { background: var(--accent); transform: scale(1.4); }
.thread__label {
  position: absolute; right: 22px; top: 50%; transform: translateY(-50%);
  font-size: .68rem; letter-spacing: .16em; text-transform: uppercase; white-space: nowrap; color: #fff;
  opacity: 0; transition: opacity .3s ease; pointer-events: none;
}
.thread__item:hover .thread__label,
.thread__item.is-active .thread__label { opacity: .85; }
@media (max-width: 760px) { .thread { display: none; } }

/* page header (inner pages) */
.page-head {
  padding: calc(var(--nav-h) + clamp(60px, 9vw, 120px)) 0 clamp(40px, 6vw, 80px);
  text-align: center;
}
/* Love/Family Story zero out page-head's own padding (inline style) to hold a fixed 16px gap
   on both sides instead — but the lede paragraph's own bottom margin would otherwise collapse
   straight through that zeroed padding and add extra space anyway. Kill it here since the lede
   is always page-head's last/only line of text. */
.page-head .lede { margin-bottom: 0; }

/* ---------- gallery grid ----------
   Editorial masonry. Each image keeps its own natural aspect ratio (no
   object-fit crop). Gap is a fixed 10px, matching theferros.com's grid
   exactly (measured, not guessed).

   Base layout is CSS multi-column (works with no JS). Once main.js runs, it
   redistributes each .grid's children round-robin into explicit .grid__col
   columns (see layoutMasonry in main.js) — CSS column's own auto-balancing
   fills column 1 to a target height before touching column 2, which for a
   deliberately-sequenced gallery (e.g. a color "chapter" followed by a
   black-and-white "chapter") can dump an entire chapter into a single
   column. Round-robin guarantees every column gets an even slice of the
   sequence at all times. */
.grid {
  column-count: 3;
  column-gap: 10px;
}
.grid--2 { column-count: 2; }
.grid__item {
  position: relative;
  overflow: hidden;
  display: block;
  margin: 0 0 10px;
  break-inside: avoid;
}

.grid.js-cols {
  display: flex;
  align-items: flex-start;
  column-count: unset;
  column-gap: unset;
  gap: 10px;
}
.grid.js-cols .grid__col {
  display: flex;
  flex-direction: column;
  gap: 10px;
  flex: 1 1 0;
  min-width: 0;
}
.grid.js-cols .grid__item { margin: 0; }

/* editorial variant (Love Stories gallery): the same no-crop masonry mechanics, but two large
   columns and generous gutters instead of three tight ones — bigger photographs, more breathing
   room, closer to a curated exhibition than a dense portfolio grid. Each .grid__item also carries
   the site's existing .reveal class, so images fade up individually as they enter the viewport
   using the same IntersectionObserver already wired up in main.js — no new JS behavior. */
.grid--editorial { column-gap: clamp(28px, 4vw, 64px); }
.grid--editorial .grid__item { margin-bottom: clamp(28px, 4vw, 64px); }
.grid--editorial.js-cols { gap: clamp(28px, 4vw, 64px); }
.grid--editorial.js-cols .grid__col { gap: clamp(28px, 4vw, 64px); }
.grid--editorial.js-cols .grid__item { margin: 0; }

/* dense variant (Family Story gallery): same editorial mechanics and reveal-on-scroll as Love
   Stories, but three tighter columns instead of two — slightly smaller photographs, slightly
   tighter gutters, more visual rhythm per scroll, while staying well short of the site's original
   dense 10px portfolio grid. */
.grid--editorial.grid--dense { column-gap: 8px; }
.grid--editorial.grid--dense .grid__item { margin-bottom: 8px; }
.grid--editorial.grid--dense.js-cols { gap: 8px; }
.grid--editorial.grid--dense.js-cols .grid__col { gap: 8px; }

/* compact variant (Love Stories gallery): now three columns (colsFor in main.js), same as
   Family Story's density, plus a slightly tighter gutter — noticeably smaller thumbnails so more
   of the gallery is visible on screen at once, without touching aspect ratios, hover, reveal-on-
   scroll, or masonry mechanics. */
.grid--editorial.grid--compact { max-width: 93%; margin: 0 auto; column-gap: 8px; }
.grid--editorial.grid--compact .grid__item { margin-bottom: 8px; }
.grid--editorial.grid--compact.js-cols { gap: 8px; }
.grid--editorial.grid--compact.js-cols .grid__col { gap: 8px; }

.grid__item img,
.grid__item video {
  width: 100%; height: auto;
  display: block;
  transition: transform 1.1s cubic-bezier(.2,.6,.2,1);
}
.grid__item:hover img,
.grid__item:hover video { transform: scale(1.03); }

.grid__cap {
  position: absolute;
  inset: auto 0 0 0;
  padding: 22px;
  color: #fff;
  font-size: .68rem;
  letter-spacing: .24em;
  text-transform: uppercase;
  background: linear-gradient(0deg, rgba(0,0,0,.5), transparent);
  opacity: 0;
  transition: opacity .4s ease;
}
.grid__item:hover .grid__cap { opacity: 1; }

/* editorial "story" tile — full centered overlay, ferros-style */
.grid__item--story .grid__cap {
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  background: rgba(0,0,0,.34);
  padding: 22px;
}

/* ---------- split text/image ---------- */
.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(30px, 6vw, 90px);
  align-items: center;
}
.split--reverse .split__media { order: 2; }
.split__body { max-width: 520px; }
/* The only global img rule is `max-width: 100%` — it caps the width but says nothing about
   height, so an <img> carrying width/height attributes (kept for layout stability) renders
   at its literal attribute height and the picture comes out stretched. .grid__item img has
   carried height:auto for exactly this reason; .split__media never did, which went unnoticed
   for as long as this slot held a grey placeholder graphic. */
.split__media img { height: auto; }

/* ---------- service cards ---------- */
.cards {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(20px, 3vw, 44px);
}
.card__media { overflow: hidden; margin-bottom: 22px; aspect-ratio: 4/3; }
.card__media img { width: 100%; height: 100%; object-fit: cover; transition: transform 1s ease; }
.card:hover .card__media img { transform: scale(1.05); }
.card h3 { margin-bottom: .35em; }
.card p { font-size: .95rem; color: var(--ink-soft); }

/* ---------- project meta / story pages ---------- */
.hero--project { height: 78vh; min-height: 460px; }

/* ---------- documentary project pages (trailer/preview + full film) ----------
   Cinematic poster hero (no text overlay — the title sits in its own quiet block
   below, per her spec), then synopsis, then each video in an identical fixed
   16:9 frame so nothing jumps while a video's metadata loads. */
.project-title {
  text-align: center;
  padding: clamp(48px, 7vw, 80px) 24px 0;
}
.project-title .eyebrow { margin-bottom: .9em; }
.project-title h1 { margin-bottom: 0; }
.project-synopsis {
  max-width: 720px; margin: 16px auto 0;
  text-align: center;
}
.project-synopsis p { margin: 0; line-height: 1.8; color: var(--ink-soft); }
.project-player-block { max-width: 960px; margin: 0 auto; }
/* This is now an <h2> for each video's visible title (previously a <span>, no semantic
   heading) — pin font-family/weight/line-height back to the body defaults so it renders
   identically to before; without this it would inherit the global h1-h4 display-serif rule. */
.project-player-block__label {
  display: block; text-align: center;
  font-family: var(--font-body); font-weight: 400; line-height: normal;
  font-size: .72rem; letter-spacing: .26em; text-transform: uppercase;
  color: var(--ink-soft); margin-bottom: .9em;
}
.project-player {
  position: relative; width: 100%; aspect-ratio: 16 / 9;
  background: #000; border-radius: 4px; overflow: hidden;
  box-shadow: 0 14px 44px rgba(17, 17, 17, .12);
}
.project-player video { width: 100%; height: 100%; object-fit: contain; background: #000; display: block; }
.project-player iframe { width: 100%; height: 100%; border: 0; display: block; }

/* ---------- film language selector (documentary project pages) ----------
   Sits inside the same .project-player frame as the video itself, so the
   choice-to-player handoff never changes the block's size or position.
   The poster photo stays put underneath; only the dark overlay + choices
   fade out once the visitor presses Watch and the player iframe fades in. */
.film-select__poster { width: 100%; height: 100%; object-fit: cover; display: block; }
.film-select__overlay {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  background: rgba(10, 9, 8, .5);
  transition: opacity .5s ease;
}
.film-select__choices {
  display: flex;
  gap: clamp(14px, 3vw, 28px);
}
.film-select__btn {
  background: transparent;
  border: 1px solid rgba(255, 255, 255, .55);
  color: #fff;
  padding: 16px 30px;
  font-family: var(--font-body);
  font-size: .72rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  cursor: pointer;
  transition: background .3s ease, border-color .3s ease, color .3s ease;
}
.film-select__btn:hover,
.film-select__btn:focus-visible {
  background: rgba(255, 255, 255, .1);
  border-color: #fff;
}
.film-select__btn.is-active { background: var(--accent); border-color: var(--accent); }
.film-select.is-loading .film-select__overlay { opacity: 0; pointer-events: none; }
.film-select__stage iframe {
  position: absolute; inset: 0;
  opacity: 0;
  transition: opacity .5s ease;
}
.film-select.is-loading .film-select__stage iframe { opacity: 1; }
@media (max-width: 560px) {
  .film-select__choices { flex-direction: column; width: 100%; padding: 0 22px; }
  .film-select__btn { width: 100%; }
}
/* ---------- FAQ accordion (Wedding page) ----------
   Minimal editorial accordion: hairline rules (var(--line)) instead of
   cards, a thin plus/minus mark instead of a chevron icon, and a CSS-only
   grid-template-rows expand (no JS height math) so it keeps working
   correctly even if the a11y panel's text-size/line-height controls change
   the answer's rendered height after it's opened. */
/* Heading scoped to this section only (not the global h2 rule, which is
   shared by every other section title on the site) — same family/weight/
   alignment/margin as a regular h2, just ~3 steps down the h1→h2→h3 scale
   so it reads as a quiet section label rather than a big display heading. */
.faq-section h2 { font-size: clamp(1.15rem, 1.6vw, 1.35rem); }
.faq { margin-top: clamp(28px, 4vw, 44px); border-top: 1px solid var(--line); }
.faq__item { border-bottom: 1px solid var(--line); }
.faq__trigger {
  width: 100%; display: flex; align-items: center; justify-content: space-between; gap: clamp(16px, 4vw, 32px);
  background: none; border: 0; margin: 0; cursor: pointer; text-align: left;
  padding: clamp(20px, 3vw, 28px) 0;
  font-family: var(--font-body); font-size: 1.02rem; color: var(--ink);
}
/* Tighter question-row rhythm, used only where a page opts in via
   .faq--compact (currently Family Photography) — Wedding's FAQ keeps the
   original spacing untouched since only .faq__trigger's own padding
   changes here, not the shared .faq/.faq__item/.faq__panel rules that the
   answer spacing and other pages still rely on. */
.faq--compact .faq__trigger { padding: clamp(13px, 1.8vw, 19px) 0; }
.faq__trigger:hover .faq__q-text,
.faq__trigger:focus-visible .faq__q-text { color: var(--accent); }
.faq__q-text { transition: color .3s ease; }
.faq__icon { position: relative; flex: 0 0 auto; width: 18px; height: 18px; }
.faq__icon::before, .faq__icon::after {
  content: ""; position: absolute; top: 50%; left: 50%; background: var(--ink-soft);
  transition: transform .35s ease, opacity .35s ease;
}
.faq__icon::before { width: 12px; height: 1px; transform: translate(-50%, -50%); }
.faq__icon::after { width: 1px; height: 12px; transform: translate(-50%, -50%); }
.faq__trigger[aria-expanded="true"] .faq__icon::after { transform: translate(-50%, -50%) scaleY(0); opacity: 0; }
.faq__trigger[aria-expanded="true"] .faq__icon::before { background: var(--accent); }
.faq__panel {
  display: grid; grid-template-rows: 0fr;
  transition: grid-template-rows .5s cubic-bezier(.16, 1, .3, 1);
}
.faq__panel-inner { overflow: hidden; }
.faq__item.is-open .faq__panel { grid-template-rows: 1fr; }
.faq__panel p {
  margin: 0 0 clamp(20px, 3vw, 26px);
  color: var(--ink-soft);
  line-height: 1.9;
  max-width: 68ch;
}
.faq__panel p[dir="rtl"] {
  font-family: "Heebo", Arial, sans-serif;
  text-align: right;
  line-height: 2;
  max-width: 62ch;
  margin-left: auto;
}
@media (max-width: 560px) {
  .faq__trigger { font-size: .96rem; }
}
.project-meta {
  font-size: .74rem;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 1.6em;
}
.project-meta span:not(:last-child)::after { content: "  \00b7  "; }

/* ---------- cinematic play button ---------- */
.project-media { position: relative; overflow: hidden; }
.project-media img { width: 100%; }
.play-btn {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 14px;
  width: 100%; height: 100%;
  background: rgba(0,0,0,.22);
  border: 0;
  color: #fff;
  transition: background .35s ease;
}
.play-btn:hover { background: rgba(0,0,0,.34); }
.play-btn__icon {
  width: 84px; height: 84px;
  border: 1px solid rgba(255,255,255,.8);
  border-radius: 50%;
  display: grid;
  place-items: center;
  font-size: 1.05rem;
  transition: transform .35s ease, background .35s ease;
}
.play-btn:hover .play-btn__icon { background: rgba(255,255,255,.14); transform: scale(1.06); }
.play-btn__label {
  font-size: .72rem;
  letter-spacing: .26em;
  text-transform: uppercase;
}

/* ---------- video modal ---------- */
.video-modal {
  position: fixed; inset: 0;
  background: rgba(10,9,8,.92);
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(16px, 4vw, 48px);
  opacity: 0;
  visibility: hidden;
  transition: opacity .35s ease, visibility .35s ease;
}
.video-modal.is-open { opacity: 1; visibility: visible; }
.video-modal__inner {
  position: relative;
  width: 100%; max-width: 1100px;
  aspect-ratio: 16/9;
  background: #000;
}
.video-modal__inner iframe,
.video-modal__inner video { width: 100%; height: 100%; border: 0; display: block; }
.video-modal__close {
  position: absolute;
  top: 26px; right: clamp(20px, 5vw, 64px);
  background: none; border: 0;
  color: #fff;
  font-size: .74rem; letter-spacing: .24em; text-transform: uppercase;
}

/* ---------- lightbox ----------
   The full-screen viewer a gallery tile opens into (see the LIGHTBOX block at
   the bottom of main.js). Same dark ground as the video modal, one step higher
   in the stack so it can also be opened from a page that carries one. The
   photograph is never cropped and never enlarged past its own pixels: it is
   fitted inside 90vw x 88vh and left alone. */
.grid__item--zoom { cursor: zoom-in; }

.lightbox {
  position: fixed; inset: 0;
  background: rgba(10,9,8,.95);
  z-index: 320;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  visibility: hidden;
  transition: opacity .35s ease, visibility .35s ease;
}
.lightbox.is-open { opacity: 1; visibility: visible; }

.lightbox__stage {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%; height: 100%;
}
/* Each frame fades up once its full-size file has decoded; until then the
   previous one is still on screen underneath it, which is why these are stacked
   on top of each other rather than laid out side by side. */
.lightbox__media {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  opacity: 0;
  transition: opacity .3s ease;
}
.lightbox__media.is-ready { opacity: 1; }
.lightbox__media img {
  display: block;
  width: auto; height: auto;
  max-width: 90vw;
  max-height: 88vh;
  -webkit-user-select: none;
  user-select: none;
}
/* A neighbouring photograph being warmed into the cache: present, but taking up
   no room and catching no clicks. */
.lightbox__preload {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  pointer-events: none;
}

.lightbox__btn {
  position: absolute;
  z-index: 2;
  background: none;
  border: 0;
  padding: 10px;
  color: rgba(255,255,255,.8);
  cursor: pointer;
  transition: color .25s ease, opacity .25s ease;
}
.lightbox__btn:hover { color: #fff; }
.lightbox__btn[aria-disabled="true"] { opacity: .3; cursor: default; }
.lightbox__btn[aria-disabled="true"]:hover { color: rgba(255,255,255,.8); }
.lightbox__btn svg { display: block; }

.lightbox__close { top: clamp(10px, 3vw, 30px); right: clamp(10px, 3vw, 30px); }
.lightbox__prev  { left: clamp(2px, 2vw, 22px); }
.lightbox__next  { right: clamp(2px, 2vw, 22px); }

/* Hebrew pages run right-to-left, so the arrow that means "previous" belongs on
   the right — main.js mirrors the keyboard and swipe directions to match. */
[dir="rtl"] .lightbox__close { right: auto; left: clamp(10px, 3vw, 30px); }
[dir="rtl"] .lightbox__prev  { left: auto; right: clamp(2px, 2vw, 22px); }
[dir="rtl"] .lightbox__next  { right: auto; left: clamp(2px, 2vw, 22px); }

/* On a phone the arrows would otherwise sit on top of the photograph. Dropping
   them to the bottom edge keeps the picture clear, and swiping does the same
   job anyway. */
@media (max-width: 560px) {
  .lightbox__media img { max-width: 96vw; max-height: 80vh; }
  .lightbox__prev,
  .lightbox__next {
    top: auto;
    bottom: max(18px, env(safe-area-inset-bottom));
  }
  .lightbox__prev,
  [dir="rtl"] .lightbox__next { left: 12vw; right: auto; }
  .lightbox__next,
  [dir="rtl"] .lightbox__prev { right: 12vw; left: auto; }
}

/* ==========================================================================
   VISUAL STORYTELLING — a curated pair, not a grid (see the matching block
   in main.js). Two contained cards side by side, proportions and generous
   surrounding whitespace modeled on Concept 1's restrained white-page feel
   — not full-bleed. Each poster crossfades into a looping muted preview as
   it scrolls into view, then opens fullscreen with sound on click, growing
   from its own on-screen box rather than cutting. Image/video scaling is
   left to the browser's default (high-quality) algorithm throughout — no
   filter is ever applied that would soften them. ========================================================================== */
.vs-grid {
  display: grid; grid-template-columns: 1fr 1fr;
  /* capping the pair's own width (not touching the columns, gap or aspect-ratio) is what
     shrinks both cards while keeping their proportions and the space between them exactly
     as they were — a smaller stage for the same composition, not a different one */
  max-width: clamp(560px, 62vw, 760px); margin: 0 auto;
  gap: clamp(28px, 4vw, 56px);
  padding-top: clamp(10px, 3vw, 30px);
  padding-bottom: clamp(80px, 11vw, 150px);
}
.vs-project { color: var(--ink); }
/* Fixed portrait box at all times — poster and video are both position:absolute/inset:0 inside
   it (identical dimensions), crossfading purely via opacity. The box itself used to change
   aspect-ratio on hover (2/3 → 16/9), which read as a jump/reflow; that's gone. Instead the
   whole box gets a slight scale + shadow lift on hover — transform/box-shadow only, so it's
   hardware-accelerated, stays perfectly centered (transform doesn't move surrounding layout),
   and never touches width/height. Gated to real hover devices (matches the play-on-hover JS
   gate in main.js) so touch taps never get stuck "hovered". */
.vs-project__media {
  position: relative; aspect-ratio: 2 / 3; overflow: hidden;
  background: #000; cursor: pointer;
  transition: transform .6s cubic-bezier(.22,.61,.36,1), box-shadow .6s cubic-bezier(.22,.61,.36,1);
  will-change: transform;
}
@media (hover: hover) {
  .vs-project__media:hover { transform: scale(1.045); box-shadow: 0 30px 70px rgba(17, 17, 17, .3); }
}
.vs-project__poster,
.vs-project__video {
  position: absolute; inset: 0;
  width: 100%; height: 100%; object-fit: cover;
  transition: opacity .9s ease;
}
.vs-project__video { opacity: 0; }
.vs-project__media.is-live .vs-project__poster { opacity: 0; }
.vs-project__media.is-live .vs-project__video { opacity: 1; }

.vs-project__body { padding-top: 1.3em; }
.vs-project__num {
  font-family: var(--font-display); font-size: .95rem; letter-spacing: .08em;
  color: var(--ink-soft); display: block; margin-bottom: .6em;
}
.vs-project__title { font-size: clamp(1.2rem, 2vw, 1.6rem); margin-bottom: .3em; }
.vs-project__line { color: var(--ink-soft); margin-bottom: 1.1em; line-height: 1.7; }
.vs-project__watch {
  display: inline-flex; align-items: center; gap: .7em;
  font-size: .72rem; letter-spacing: .22em; text-transform: uppercase;
  background: none; border: 0; border-bottom: 1px solid var(--ink);
  padding: 0 0 .3em; color: var(--ink);
  transition: border-color .3s ease, color .3s ease, gap .3s ease;
}
.vs-project__watch:hover { border-color: var(--accent); color: var(--accent); gap: 1em; }

@media (max-width: 760px) {
  .vs-grid { grid-template-columns: 1fr; max-width: 420px; gap: clamp(46px, 12vw, 70px); }
}
@media (prefers-reduced-motion: reduce) {
  .vs-project__media, .vs-project__poster, .vs-project__video { transition: none; }
}

/* ---------- quote band ---------- */
.quote {
  position: relative;
  padding: clamp(90px, 14vw, 190px) 0;
  text-align: center;
  color: #fff;
  overflow: hidden;
}
.quote__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.quote::after { content: ""; position: absolute; inset: 0; background: rgba(0,0,0,.4); }
.quote__inner { position: relative; z-index: 2; }
.quote blockquote {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3.4vw, 2.6rem);
  line-height: 1.35;
  margin: 0 auto 1em;
  max-width: 820px;
  padding: 0 24px;
}

/* ---------- form ---------- */
.form { display: grid; gap: 26px; }
/* `display: grid` above outranks the browser's own `[hidden] { display: none }`, so
   without this the form stays on screen after js/main.js sets form.hidden = true on
   a successful send, and the visitor sees the confirmation panel AND the form they
   just submitted. Scoped to .form on purpose — a blanket [hidden] rule would also
   hit the accessibility panel, which is display:flex while hidden by design. */
.form[hidden] { display: none; }
.form__row { display: grid; grid-template-columns: 1fr 1fr; gap: 26px; }
.field { display: flex; flex-direction: column; }
.field label {
  font-size: .68rem;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 9px;
}
.field input,
.field select,
.field textarea {
  font: inherit;
  font-size: .95rem;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--line);
  padding: 10px 0;
  border-radius: 0;
  transition: border-color .3s ease;
}
/* Focus used to be `outline: none` plus a border-colour change, which signalled
   focus by colour alone (1.4.1) and left no visible ring at all in Windows High
   Contrast mode (2.4.7). The ring is now real; the accent underline is kept on
   top of it because it is part of the form's look. */
.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-bottom-color: var(--accent);
}
.field input:focus,
.field select:focus,
.field textarea:focus { border-bottom-color: var(--accent); }

/* ---------- form validation ----------
   Errors are announced through .form__status (role="status") and tied to their
   field with aria-describedby — see the form handler in js/main.js. The red is
   never the only signal: the field also gets a thicker underline and the message
   itself spells out what is wrong. */
.field__error {
  display: none;
  margin-top: 7px;
  font-size: .78rem;
  letter-spacing: .02em;
  text-transform: none;
  color: var(--error);
}
/* .form-consent is a <label>, not a .field, so it needs naming here too — without
   it the consent error was built, given an id and pointed at by aria-describedby,
   but never actually shown to a sighted visitor. */
.field.has-error .field__error,
.form-consent.has-error .field__error { display: block; }
.field.has-error input,
.field.has-error select,
.field.has-error textarea { border-bottom: 2px solid var(--error); }
/* The contact form's consent tick-box. Named form-consent, NOT consent: the cookie
   banner further down this file owns .consent, and it is declared later, so a shared
   name handed this label the banner's `position: fixed; opacity: 0` and made a
   required field invisible and untickable on every device. Found 16 Aug 2026. */
.form-consent.has-error { color: var(--error); }
.form__status {
  margin: 0;
  font-size: .85rem;
  color: var(--error);
}
.form__status:empty { display: none; }
/* While the enquiry is in flight the same live region carries a neutral "Sending"
   line — it is not an error, so it must not be red. */
.form__status--busy { color: var(--ink-soft); }

/* ---------- sent confirmation ----------
   Replaces the form in place after a successful send; the visitor never leaves
   /contact.html. Sized and spaced like the form it stands in for, so the page does
   not lurch when they swap. See the submit handler in js/main.js. */
.form-sent {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  background: #fff;
  padding: clamp(30px, 7vw, 48px) clamp(20px, 5vw, 44px);
  text-align: center;
}
.form-sent:focus { outline: none; }            /* focused programmatically, not by the visitor */
.form-sent:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
.form-sent__mark {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  margin: 0 auto clamp(16px, 3vw, 22px);
  border: 1px solid var(--accent);
  border-radius: 50%;
  color: var(--accent);
}
.form-sent__mark svg { width: 24px; height: 24px; display: block; }
.form-sent__title {
  font-family: var(--font-display);
  font-weight: 300;
  font-size: clamp(1.35rem, 4.4vw, 1.85rem);
  line-height: 1.25;
  color: var(--ink);
  margin: 0 0 .6em;
}
.form-sent__lede {
  margin: 0 auto;
  max-width: 44ch;
  font-size: .9rem;
  color: var(--ink-soft);
}
/* The site header and the floating language / accessibility buttons are fixed, so
   anything scrolled to the very top of the viewport sits under them. Keeping a
   field's scroll position clear of that band is what stops an error message from
   being half-covered — in Hebrew it is the beginning of the sentence that is lost. */
.form .field,
.form-consent,
.form__status { scroll-margin-top: 190px; scroll-margin-bottom: 40px; }
.field textarea { resize: vertical; min-height: 130px; }
.field select { appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--ink-soft) 50%), linear-gradient(135deg, var(--ink-soft) 50%, transparent 50%); background-position: calc(100% - 14px) 20px, calc(100% - 8px) 20px; background-size: 6px 6px; background-repeat: no-repeat; }
/* The whole row is a <label>, so the sentence is part of the tick target, not just
   the box. The padding is what makes that target comfortable under a finger; the
   matching negative margin cancels it again so the form's 26px rhythm and the
   desktop layout are exactly as they were. flex-wrap lets the error message drop
   onto its own line instead of being squeezed in beside the sentence.
   The box itself is drawn at the very end of this file — see "CONSENT TICK-BOX". */
.form-consent {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  align-items: flex-start;
  font-size: .84rem;
  color: var(--ink-soft);
  cursor: pointer;
  padding: 6px 0;
  margin: -6px 0;
}
/* The sentence takes the rest of the row and wraps inside it. Without min-width:0 a
   long unbroken word could push the box off the side of a 320px screen. */
.form-consent > span { flex: 1 1 0; min-width: 0; }
.form-consent .field__error { flex: 1 0 100%; margin-top: 2px; }
.hp { position: absolute; left: -9999px; }

/* ---------- footer ----------
   Redesigned as one compact, centered stack — no nav, no far-right email, no
   full-width bar — reads as a quiet signature at the very end of every page
   rather than a conventional site footer. Same treatment on every page. */
.footer {
  background: var(--bg);
  color: var(--ink-soft);
  padding: clamp(40px, 6vw, 64px) 0;
  font-size: .84rem;
}
.footer__inner {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 clamp(20px, 5vw, 64px);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 18px;
}
.footer__logo {
  font-family: var(--font-display);
  color: var(--ink);
  font-size: 1rem;
  letter-spacing: .3em;
  text-transform: uppercase;
}
.footer__divider { width: 40px; height: 1px; background: var(--ink); opacity: .15; }
.footer__desc {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: .35em;
  font-size: .74rem;
  letter-spacing: .08em;
  color: var(--ink-soft);
}
.footer__icons {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(26px, 4vw, 42px);
}
.footer__icon {
  display: flex; align-items: center; justify-content: center;
  width: 20px; height: 20px;
  color: var(--ink-soft);
  transition: color .35s ease, transform .35s ease;
}
.footer__icon svg { width: 100%; height: 100%; }
.footer__icon:hover, .footer__icon:focus-visible { color: var(--accent); transform: translateY(-2px); }
.footer__copyright {
  margin: 0;
  font-size: .68rem;
  letter-spacing: .05em;
  color: var(--ink-soft);
  /* the .8 opacity this used to carry knocked #666 back to an effective #838382,
     which is 3.54:1 on the cream background — below the 4.5:1 AA minimum for text
     this small. Dropped rather than compensated for, so the colour stays exactly
     var(--ink-soft) like every other quiet line on the page. */
}

/* ---------- footer legal row: privacy / accessibility / contact ----------
   Deliberately styled as the quietest thing in the footer — same muted ink and
   same small caps as the copyright line directly beneath it, so it reads as
   fine print rather than as a second navigation. */
.footer__legal {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: .55em 1.1em;
  margin: 0 0 1.1em;
}
.footer__legal a,
.footer__legal button {
  font-family: var(--font-body);
  font-size: .68rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-soft);
  background: none; border: 0; padding: 0; cursor: pointer;
  border-bottom: 1px solid transparent;
  transition: color .3s ease, border-color .3s ease;
}
.footer__legal a:hover, .footer__legal a:focus-visible,
.footer__legal button:hover, .footer__legal button:focus-visible {
  color: var(--accent); border-bottom-color: var(--accent);
}
.footer__legal .footer__legal-sep { color: var(--line); font-size: .68rem; }

/* ---------- reveal on scroll ---------- */
.reveal { opacity: 0; transform: translateY(26px); transition: opacity .9s ease, transform .9s ease; }
.reveal.is-in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  html { scroll-behavior: auto; }
}

/* ---------- responsive ---------- */
@media (max-width: 900px) {
  .nav__links { display: none; }
  .nav__toggle { display: block; }
  .grid, .grid--2 { column-count: 2; }
  .cards { grid-template-columns: 1fr; max-width: 460px; margin: 0 auto; }
  .split { grid-template-columns: 1fr; }
  .split--reverse .split__media { order: 0; }
  .split__body { max-width: none; }
  .gateway__grid { grid-template-columns: repeat(2, 1fr); grid-template-rows: none; }
  .gateway__tile--love,
  .gateway__tile--weddings,
  .gateway__tile--family,
  .gateway__tile--documentary,
  .gateway__tile--about { grid-column: span 2; grid-row: auto; min-height: 62vh; }
  .gateway__tile--contact { grid-column: span 2; min-height: 50vh; }
}
@media (max-width: 560px) {
  .grid, .grid--2, .grid--editorial { column-count: 1; }
  .grid--editorial { column-gap: clamp(20px, 6vw, 36px); }
  .grid--editorial .grid__item { margin-bottom: clamp(20px, 6vw, 36px); }
  /* The Wedding and Family galleries stay two-up on a phone. This is only the
     no-JavaScript fallback — with JS the same two grids are rebuilt into explicit
     .grid__col columns by colsFor()/layoutMasonry() in main.js, which has the
     matching rule and must be kept in step with this one. The 8px gutter is the
     same one those two galleries already use at every other width, so the rhythm
     does not change, only how many photographs fit across. */
  .grid--editorial.grid--compact,
  .grid--editorial.grid--dense { column-count: 2; column-gap: 8px; }
  .grid--editorial.grid--compact:not(.js-cols) .grid__item,
  .grid--editorial.grid--dense:not(.js-cols) .grid__item { margin-bottom: 8px; }
  /* …and in the JS layout the vertical rhythm must come from the column's flex
     `gap` alone. `.grid--editorial.js-cols .grid__item { margin: 0 }` further up
     is meant to do that, but `.grid--editorial.grid--compact .grid__item
     { margin-bottom: 8px }` sits after it at equal specificity and wins, so 8px
     of margin was still being stacked on top of the 8px gap — a 16px vertical
     gutter against an 8px horizontal one. Measured, not assumed.

     This is a long-standing quirk that applies at every width, and on a desktop
     three-column gallery it is barely visible, so it is corrected HERE rather
     than at the source: fixing the shared rule would change the desktop
     galleries, which is exactly what must not happen. Two columns on a 390px
     screen is where a mismatched gutter actually shows. */
  .grid--editorial.grid--compact.js-cols .grid__item,
  .grid--editorial.grid--dense.js-cols .grid__item { margin: 0; }
  .form__row { grid-template-columns: 1fr; }
  .footer__top { flex-direction: column; }
  .play-btn__icon { width: 64px; height: 64px; }
}

/* ==========================================================================
   NARROW-SCREEN FIXES

   Measured at 320 / 360 / 375 / 390 / 414 / 430px, not eyeballed.
   ========================================================================== */

/* ---------- the wordmark was pushing the Menu button off the screen ----------
   .nav__logo is white-space:nowrap and was 283px wide at its desktop size. Inside a
   320px bar with 20px padding either side and a 56px "Menu" button, only 212px was
   available — so the flex row overflowed and the Menu button sat 39px PAST the right
   edge of the viewport, unreachable. It caused no scrollbar (the nav is position:fixed,
   so it never contributed to document width), which is exactly why it was invisible
   in a normal desktop check.

   The mobile drawer is the ONLY navigation below 900px, so this made the whole site
   un-navigable on the narrowest phones. The wordmark now scales with the viewport
   instead of holding a fixed size. */
@media (max-width: 560px) {
  .nav__logo {
    font-size: clamp(.82rem, 3.6vw, 1.05rem);
    letter-spacing: .2em;
  }
}

/* Touch-target sizing lives at the very end of this file — several of the controls
   it has to grow are styled further down, and an equal-specificity rule placed here
   would simply be overwritten by them. See "TOUCH TARGETS" at the bottom. */

/* ---------- smallest text sizes on phones ----------
   The 0.68rem eyebrow/caption scale renders at 10.9px, which is legible on a desktop
   monitor and genuinely hard on a phone held at arm's length. Raised to ~11.5px below
   560px only — desktop typography is untouched. */
@media (max-width: 560px) {
  .footer__copyright,
  .footer__legal a,
  .footer__legal button,
  .footer__legal .footer__legal-sep,
  .lang-switch button,
  .grid__cap,
  .hero__scroll,
  .slideshow__scroll { font-size: .72rem; }
}

/* ==========================================================================
   HEROES ON PHONES — a crop fix, not a redesign.

   Every hero on the site is a landscape video shown full-bleed with
   `object-fit: cover` (16:9 on the home, family, documentary, about and contact
   heroes; 1.60:1 on the wedding hero; 1.74:1 on the two film pages). On a
   desktop window that shape is close to the video's own, so nearly the whole
   frame survives. Held upright a phone is nothing like it: 390x844 is 0.46:1
   against a 1.78:1 video, so `cover` scales the video until it fills the HEIGHT
   and discards everything outside a narrow vertical band — only about 26% of
   the filmed width was reaching the screen. The complaint was "almost half cut
   off"; measured, it was closer to three quarters.

   Two mobile-only corrections. Neither touches a video file, and neither exists
   above 700px, so desktop and tablet render from exactly the same rules as
   before.

     1. A slightly shorter hero. 100svh -> 72svh makes the visible box less
        extremely tall, which widens the surviving band from ~26% to ~36% of
        every shot — half as much again — and lets the top of the next section
        show, so the page reads as scrollable instead of as a dead end. The hero
        is still far and away the dominant thing on the screen.

     2. A per-video `object-position`, so the band that survives is the one with
        the subject in it. This is the half that actually answers the complaint:
        four of the eight heroes are composed off-centre and a centred crop was
        throwing their subject away completely.

   Vertical position is deliberately not set anywhere below: because the
   overflow is entirely horizontal here, the full height of every frame is on
   screen already and a Y value would do nothing.

   The selectors key off the video's FILE NAME, which means one rule covers both
   the English page and its Hebrew twin (tools/build-he.py rewrites the path but
   never the file name) and no page markup has to change. Swap a hero for a
   differently-named file and its rule here must be renamed with it — otherwise
   that hero quietly falls back to a centred crop.
   ========================================================================== */
@media (max-width: 700px) {
  .hero,
  .slideshow {
    height: 72svh;
    min-height: 440px;
  }
  .hero--project { height: 64svh; min-height: 400px; }

  /* Home. The bride is the subject and holds the left of centre through the
     whole push-in; the groom is hard against the right edge and cannot be kept
     as well — at this crop width no single band contains both. */
  .slideshow__video[src$="hero-d.mp4"] { object-position: 30% 50%; }

  /* Wedding collection. Keeps the couple and the bouquet between them, and
     loses the guest in the near foreground, who is the least of the frame. */
  .hero__video[src$="love-hero-g.mp4"] { object-position: 35% 50%; }

  /* Family. The floating toy IS the shot and it drifts through the left third,
     so the centred crop was showing a phone-shaped rectangle of open water with
     the subject entirely outside it. The worst of the eight. */
  .hero__video[src$="family-hero-o.mp4"] { object-position: 8% 50%; }

  /* Documentary. The farmhouse sits in the right half with the washing line
     running into it; centred, the crop kept a length of line and cut the house
     that the line is about. */
  .hero__video[src$="documentary-hero-l.mp4"] { object-position: 75% 50%; }

  /* about-hero-k, contact-hero-j, between-us-hero and last-home-hero are all
     centre-composed — the laid table, the embrace, the figure among the paper
     stars, the arched window — and every one of them lands inside the middle
     band on its own. They are left at the browser's default 50% deliberately.
     Don't add rules for them just to make the set look complete. */

  /* And the copy comes off the middle of the frame. A tighter crop means the
     subject fills far more of the hero than it does on a desktop window, and the
     centred title was landing straight across it — measured, the text band ran
     from 35% to 51% of the hero's height, which on the home and wedding heroes
     is exactly where the bride's face is. Dropping the block to the base of the
     hero puts it over the quiet bottom of every one of these shots instead.

     Desktop is untouched and should stay that way: at desktop crop widths the
     faces sit well above the centred title and there is nothing to move away
     from. The 6.5em below the subtitle is what lifts the block off centre in the
     centred version, so it is zeroed here — bottom-anchored, it would only push
     the text back up into the frame. The two film heroes carry no text at all
     (their title sits in .project-title below the video), so none of this
     reaches them. */
  .hero { place-items: end center; }
  /* .slideshow__inner needs align-CONTENT, not align-items. It is a full-height
     grid holding three separate children, so each one gets its own implicit row
     and those rows stretch to share the height — align-items would only move
     each line to the bottom of its own stretched row and leave the group spread
     down the whole hero (measured: it still ran 23%-88%). align-content sizes
     the rows to their content and packs the group at the end, which is what the
     single-child .hero above gets for free. */
  .slideshow__inner { align-content: end; }
  .hero__inner,
  .slideshow__inner { padding-bottom: 92px; }   /* clears the 34px scroll cue */
  .hero__sub,
  .slideshow__sub { margin-bottom: 0; }

  /* The scrim gains a heavier foot to go with it. Measured against the actual
     frames, white text over the new band sat at 3.1:1 on the About hero (a white
     tablecloth fills that shot) and 2.5:1 on the documentary hero's sunlit wall
     — under the 4.5:1 that small text needs, and the old centred placement was
     no better, at 2.5:1 and 3.4:1. Weighting the bottom of the existing gradient
     lifts every hero to 4.1:1 or above without touching the top 45%, so the part
     of the frame the subject is in keeps exactly its current brightness.
     .hero--project is excluded: those two carry no text over the video, so there
     is nothing to make legible and no reason to darken a film still. */
  .hero:not(.hero--project)::after {
    background: linear-gradient(180deg,
      rgba(0,0,0,.34) 0%, rgba(0,0,0,.16) 45%, rgba(0,0,0,.40) 62%, rgba(0,0,0,.74) 100%);
  }
  .slideshow::after {
    background: linear-gradient(180deg,
      rgba(0,0,0,.34) 0%, rgba(0,0,0,.12) 45%, rgba(0,0,0,.40) 62%, rgba(0,0,0,.74) 100%);
  }
}

/* ---------- landscape phones ----------
   A 100vh chapter on a 375x390 landscape screen leaves almost no room for its text.
   Letting those sections size to their content prevents the title and link from being
   pushed out of the visible area. */
@media (max-height: 460px) and (orientation: landscape) {
  .chapter { min-height: 0; padding: 64px 0; }
  /* Named explicitly so the phone rule above (.chapter[data-chapter], which is a
     more specific selector and would otherwise win here regardless of order) does
     not quietly reinstate a minimum height on a 375px-tall screen. */
  .chapter[data-chapter] { min-height: 0; }
  .hero, .slideshow { min-height: 420px; }
}

/* ==========================================================================
   OPENING SEQUENCE — the homepage intro. A real over-the-shoulder video push
   into the camera LCD, ending on a shutter click that fires an instant
   full-viewport white flash straight into the homepage (no fade, no
   expanding shape — see js/main.js for the click-sync timing). This is the
   permanent homepage intro; treat future changes here as refinements, not a
   rewrite.
   ========================================================================== */
.opener{
  position:fixed; inset:0; z-index:1000; background:var(--ink);
  overflow:hidden; pointer-events:none;
}
body.is-opening{ overflow:hidden; }

.opener__video{
  position:absolute; inset:0; width:100%; height:100%; object-fit:cover;
}
.opener__vignette{
  position:absolute; inset:0; pointer-events:none;
  background:linear-gradient(180deg, rgba(0,0,0,.30) 0%, rgba(0,0,0,0) 26%, rgba(0,0,0,0) 62%, rgba(0,0,0,.4) 100%);
}

/* the transition point: an instant, blinding flash — the whole viewport floods with solid white
   in one hit, not a growing shape or a soft dissolve — that exists purely to hide the swap into
   the real homepage underneath. Never a visual effect for its own sake. */
.opener__flash{
  position:fixed; inset:0; z-index:1005; opacity:0; pointer-events:none;
  background:#ffffff;
}

.opener__caption{
  position:absolute; left:8%; bottom:9%; z-index:2;
  font-size:.68rem; letter-spacing:.28em; text-transform:uppercase; color:rgba(255,255,255,.55);
  opacity:0; transition:opacity 1s ease .3s;
}
.opener.is-settled .opener__caption{ opacity:1; }

.opener__skip{
  position:absolute; right:clamp(20px,4vw,40px); top:clamp(20px,4vw,40px); z-index:3;
  font-size:.66rem; letter-spacing:.2em; text-transform:uppercase; color:rgba(255,255,255,.65);
  background:none; border:1px solid rgba(255,255,255,.3); padding:.7em 1.3em; cursor:pointer;
  opacity:0; pointer-events:auto; transition:opacity .6s ease, border-color .3s ease, color .3s ease;
}
.opener__skip:hover,
.opener__skip:focus-visible{ border-color:var(--accent-on-dark); color:var(--accent-on-dark); }
.opener__skip.is-visible{ opacity:1; }

/* ---------- the opening sequence on a phone ----------
   The same crop arithmetic as the heroes, and the most damaging instance of it.
   The intro is a 2560x1432 (1.79:1) film frame, and `object-fit: cover` in a
   390x844 portrait viewport was putting 25.8% of its width on screen. This shot
   in particular cannot survive that: it is an over-the-shoulder push into the
   camera's LCD, with the photographer on the left and the camera and its screen
   centre-right, so a narrow central band cut the camera in half early on and
   later showed nothing but a magnified piece of the LCD image. Hence "the intro
   is too large for the phone screen" — it was not too large, it was too close.

   `contain` shows the whole frame, as wide as the screen allows, centred on the
   opener's own near-black background. Nothing is cropped, nothing overflows, and
   the proportions are exactly the film's own — contain never distorts. The
   timing, the shutter-click flash, the animation and the type are all untouched,
   and desktop keeps `cover`, where the window is already close to the film's
   shape and full-bleed is the whole point.

   The vignette is deliberately left alone: its gradient is transparent between
   26% and 62% of the height, which is exactly where the letterboxed band lands,
   so it darkens only the black bars and changes nothing visible. */
@media (max-width: 700px) {
  .opener__video{ object-fit:contain; }
  /* Follow the frame down. Pinned to the foot of the screen the caption would be
     stranded roughly 200px below the picture on an empty black field; under the
     band it reads as a caption to it. The band is 100vw / 1.79 = 55.94vw tall and
     vertically centred, so its lower edge sits 50vh - 27.97vw above the bottom.
     max() is the guard: on a short landscape phone still under 700px wide the
     band nearly fills the height and that figure goes negative, which would put
     the caption off screen. The 46px is the clear gap plus the caption's own line
     box (~19px at this size), so the type sits about 27px below the picture. */
  .opener__caption{ bottom:max(20px, calc(50vh - 27.97vw - 46px)); left:24px; }
}

/* ==========================================================================
   LANGUAGE SWITCHER — minimal EN / עברית toggle, sits in the nav and drawer.
   ========================================================================== */
.lang-switch {
  display: flex; align-items: center; gap: .5em;
  font-size: .68rem; letter-spacing: .12em; text-transform: uppercase;
}
.lang-switch button {
  background: none; border: 0; padding: 0; cursor: pointer; color: inherit;
  opacity: .55; transition: opacity .3s ease, color .3s ease;
  font-family: inherit; font-size: inherit; letter-spacing: inherit;
}
.lang-switch button:hover { opacity: .85; }
.lang-switch button.is-active-lang { opacity: 1; color: var(--accent); }
.lang-switch__sep { opacity: .35; }
.drawer .lang-switch {
  margin-top: clamp(20px, 4vw, 36px);
  font-size: .72rem;
}

/* ==========================================================================
   RTL — activated via <html dir="rtl"> (see i18n/i18n.js). Mirrors the
   site's chrome (nav, thread rail, footer) and right-aligns body text so
   Hebrew reads naturally, while leaving photography, video and the overall
   grid/masonry mechanics untouched — the imagery itself is never mirrored.
   ========================================================================== */
/* Hebrew shouldn't read as an English site in translation — Heebo (a clean Hebrew/Latin
   grotesk) throughout, headings included: no separate serif for titles, just a heavier Heebo
   weight. Every heading, button, nav link and paragraph already goes through
   var(--font-display) / var(--font-body), so overriding the two variables here retypesets the
   entire RTL site without touching individual selectors. */
[dir="rtl"] {
  --font-display: "Heebo", Arial, sans-serif;
  --font-body: "Heebo", Arial, sans-serif;
}
[dir="rtl"] h1, [dir="rtl"] h2, [dir="rtl"] h3, [dir="rtl"] h4,
[dir="rtl"] .chapter__title, [dir="rtl"] .nav__logo, [dir="rtl"] .footer__logo {
  font-weight: 700; /* Heebo Bold stands in for the site's separate display face */
}
[dir="rtl"] body { text-align: right; }
[dir="rtl"] .chapter__title,
[dir="rtl"] .hero h1,
[dir="rtl"] .slideshow__inner h1,
[dir="rtl"] blockquote { line-height: 1.22; } /* Hebrew serif caps run taller than Latin at the same size */
[dir="rtl"] .eyebrow,
[dir="rtl"] .nav__links a,
[dir="rtl"] .footer__desc,
[dir="rtl"] .btn,
[dir="rtl"] .chapter__link,
[dir="rtl"] .grid__cap { letter-spacing: .05em; } /* Hebrew doesn't want Latin-style wide tracking */
[dir="rtl"] .nav { flex-direction: row-reverse; }
[dir="rtl"] .drawer__close { right: auto; left: clamp(20px, 5vw, 64px); }
[dir="rtl"] .chapter__body { text-align: right; }
[dir="rtl"] .chapter__link { flex-direction: row-reverse; }
[dir="rtl"] .chapter--right .chapter__link { flex-direction: row; }
[dir="rtl"] .thread { right: auto; left: clamp(18px, 3vw, 34px); }
[dir="rtl"] .thread__label { right: auto; left: 22px; }
[dir="rtl"] .form__row,
[dir="rtl"] .field { text-align: right; }
[dir="rtl"] input,
[dir="rtl"] textarea,
[dir="rtl"] select { text-align: right; }
/* No flex-direction here on purpose. A plain `row` flex line ALREADY runs
   right-to-left inside dir="rtl", so it puts the tick-box at the start of the
   sentence, on the right, which is where a Hebrew reader looks for it. Adding
   `row-reverse` — which this rule used to do — reverses that back to
   left-to-right and parks the box at the far END of the sentence instead. */
[dir="rtl"] .form-consent { text-align: right; }

/* Replaces the previous patchwork of per-element Hebrew font-size overrides (body copy only,
   headings/buttons/nav deliberately excluded). She's now asked for the opposite: the ENTIRE
   Hebrew site — headings included — one uniform step down from the English site, nothing
   excluded. Scaling the RTL <html> root itself (rather than body, or individual classes) is
   what actually reaches everything in one place: virtually every font-size in this stylesheet
   is in rem or em, so every element sized off the root (headings) or off an ancestor that's
   ultimately rooted there (body copy, buttons, nav, footer, cards) shrinks by the same 10% —
   one accessibility-stepper step (see js/a11y.js DEFAULTS.scale, where one step = ±10%) — while
   every ratio between them (hierarchy, spacing driven by em/rem, line-height) stays intact,
   since it's all still relative to the same root, just a smaller one. English is untouched:
   this selector only ever matches when <html dir="rtl">. */
[dir="rtl"] { font-size: calc(90% * var(--a11y-scale, 1)); }
/* body's own font-size is a flat 16px (not relative), so plain unclassed text (<p>, <li>, etc.
   that inherit straight from body) would otherwise ignore the root scale above entirely — this
   makes body relative to the (now smaller) root too, so that inherited text shrinks along with
   everything else instead of silently staying at English size. */
[dir="rtl"] body { font-size: 1rem; }

/* ==========================================================================
   ACCESSIBILITY — a custom panel (no third-party widget), toggled from the
   nav, next to the language switcher. Every effect below is applied as a
   class or CSS custom property on <html> by js/a11y.js and persists via
   localStorage, independent of language — switching EN/HE never resets it.
   ========================================================================== */
.a11y-toggle {
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; padding: 0; margin-inline-start: .6em;
  background: none; border: 1px solid currentColor; border-radius: 50%;
  color: inherit; cursor: pointer; opacity: .75;
  transition: opacity .3s ease, border-color .3s ease, color .3s ease;
}
.a11y-toggle:hover, .a11y-toggle[aria-expanded="true"] { opacity: 1; color: var(--accent); border-color: var(--accent); }
.a11y-toggle svg { width: 16px; height: 16px; fill: currentColor; }
.drawer .a11y-toggle { margin: 18px auto 0; }

/* ==========================================================================
   FLOATING CONTROLS — accessibility + language switcher, living just below
   the nav bar instead of inside it. Left side on LTR (mirrors to the right
   under [dir="rtl"] below) so they never sit under the .thread rail, which
   lives on the opposite edge, and never cross into the centred hero copy.
   Own frosted-card background so both buttons stay legible over the dark
   slideshow, the photo chapters and the cream sections alike, instead of
   relying on inherited text colour the way the old nav-bar versions did.
   ========================================================================== */
.nav-float {
  position: fixed;
  top: calc(var(--nav-h) + clamp(14px, 2.4vw, 24px));
  left: clamp(20px, 5vw, 64px);
  z-index: 95;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 10px;
}
.nav-float .a11y-toggle {
  margin-inline-start: 0;
  width: 38px; height: 38px;
  background: rgba(248, 247, 244, .92);
  backdrop-filter: blur(6px);
  border-color: var(--line);
  color: var(--ink);
  box-shadow: 0 14px 44px rgba(17, 17, 17, .08);
}
.nav-float .a11y-toggle:hover,
.nav-float .a11y-toggle[aria-expanded="true"] { color: var(--accent); border-color: var(--accent); }
.nav-float__lang {
  background: rgba(248, 247, 244, .92);
  backdrop-filter: blur(6px);
  border: 1px solid var(--line);
  border-radius: 15px;
  padding: 8px 12px;
  box-shadow: 0 14px 44px rgba(17, 17, 17, .08);
}
@media (max-width: 560px) {
  .nav-float { left: clamp(16px, 5vw, 24px); }
}
[dir="rtl"] .nav-float { left: auto; right: clamp(20px, 5vw, 64px); align-items: flex-end; }
@media (max-width: 560px) {
  [dir="rtl"] .nav-float { right: clamp(16px, 5vw, 24px); }
}

.a11y-panel {
  position: fixed; inset: 0; z-index: 300;
  display: flex; justify-content: flex-end;
  background: rgba(17, 17, 17, .38);
  opacity: 0; pointer-events: none;
  transition: opacity .3s ease;
}
.a11y-panel.is-open { opacity: 1; pointer-events: auto; }
.a11y-panel__inner {
  width: min(380px, 100vw); height: 100%; overflow-y: auto;
  background: var(--bg); color: var(--ink);
  padding: clamp(24px, 4vw, 36px);
  transform: translateX(100%);
  transition: transform .35s cubic-bezier(.16,1,.3,1);
  box-shadow: -20px 0 60px rgba(0,0,0,.15);
}
.a11y-panel.is-open .a11y-panel__inner { transform: translateX(0); }
[dir="rtl"] .a11y-panel { justify-content: flex-start; }
[dir="rtl"] .a11y-panel__inner { transform: translateX(-100%); }
[dir="rtl"] .a11y-panel.is-open .a11y-panel__inner { transform: translateX(0); }

.a11y-panel__header {
  display: flex; align-items: center; justify-content: space-between;
  padding-bottom: 20px; margin-bottom: 20px; border-bottom: 1px solid var(--line);
}
.a11y-panel__header h2 {
  font-family: var(--font-display); font-size: 1.5rem; margin: 0;
}
.a11y-panel__close {
  width: 32px; height: 32px; display: flex; align-items: center; justify-content: center;
  background: none; border: 1px solid var(--line); border-radius: 50%;
  font-size: 1.1rem; line-height: 1; cursor: pointer; color: inherit;
  transition: border-color .3s ease, color .3s ease;
}
.a11y-panel__close:hover { border-color: var(--accent); color: var(--accent); }

.a11y-row {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 13px 0; border-bottom: 1px solid var(--line);
  font-size: .84rem;
}
.a11y-row__label { flex: 1; }

/* boolean rows: an accessible switch, role="switch" on the button itself */
.a11y-switch {
  position: relative; width: 40px; height: 22px; flex: 0 0 auto;
  background: var(--bg-soft); border: 1px solid var(--line); border-radius: 999px;
  cursor: pointer; padding: 0;
  transition: background .25s ease, border-color .25s ease;
}
.a11y-switch::after {
  content: ""; position: absolute; top: 1px; left: 1px; width: 18px; height: 18px;
  background: #fff; border-radius: 50%; box-shadow: 0 1px 3px rgba(0,0,0,.25);
  transition: transform .25s cubic-bezier(.16,1,.3,1);
}
.a11y-switch[aria-checked="true"] { background: var(--accent); border-color: var(--accent); }
.a11y-switch[aria-checked="true"]::after { transform: translateX(18px); }
[dir="rtl"] .a11y-switch[aria-checked="true"]::after { transform: translateX(-18px); }

/* stepper (text size) */
.a11y-stepper { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.a11y-stepper button {
  width: 28px; height: 28px; border-radius: 50%; border: 1px solid var(--line);
  background: none; color: inherit; cursor: pointer; font-size: .78rem; line-height: 1;
  transition: border-color .3s ease, color .3s ease;
}
.a11y-stepper button:hover { border-color: var(--accent); color: var(--accent); }

/* cycler select (letter spacing / line height) */
.a11y-select {
  flex: 0 0 auto; font-size: .78rem; font-family: inherit; color: inherit;
  background: var(--bg-soft); border: 1px solid var(--line); border-radius: 4px;
  padding: 5px 8px; cursor: pointer;
}

.a11y-reset {
  width: 100%; margin-top: 24px; padding: 12px; text-align: center;
  background: none; border: 1px solid var(--ink); color: var(--ink);
  font-size: .74rem; letter-spacing: .14em; text-transform: uppercase; cursor: pointer;
  transition: background .3s ease, color .3s ease;
}
.a11y-reset:hover { background: var(--ink); color: #fff; }

.a11y-skiplink {
  position: fixed; top: -60px; left: 50%; transform: translateX(-50%); z-index: 400;
  background: var(--ink); color: #fff; padding: 12px 22px; border-radius: 4px;
  font-size: .78rem; letter-spacing: .08em; transition: top .25s ease;
}
.a11y-skiplink:focus { top: 14px; }

/* ---------- functional effects, applied as classes on <html> ---------- */
html.a11y-readable-font, html.a11y-readable-font body {
  font-family: "Atkinson Hyperlegible", var(--font-body) !important;
}
[dir="rtl"] html.a11y-readable-font, [dir="rtl"] html.a11y-readable-font body {
  font-family: "Heebo", var(--font-body) !important; /* no Hebrew glyphs in Atkinson Hyperlegible */
}
html.a11y-readable-font * { font-family: inherit !important; }

html.a11y-heading-highlight h1,
html.a11y-heading-highlight h2,
html.a11y-heading-highlight h3 {
  background: rgba(255, 214, 0, .28); box-shadow: 0 0 0 6px rgba(255, 214, 0, .28); outline: 2px solid #a8860a;
}

html.a11y-link-highlight a {
  outline: 2px solid var(--accent) !important; outline-offset: 2px;
  background: rgba(0, 85, 140, .08) !important; text-decoration: underline !important;
}

html.a11y-contrast body { background: #000 !important; color: #fff !important; }
html.a11y-contrast a, html.a11y-contrast .btn, html.a11y-contrast button { color: #ffe500 !important; }
html.a11y-contrast img, html.a11y-contrast video { filter: contrast(1.15) grayscale(.2); }

html.a11y-dark body { background: #14120f; color: #ece7de; }
html.a11y-dark .chapter--card,
html.a11y-dark .a11y-panel__inner,
html.a11y-dark .footer,
html.a11y-dark .signature,
html.a11y-dark section[style*="background:#fff"],
html.a11y-dark section[style*="background:var(--bg)"],
html.a11y-dark .section--soft { background: #1c1a16 !important; color: #ece7de !important; }
html.a11y-dark .hero-spacer, html.a11y-dark .section-spacer { background: #14120f !important; }
html.a11y-dark .signature__mark { color: #ece7de !important; }
html.a11y-dark .signature__icon { color: #b9b3a6 !important; }

html.a11y-invert { filter: invert(1) hue-rotate(180deg); }
html.a11y-invert img, html.a11y-invert video { filter: invert(1) hue-rotate(180deg); }

html.a11y-grayscale { filter: grayscale(1); }

html.a11y-reduce-motion *,
html.a11y-reduce-motion *::before,
html.a11y-reduce-motion *::after {
  animation-duration: 0.001ms !important; animation-iteration-count: 1 !important;
  transition-duration: 0.001ms !important; scroll-behavior: auto !important;
}

html.a11y-spacing-wide body, html.a11y-spacing-wide body * { letter-spacing: .06em !important; }
html.a11y-spacing-wider body, html.a11y-spacing-wider body * { letter-spacing: .12em !important; }
html.a11y-lh-relaxed body, html.a11y-lh-relaxed body * { line-height: 1.9 !important; }
html.a11y-lh-loose body, html.a11y-lh-loose body * { line-height: 2.3 !important; }

html.a11y-focus-strong :focus-visible {
  outline: 3px solid var(--accent) !important; outline-offset: 3px !important;
  box-shadow: 0 0 0 6px rgba(0, 85, 140, .25) !important;
}
html.a11y-keyboard-nav :focus { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ==========================================================================
   BASELINE ACCESSIBILITY — applies to everyone, always, with no toggle.

   The two rules above are opt-in enhancements from the accessibility panel.
   They are not a substitute for the site being accessible by default, so the
   fundamentals live here: a keyboard user who never opens the panel still gets
   a visible focus ring on every interactive element.
   ========================================================================== */

/* ---------- visible focus, site-wide (WCAG 2.4.7) ----------
   :focus-visible rather than :focus, so a mouse click on a button does not leave
   a ring behind — the indicator appears for keyboard and other non-pointer input,
   which is who needs it. */
:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: 2px;
}
/* Over photography and dark chapters the dark-blue ring disappears, so anything
   sitting on a dark surface gets the pale variant instead. */
.chapter :focus-visible,
.hero :focus-visible,
.slideshow :focus-visible,
.opener :focus-visible,
.footer--dark :focus-visible,
.video-modal :focus-visible,
.lightbox :focus-visible,
.film-select :focus-visible,
.nav--over :focus-visible {
  outline-color: var(--accent-on-dark);
}
/* The skip link paints its own dark chip; a matching dark ring on top of it is
   invisible, so it uses the light one. */
.a11y-skiplink:focus-visible { outline-color: #fff; outline-offset: 2px; }

/* ---------- screen-reader-only text ----------
   Used by the consent live region and by the icon-link labels. Kept focusable-safe:
   clip-path rather than display:none, so assistive tech still reads it. */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  margin: -1px; padding: 0; border: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ---------- honeypot ----------
   Netlify's spam trap. It has to stay in the DOM and stay submittable, but a
   screen-reader user must not be offered a field whose only purpose is to catch
   robots — filling it in would silently discard a real enquiry. It is therefore
   hidden from assistive tech (aria-hidden) and from the tab order (tabindex=-1)
   in contact.html, and pushed off-screen here. */
.hp { position: absolute !important; left: -9999px !important; top: auto; width: 1px; height: 1px; overflow: hidden; }

/* ==========================================================================
   CONSENT BANNER — see js/consent.js.

   Anchored bottom-centre as a narrow card rather than a full-width bar: at 320px
   a full-width bar with two buttons eats roughly a third of the viewport, and the
   brief is specifically that consent UI must not cover important content. The
   card is also capped in height and scrolls internally, so enlarging text (or the
   panel's own text-size control) can never push the buttons off-screen.
   ========================================================================== */
.consent {
  position: fixed;
  z-index: 500;
  left: 50%;
  bottom: clamp(14px, 3vw, 28px);
  transform: translate(-50%, 14px);
  width: min(520px, calc(100vw - 28px));
  max-height: min(70vh, 520px);
  overflow-y: auto;
  background: #fff;
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 18px 50px rgba(0, 0, 0, .18);
  padding: clamp(18px, 3.4vw, 26px);
  opacity: 0;
  transition: opacity .3s ease, transform .3s ease;
}
.consent.is-in { opacity: 1; transform: translate(-50%, 0); }
.consent__title {
  margin: 0 0 .5em;
  font-family: var(--font-display);
  font-size: 1.15rem;
  font-weight: 400;
  color: var(--ink);
}
.consent__title:focus { outline: none; } /* focused programmatically on open, not by the user */
.consent__title:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.consent__body {
  margin: 0 0 1.2em;
  font-size: .86rem;
  line-height: 1.65;
  color: var(--ink-soft);
}
.consent__link { color: var(--accent); border-bottom: 1px solid var(--accent); }
.consent__actions { display: flex; flex-wrap: wrap; gap: 10px; }
/* Both buttons are the same size and weight. The accepted one is filled and the
   declined one outlined, but neither is hidden, shrunk or greyed out — a decline
   that is harder to press is not a real choice. */
.consent__btn {
  flex: 1 1 auto;
  min-width: 140px;
  min-height: 44px;              /* touch target */
  font-family: var(--font-body);
  font-size: .78rem;
  letter-spacing: .14em;
  text-transform: uppercase;
  padding: .85em 1.4em;
  border: 1px solid var(--ink);
  border-radius: var(--radius);
  background: transparent;
  color: var(--ink);
  cursor: pointer;
  transition: background .25s ease, color .25s ease, border-color .25s ease;
}
.consent__btn:hover { background: var(--ink); color: #fff; }
.consent__btn--accept {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.consent__btn--accept:hover { background: var(--accent-dark); border-color: var(--accent-dark); }
@media (max-width: 380px) {
  .consent__btn { flex: 1 1 100%; }
}
@media (prefers-reduced-motion: reduce) {
  .consent { transition: none; transform: translate(-50%, 0); }
}
/* Landscape phones are very short — let the card use the width instead. */
@media (max-height: 460px) and (orientation: landscape) {
  .consent { width: min(680px, calc(100vw - 28px)); max-height: 84vh; padding: 16px; }
  .consent__body { margin-bottom: .8em; }
}

/* ==========================================================================
   LEGAL / PROSE PAGES — privacy.html, accessibility.html, 404.html

   These are the only pages on the site that are pure running text. They reuse
   the existing tokens and the existing .wrap-sm measure, so they read as part of
   the same site rather than as a bolted-on legal template.
   ========================================================================== */
.legal {
  padding: calc(var(--nav-h) + clamp(50px, 8vw, 100px)) 0 clamp(60px, 9vw, 110px);
}
.legal__meta {
  margin: 0 0 2.6em;
  font-size: .74rem;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.legal h2 {
  margin: 2.4em 0 .7em;
  font-size: clamp(1.15rem, 2.2vw, 1.45rem);
  font-weight: 400;
}
.legal h2:first-of-type { margin-top: 1.4em; }
.legal h3 {
  margin: 1.8em 0 .5em;
  font-family: var(--font-body);
  font-size: .96rem;
  font-weight: 600;
  letter-spacing: .01em;
}
.legal p, .legal li { line-height: 1.85; color: var(--ink); }
.legal p { margin: 0 0 1.1em; }
.legal ul { margin: 0 0 1.3em; padding-inline-start: 1.3em; }
.legal li { margin-bottom: .55em; }
.legal a { color: var(--accent); border-bottom: 1px solid var(--accent); }
.legal a:hover { color: var(--accent-dark); border-bottom-color: var(--accent-dark); }
/* A short summary panel at the top of the privacy policy — the plain-language
   version, so the page is usable without reading all of it. */
.legal__summary {
  margin: 0 0 2.4em;
  padding: clamp(18px, 3vw, 26px);
  background: #fff;
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.legal__summary p:last-child, .legal__summary ul:last-child { margin-bottom: 0; }
/* Placeholders Talya still has to fill in. Marked visually AND in the text
   itself, never colour alone, so they are impossible to ship by accident. */
.legal .todo {
  background: #fff4d6;
  border-bottom: 2px solid #b9891f;
  padding: .05em .35em;
  font-weight: 600;
  color: #4a3708;
}
/* A table for the third-party / retention lists. Scrolls on its own on narrow
   screens rather than forcing the page to scroll sideways. */
.legal__tablewrap { overflow-x: auto; margin: 0 0 1.6em; -webkit-overflow-scrolling: touch; }
.legal table { width: 100%; min-width: 460px; border-collapse: collapse; font-size: .88rem; }
.legal th, .legal td { text-align: start; padding: .7em .8em; border-bottom: 1px solid var(--line); vertical-align: top; line-height: 1.6; }
.legal th { font-weight: 600; font-size: .74rem; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-soft); }

/* ---------- 404 ---------- */
.notfound {
  min-height: 76vh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center;
  padding: calc(var(--nav-h) + 40px) clamp(20px, 5vw, 40px) 60px;
}
.notfound__code {
  font-family: var(--font-display);
  font-size: clamp(3.4rem, 12vw, 6rem);
  line-height: 1;
  color: var(--ink);
  margin: 0 0 .2em;
}
.notfound__lead { max-width: 46ch; margin: 0 auto 2em; color: var(--ink-soft); line-height: 1.8; }
.notfound__links { display: flex; flex-wrap: wrap; gap: 12px; justify-content: center; }

/* ==========================================================================
   TOUCH TARGETS — last block in the file, deliberately.

   WCAG 2.2 (2.5.8) asks for at least 24x24 CSS pixels of tappable area. Measured
   on a real 320px viewport, several controls were 17-23px: the language switcher
   (17x19), the footer social icons (20x20), the footer legal links (20px tall),
   the "Menu" button (22px) and the accessibility switches (22px).

   These rules only grow the HIT AREA. Padding expands the box, a matching negative
   margin cancels the effect on surrounding layout, and nothing visible moves — the
   icons, labels and spacing all render exactly as they did before.

   This block sits at the end of the stylesheet because .lang-switch button and
   .a11y-switch are both styled further up AND further down; an equal-specificity
   rule placed next to the other narrow-screen fixes lost the cascade to them and
   silently did nothing (measured: still 19px tall after the rule was added).

   display:inline-block matters too — vertical padding on a plain inline <a> does
   not grow its line box, so the footer links and the wordmark stayed 20-23px tall
   until they were given a block-ish box to pad.
   ========================================================================== */
.nav__logo {
  display: inline-block;
  padding-block: 5px;
  margin-block: -5px;
}
.nav__toggle {
  min-height: 44px;
  padding: 0 6px;
  margin: 0 -6px;
}
.lang-switch button {
  min-height: 24px;
  padding: 4px 7px;
  margin: -4px -7px;
}
.footer__icon {
  width: 24px;
  height: 24px;
  padding: 2px;   /* the glyph itself stays 20px, as drawn */
}
.footer__legal a,
.footer__legal button {
  display: inline-block;
  min-height: 24px;
  padding-block: 4px;
}
.signature__icon { padding: 4px; margin: -4px; }
.a11y-switch { min-height: 24px; }
.a11y-panel__close { min-width: 32px; min-height: 32px; }

/* Form controls were 21px tall — a 1px underline with no vertical padding to grab.
   The extra padding also gives the focus ring somewhere to sit. */
.field input,
.field select,
.field textarea { min-height: 34px; }
/* The consent box used to be sized here (18px) and again below (20px), so this rule
   only ever lost. It is now drawn once, at the end of the file. */

/* ==========================================================================
   HONEYPOT — must not create horizontal scroll on the Hebrew pages.

   The Netlify spam trap was hidden with `position:absolute; left:-9999px`. In a
   left-to-right page that is harmless: overflow to the left of the origin is not
   scrollable. On the RTL Hebrew pages the scroll direction is mirrored, so that
   same -9999px became REAL scrollable width — /he/contact.html measured a
   scrollWidth of 10299px against a 320px viewport, i.e. the entire Hebrew contact
   page could be dragged sideways into ten thousand pixels of empty space.

   Clipping to a 1px box instead hides it identically in both directions while
   keeping the input in the DOM and still submittable, which is what Netlify needs.
   ========================================================================== */
.hp {
  position: absolute !important;
  width: 1px !important;
  height: 1px !important;
  margin: -1px !important;
  padding: 0 !important;
  border: 0 !important;
  overflow: hidden !important;
  clip: rect(0 0 0 0) !important;
  clip-path: inset(50%) !important;
  white-space: nowrap !important;
  inset-inline-start: auto !important;
  left: auto !important;
}

/* ==========================================================================
   LEGAL TABLES ON PHONES

   The cookie and retention tables have 3-4 columns. Below 560px they were kept
   readable by letting the table scroll sideways inside its own box, which works
   but means a visitor has to discover the scroll to read the third column.
   Restacking each row as its own labelled block is easier to read and removes the
   sideways drag entirely. The <th> text is carried in via a data-label attribute
   written by js/main.js, so the markup stays a real table for screen readers.
   ========================================================================== */
@media (max-width: 560px) {
  .legal__tablewrap { overflow-x: visible; }
  .legal table { min-width: 0; }
  .legal thead { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); }
  .legal tbody tr {
    display: block;
    padding: 12px 0;
    border-bottom: 1px solid var(--line);
  }
  .legal tbody td {
    display: block;
    border: 0;
    padding: 2px 0;
  }
  .legal tbody td[data-label]::before {
    content: attr(data-label);
    display: block;
    font-size: .68rem;
    letter-spacing: .1em;
    text-transform: uppercase;
    color: var(--ink-soft);
    margin-bottom: 2px;
  }
}

/* Remaining sub-24px targets, found by measuring at 320px:
     - the WhatsApp / Instagram links in the contact page's social row are
       standalone links, not words inside a sentence, so the WCAG 2.5.8 "inline"
       exception does not cover them;
     - the homepage chapter links landed at 23.5px;
     - the consent checkbox rendered 13px wide.
   The email/phone links inside running paragraphs are deliberately NOT changed —
   those ARE inline text links and are exempt; padding them would break the line
   spacing of the paragraphs they sit in. */
.footer__social .link-underline {
  display: inline-block;
  min-height: 24px;
  padding-block: 3px;
}
.chapter__link { min-height: 24px; }

/* ==========================================================================
   CONSENT TICK-BOX — the one place the box is drawn.

   The native control could not be made dependably visible on a phone. Two
   separate problems:
     - iOS Safari draws its own glyph at roughly 13px and ignores the width and
       height set on it, so every attempt to enlarge it (18px, then 20px, both
       further up this file) changed nothing on the device the complaint came
       from;
     - what it draws is a hairline light-grey outline, which on the cream page
       background is close to invisible — a visitor could not see where to tap
       for a field they are not allowed to skip.

   So the box is drawn here instead of being left to the browser: a real 24px
   square on every browser (WCAG 2.5.8 without relying on the label), a 2px
   border in the body ink so the empty state is unmistakable, and a fill in the
   brand blue once it is ticked. The tick is a background-image, NOT an ::after —
   Firefox does not render generated content inside an <input>.
   ========================================================================== */
.form-consent input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  margin: 1px 0 0;   /* sits the box on the first line of the sentence */
  padding: 0;
  border: 2px solid var(--ink-soft);
  border-radius: var(--radius);
  background-color: #fff;
  background-repeat: no-repeat;
  background-position: center;
  background-size: 15px 15px;
  cursor: pointer;
  transition: background-color .18s ease, border-color .18s ease;
}
.form-consent:hover input[type="checkbox"] { border-color: var(--accent); }
.form-consent input[type="checkbox"]:checked {
  background-color: var(--accent);
  border-color: var(--accent);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23ffffff' stroke-width='2.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M3 8.4 6.4 11.8 13 4.6'/%3E%3C/svg%3E");
}
.form-consent input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}
.form-consent.has-error input[type="checkbox"] { border-color: var(--error); }
/* In Windows High Contrast the drawn version would lose its tick (background
   images are stripped there), so hand the box back to the browser, which knows
   how to render a checkbox in that mode — just at the larger size. */
@media (forced-colors: active) {
  .form-consent input[type="checkbox"] {
    appearance: auto;
    -webkit-appearance: auto;
    background-image: none;
  }
}
@media (prefers-reduced-motion: reduce) {
  .form-consent input[type="checkbox"] { transition: none; }
}

/* ==========================================================================
   FORM AT LARGE TEXT SIZES (WCAG 1.4.4 — resize text to 200%)

   With the accessibility panel's largest text setting the contact form pushed the
   page 6px wider than a 320px viewport, and 31px wider at the 640px equivalent of
   a desktop 200% zoom — enough to produce a horizontal scrollbar on a form.

   Cause: .form and .form__row are CSS grids, and a grid item's default
   `min-width: auto` refuses to shrink below its content's intrinsic minimum. An
   <input> reports an intrinsic width based on its default size of ~20 characters,
   so at 24px text that minimum exceeded the column and forced the whole track wider
   than its container. min-width:0 lets the tracks shrink as intended; max-width
   keeps the controls inside the field once they can.
   ========================================================================== */
.form > *,
.form__row > * { min-width: 0; }
.field { min-width: 0; }
.field input,
.field select,
.field textarea {
  max-width: 100%;
  width: 100%;
}

/* ==========================================================================
   WHATSAPP ICON — official brand green (#25D366).

   One rule covers every page: the mark appears as .footer__icon in the shared
   footer (26 pages) and as .signature__icon in the homepage signature block,
   and both are matched on the href rather than on a per-page class, so no HTML
   needed touching and a new page picks the colour up for free.

   It has to be `fill`, not `color`. The other two icons are stroked outlines
   drawn with stroke="currentColor", so they follow the anchor's `color`. The
   WhatsApp glyph is a solid filled path carrying no fill attribute at all,
   which means it was falling through to the SVG initial value — black — and
   ignoring `color` entirely. Setting `color` here would change nothing visible.
   Setting `fill` on the <svg> covers the path by inheritance.

   Deliberately no :hover pair. Because the glyph never read `color`, the
   existing `.footer__icon:hover { color: var(--accent) }` never recoloured this
   icon — only the translateY lift ever applied to it. Leaving fill untouched on
   hover therefore keeps the current behaviour exactly, and keeps the mark on
   brand instead of flipping green to blue under the cursor.

   Survives the accessibility modes without !important: a11y-dark and
   a11y-contrast both override `color`, not `fill`. Invert and grayscale are
   whole-page filters and are meant to affect it.
   ========================================================================== */
.footer__icon[href*="wa.me"] svg,
.signature__icon[href*="wa.me"] svg { fill: #25D366; }
