/* =============================================================================
   mobile/css/lounge.css — the REAL 3D rotating ring on phones (v4)
   ---------------------------------------------------------------------------
   2026-06-11 mobile-fidelity pass (replaces the v3 "coverflow carousel").

   Mandate: mobile must run the SAME 3D ring as desktop — 8 photos on a Y-axis
   perspective ring, rotated by touch-drag + fling + idle drift (js/lounge-
   selector.js, which already binds unified PointerEvents + setPointerCapture +
   IO-gated RAF, and whose .lounge__stage already has touch-action:pan-y). The
   v3 coverflow hard-killed the ring (transform:none!important + flex scroll-
   snap); that is GONE. The core grid fallback that used to also fire at
   ≤767.98px was removed from css/sections/lounge.css (its prefers-reduced-
   motion arm stays — the one allowed static fallback).

   The only mobile problem the ring has is FIT: js computes radius
   clamp(280, 38vw, 480) → floors at 280px on a 360px screen, so the ring's
   projected extent (~±326px at the near point with perspective:2000) overflows
   a 180px half-viewport. We cannot change the JS radius (core; no hook). So we
   scale the PERSPECTIVE PARENT (.lounge__stage) — which carries NO JS-written
   transform (js writes rotateY() on the .lounge__ring CHILD and
   rotateY()/translateZ() on each item). Scaling the stage shrinks the whole
   projected ring to fit while preserving rotation / drag / depth. Drag math
   (px → deg) is scale-independent, so the gesture still feels 1:1.

   Overflow math (read from css/sections/lounge.css + lounge-selector.js):
     item width clamp(220, 22vw, 320) → 220px at 360vw (half 110px)
     radius floor 280px ; perspective 2000 → nearScale ≈ 2000/1720 ≈ 1.16
     worst half-extent ≈ 280·sin45° + 110·1.16 ≈ 198 + 128 ≈ 326px
     × 0.6 → ≈196px vs 180px half-viewport ; the ~16px sliver at the worst
     rotation angle is harmlessly clipped by .lounge{overflow:hidden} (a CSS
     transform does NOT grow an ancestor's scrollWidth). Common front angles
     fit with margin, so it still reads as a RING (varied depths), not a row.

   §4 logical props (the unavoidable transform aside). §5 no new colors.
   §13 self-contained. RM users get the core grid (guarded below).
   ========================================================================== */

@media (max-width: 768px) and (prefers-reduced-motion: no-preference) {

  /* Scale the perspective parent so the genuine 3D ring fits a phone. This is
     the ONLY transform we touch — never .lounge__ring / .lounge__item (those
     are the JS rotation/placement channels; overriding them would re-kill the
     ring, which is the v3 bug we are removing). */
  .lounge__stage {
    transform: scale(0.6);
    transform-origin: center center;
    /* Compress the reserved height so the 0.6-scaled ring sits snug instead of
       leaving a tall empty gap (desktop reserves clamp(420px,64vh,720px)).
       2026-09-17 client pass: 44vh still left ~80px of air above and below the
       ~205px-tall scaled ring (measured at 390×844); 30vh with a 240px floor
       keeps the front item clear of both edges. */
    block-size: clamp(240px, 30vh, 320px);
  }

  /* Captions (2026-06-15 — typographic hierarchy fix).
     The 0.6 stage scale shrinks the figcaption too, so an earlier pass bumped
     both lines way up (h3 2xl/39px, p lg/25px). After the ×0.6 those render at
     ~23px / ~15px — close enough in size that title and body read as ONE block
     (the user's complaint: "שניהם אותו גודל"). We now (a) pull both DOWN so the
     ring's captions don't shout over the photo, and (b) open a clear gap between
     them — title stays a display weight, body drops to a lighter, smaller line.
     Sizes are pre-downscale; ÷ the 0.6 stage scale gives the on-screen px. */
  .lounge__item figcaption h3 {
    font-size: var(--text-xl);          /* 31px → ~19px on-screen (title) */
    line-height: var(--lh-tight);
  }
  .lounge__item figcaption p {
    /* 2026-09-17 client pass: 16px rendered at ~11px on-screen (×0.6 stage,
       ×1.16 front perspective) — under the 12px floor. 20px → ~14px. */
    font-size: var(--text-md);
    font-weight: var(--fw-regular);
    line-height: var(--lh-snug);
    /* Soften the body so the eye lands on the title first. */
    color: color-mix(in srgb, var(--ivory) 80%, transparent);
  }

  /* Ring prev/next (client pass 2026-09-17). Injected by mobile/loader.js
     setupLoungeArrows() as a sibling AFTER the stage (outside the pointer-
     captured stage). DOM order prev → next, so the flex row mirrors with dir:
     RTL prev on the right / next on the left, LTR the reverse. */
  .lounge__arrows {
    position: relative;
    z-index: 2;
    display: flex;
    justify-content: center;
    gap: var(--space-8);
    margin-block-start: var(--space-4);
  }
  .lounge__arrow {
    display: grid;
    place-items: center;
    inline-size: 48px;
    block-size: 48px;
    padding: 0;
    border-radius: var(--radius-circle);
    /* 2026-10-11: was ink-deep @45% — on the ivory section that read as a muddy
       grey disc (client report). Same ivory/brass language as the testimonials
       arrows now. */
    border: 1px solid color-mix(in srgb, var(--brass) 70%, transparent);
    background: var(--ivory);
    color: var(--brass-deep);
    cursor: pointer;
    transition: background-color var(--dur-fast) var(--ease-out-cinema),
                transform var(--dur-fast) var(--ease-out-cinema);
  }
  .lounge__arrow::before {
    content: "";
    inline-size: 10px;
    block-size: 10px;
    border-top: 2px solid currentColor;
    border-right: 2px solid currentColor;
  }
  /* Chevrons point outward, toward their own side. */
  .lounge__arrow--prev::before { transform: rotate(45deg); margin-inline-start: 3px; }
  .lounge__arrow--next::before { transform: rotate(-135deg); margin-inline-end: 3px; }
  html[dir="ltr"] .lounge__arrow--prev::before { transform: rotate(-135deg); }
  html[dir="ltr"] .lounge__arrow--next::before { transform: rotate(45deg); }
  .lounge__arrow:active {
    background: color-mix(in srgb, var(--brass) 30%, transparent);
    transform: scale(0.94);
  }
  .lounge__arrow:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
  }

  /* 2026-10-11: the absolute "גררו לסיבוב הגלריה" drag cue sat under the arrow
     pair (they overlapped). The arrows are the phone's affordance now; the cue
     stays on desktop. */
  #lounge > .ix-hint { display: none; }
}

/* Ring arrows exist only while the ring engine runs on a phone. */
@media (min-width: 769px), (prefers-reduced-motion: reduce) {
  .lounge__arrows { display: none; }
}

/* ---------------------------------------------------------------------------
   Lightbox prev/next on phones (client pass 2026-09-17 — replaces the
   2026-06-22 "hide the chevrons, swipe only" rule; the client could not find a
   way to move between photos). Swipe still works (js/lightbox.js); the arrows
   complement it.
   - 48×48 dark translucent discs with an ivory chevron → legible on bright AND
     dark photos.
   - Sides follow the reading direction: next at inline-end (Hebrew: left,
     EN/FR: right), prev at inline-start. The desktop file anchors them the
     other way round; that desktop placement is intentionally left as is.
   - touch-action pan-y pinch-zoom: horizontal drags reach the swipe handler,
     pinch-zoom stays with the browser.
   Outside the ring's RM guard on purpose — the lightbox opens under RM too.
   ------------------------------------------------------------------------- */
@media (max-width: 768px) {
  .lounge-lightbox {
    touch-action: pan-y pinch-zoom;
  }
  .lounge-lightbox__nav,
  .lounge-lightbox__close {
    inline-size: 48px;
    block-size: 48px;
    border-radius: var(--radius-circle);
    background: color-mix(in srgb, var(--ink-deep) 58%, transparent);
    border: 1px solid color-mix(in srgb, var(--ivory) 30%, transparent);
    color: var(--ivory);
    -webkit-backdrop-filter: blur(6px);
    backdrop-filter: blur(6px);
  }
  .lounge-lightbox__nav {
    display: flex;
    inset-block-end: calc(var(--space-6) + env(safe-area-inset-bottom));
  }
  .lounge-lightbox__nav--next {
    inset-inline-start: auto;
    inset-inline-end: var(--space-4);
  }
  .lounge-lightbox__nav--prev {
    inset-inline-end: auto;
    inset-inline-start: var(--space-4);
  }
  .lounge-lightbox__nav::before {
    inline-size: 11px;
    block-size: 11px;
  }
  /* RTL: the base glyphs already point outward for these sides (prev right →
     45°, next left → −135°). LTR swaps them. */
  html[dir="ltr"] .lounge-lightbox__nav--prev::before {
    transform: rotate(-135deg);
    margin-inline-start: 0;
    margin-inline-end: 3px;
  }
  html[dir="ltr"] .lounge-lightbox__nav--next::before {
    transform: rotate(45deg);
    margin-inline-end: 0;
    margin-inline-start: 3px;
  }
  .lounge-lightbox__nav:hover,
  .lounge-lightbox__close:hover,
  .lounge-lightbox__close:focus-visible {
    transform: none;
  }
  .lounge-lightbox__nav:active,
  .lounge-lightbox__close:active {
    background: color-mix(in srgb, var(--ink-deep) 80%, transparent);
    transform: scale(0.94);
  }
}

/* =============================================================================
   mobile/css/culinary.css — dish gallery density on phones
   ---------------------------------------------------------------------------
   v3 (mobile pass 2026-06-08, after Playwright section sweep).

   Discovered in QA:
   - The .culinary__post grid stays 1-col below 560px. With 9 dishes at
     aspect-ratio 4/5, that produces a ~3950px-tall column on a 390-wide
     phone — the section becomes a tedious endless scroll.
   - The actual brand intent for culinary post-gallery is "menu-card
     density", not "single-photo per screen." A 2-col grid lands ~1900px
     tall — half the height, twice the visual rhythm, and both adjacent
     dishes live in peripheral vision while the user reads either one.

   Fix: bump grid to 2-col starting at 360px (the smallest target). Keeps
   1-col only on the very smallest viewports (≤359px, basically nothing
   sold in the last 5 years).

   The canvas-scrub above the gallery is unaffected; it's a separate
   .scroll-scene__sticky element. The mobile manifest swap
   (data-manifest-url-mobile + canvas-frames-mobile.js) handles that.
   ========================================================================== */

/* ---------------------------------------------------------------------------
   Culinary scrub OVERLAY text on phones (2026-06-16). The shared
   .scroll-scene__overlay (css/sections/scroll-scene.css) is desktop-tuned:
   flex-end + --gutter (16px) padding + section-header clamp() minimums that
   bottom out small on a 390px portrait. Over the dark conveyor canvas the
   eyebrow/title/lede then read cramped against the right edge and a touch too
   small. Give the text real breathing room from the edges, lift it clear of
   the bottom, and bump the type one notch. (§13 mobile override; tokens only.)
   --------------------------------------------------------------------------- */
/* ---------------------------------------------------------------------------
   Stabilize the pinned scrub stage on phones (2026-06-18). The core
   .scroll-scene__sticky (css/sections/scroll-scene.css) is `block-size: 100vh`.
   On mobile, `100vh` is the LARGE viewport (toolbar hidden); the dynamic
   browser toolbar then shrinks the actual visible area mid-scroll, so the
   sticky stage is taller than what's on screen and the canvas — which the
   renderer centers via dx/dy — appears pushed toward the BOTTOM / off-screen.
   Pin to `100svh` (small viewport height = toolbar-visible, the stable floor)
   so the stage matches the always-visible area and the frame re-centers.
   Mirrors the hero pin fix. Desktop is untouched (gated ≤768px). The canvas
   inset:0 fill (block-size:100%) follows the stage automatically. (§13 mobile
   override; tokens/logical only.)
   --------------------------------------------------------------------------- */
@media (max-width: 768px) {
  .culinary .scroll-scene__sticky {
    block-size: 100svh;
  }
  .culinary .scroll-scene__overlay {
    /* Move the text to the TOP so the center/bottom of each dish frame — where
       the plated food sits — stays visible while scrubbing (user 2026-07-01).
       Core is flex-end (bottom); flip to flex-start and swap the padding. */
    align-items: flex-start;
    padding-inline: clamp(20px, 6vw, 28px);
    padding-block-start: clamp(var(--space-12), 10vh, var(--space-16));
    padding-block-end: var(--space-8);
    /* Flip the scrim to darken the TOP band (behind the text) instead of the
       bottom — mirror of --scrim-overlay (tokens.css) with direction reversed —
       so the effect below reads clear. */
    background: linear-gradient(to bottom,
      color-mix(in srgb, var(--ink-deep) 72%, transparent) 0%,
      color-mix(in srgb, var(--ink-deep) 45%, transparent) 30%,
      color-mix(in srgb, var(--ink-deep) 10%, transparent) 60%,
      transparent 78%);
    /* keep the column from stretching full-bleed so lines wrap pleasantly */
  }
  .culinary .scroll-scene__overlay > * {
    max-inline-size: 30rem;
  }
  .culinary .scroll-scene__overlay .section-header__eyebrow {
    font-size: var(--text-sm);   /* 14px (was 12px) */
  }
  .culinary .scroll-scene__overlay .section-header__title {
    font-size: clamp(2.4rem, 9vw, 3rem);   /* larger, fills the wide portrait */
  }
  .culinary .scroll-scene__overlay .section-header__lede {
    font-size: var(--text-md);   /* 20px (was ~18px), more legible over media */
    line-height: var(--lh-loose);
  }
}

@media (min-width: 360px) and (max-width: 559.98px) {
  .culinary__grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-4);
  }

  /* Captions inside each tile are a bit dense in 2-col @ 390 viewport.
     Drop the description size one notch and let the tag / name carry. */
  .culinary__item-desc {
    font-size: var(--text-xs);
  }
  .culinary__item-name {
    font-size: var(--text-sm);
  }
}

/* On very narrow viewports the 2-col cards pinch the dish photos —
   keep 1-col there. */
@media (max-width: 359.98px) {
  .culinary__grid {
    grid-template-columns: 1fr;
  }
}

/* ---------------------------------------------------------------------------
   Run the culinary scrub under reduced-motion ON MOBILE (2026-06-11, §8
   mobile exception — user-requested). Phones force prefers-reduced-motion via
   battery-saver / iOS Low-Power Mode, which the core css/sections/scroll-scene.css
   RM block treats as "kill the scrub": it sets .scroll-scene__canvas{display:none}
   and collapses .scroll-scene__spacer to ~100vh (no scroll distance). The
   companion js/scrollytelling.js change registers the scene on mobile-RM; these
   rules undo the two CSS kills so the canvas actually paints + has the full
   320vh to scrub over. Desktop RM is untouched (this is gated ≤768px).
   --------------------------------------------------------------------------- */
@media (max-width: 768px) and (prefers-reduced-motion: reduce) {
  .culinary .scroll-scene__canvas {
    display: block;
  }
  /* Restore the scrub distance (core RM collapsed it to auto/100vh) — at the
     phone length set below (client pass 2026-09-17). */
  .culinary .scroll-scene__spacer {
    block-size: 220vh;
    min-block-size: 0;
  }
}

/* ---------------------------------------------------------------------------
   Shorter scrub track on phones (client pass 2026-09-17, Moshe's decision:
   "shorten only the culinary"). The canvas carries data-scrollytelling, so
   js/scroll-scene.js skips this scene and the inline
   `--scene-spacer: 320vh` (index.html) is what sizes the spacer via
   `block-size: var(--scene-spacer)`. We override block-size on the element
   itself (a custom-property override would lose to the inline style). The
   renderer derives progress from the section rect
   (js/canvas-frame-renderer.js computeProgress), so every frame still plays —
   just over ~2.2 screens instead of ~3.2. Desktop keeps 320vh.
   --------------------------------------------------------------------------- */
@media (max-width: 768px) {
  .culinary .scroll-scene__spacer {
    block-size: 220vh;
  }
  .culinary .scroll-scene__overlay {
    padding-block-start: 88px;
    background: linear-gradient(to bottom, var(--ink-deep), color-mix(in srgb, var(--ink-deep) 90%, transparent) 55%, transparent);
  }
  .culinary .scroll-scene__overlay .section-header__title { font-size: clamp(var(--text-xl), 7vw, var(--text-2xl)); }
  .culinary .scroll-scene__overlay .section-header__lede { font-size: var(--text-base); line-height: 1.5; }
  /* Captions take their own flow space, so longer EN/FR text never covers
     dishes or gets clipped by the image's fixed aspect ratio. */
  .culinary__item { display: flex; flex-direction: column; aspect-ratio: auto; }
  .culinary__item picture { block-size: auto; aspect-ratio: 4 / 3; }
  .culinary__item .media-caption { position: static; flex: 1; padding: var(--space-3); background: var(--ink-deep); }
  .culinary__item-name { font-size: var(--text-base); }
  .culinary__item-desc { font-size: var(--text-sm); line-height: 1.5; }
}

/* =============================================================================
   mobile/css/hero-scene.css — scroll hero + composer, PORTRAIT pass (2026-06-15)
   ---------------------------------------------------------------------------
   The v10 hero (commit 51ce64a, "verbatim 1:1 sandbox port") shipped WITHOUT a
   mobile re-layout. The scroll-pinned INTRO (sky/desert/clouds/logo/smoke +
   title) does scale correctly on phones — every dimension is driven by the
   viewport-responsive `--rem` base scoped on #hero.hero_root (2.6667vw < 768px)
   in css/sections/hero-scene.css, exactly as the sandbox shipped it — so it
   needs no override here.

   But the INTERACTIVE COMPOSER (#hall-portal > .gamos-hero) was authored for a
   LANDSCAPE box (`aspect-ratio:2048/1360`) with its two wordmark CTAs hard-pinned
   at top:42% / top:51% and a `white-space:nowrap` prompt. In portrait that box
   collapses to ~259px tall at 390px wide, so EVENTS and RESORT overlap into an
   illegible garble and the prompt bleeds off the edge. This file gives the
   composer a portrait-friendly layout: real vertical room + cleanly stacked,
   finger-sized CTAs + a prompt that fits.

   §13: @media (max-width:768px) overrides ONLY — no general rules. The desktop
   composition (≥769px) is untouched. §1/§13-rule-8: this is an EQUIVALENT of the
   desktop experience (same backdrop + logo + prompt + EVENTS/RESORT choice +
   cue), re-flowed for the phone — not a flat fallback.
   ========================================================================= */

@media (max-width: 768px) {

  /* ----- PIN STABILITY: small-viewport height so the dynamic browser toolbar
     doesn't resize the pinned scene mid-scroll ------------------------------
     `100vh` on a phone is the LARGE viewport (toolbar collapsed); as the URL bar
     shows/hides during scroll the real height changes under the sticky pin, so
     the scene jumps and the GSAP scrub desyncs. `100svh` (small viewport) is the
     stable minimum height → the pin box stays constant regardless of toolbar
     state. Pairs with ScrollTrigger.config({ignoreMobileResize:true}) in
     js/hero-scene.js. (§13 mobile-only override; desktop ≥769px keeps 100vh.) */
  #hero.hero_root .hero_top { block-size: 100svh; }

  /* ----- SCRUB PACING: hero scroll track on mobile -------------------------
     Mobile track = 350vh — the reference sandbox's own <768px value
     (2026-07-13, user: 400vh scrolled too long vs the source site; history:
     300vh felt too rushed 2026-06-22, 400vh overshot). The reduced desert
     amplitude in js/hero-scene.js keeps the desert in frame across the track.
     ScrollTrigger end:"bottom top" + the gamosHero progress stub both derive
     from this height, so no JS change is needed. */
  #hero.hero_root { block-size: 350vh; }

  /* ----- DESERT FILL FIX (2026-06-22) --------------------------------------
     subject.png is a very wide panorama (6240×1599, AR 3.9). With the base
     object-fit:contain in a narrow portrait box it fits-to-WIDTH → a ~100px
     (~12vh) strip stuck low in the frame, while desktop's wide box renders it
     ~41% of the frame. Result on mobile: the logo letters "fill" against empty
     pale sky (the desert never reaches the centred logo) — the #1 reported
     defect. Fix: object-fit:cover gives a tall desert band (centre-crop of the
     panorama), sat lower-centre so the rising ridge sweeps UP behind the centred
     logo at the fill beat — the same choreography the desktop box produces.
     Paired with a larger desert rise in js/hero-scene.js (isNarrow branch). */
  #hero.hero_root .hero_bg > .hero_house,
  #hero.hero_root .hero_composite .hero_house {
    /* 2026-10-11: never above the text block. The block's height scales with
       --rem (width) while 56vh scales with height, so on short phones the ridge
       covered the subtitle + trust line (measured overlap 20–92px at 412×915 …
       375×667). 42·rem ≈ the block's height (eyebrow → trust line) + a margin. */
    /* + --hero-text-end: the measured end of the text block (mobile/js/hero-fit.js)
       so longer EN/FR copy also clears the ridge; the rem estimate is the
       no-JS / first-paint fallback. */
    top: max(56vh, calc(9svh + 42 * var(--rem)), calc(var(--hero-text-end, 0px) + 24px));
    height: 58vh;
  }
  #hero.hero_root .hero_house img {
    object-fit: cover;
    object-position: center 30%;
  }

  /* ----- COMPOSER: give the landscape box real portrait height -------------
     aspect-ratio:auto cancels the 2048/1360 landscape ratio; min-block-size
     gives the absolutely-positioned children room to spread without overlap.
     The base image stays object-fit:cover (a soft centre crop of the GAMOS
     scene reads fine); the ivory background fills any remainder. */
  #hall-portal .gamos-hero {
    aspect-ratio: auto;
    min-block-size: 92vh;
  }

  /* Brand logo (top-inline-start). Slightly smaller so it doesn't crowd the
     narrow column; position unchanged (still the top corner mark). */
  #hall-portal .gamos-hero__logo {
    inline-size: clamp(116px, 34vw, 150px);
  }

  /* Prompt: the desktop nowrap + .32em tracking overflows 390px. Tighten the
     type + tracking so the single line fits the phone width, and lift it above
     the (now lower) EVENTS wordmark. */
  #hall-portal .gamos-hero__prompt {
    top: 28%;
    gap: .7rem;
  }
  #hall-portal .gamos-hero__prompt-text {
    font-size: clamp(1rem, 3.6vw, 1.3rem);
    letter-spacing: .14em;
  }

  /* CTAs: re-stack with generous vertical spacing so the two wordmarks never
     touch. Each image is ~0.4× its width tall → at this width ≈85px, and the
     ~17% gap (≈130px on a 92vh box) keeps them clearly separate + ≥44px tap. */
  #hall-portal .gamos-hero__cta {
    inline-size: clamp(196px, 56vw, 260px);
  }
  #hall-portal .gamos-hero__cta--events { top: 44%; }
  #hall-portal .gamos-hero__cta--resort { top: 64%; }

  /* The dim-siblings :has() hover is desktop-only by nature; on touch the CTAs
     stay at full opacity. Nothing to override — left here as a note. */

  /* ----- HERO TEXT BLOCK: tight centered column (2026-06-30) ---------------
     The v10 title was a SHORT 2-line phrase; the copy doc (2026-06-30) swapped
     in a longer sentence ("המקום שבו האירועים מקבלים / תפאורה יחודית.") + a longer
     subtitle ("…ועוצר נשימה של מדבר יהודה"). `.hero_content` is a grid whose rows
     STRETCH across the full 100svh, so the three lines spread far apart ("the
     order doesn't sit right") and the now-taller wrapped title shoves the
     subtitle's track to the bottom edge — where the scroll-fade (js/hero-scene.js
     `content → opacity:0`) swallows it before it's read ("can't see it"). Fix:
     re-flow the block as a tight centered flex column with even gaps so all three
     lines read together and stay in the first screen; shrink the title a touch so
     line 1 wraps to a clean 2 lines instead of 3. Desktop (≥769px) untouched. */
  #hero.hero_root .hero_content {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;            /* anchor from the top (2026-06-30,
                                               user: "lift the text block up") so
                                               the whole block clears the desert +
                                               smoke at the horizon, instead of
                                               sitting centred on the ridge line. */
    align-items: center;
    gap: calc(2 * var(--rem));
    padding-block-start: max(9svh, 72px);   /* ≥72px: clears the 44px language
                                               globe at top:1rem — the longer EN/FR
                                               eyebrow ran under it on small phones.
                                               top offset — block lands in the
                                               upper sky zone, above the ridge
                                               (11→9svh 2026-10-11, pairs with the
                                               desert's max() top above). */
    padding-bottom: 0;
  }
  #hero.hero_root .hero_eyebrow { margin: 0; }
  /* 12px floor (client pass 2026-09-17): at exactly 768px the desktop --rem
     (0.5208vw ≈ 4px) applies, so the eyebrow rendered at ~10px and the trust
     line at ~9px; at 320px the trust line was 11.9px. max() keeps the larger
     scene-scaled size everywhere else. */
  #hero.hero_root .hero_eyebrow { font-size: max(var(--text-xs), calc(2.6 * var(--rem))); }
  #hero.hero_root .hero_stats { font-size: max(var(--text-xs), calc(1.4 * var(--rem))); }
  #hero.hero_root .hero_title h1 {
    font-size: calc(4.4 * var(--rem));      /* was 5.4·rem — fits the longer title */
    line-height: 112%;
    text-wrap: balance;
  }
  #hero.hero_root .hero_text {
    margin: 0;
    max-inline-size: 40ch;                  /* keep the long subtitle to ~2 lines */
  }

  /* SINGLE CUE on mobile. The shared `:not([data-hero-mode]) .hero_top .gamos-cue`
     rule (css/sections/hero-scene.css) hides the hero cue once the hero releases
     — but on a phone the 460vh hero stays "hero-mode true" until ~99% scrolled,
     and the composer (margin-bottom:-100vh) overlaps into the last frame BEFORE
     that flips, so both cues briefly show together (measured pf 0.95-1.0). The
     composer cue sits right below and serves the same "scroll on" purpose, so on
     mobile we simply retire the hero cue — one clean cue throughout. */
  #hero .hero_top .gamos-cue { display: none; }

  /* NOTE (2026-07-02): the former `html[lang="en"] … mask-size` override here is
     gone — js/hero-scene.js now fits the composite mask to each logo's viewBox
     ratio inside the measured .hero_logo box at every breakpoint (EN/FR/HE), so
     no per-language, per-breakpoint mask-size is needed. */

  /* ----- CONVERSION PASS (2026-07-13): no CTA pair inside the mobile hero ---
     User feedback: on phones the hero CTA pair lands on the dark desert layer
     (unreadable) AND duplicates the fixed bottom CTA bar (tour + WhatsApp are
     always one tap away). The trust line (.hero_stats) stays; the buttons are
     desktop-only. */
  #hero .hero_ctas { display: none; }

  /* D06: keep readable copy inside the sky zone, above animated scenery.
     The GSAP entrance/scrub and mask choreography remain unchanged. */
  #hero.hero_root .hero_content {
    padding-block-start: 80px;
    padding-inline: var(--space-4);
    gap: var(--space-3);
  }
  #hero.hero_root .hero_eyebrow { font-size: var(--text-xs); letter-spacing: .06em; }
  #hero.hero_root .hero_title { inline-size: 100%; }
  #hero.hero_root .hero_title h1 { font-size: clamp(var(--text-xl), 7vw, var(--text-2xl)); line-height: 1.12; }
  #hero.hero_root .hero_text { font-size: var(--text-base); line-height: 1.45; color: var(--ink-deep); }
  #hero.hero_root .hero_stats { font-size: var(--text-xs); line-height: 1.5; margin-block-start: 0; color: var(--ink-deep); }
  #hero.hero_root .hero_bg > .hero_house,
  #hero.hero_root .hero_composite .hero_house { top: max(68svh, 390px); }
}

/* =============================================================================
   mobile/css/responsive-images.css — placeholder
   ---------------------------------------------------------------------------
   Phase 1A (mobile pass 2026-06-08).

   The responsive image strategy is implemented at the markup layer in
   /index.html via <picture> with a `<source media="(max-width: 768px)"
   srcset="…half.webp">` row inserted before each desktop source. No CSS
   is required for the swap itself.

   This file ships so the <link rel="stylesheet"> in index.html doesn't
   404, and is reserved for any future image-related mobile-only rules
   (e.g. object-fit tweaks, art-direction crops via clip-path).
   ========================================================================== */

/* (no rules at present) */

/* =============================================================================
   mobile/css/touch-targets.css — WCAG 2.5.5 minimum tap-area enforcement v2
   ---------------------------------------------------------------------------
   Phase 3B (mobile pass 2026-06-08, rewritten v2 after design-taste audit).

   v1 mistake: I used `background-clip: content-box` + `::before` proxy to
   keep the visible dot at 10×10 while expanding hit area to 44×44. That
   was clever but fragile — slider.js applies `.is-active` to the host
   element, and the ::before swap meant any state class would need its
   own ::before override. Brittle.

   v2 approach: keep the visible dot exactly as the desktop CSS draws it,
   just extend the hit area via `padding` outside the dot. The
   `background-clip: content-box` trick keeps the visible fill INSIDE the
   padded box — but we don't need that here because the dot is already
   1px-bordered and only 10×10. We simply give it 17px padding all around
   so the total tappable element is 44×44, and we don't touch the
   visible dot's styling. State classes from slider.js (.is-active,
   :hover) keep working because we never removed the original styling.
   ========================================================================== */

@media (max-width: 768px) {

  /* ROOT-LEVEL FIX (mobile pass v3 — discovered via Playwright DOM probe
     2026-06-08): on RTL pages, when ANY descendant exceeds the viewport
     width even momentarily (testimonials track, lounge ring at certain
     rotation angles, marquee mid-animation), the <html> element grows
     `scrollWidth` past its `clientWidth`. Because base.css only sets
     `overflow-x: hidden` on <body> (not <html>), the <html> still
     develops horizontal overflow, and Chrome's RTL scroll model snaps
     to the right edge — pushing the visible viewport ~21px to the left
     of the document origin. The user sees the whole page shifted left,
     hero CTAs appear in the "left half" of the screen, etc.

     Fix: clip <html> horizontal overflow on phones too. Pinning this only
     at ≤768px keeps desktop scroll behaviour pristine.

     ⚠️ overflow-x:CLIP, not HIDDEN (fix 2026-06-15, proven via Playwright):
     `overflow:hidden` makes <html>/<body> a SCROLL CONTAINER, which by the
     CSS spec becomes the containing block for `position:sticky` descendants
     — that BROKE the culinary canvas pin (`.scroll-scene__sticky`): on mobile
     the canvas scrolled away (canvasTop −1720 instead of 0) so the scroll-
     scrub never appeared. `overflow-x:clip` clips the same overflow WITHOUT
     establishing a scroll container, so sticky survives. Live toggle proved
     clip restores canvasTop=0 AND still suppresses the RTL marquee overflow
     (scrollWidth==viewport). Do NOT revert to `hidden`. */
  html {
    overflow-x: clip;
    /* Anchor links (#culinary, #shabbat-chatan, etc.) on mobile would
       otherwise land sections directly under the sticky 64px-tall nav,
       hiding the section heading. scroll-padding-top reserves a buffer
       so the section heading is visible the moment the user lands. */
    scroll-padding-block-start: 80px;
  }

  /* base.css sets `body{overflow-x:hidden}` (core, all viewports). On phones
     that `hidden` computes overflow-y → `auto`, making <body> a SECOND scroll
     container nested under <html> — the nested-scroll-container mismatch is
     what actually breaks the culinary `position:sticky` pin on mobile (html
     alone wasn't enough). Override body to `clip` too at ≤768px so neither
     <html> nor <body> is a scroll container, restoring the pin while still
     clipping the RTL marquee overflow. Desktop body stays `hidden` (base.css)
     where the pin already works. Proven: html+body clip → canvasTop 0. */
  body {
    overflow-x: clip;
  }

  /* Submit + secondary CTAs in the contact form. The submit pill is
     comfortably tall (~52px), but the transparent-outline waze/maps CTAs
     can drop below 44 when copy is short. Min-size guarantees the floor. */
  .contact__submit,
  .contact__cta {
    min-block-size: 44px;
    min-inline-size: 44px;
  }

  /* Footer social links — a 44px floor. (2026-09-17: was a FIXED 44×44 box,
     written for icon-only pills; the links now carry an icon + text label, so
     the fixed width squeezed the label out of the box. Floor, not fixed.) */
  .site-footer__social-link {
    min-inline-size: 44px;
    min-block-size: 44px;
  }

  /* Slider dots — visible dot stays 10×10 with its border + active fill
     intact. We expand the tappable bounding box via padding. The dots
     are arranged in a flex row with `gap`, so adding padding shifts the
     visible dots apart slightly — we compensate by shrinking the gap
     to zero. The original gap was --space-2 (8px); after padding the
     hit boxes are 44px wide each with the visible dots centered, so
     gap:0 gives us 44px-wide hit zones with the visible dots ~17px
     apart center-to-center (which still reads as a clean dot row). */
  .testimonials__dots {
    gap: 0;
  }
  .testimonials__dots [data-slider-dot] {
    /* Visible dot stays 10×10; padding makes the box ≥44×44. 18px (was 17px)
       gives 10+36=46px → clears the floor with a 1px buffer against sub-pixel
       rounding that left the box at 43px on a 360px viewport. */
    inline-size: 10px;
    block-size: 10px;
    padding: 18px;
    /* The padded area is transparent and outside the visible dot —
       background-clip:content-box ensures the dot's background only
       paints inside the 10px content box, not in the padded area.
       This is the canonical pattern for "expanded hit area without
       visible bloat." */
    background-clip: content-box;
    -webkit-background-clip: content-box;
    box-sizing: content-box;
  }

  /* Mobile-overlay nav links — already padded but reaffirm a 44px floor
     so a renamed font-size token can't regress this. */
  .site-nav__mobile__links a {
    min-block-size: 44px;
    display: flex;
    align-items: center;
    justify-content: center;
  }

  /* NAV reveal-grow cap (touch) — on touch, site-nav-hover-reveal.js keeps the
     bar revealed (data-hero-mode + data-nav-revealed both true) so the hamburger
     stays tappable in the hero. But that fires the DESKTOP reveal-grow rule
     (css/sections/site-nav.css: 160px tall, to cover the brand on cursor reveal),
     which has no meaning on touch and made the phone bar ~160px. Cap it to a
     normal compact mobile bar: 64px (same padding token as .is-scrolled) with a
     48px logo. The 44px hamburger stays fully inside. (§13 mobile override.) */
  html[data-hero-mode="true"][data-nav-revealed="true"] .site-nav__inner {
    padding-block: var(--space-3);
    min-block-size: 64px;
  }
  html[data-hero-mode="true"][data-nav-revealed="true"] .site-nav__brand-logo {
    height: 48px;
  }

  /* Contact channel values (tel: / mailto: / WhatsApp links). Desktop draws
     them ~30px tall in a label-over-value stack; on touch that's under the
     44px floor. Grow the LINK box to 44px (icon stays centered against the
     row via .contact__channel's align-items:center). The visible text keeps
     its size/position — only the hit box grows. (fidelity pass 2026-06-15) */
  .contact__channel-value {
    display: inline-flex;
    align-items: center;
    min-block-size: 44px;
  }

  /* Skip-link — first focusable (§9). Visible only on focus; desktop renders
     it ~40px tall. Reaffirm the 44px floor + center the label. */
  .skip-link {
    min-block-size: 44px;
    display: inline-flex;
    align-items: center;
  }

  /* Hero scroll cue — a deliberately SLIM 24px-wide pill (its border IS the
     visual). We must not widen the element itself (that would fatten the
     visible pill), so expand the tappable area with a transparent ::before
     overlay inset by -10px → ~44×76 hit box around the 24px pill. Canonical
     "expand hit area without visible bloat" pattern. */
  .hero-static__cue { position: absolute; }   /* reaffirm (base already sets it) */
  .hero-static__cue::before {
    content: "";
    position: absolute;
    /* 24px pill + (12px × 2) = 48px → comfortably clears the 44px floor on
       the slim axis. Transparent — purely a larger pointer target. */
    inset: -12px;
  }
}

/* =============================================================================
   mobile/css/headings.css — Hebrew descender clearance on small viewports
   ---------------------------------------------------------------------------
   v3 (mobile pass 2026-06-08, after Playwright section sweep).

   Root cause discovered during visual QA on iPhone 12 Pro:
   ----------------------------------------------------------
   Section headings using `.texture-text` + `background-clip: text` at
   `line-height: 1.05` (Constitution --lh-tight) clip the top + bottom
   descenders of Hebrew letters with vertical ink (ת ש ק ק ך ן ה).
   On desktop the type sits at 3.75rem so the 5% leading gap = ~3px of
   slack, enough to hide the issue. On mobile (clamp 2rem → 1rem ish at
   the smallest device) 5% = 1.6px which is below the descender
   thickness — letters get visibly chopped from above and below.

   Plus: `--shabbat__heading` and other section-specific headings inherit
   this, and `.shabbat__heading` had its own `clamp(2rem, 9vw, 2.75rem)`
   override that locked line-height: 1.05 too.

   Fix on mobile only: bump line-height to 1.18 on heading elements that
   carry texture-fill, and reserve a small padding-block buffer so the
   clip doesn't catch a stray descender pixel.
   ========================================================================== */

@media (max-width: 768px) {

  .section-header__title,
  .shabbat__heading,
  .lounge__title,
  .culinary__title,
  .hall-venue h1,
  .hall-venue h2,
  .hall-resort h1,
  .hall-resort h2 {
    line-height: 1.18;
    /* Tiny block padding inside the heading box so the texture-clip
       doesn't catch a half-pixel of descender against the box edge. */
    padding-block: 0.08em;
  }

  /* The hero composes its own type elsewhere — nothing to do for it.
     Eyebrow + lede stay at their existing line-heights; they don't
     use texture-fill. */
}

/* (2026-07-13) The kosher title texture-flip block that lived here was
   removed — the #kosher section was retired (its copy is a detail block in
   #contact). See git history if the section ever returns. */

/* ---------------------------------------------------------------------------
   Mobile: LIGHT-texture headings render as flat light text (no texture).
   ---------------------------------------------------------------------------
   User request 2026-07-01: the cream/gold `background-clip:text` fill
   (--typo-on-dark) reads muddy at phone sizes. Wherever the LIGHT texture is
   painted, show plain light text instead. Mirrors the light-texture selector
   group in css/components/texture-text.css. Dark-texture headings on ivory
   backgrounds are untouched. Gated ≤768px. */
@media (max-width: 768px) {
  [data-bg="dark"] .section-header__title,
  [data-bg="dark"] .band__num,
  .bg-dark .section-header__title,
  .culinary .section-header__title,
  .hero .section-header__title,
  .lounge__title,
  .texture-text--light {
    background-image: none !important;
    -webkit-text-fill-color: var(--fg-on-dark) !important;
    color: var(--fg-on-dark) !important;
  }
}

/* On the smallest viewports (iPhone SE 320), the same rule applies but
   the type is small enough that 1.22 reads more comfortably. */
@media (max-width: 380px) {
  .section-header__title,
  .shabbat__heading,
  .lounge__title,
  .culinary__title {
    line-height: 1.22;
  }
}

/* ===========================================================================
   mobile/css/directions.css — #routes branded map, mobile overrides (§13)
   ---------------------------------------------------------------------------
   Desktop floats a glass card over the map's inline-end/bottom corner. On a
   phone that card would swallow the map, so below 768px we pull it OUT of the
   stage and render it as a static bar beneath the map; tabs scroll
   horizontally; CTAs grow to ≥44px touch targets.

   @media-only per §13. Loaded after css/sections/directions.css via
   mobile/loader.js, so these win the cascade.
   ========================================================================= */

@media (max-width: 768px) {
  /* Taller, full-bleed-ish stage so the map has room above the bar. */
  .directions__stage {
    min-block-size: clamp(300px, 56vh, 460px);
  }

  /* Address search: stack on a phone so neither field is cramped. Input goes
     full-width; the submit button drops below it, also full-width. Both clear
     the ≥44px touch floor (§9). */
  .directions__search { flex-direction: column; align-items: stretch; }
  .directions__search input {
    inline-size: 100%;
    flex: 1 1 auto;
    min-block-size: 44px;
  }
  .directions__search-btn {
    inline-size: 100%;
    min-block-size: 44px;
  }

  /* Card leaves the overlay and becomes a static bar under the map. */
  .directions__card {
    position: static;
    inset: auto;
    max-inline-size: none;
    inline-size: 100%;
    margin-block-start: var(--space-4);
    /* Solid (not glass) once it's on ivory, for crisp contrast. */
    background: var(--ivory);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
    box-shadow: var(--shadow-soft);
  }

  /* The 4 origin tabs (מהמרכז · מירושלים · מהצפון · מהדרום) overflow a phone
     row. The old `justify-content: center` + `overflow-x: auto` was the
     classic flexbox trap: centered overflow clips BOTH edge tabs and they're
     unreachable by scroll (the "מהדרום"/edge-tab clipping the user reported).
     Fix: (1) `safe center` falls back to start-alignment when content
     overflows (so nothing is clipped + the row is scrollable to the edges);
     (2) tighten the label padding/size so all 4 usually FIT without scrolling
     at all; (3) tabs don't shrink, and scroll-padding gives a little inset. */
  .directions__tabs {
    align-self: center;
    max-inline-size: 100%;
    justify-content: safe center;
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    scroll-padding-inline: var(--space-2, 0.5rem);
  }
  .directions__tabs::-webkit-scrollbar { display: none; }

  .directions__tab { flex: 0 0 auto; }

  .directions__tab-label {
    white-space: nowrap;
    /* Tighter than desktop's space-3/space-6 so 4 Hebrew labels fit a 360px
       card without overflow. */
    padding: var(--space-2, 0.5rem) var(--space-3, 0.75rem);
    font-size: var(--text-sm);
  }

  /* §9 touch floor: the radio (inset:0, the real hit target) fills the tab,
     so growing the tab to ≥44px tall grows the tappable area. The tighter
     label padding above keeps 4 labels fitting; this just guarantees the
     vertical hit floor without fattening the visible pill text.
     (fidelity pass 2026-06-15) */
  .directions__tab {
    min-block-size: 44px;
  }

  /* Leaflet zoom +/− default to 30×30. The default 44px sizing made the
     controls look oversized against the branded map on phones; trim to 36px
     — still a comfortable tap target while reading as part of the map UI.
     The glyph stays centered by Leaflet's own line-height/text-align. The map
     control is the one third-party UI on the page — recolor stays in the
     base directions.css; here we only set the size. */
  .directions__map .leaflet-control-zoom a {
    inline-size: 36px;
    block-size: 36px;
    line-height: 36px;
  }

  /* ≥44px touch targets (§9 / touch-targets convention). */
  .directions__cta {
    min-block-size: 44px;
    display: inline-flex;
    align-items: center;
    flex: 1 1 auto;
    justify-content: center;
  }
}

@media (max-width: 480px) {
  .directions__stage {
    min-block-size: clamp(280px, 50vh, 380px);
    border-radius: var(--radius-md);
  }

  /* Stack the three CTAs full-width so labels never truncate. */
  .directions__cta-row { flex-direction: column; }
  .directions__cta { inline-size: 100%; }
}

/* Client pass 2026-09-17 ---------------------------------------------------
   - Leaflet's attribution rendered at 11.9px; lift to the 12px floor.
   - Under 375px the four origin tabs no longer fit one row and the first tab
     sat half off-screen inside the scroller (x = −54 at 320px). Wrap them
     into a centred two-row block instead of hiding tabs behind a scroll. */
@media (max-width: 768px) {
  .directions__map .leaflet-control-attribution { font-size: var(--text-xs); }
}
@media (max-width: 374px) {
  .directions__tabs {
    flex-wrap: wrap;
    justify-content: center;
    overflow-x: visible;
    row-gap: 0;
  }
}

/* =============================================================================
   mobile/css/testimonials.css — #testimonials ("מה מספרים על האירוע אצלנו")
   phone re-layout (§13 mobile-only override pass 2026-06-18)
   ---------------------------------------------------------------------------
   Two problems surfaced via Playwright at 360–390px:

   1. CONTROLS ROW BLEW APART. `.testimonials__controls` is a single flex row
      [ prev · dots · next ]. The dots row carries 8 dots whose tappable boxes
      are 46px each (the §9 floor lives in touch-targets.css). 8 × 46 = 368px,
      which is WIDER than the 328–358px controls row — so the dots overflowed
      to the full viewport width, shoved the ‹ › arrows into a cramped cluster,
      and spilled the dots onto a second full-bleed band below the card. Fix:
      let the controls WRAP (dots get their own centred row, arrows on their
      own row) and cap the dots row so it never exceeds the content width.

   2. ACTIVE DOT RENDERED AS A GIANT BRASS BLOB once touch-targets.css pads the
      box to 46px (full root-cause + fix in the .is-active rule comment below).
      The active dot is meant to be a small brass pill (the intended slider
      indicator from css/sections/motion-slider.css) — we keep that, just stop
      it flooding the padded tap box.

   Heading descender clearance is already handled in mobile/css/headings.css;
   here we only add a little extra top breathing room so the title never reads
   as crowding the card on a narrow column. Card padding is fine (core drops to
   --space-8 ≤600px); we keep that.

   §4 logical props · §5 token colors only · §13 self-contained, @media only.
   The 46px dot hit area stays owned by touch-targets.css — NOT duplicated here.
   ========================================================================= */

@media (max-width: 768px) {

  /* A touch more space below the section header so the large texture-fill
     title sits clear of the card on the narrow column. (Descender clearance
     itself lives in headings.css.) */
  .testimonials > .section-header {
    margin-block-end: var(--space-10);
  }

  /* Prev/next arrows are BACK on phones (client pass 2026-09-17 — "add arrows
     to every gallery"; they were hidden 2026-06-22). They sit on their own
     centred row BELOW the dots grid (dots order 1, arrows order 2), so a 320px
     column never has to fit 2×44 + 4×46 on one line. DOM order prev → next
     keeps RTL (prev right, next left). Circle frame = visible without hover.
     The base .btn--icon hover/active scales are neutralised: they replaced
     the inherited translateY and made the buttons jump. */
  .testimonials__btn--prev,
  .testimonials__btn--next {
    order: 2;
    position: static;
    inset: auto;
    transform: none;
    inline-size: 48px;
    block-size: 48px;
    border-radius: var(--radius-circle);
    border: 1px solid color-mix(in srgb, var(--brass-deep) 60%, transparent);
    background: color-mix(in srgb, var(--ivory) 70%, transparent);
    color: var(--brass-deep);
    font-size: var(--text-xl);   /* the ‹ › glyph inherited the 13px .btn label size */
    line-height: 1;
  }
  .testimonials__btn--prev:hover,
  .testimonials__btn--next:hover,
  .testimonials__btn--prev:focus-visible,
  .testimonials__btn--next:focus-visible {
    transform: none;
  }
  /* 2026-10-11: the scaleX(-1) "flip" that lived here was wrong — ‹ › ARE
     bidi-mirrored in RTL (UAX#9 L4), so they already point outward (as on
     desktop); the flip turned both arrows inward (client report). Removed. */
  .testimonials__btn--prev:active,
  .testimonials__btn--next:active {
    transform: scale(0.94);
    background: color-mix(in srgb, var(--brass) 22%, var(--ivory));
  }

  /* Controls wrap: dots grid on its own line, the arrow pair beneath. */
  .testimonials__controls {
    flex-wrap: wrap;
    justify-content: center;
    column-gap: var(--space-8);
    row-gap: var(--space-4);
  }

  /* Eight dots each carry a 46px tap box (touch-targets.css) → 8 × 46 = 368px,
     which overflows a 360px phone. They can't all fit one row while honouring
     the §9 tap floor, so the dots take their own full-width line and lay out as
     a fixed 4-column GRID → a BALANCED, even 4 + 4 block, centred. Grid (not
     flex-wrap) keeps the columns even regardless of the active dot being a wider
     brass pill (which threw the flex-wrap onto uneven 3 + 4 + 1 lines). */
  /* [data-slider-dots] added 2026-07-13: the core fix that un-absoluted the
     dots (css/sections/testimonials.css) uses specificity (0,2,0), so this
     grid re-layout must match it or the dots collapse back to one overflowing
     flex row. */
  .testimonials__dots[data-slider-dots] {
    order: 1;
    flex-basis: 100%;
    display: grid;
    /* 2026-09-17: 13 dots × 46px in 4 columns stacked FOUR rows (~200px of
       dots). 6 columns of the full 46px tap box fit a 320px phone (276px)
       → 3 rows, keeping the §9 44px floor. */
    grid-template-columns: repeat(6, 46px);
    /* The core gap (space-2) survived as column-gap → 6×46 + 5×8 = 316px, wider
       than the 288px column at 320. The 46px boxes already space the 10px dots. */
    column-gap: 0;
    justify-content: center;
    justify-items: center;
    align-items: center;
    row-gap: var(--space-2, 0.5rem);
    margin-inline: auto;
  }

  /* The active dot's brass fill comes from a `background` SHORTHAND (in both
     testimonials.css and motion-slider.css) which RESETS background-clip back
     to border-box — so on mobile, where touch-targets.css pads the box to 46px,
     the brass floods the whole 46px padded box into a giant blob. Re-assert the
     content-box clip so the active fill stays inside its small content box and
     reads as the intended brass pill, not a blob. (Both this and the inactive
     dots inherit the 46px tap box from touch-targets.css unchanged.) */
  .testimonials__dots [data-slider-dot].is-active {
    background-clip: content-box;
    -webkit-background-clip: content-box;
    /* The active pill is 24px wide (motion-slider.css); with the 18px tap padding
       its box grew to 60px (72px after scale 1.2) and spilled past a 320px screen
       from the edge columns. 11px inline padding keeps a 46px box. */
    padding-inline: 11px;
  }

  /* 2026-10-11 (Moshe): no dots on phones — 14 dots stacked three rows under
     the card. The arrows (and swipe) navigate; the grid rules above stay in
     case the dots return. Placed last so it wins over the grid display. */
  .testimonials__dots[data-slider-dots] { display: none; }
}

/* =============================================================================
   mobile/css/interaction-hint.css — phone tuning for the brass affordance cue
   ---------------------------------------------------------------------------
   §13: @media (max-width:768px) overrides ONLY. The base cue (css/components/
   interaction-hint.css) is minimalist text+icon; on a 390px phone the wide
   letter-spacing can push it past the viewport edge, so tighten it and lift it
   a touch clear of the thumb zone. Loaded by mobile/loader.js.
   ========================================================================== */

@media (max-width: 768px) {
  .ix-hint {
    inset-block-end: clamp(20px, 5vh, 56px);
    gap: .45rem;
    letter-spacing: .12em;
    font-size: var(--text-sm);
    max-inline-size: calc(100vw - 32px);
  }
  .ix-hint__text { white-space: normal; text-align: center; line-height: 1.4; }
  /* Keep the two-line French cue above the fixed 52px contact bar. */
  .culinary .ix-hint { inset-block-end: calc(52px + var(--space-4) + env(safe-area-inset-bottom)); }
}

/* =============================================================================
   mobile/css/lang-switch.css — always-visible language FAB on mobile hero
   ---------------------------------------------------------------------------
   Injected by mobile/loader.js injectLangFab(). Fixed at the top-end corner
   of the viewport so the HE⇄EN toggle is reachable BEFORE the navbar reveals.
   Once the navbar reveals (data-nav-revealed) we hide the FAB so only one
   toggle is visible at a time.
   Logical properties + tokens only (§4, §5). @media (max-width: 768px) scoped.
   ========================================================================= */

@media (max-width: 768px) {
  .mobile-lang-fab {
    position: fixed;
    inset-block-start: 1rem;
    inset-inline-end: 1rem;
    z-index: 120; /* above hero (110/111) and below nav overlay (130+) */
    /* Subtle blur backing so the pill reads over any hero art */
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    border-radius: 999px;
  }

  /* Keep the FAB visible through the hero (the user wants the language switch
     reachable IN the hero, where the navbar is hidden). PAST the hero the
     navbar carries its own permanent lang toggle (injectNavbarLang, 2026-07-13)
     — so the FAB now hides whenever hero-mode is off, unconditionally. (The
     old data-nav-revealed condition left windows where BOTH toggles showed,
     overlapping at the top corner.) */
  html:not([data-hero-mode="true"]) .mobile-lang-fab {
    display: none;
  }
  /* 2026-10-11: inside the hero the navbar can slide in (scroll-up / top band,
     data-nav-revealed). It carries its own language button, and the FAB sat
     right on top of the hamburger — two globes, no menu (client report). The
     FAB is only for while the bar is hidden. */
  html[data-nav-revealed="true"] .mobile-lang-fab {
    display: none;
  }

  /* --- Lang toggle inside the navbar (2026-07-13, user request) ------------
     Injected by loader injectNavbarLang() between the brand and the hamburger.
     The desktop bar's own toggle lives in .site-nav__links, which is
     display:none ≤768px — this slot is the phone's permanent in-bar switch
     (the FAB still covers the hero, where the navbar is hidden). */
  .site-nav__lang-bar-slot {
    display: flex;
    align-items: center;
    /* Sit against the hamburger; brand keeps the other end (space-between). */
    margin-inline-start: auto;
    margin-inline-end: 0.6rem;
  }
  .site-nav__lang-bar-slot .site-nav__lang--bar { flex: none; }
}

/* --- Narrow-phone fit (client pass 2026-09-17) ----------------------------
   Measured at 320px: brand (≈152px) + lang group (≈120px) + hamburger (44px)
   + gaps overflowed the bar by ~90px and pushed the HAMBURGER off-screen
   (x = −60…−20, the menu was unreachable); at 375px it was still clipped.
   Cap the brand width, tighten gaps, drop the decorative globe glyph under
   400px. The language options also rendered at 11.4px — lift to ≥12px. */
@media (max-width: 768px) {
  .site-nav__lang-opt { font-size: 0.78rem; }
}
@media (max-width: 480px) {
  .site-nav__inner {
    gap: var(--space-3);
    padding-inline: var(--space-4);
  }
  .site-nav__brand-logo { max-inline-size: 28vw; }
  .site-nav__lang-bar-slot { margin-inline-end: var(--space-1); }
  .site-nav__lang--bar .site-nav__lang-opt {
    padding-inline: 0.5em;
    min-width: 2em;
  }
}
@media (max-width: 400px) {
  .site-nav__lang--bar .site-nav__lang-globe { display: none; }
}

/* --- Menu overlay on phones (client pass 2026-09-17) ----------------------
   Measured at 320×568: 11 links at 71px each (≈1160px of list) and the close
   (×) button — pinned at 64px — overlapped the first link's frame. Lift the
   close button into the top gutter, start the list below it, and use a
   compact 46px row (still ≥44px) so most phones show the whole menu without
   scrolling (the overlay stays scrollable for the smallest screens). */
@media (max-width: 768px) {
  .site-nav__mobile {
    padding-block: calc(var(--space-16) + var(--space-2)) var(--space-10);
  }
  .site-nav__mobile__close {
    inset-block-start: var(--space-3);
  }
  .site-nav__mobile__links {
    gap: var(--space-1);
  }
  .site-nav__mobile__links a {
    font-size: var(--text-lg);
    padding-block: var(--space-2);
  }
}

/* Desktop safety: the slot is only injected ≤768px, but if a resized session
   carries it up, keep it out of the desktop bar (the links-list toggle owns
   that breakpoint). */
@media (min-width: 769px) {
  .site-nav__lang-bar-slot { display: none; }
}

@media (max-width: 768px) {
  .mobile-lang-fab { display: none; }
  .site-nav__inner { block-size: 64px; min-block-size: 64px; padding-block: var(--space-2); padding-inline: var(--space-3); gap: var(--space-2); }
  .site-nav__brand-logo { inline-size: clamp(80px, 26vw, 130px); max-inline-size: 100%; }
  .site-nav__lang-bar-slot { margin-inline: auto 0; }
  .site-nav__lang--bar .site-nav__lang-opt { min-inline-size: 32px; min-block-size: 44px; }
}

/* =============================================================================
   mobile/css/buttons.css — long-CTA wrap on phones (2026-07-02)
   ---------------------------------------------------------------------------
   The primary CTAs (.btn) are single-line, uppercase, letter-spaced "engraved"
   labels (css/components/buttons.css: white-space:nowrap + letter-spacing .18em).
   Hebrew labels are short and fit a 390px phone; the English/French translations
   are much longer — e.g. FR "Envoyer vos coordonnées et organiser une visite au
   domaine" — and overflow the right edge on one nowrap line (the last words get
   clipped off-screen). Let the large/primary CTA WRAP on phones and tighten the
   tracking a touch so any language fits cleanly. Desktop (≥769px) keeps the
   single engraved line. §13: @media (max-width:768px) overrides only.
   ========================================================================= */

@media (max-width: 768px) {
  .btn--primary-lg,
  .contact__submit {
    white-space: normal;          /* allow multi-line for long EN/FR labels */
    letter-spacing: 0.12em;       /* was .18em — a hair tighter for wrapped lines */
    line-height: 1.4;
    text-wrap: balance;           /* even, centred line breaks */
    max-inline-size: 100%;        /* never exceed the form/column width */
  }
}

/* =========================================================================
   mobile/css/why-gamos.css — ≤768px fit for the #why-gamos trust section
   (conversion pass 2026-07-13). §13: media-query overrides ONLY.
   ========================================================================= */

@media (max-width: 768px) {
  .why-gamos__stats {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-4);
  }

  /* Odd count (5) — the last stat centres across both columns. */
  .why-gamos__stat:last-child:nth-child(odd) { grid-column: 1 / -1; }

  .why-gamos__quotes {
    grid-template-columns: 1fr;
    gap: var(--space-4);
  }
}

@media (max-width: 480px) {
  .why-gamos__stat dd { font-size: 2rem; }
}

/* =========================================================================
   mobile/css/cta-bar.css — fixed bottom action bar (conversion pass
   2026-07-13, marketing critique: "בר קבוע במובייל עם שתי-שלוש פעולות").
   §13: media-query overrides ONLY; injected markup lives in mobile/loader.js
   (injectCtaBar). Three ≥44px targets: phone · WhatsApp · book-a-tour
   (the visually-primary action, #contact). Tokens only (§10.2).
   ========================================================================= */

@media (max-width: 768px) {
  .mobile-cta-bar {
    position: fixed;
    inset-inline: 0;
    inset-block-end: 0;
    z-index: 120; /* above hero (110/111), below nav overlay (130+) and --z-modal lightbox */
    display: flex;
    align-items: stretch;
    background: color-mix(in srgb, var(--ivory) 94%, transparent);
    -webkit-backdrop-filter: blur(10px);
    backdrop-filter: blur(10px);
    border-block-start: 1px solid var(--border);
    padding-block-end: env(safe-area-inset-bottom);
  }

  .mobile-cta-bar__action {
    flex: 1 1 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    min-block-size: 52px; /* ≥44px WCAG target */
    font-family: var(--font-body);
    font-weight: var(--fw-medium);
    font-size: 0.9rem;
    color: var(--cocoa);
    text-decoration: none;
    white-space: nowrap;
  }

  .mobile-cta-bar__action + .mobile-cta-bar__action {
    border-inline-start: 1px solid var(--border);
  }

  /* The one primary action — brass-weighted, same line-language (no box). */
  .mobile-cta-bar__action--primary {
    font-weight: var(--fw-semibold);
    color: var(--brass-deep);
    box-shadow: inset 0 -2.5px 0 var(--brass); /* the .btn--filled underline, flush to the bar edge */
  }

  .mobile-cta-bar__action:focus-visible {
    outline: 3px solid var(--brass);
    outline-offset: -3px;
  }

  .mobile-cta-bar__icon { display: inline-flex; inline-size: 1.05em; block-size: 1.05em; }
  .mobile-cta-bar__icon svg {
    inline-size: 100%; block-size: 100%;
    stroke: currentColor; fill: none;
    stroke-width: 1.6; stroke-linecap: round; stroke-linejoin: round;
  }

  /* Keep the last in-flow content reachable above the fixed bar. Same
     condition the bar is injected under (loader matchMedia ≤768px). Was scoped
     to html[data-view="mobile"] — the retired /mobile/ page; the bar is injected
     on / at this width, so the reservation applies there now (2026-09-17). */
  body { padding-block-end: calc(52px + env(safe-area-inset-bottom)); }

  /* 2026-10-11 (Moshe: "למה יש כל כך הרבה CTA"): on phones the bar above is
     the one standing call to action, plus the #contact form. Removed here:
     the tour/WhatsApp pairs after #why-gamos and #testimonials (.cta-band,
     incl. their lead-in line), the footer's closing pair, and the footer's
     contact list — the same address/phones/email as the #contact rows just
     above it. Desktop keeps all of them. */
  .cta-band,
  .site-footer__cta-row,
  .site-footer__col--contact { display: none; }
}

/* ===========================================================================
   mobile/css/events.css — #events editorial gallery, phone layout (§13)
   (2026-07-13 v4 — stacked cards, user-chosen after the sticky panel
   overlapped rows and clipped their titles on phones)

   On ≤768px the section becomes a calm stacked catalog: each event is a
   self-contained card — framed image, name, and the full description always
   visible. No sticky panel, no scroll-driven activation, no moving state
   (the core module skips its coarse branch entirely). The desktop preview
   panel is hidden; each row's own <picture> (events__media) shows instead,
   loaded by native lazy-loading with the .half.webp source.

   §13: @media rules only. No desktop leakage.

   Gate note (2026-07-13 review fix): the query includes (hover: none) in
   addition to ≤768px so it exactly mirrors the core module's coarse gate —
   without it, wide touch devices (iPad portrait/landscape, >768px) got the
   desktop two-column stage with a permanently EMPTY panel and collapsed
   descriptions (the JS deliberately does nothing on coarse pointers).
   Wherever the JS opts out, these cards apply. ========================== */

@media (max-width: 768px), (hover: none) {
  /* The desktop preview panel is replaced by per-card images. */
  .events__preview { display: none; }

  .events__stage {
    grid-template-columns: 1fr;
    grid-template-areas: "list";
  }

  /* Cards instead of hairline rows. */
  .events__list { border-block-end: none; }
  .events__row { border-block-start: none; }
  .events__row + .events__row { margin-block-start: var(--space-10); }

  .events__link {
    padding: 0;
    min-block-size: 44px;
  }

  /* Framed card image — same frame language as the desktop panel. */
  .events__media {
    display: block;
    aspect-ratio: 3 / 2;
    border: 1px solid color-mix(in srgb, var(--brass) 30%, transparent);
    border-radius: var(--radius-md);
    overflow: hidden;
    margin-block-end: var(--space-4);
  }

  .events__media picture,
  .events__media img {
    display: block;
    inline-size: 100%;
    block-size: 100%;
    object-fit: cover;
  }

  .events__name {
    font-size: clamp(1.5rem, 6.5vw, 2rem);
    color: var(--brass-glow);
  }

  /* Description always visible — no expand/collapse state on phones. */
  .events__desc-wrap { grid-template-rows: 1fr; }
  .events__desc {
    opacity: 1;
    padding-block-start: var(--space-3);
    transition: none;
  }

  /* No active-state chrome on phones (no hover, no activation). */
  .events__row .events__link::before { display: none; }
  .events__row.is-active .events__link { padding-inline-start: 0; }
}

/* =========================================================================
   mobile/css/site-footer.css — narrow-viewport fit for the closing footer
   (footer-craft pass 2026-07-21; hierarchy pass 2026-09-17). §13:
   media-query overrides ONLY; the desktop-first rules live in
   css/sections/site-footer.css. Injected by mobile/loader.js (MOBILE_CSS
   array) AFTER the desktop CSS, so these win ties without !important.

   2026-09-17 client pass — "make the footer tidy and easy to use":
     1. brand mark (injected by loader setupFooterLogo) heads the column;
     2. claim + lede;
     3. the two CTAs as full-width buttons (mobile/css/contact-actions.css);
     4. contact rows as tappable cards (contact-actions.css);
     5. social links as bordered chips (icon + label);
     6. legal links (privacy · terms · accessibility) as a centred, wrapping
        row of ≥44px targets at 14px — they were 12px/18px-tall;
     7. copyright + back-to-top.
   Tighter rhythm (48/32 instead of 64/48) — no content removed.

   CTA-bar clearance: the fixed bottom bar (mobile/css/cta-bar.css) reserves
   its height on <body>, so the bar floats over empty space below the footer;
   the footer adds safe-area on top of its own bottom padding.
   ========================================================================= */

@media (max-width: 768px) {
  .site-footer {
    padding-block: var(--space-12) var(--space-10);
    padding-block-end: calc(var(--space-10) + env(safe-area-inset-bottom));
  }

  .site-footer__inner {
    gap: var(--space-8);
  }

  .site-footer__close {
    gap: var(--space-4);
  }

  /* 1. Brand mark */
  .site-footer__logo {
    display: block;
    inline-size: clamp(112px, 34vw, 148px);
    block-size: auto;
    margin-block-end: var(--space-2);
  }

  .site-footer__cta-row {
    margin-block-start: var(--space-2);
  }

  /* Contact + social stack into a single readable column. */
  .site-footer__meta {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-8);
    padding-block-start: var(--space-8);
  }

  .site-footer__col-title {
    margin-block-end: var(--space-3);
    text-align: center;
  }

  .site-footer__contact {
    max-inline-size: none;
  }

  /* 5. Social chips */
  .site-footer__social {
    flex-direction: row;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-3);
  }
  .site-footer__social-link {
    min-block-size: 48px;
    padding-inline: var(--space-5);
    border: 1px solid color-mix(in srgb, var(--brass) 45%, transparent);
    border-radius: var(--radius-pill);
    background: color-mix(in srgb, var(--ivory) 5%, transparent);
  }
  .site-footer__social-link:active {
    background: color-mix(in srgb, var(--brass) 18%, transparent);
  }
  .site-footer__social-link:focus-visible {
    box-shadow: var(--focus-ring);
  }

  /* Copyright / legal / back-to-top stack and centre — no cramped row. */
  .site-footer__base {
    flex-direction: column;
    align-items: center;
    text-align: center;
    gap: var(--space-3);
    padding-block-start: var(--space-6);
  }

  /* 6. Legal links */
  .site-footer__legal {
    justify-content: center;
    gap: 0 var(--space-2);
  }
  .site-footer__legal a {
    display: inline-flex;
    align-items: center;
    min-block-size: 44px;
    padding-inline: var(--space-3);
    font-size: var(--text-sm);
    text-decoration: underline;
    text-decoration-color: color-mix(in srgb, var(--ivory) 30%, transparent);
    text-underline-offset: 4px;
  }
  .site-footer__legal a:focus-visible {
    box-shadow: var(--focus-ring);
    border-radius: var(--radius-sm);
  }

  .site-footer__copyright {
    font-size: var(--text-sm);
  }
}

@media (max-width: 480px) {
  .site-footer__claim {
    font-size: clamp(1.75rem, 8vw, 2.25rem);
  }
}

/* The brand mark is injected on phones only; never show it wider. */
@media (min-width: 769px) {
  .site-footer__logo { display: none; }
}

/* =============================================================================
   mobile/css/spacing.css — phone section rhythm (client pass 2026-09-17)
   ---------------------------------------------------------------------------
   Client note: "a lot of empty space between the blocks". Root cause (measured
   at 390×844): every section kept the DESKTOP rhythm token --section-pad
   (128px) on BOTH edges → 256px of air between any two sections, plus a few
   fixed viewport-height boxes (lounge 25vh top pause).

   Fix: an asymmetric phone rhythm derived from the spacing scale —
     top    ≈104px (--space-24 + --space-2)  clears the fixed ~81px nav, so an
            in-page anchor jump (GSAP scrollTo lands the section top at y=0,
            no offset — js/scrollytelling.js) never hides the heading;
     bottom ≈ 56px (--space-12 + --space-2)
   → ~160px between sections instead of 256px. Not a global reset: only the
   section shells listed below, each measured by
   mobile/scripts/qa-client-pass.mjs (gap ≤176px).

   §13: width-only @media, tokens only, logical properties. Desktop untouched.
   ========================================================================== */

@media (max-width: 768px) {
  :root {
    --m-section-top: calc(var(--space-24) + var(--space-2));    /* 104px */
    --m-section-bottom: calc(var(--space-12) + var(--space-2)); /* 56px */
  }

  .why-gamos,
  .rooms,
  .about,
  .testimonials,
  .contact,
  .routes,
  .culinary__post {
    padding-block: var(--m-section-top) var(--m-section-bottom);
  }

  /* Lounge: the desktop 25vh "post-hero pause" becomes the phone rhythm. */
  .lounge {
    padding-block: var(--m-section-top) var(--m-section-bottom);
  }

  /* 2026-10-11 (Moshe: "too much space after the reviews on small phones"):
     the reviews used to end on a tour/WhatsApp band, now hidden on phones
     (mobile/css/cta-bar.css), so they end on the arrow pair and the 56px
     bottom + the gallery's 104px top read as a 160px hole (67% of a 568px
     screen). Trim only the reviews' bottom — the gallery's 104px top stays,
     it keeps its heading clear of the fixed bar on a menu jump. → 112px. */
  .testimonials {
    padding-block-end: var(--space-2);
  }

  /* Same for contact → map (Moshe, 2026-10-11): the contact section now ends on
     its kosher/access text (the "scroll to the map" cue was removed), so its
     56px bottom + the map section's 104px top was another 160px hole. Trim the
     contact bottom only; #routes keeps its 104px top (menu-jump clearance). */
  .contact {
    padding-block-end: var(--space-2);
  }

  /* Gallery keeps its zero bottom (the decorative outro closes it). */
  .gallery {
    padding-block-start: var(--m-section-top);
  }

  /* Events: the arch is only the rounded top corners; capped so it doesn't
     inflate the gap on wider phones (6vw = 46px at 767px). Width-only block on
     purpose — mobile/css/events.css shares a (hover:none) alternative that
     also reaches wide touch screens. */
  .events {
    padding-block: var(--m-section-top) var(--m-section-bottom);
  }

  /* The gallery's decorative outro (arch + GAMOS mark) already closes the
     section; trim its own bottom padding so outro → events isn't a double pause. */
  .gallery__outro-inner {
    padding-block-end: var(--space-4);
  }

  /* (events arch: the rounded top corners live inside the 104px top.) */

  /* Header → content: 48px + the gallery's own 48px grid offset stacked
     ~100px between a title and its photos on a phone. */
  .section-header {
    margin-block-end: var(--space-8);
  }
  .gallery__grid {
    margin-block-start: var(--space-6);
  }

  /* A cta-band closing a section adds its own bottom padding on top of the
     section's — drop it so the band ends on the section rhythm. */
  .why-gamos > .cta-band:last-child,
  .testimonials > .cta-band:last-child,
  .why-gamos .cta-band:last-child,
  .testimonials .cta-band:last-child {
    padding-block-end: 0;
  }
}

/* Phones in landscape (≤500px tall): a 160px pause is half the screen. Keep
   enough top to clear the compact nav, shorten the rest (→ ~128px). */
@media (max-width: 768px) and (max-height: 500px) {
  :root {
    --m-section-top: calc(var(--space-20) + var(--space-2));   /* 88px */
    --m-section-bottom: var(--space-10);                        /* 40px */
  }
}

/* =============================================================================
   mobile/css/contact-actions.css — make contact + WhatsApp actions read as
   tappable on phones (client pass 2026-09-17, "מסגרת בשפת המותג")
   ---------------------------------------------------------------------------
   Desktop language (css/components/buttons.css) is deliberately "text + a
   brass underline, no box". On a phone there is no hover, so the growing
   underline never appears and every CTA reads as plain text — the client could
   not tell what is clickable. On ≤768px ONLY, the contact-related actions get
   a box in the brand vocabulary:

     PRIMARY  (book a tour, form submit)  → solid brass, ink-deep label (8.7:1)
     SECONDARY (WhatsApp, directions)     → brass hairline frame (brass-deep on
                                            ivory 4.4:1; brass on ink 8.7:1)
     CHANNEL ROWS (#contact + footer)     → full-width card rows, icon chip,
                                            reading-direction chevron, the WHOLE
                                            row is the link's hit area

   Scoped selectors only — never the global .btn. Links, labels and hrefs are
   untouched (no copy change; the existing "דברו איתנו בוואטסאפ" stays).
   Feedback: :active dims + dips; :focus-visible keeps the site --focus-ring.
   §13 width-only @media · §5 tokens only · §4 logical properties.
   ========================================================================== */

@media (max-width: 768px) {

  /* ---------- shared box geometry ---------- */
  .cta-band__actions .btn,
  .site-footer__cta-row .btn,
  .directions__cta-row .btn,
  #contact .contact__submit {
    min-block-size: 48px;
    padding-block: var(--space-3);
    padding-inline: var(--space-6);
    border-radius: var(--radius-sm);
    white-space: normal;
    text-align: center;
    line-height: 1.25;
    letter-spacing: 0.06em;
    text-shadow: none;
    transition: background-color var(--dur-fast) var(--ease-out-cinema),
                border-color var(--dur-fast) var(--ease-out-cinema);
  }

  /* The underline affordance is replaced by the box on phones. */
  .cta-band__actions .btn::after,
  .site-footer__cta-row .btn::after,
  .directions__cta-row .btn::after,
  #contact .contact__submit::after,
  .site-footer .site-footer__cta--primary::after {
    display: none;
  }

  /* Stack the pair full-width so the tap targets are thumb-sized. */
  .cta-band__actions,
  .site-footer__cta-row {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-3);
    inline-size: min(100%, 22rem);
    margin-inline: auto;
  }

  /* ---------- PRIMARY: solid brass ---------- */
  .cta-band__actions .btn--filled,
  .site-footer__cta-row .site-footer__cta--primary,
  #contact .contact__submit {
    background: var(--brass);
    color: var(--ink-deep);
    border: 1.5px solid var(--brass);
  }
  .cta-band__actions .btn--filled:hover,
  .cta-band__actions .btn--filled:focus-visible,
  .site-footer__cta-row .site-footer__cta--primary:hover,
  .site-footer__cta-row .site-footer__cta--primary:focus-visible,
  #contact .contact__submit:hover,
  #contact .contact__submit:focus-visible {
    color: var(--ink-deep);
  }
  .cta-band__actions .btn--filled:active,
  .site-footer__cta-row .site-footer__cta--primary:active,
  #contact .contact__submit:active {
    background: color-mix(in srgb, var(--brass) 82%, var(--ink-deep));
    transform: scale(0.98);
  }

  /* ---------- SECONDARY: brass hairline frame ---------- */
  .cta-band__actions .btn:not(.btn--filled),
  .directions__cta-row .btn {
    background: transparent;
    color: var(--cocoa);
    border: 1.5px solid var(--brass-deep);
  }
  .site-footer__cta-row .btn:not(.site-footer__cta--primary) {
    background: transparent;
    color: var(--ivory);
    border: 1.5px solid var(--brass);
  }
  .cta-band__actions .btn:not(.btn--filled):active,
  .directions__cta-row .btn:active {
    background: color-mix(in srgb, var(--brass) 18%, transparent);
    transform: scale(0.98);
  }
  .site-footer__cta-row .btn:not(.site-footer__cta--primary):active {
    background: color-mix(in srgb, var(--brass) 22%, transparent);
    transform: scale(0.98);
  }

  /* WhatsApp glyph on every WhatsApp button (matched by destination, so no
     markup change). currentColor via mask → stays in the brand palette. */
  .cta-band__actions a.btn[href^="https://wa.me/"]::before,
  .site-footer__cta-row a.btn[href^="https://wa.me/"]::before,
  .directions__cta-row a.btn[href^="https://wa.me/"]::before {
    content: "";
    flex: none;
    inline-size: 1.25em;
    block-size: 1.25em;
    background: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 11.5a8.4 8.4 0 0 1-12.3 7.4L3 21l2.2-5.4A8.5 8.5 0 1 1 21 11.5z'/%3E%3Cpath d='M9 9.5c.5 2.5 3 5 5.5 5.5l1-1.5-2-1-1 .5c-.8-.5-1.5-1.2-2-2l.5-1-1-2z'/%3E%3C/svg%3E") center / contain no-repeat;
    mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M21 11.5a8.4 8.4 0 0 1-12.3 7.4L3 21l2.2-5.4A8.5 8.5 0 1 1 21 11.5z'/%3E%3Cpath d='M9 9.5c.5 2.5 3 5 5.5 5.5l1-1.5-2-1-1 .5c-.8-.5-1.5-1.2-2-2l.5-1-1-2z'/%3E%3C/svg%3E") center / contain no-repeat;
  }

  /* ---------- CHANNEL ROWS (#contact on ivory, footer on ink) ---------- */
  .contact__channels { gap: var(--space-3); }
  .site-footer__contact { gap: var(--space-3); }

  .contact__channel:has(a[href]),
  .site-footer__contact-row:has(a[href]) {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-4);
    min-block-size: 64px;
    padding-block: var(--space-3);
    padding-inline: var(--space-4);
    border-radius: var(--radius-md);
    transition: background-color var(--dur-fast) var(--ease-out-cinema),
                transform var(--dur-fast) var(--ease-out-cinema);
  }
  .contact__channel:has(a[href]) {
    background: color-mix(in srgb, var(--mist) 60%, var(--ivory));
    border: 1px solid color-mix(in srgb, var(--brass-deep) 55%, transparent);
  }
  .site-footer__contact-row:has(a[href]) {
    flex-wrap: nowrap;
    background: color-mix(in srgb, var(--ivory) 5%, transparent);
    border: 1px solid color-mix(in srgb, var(--brass) 45%, transparent);
  }

  /* Non-link row (address) stays a quiet line — visibly NOT a button. */
  .contact__channel:not(:has(a[href])) {
    padding-inline: var(--space-4);
  }

  /* Icon chip. */
  .contact__channel:has(a[href]) .contact__channel-icon {
    box-sizing: content-box;
    padding: 9px;
    border-radius: var(--radius-circle);
    background: color-mix(in srgb, var(--brass) 22%, var(--ivory));
  }

  /* The whole row is the link: the anchor goes static so its ::before (unused
     by the desktop CSS) stretches over the positioned row. */
  .contact__channel:has(a[href]) .contact__channel-value,
  .site-footer__contact-row:has(a[href]) .site-footer__contact-value {
    position: static;
    min-block-size: 0;
  }
  .contact__channel:has(a[href]) .contact__channel-value::before,
  .site-footer__contact-row:has(a[href]) .site-footer__contact-value::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    z-index: 1;
  }
  /* The footer link's desktop hover underline would now span the row. */
  .site-footer__contact-row .site-footer__contact-value::after {
    display: none;
  }
  .site-footer__contact-row:has(a[href]) .site-footer__contact-label {
    min-inline-size: 5.5rem;
  }

  /* Reading-direction chevron at the row end (points "forward"). */
  .contact__channel:has(a[href])::after,
  .site-footer__contact-row:has(a[href])::after {
    content: "";
    flex: none;
    margin-inline-start: auto;
    inline-size: 8px;
    block-size: 8px;
    border-block-end: 2px solid currentColor;
    border-inline-end: 2px solid currentColor;
    transform: rotate(-45deg);
  }
  html[dir="rtl"] .contact__channel:has(a[href])::after,
  html[dir="rtl"] .site-footer__contact-row:has(a[href])::after {
    transform: rotate(45deg);
  }
  .contact__channel:has(a[href])::after { color: var(--brass-deep); }
  .site-footer__contact-row:has(a[href])::after { color: var(--brass-glow); }

  /* Press + focus feedback on the whole row. */
  .contact__channel:has(a[href]:active) {
    background: color-mix(in srgb, var(--brass) 20%, var(--ivory));
    transform: scale(0.99);
  }
  .site-footer__contact-row:has(a[href]:active) {
    background: color-mix(in srgb, var(--brass) 18%, transparent);
    transform: scale(0.99);
  }
  .contact__channel:has(a[href]:focus-visible),
  .site-footer__contact-row:has(a[href]:focus-visible) {
    box-shadow: var(--focus-ring);
  }
  .contact__channel a[href]:focus-visible,
  .site-footer__contact-row a[href]:focus-visible {
    outline: none;
    box-shadow: none;
  }
}

/* Narrow phones: at 320px the footer row (label 5.5rem + e-mail + chevron +
   padding) was 9px wider than the column, and the footer's reveal clip cut
   off — and made untappable — that sliver. Tighten the row geometry. */
@media (max-width: 374px) {
  .contact__channel:has(a[href]),
  .site-footer__contact-row:has(a[href]) {
    gap: var(--space-3);
    padding-inline: var(--space-3);
  }
  .site-footer__contact-row:has(a[href]) .site-footer__contact-label {
    min-inline-size: 4.25rem;
  }
}

@media (max-width: 768px) and (prefers-reduced-motion: reduce) {
  .contact__channel:has(a[href]),
  .site-footer__contact-row:has(a[href]),
  .cta-band__actions .btn,
  .site-footer__cta-row .btn,
  .directions__cta-row .btn,
  #contact .contact__submit {
    transition: none;
  }
}

/* =============================================================================
   mobile/css/perf.css — phone rendering-cost overrides (2026-10-11 perf pass)
   PLANS/next-steps/2026-10-11_mobile-perf-pass.md. @media-only per §13.
   ========================================================================== */

@media (max-width: 768px) {
  /* Reveals: css/sections/motion-reveals.css keeps will-change on all 75
     [data-reveal] elements and ends on translate3d(0,0,0) — both pin a GPU layer
     per element forever (gallery photos included). The transition is still
     composited while it runs; after it, the element goes back to normal paint.
     `transform: none` is the same identity transform as the desktop end state. */
  [data-reveal] { will-change: auto; }
  [data-reveal].is-visible { transform: none; }

  /* Full-width fixed bars: a backdrop blur is recomputed every scroll frame over
     the moving hero/canvas. Near-opaque fills read the same without it. */
  .site-nav,
  html[data-hero-mode="true"][data-nav-revealed="true"] .site-nav,
  html[data-hero-mode="true"] .site-nav:focus-within,
  .mobile-cta-bar {
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
  .site-nav { background: color-mix(in srgb, var(--ivory) 97%, transparent); }
  .mobile-cta-bar { background: color-mix(in srgb, var(--ivory) 98%, transparent); }

  /* Past the hero the bar sat at --z-sticky (100), under #hall-portal (111,
     css/sections/hero-scene.css) — the portal covered the bar and hamburger. */
  .site-nav { z-index: 121; }

  /* Language options rendered ~30×21px — widen the hit area to ≥44px tall
     without moving the pill (an invisible ::after around each option). */
  .site-nav__lang-opt { position: relative; }
  .site-nav__lang-opt::after {
    content: "";
    position: absolute;
    inset-block: -12px;
    inset-inline: -3px;
  }

  /* Leaflet's +/− rendered 30×30px. */
  .leaflet-touch .leaflet-bar a {
    inline-size: 44px;
    block-size: 44px;
    line-height: 44px;
  }

  /* The culinary "keep scrolling" cue sat under the fixed bottom bar
     (52px + safe area, mobile/css/cta-bar.css). */
  #culinary .ix-hint--scroll {
    inset-block-end: calc(52px + env(safe-area-inset-bottom, 0px) + 16px);
  }
}

/* =============================================================================
   mobile/css/about.css — #about stats on phones (2026-10-11, Moshe: "one row").
   css/sections/about.css stacks the three stats in one column below 640px,
   which took a full screen of scroll. Here they sit in one row of three; the
   number scales with the width so "1,000+" fits at 320px. @media-only (§13).
   ========================================================================== */

@media (max-width: 768px) {
  .about__stats {
    grid-template-columns: repeat(3, minmax(0, 1fr));
    gap: var(--space-3);
  }
  /* Labels wrap to 1–2 lines; a 2-line floor keeps the numbers on one baseline. */
  .about__stat dt {
    min-block-size: 2.6em;
    display: flex;
    align-items: flex-end;
    justify-content: center;
    line-height: 1.3;
  }
  .about__stat dd {
    font-size: clamp(1.5rem, 7.5vw, 2.25rem);
  }
}

/* =============================================================================
   mobile/css/lang-menu.css — phone language picker (mobile/js/lang-menu.js).
   One 44px globe button; tap opens a small menu of native language names with
   the active one checked. Loaded on / (mobile/loader.js) and /press/.
   @media-only (§13). Tokens only (§10.2).
   ========================================================================== */

@media (max-width: 768px) {
  /* The segmented-pill chrome of .site-nav__lang (css/components/lang-toggle.css)
     → a bare anchor for the trigger + panel. */
  .site-nav__lang.lang-menu {
    position: relative;
    display: inline-flex;
    padding: 0;
    gap: 0;
    border: 0;
    background: none;
  }

  .lang-menu__trigger {
    display: grid;
    place-items: center;
    inline-size: 44px;
    block-size: 44px;
    padding: 0;
    border-radius: 50%;
    border: 1px solid color-mix(in srgb, var(--brass) 55%, transparent);
    background: color-mix(in srgb, var(--ivory) 92%, transparent);
    color: var(--brass-deep);
    cursor: pointer;
    transition: background-color var(--dur-fast, 160ms) var(--ease-out-cinema, ease),
                border-color var(--dur-fast, 160ms) var(--ease-out-cinema, ease);
  }
  .lang-menu.is-open .lang-menu__trigger {
    border-color: var(--brass);
    background: var(--ivory);
  }
  .lang-menu__trigger:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
  }

  .lang-menu__panel {
    position: absolute;
    inset-block-start: calc(100% + 8px);
    inset-inline-end: 0;
    z-index: 5;
    min-inline-size: 9.5rem;
    display: flex;
    flex-direction: column;
    padding: 6px;
    border-radius: 14px;
    border: 1px solid color-mix(in srgb, var(--brass) 35%, transparent);
    background: var(--ivory);
    box-shadow: 0 12px 32px color-mix(in srgb, var(--ink-deep) 18%, transparent);
    /* closed state: out of the way, no hit area */
    opacity: 0;
    visibility: hidden;
    transform: translateY(-4px);
    transition: opacity 160ms var(--ease-out-cinema, ease),
                transform 160ms var(--ease-out-cinema, ease),
                visibility 0s linear 160ms;
  }
  .lang-menu.is-open .lang-menu__panel {
    opacity: 1;
    visibility: visible;
    transform: none;
    transition-delay: 0s;
  }
  /* Hamburger menu overlay (Moshe, 2026-10-11: "at the top of the menu"): the
     globe's list item leaves the link list and sits in the top corner opposite
     the close ×, at the same height (lang-switch.css puts the × at --space-3).
     position:fixed resolves against the overlay (its backdrop-filter makes it
     the containing block — the same trick the × uses), so it stays put while
     the list scrolls. The menu opens downward from the corner. */
  .site-nav__mobile .site-nav__lang-item {
    position: fixed;
    inset-block-start: var(--space-3);
    inset-inline-start: var(--gutter);
    z-index: 2;
  }
  .site-nav__mobile .lang-menu__panel {
    inset-inline-start: 0;
    inset-inline-end: auto;
  }

  /* /press/ puts the picker at the inline-START corner — open toward the page. */
  [data-lang-toggle] .lang-menu__panel {
    inset-inline-end: auto;
    inset-inline-start: 0;
  }

  .lang-menu .lang-menu__opt {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    min-block-size: 44px;
    inline-size: 100%;
    padding: 0 0.9rem;
    border-radius: 10px;
    font-size: 0.95rem;
    font-weight: 500;
    letter-spacing: 0;
    text-align: start;
    color: var(--ink-deep);
    background: transparent;
  }
  .lang-menu .lang-menu__opt::after { content: none; } /* perf.css hit-area extender — rows are 44px already */
  .lang-menu .lang-menu__opt.is-active {
    color: var(--brass-deep);
    font-weight: 600;
    background: color-mix(in srgb, var(--brass) 14%, transparent);
  }
  .lang-menu .lang-menu__opt.is-active::before {
    content: "✓";
    order: 2;
    font-weight: 600;
  }
  .lang-menu .lang-menu__opt:focus-visible {
    outline: none;
    box-shadow: var(--focus-ring);
  }

  @media (prefers-reduced-motion: reduce) {
    .lang-menu__panel { transition: none; }
  }
}

/* =========================================================================
   mobile/css/a11y-widget.css — accessibility button on phones (2026-10-11).
   §13: media-query overrides only. Lifts the button above the fixed CTA bar
   (52px + safe area, mobile/css/cta-bar.css) and tightens the corner gap.
   ========================================================================= */

@media (max-width: 768px) {
  .a11y-widget {
    left: 12px;
    bottom: calc(52px + env(safe-area-inset-bottom) + 12px);
  }

  .a11y-widget__fab {
    inline-size: 48px;
    block-size: 48px;
  }

  .a11y-widget__panel {
    max-block-size: calc(100svh - 160px);
  }
}
