/* =============================================================================
   BASE — custom properties still in use, html/body defaults, smooth-scroll (Lenis) and reduced-motion guards.
   (Regrouped 2026-10-02 from the old 18 partials by scripts/rebuild/regroup_css.js.)
   ========================================================================== */
/* =============================================================================
   Ageshio redesign — component & layout stylesheet
   -----------------------------------------------------------------------------
   Architecture: Elementor owns STRUCTURE + CONTENT; this file owns LAYOUT.
   Containers carry semantic BEM classes (assigned by scripts/build_homepage.py);
   the rules below own their geometry precisely. All magic values are tokens in
   :root so there is a single source of truth. No !important — specificity comes
   from scoping under the component root (e.g. .ags-hero ...).

   Naming: block .ags-hero  ·  element .ags-hero__mid  ·  standalone .ags-textlink
   ========================================================================== */

:root {
  /* ---- Palette (measured EXACT from Figma) ---- */
  --ags-red:      #8E1E00;
  --ags-black:    #111010;
  --ags-cream:    #F7F4F2;
  --ags-white:    #FFFFFF;
  --ags-red-dark: #7A1900;

  /* ---- REFERENCE FRAME (ultrawide pass, 2026-09-08) ---------------------------------
     THE PROBLEM. Above ~1440 every value that SIZES content froze (px caps on the cqw
     fonts, the root rem's 22px ceiling, min(14cqw,200px) on the GT photo) while section
     heights kept growing with the viewport. At 3440x1440 that left the 1440-sized
     composition sitting in a 1440-tall section: VIP's copy marooned over a ~275px void,
     the GT small photo tiny beside a 547px-tall neighbour, Values' middle sparse. It is
     ONE cause, so it gets ONE fix — patching gap-by-gap did not converge (3 attempts).

     THE MODEL (Simon's call: "keep our layout similar to how it is on 1440, we are happy
     to have more space on the sides but keep the sections filling the viewport"). Treat
     1440x766 as the design frame — itself derived from Mika's capture, whose hero is
     exactly one viewport at 3600:1916 — and make ultrawide a literal ENLARGEMENT of it.
     Every gap, photo and font then holds its 1440 proportion, sections still fill, and
     the pad stays consistent by construction.

     TWO scales, deliberately separate, because welding them is what broke earlier passes:

       --ags-fsc    FRAME scale. How wide the layout is. Tracks --ags-frame, which is
                    100vw until the viewport is BOTH wide and tall. The max(1920px, ...)
                    floor is what keeps every <=1920 viewport byte-identical to before:
                    the frame never gets NARROWER than it used to be, so no viewport
                    suddenly grows side margins it didn't have.

       --ags-scale  CONTENT scale. How big the content is. A contain-fit of 1440x766, so
                    it is bounded by viewport HEIGHT as well as frame width and the
                    composition can therefore never overflow a short viewport. This is
                    the one that must never be floored: flooring it would let content
                    grow past the frame on a 1024-wide screen.

       --ags-tsc    scale / fsc — how much the content is shrunk RELATIVE to its frame.
                    1 whenever width is the binding constraint. cqw tracks the FRAME, so
                    any cqw-sized font must multiply by this to track CONTENT instead;
                    without it a wide-but-short viewport (1920x900, 2560x1080) oversizes
                    the type and collides. It reduces to the OLD --gt-tsc exactly at the
                    heights that were already verified (0.914 at 1440x700, 1 at >=766
                    when width-bound), which is why the laptop range does not move.

       --ags-px     ONE REFERENCE PIXEL: 1px at 1440x766, growing with --ags-scale. Every
                    frozen px literal below becomes calc(N * var(--ags-px)) where N is
                    that value's measured 1440 size — so the number in the CSS still
                    reads as the design's own px value.

     Worked examples (frame / content scale / side space per edge):
       1280x700   1280 / 0.889 / 0        1440x766   1440 / 1.000 / 0
       1440x700   1440 / 0.914 / 0        1920x1080  1920 / 1.333 / 0
       2560x1080  2030 / 1.410 / 265      2560x1440  2560 / 1.778 / 0
       3440x900   1920 / 1.175 / 760      3440x1440  2706 / 1.880 / 367
     Type at 3440x1440 is ~1.88x its 1440 size — the deliberate freeze in docs/TYPE-SPEC.md
     is what this pass replaces, and Simon accepted that trade-off explicitly. --- */
  --ags-frame:   min(100vw, max(1920px, calc(1.8799 * 100lvh)));
  --ags-fsc:     tan(atan2(var(--ags-frame), 1440px));
  --ags-scale:   min(var(--ags-fsc), tan(atan2(100lvh, 766px)));
  --ags-px:      calc(var(--ags-scale) * 1px);
  /* Frame letterbox + the content inset that lands on the frame GUIDE at any width. --ags-well is 0
     below ~1920 (frame == 100vw) and opens as side margin past it, so --ags-edge-guide == --ags-edge
     at/below the reference and caps content to the frame guide on ultrawide (keeps the layout
     faithful with side space — Simon 2026-09-21). Use for HORIZONTAL insets, NOT gaps/widths. */
  /* DAMPENED HEADING SCALE (Simon 2026-09-21): display titles scaled 1.88x at 3440 read too big.
     --ags-hsc tracks --ags-scale up to ~1920 (scale 1.333) then grows at only 35% of the rate, so
     ≤1920 is UNCHANGED and ultrawide titles are ~1.52x instead of 1.88x. --ags-hpx is the matching
     "reference pixel" for TITLE font-sizes (swap --ags-px -> --ags-hpx on display headings only). */
  --ags-hsc:     calc(min(var(--ags-scale), var(--ags-k-damp-at, 1.333)) + max(0, var(--ags-scale) - var(--ags-k-damp-at, 1.333)) * var(--ags-k-damp-rate, 0.35));   /* knobs: Layout & Scale → growth damping */
  --ags-hpx:     calc(var(--ags-hsc) * 1px);

  /* ---- CTA PILL — ONE definition, consumed by the homepage §10 and every interior §10 ----
     Simon 2026-09-09: "take the same consistent button as the home page". They had drifted badly —
     interior measured 233x53 against the homepage's 300x32 (font 15.1 vs 14.63, vertical padding
     15.9 vs 6, and no min-width at all). Tokens rather than two copies of the numbers, so the two
     consumers cannot drift apart again; the ONLY place to change the CTA pill is here.
     The font is the Kit button global's own 0.8125rem — the interior had been overriding it. */   /* CTA-band pill sides (thickness = the button knob) */
  /* Heading + the heading→pill gap, added 2026-09-09 when Simon asked to match the heading too. The
     interior had 51.8px/1.12 against the homepage's 33.75px/1.0 and no gap rule at all (22px vs 46).
     Same principle: one definition, two consumers. Mobile is EXCLUDED by design — both CTAs have
     their own treatment inside the ≤1023 block (the homepage's is a text link, not a pill). */
  --ags-cta-h-fs: 1.875rem;
  /* The heading→pill distance is TWO things stacked: the flex container's own row-gap PLUS the button
     widget's margin-top. Only the second was shared, so the total still differed (46 vs 41) — the
     interior inner sat on Elementor's default 22px against the homepage's 28. Both live here now. */
  /* The BAND itself (Simon 2026-09-09: "match the padding too"). These differed in BEHAVIOUR, not
     just value: the homepage carried a flat 90px from Elementor's per-element setting (so it never
     scaled) and got its height from min-height:48svh, while the interior used a scaled 158.4 and had
     no min-height at all — so its height was padding+content. Matching the padding alone would still
     have left the bands 313 vs 368 tall, so the min-height is shared too and both bands are now
     identical. 90 is the homepage's own value at the 1440 reference, now scaling like everything
     else — which also un-freezes it on ultrawide (it was stuck at 90 there). */
  --ags-cta-band-minh: 48svh;

  /* ---- Radii ---- (catalog: docs/DESIGN-KIT.md) */
  --ags-radius-pill: 999px; /* every pill: buttons, tags, chips, dots, back-to-top */
  --ags-radius-md:   10px;

  /* ---- Component constants ---- flat px on purpose: a button's thickness is a stroke-like
     constant, not a proportion of the frame (horizontal padding stays per-variant). */  /* standard CTA / pill vertical padding */
  --ags-btn-pad-y-thin: 3px;
  /* What every button/pill actually consumes: flat 6px on mobile; on desktop 6 reference px on the
     DAMPED scale (--ags-hpx: full growth to 1.333x, then 35%) so CTAs stay thin on big screens
     instead of growing linearly (Simon 2026-09-25: "some screens may get increasingly large"). */
  --ags-btn-pad-y-fluid: calc(6 * var(--ags-u));
  /* THE KNOB UNIT. Site Settings → Ageshio Components size knobs emit calc(N * var(--ags-u)), so a
     value typed in design px scales like the rest of the site: flat px on mobile, the DAMPED
     reference-frame px on desktop. Knob-driven values are set on body (the kit wrapper) — never
     build a :root token on top of one (a :root calc can't see body-level values). */
  --ags-u: 1px;
}
@media (min-width: 1024px) {
  :root { --ags-u: var(--ags-hpx); }
}
/* LAYOUT & SCALE KNOBS (Site Settings → Ageshio Layout & Scale) write --ags-k-* on body (the kit wrapper).
   A custom property resolves where it is DECLARED, so the tokens built from those inputs are re-declared
   on body too (same formulas as :root above) — otherwise :root would keep the default. Unset knobs →
   the fallbacks = the shipped numbers → identical. */
body {
  --ags-hsc:        calc(min(var(--ags-scale), var(--ags-k-damp-at, 1.333)) + max(0, var(--ags-scale) - var(--ags-k-damp-at, 1.333)) * var(--ags-k-damp-rate, 0.35));
  --ags-hpx:        calc(var(--ags-hsc) * 1px);
  --ags-u:          1px;
  --ags-btn-pad-y-fluid: calc(6 * var(--ags-u));
}
/* --ags-sec-pad on its own `:root body` rule: postcss.config.js leaves svh in `:root` custom-property
   definitions NATIVE (anything else becomes the JS-frozen --vh), and this keeps it native like the :root one.
   Only this token lives here — `:root body` out-ranks the kit wrapper, so knob-SET tokens stay on `body`. */
@media (min-width: 1024px) {
  body { --ags-u: var(--ags-hpx); }
}

/* ── BREAKPOINT SWITCH + MOBILE DESIGN PIXEL (components-as-widgets, 2026-09-27) ────────────────────────
   Design-pixel fields (containers + Ageshio widgets) take a Desktop value in 1440-design px and a Mobile
   value in 390-design px. Containers can't carry theme CSS, so their fields write ONE value that switches
   with these multipliers instead of a media query: calc(var(--ags-d) * D * var(--ags-px) + var(--ags-m) * M * var(--ags-mpx)).
   The switch is the site's real breakpoint (desktop >=1024 / mobile <=1023), NOT Elementor's tablet breakpoint.
   --ags-mpx = 1px at >=390 wide (flat above, like the live mobile knobs e.g. min(4.6vw,18px)), shrinking
   proportionally on phones narrower than 390. */
body { --ags-d: 1; --ags-m: 0; }
@media (max-width: 1023px) { body { --ags-d: 0; --ags-m: 1; } }
/* =============================================================================
   OLD-SAFARI / iOS-12 FALLBACK for the scale engine + viewport-unit tokens (Simon 2026-09-24).
   Safari <15.4 (e.g. the 2013 iPad Air on iOS 12) lacks CSS trig (tan/atan2 — which powers
   --ags-fsc/--ags-scale) AND svh/lvh, so the raw definitions above are INVALID there → --ags-frame,
   --ags-fsc, --ags-scale and the whole --ags-px cascade collapse → "layout ruined". When trig is
   unsupported we route those roots to JS-computed equivalents that use NO trig (functions.php
   `ageshio_stable_vh` publishes --ags-frame-fb / --ags-fsc-fb / --ags-scale-fb from innerWidth/
   innerHeight, refreshed on width/orientation) and swap the svh/lvh tokens to vh. tan(atan2(a,b)) == a/b,
   so the JS scale is mathematically IDENTICAL to the CSS one. MODERN browsers support trig → they SKIP
   this whole block = zero change. Every derived token (--ags-tsc/px/edge/well/hsc/hpx/edge-guide)
   cascades from these roots, so no others need overriding here.
   ============================================================================= */
@supports not (width: tan(1deg)) {
  :root {
    --ags-frame: var(--ags-frame-fb, 100vw);
    --ags-fsc:   var(--ags-fsc-fb, 1);
    --ags-scale: var(--ags-scale-fb, 1);
    --ags-cta-band-minh: 48vh;
  }
}
/* =============================================================================
   SMOOTH SCROLL (Lenis) — the recommended helper CSS.
   -----------------------------------------------------------------------------
   Lenis adds `.lenis` to <html> when it's driving the scroll (see
   js/ageshio-smooth-scroll.js, enqueued front-page-only). These rules let the page
   size correctly under JS-driven scrolling and stop native CSS smooth-scroll from
   fighting Lenis on anchor jumps. Absent Lenis (reduced-motion / other pages / no-JS),
   `.lenis` is never added, so none of this applies and native scroll is untouched. */
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-smooth [data-lenis-prevent] { overscroll-behavior: contain; }
.lenis.lenis-stopped { overflow: hidden; }
/* SAFETY NET — never let a CONTAINER stay stuck hidden. Elementor's entrance-animation
   JS reliably reveals WIDGETS but is flaky on inner flex containers (it adds
   `.elementor-invisible` at render but doesn't always remove it), which once blanked the
   blue Values columns + Masters cards. So we only ever tag WIDGETS for entrance anim
   (see anim_stagger in build_homepage.py); this rule is belt-and-suspenders: any .e-con
   that still carries .elementor-invisible is forced visible so content can't vanish. */
/* OLD SAFARI (<15, e.g. the iOS-12 iPad): Elementor's scroll-triggered entrance animations don't
   reliably remove `.elementor-invisible` from WIDGETS there, so animated headings (the Mission
   statement, etc.) stayed hidden = "blank red section". Force ALL invisible content visible with no
   motion — content over animation. Gated on aspect-ratio (Safari 15+) so MODERN browsers skip this and
   keep their fade-ins. (Mirrors the reduced-motion fallback below.) */
@supports not (aspect-ratio: 1 / 1) {
  .elementor-invisible {
    visibility: visible !important;
    opacity: 1 !important;
    animation: none !important;
    transform: none !important;
  }
}

@media (prefers-reduced-motion: reduce) {
  .elementor-invisible {
    visibility: visible !important;
    opacity: 1 !important;
    animation: none !important;
  }
  .elementor-element {
    transition: none !important;
  }
}

/* =============================================================================
   MOBILE NAV (≤1023) — custom .ags-mnav overlay REPLACES Elementor's default
   mobile UI. Elementor renders one Nav Menu widget (native "Primary Nav" menu);
   ageshio-nav.js reads that hidden <ul> and builds .ags-mnav. So here we HIDE
   Elementor's mobile toggle + dropdown AND the desktop pill/crest — .ags-mnav is
   the only visible nav ≤1023. The menu <ul> stays in the DOM (display:none is
   still queryable) as the data source. Desktop ≥1024 is untouched.
   Sizing = one-knob scale-as-one-unit (memory: wp-redesign-mobile-scaling-pattern):
   ONE capped-vw knob on .ags-mnav, every child dimension = calc(N * var(--knob)).
   Calibrated ONCE at the 390px reference; never per-width tweaked. --- */
@media (max-width: 1023px) {

  /* MOBILE SAFETY — never allow the page to scroll horizontally. Translated interior pages occasionally
     picked up a stray horizontal scrollbar (an element ended up slightly wider than the viewport in some
     languages, shifting the whole page + crest). `overflow-x: clip` removes it WITHOUT creating a scroll
     container — so the fixed nav, the overlay's scroll-lock, and normal vertical scrolling all keep
     working (unlike `overflow: hidden`, and `clip` leaves overflow-y: visible untouched). Must be on
     BOTH html AND body — in Chromium a single one doesn't clip the viewport's horizontal scroll. */
  html, body { overflow-x: hidden; overflow-x: clip; }
}
/* =============================================================================
   FOOTER (§11) — Theme Builder footer (build_footer_v2.py). The contact + social
   text widgets carried the theme's default `<p> { margin: ~16px 0 20px }`. The old rule
   reset only margin-BOTTOM, leaving a ~16px margin-TOP on every line → label sat far above
   its value (Phone ⇕ 098-…, Email ⇕ address) and the socials were loosely spaced, vs the
   reference where each label hugs its value + only a small gap divides the Phone/Email
   groups. Reset BOTH top+bottom to 0 so label/value pairs + socials are COMPACT (both
   desktop AND mobile — this base rule isn't media-gated). --- */
/* =============================================================================
   DESKTOP LAYOUT (≥1024) — wrapped ONCE.
   -----------------------------------------------------------------------------
   Everything below applies ONLY at desktop width. Below 1024 none of it exists, so the
   MOBILE region (further down) builds on a blank canvas — no !important, no specificity
   fights. Section background COLOURS live in Elementor's post-<id>.css (band() generator,
   no media query) so mobile keeps its colours even though these layout blocks are gated.
   ========================================================================== */

/* Remove the wpfront-scroll-top plugin's floating karateka "Page Top" back-to-top button site-wide —
   it's not part of the redesign (per feedback). The plugin sets display:block INLINE via JS, so an ID
   selector + !important is the only way an external stylesheet can hide it. Global (all theme pages,
   both breakpoints); was previously mobile-only. */

@media (min-width: 1024px) {   /* label↔video gap is the DB flex_gap */
/* Play button: hidden until the video is hovered / keyboard-focused (Simon 2026-09-23). */
/* V4 rebuild: native (atomic) sections lay themselves out from their own Classes — skipped here (same specificity). */  /* 730 */

/*@@P 40-home-sections-a.css@@*/
/* =============================================================================
   HERO (section 1)
   ========================================================================== */
/* The ORIGINAL 4-line keyframe (live, unchanged): visible window (fade-in start → fade-out end)
   is 2%→26% of the 12s cycle. That's sized for a 25%-per-line slot (4 lines): visible/slot =
   24/25 ≈ 1.04, i.e. a deliberate ~1% cross-fade cushion — in seconds, a ~0.12s GAP where both
   neighbouring lines are momentarily at opacity 0 (2.64s→2.76s of real time between lines, at the
   default 3s/line pace). For any OTHER line count N, ageshio-values.js generates (once, idempotent,
   cached by N — see its `ensureKeyframes`) a SIBLING keyframe `agsRotate-n<N>` by scaling every
   percentage below by k = 4/N (0, 2k, 6k, 22k, 26k, 100) — i.e. the SAME shape, just resized so the
   visible window is 1/N of ITS cycle instead of 1/4. Because duration also scales to N × the
   per-line seconds, the ABSOLUTE gap stays the SAME ~0.12s (at the default pace) for any N — it's
   a fixed fraction of duration (k%) × a duration that grows by 1/k, so the seconds cancel out. n=4
   ALWAYS keeps using this original rule (never `agsRotate-n4`) — the default case is byte-for-byte
   identical to live, keyframe included. */
@keyframes agsRotate {
  0%, 2%    { opacity: 0; transform: translateY(8px); }
  6%, 22%   { opacity: 1; transform: translateY(0); }
  26%, 100% { opacity: 0; transform: translateY(-8px); }
}

}
/* ---- FILM section — MOBILE/TABLET (≤1023, Simon 2026-09-24). IMPORTANT: the ENTIRE desktop film
   composition lives in the min-width:1024 block above, so mobile inherits NONE of it — this block must
   re-declare the section's look (dark bg + cream text), the click-to-play VIDEO FACADE structure, and a
   fresh single-column layout sized off ONE capped-vw knob (scales 390→tablet). Layout: full-width
   stacked paragraphs, left statement, full-width video, kanji ABOVE-LEFT (遊ぶ/Play) + BELOW-RIGHT
   (動画/Video), wrapping title + crest. Bands capped + centred so tablet is a column, not edge-to-edge. */
/* FILM — TABLET (768–1023): here Elementor keeps the intro + stage as ROWS (video flanked by the kanji,
   like a scaled desktop). Drop the phone auto-margins and CENTRE the [遊ぶ · video · 動画] group tight
   around the video (not spread to the stage edges), cap the video so the flank stays balanced, and give
   the intro columns a floor width so the paragraphs + statement don't wrap into skinny ribbons. */
/* BOTH wrapper levels, matching the Marquee (60-interior-components.css ~1038) and Card Carousel
   (~1410 below) widgets' own established two-level pattern: Elementor always wraps a widget's
   render() output in `.elementor-widget-{type} > .elementor-widget-container`, TWO real boxes, not
   one — `display:contents` on only the outer one (found missing here by this task's own strict
   pixel-proof pass, baselines/phase2b/t17c/strict/) still leaves the inner `.elementor-widget-
   container` as a real DIV between `.ags-exp` and its actual parent, which breaks the homepage
   sticky-deck's CHILD-COMBINATOR selector `.ags-stack-wrap > .ags-exp` (30-home-layout-rhythm.css)
   that gives this section its full-viewport height — measured: the migrated homepage §7 card
   shrank from 100lvh to its natural (much shorter) content height. Harmless for the INTERIOR
   variant (no such child-combinator selector targets it). */
/* background-color: the ORIGINAL `.ags-exp{background-color:...}` rule (~line 411, untouched,
   predates this task) is ITSELF one of the rules silently trapped desktop-only by the structural
   quirk described above — confirmed live (a bare, brand-new `<div class="ags-exp">` appended
   straight to `<body>` shows the cream background at 1024px+ and transparent below it, with
   NOTHING else on the page involved). Restated here, safely outside that trap, so the widget's
   background paints at every width exactly as the Global Colour "Accent" always resolved to. */

/* `display:flex`/`align-items:center` are safe unconditionally (nothing else sets them for the
   home root at any width — mobile's own `.ags-programs__slide` layer repositions CHILDREN via
   `position:absolute`, never touching the root's own display/align-items). `padding` is NOT
   unconditional — tried first, and it beat the ≤767 "padding_mobile" override below (same
   specificity tie resolved the wrong way once chained to 4 classes); scoped to the SAME desktop
   floor as that override's own ceiling instead. */
/* =============================================================================
   MOBILE (≤1023px) — SINGLE-COLUMN LAYOUT (self-contained)
   =============================================================================
   The desktop LAYOUT is gated to ≥1024 (see the DESKTOP LAYOUT region above), so below 1024
   there is NO sticky deck, NO container-query stage, NO absolute positioning to fight — the
   DOM simply stacks in source order. This block builds the mobile design on that blank canvas:
   plain, low-specificity rules, no !important, no specificity tricks.

   Section background COLOURS come from Elementor's post-<id>.css (band() generator, no media
   query), so they persist here automatically. We only lay out geometry.

   RHYTHM: every section up to & including Group Tour = ONE screen (100svh, content centred),
   so the next section arrives only on scroll. Authentic Lineage (masters) onward = natural
   long scroll (Simon's spec). `svh` (small viewport) is used everywhere so a section is never
   taller than the guaranteed-visible height → no next-section "bleed" under the mobile URL bar.

   DOM recap (source order): hero · [stack-wrap: mission·gallery·values·grouptour·vip·exp] ·
   about · masters · cta · footer(template).
   ========================================================================== */
@media (max-width: 1023px) {

  /* ---- Scroll stability (Simon 2026-09-24): Firefox for Android reveals its toolbar by RESIZING the
     layout viewport on scroll-UP, and the browser's scroll-anchoring then re-picks an anchor mid-gesture
     → the view "jumps a bit" as the search bar appears (the sections themselves no longer resize — that's
     the frozen --vh). Turn scroll-anchoring OFF on the phone so the scroll offset stays put while the
     toolbar animates. Safe here: full-height sections are frozen (--vh) and images sit in fixed-ratio
     boxes, so nothing above the fold reflows to need anchoring. NOTE: some toolbar-reveal motion is
     inherent to Firefox's dynamic toolbar (it resizes the layout viewport) and can't be fully removed
     page-side. ---- */
  html, body { overflow-anchor: none; }

  /* ---- 0. Frame: release the desktop min-width lock; no horizontal scroll ---------------- */
  body.ags-redesign.elementor-page { min-width: 0; }
  body.ags-redesign,
  body.ags-redesign [data-elementor-type="wp-page"] {
    max-width: 100vw;
    /* overflow-x:CLIP, not hidden. `hidden` on one axis forces the other axis to `auto` (CSS spec),
       which turns body + wp-page into SCROLL CONTAINERS — and position:sticky sticks to its nearest
       scroll container, so the mobile deck (below) would stick inside these boxes instead of the
       viewport and the slide-over would break. `clip` prevents horizontal scroll WITHOUT creating a
       scroll container (overflow-y stays visible), so the sticky deck pins to the viewport correctly. */
    overflow-x: hidden;
    overflow-x: clip;
  }
}
/* =============================================================================
   §§ INTERIOR COMPONENTS — MOBILE REFLOW (≤1023). Same DOM, single-column layout
   per Okinawan Group Tours Mobile.png. Three-region model (memory: mobile-build):
   the desktop rules above gate at ≥1024, this block owns ≤1023. Desktop UNCHANGED.

   SPECIFICITY: globally-typed headings render their SIZE on .elementor-heading-title
   via Elementor's per-element Kit global (0,4,0) — and the marquee text has an UNGATED
   scoped size just above. A plain wrapper class (.ags-c-…) loses to both, so every mobile
   heading size here is SCOPED to the rendered element + page type (0,4,0) and sits AFTER
   those rules → wins on source order. Text-editor globals sit on the widget → target
   .elementor-widget-container. (Same convention as the desktop region header. NOT
   body.ags-redesign — that class is FRONT-PAGE-only, absent on interior pages.)
   ============================================================================= */
@media (max-width: 1023px) {
  /* History's .ags-masters__row is e-con-FULL (no .e-con-inner) unlike the homepage's boxed row, so the
     ROW ITSELF is the card frame here (the JS uses `.e-con-inner || row` → lands on the row too). Centre
     it at the card width; it becomes the fixed-height rounded viewport in carousel mode below. */
  /* Carousel mode (JS present): the ROW becomes the fixed-height rounded viewport; cards stack absolute + slide. */
  /* `.ags-master` excluded from the wildcard (Task 4, 2026-09-24) — its transform+transition is now
     the ONE shared page-agnostic rule (components.css ~5300, near the homepage's mobile masters
     block); see that rule's comment for why. */
  /* §6 LOCATIONS → the Authentic-Lineage stack (Simon 2026-09-24). The TITLE + DESCRIPTION live together
     in the titleband section; the card carousel + 場所 kanji live in the SEPARATE .ags-masters section
     that follows. To get the reference order (title → card → 場所 → description) the card must slot
     BETWEEN the title and the description — so flatten the titleband + its inner wrappers with
     display:contents (promoting the title __h and description __caption to direct flex items of the page
     wrapper), make the wrapper a flex column, then flex-order the whole run. (Safe: the full-width
     sections have no vertical margins to collapse, so flex-column stacks them exactly like block flow.)

     GENERALISED (Phase 2b Task 4, G4b): every rule below whose selector is `.ags-c-locations*` (or
     `.ags-c-titleband.ags-c-locations-title`) had its `body.ags-page-karate-history-tour` scope
     dropped (or, where the rule ALSO needs to out-specificity-tie an Elementor per-element global —
     i.e. it targets `.elementor-heading-title`/`.elementor-widget-container` — swapped for the same
     generic `:where(.elementor-page [data-elementor-type="wp-page"])` ancestor every other ags-c-*
     component in this codebase uses). These are the Locations Filmstrip component's OWN structural/
     colour rules, not content tuned to this page, so any page built from the "Experience Pages"
     template (21407) — which embeds this component — now renders it correctly.
     KEPT SCOPED to body.ags-page-karate-history-tour, by contrast (not `.ags-c-locations*`-named, so
     out of this generalisation): the whole-page `display:flex` flatten trick right below, the body
     background-colour + `--m-card-w`/`--m-card-h`/`--m-rank` card-size knobs, the `.ags-masters`
     order, and the `.ags-c-closing`/`.ags-c-footer` order/kanji rules — these are the
     PAGE's own composition decision (interleave Locations between two other unrelated sections in a
     specific 6-slot running order) rather than anything the Locations component itself needs; a new
     page gets the component in normal DOM order instead (title → description, carousel in its own
     section) — a reasonable default, not a broken one. Desktop mirror of this same call at
     60-interior-components.css:230 and the block starting ~1930 below. History Tour, About and home
     verified cstyle-identical after this change.
     UPDATE (2026-09-27, Task 9): the `.ags-c-cta { order: 5; }` rule that used to be part of this same
     list was deleted along with the rest of the CTA-band CSS (the class it matched is gone). The band's
     slot 5 in this flex-column run is now the container's own native Advanced -> Layout -> Order field
     (`_flex_order`/`_flex_order_custom`), set once by migrate_cta_band_to_widgets.php — not theme CSS. */
  body.ags-page-karate-history-tour .elementor[data-elementor-type="wp-page"] { display: flex; flex-direction: column; }
  /* Cream base (accent global) so the FLATTENED title + description show the same colour as the section
     bands around them — flattening the titleband dropped its own cream band, which left them on the white
     body → the "different background" seam. Also hoist the card knob to the body so the title can align to
     the card. */
  body.ags-page-karate-history-tour {
    background-color: var(--e-global-color-accent, var(--ags-cream));
    /* reduced ~15% (0.1→0.085, mobile kanji-size pass 2026-09-24) — the caption clearance
       (calc(var(--m-rank) + 14px) etc., below) derives from it so it shrinks coherently with the glyph. */
  }

  /* §9 MASTERS FILMSTRIP — static CSS-only port (no masters JS on interior pages),
     same treatment as Karate History Tour's own static override (~6257, ported
     verbatim to this page's scope): a uniform full-width portrait card stack,
     fixed height (not aspect-ratio) so the tall source photo can't balloon it. */

  /* 1 — MARQUEE overlays the hero photo bottom on mobile (Simon 2026-09-24 r2) — like the desktop /about.
     The earlier negative-margin GUESS left gaps because it didn't match the band's real height. FIX: give
     the band an EXPLICIT height and set margin-top = exactly -that height → it overlaps precisely its own
     height onto the photohero with NO gap, stable through the scrolling text. Transparent bg so the cream
     text + crest read over the photo; z-index:3 above the hero scrim. Crest keeps nav-icon-red.svg. */
  /* --m-rank reduced ~15% (0.1→0.085, mobile kanji-size pass 2026-09-24) — the caption clearance
     (calc(var(--m-rank) + 14px) etc., below) derives from it so it shrinks coherently with the glyph. */
  /* `.ags-master` excluded from the wildcard (Task 4, 2026-09-24) — its transform+transition is now
     the ONE shared page-agnostic rule (components.css ~5300, near the homepage's mobile masters
     block); see that rule's comment for why. */

  /* 6 — GRAND MASTERS reorder: image carousel → kanji → button (Simon 2026-09-24). The kanji legend +
     button live in the PRECEDING .ags-c-masters-intro section (nth-child 8); the photos in .ags-masters
     (nth-child 9). Flex the page root + display:contents the masters-intro wrappers to promote its pieces,
     then give EVERY top-level section an explicit :nth-child order so only masters moves (the two maroon
     statements stay put). (Approach's intro→approach reorder is JS; its hidden intro is harmless here.) */
  body.ags-page-about .elementor[data-elementor-type="wp-page"] { display: flex; flex-direction: column; }
}
/* =============================================================================
   HEADER + NAV — the Theme Builder header (desktop pill nav, dropdown windows) and the mobile menu that
   js/ageshio-nav.js builds (.ags-mnav*). Hooks: [data-ags-nav="bar|logo|crest|pill"] set in Elementor → Attributes.
   (Regrouped 2026-10-02 from the old 18 partials by scripts/rebuild/regroup_css.js.)
   ========================================================================== */
/* =============================================================================
   IMAGE WIPE (§8 About) — the ONE effect Elementor Pro can't do natively, so it
   gets a tiny scoped bit of custom CSS + a dedicated observer in the theme footer
   (see functions.php ageshio_wipe_script). A colored panel covers the media panel,
   then sweeps off to the right when the section scrolls in — the single dramatic
   brand moment (spec M3). Panel is red (brand); scaleX 1→0 from the right edge.
   Reduced-motion / no-JS safe: the panel starts hidden and only APPEARS once JS adds
   `.ags-wipe--armed` (so if JS is off, the image is just visible, never covered). --- */
/* item 3 (Simon 2026-09-22): REMOVE the homepage About slide-reveal — the red wipe panel never
   covers, so the photo simply shows on scroll-in. Scoped to the About media (the only .ags-wipe
   on the site); the ::after panel is what animated, so hiding it removes the reveal entirely. */
/* JS armed the wipe (and we're not reduced-motion): panel covers the image, ready. */
/* Triggered on scroll-in: sweep the panel off to the right. */

/* =============================================================================
   RESPONSIVE SCOPE — desktop ≥1024 (locked) · mobile ≤1023 (real single-column layout)
   -----------------------------------------------------------------------------
   The fluid clamp()/vw/grid layout is tuned to hold from ~1024px up through ultrawide
   (3440), so it stays LOCKED to min-width:1024 — the boundary it's designed to hold to.
   The instant the viewport drops BELOW that (i.e. the desktop layout can no longer fit),
   the mobile layout takes over: a real single-column reflow covering the ENTIRE <1024 range
   (phones AND small tablets). See the MOBILE block far below +
   docs/superpowers/specs/2026-07-31-mobile-homepage-design.md.
   The old 768–1023 "coming soon" placeholder gate is RETIRED — mobile now covers that band.
   The base min-width:1024 below still applies at ≥1024 (unchanged desktop); the mobile media
   block releases it. --- */

/* The branded placeholder gate (ageshio_mobile_gate() in functions.php) is now DORMANT — the
   real mobile layout covers the whole <1024 range, so the gate never shows. Kept hidden (and the
   PHP kept) as a harmless fallback; safe to delete the markup + this line together later. */

/* =============================================================================
   BUTTONS — single source of truth (Figma).
   -----------------------------------------------------------------------------
   btn() emits NO inline background/radius/padding in the widget JSON, so Elementor
   generates no competing per-element rule — these plain component classes win with
   NORMAL specificity (no compounding tricks). Variants: solid · invert · bar · link.
   Font is the accent global (Red Rose 700). --- */
/* Borderless underlined text link (READ OUR REVIEWS §3, MEET THE GRAND MASTERS §9). */
/* PILL — the shared red-outline-on-cream chip (.ags-c-pill). The existing pills are aliased at their
   ORIGINAL specificity (so the Kit typography rules still lose to them exactly as before); each
   component rule keeps only its own font-size / horizontal padding / hover. Thickness = the button
   standard (--ags-btn-pad-y-fluid). */
/* =============================================================================
   SITE NAVBAR — Theme Builder header (.elementor-location-header)
   -----------------------------------------------------------------------------
   FAITHFUL PORT of the old hero nav. build_header_v2.py emits the SAME markup + classes
   the hero nav used (.ags-hero__nav row · .ags-nav-logo · .ags-nav-group ×3 ·
   .ags-nav-spacer · .ags-nav-lang · .ags-nav-cta), so the original hero-nav rules below
   style it identically. Only difference from the hero: the gutter/top-offset that the
   hero STAGE used to supply is applied here on the header's own inner container (the nav
   is no longer wrapped by the hero stage). Position/hide-show is the block further down. */
/* Whole-navbar top spacing: push the entire bar (logo + links + CTA together) down off
   the top edge. Applied on the header's outer wrapper — the reliable hook (Elementor's
   default 10px here is replaced with a comfortable, uniform offset). */
.elementor-location-header > .e-con {
  padding-top: 20px;
  padding-bottom: max(8px, min(1vh, 14px));
  padding-bottom: clamp(8px, 1vh, 14px);
}

/* Nav link type is Kit-owned (nav_link: Red Rose Reg 14/-3%). Here: only layout — zero the
   <p> margin so link tops align (Events line == English+ line), and make the <a> inherit the
   widget's Kit type instead of the theme's default anchor styling. */
/* CAP the nav font growth. The Kit nav_link global is 0.875rem, so it scales with the "scales
   as one unit" root knob (14px@1280 → 19px@1920) — but the navbar is CHROME/UI, not editorial
   content, so it shouldn't balloon on wide screens. clamp() lets it track the rem a little then
   freezes at 15px. Scoped under the header (0-2-1) to beat the Kit global; the <a> font:inherit
   above picks it up from the <p>. Same cap on the BOOK WITH US pill. */
/* Same large lead before the English+ link, so the Events/Shop column separates from the
   right cluster (English+ · BOOK WITH US) the way the two left column-pairs do. */

/* English + : plain nav link, inline with the right cluster. */

/* --- ANIMATED MEGA-MENU (two states) -------------------------------------------------------
   DEFAULT (no hover): a floating rounded CREAM PILL holding only the links; the crest sits
   OUTSIDE it, on the page/hero background to the left (crest is NOT on cream).
   EXPANDED (hovering a dropdown parent, via :has()): the pill animates out to the PAGE EDGES,
   squaring into a full-width rectangle that now supports BOTH the crest and the links (crest
   becomes part of the cream), and the dropdown WINDOW drops below it as a connected cream panel.
   Reference: figma/frames/navbar-v2/navbar-dropdown-open.png.
   --------------------------------------------------------------------------------------------- */
.ags-nav2, [data-ags-nav~="bar"] {
  --ags-nav-crest: 112px;                     /* emblem size */
  --ags-nav-gutter: max(10px, min(1.4vw, 20px));
  --ags-nav-gutter: clamp(10px, 1.4vw, 20px); /* side gutter → pill reaches close to both edges */
  --ags-crest-gutter: max(-8px, min(0.1vw, 0px));
  --ags-crest-gutter: clamp(-8px, 0.1vw, 0px);/* crest shifted further LEFT (near/at the viewport edge) */
  --ags-nav-gap: max(16px, min(1.8vw, 30px));
  --ags-nav-gap: clamp(16px, 1.8vw, 30px);   /* gap crest→pill: a little room so the pill isn't crowded by the logo */
  --ags-nav-top: 20px;                        /* rest gap: viewport top → crest & pill top */
  --ags-nav-photo-w: 108px;                   /* dropdown photo size (square) */
  --ags-nav-photo-gap: 12px;                  /* gap between the two dropdown photos */
  --ags-nav-photos-w: calc(2 * var(--ags-nav-photo-w) + var(--ags-nav-photo-gap));  /* the 2-photo cluster width — the BOOK pill matches this so the photos line up directly under it (per feedback) */
  /* easeOutQuad — a GENTLE decelerate. NOT an extreme ease-out (e.g. .2,.8,.2,1): those fling ~80% of
     the distance out in the first ~20% of time then crawl the final bit for the rest, which read as a
     "hesitation before going all the way out". easeOutQuad keeps a more even velocity to the edge. */
  --ags-nav-anim: .42s cubic-bezier(.25, .46, .45, .94); /* the DROPDOWN reveal (window height). */
  --ags-nav-geo: .34s cubic-bezier(.25, .46, .45, .94);  /* PILL GEOMETRY, SHORT edges (top ~20px, right ~20px) + corners — easeOutQuad. */
  --ags-nav-geo-x: .34s cubic-bezier(.25, 1, .5, 1);     /* PILL GEOMETRY, LONG left edge (margin-left+padding-left travel ~138px, the crest zone, vs 20px for the others). SAME .34s duration as the short edges so ALL edges reach the corners at the EXACT SAME instant; a more front-loaded curve (easeOutQuart) covers its 7× distance so it's visually arrived at the same moment the short edges are — no lag, no early finish. (margin-left+padding-left share this var so links stay pinned.) */
  --ags-nav-lead: .1s;
  /* Expanded padding = the rest insets, added back so content (links) never moves as the pill
     fills to the edges. The window uses the NEGATIVES of these to span the full viewport. */
  --ags-pad-l-open: calc(var(--ags-crest-gutter) + var(--ags-nav-crest) + var(--ags-nav-gap));
  --ags-pad-r-open: var(--ags-nav-gutter);
}
:is(.ags-nav2, [data-ags-nav~="bar"]) {
  --ags-nav-crest: 112px;                     /* emblem size */
  --ags-nav-gutter: max(10px, min(1.4vw, 20px));
  --ags-nav-gutter: clamp(10px, 1.4vw, 20px); /* side gutter → pill reaches close to both edges */
  --ags-crest-gutter: max(-8px, min(0.1vw, 0px));
  --ags-crest-gutter: clamp(-8px, 0.1vw, 0px);/* crest shifted further LEFT (near/at the viewport edge) */
  --ags-nav-gap: max(16px, min(1.8vw, 30px));
  --ags-nav-gap: clamp(16px, 1.8vw, 30px);   /* gap crest→pill: a little room so the pill isn't crowded by the logo */
  --ags-nav-top: 20px;                        /* rest gap: viewport top → crest & pill top */
  --ags-nav-photo-w: 108px;                   /* dropdown photo size (square) */
  --ags-nav-photo-gap: 12px;                  /* gap between the two dropdown photos */
  --ags-nav-photos-w: calc(2 * var(--ags-nav-photo-w) + var(--ags-nav-photo-gap));  /* the 2-photo cluster width — the BOOK pill matches this so the photos line up directly under it (per feedback) */
  /* easeOutQuad — a GENTLE decelerate. NOT an extreme ease-out (e.g. .2,.8,.2,1): those fling ~80% of
     the distance out in the first ~20% of time then crawl the final bit for the rest, which read as a
     "hesitation before going all the way out". easeOutQuad keeps a more even velocity to the edge. */
  --ags-nav-anim: .42s cubic-bezier(.25, .46, .45, .94); /* the DROPDOWN reveal (window height). */
  --ags-nav-geo: .34s cubic-bezier(.25, .46, .45, .94);  /* PILL GEOMETRY, SHORT edges (top ~20px, right ~20px) + corners — easeOutQuad. */
  --ags-nav-geo-x: .34s cubic-bezier(.25, 1, .5, 1);     /* PILL GEOMETRY, LONG left edge (margin-left+padding-left travel ~138px, the crest zone, vs 20px for the others). SAME .34s duration as the short edges so ALL edges reach the corners at the EXACT SAME instant; a more front-loaded curve (easeOutQuart) covers its 7× distance so it's visually arrived at the same moment the short edges are — no lag, no early finish. (margin-left+padding-left share this var so links stay pinned.) */
  --ags-nav-lead: .1s;
  /* Expanded padding = the rest insets, added back so content (links) never moves as the pill
     fills to the edges. The window uses the NEGATIVES of these to span the full viewport. */
  --ags-pad-l-open: calc(var(--ags-crest-gutter) + var(--ags-nav-crest) + var(--ags-nav-gap));
  --ags-pad-r-open: var(--ags-nav-gutter);
}

/* Header row — holds the absolute crest overlay + the pill. position:relative anchors the crest;
   the pill is the single flow child that fills the width. NO cream on the row itself — the PILL is
   the one cream shape (it morphs into the full panel), so there's no crossfade/two-shape flash. */
.ags-nav2.e-con, .e-con[data-ags-nav~="bar"] {
  background-color: transparent;
  padding: 0;
  width: 100%;
  position: relative;
  flex-direction: row;
  align-items: stretch;
}
:is(.ags-nav2, [data-ags-nav~="bar"]).e-con {
  background-color: transparent;
  padding: 0;
  width: 100%;
  position: relative;
  flex-direction: row;
  align-items: stretch;
}

/* CREST — an ABSOLUTE OVERLAY (not in flow). This is REQUIRED for the Model-B morph: the pill
   must be able to fill edge-to-edge UNDER the crest on expand. The crest floats on top at the
   far left, its own fixed position, and never moves when the pill morphs. Decorative → no pointer
   events. Kill the Elementor image-widget inner inset so the crest box == the crest image. */
.ags-nav2__logo.e-con, .e-con[data-ags-nav~="logo"] {
  position: absolute;
  left: var(--ags-crest-gutter);
  top: -10px;                              /* lifted UP a little → more space between crest & the pill */
  z-index: 5;
  width: auto;
  align-items: flex-start;
  pointer-events: none;
}
:is(.ags-nav2__logo, [data-ags-nav~="logo"]).e-con {
  position: absolute;
  left: var(--ags-crest-gutter);
  top: -10px;                              /* lifted UP a little → more space between crest & the pill */
  z-index: 5;
  width: auto;
  align-items: flex-start;
  pointer-events: none;
}
/* The crest is a home link, but its parent :is(.ags-nav2__logo, [data-ags-nav~="logo"]) is pointer-events:none (it overlays the
   hero; the logo was decorative). Re-enable clicks on the crest itself so the <a href="/"> fires. */
.ags-nav2__crest, [data-ags-nav~="crest"] { width: var(--ags-nav-crest); flex: 0 0 auto; pointer-events: auto; cursor: pointer; }
:is(.ags-nav2__crest, [data-ags-nav~="crest"]) { width: var(--ags-nav-crest); flex: 0 0 auto; pointer-events: auto; cursor: pointer; }
.ags-nav2__crest .elementor-widget-container, [data-ags-nav~="crest"] .elementor-widget-container { width: var(--ags-nav-crest); padding: 0; }
:is(.ags-nav2__crest, [data-ags-nav~="crest"]) .elementor-widget-container { width: var(--ags-nav-crest); padding: 0; }
.ags-nav2__crest .elementor-widget-container > a, [data-ags-nav~="crest"] .elementor-widget-container > a { display: contents; }
:is(.ags-nav2__crest, [data-ags-nav~="crest"]) .elementor-widget-container > a { display: contents; }
/* Phase 2b Task 3 (item 3, Navbar override-respect): `!important` here was never beating a competing
   STYLESHEET rule — checked the generated `post-19411.css` (empty aside from `text-align`) and
   Elementor's own generic image-widget CSS (no `img` width/max-width default exists at all for this
   widget). The intrinsic-sizing collapse the comment above describes has nothing to do with cascade
   specificity (no rule fights this one), so a plain declaration fixes the collapse exactly as well as
   an `!important` one — and, unlike `!important`, a plain declaration correctly LOSES to any future
   real per-element size control the client might set on this Image widget (Elementor's own per-element
   rule would be `.elementor-element.elementor-element-b98f26f9 img{…}`, specificity (0,3,1), well above
   this rule's (0,1,1)). Dropped `!important`; re-verified the crest still renders at the pinned
   --ags-nav-crest size (no regression) — see override_targets.json `nav-crest-width`.
   CORRECTION (2026-09-27 live-parity pass): Elementor DOES ship a generic default that fights this —
   `.elementor-widget-image a img[src$=".svg"]{width:48px}` (0,2,2) — the crest is an SVG inside a link,
   so the plain (0,1,1) rule lost and the crest shrank to 48px. The selector below matches that default's
   shape (0,2,2; theme loads later → wins the tie) and still loses to a real per-element size (0,3,1). */
.ags-nav2__crest.elementor-widget-image a img, .elementor-widget-image[data-ags-nav~="crest"] a img, .ags-nav2__crest img, [data-ags-nav~="crest"] img { height: var(--ags-nav-crest); width: var(--ags-nav-crest); max-width: none; -o-object-fit: contain; object-fit: contain; display: block; }
:is(.ags-nav2__crest, [data-ags-nav~="crest"]).elementor-widget-image a img,
:is(.ags-nav2__crest, [data-ags-nav~="crest"]) img { height: var(--ags-nav-crest); width: var(--ags-nav-crest); max-width: none; -o-object-fit: contain; object-fit: contain; display: block; }
/* THE PILL is the single cream shape that MORPHS. At REST: a short rounded floating bar, inset by
   the top gap, the crest zone on the left, and the gutter on the right. On EXPAND (.is-mega-open):
   margins → 0 so it grows to fill the viewport top + both edges, corners square. The removed
   margins are added back as PADDING so the links never move. Its child .ags-nav2__window unfolds
   downward inside it (grid reveal) — because the window is a CHILD, its width == the pill's width
   at EVERY frame, so pill + panel morph as ONE shape. Reverse (mouse-leave removes the class) runs
   all these transitions backwards → shrinks back into the pill. ONE shared timing (--ags-anim). */
.ags-nav2__pill.e-con, .e-con[data-ags-nav~="pill"] {
  position: relative;
  /* = half the ~44px pill height → an IDENTICAL fully-rounded pill at rest, but it animates COHESIVELY.
     The global --ags-radius-pill (999px) kept the corners visibly round until ~98% of the morph, so they
     squared off ~100ms AFTER the edges reached the corners — that's the "corners aren't part of the same
     animation" issue. A 22px start squares the top corners in step with the edges reaching the viewport. */
  flex: 1 1 auto;
  min-width: 0;
  margin: var(--ags-nav-top) var(--ags-nav-gutter) 0 var(--ags-pad-l-open);  /* left margin reserves crest zone */
  padding: 6px 10px 6px 20px;
  align-items: center;
  /* UNIFIED BLOOM: keep the links on one row, but let the dropdown WINDOW wrap onto its OWN full-
     width line BELOW them (flex-wrap) so it's IN-FLOW — the pill's OWN height then includes it. The
     window reveals via its grid 0fr→1fr; as it grows the pill grows with it, on the SAME transition
     as the pill's margin/padding/radius → the whole cream shape morphs as ONE motion. At rest the
     window is 0-height, so this second line adds nothing and the pill looks EXACTLY as before. */
  flex-wrap: wrap;
  row-gap: 0;
  /* CLOSE: everything reverts TOGETHER (no lead) — the pill geometry and the dropdown collapse on the
     same clock, so the close reads as ONE cohesive motion, not a staged one-by-one revert. */
  transition: margin-top var(--ags-nav-geo), margin-right var(--ags-nav-geo), margin-left var(--ags-nav-geo-x), padding-top var(--ags-nav-geo), padding-right var(--ags-nav-geo), padding-left var(--ags-nav-geo-x), border-radius var(--ags-nav-geo);
}
:is(.ags-nav2__pill, [data-ags-nav~="pill"]).e-con {
  position: relative;
  /* = half the ~44px pill height → an IDENTICAL fully-rounded pill at rest, but it animates COHESIVELY.
     The global --ags-radius-pill (999px) kept the corners visibly round until ~98% of the morph, so they
     squared off ~100ms AFTER the edges reached the corners — that's the "corners aren't part of the same
     animation" issue. A 22px start squares the top corners in step with the edges reaching the viewport. */
  flex: 1 1 auto;
  min-width: 0;
  margin: var(--ags-nav-top) var(--ags-nav-gutter) 0 var(--ags-pad-l-open);  /* left margin reserves crest zone */
  padding: 6px 10px 6px 20px;
  align-items: center;
  /* UNIFIED BLOOM: keep the links on one row, but let the dropdown WINDOW wrap onto its OWN full-
     width line BELOW them (flex-wrap) so it's IN-FLOW — the pill's OWN height then includes it. The
     window reveals via its grid 0fr→1fr; as it grows the pill grows with it, on the SAME transition
     as the pill's margin/padding/radius → the whole cream shape morphs as ONE motion. At rest the
     window is 0-height, so this second line adds nothing and the pill looks EXACTLY as before. */
  flex-wrap: wrap;
  row-gap: 0;
  /* CLOSE: everything reverts TOGETHER (no lead) — the pill geometry and the dropdown collapse on the
     same clock, so the close reads as ONE cohesive motion, not a staged one-by-one revert. */
  transition: margin-top var(--ags-nav-geo), margin-right var(--ags-nav-geo), margin-left var(--ags-nav-geo-x), padding-top var(--ags-nav-geo), padding-right var(--ags-nav-geo), padding-left var(--ags-nav-geo-x), border-radius var(--ags-nav-geo);
}
.ags-nav2.is-mega-open .ags-nav2__pill.e-con, .ags-nav2.is-mega-open .e-con[data-ags-nav~="pill"], .is-mega-open[data-ags-nav~="bar"] .ags-nav2__pill.e-con, .is-mega-open[data-ags-nav~="bar"] .e-con[data-ags-nav~="pill"] {
  margin: 0;
  /* add the removed insets back as padding so the links + panel anchor DON'T move: margin-top
     collapses 20→0 (the white fills UP to the viewport top) while padding-top grows by the same
     amount, so the content baseline and the pill's BOTTOM edge stay fixed → no shift-up, and the
     panel (top:100%) unfolds from a stationary line in sync with the fill. */
  padding-top: calc(6px + var(--ags-nav-top));
  padding-left: calc(20px + var(--ags-pad-l-open));
  padding-right: calc(10px + var(--ags-pad-r-open));
  border-radius: 0 0 18px 18px;              /* square top (flush to viewport top/edges), rounded bottom — as before */
  box-shadow: 0 22px 34px rgba(17, 16, 16, .10);
  /* OPEN ordering: the geometry (edges + corners) goes FIRST — no delay — so the pill forms its shape
     before the dropdown drops into it (the window height below is delayed by --ags-nav-lead). The long
     left edge uses the faster --ags-nav-geo-x so it reaches the corner in step with the short edges. */
  transition: margin-top var(--ags-nav-geo), margin-right var(--ags-nav-geo), margin-left var(--ags-nav-geo-x), padding-top var(--ags-nav-geo), padding-right var(--ags-nav-geo), padding-left var(--ags-nav-geo-x), border-radius var(--ags-nav-geo);
}
:is(.ags-nav2, [data-ags-nav~="bar"]).is-mega-open :is(.ags-nav2__pill, [data-ags-nav~="pill"]).e-con {
  margin: 0;
  /* add the removed insets back as padding so the links + panel anchor DON'T move: margin-top
     collapses 20→0 (the white fills UP to the viewport top) while padding-top grows by the same
     amount, so the content baseline and the pill's BOTTOM edge stay fixed → no shift-up, and the
     panel (top:100%) unfolds from a stationary line in sync with the fill. */
  padding-top: calc(6px + var(--ags-nav-top));
  padding-left: calc(20px + var(--ags-pad-l-open));
  padding-right: calc(10px + var(--ags-pad-r-open));
  border-radius: 0 0 18px 18px;              /* square top (flush to viewport top/edges), rounded bottom — as before */
  box-shadow: 0 22px 34px rgba(17, 16, 16, .10);
  /* OPEN ordering: the geometry (edges + corners) goes FIRST — no delay — so the pill forms its shape
     before the dropdown drops into it (the window height below is delayed by --ags-nav-lead). The long
     left edge uses the faster --ags-nav-geo-x so it reaches the corner in step with the short edges. */
  transition: margin-top var(--ags-nav-geo), margin-right var(--ags-nav-geo), margin-left var(--ags-nav-geo-x), padding-top var(--ags-nav-geo), padding-right var(--ags-nav-geo), padding-left var(--ags-nav-geo-x), border-radius var(--ags-nav-geo);
}

/* Nav menu widget fills the pill; lay links out horizontally (the Pro widget's wrapper chain
   collapses to min-content otherwise, stacking links vertically). Force the widget + its inner
   wrappers to position:static so the full-width dropdown window resolves against :is(.ags-nav2, [data-ags-nav~="bar"])
   (x=0, full viewport width) — NOT the narrower widget wrapper (which offset the panel left). */
.ags-nav2__menu { flex: 1 1 auto; min-width: 0; width: 100%; position: static; }
/* ALL wrappers between the sub-menu and :is(.ags-nav2, [data-ags-nav~="bar"]) must be static, so the full-width dropdown
   window's containing block is :is(.ags-nav2, [data-ags-nav~="bar"]) (only positioned ancestor = x:0, full 1280 width). The
   nav <ul> (.elementor-nav-menu) is the sneaky one — Elementor makes it position:relative, which
   was the sub-menu's offsetParent, anchoring the panel narrow + offset. */
.ags-nav2__menu .elementor-widget-container,
.ags-nav2__menu .elementor-nav-menu--main,
.ags-nav2__menu .elementor-nav-menu--main > .elementor-nav-menu,
.ags-nav2__pill.e-con,
.e-con[data-ags-nav~="pill"] { position: static; }
.ags-nav2__menu .elementor-widget-container,
.ags-nav2__menu .elementor-nav-menu--main,
.ags-nav2__menu .elementor-nav-menu--main > .elementor-nav-menu,
:is(.ags-nav2__pill, [data-ags-nav~="pill"]).e-con { position: static; }
.ags-nav2__menu .elementor-widget-container { width: 100%; }
.ags-nav2__menu .elementor-nav-menu--main { width: 100%; display: flex; }
.ags-nav2__menu .elementor-nav-menu--main > .elementor-nav-menu {
  display: flex;
  flex-direction: row;
  flex-wrap: nowrap;
  align-items: center;
  gap: max(12px, min(1.6vw, 26px));
  gap: clamp(12px, 1.6vw, 26px);
  width: 100%;
  margin: 0;
  padding: 0;
}
/* Main-menu li stays STATIC so the full-width dropdown window resolves against :is(.ags-nav2, [data-ags-nav~="bar"]) (the
   nearest positioned ancestor = full viewport width), not the narrow <li>. */
.ags-nav2__menu .elementor-nav-menu--main > .elementor-nav-menu > li.menu-item { position: static; }

/* LEFT vs RIGHT split — the RIGHT cluster is the language switcher + BOOK pill; everything before it
   (Tour · Retreat · Experiences · Discover — plus any not-yet-ready items like Events when they get added
   back) is the LEFT cluster. Anchor the split to the item IMMEDIATELY BEFORE the permanent BOOK pill
   (:has) instead of a hand-placed .ags-nav-right-start marker. The old marker rode on "Events", so
   REMOVING Events killed the split (right cluster collapsed left) and re-adding it could double it up
   (two margin-left:autos share the free space). BOOK is always the last item and the language switcher
   always sits just before it, so this self-heals across ANY add/remove of left items — nothing to move.
   (.ags-nav-right-start on menu items is now inert/harmless; the menu scripts may still stamp it.) */
.ags-nav2__menu .elementor-nav-menu > li:has(+ li.ags-nav-book) { margin-left: auto; }
/* BOOK sticky to the pill's RIGHT edge, so its right gap matches the 10px top/bottom (per feedback). The
   flex `gap` + the margin-left:auto split leaves a phantom gap-worth of trailing space after the last
   item; pull BOOK back by exactly one gap so it sits flush (right gap → the pill's 10px padding-right). */
.ags-nav2__menu .elementor-nav-menu > li.ags-nav-book { margin-right: calc(-1 * max(12px, min(1.6vw, 26px)) + 10px); margin-right: calc(-1 * clamp(12px, 1.6vw, 26px) + 10px); margin-left: 34px; }

/* Top-level links: Red Rose BOLD (matches the reference weight — the Kit's link_bold/accent
   weight 700), RED (global 'primary') on cream. Darkens slightly on hover.
   Phase 2b Task 3 (item 3, Navbar override-respect) — TRIED, then REVERTED: wrapping `.ags-nav2__menu`
   in `:where()` here looked right by the same recipe used elsewhere (drop below what I estimated as
   Elementor's real per-widget Style-tab specificity), and it DID make the `nav-item-color` probe
   target pass — but the full-site cstyle proof cycle caught a real regression the probe target
   missed: the CURRENT-PAGE nav item (`.elementor-item-active`) flipped from red to cream on every
   interior page, because Elementor's OWN Kit-wide active-item default
   (`.elementor-widget-nav-menu .elementor-nav-menu--main .elementor-item.elementor-item-active{color:
   var(--e-global-color-accent)}`, global.css, specificity (0,4,0)) sits BETWEEN the un-wrapped
   (0,4,2) and the wrapped (0,3,2) — confirmed via a live CSS.getMatchedStylesForNode-style scan, not
   guessed. The un-wrapped (0,4,2) rule correctly beats that Kit default (same b, higher c) AND —
   checked directly — a hypothetical real per-widget colour override would render at a HIGHER
   specificity than my (0,5,0) probe sentinel estimated (the probe's sentinel shape already passes at
   (0,4,2) unwrapped: (0,5,0) > (0,4,2) with room to spare), so the un-wrapped rule was never actually
   beating a real per-element override in the first place — nothing to fix here. Left UN-WRAPPED;
   `nav-item-color` in override_targets.json still exists and still PASSES against the plain rule. */
.ags-nav2__menu .elementor-nav-menu > li.menu-item > a.elementor-item {
  font-family: "Red Rose", serif;
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -0.01em;
  color: var(--e-global-color-primary, var(--ags-red));
  padding: 8px 2px;
  background: transparent;
  white-space: nowrap;
}
/* Darker red on HOVER only — the ACTIVE page is indicated by the underline below, NOT a colour
   change (a colour "highlight" on current-menu-item also turned the BOOK pill's cream text dark-red
   → it blended into the red pill). */
.ags-nav2__menu .elementor-nav-menu > li.menu-item:not(.ags-nav-book) > a.elementor-item:hover,
.ags-nav2__menu .elementor-nav-menu > li.menu-item:not(.ags-nav-book) > a.elementor-item:focus,
.ags-nav2__menu .elementor-nav-menu > li.menu-item:not(.ags-nav-book) > a.elementor-item:focus-visible {
  color: var(--e-global-color-agsreddark, var(--ags-red-dark));
  background: none;
}

/* ACTIVE / HOVER INDICATOR on top-level links — a red underline that grows from the centre.
   Shows on hover, and STAYS while the item's dropdown is open (li.is-open, set by ageshio-nav.js).
   Uses a box-shadow-free inline-block underline via a pseudo so it doesn't shift layout. The
   BOOK YOUR TRIP pill is excluded (it's a solid button, not a text link). */
.ags-nav2__menu .elementor-nav-menu > li.menu-item:not(.ags-nav-book) > a.elementor-item {
  position: relative;
}
.ags-nav2__menu .elementor-nav-menu > li.menu-item:not(.ags-nav-book) > a.elementor-item::before {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 6px;   /* close under the text (the <a> has 8px vertical padding) */
  height: 2px;
  background: var(--e-global-color-primary, var(--ags-red));
  transform: scaleX(0);
  transform-origin: center;
  transition: transform .25s cubic-bezier(.25,1,.5,1);
}
.ags-nav2__menu .elementor-nav-menu > li.menu-item:not(.ags-nav-book) > a.elementor-item:hover::before,
.ags-nav2__menu .elementor-nav-menu > li.menu-item.is-open > a.elementor-item::before,
.ags-nav2__menu .elementor-nav-menu > li.menu-item.current-menu-item:not(.ags-nav-book) > a.elementor-item::before,
.ags-nav2__menu .elementor-nav-menu > li.menu-item.current-menu-ancestor:not(.ags-nav-book) > a.elementor-item::before {
  transform: scaleX(1);
}
/* Dropdown indicator: the "+" is baked INTO the menu label text (e.g. "Experiences +") by the
   menu script — the most reliable approach (Elementor's caret lives in a .sub-arrow flex child
   whose ::after replacement kept dropping to its own line). Just hide Elementor's caret icon. */
.ags-nav2__menu .elementor-nav-menu > li.menu-item-has-children > a .sub-arrow { display: none; }

/* BOOK YOUR TRIP — the .ags-nav-book menu item rendered as a THIN solid RED PILL (global
   primary bg, cream text). SINGLE LINK to the contact-forms page (no dropdown). */
.ags-nav2__menu .elementor-nav-menu > li.ags-nav-book > a.elementor-item {
  background: var(--ags-btn-solid-bg, var(--e-global-color-primary, var(--ags-red)));      /* = Thin size + Solid colours (Ageshio Components knobs) */
  color: var(--ags-btn-solid-text, var(--e-global-color-accent, var(--ags-cream)));
  border-radius: var(--ags-btn-radius, var(--ags-radius-pill));
  padding: var(--ags-btn-thin-pad-y, var(--ags-btn-pad-y-thin)) 30px;
  /* Width == the dropdown photo pair, so the two photos line up directly beneath the button (per
     feedback: "extend the button length to match the pictures"). Centred label. */
  display: inline-flex; align-items: center; justify-content: center;
  box-sizing: border-box; width: var(--ags-nav-photos-w); text-align: center;
  margin-left: 6px;
  line-height: 1.25;
}
/* BOOK YOUR TRIP is a plain link (no dropdown) → no "+" after it. */
.ags-nav2__menu .elementor-nav-menu > li.ags-nav-book > a.elementor-item::after { content: none; }
.ags-nav2__menu .elementor-nav-menu > li.ags-nav-book > a.elementor-item:hover { color: var(--ags-btn-solid-text, var(--e-global-color-accent, var(--ags-cream))); background: var(--ags-btn-solid-hover, var(--e-global-color-agsreddark, var(--ags-red-dark))); }

/* Elementor's OWN sub-menus are HIDDEN on desktop — we render our own full-width window
   (.ags-nav2__window) via ageshio-nav.js instead (the widget JS-positions sub-menus as narrow
   per-item dropdowns that can't be forced full-width). The widget's sub-menus still drive the
   MOBILE burger menu, so only suppress them at desktop width.
   DESKTOP boundary = ≥1024 (matches body{min-width:1024} + the whole desktop layout). Below that
   the mobile burger takes over — so this desktop-only suppression must start at 1024, NOT 768,
   or the 768–1023 tablet band gets desktop treatment while the rest of the page is mobile. */
@media (min-width: 1024px) {
  .ags-nav2__menu .elementor-nav-menu--main .sub-menu { display: none !important; }
  /* Elementor's Nav Menu widget shows its burger toggle at ≤1024 (its default "tablet"
     breakpoint) — but our desktop starts AT 1024, so the burger doubled up with the horizontal
     menu across 768–1024. Hide the burger on desktop so the nav flips cleanly at the SAME
     1024/1023 line as the rest of the site. */
  .ags-nav2__menu .elementor-menu-toggle { display: none !important; }
}

/* Pill must NOT clip the window (which escapes its padding via negative offsets) at rest, but the
   window is height-collapsed then so nothing shows regardless. */
.ags-nav2__pill.e-con, .e-con[data-ags-nav~="pill"] { overflow: visible; }
:is(.ags-nav2__pill, [data-ags-nav~="pill"]).e-con { overflow: visible; }
/* MEGA-MENU WINDOW — an IN-FLOW full-width line at the bottom of the pill (via the pill's flex-wrap),
   so the pill's OWN height includes it → pill + dropdown grow/shrink as ONE shape. Its HEIGHT animates
   (JS sets the open height to the content's natural height; rest is 0) under overflow:hidden.
   We animate HEIGHT, not grid 0fr↔1fr, because grid-fr animates 0fr→1fr (open) but SNAPS 1fr→0fr on
   close in this engine — which made the dropdown vanish abruptly. height animates BOTH ways. Content
   sits in the pill's stationary content box → revealed in place, no slide. */
.ags-nav2__window {
  --ags-win-pad-left: var(--ags-pad-l-open);          /* fallback; JS sets the per-parent value */
  flex: 0 0 100%;                                     /* full-width line below the links */
  overflow: hidden;                                   /* clip content while collapsed / collapsing */
  height: 0;
  /* CLOSE: retracts immediately (no lead) so it collapses TOGETHER with the pill geometry → cohesive close. */
  transition: height var(--ags-nav-anim);
}
.ags-nav2.is-mega-open .ags-nav2__window, .is-mega-open[data-ags-nav~="bar"] .ags-nav2__window {
  /* OPEN: drops AFTER the pill forms its shape (lead delay). Height is set inline by JS to the content height. */
  transition: height var(--ags-nav-anim) var(--ags-nav-lead);
}
:is(.ags-nav2, [data-ags-nav~="bar"]).is-mega-open .ags-nav2__window {
  /* OPEN: drops AFTER the pill forms its shape (lead delay). Height is set inline by JS to the content height. */
  transition: height var(--ags-nav-anim) var(--ags-nav-lead);
}
/* The collapsing grid item — BARE (no padding/border) so grid-row 0fr shrinks it to a true 0. */
.ags-nav2__window-inner {
  overflow: hidden;
  min-height: 0;
}
/* The padded content row (list left, photos right). Padding lives HERE, not on the grid item above,
   so the item can collapse fully. Right padding aligns the photos with the BOOK YOUR TRIP button edge. */
.ags-nav2__window-pad {
  padding: 6px var(--ags-nav-window-r, 53px) 26px var(--ags-win-pad-left);
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
}
.ags-nav2__window-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
/* Dropdown photos: a right-aligned PAIR, matching the mobile nav corner images. Placeholder fill
   until real images are set — swap the background-image on --photo-1 / --photo-2 (also drives the
   mobile corners so both stay in sync). */
.ags-nav2__window-photos { flex: 0 0 auto; display: flex; gap: var(--ags-nav-photo-gap); align-self: flex-start; margin-top: 20px; }   /* dropped so the button→photos gap EQUALS the space above the BOOK button (~30px open): pad content top 64 + 20 → photo top 84 = book bottom 54 + 30. Consistent in every dropdown; bottom falls naturally. Right edge aligns to the BOOK button (JS sets --ags-nav-window-r). */
.ags-nav2__window-photo {
  width: var(--ags-nav-photo-w); height: var(--ags-nav-photo-w); border-radius: var(--ags-card-radius, var(--ags-radius-md));   /* SQUARE, matching the mobile nav corner photos */
  background: #d8d0cb center/cover no-repeat;
}
/* Real nav photos (shared desktop dropdown + mobile corners): tea-ceremony LEFT/TR, wood-texture
   RIGHT/BL — matches figma/frames/navbar-v2/navbar-dropdown-open (WebP under uploads redesign dir). */
.ags-nav2__window-photo--1 { background-image: var(--ags-img-nav-tea, url("/wp-content/uploads/redesign/nav-tea.jpg")); background-image: var(--ags-img-nav-tea, url("/wp-content/uploads/redesign/nav-tea.webp")); }
.ags-nav2__window-photo--2 { background-image: var(--ags-img-nav-wood, url("/wp-content/uploads/redesign/nav-wood.jpg")); background-image: var(--ags-img-nav-wood, url("/wp-content/uploads/redesign/nav-wood.webp")); }
.ags-nav2__window-list a {
  position: relative;
  display: inline-block;
  font-family: "Red Rose", serif;
  font-weight: 700;
  font-size: 15px;
  letter-spacing: -0.01em;
  color: var(--e-global-color-primary, var(--ags-red));
  padding: 7px 0;
  white-space: nowrap;
  -webkit-text-decoration: none;
  text-decoration: none;
  transition: color .2s ease;
}
/* HOVER/ACTIVE INDICATOR on dropdown items — an underline grows in under the label. The link does
   NOT move (no padding shift) — the underline is a pseudo positioned under it. */
.ags-nav2__window-list a::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: 2px;
  height: 2px;
  background: var(--e-global-color-primary, var(--ags-red));
  transform: scaleX(0);
  transform-origin: left center;
  transition: transform .2s cubic-bezier(.25,1,.5,1);
}
/* Kill Elementor's default sub-item hover skin (a dark-grey rgb(63,68,75) box) — hover is indicated
   by the colour shift + the growing underline only, NO highlight box (Simon 2026-08-18). */
.ags-nav2__window-list a:hover, .ags-nav2__window-list a:focus-visible { color: var(--e-global-color-agsreddark, var(--ags-red-dark)); background: none; }
.ags-nav2__window-list a.elementor-sub-item:hover,
.ags-nav2__window-list a.elementor-sub-item:focus,
.ags-nav2__window-list a.elementor-sub-item:focus-visible { background: transparent; }
.ags-nav2__window-list a:hover::after, .ags-nav2__window-list a:focus-visible::after { transform: scaleX(1); }
/* ACTIVE dropdown item = the current page under this parent. Elementor tags the cloned sub-link
   .elementor-item-active + aria-current="page" and its default skin paints a dark-grey box with
   white text. Override to the SAME red text as the other items + a persistent underline (the
   selected item is indicated by the underline, NOT a highlight box). */
.ags-nav2__window-list a.elementor-item-active,
.ags-nav2__window-list a[aria-current="page"] {
  background: none;
  color: var(--e-global-color-primary, var(--ags-red));
}
.ags-nav2__window-list a.elementor-item-active::after,
.ags-nav2__window-list a[aria-current="page"]::after { transform: scaleX(1); }
@media (max-width: 1023px) { .ags-nav2__window { display: none; } }
@media (max-width: 1023px) {
  /* Elementor's own mobile UI + the desktop pill/crest are all hidden. */
  .ags-nav2__menu .elementor-nav-menu--main { display: none; }
  .ags-nav2__menu .elementor-menu-toggle { display: none !important; }
  .ags-nav2__menu .elementor-nav-menu--dropdown { display: none !important; }
  .ags-nav2__logo, [data-ags-nav~="logo"], .ags-nav2__pill, [data-ags-nav~="pill"] { display: none; }
  :is(.ags-nav2__logo, [data-ags-nav~="logo"]), :is(.ags-nav2__pill, [data-ags-nav~="pill"]) { display: none; }

  .ags-mnav {
    --mnav-u: min(4.6vw, 18px);          /* the ONE knob. 390px → 17.9px ≈ 18px cap */
    --mnav-pad: calc(1.1 * var(--mnav-u));
    /* Shared pill height for the three bottom-bar buttons (ENG · BOOK · burger) so they match
       EXACTLY — declared on the common ancestor so all three inherit it (memory:
       wp-redesign-mobile-scaling-pattern → shared sub-knob on the common ancestor). */
    --mnav-pill-h: calc(1.55 * var(--mnav-u));  /* ~28px @390 — thin pills, per reference */
    display: block;
    font-family: "Red Rose", serif;
  }

  /* PERSISTENT CREST — fixed top-left, ALWAYS visible: over the maroon page when the menu is
     closed, over the cream panel when open (z above the overlay so it's never covered). The menu
     has NO top bar — the only chrome is this crest + the bottom action bar. Decorative. */
  .ags-mnav__crest {
    position: fixed;
    /* MATCH the desktop crest position: desktop img sits flush at top:0, left:10px. */
    top: 0; left: calc(0.56 * var(--mnav-u));   /* ~10px @390 */
    z-index: 960;
    height: calc(6.25 * var(--mnav-u));   /* ~112px @390 — MATCHES the desktop crest size (:is(.ags-nav2, [data-ags-nav~="bar"]) --ags-nav-crest:112px); knob-scaled so it shrinks on smaller phones */
    width: auto; display: block;
    pointer-events: auto;   /* the crest is now a HOME link (like desktop) — tappable */
    cursor: pointer;
    transition: transform .45s cubic-bezier(.4,0,.2,1);
  }
  .ags-mnav__crest img { display: block; height: 100%; width: auto; }
  /* HIDE ON SCROLL (in lockstep with the desktop header, toggled by ageshio-nav.js): crest slides
     UP off the top, action bar slides DOWN off the bottom. Reveals on scroll-up. */
  .ags-mnav.is-hidden .ags-mnav__crest { transform: translateY(calc(-100% - 6px)); }
  /* THE THREE BOTTOM-BAR BUTTONS (ENG · BOOK YOUR TRIP · burger) share ONE pill treatment so they
     match exactly: same height (--mnav-pill-h), pill radius, 700 Red Rose. COLOURS FLIP with menu
     state (set in the .ags-mnav__actions block below): CLOSED = cream pill + red content; OPEN
     (.is-open) = red pill + cream content. Base geometry only here. */
  .ags-mnav__eng,
  .ags-mnav__actions .ags-mnav__lang-btn,
  .ags-mnav__actions .ags-mnav__book,
  .ags-mnav__actions .ags-mnav__burger {
    flex: 0 0 auto;
    box-sizing: border-box;
    height: var(--mnav-pill-h);
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: var(--ags-radius-pill);
    font-family: "Red Rose", serif; font-weight: 700;
    font-size: calc(0.82 * var(--mnav-u));
    letter-spacing: 0.02em;
    -webkit-text-decoration: none;
    text-decoration: none; white-space: nowrap;
  }
  /* Widths tuned so ENG + BOOK + burger + gaps fit inside the bar's content box at 390 (they were
     overflowing the right edge under space-between). All knob-based → constant viewport fraction,
     so fitting at 390 fits every width. */
  .ags-mnav__eng { padding-left: calc(0.9 * var(--mnav-u)); padding-right: calc(0.9 * var(--mnav-u)); }
  .ags-mnav__actions .ags-mnav__book { padding-left: calc(1.5 * var(--mnav-u)); padding-right: calc(1.5 * var(--mnav-u)); }
  .ags-mnav__actions .ags-mnav__burger { width: calc(2.8 * var(--mnav-u)); padding: 0; border: 0; cursor: pointer; }

  /* Burger glyph: 3 bars via the button's <span> + its ::before/::after. currentColor so the
     bars flip with the pill's text colour (red when closed, cream when open). */
  .ags-mnav__burger { position: relative; }
  .ags-mnav__burger > span { position: relative; display: block; width: calc(1.15 * var(--mnav-u)); }
  .ags-mnav__burger > span,
  .ags-mnav__burger > span::before,
  .ags-mnav__burger > span::after {
    content: ""; height: calc(0.12 * var(--mnav-u)); min-height: 1.5px;
    background: currentColor; border-radius: 2px;
  }
  .ags-mnav__burger > span::before,
  .ags-mnav__burger > span::after {
    position: absolute; left: 0; right: 0;
  }
  /* Lines closer together (smaller glyph inside the slim pill). */
  .ags-mnav__burger > span::before { top: calc(-0.32 * var(--mnav-u)); }
  .ags-mnav__burger > span::after  { top: calc(0.32 * var(--mnav-u)); }

  /* THE FADING CREAM PANEL — full-screen, holds the corner photos + the sliding list stage. This
     is the ONLY part that shows/hides; the crest + action bar are persistent chrome ON TOP of it.
     Fade IN/OUT via opacity, gated by visibility (mirrors .ags-nav2__window): kept display:flex
     always so the opacity transition has a rendered "from" state — a display:none↔flex toggle would
     pop instead of fade. visibility snaps OFF after the fade completes (0s delay = --mnav-anim) so
     the closed panel is non-interactive and invisible; pointer-events guards taps at rest. Padding
     leaves room for the fixed crest (top) and the fixed action bar (bottom) so the centred list
     never tucks under them. Under reduced-motion the guard below zeroes the transition → instant
     show/hide. (≥1024 the whole .ags-mnav is display:none via the min-width rule.) */
  .ags-mnav__overlay {
    display: flex;
    position: fixed; top: 0; right: 0; bottom: 0; left: 0;
    z-index: 940;
    background: var(--e-global-color-accent, var(--ags-cream));
    flex-direction: column;
    /* Top/bottom padding reserve room for the fixed crest (top) and action bar (bottom). The bottom
       must also account for the ~var(--mnav-bar-h) action bar so the centred list sits in the TRUE
       optical centre of the open space (Simon: lift the links to centred) — the list was ~27px low
       because the bar height wasn't included in the bottom reserve. */
    padding: calc(5.6 * var(--mnav-u)) var(--mnav-pad) calc(4.4 * var(--mnav-u) + var(--mnav-bar-h, 4.2 * var(--mnav-u)));
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity var(--mnav-anim), visibility 0s linear var(--mnav-anim);
  }
  .ags-mnav { --mnav-anim: .28s cubic-bezier(.22,1,.36,1); }
  .ags-mnav.is-open .ags-mnav__overlay {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition: opacity var(--mnav-anim), visibility 0s;
  }
  /* Corner photos sit BEHIND the menu text (z-index:0), as decorative accents — a tall
     list (e.g. Experiences' 6 wrapped items) can reach a corner, and text must stay readable
     over the placeholder rather than being painted over by it. */
  .ags-mnav__photo {
    position: absolute; z-index: 0;
    width: calc(8.2 * var(--mnav-u)); height: calc(8.2 * var(--mnav-u));
    background: #d8d0cb center/cover no-repeat;   /* neutral placeholder fill (Simon supplies photos) */
    border-radius: calc(0.5 * var(--mnav-u));
    /* iOS WebKit: pin the corner photos to their OWN compositing layer so the panels' translateX
       slide (below) can't repaint/reposition them mid-animation — the "photo jumps out of its
       top-right spot when clicking into a submenu" on interior. translateZ(0) promotes the layer
       WITHOUT changing the top/right placement; backface-visibility:hidden keeps the layer stable. */
    transform: translateZ(0);
    -webkit-backface-visibility: hidden; backface-visibility: hidden;
  }
  /* Corner photos hug the corners (Simon ref #3/#4): TR flush top-right; BL bottom-left but it MUST
     CLEAR the fixed action bar (ENG · BOOK · burger). The bar is ~var(--mnav-bar-h) tall; place the
     BL photo's bottom just above it (bar height + small gap) so it never sits over the button.
     (Was 2.4u → photo bottom overlapped the bar by ~20px.) */
  .ags-mnav__photo--tr { top: var(--mnav-pad); right: var(--mnav-pad); background-image: var(--ags-img-nav-tea, url("/wp-content/uploads/redesign/nav-tea.jpg")); background-image: var(--ags-img-nav-tea, url("/wp-content/uploads/redesign/nav-tea.webp")); }
  .ags-mnav__photo--bl { bottom: calc(var(--mnav-bar-h, 4.2 * var(--mnav-u)) + 0.1 * var(--mnav-u)); left: var(--mnav-pad); background-image: var(--ags-img-nav-wood, url("/wp-content/uploads/redesign/nav-wood.jpg")); background-image: var(--ags-img-nav-wood, url("/wp-content/uploads/redesign/nav-wood.webp")); }

  .ags-mnav__stage {
    flex: 1 1 auto; position: relative; overflow: hidden;
    display: flex;
    z-index: 1;
  }
  /* Each panel scrolls INTERNALLY when its content is taller than the stage (the tallest case
     is Experiences: 6 items, several wrapping to 2 lines, on a short phone). `safe center`
     centres when it fits but falls back to top-aligned + scrollable when it overflows, instead
     of clipping the top unreachably (the classic flex-centering overflow trap). */
  .ags-mnav__panel {
    position: absolute; top: 0; right: 0; bottom: 0; left: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    justify-content: safe center;
    overflow-y: auto;
    gap: calc(0.9 * var(--mnav-u));
    padding: calc(3.2 * var(--mnav-u)) var(--mnav-pad) calc(1.4 * var(--mnav-u));  /* top room for the pinned < Back */
    transition: transform var(--mnav-anim);
    /* iOS WebKit: keep each panel on its OWN persistent compositing layer. The sub-panel is populated
       while off-screen (translateX(100%)); without a live layer WebKit slides in a stale/blank snapshot
       (the missing sub-links) — the JS reflow-then-next-frame in ageshio-nav.js commits the new links,
       and will-change keeps the layer around so they actually paint. Also isolates each panel's repaint
       from the corner photos. */
    -webkit-backface-visibility: hidden; backface-visibility: hidden;
    will-change: transform;
  }
  /* Centre the MAIN list in the band BETWEEN the two corner photos (TR-bottom ↔ BL-top), not in the
     raw stage — the fixed action bar made the stage's optical centre ~17px low. A small bottom pad
     bump lifts the centred column up into the photo band. (Main only; the sub-panel band differs.) */
  .ags-mnav__panel--main { transform: translateX(0); padding-bottom: calc(3.3 * var(--mnav-u)); }
  .ags-mnav__panel--sub  { transform: translateX(100%); }
  .ags-mnav.is-sub .ags-mnav__panel--main { transform: translateX(-100%); }
  .ags-mnav.is-sub .ags-mnav__panel--sub  { transform: translateX(0); }
  /* AUTO-FIT the sub-panel (Simon 2026-09-24): a submenu can hold ANY number of links — the client
     re-adds items over time — and long labels wrap to 2 lines, so on a SHORT phone the < Back + link
     stack grew into the corner photos. ageshio-nav.js fitSubList() measures that stack against the
     CLEAR BAND between the two corner photos and sets --mnav-subfit (≤1); the WHOLE sub-panel then
     scales down to fit, so every add renders SMALLER instead of reaching those edges. ONE factor
     drives every height contributor (same idea as --mnav-fit on the interior top bar). Default 1 =
     design size for small submenus; the JS clamps a legible floor and scroll is the last-resort
     fallback. Only VERTICAL scale — the wrap width (max-width below) is unchanged. Padding is made
     SYMMETRIC here (was 3.2u top / 1.4u bottom for a since-removed pinned < Back) so the column
     centres at the stage mid-line, matching the symmetric clear band the JS reserves. */
  .ags-mnav__panel--sub {
    --mnav-subfit: 1;
    padding-top: calc(1.6 * var(--mnav-u) * var(--mnav-subfit));
    padding-bottom: calc(1.6 * var(--mnav-u) * var(--mnav-subfit));
  }
  /* Submenu items are spaced by a short, faint divider between them (Simon 2026-10-07, client feedback: "nothing
     separating the experiences") — the divider's margins ARE the item spacing, so the list gap is 0. */
  .ags-mnav__panel--sub .ags-mnav__list { gap: 0; }
  .ags-mnav__panel--sub .ags-mnav__list > li { display: flex; flex-direction: column; align-items: center; }
  .ags-mnav__panel--sub .ags-mnav__list > li + li::before {
    content: ""; display: block;
    width: calc(1.6 * var(--mnav-u)); height: 1px;
    background: var(--e-global-color-primary, var(--ags-red)); opacity: .35;
    margin: calc(0.75 * var(--mnav-u) * var(--mnav-subfit, 1)) 0;
  }
  /* < Back sits WITH the links — in the normal flex flow at the top of the centred sub-list column
     (Simon: "back button still not with the links"). Was position:absolute pinned to the panel top
     (~148px above the list). Now it's a flex child so it centres together with the list; a small
     margin separates it from the first item. */
  .ags-mnav__back {
    align-self: center;
    margin-bottom: calc(0.8 * var(--mnav-u) * var(--mnav-subfit, 1));
    border: 0; background: transparent; cursor: pointer;
    font-family: "Red Rose", serif; font-weight: 700;
    font-size: calc(0.95 * var(--mnav-u) * var(--mnav-subfit, 1));
    color: var(--e-global-color-primary, var(--ags-red));
  }
  .ags-mnav__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; align-items: center; gap: calc(0.9 * var(--mnav-u)); }
  .ags-mnav__list a {
    display: inline-flex; align-items: baseline;
    font-family: "Red Rose", serif; font-weight: 700;
    font-size: calc(1.7 * var(--mnav-u));
    color: var(--e-global-color-primary, var(--ags-red));
    -webkit-text-decoration: none;
    text-decoration: none; white-space: nowrap;
    line-height: 1.0;
  }
  /* Sub-panel children can be long ("Traditional Dojo Training Experience") — let them
     wrap, centered, instead of clipping the 390px stage. Main list stays nowrap (short
     labels). Width capped in knob units so short items don't stretch; tune at 390 ref.
     TIGHT line-height so a label that wraps to 2 lines reads clearly as ONE option — the
     inter-line gap must stay well below the inter-item gap (.ags-mnav__list gap). */
  .ags-mnav__panel--sub .ags-mnav__list a {
    display: block;
    white-space: normal;
    text-align: center;
    font-size: calc(1.15 * var(--mnav-u) * var(--mnav-subfit, 1));   /* smaller than the main list (2026-10-07); scaled by the auto-fit factor */
    max-width: calc(15 * var(--mnav-u));   /* ~270px @390 → long items wrap to 2 lines (wrap width NOT scaled) */
    line-height: 1.05;
  }
  .ags-mnav__plus { margin-right: calc(0.2 * var(--mnav-u)); }

  /* PERSISTENT ACTION BAR — fixed at the BOTTOM, ALWAYS visible (over the maroon page when closed,
     over the cream panel when open). z above the overlay so it's never covered; it carries the ONE
     burger that toggles the menu, so it must stay tappable in both states. */
  .ags-mnav__actions {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 960;
    display: flex; align-items: center; justify-content: space-between;
    gap: calc(0.6 * var(--mnav-u));
    padding: calc(1.0 * var(--mnav-u)) var(--mnav-pad);
    transition: transform .45s cubic-bezier(.4,0,.2,1);
  }
  /* HIDE ON SCROLL — the bar slides DOWN off the bottom edge (crest slides up; see above). */
  .ags-mnav.is-hidden .ags-mnav__actions { transform: translateY(calc(100% + 6px)); }
  /* COLOUR STATE — all three bottom buttons flip together.
     CLOSED (default): cream pill, red content (over the maroon page). */
  .ags-mnav__actions .ags-mnav__eng,
  .ags-mnav__actions .ags-mnav__lang-btn,
  .ags-mnav__actions .ags-mnav__book,
  .ags-mnav__actions .ags-mnav__burger {
    background: var(--e-global-color-accent, var(--ags-cream));
    color: var(--e-global-color-primary, var(--ags-red));
  }
  /* OPEN: red pill, cream content (over the cream panel). */
  .ags-mnav.is-open .ags-mnav__actions .ags-mnav__eng,
  .ags-mnav.is-open .ags-mnav__actions .ags-mnav__lang-btn,
  .ags-mnav.is-open .ags-mnav__actions .ags-mnav__book,
  .ags-mnav.is-open .ags-mnav__actions .ags-mnav__burger {
    background: var(--e-global-color-primary, var(--ags-red));
    color: var(--e-global-color-accent, var(--ags-cream));
  }
  /* Mobile language switcher: current-code button (pill above) + a stack of the other languages that
     pops UP above the bar on tap. List items are always cream pills / red text (they sit over the
     maroon page or the cream panel edge). */
  .ags-mnav__lang { position: relative; flex: 0 0 auto; display: inline-flex; }
  .ags-mnav__actions .ags-mnav__lang-btn { padding-left: calc(0.9 * var(--mnav-u)); padding-right: calc(0.9 * var(--mnav-u)); gap: calc(0.32 * var(--mnav-u)); border: 0; cursor: pointer; }
  .ags-mnav__lang-caret { font-size: 0.8em; line-height: 1; }
  .ags-mnav__lang-list {
    position: absolute; bottom: calc(100% + (0.5 * var(--mnav-u))); bottom: calc(100% + calc(0.5 * var(--mnav-u))); left: 0;
    list-style: none; margin: 0; padding: 0;
    display: flex; flex-direction: column; gap: calc(0.4 * var(--mnav-u));
    opacity: 0; visibility: hidden; transform: translateY(6px);
    transition: opacity .2s ease, transform .2s ease, visibility .2s;
  }
  .ags-mnav__lang.is-open .ags-mnav__lang-list { opacity: 1; visibility: visible; transform: none; }
  /* Interior pages pin the action bar to the TOP, so the list must open DOWNWARD (JS adds --down when
     the button is in the top half of the viewport); otherwise it pops up off-screen. */
  .ags-mnav__lang--down .ags-mnav__lang-list { top: calc(100% + (0.5 * var(--mnav-u))); top: calc(100% + calc(0.5 * var(--mnav-u))); bottom: auto; transform: translateY(-6px); }
  .ags-mnav__lang-list a {
    display: inline-flex; align-items: center; justify-content: center;
    height: var(--mnav-pill-h); padding-left: calc(0.9 * var(--mnav-u)); padding-right: calc(0.9 * var(--mnav-u));
    border-radius: var(--ags-radius-pill);
    background: var(--e-global-color-accent, var(--ags-cream));
    color: var(--e-global-color-primary, var(--ags-red));
    font-family: "Red Rose", serif; font-weight: 700; font-size: calc(0.82 * var(--mnav-u));
    letter-spacing: 0.02em; -webkit-text-decoration: none; text-decoration: none; white-space: nowrap;
  }
}
@media (max-width: 1023px) and (prefers-reduced-motion: reduce) {
  /* Kill ALL overlay/panel transitions under reduced-motion. The .is-open rule re-declares the
     overlay's opacity transition at higher specificity (0,3,0), so the guard must match that
     specificity too or the open-fade leaks through — hence the .ags-mnav[.is-open] selectors. */
  .ags-mnav .ags-mnav__overlay,
  .ags-mnav.is-open .ags-mnav__overlay,
  .ags-mnav .ags-mnav__panel { transition: none; }
}
/* .ags-mnav is desktop-hidden so it never shows ≥1024 (built on body always). Scoped to a
   min-width query — NOT a bare `.ags-mnav{display:none}` — because a bare rule after the ≤1023
   block has equal specificity and (being later in source) would win there too, hiding the bar at
   every width. The min-width gate is mutually exclusive with the ≤1023 block, so no cascade fight. */
@media (min-width: 1024px) { .ags-mnav { display: none; } }

/* =============================================================================
   INTERIOR MOBILE NAV — TOP BAR (Simon 2026-08-17, ref Image #7). Interior pages ONLY:
   :root body = every non-homepage page (the homepage keeps its approved BOTTOM bar).
   The shared .ags-mnav__actions bar is relocated to a transparent TOP bar with the crest on the LEFT and
   the three buttons clustered on the RIGHT in the order BOOK · burger · ENG. Closed: BOOK = cream pill
   (red text), burger + ENG = dark pills (cream content). Open (cream panel): all three flip to red
   pills + cream so the cream BOOK pill isn't invisible on the cream panel. --- */
@media (max-width: 1023px) {
  /* Bar → maroon strip pinned at the TOP; pills cluster right; left space reserved for the crest. */
  :root body .ags-mnav__actions {
    top: 0; bottom: auto;
    justify-content: flex-end; align-items: center;
    gap: calc(0.3 * var(--mnav-u) * var(--mnav-fit, 1));
    /* tighter side padding shifts the whole cluster right → BOOK clears the full-size crest. */
    padding: calc(1.0 * var(--mnav-u)) calc(0.6 * var(--mnav-u));
  }
  /* The pills (BOOK · burger · ENG) pack to the right and must clear the FIXED left crest. Their base size
     scales with the --mnav-u (vw) knob, but that can't know the LABEL length — a longer translation (e.g.
     German/Spanish BOOK text) widens the cluster into the crest. So --mnav-fit (set by ageshio-nav.js,
     default 1) uniformly scales the whole cluster DOWN just enough to keep it clear of the crest at any
     width AND any translation. Type + BOOK/ENG padding + burger width + gap all ride the same multiplier. */
  :root body .ags-mnav__actions .ags-mnav__eng,
  :root body .ags-mnav__actions .ags-mnav__lang-btn,
  :root body .ags-mnav__actions .ags-mnav__book,
  :root body .ags-mnav__actions .ags-mnav__burger { font-size: calc(0.72 * var(--mnav-u) * var(--mnav-fit, 1)); }
  :root body .ags-mnav__actions .ags-mnav__book { padding-left: calc(0.6 * var(--mnav-u) * var(--mnav-fit, 1)); padding-right: calc(0.6 * var(--mnav-u) * var(--mnav-fit, 1)); }
  :root body .ags-mnav__actions .ags-mnav__eng,
  :root body .ags-mnav__actions .ags-mnav__lang-btn { padding-left: calc(0.6 * var(--mnav-u) * var(--mnav-fit, 1)); padding-right: calc(0.6 * var(--mnav-u) * var(--mnav-fit, 1)); }
  :root body .ags-mnav__actions .ags-mnav__burger { width: calc(2.5 * var(--mnav-u) * var(--mnav-fit, 1)); }
  /* CREST → keep the FULL homepage size (Simon 2026-08-17: it must NOT shrink). Inherit the shared
     112px crest (top:0, left:~10px); only lift it above the maroon bar so it's never covered. */
  :root body .ags-mnav__crest { z-index: 962; }
  /* HIDE-ON-SCROLL: the bar is at the top now → slide UP off the top (0,4,1 beats the shared slide-down). */
  :root body .ags-mnav.is-hidden .ags-mnav__actions { transform: translateY(calc(-100% - 6px)); }
  /* OPEN PANEL padding: crest + bar now both sit at the top → reserve room up top, release the old
     bottom-bar reserve so the centred link list sits true-centre. */
  :root body .ags-mnav__overlay {
    padding-top: calc(6.6 * var(--mnav-u)); padding-bottom: calc(4.4 * var(--mnav-u));
  }
  /* Corner photos on interior: ALL the chrome (crest + ENG·BOOK·☰ bar) is at the TOP, so ONLY the TR
     photo needs to dodge it — drop TR just below the bar (--mnav-photo-y ≈ 68px, clears the ~64px bar /
     ~46px pills). The BL photo has NOTHING beneath it (no bottom bar anywhere now), so it
     HUGS the bottom-left corner at the standard --mnav-pad edge inset. Lifting BL made it look like it
     was clearing a phantom bottom bar (Simon 2026-09-24: "not far down enough — buttons are top-right").
     So NOT a symmetric pair here: each photo just clears its own real chrome. Was: TR top 6.6u (dodged
     the CREST height even though the crest is top-LEFT and never overlaps the top-RIGHT photo). */
  :root body .ags-mnav { --mnav-photo-y: calc(3.8 * var(--mnav-u)); }
  :root body .ags-mnav__photo--tr { top: var(--mnav-photo-y); }
  :root body .ags-mnav__photo--bl { bottom: var(--mnav-pad); }
}

/* Navbar hide/show + transparent→solid states (toggled by ageshio-nav.js). */
.elementor-location-header {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 900;
  background: transparent;
  transition: transform .45s cubic-bezier(.4,0,.2,1), background-color .35s ease, box-shadow .35s ease;
  will-change: transform;
}
/* NO dark background on scroll — the navbar is a floating cream pill on a transparent header, so
   it stays transparent whether at rest or scrolled (dropped the old .is-solid dark fill per Simon).
   The hide-on-scroll (transform) behaviour is kept; only the colour swap is removed. */
.elementor-location-header.is-solid { background: transparent; }
/* Hide fully: the CREST overhangs the header bottom (it's taller than the 64px header + lifted up),
   so translateY(-100%) alone leaves its lower half visible. Move up by the header height PLUS a
   crest-clearance so the whole crest clears the top edge. NOTE: --ags-nav-crest lives on the child
   :is(.ags-nav2, [data-ags-nav~="bar"]) and can't be read here (custom props inherit DOWN only), so use a literal clearance
   (≥ the 112px crest) with the var as a hint fallback. */
.elementor-location-header.is-hidden { transform: translateY(calc(-100% - 120px)); }
@media (prefers-reduced-motion: reduce) {
  .elementor-location-header { transition: background-color .35s ease; }
  .elementor-location-header.is-hidden { transform: none; }
}
@media (max-width: 1023px) {

  /* =====================  NAV (header)  =====================
     Mockup: crest left · BOOK WITH US pill + hamburger right, ONE row. Hide the link columns.
     Behaviour matches desktop: the header stays position:FIXED (inherited from the base rule) so it
     rides along, goes solid past the hero, hides on scroll-down and slides back on a deliberate
     scroll-up (ageshio-nav.js + the base .is-hidden transform). We DON'T override position or
     .is-hidden here anymore — the old `position:absolute` made the bar scroll away with the hero and
     never return, and `is-hidden{transform:none}` disabled the hide/show entirely on mobile. */
  body.ags-redesign .elementor-location-header { background: transparent; }
  /* ---- MOBILE NAVBAR — scale-as-one-unit (see memory: mobile-scaling-pattern) ----------------
     ONE knob: --nav-u, a capped-vw font-size on the row. Below the cap it's pure vw, so every
     element is a CONSTANT FRACTION of the viewport → the row keeps its proportions at every width
     and can't overlap (calibrated once at the 390 reference; the ratio also clears 320). Above the
     cap it stops growing on large phones. To rescale the WHOLE navbar, change --nav-u alone.
     NOTE: children are sized `calc(N * var(--nav-u))`, NOT `Nem` — `em` resolves against each
     element's OWN font-size (which doesn't inherit --nav-u), so it wouldn't scale. calc() off the
     custom property scales regardless of font context. */
  /* HIDE FULLY on mobile. The base .is-hidden uses translateY(-100%) = only the header's OWN height
     (~60px), but the crest is a 108px-tall absolute element anchored at top:-0.5u that hangs ~58px
     BELOW the header box — so -100% left it peeking. Translate up by the crest's full bottom reach
     instead: top(-0.5·nav-u) + img height(6·nav-u) = 5.5·nav-u below the header top, + a small margin.
     --nav-u lives on .ags-hero__nav (the header's child), so recompute the same capped-vw here rather
     than relying on inheritance onto the header. Clears the crest at every width (the unit is capped). */
  :where(body.ags-redesign) .elementor-location-header.is-hidden {
    transform: translateY(calc(-100% - 6 * min(4.6vw, 18px)));
  }
  /* Nav hide-on-scroll still works on mobile (that's not "stacking") — only neutralise its full-hide
     transform under reduced-motion (matches the shared motion guard, keeps the crest on-screen). */
  @media (prefers-reduced-motion: reduce) {
    :where(body.ags-redesign) .elementor-location-header.is-hidden { transform: none; }
  }
}
@media (max-width: 1023px) {

  /* =============================================================================
     ARTICLE CARD MOBILE (.ags-c-articlecard) — SHARED, NOT page-scoped (Task M2).
     -----------------------------------------------------------------------------
     Ports the desktop overlay-card ruleset (components.css ~7255, `@media (min-width:
     1024px)`) down to <=1023: identical architecture (absolute-fill photo, bottom
     scrim, top pills, bottom-anchored title/excerpt/READ-MORE stack) — only the sizes
     move from --ags-px to clamp/px (same mechanical conversion as every other rule in
     this shared block). The aspect-ratio (405/442) is scale-free already (mockup's own
     ratio, no --ags-px), so it's copied verbatim — holds the same card shape at any
     width. Reused by BOTH the /articles Loop Grid (body.ags-page-articles below) and
     the single-post template's "Keep exploring" related-posts grid (body.single-post,
     Task M3) — kept HERE, unscoped, so both pick it up; scoped to the loop-item's own
     wrapper attributes (never a page slug) for the same reason as the desktop block:
     `.ags-c-articlecard` only ever renders AS a loop-item. */
  /* --ags-ac-cardw: the single card column's own rendered width (viewport minus the
     section's own padding-inline, same formula as that padding below) — reused by
     grid-auto-rows just below to sidestep a real Chromium layout bug (see that rule's
     comment). Keep this in sync with .ags-c-articles-header's padding-inline. */

  /* NAV READABILITY on this LIGHT page — the interior top-bar pills are cream fill + red content
     over a TRANSPARENT bar, tuned to sit over the site's DARK heroes; on the articles page's cream
     background the cream pills vanish and the elements blend in. Flip the CLOSED pills to a solid
     maroon fill + cream content so the nav reads clearly on the light bg (the OPEN cream panel keeps
     the shared red/cream flip — :not(.is-open) leaves it alone). Matches the maroon crest already
     on the left, so the whole bar reads as one cohesive maroon nav on cream. */
  :where(body.ags-page-articles) .ags-mnav:not(.is-open) .ags-mnav__actions .ags-mnav__eng,
  :where(body.ags-page-articles) .ags-mnav:not(.is-open) .ags-mnav__actions .ags-mnav__lang-btn,
  :where(body.ags-page-articles) .ags-mnav:not(.is-open) .ags-mnav__actions .ags-mnav__book,
  :where(body.ags-page-articles) .ags-mnav:not(.is-open) .ags-mnav__actions .ags-mnav__burger {
    background: var(--e-global-color-primary, var(--ags-red));
    color: var(--e-global-color-accent, var(--ags-cream));
  }
}
/* =============================================================================
   BEHAVIOURS — mechanics for the data-ags-* attributes set in Elementor → Attributes (rotate, auto-carousel,
   card-grow, swipe-row, tabs-swipe, marquee, video-cover, drift, search/load-more, embed fill, tag pills, hide-empty,
   balanced text, numbered-grid hover preview) plus text-link and rich-text details V4 has no field for, and the
   back-to-top button. Every LOOK is a V4 Class/Variable; only movement and wiring live here.
   (Regrouped 2026-10-02 from the old 18 partials by scripts/rebuild/regroup_css.js.)
   ========================================================================== */
/* =============================================================================
   BACK TO TOP button (ageshio-totop.js) — faithful to Simon's ref (2026-09-22): a
   SLIM VERTICAL pill, bottom-right, up-arrow over "TO TOP" reading upward. Light/
   outlined (cream + maroon), fills maroon on hover; fades in past ~60% scroll. Site-wide. */
.ags-totop {
  position: fixed; right: max(16px, min(2vw, 30px)); right: clamp(16px, 2vw, 30px); bottom: max(16px, min(2vw, 30px)); bottom: clamp(16px, 2vw, 30px); z-index: 900;
  display: inline-flex; flex-direction: column; align-items: center; gap: 8px;
  width: 42px; padding: 13px 0 15px; border-radius: var(--ags-radius-pill); cursor: pointer;
  background: #F7F4F2; color: var(--e-global-color-primary, var(--ags-red));
  border: 1px solid rgba(142, 30, 0, .5);
  box-shadow: 0 6px 20px rgba(20, 16, 16, .16);
  opacity: 0; visibility: hidden; transform: translateY(12px);
  transition: opacity .3s ease, transform .3s ease, visibility .3s, background .2s ease, color .2s ease, border-color .2s ease, bottom .45s cubic-bezier(.4,0,.2,1);
}
.ags-totop.is-shown { opacity: 1; visibility: visible; transform: none; }
/* (Retired 2026-09-30: the homepage's bottom action bar is gone — every page uses the top bar — so the
   back-to-top button no longer needs the homepage-only lift above it.) */
.ags-totop:hover { background: var(--e-global-color-primary, var(--ags-red)); color: #F7F4F2; border-color: transparent; }
.ags-totop:focus-visible { outline: 2px solid var(--e-global-color-primary, var(--ags-red)); outline-offset: 2px; }
.ags-totop svg { display: block; }
.ags-totop__label { writing-mode: vertical-rl; transform: rotate(180deg); font-size: 9px; letter-spacing: .18em; font-weight: 700; line-height: 1; }
@media (prefers-reduced-motion: reduce) { .ags-totop { transition: opacity .01s, visibility .01s; } }

/* =============================================================================
   LINK HOVER — underline sweep (in-from-left on enter, out-to-right on leave).
   On the nav text links (.ags-nav-group / .ags-nav-lang) + borderless .ags-textlink.
   currentColor adapts to dark (nav) and light (sections). NOT on the pill. */
/* .ags-textlink renders its own static underline — drop it so only the swept ::after shows. */

/* =============================================================================
   LINK SWEEP (V4 rebuild, D17) — hover/focus-visible underline sweep for any
   element carrying the V4 Elementor Global Class `link-sweep`. The V4 atomic
   Style-tab schema has no pseudo-element support at all (proven this session:
   a `&::after` block on a Global Class falls 100% to Custom CSS, and the
   `background-image` gradient/position/size alternative fares no better) — so
   per Simon's D17 update, the animated bar lives here in theme CSS, keyed to
   the Class name, instead of in the Class's own Custom CSS box. The `link-sweep`
   Class itself holds only native props (position:relative, text-decoration:none
   at rest and on hover). currentColor so it adapts to whatever colour the
   composed Class (e.g. `statement-link`) sets. */
.link-sweep { position: relative; }
.link-sweep::after {
  content: "";
  position: absolute;
  left: 0; right: 0; bottom: -0.15em;
  height: 1px;
  background-color: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .25s ease;
}
.link-sweep:hover::after,
.link-sweep:focus-visible::after {
  transform: scaleX(1);
}
@media (prefers-reduced-motion: reduce) {
  /* Same hover/focus underline, just without the sweep animation. */
  .link-sweep::after { transition: none; }
}

/* =============================================================================
   KANJI LINK (V4 rebuild, D24) — the linked kanji (Component "Kanji — link", Class `kanji--link`).
   The Class holds the native props (cursor, hover/focus scale). V4's Style tab has no text-shadow and
   stores this transition as Custom CSS, so — like the D17 link sweep — the legibility shadow, the white
   hover/focus glow (inherited by kanji AND word) and the motion live here, keyed to the Class name, in the
   same selector shape Elementor writes for Class states so they apply. Reduced motion: no scale, no
   transition; the glow still shows. (Simon 2026-09-30: theme CSS keyed to the Class.) */
.elementor .kanji--link {
  text-shadow: 0 1px 14px rgba(0, 0, 0, .5);
  transition: transform .3s cubic-bezier(.25, 1, .5, 1), text-shadow .3s ease;
}
.elementor .kanji--link:hover,
.elementor .kanji--link:focus-visible {
  text-shadow: 0 1px 14px rgba(0, 0, 0, .5), 0 0 24px rgba(255, 255, 255, .5);
}
@media (prefers-reduced-motion: reduce) {
  .elementor .kanji--link,
  .elementor .kanji--link:hover,
  .elementor .kanji--link:focus { transform: none; transition: none; }
}

/* =============================================================================
   SWIPE ROW (V4 rebuild, reviews/numgrid.md; D3.2) — mechanics for any element carrying
   data-ags-behaviour="swipe-row": on tablet + mobile it scrolls sideways (the page never does), snaps each
   card at its start, hides the scrollbar. The strip's LAYOUT (flex row, card width, gap) is its own V4 Global
   Classes; nothing here is a design value. The optional next-arrow (data-ags-swipe-next, driven by
   js/ageshio-swipe-row.js) fades once the row is fully scrolled. Keyboard: the strip is focusable
   (tabindex) and the arrow is a real <button>; both get a visible focus ring in the current text colour. */
@media (max-width: 1023px) {
  [data-ags-behaviour~="swipe-row"] {
    overflow-x: auto; overflow-y: hidden;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
  }
  [data-ags-behaviour~="swipe-row"]::-webkit-scrollbar { display: none; }
  [data-ags-behaviour~="swipe-row"] > * { scroll-snap-align: start; }
  /* Full-bleed strips whose first card sits on the 20px mobile guide (the strip's own inline padding) opt in
     with data-ags-swipe-inset="guide", so cards snap to the guide instead of the screen edge. */
  [data-ags-behaviour~="swipe-row"][data-ags-swipe-inset~="guide"] { scroll-padding-inline-start: var(--layout-inline-mobile, 20px); }
}
[data-ags-behaviour~="swipe-row"]:focus-visible,
[data-ags-swipe-next]:focus-visible { outline: 2px solid currentColor; outline-offset: 4px; }
[data-ags-swipe-next][data-ags-at-end] { opacity: .35; cursor: default; }

/* =============================================================================
   VIDEO COVER (V4 rebuild, D12; js/ageshio-video-cover.js) — mechanics only. Until the visitor clicks the
   cover, the native e-youtube player is display:none, so Elementor's lazy-load never starts it and nothing
   from YouTube loads. After the click the cover goes and the player shows (Elementor loads + autoplays it).
   The cover's look (poster, play icon, sizes) is all V4 Classes. */
[data-ags-behaviour~="video-cover"]:not([data-ags-playing]) .e-youtube-base { display: none; }
[data-ags-behaviour~="video-cover"][data-ags-playing] [data-ags-video-play] { display: none; }
[data-ags-behaviour~="video-cover"][data-ags-playing] [data-ags-video-poster] { display: none; }
[data-ags-video-play]:focus-visible { outline: 2px solid currentColor; outline-offset: 4px; }

/* =============================================================================
   MARQUEE (V4 rebuild, I2/I3; D3.2) — mechanics for the scrolling title band. The track (the element with
   data-ags-behaviour="marquee") holds ONE unit in Elementor; js/ageshio-marquee.js clones it to 3 identical units
   and sets data-ags-marquee-ready. The track is as wide as its content and moves left by exactly one unit (-100%/3)
   per loop, so it wraps seamlessly. The text never wraps (a ticker, I3). Speed = the V4 Variable
   `marquee-duration` (Variables panel; default 22s). Looks (sizes, gaps, colours) = V4 Classes. */
[data-ags-behaviour~="marquee"] { width: -webkit-max-content; width: -moz-max-content; width: max-content; }
[data-ags-behaviour~="marquee"][data-ags-marquee-ready] {
  animation: ags-marquee-scroll var(--marquee-duration, 22s) linear infinite;
}
[data-ags-behaviour~="marquee"] * { white-space: nowrap; }
@keyframes ags-marquee-scroll { from { transform: translateX(0); } to { transform: translateX(calc(-100% / 3)); } }
@media (prefers-reduced-motion: reduce) {
  [data-ags-behaviour~="marquee"],
  [data-ags-behaviour~="marquee"][data-ags-marquee-ready] { animation: none; }
}

/* =============================================================================
   AUTO CAROUSEL (V4 rebuild, X3; js/ageshio-auto-carousel.js) — mechanics only, tablet + mobile. The element's
   own V4 Classes give the frame its size/shape (e.g. square, rounded); here the slides stack in that frame and
   glide by --ags-off, plus the injected progress line (colours = the V4 colour Variables). */
@media (max-width: 1023px) {
  [data-ags-carousel="on"] {
    position: relative; display: block; overflow: hidden;
    touch-action: pan-y; -webkit-user-select: none; -moz-user-select: none; user-select: none;
  }
  /* !important: a slide may also carry a desktop-only V4 Interaction (e.g. the homepage Gallery drift); Elementor's
     runtime ignores "excluded breakpoints" for Pro scroll interactions (⚑ bug, 4.3.x) and writes an inline transform
     + transition:none — the carousel position must win on tablet/mobile. */
  [data-ags-carousel="on"] > :not(.ags-ind) {
    position: absolute; top: 0; right: 0; bottom: 0; left: 0; width: 100%; height: 100%; margin: 0;
    transform: translateX(calc(var(--ags-off, 2) * 100%)) !important;
    transition: transform .5s ease !important;
  }
  [data-ags-carousel="on"] > .is-active { z-index: 2; }
  [data-ags-carousel="on"] img { -webkit-user-drag: none; }
  [data-ags-carousel="on"] > .ags-ind { position: absolute; top: 12px; left: 14px; right: 14px; z-index: 3; }
  .ags-ind__track { position: relative; width: 100%; height: 2px; border-radius: 2px; overflow: hidden; cursor: pointer;
    background: color-mix(in srgb, var(--color-cream, #F7F4F2) 28%, transparent); }
  .ags-ind__fill { position: absolute; top: 0; right: auto; bottom: 0; left: 0; width: calc(var(--ags-ind-p, 0) * 100%);
    background: var(--color-cream, #F7F4F2); transition: width .6s cubic-bezier(.5, 0, .15, 1); }
}
@media (max-width: 1023px) and (prefers-reduced-motion: reduce) {
  [data-ags-carousel="on"] > :not(.ags-ind), .ags-ind__fill { transition: none !important; }
}

/* =============================================================================
   RICH-TEXT LISTS (V4 rebuild, D25) — bullet lists typed inside a V4 Paragraph (Tag = span — a list inside <p>
   is thrown out by the browser and loses its styles). V4 Classes can't reach elements INSIDE the text, so the
   list's own spacing is this site-wide typographic default (live's values): no outer margin, 1.1em indent,
   0.35em between items on desktop, none on tablet/mobile. Size/colour/font still come from the V4 Class. */
.elementor .e-paragraph-base ul,
.elementor .e-paragraph-base ol { margin: 0; padding-left: 1.1em; }
.elementor .e-paragraph-base li + li { margin-top: .35em; }
@media (max-width: 1023px) { .elementor .e-paragraph-base li + li { margin-top: 0; } }
/* End-aligned lists (Overlay's top-right text box, desktop): bullets sit inside the text, no indent (live). */
@media (min-width: 1024px) {
  .elementor .overlay-text--b ul, .elementor .overlay-text--b ol { padding-left: 0; list-style-position: inside; }
}

/* =============================================================================
   CARD GROW (V4 rebuild, I13; js/ageshio-card-grow.js) — desktop state mechanics for a row with
   data-ags-behaviour="card-grow". One card (.is-active, toggled by the script) grows to 3.4× the others' share,
   capped at 1.5 × the row height (--ags-card-max) so its cover photo never zooms; the others are slivers that show
   only their kanji (parts marked data-ags-card-reveal are hidden, the caption row centres). Card LOOKS (radius,
   sizes, colours, the photo) are V4 Classes. Tablet/mobile: the same row is an auto-carousel (all parts shown). */
@media (min-width: 1024px) {
  /* The cap sits on EVERY card, not only the open one: if only .is-active carried it, a closing card would lose its
     cap instantly while its flex-grow is still animating down, balloon wider for a moment, slide under the pointer
     and re-open (hover flicker between cards — seen on the shorter Masters rows, where the cap is always reached). */
  [data-ags-behaviour~="card-grow"] > * { flex: 1 1 0; min-width: 0; max-width: var(--ags-card-max, none); cursor: pointer; transition: flex-grow .5s cubic-bezier(.4, 0, .2, 1); }
  [data-ags-behaviour~="card-grow"] > .is-active { flex-grow: 3.4; }
  /* The kanji never wraps or shrinks: while a card is still narrow (just starting to open) its name/rank already
     share the caption row and would squeeze the kanji onto two lines. The revealed parts stay on one line and clip. */
  [data-ags-behaviour~="card-grow"] [data-ags-card-caption] > :not([data-ags-card-reveal]) { flex-shrink: 0; white-space: nowrap; }
  [data-ags-behaviour~="card-grow"] [data-ags-card-reveal] { white-space: nowrap; overflow: hidden; }
  [data-ags-behaviour~="card-grow"] > :not(.is-active) [data-ags-card-reveal] { display: none; }
  [data-ags-behaviour~="card-grow"] > :not(.is-active) [data-ags-card-caption] { justify-content: center; }
  [data-ags-behaviour~="card-grow"] > .is-active [data-ags-card-reveal] { animation: ags-card-reveal .35s ease .15s both; }
}
@keyframes ags-card-reveal { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }
@media (prefers-reduced-motion: reduce) {
  [data-ags-behaviour~="card-grow"] > * { transition: none; }
  [data-ags-behaviour~="card-grow"] > .is-active [data-ags-card-reveal] { animation: none; }
}
[data-ags-embed~="fill"] iframe { display: block; width: 100%; height: 100%; border: 0; }

/* BALANCED TEXT (V4 rebuild, D22 A12) — a heading carrying data-ags-balance="1" evens out its lines when it wraps
   (no lonely last word in any language). text-wrap isn't a V4 Style field. data-ags-balance="mobile" = only on the
   phone/tablet layout (≤1023, D1d), e.g. the numbered-grid swipe row whose labels wrap unevenly on wide phones while the
   signed-off desktop line breaks stay (2026-10-07). text-wrap is inherited, so it can sit on the row around the labels. */
[data-ags-balance]:not([data-ags-balance="mobile"]) { text-wrap: balance; }
@media (max-width: 1023px) { [data-ags-balance="mobile"] { text-wrap: balance; } }

/* JAPANESE LINE BREAKING (2026-10-05, TranslatePress /ja/) — Japanese has no spaces, so browsers break lines anywhere:
   a lone last character (直接学 / ぶ), a word cut in half (ブルーゾ / ーン), 「ー」 or 「。」 starting a line. Japanese pages
   only (TranslatePress sets <html lang="ja">): break between phrases (auto-phrase, Chrome/Edge), follow the strict
   Japanese line-start rules, and avoid a lonely last line. English and the European languages are untouched. */
html:lang(ja) body { word-break: auto-phrase; line-break: strict; text-wrap: pretty; }

/* LONG COMPOUND WORDS (2026-10-05, TranslatePress /de/ /nl/) — German and Dutch headings can hold one word wider than
   its column (About accordion "Optionale Zusatzmodule" at 1024–1440 → sideways scroll). Let headings hyphenate at a
   syllable when a word doesn't fit (only then); `anywhere` (not break-word) so a flex-item heading can also shrink to its column — hyphen points still come first. English/Spanish/Japanese untouched. */
html:lang(de) h1 { -webkit-hyphens: auto; hyphens: auto; word-wrap: anywhere; overflow-wrap: anywhere; }
html:lang(de) h2 { -webkit-hyphens: auto; hyphens: auto; word-wrap: anywhere; overflow-wrap: anywhere; }
html:lang(de) h3 { -webkit-hyphens: auto; hyphens: auto; word-wrap: anywhere; overflow-wrap: anywhere; }
html:lang(de) h4 { -webkit-hyphens: auto; hyphens: auto; word-wrap: anywhere; overflow-wrap: anywhere; }
html:lang(de) h5 { -webkit-hyphens: auto; hyphens: auto; word-wrap: anywhere; overflow-wrap: anywhere; }
html:lang(de) h6 { -webkit-hyphens: auto; hyphens: auto; word-wrap: anywhere; overflow-wrap: anywhere; }
html:lang(nl) h1 { -webkit-hyphens: auto; hyphens: auto; word-wrap: anywhere; overflow-wrap: anywhere; }
html:lang(nl) h2 { -webkit-hyphens: auto; hyphens: auto; word-wrap: anywhere; overflow-wrap: anywhere; }
html:lang(nl) h3 { -webkit-hyphens: auto; hyphens: auto; word-wrap: anywhere; overflow-wrap: anywhere; }
html:lang(nl) h4 { -webkit-hyphens: auto; hyphens: auto; word-wrap: anywhere; overflow-wrap: anywhere; }
html:lang(nl) h5 { -webkit-hyphens: auto; hyphens: auto; word-wrap: anywhere; overflow-wrap: anywhere; }
html:lang(nl) h6 { -webkit-hyphens: auto; hyphens: auto; word-wrap: anywhere; overflow-wrap: anywhere; }
html:is(:lang(de), :lang(nl)) :is(h1, h2, h3, h4, h5, h6) { -webkit-hyphens: auto; hyphens: auto; word-wrap: anywhere; overflow-wrap: anywhere; }

/* LINKS INSIDE TEXT (2026-10-01 refinement) — an email, phone or web link written inside a V4 Paragraph (FAQ,
   Terms, Privacy, legacy pages, any future body copy) reads as a link: underlined in the text's own colour, brand
   colour on hover/focus. Only bare inline links — elements that ARE links (buttons, cards) carry classes and are
   untouched. */
.e-paragraph-base a:not([class]) { color: inherit; -webkit-text-decoration: underline; text-decoration: underline; text-decoration-thickness: 1px; text-underline-offset: 0.18em; transition: color .2s; }
.e-paragraph-base a:not([class]):hover, .e-paragraph-base a:not([class]):focus-visible { color: var(--color-brand); }
.e-paragraph-base a[href^="tel:"]:not([class]) { white-space: nowrap; }

/* TAG PILLS + CLAMP (V4 rebuild, D1 Article Card). A Paragraph carrying data-ags-tags="1" holds the
   [ags_post_tags] shortcode output (one <span> per WP tag; V4 strips classes from dynamic text). Font, size and
   colour come from the Paragraph's V4 Class (postcard-taglist); only the pill SHAPE is here, in em so it scales
   with that Class and matches the postcard-tag Class (date pill, About postcards). data-ags-clamp="2|3" trims
   text to N lines (line-clamp isn't a V4 Style field). */
[data-ags-tags] > span { display: inline-block; padding: 0.3334em 1em; border: 1px solid currentColor; border-radius: 999px; background-color: rgba(20, 20, 20, 0.3); line-height: 1; }
@media (max-width: 1023px) { [data-ags-tags] > span { padding: 0.2728em 0.8182em; } }
/* data-ags-tags="outline" = the single post's outline pills (no glass fill, roomier). */
[data-ags-tags="outline"] > span { padding: 0.6154em 1.3846em; background-color: transparent; }
/* data-ags-hide-empty: an element whose dynamic text came back empty (e.g. a post with no kanji title) takes no space. */
[data-ags-hide-empty]:empty { display: none; }
[data-ags-clamp] { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden; }
[data-ags-clamp="3"] { -webkit-line-clamp: 3; }

/* =============================================================================
   DRIFT + SEARCH (V4 rebuild, D28 Q8 / D22 A10; js/ageshio-drift.js, js/ageshio-search.js) — mechanics only.
   Drift row: a real horizontal scroller with a hidden scrollbar (swipe/trackpad work); the script moves it.
   Search: filtered-out cards hide; the empty message shows when nothing matches; the native input loses its
   form chrome and its placeholder is a faint tint of the input's own colour (V4 has no placeholder field). */
[data-ags-behaviour~="drift"] { overflow-x: auto; overflow-y: hidden; scrollbar-width: none; overscroll-behavior-x: contain; -webkit-overflow-scrolling: touch; }
[data-ags-behaviour~="drift"]::-webkit-scrollbar { display: none; }
[data-ags-behaviour~="drift"] > * { flex-shrink: 0; }
[data-ags-behaviour~="drift"][data-ags-filtering] > [data-ags-clone] { display: none; }
[data-ags-hidden], [data-ags-paged-out] { display: none !important; }
/* A hidden card inside a native V4 Loop takes its loop item (the grid cell) with it. */
[role="listitem"]:has(> [data-ags-hidden]), [role="listitem"]:has(> [data-ags-paged-out]) { display: none !important; }
[data-ags-search-empty] { display: none; }
[data-ags-search-scope][data-ags-noresults] [data-ags-search-empty] { display: block; }
/* The chosen tag pill reads as pressed: filled with its own border colour, text in the page background. */
[data-ags-search-tag][aria-pressed="true"] { background-color: var(--color-brand); border-color: var(--color-brand); color: var(--color-cream); }
input[data-ags-search] { -webkit-appearance: none; -moz-appearance: none; appearance: none; outline: none; box-shadow: none; caret-color: currentColor; field-sizing: content; }
input[data-ags-search]::-moz-placeholder { color: currentColor; opacity: .22; }
input[data-ags-search]::placeholder { color: currentColor; opacity: .22; }
input[data-ags-search]:focus-visible { outline: 2px solid currentColor; outline-offset: 6px; }

/* =============================================================================
   ROTATE (V4 rebuild, D28 Q3; js/ageshio-rotate.js) — mechanics only. While the script runs
   ([data-ags-rotating]) the children share ONE grid cell (the box keeps the tallest child's height, so nothing
   below jumps) and only .is-active is shown. The swap is sequential, never a cross-fade (the two never overlap):
   the previous one fades out with a small lift (.35s), THEN the new one fades in with a small rise (.5s, delayed
   .35s). Every look (sizes, colours, fonts) is the children's own V4 Classes. Reduced motion: no motion. */
html [data-ags-behaviour~="rotate"][data-ags-rotating] { display: grid; grid-template-columns: minmax(0, 1fr); justify-items: stretch; align-items: center; }
html [data-ags-behaviour~="rotate"][data-ags-rotating] > * {
  grid-area: 1 / 1; opacity: 0; visibility: hidden; transform: translateY(8px);
  transition: opacity .35s ease, transform .35s ease, visibility 0s linear .35s;
}
html [data-ags-behaviour~="rotate"][data-ags-rotating] > .was-active { transform: translateY(-8px); }
html [data-ags-behaviour~="rotate"][data-ags-rotating] > .is-active {
  opacity: 1; visibility: visible; transform: none;
  transition: opacity .5s ease .35s, transform .5s ease .35s, visibility 0s linear .35s;
}
@media (prefers-reduced-motion: reduce) {
  html [data-ags-behaviour~="rotate"][data-ags-rotating] > * { transition: none; transform: none; }
}
/* Desktop-only rotation (Hero tagline): below 1024 only the first line shows, statically. */
@media (max-width: 1023px) {
  html [data-ags-behaviour~="rotate"][data-ags-rotate-on="desktop"] > * + * { display: none; }
}
/* Elementor shows/hides panels with an inline display:none (Alpine x-show) and leaves the hidden attribute stale,
   so the selected panel is read from its native .e--selected class and the inline display is overridden. */
[data-ags-behaviour~="tabs-swipe"] [role="tabpanel"] { display: contents !important; }
[data-ags-behaviour~="tabs-swipe"] [role="tabpanel"] > * { position: relative; z-index: 1; opacity: 0; visibility: hidden; pointer-events: none; transition: opacity .35s ease, visibility 0s linear .35s; }
[data-ags-behaviour~="tabs-swipe"] [role="tabpanel"] > :has(img) { transition: opacity 0s linear .6s, visibility 0s linear .6s; }
[data-ags-behaviour~="tabs-swipe"] [role="tabpanel"].e--selected > * { z-index: 2; opacity: 1; visibility: visible; pointer-events: auto; transition: opacity .6s ease; }
/* The tab menu (on phones: the experience title + progress bar over the photo card) stays above the panels. */
[data-ags-behaviour~="tabs-swipe"] [role="tablist"] { position: relative; z-index: 3; }
@keyframes ags-tab-in { from { opacity: 0; } to { opacity: 1; } }
[data-ags-behaviour~="tabs-swipe"] [role="tab"]:focus-visible { outline: 2px solid currentColor; outline-offset: 4px; }
@media (max-width: 1023px) {
  [data-ags-behaviour~="tabs-swipe"] [role="tablist"] { touch-action: pan-y; -webkit-user-select: none; -moz-user-select: none; user-select: none; cursor: pointer; }
  [data-ags-tabs-swapped] [role="tab"].e--selected { animation: ags-tab-in .45s ease both; }
  .ags-tabs-ind { position: absolute; left: 6%; right: 6%; bottom: max(4px, min(1.5vw, 8px)); bottom: clamp(4px, 1.5vw, 8px); z-index: 2; display: flex; gap: 6px; }
  .ags-tabs-ind__seg { flex: 1 1 0; height: 19px; padding: 8px 0; border: 0; border-radius: 2px; cursor: pointer;
    background: color-mix(in srgb, var(--color-cream, #F7F4F2) 35%, transparent); background-clip: content-box;
    transition: background-color .4s ease; }
  .ags-tabs-ind__seg.is-active { background-color: var(--color-cream, #F7F4F2); }
  /* Experience List card title: the soft shadow that lifts it off the photo (text-shadow isn't a V4 Style
     property — D25, same pattern as the kanji-link glow). */
  .explist-tab.e--selected { text-shadow: 0 2px 16px rgba(0, 0, 0, .45); }
}
@media (prefers-reduced-motion: reduce) {
  [data-ags-behaviour~="tabs-swipe"] [role="tabpanel"] > *, [data-ags-behaviour~="tabs-swipe"] [role="tabpanel"].e--selected > *,
  [data-ags-tabs-swapped] [role="tab"].e--selected { animation: none; transition: none; }
  .ags-tabs-ind__seg { transition: none; }
}
@media (max-width: 1023px) {
  /* ONE KNOB, scale-as-one-unit (see [[wp-redesign-mobile-scaling-pattern]]). The whole program-slide
     composition scales off a single unit --pu: every font is `calc(N * var(--pu))` and every text
     block is anchored on ONE side with its WIDTH also `calc(N * var(--pu))` — so blockwidth/fontsize
     is CONSTANT for any value of --pu, which freezes the reference line-wrapping from 320→1023 (it no
     longer drifts the way a `clamp(px,vw,px)` font + viewport-% block did).
       --pu = min(1vw, 0.46svh). The 0.46 is the mockup's aspect ratio (390/844): at the 390 phone
     reference both terms equal 1% of width, so the composition matches the mockup exactly; on a
     PROPORTIONALLY SHORTER (squarer) viewport like iPad Pro 11 the 0.46svh term wins, so the whole
     composition scales down to FIT the height instead of ballooning off the width and overlapping.
     Defined on the sections too so it resolves even before the carousel JS wraps them. */
  /* Title column — LEFT-aligned, hugging the gutter (reference sits it slightly LEFT of the card).
     Kill the column's own e-con padding so the title reads from the band edge, not indented. */

  /* ---- Featured card / carousel frame -----------------------------------------------------
     The row's INNER becomes the card FRAME: a centred near-square box. Without JS it holds the
     single server-featured (.is-active) card in flow; with JS (.is-carousel) all 5 cards stack
     absolute inside it and cross-fade. */
  /* e-con-FULL equivalent (Task 4, 2026-09-24, defensive/additive): a future repeater widget's row
     renders e-con-full (no .e-con-inner) — there's no separate outer/inner to split the two rules
     above across, so merge them onto the row itself directly (mirrors About/History's own native
     e-con-full mobile treatment, e.g. ~7319 below). Mutually exclusive with the two rules above (a
     row is boxed OR full, never both) — doesn't touch the current boxed homepage row. JS already
     falls back to the row when no .e-con-inner exists (ageshio-masters-mobile.js:
     `row.querySelector('.e-con-inner') || row`). */
  /* Portrait fill + scrim + caption apply to EVERY mobile card (in carousel mode any of the 5 can
     be the visible one). The desktop rules that pin the image are gated ≥1024, so re-establish. */

  /* Fallback (JS off / carousel bailed): the single featured card fills the frame in flow. */

  /* Carousel mode (JS present) — all 5 cards stacked absolute in the frame, sliding. */
  /* e-con-FULL equivalent of the two carousel-frame rules below (Task 4, additive): no separate
     .e-con-inner to split height vs. clip/touch across, so combine them on the row itself. */
  /* The STATIC card slot is the rounded viewport: its overflow:hidden + border-radius clips the sliding
     (transform-composited) cards to rounded corners throughout. Rounding the moving card itself doesn't
     survive — Chromium drops a rounded clip on the layer it's animating; a NON-animating ancestor's clip
     is reliably applied to composited descendants. position:relative also = containing block for the arrows. */
  /* SHARED slide animation (Task 4, 2026-09-24) — promoted from History's clean implementation; the ONE
     rule for the masters carousel's transform+transition, reused verbatim by homepage/About/History (all
     three used the exact same selector shape + --ags-off var already; only the surrounding layout
     properties differ per page and stay in the page-scoped rules around this one). Was previously
     redeclared 3× — the homepage's copy also fought a dead legacy cross-fade rule (opacity/visibility
     .6s transition, removed above) left behind by an earlier cross-fade design that was superseded by
     this slide; now a single source of truth so all three carousels animate identically. Deliberately
     UNSCOPED + low-specificity (2 classes) — see the blanket-reset comment above for why that's safe. */

  /* Shared carousel indicator — one filling line, reused by every carousel (B4). */
  .ags-ind__track { position: relative; width: 100%; height: 2px; border-radius: 2px;
    background: rgba(247,244,242,0.28); overflow: hidden; cursor: pointer; }
  .ags-ind__fill { position: absolute; top: 0; right: auto; bottom: 0; left: 0; width: calc(var(--ags-ind-p, 0) * 100%);
    background: var(--e-global-color-accent, var(--ags-cream)); transition: width .6s cubic-bezier(.5,0,.15,1); }
  @media (prefers-reduced-motion: reduce) { .ags-ind__fill { transition: none; } }
}
/* =============================================================================
   INTERIOR COMPONENTS (ags-c-*) — reusable, Navigator-named, template-saved
   components for the ~30 interior pages (sub-project C). First page: Okinawan
   Group Tours. Same CSS-ownership model as the homepage: Elementor owns structure
   + content + Navigator names; THIS owns design/layout/animation, keyed to the
   ags-c-* classes. Colours via Global Colors, type via Kit globals.
   Responsive: shared here; desktop ≥1024 + mobile ≤1023 blocks per component.

   CONVENTION — interior-page heading/text SIZE overrides MUST target the rendered
   element with page-type scope to beat Elementor's per-element global rule (0,4,0):
     .elementor-page [data-elementor-type="wp-page"] .ags-c-X .elementor-heading-title { font-size: ... }
   (text-editor globals sit on the widget → target .elementor-widget-container instead.)
   Do NOT use body.ags-redesign here — that class is FRONT-PAGE-only (functions.php),
   absent on interior pages, so it silently fails. Tasks 4–6 reuse this pattern.
   ============================================================================= */

/* Neutral placeholder box — stands in for an unsourced photo (no broken-img glyph). The slot is a
   REAL Elementor image widget pointing at Elementor's placeholder.png (so it's editable from
   Navigator/Media Library); these rules paint the neutral #d8d0cb box over it and HIDE the
   placeholder pixels until a real image is set. Once an editor picks an image, its src no longer
   matches [src*="placeholder.png"], so the real photo shows and the mask lifts automatically. */

/* ── "What's included" HOVER-REVEAL — floating preview near the hovered item (Simon 2026-09-23) ──────
   Show a photo on hover for each of the 8 inclusions. DESKTOP: a compact preview card floats in the void
   just LEFT of the grid and VERTICALLY TRACKS whichever item you hover — it slides to that item's level
   and cross-fades to its photo, so the image reads as belonging to the item you're on (ageshio-numgrid.js
   reads the live layout → the position + size stay right across desktop widths). Photos are per-ITEM
   Elementor Image widgets (.ags-c-numgrid__img--N, client-editable — one per inclusion); the JS uses each
   one's src, so they're the single editable source. A GREY card + cream number badge stand in until real
   photos are set. Scoped to Group Tour for now (the feedback); History's "What's included" can inherit it
   later. MOBILE (≤1023) is deferred — the per-item photos will render inline in the mobile pass. */
.ags-c-numgrid__img,
.numgrid-photo { display: none; }   /* the per-item widget is the editable SOURCE; the preview shows it */

@media (min-width: 1024px) {
  /* .ags-has-photo-preview is added by ageshio-numgrid.js to numgrids that have per-item photo slots
     (opt-in, client-added), so these rules never touch numgrids without the feature. Scoped to
     :is(.ags-c-numgrid, [data-ags-behaviour="numgrid-preview"]) since B4c (2026-09-29) — the V4 rebuild's
     numbered grid (GT/VIP, page section built from Global Classes, not this old BEM markup) carries the
     behaviour attribute instead of the class; the JS (ageshio-numgrid.js) drives both the same way. */
  .ags-c-numgrid.ags-has-photo-preview, .ags-has-photo-preview[data-ags-behaviour="numgrid-preview"] { position: relative; }
  :is(.ags-c-numgrid, [data-ags-behaviour="numgrid-preview"]).ags-has-photo-preview { position: relative; }
  /* grid + kanji stay above the floating preview so a photo can never cover the numbers/labels/旅行 */
  .ags-c-numgrid.ags-has-photo-preview .ags-c-numgrid__grid, .ags-c-numgrid.ags-has-photo-preview .numgrid-grid, .ags-has-photo-preview[data-ags-behaviour="numgrid-preview"] .ags-c-numgrid__grid, .ags-has-photo-preview[data-ags-behaviour="numgrid-preview"] .numgrid-grid { position: relative; z-index: 3; }
  :is(.ags-c-numgrid, [data-ags-behaviour="numgrid-preview"]).ags-has-photo-preview :is(.ags-c-numgrid__grid, .numgrid-grid) { position: relative; z-index: 3; }
  .ags-c-numgrid.ags-has-photo-preview .ags-c-numgrid__eyebrow, .ags-c-numgrid.ags-has-photo-preview .numgrid-eyebrow, .ags-has-photo-preview[data-ags-behaviour="numgrid-preview"] .ags-c-numgrid__eyebrow, .ags-has-photo-preview[data-ags-behaviour="numgrid-preview"] .numgrid-eyebrow { position: relative; z-index: 4; }
  :is(.ags-c-numgrid, [data-ags-behaviour="numgrid-preview"]).ags-has-photo-preview :is(.ags-c-numgrid__eyebrow, .numgrid-eyebrow) { position: relative; z-index: 4; }

  /* Preview card — left edge on the section guide, top+height matched to the grid rows (set live by
     ageshio-numgrid.js). Flat (no shadow, per Simon); same spot for every item → hover cross-fades. */
  .ags-c-numgrid__preview {
    position: absolute; z-index: 2; pointer-events: none;   /* left/width/top/height set live by JS */
    border-radius: 8px; overflow: hidden;
    background: #D9D5D0;                        /* grey placeholder until a photo loads */
    opacity: 0; transform: translateY(8px);
    transition: opacity .28s ease, transform .28s ease;
  }
  .ags-c-numgrid__preview.is-visible { opacity: 1; transform: none; }
  /* two stacked panes cross-fade between items */
  .ags-c-numgrid__pane {
    position: absolute; top: 0; right: 0; bottom: 0; left: 0; background-size: cover; background-position: center;
    opacity: 0; transition: opacity .28s ease;
  }
  .ags-c-numgrid__pane.is-active { opacity: 1; }
  /* cream number badge — placeholder legibility while the slots are grey (drop/restyle once photos land) */
  .ags-c-numgrid__prevnum {
    position: absolute; top: 0; right: 0; bottom: 0; left: 0; z-index: 1; display: flex; align-items: center; justify-content: center;
    font-family: var(--e-global-typography-accent-font-family, "Red Rose"), sans-serif;
    font-weight: 700; font-size: calc(46 * var(--ags-hpx)); line-height: 1;
    color: var(--e-global-color-accent, var(--ags-cream)); opacity: .5; text-shadow: 0 2px 12px rgba(0, 0, 0, .35);
  }
  /* gentle focus cue: dim the other items while hovering the list, so the active one reads */
  .ags-c-numgrid.ags-has-photo-preview .ags-c-numgrid__item, .ags-c-numgrid.ags-has-photo-preview .numgrid-item, .ags-has-photo-preview[data-ags-behaviour="numgrid-preview"] .ags-c-numgrid__item, .ags-has-photo-preview[data-ags-behaviour="numgrid-preview"] .numgrid-item { transition: opacity .2s ease; }
  :is(.ags-c-numgrid, [data-ags-behaviour="numgrid-preview"]).ags-has-photo-preview :is(.ags-c-numgrid__item, .numgrid-item) { transition: opacity .2s ease; }
  .ags-c-numgrid.ags-has-photo-preview .ags-c-numgrid__grid:hover .ags-c-numgrid__item:not(:hover), .ags-c-numgrid.ags-has-photo-preview .ags-c-numgrid__grid:hover .numgrid-item:not(:hover), .ags-c-numgrid.ags-has-photo-preview .numgrid-grid:hover .ags-c-numgrid__item:not(:hover), .ags-c-numgrid.ags-has-photo-preview .numgrid-grid:hover .numgrid-item:not(:hover), .ags-has-photo-preview[data-ags-behaviour="numgrid-preview"] .ags-c-numgrid__grid:hover .ags-c-numgrid__item:not(:hover), .ags-has-photo-preview[data-ags-behaviour="numgrid-preview"] .ags-c-numgrid__grid:hover .numgrid-item:not(:hover), .ags-has-photo-preview[data-ags-behaviour="numgrid-preview"] .numgrid-grid:hover .ags-c-numgrid__item:not(:hover), .ags-has-photo-preview[data-ags-behaviour="numgrid-preview"] .numgrid-grid:hover .numgrid-item:not(:hover) { opacity: .55; }
  :is(.ags-c-numgrid, [data-ags-behaviour="numgrid-preview"]).ags-has-photo-preview :is(.ags-c-numgrid__grid, .numgrid-grid):hover :is(.ags-c-numgrid__item, .numgrid-item):not(:hover) { opacity: .55; }
}
@media (min-width: 1024px) and (prefers-reduced-motion: reduce) {
  .ags-c-numgrid__preview { transition: opacity .01s linear; }
  .ags-c-numgrid__pane { transition: opacity .01s linear; }
}
@keyframes ags-c-marquee-scroll { from { transform: translateX(0); } to { transform: translateX(calc(-100% / var(--marquee-copies, 3))); } }

/* ───────────────────────────────────────────────────────────────────────────────────────────────────────────
   ACCORDION (V4 rebuild, D29; js/ageshio-accordion.js) — mechanics only, every native V4 Accordion.
   Elementor 4.3 animates ::details-content with block-size 0 → auto + overflow:hidden, which (a) clips panels
   that are wider than their item — ours are on purpose (acc-content 34em vs the acc-item divider width), so the
   text lost its right edge on desktop — and (b) only animates where `interpolate-size` exists. Here the panel is
   never clipped sideways; the theme script animates the item's own height instead (all browsers). While it moves
   the item clips VERTICALLY only (overflow-y: clip + overflow-x: visible is a valid pair, unlike hidden), so wide
   text stays visible during the animation too. Looks stay on the V4 Classes. */
html .e-accordion-base .e-accordion-item-base::details-content { height: auto; overflow: visible; transition: none; }
html .e-accordion-base .e-accordion-item-base[data-ags-acc-anim] { overflow-x: visible; overflow-y: hidden; overflow-y: clip; box-sizing: border-box; }

/* ───────────────────────────────────────────────────────────────────────────────────────────────────────────
   AUTO NUMBER (V4 rebuild, D29) — mechanics only. A list with the attribute data-ags-numbered (Elementor →
   Attributes) counts its direct children (the item Components); an element inside an item carrying data-ags-num
   (set in the item Component's master, its text left empty) shows the item's position + "." — so reordering,
   duplicating or deleting items in the Navigator always renumbers. The numeral's look (font, size, colour) stays
   the number element's own V4 Class (numgrid-num, approach-num, wwd-num). Used by: numbered grid (GT, VIP),
   About Approach, About What we do. */
html [data-ags-numbered] { counter-reset: ags-num; }
html [data-ags-numbered] > * { counter-increment: ags-num; }
html [data-ags-numbered] > * [data-ags-num]::before { content: counter(ags-num) "."; }

/* EMPTY POSTCARD TAG (D29) — the Postcard Component has 3 Tag fields; a field left empty shows no pill. */
html .postcard-tag:empty { display: none; }

/* SECTION PAGES (D29 / sections spec) — pills no card uses are hidden; the Upcoming empty box shows only when no
   upcoming card is in the section (pure CSS :has, so it also updates when ageshio-events.js moves cards).
   The JS puts the attribute on the pill's Loop CELL ([data-element_type="e-collection-loop-item"], the native
   V4 wrapper — a data attribute here, not a class; falls back to the button itself if that wrapper is ever
   missing) so the whole flex slot collapses, not just the button inside it (else the empty cell would still
   take its gap in the pill row). !important: Elementor's atomic .e-con/.e-button-base rules (2 classes, e.g.
   .elementor .e-button-base) beat this single-element+attribute selector on specificity alone — same reason
   [data-ags-hidden] below is !important. */
html [data-ags-pill-unused] { display: none !important; }
html [data-ags-events-upcoming] [data-ags-events-empty] { display: none; }
html [data-ags-events-upcoming]:not(:has([data-ags-search-item])) [data-ags-events-empty] { display: block; }

/* =============================================================================
   EDITOR GUARD RAILS (guide review 2026-10-02, items 5 + Q4) — visible ONLY inside the Elementor editor
   (.elementor-editor-active is the editor preview's body class); the live site never matches these rules.
   1. Accordion items added with "+" arrive without the site's Classes (acc-item / faq-item) and look unstyled:
      outline them and say what to do. The same for an icon slot that lost acc-icon / faq-icon.
   2. Elements whose behaviour attribute has an unknown value (js/ageshio-editor-guards.js sets data-ags-guard).
   ========================================================================== */
.elementor-editor-active .e-accordion-base > .e-accordion-item-base:not(.acc-item):not(.faq-item),
.elementor-editor-active .acc-item .e-accordion-item-icon-base:not(.acc-icon):not(.faq-icon),
.elementor-editor-active .faq-item .e-accordion-item-icon-base:not(.acc-icon):not(.faq-icon),
.elementor-editor-active [data-ags-guard] {
  outline: 2px dashed #d00000 !important;
  outline-offset: -2px;
}
.elementor-editor-active .e-accordion-base > .e-accordion-item-base:not(.acc-item):not(.faq-item),
.elementor-editor-active :is(.acc-item, .faq-item) .e-accordion-item-icon-base:not(.acc-icon):not(.faq-icon),
.elementor-editor-active [data-ags-guard] {
  outline: 2px dashed #d00000 !important;
  outline-offset: -2px;
}
.elementor-editor-active .e-accordion-base > .e-accordion-item-base:not(.acc-item):not(.faq-item) { position: relative; }
.elementor-editor-active .e-accordion-base > .e-accordion-item-base:not(.acc-item):not(.faq-item)::after,
.elementor-editor-active [data-ags-guard]::after {
  position: absolute; top: 0; left: 0; right: auto; bottom: auto; z-index: 9999;
  width: -webkit-max-content;
  width: -moz-max-content;
  width: max-content; max-width: 90%; height: auto; margin: 0; padding: 4px 8px; transform: none; opacity: 1;
  font: 600 12px/1.3 system-ui, sans-serif; color: #fff; background: #d00000; pointer-events: none;
}
.elementor-editor-active .e-accordion-base > .e-accordion-item-base:not(.acc-item):not(.faq-item)::after {
  content: "Unstyled item (added with +): delete it and use Duplicate on an existing item";
}
.elementor-editor-active [data-ags-guard] { position: relative; }
.elementor-editor-active [data-ags-guard]::after { content: attr(data-ags-guard); }
/* =============================================================================
   CONTACT FORM — the Contact Form 7 enquiry form inside the Contact page (classic widget in a V4 box).
   (Regrouped 2026-10-02 from the old 18 partials by scripts/rebuild/regroup_css.js.)
   ========================================================================== */
/* =============================================================================
   EMBED FILL (V4 rebuild, D22 H13) — a V4 box carrying data-ags-embed="fill" holds a classic widget with an
   embed (the shared map shortcode [ags_contact_map] — V4 has no embed element). The widget wrappers and the
   iframe fill the box; the box's size is its own V4 Class. */
[data-ags-embed~="fill"] > .elementor-element,
[data-ags-embed~="fill"] .elementor-widget-container,
[data-ags-embed~="fill"] .elementor-shortcode,
[data-ags-embed~="fill"] .ags-c-contact__mapframe { position: absolute; top: 0; right: 0; bottom: 0; left: 0; width: 100%; height: 100%; margin: 0; }

/* ===== MARQUEE WIDGET DEFAULTS (Phase 2b Task 17b, 2026-09-27) =====
   The "Ageshio — Marquee" widget (widgets/class-ags-marquee.php, registered by inc/widgets.php) renders
   this SAME `.ags-c-marquee` markup, but as a single self-contained widget instead of three individually-
   editable heading/image widgets inside a hand-built container — so there's no more per-element Elementor
   setting to supply the background colour or the title's colour. These two rules are the FALLBACK DEFAULT
   for a widget instance with an empty Style tab, set to the exact value every current page's per-element
   settings already resolve to (Global Colour "Primary" / "Accent") — so converting a page to this widget
   is a zero-diff operation; a client's own Style-tab override on ANY one instance still wins (see the
   widget's own Style-tab doc comment for the specificity margin). Harmless on the OLD hand-built markup
   too: Elementor's own per-element rule there is a higher-specificity `.elementor-element.elementor-
   element-<id>` selector, so these two never win against it — they're simply inert until a page has no
   such per-element rule (i.e. is rendered by the widget). */
/* The ROOT `.ags-c-marquee` box itself has never had an EXPLICIT `display`/`flex-direction` of its own
   for the same reason as __track/__unit above: on the OLD hand-built markup it's a REAL Elementor
   container element, so Elementor's own per-element generated CSS supplies `--display:flex`/
   `--flex-direction:column`. This widget's `.ags-c-marquee` is plain HTML with no such per-element rule
   behind it. `display` is fine either way here — core's `.e-con,.e-con>.e-con-inner{display:var(--display)}`
   is (0,1,0), ties with the bare rule below, and the theme wins that tie by source order (verified: the
   div renders `display:flex`, not its `--display`-unset fallback). `flex-direction` is NOT fine as a bare
   class the way `display` is: `.e-con-full.e-flex{flex-direction:var(--flex-direction)}` is (0,2,0) — a
   HIGHER specificity than a bare `.ags-c-marquee` (0,1,0) — and reads `--flex-direction` from Elementor's
   own `.e-flex{--flex-direction:column}` default. That default HAPPENS to already be "column" (the value
   this root wants), so today this is inert-but-harmless rather than a visible bug — pinned explicitly
   anyway, at the SAME (0,3,0) margin as __track/__unit, so it stays correct even if that upstream default
   ever changes, instead of being correct by coincidence. */
/* Same reasoning, for the per-element PADDING/GAP the old real container also carried (verified against
   the "C · Marquee Banner" template's own Elementor JSON: `padding:{top:10,right:0,bottom:10,left:0}`
   desktop, `padding_mobile:{top:56,right:20,bottom:56,left:20}`, default `flex_gap:20px` — top/bottom are
   already zeroed by the `:where(.elementor-page) .ags-c-marquee{padding-top:0;padding-bottom:25px}` rule
   above regardless, and `gap` is inert with only one flex child, but LEFT/RIGHT padding is real: without
   an explicit rule this widget's plain-HTML root falls back to Elementor core's generic container default
   (10px all sides — `.e-con{--padding-inline-start:var(--container-default-padding-inline-start,10px)}`)
   instead of the design's actual 0 (desktop/tablet) / 20px (mobile, Elementor's own ≤767px breakpoint —
   the template never had a tablet override, so tablet inherits the desktop value same as it always did).
   cstyle-class-verified (baselines/phase2b/t17b/marquee_class_style_check.py) identical to the old
   per-element values at every one of the 6 QA widths. */

/* Widget-wrapper neutralisation (all widths) — same technique as the Card Carousel widget's own
   `display:contents` note (widgets/class-ags-card-carousel.php docblock): the Elementor widget wrapper
   this widget's `.ags-c-marquee` root renders inside is an extra layout box the old hand-built row didn't
   have. `display:contents` removes that box so `.ags-c-marquee` participates in the page's section stack
   exactly like the original hand-built container did. */
/* =============================================================================
   §CONTACT — styled /contact page hosting the "Ageshio Enquiry" CF7 form.
   Width-agnostic (OUTSIDE the min/max gates): the form is a single centred column that
   reads the same at every width; only a couple of hero sizes + small mobile paddings live
   in the gated blocks below. The form markup is CF7's own (.wpcf7-form / .wpcf7-submit /
   .wpcf7-* messages) wrapped in our .ags-c-form column; the field <p> rows carry the
   .ags-c-form__row class set in the CF7 template (build_contact_form.php).
   ============================================================================= */

/* The form column: a comfortable centred measure so the fields don't sprawl on wide screens. */
.ags-c-form { max-width: 720px; margin-left: auto; margin-right: auto; width: 100%; }

/* CF7 renders a per-field <p class="ags-c-form__row"> with a <label> wrapping the field. Stack
   the label above its control and give the row breathing room. */
.ags-c-form .ags-c-form__row { margin: 0 0 16px; }
.ags-c-form .ags-c-form__row > label { display: block; }
.ags-c-form label {
  font-family: var(--e-global-typography-text-font-family, inherit);
  font-size: 15px; line-height: 1.4; color: var(--e-global-color-text, var(--ags-black));
}
/* The field label text sits on its own line (the CF7 template puts a <br> after it); add a small
   gap between that label text and the control below. */
.ags-c-form label br { content: ""; display: block; margin-bottom: 6px; }

/* Inputs / select / textarea — full-width, readable padding, subtle border, the site's body type,
   on a clean white field over the cream band. */
.ags-c-form input[type="text"],
.ags-c-form input[type="email"],
.ags-c-form input[type="number"],
.ags-c-form input[type="tel"],
.ags-c-form input[type="date"],
.ags-c-form select,
.ags-c-form textarea {
  display: block; width: 100%; box-sizing: border-box;
  margin-top: 6px; padding: var(--ags-field-pad-y, 12px) 14px;   /* look = Site Settings → Ageshio Components → Form fields */
  font-family: var(--e-global-typography-text-font-family, inherit);
  font-size: 16px; line-height: 1.4; color: var(--ags-field-text, var(--e-global-color-text, var(--ags-black)));
  background: var(--ags-field-bg, var(--e-global-color-agswhite, var(--ags-white)));
  border: 1px solid var(--ags-field-border, rgba(17,16,16,.22)); border-radius: var(--ags-field-radius, 6px);
  -webkit-appearance: none; -moz-appearance: none; appearance: none;
  transition: border-color .18s ease, box-shadow .18s ease;
}
/* Message box: CF7's [textarea] defaults to rows=10 (~240px) which blew the form past one viewport.
   Drive the height from CSS instead — viewport-relative so the whole form fits one screen, still
   user-resizable. (Simon 2026-09-23.) */
.ags-c-form textarea { min-height: 0; height: max(90px, min(13vh, 150px)); height: clamp(90px, 13vh, 150px); resize: vertical; }
/* Native select needs its dropdown caret back (appearance:none stripped it) + room for it. */
.ags-c-form select {
  padding-right: 40px;
}
/* G10 fix: the caret used to be a literal-red background-image baked into the <select> itself,
   so it never followed a Global Colour rebrand. A mask can't go directly on the select — masking
   clips the WHOLE painted box (background/border/typed text) to the icon's tiny shape, which
   would hide the field itself — so the icon moves to a ::after on CF7's own auto-generated
   `.wpcf7-form-control-wrap` span (same markup CF7 always emits around a form-tag; no template/DB
   change), sized/positioned to land exactly where the old background-image sat. `:has(> select)`
   scopes it to the wrapper of a SELECT only (text/date fields keep their plain wrapper). SVG fill
   is opaque white — only its shape/alpha matters; the visible colour is background-color below.
   Default = Ageshio Red (was baked-in #8E1E00).
   PIXEL-EXACT POSITION (fixed after controller review, 2026-09-26): the old rule's
   `background-position: right 14px center` is relative to the SELECT's own PADDING box — i.e.
   inset 14px + the select's own 1px border from the select's outer edge. This ::after sits on the
   WRAPPER SPAN instead (no border of its own), whose edge coincides with the select's OUTER/
   border-box edge — so `right` here must be 14px + that 1px border = 15px, or the caret lands 1px
   too far right. (Vertical "center" needed no correction: a symmetric top+bottom border doesn't
   move the padding box's vertical centre relative to the border box's.) Verified via cropped
   pixel-bbox comparison against karatetour.com at 390/800/1440 × DPR 1/2 — identical inset. */
.ags-c-form span.wpcf7-form-control-wrap:has(> select) { position: relative; display: block; }
.ags-c-form span.wpcf7-form-control-wrap:has(> select)::after {
  content: ""; position: absolute; top: 50%; right: 15px; width: 12px; height: 8px;
  transform: translateY(-50%); pointer-events: none;
  -webkit-mask: center / contain no-repeat url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%23fff' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  mask: center / contain no-repeat url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8'%3E%3Cpath fill='%23fff' d='M1 1l5 5 5-5'/%3E%3C/svg%3E");
  background-color: var(--e-global-color-primary, var(--ags-red));
}
.ags-c-form input:focus,
.ags-c-form select:focus,
.ags-c-form textarea:focus {
  outline: none;
  border-color: var(--ags-field-focus, var(--e-global-color-primary, var(--ags-red)));
  box-shadow: 0 0 0 3px var(--ags-field-ring, rgba(142,30,0,.15));
}
.ags-c-form ::-moz-placeholder { color: rgba(17,16,16,.45); }
.ags-c-form ::placeholder { color: rgba(17,16,16,.45); }
/* A select still on its empty "—Please choose an option—" choice reads like a placeholder (same faded ink), then full
   ink once something is chosen (2026-10-07, Simon: "the dropdown font is different from the placeholder"). The open
   list keeps full ink. :has() — current Chrome / Edge / Firefox / Safari. */
.ags-c-form .wpcf7-form select:has(option:checked[value=""]) { color: rgba(17,16,16,.45); }
.ags-c-form .wpcf7-form select option { color: #111010; }

/* Submit — CF7 outputs an <input type="submit" class="wpcf7-form-control wpcf7-submit">. Style it
   as the site's red pill (mirrors .ags-btn--solid: red bg, cream text, pill radius, accent type). */
.ags-c-form .wpcf7-submit,
.ags-c-form input[type="submit"] {
  display: inline-block; width: auto; cursor: pointer;
  margin-top: 4px; padding: var(--ags-btn-solid-pad-y, 14px) var(--ags-btn-solid-pad-x, 40px);   /* = .ags-btn--solid: thin standard, 700, darken hover */
  font-family: var(--e-global-typography-accent-font-family, inherit); font-weight: 700;
  font-size: 15px; letter-spacing: .04em; text-transform: uppercase;
  color: var(--ags-btn-solid-text, var(--e-global-color-accent, var(--ags-cream)));
  background: var(--ags-btn-solid-bg, var(--e-global-color-primary, var(--ags-red)));
  border: 0; border-radius: var(--ags-btn-radius, var(--ags-radius-pill));
  -webkit-appearance: none; -moz-appearance: none; appearance: none;
  transition: background-color .15s ease, transform .18s ease;
}
.ags-c-form .wpcf7-submit:hover,
.ags-c-form input[type="submit"]:hover { background: var(--ags-btn-solid-hover, var(--e-global-color-agsreddark, var(--ags-red-dark))); }
.ags-c-form .wpcf7-submit:active,
.ags-c-form input[type="submit"]:active { transform: translateY(1px); }
/* CF7's AJAX spinner renders inline right AFTER the submit input (24px + margin), so text-align:center
   was centring the button+spinner as a pair — the button read left-of-centre. Hide it (cosmetic only;
   the AJAX submit works without it) so the button alone is centred. */
.ags-c-form .wpcf7-spinner { display: none; }

/* CF7 response + validation messages — legible on the cream band. */
.ags-c-form .wpcf7-response-output {
  margin: 18px 0 0; padding: 12px 16px; border-radius: 6px;
  font-family: var(--e-global-typography-text-font-family, inherit);
  font-size: 15px; line-height: 1.4; border: 1px solid transparent;
}
.ags-c-form .wpcf7-not-valid-tip {
  display: block; margin-top: 6px;
  color: var(--e-global-color-primary, var(--ags-red)); font-size: 13px;
}
.ags-c-form .wpcf7-form.invalid .wpcf7-response-output,
.ags-c-form .wpcf7-form.unaccepted .wpcf7-response-output,
.ags-c-form .wpcf7-form.payment-required .wpcf7-response-output {
  color: var(--e-global-color-primary, var(--ags-red));
  border-color: rgba(142,30,0,.4); background: rgba(142,30,0,.06);
}
.ags-c-form .wpcf7-form.sent .wpcf7-response-output {
  color: #1c5b2e; border-color: rgba(28,91,46,.4); background: rgba(28,91,46,.08);
}
.ags-c-form input[aria-invalid="true"],
.ags-c-form select[aria-invalid="true"],
.ags-c-form textarea[aria-invalid="true"] { border-color: var(--e-global-color-primary, var(--ags-red)); }
/* Section eyebrow — small red caps with a hairline rule above, grouping the form into
   "Your enquiry" / "Trip details" / "Karate background" / "Your message". */
.ags-c-form__eyebrow {
  margin: 24px 0 12px; padding-top: 14px;
  border-top: 1px solid rgba(17,16,16,.12);
  font-family: var(--e-global-typography-accent-font-family, inherit);
  font-size: 12px; font-weight: 600; letter-spacing: .14em; text-transform: uppercase;
  color: var(--e-global-color-primary, var(--ags-red));
}
.ags-c-form__eyebrow:first-child { margin-top: 0; padding-top: 0; border-top: 0; }
/* The form's FIRST eyebrow ("Your enquiry") isn't :first-child — CF7 puts its hidden-fields <fieldset> first — so it
   got the hairline + top spacing meant for later sections (a stray line and ~28 px at the top of the card). */
.ags-c-form .wpcf7-form > .ags-c-form__eyebrow:first-of-type { margin-top: 0; padding-top: 0; border-top: 0; }
/* 2-col grid for paired short fields; auto-collapses to 1 col below ~500px (mobile) — self-responsive
   so it needs no media query (the form block is width-agnostic). Row bottom-margin drives the gap. */
.ags-c-form__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); grid-column-gap: 24px; -moz-column-gap: 24px; column-gap: 24px; }
.ags-c-form__submit { margin-top: 18px; }
/* Red required marker (a <span class="ags-c-form__req">*</span> placed after required labels in the
   CF7 template). CF7 still does the actual required/email validation on submit. */
.ags-c-form__req { color: var(--e-global-color-primary, var(--ags-red)); font-weight: 700; margin-left: 3px; }
/* The whole page canvas is maroon. Also set it on the BODY so that when the desktop grid constrains
   its content to --ags-frame + centres it, the ultrawide letterbox margins read as the same maroon
   (seamless full-bleed) rather than exposing a white gutter. */
body.ags-page-contact { background-color: var(--e-global-color-primary, var(--ags-red)); overflow-x: hidden; }
/* V4 rebuild (D4): the cream CARD (background, radius, shadow, padding) is now the V4 Class `contact-card`
   on the box that holds the CF7 Shortcode widget; `.ags-c-form` (the widget's own CSS class) keeps only the
   field mechanics below. */
body.ags-page-contact .ags-c-form { max-width: none; margin: 0; }
/* Submit centred under the form (matches the centred composition). */
body.ags-page-contact .ags-c-form__submit { text-align: center; }
/* Contact map — the map FILLS the whole band edge-to-edge, no padding (Simon 2026-09-23: less tall).
   No grey band, no card framing/rounding/shadow — just a full-width map at a modest fixed height. */
body.ags-page-contact .ags-c-contact__map { width: 100%; position: relative; z-index: 1; }
.ags-c-contact__mapframe {
  position: relative; width: 100%; max-width: none; margin: 0;
  height: var(--ags-cta-band-minh);   /* == the CTA band below it, so map + CTA read as two equal-height bands scrolling down */
  border-radius: 0; overflow: hidden;
}
.ags-c-contact__mapframe iframe {
  position: absolute; top: 0; right: 0; bottom: 0; left: 0; width: 100%; height: 100%; border: 0; display: block;
}

/* ---------------------------------------------------------------------------
   CONTACT — DESKTOP TWO-COLUMN (Simon 2026-09-23 redirection). Reference: the hero (kanji 接触 +
   "Contact" + intro) sits in a LEFT column beside the cream form card on the RIGHT; the map spans
   full width below on a grey band. The three sections (§1 .ags-c-contact.ags-c-hero,
   §2 .ags-c-contact__formband, §3 .ags-c-contact__mapband) are DIRECT children of the page wrapper,
   so grid the wrapper and place each by its stable class — no DOM surgery (keeps the CF7 form + its
   order-sensitive preselect script untouched). SPACING follows docs/COMPOSITION-METHODOLOGY.md: the
   content is constrained to --ags-frame and centred (the body carries the maroon so ultrawide
   letterboxes seamlessly), left/right edges + the centre gutter are --ags-edge, and the top clears
   the fixed navbar (the form was tucking under it). Base/mobile keep the stacked centred layout
   above; this block just re-flows the same DOM. Portable (slug-scoped). */
@media (min-width: 1024px) {

  /* FORM scales with the CONTENT scale (--ags-px) — composition doc: a frozen px literal becomes
     calc(N * var(--ags-px)), where N is its 1440-reference value. The earlier fixed-px/vw/vh clamps
     didn't track the frame, so the form stayed one size and overflowed the one-viewport maroon band at
     1440×766. --ags-scale is a contain-fit bounded by height, so this can't overflow a short viewport;
     at the 1440×766 reference these resolve to their N px and the whole form (~one card) sits inside the
     100svh band (row 1) with balanced padding. Desktop only — mobile keeps the base sizing above. */
  body.ags-page-contact .ags-c-form {
    width: 100%; flex: 1 1 auto; min-height: -webkit-min-content; min-height: -moz-min-content; min-height: min-content;
    display: flex; flex-direction: column;
  }
  /* Flex the CF7 chain so the <form> fills the card, then space its rows out to fill the height. Extra
     card height → even gaps between fields (airier on tall viewports), not empty cream. Hidden fields
     are display:none (not flex items); the trailing .wpcf7-response-output sits at the bottom edge. */
  :where(body.ags-page-contact) .ags-c-form > .elementor-widget-container,
  :where(body.ags-page-contact) .ags-c-form .elementor-shortcode,
  :where(body.ags-page-contact) .ags-c-form .wpcf7,
  :where(body.ags-page-contact) .ags-c-form .wpcf7-form {
    display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0;
  }
  /* The form takes its NATURAL height (2026-10-07, Simon: "compact on every screen"; replaces the old cap): no
     stretching to fill the card, so no max-height cap is needed — the cap + min-height: min-content trick only worked
     in Chrome; Firefox kept the box at the cap and the Trip-details fields spilled under the map. The card hugs the
     form and is centred in the band by its V4 Classes (`contact-card` no grow, `contact-formcol` centred), so taller
     screens get maroon around the card, not spread-out fields; a trip enquiry grows card + band + pushes the map. */
  :where(body.ags-page-contact) .ags-c-form .wpcf7-form { justify-content: flex-start; flex: 0 0 auto; }
  :where(body.ags-page-contact) .ags-c-form .ags-c-form__row { margin-bottom: calc(12 * var(--ags-px)); }
  body.ags-page-contact .ags-c-form__eyebrow {
    margin: calc(16 * var(--ags-px)) 0 calc(8 * var(--ags-px)); padding-top: calc(12 * var(--ags-px));
    font-size: calc(11 * var(--ags-px));
  }
  :where(body.ags-page-contact) .ags-c-form__eyebrow:first-child { margin-top: 0; padding-top: 0; }
  :where(body.ags-page-contact) .ags-c-form .wpcf7-form > .ags-c-form__eyebrow:first-of-type { margin-top: 0; padding-top: 0; }
  body.ags-page-contact .ags-c-form label { font-size: calc(13 * var(--ags-px)); }
  body.ags-page-contact .ags-c-form label br { margin-bottom: calc(4 * var(--ags-px)); }
  :where(body.ags-page-contact) .ags-c-form input[type="text"],
  :where(body.ags-page-contact) .ags-c-form input[type="email"],
  :where(body.ags-page-contact) .ags-c-form input[type="number"],
  :where(body.ags-page-contact) .ags-c-form input[type="tel"],
  :where(body.ags-page-contact) .ags-c-form input[type="date"],
  body.ags-page-contact .ags-c-form select,
  body.ags-page-contact .ags-c-form textarea {
    padding: var(--ags-field-pad-y, calc(9 * var(--ags-px))) calc(13 * var(--ags-px));
    font-size: calc(15 * var(--ags-px)); margin-top: calc(5 * var(--ags-px));
  }
  body.ags-page-contact .ags-c-form select { padding-right: calc(36 * var(--ags-px)); }
  body.ags-page-contact .ags-c-form textarea { height: calc(90 * var(--ags-px)); }
  body.ags-page-contact .ags-c-form__submit { margin-top: calc(16 * var(--ags-px)); }
  :where(body.ags-page-contact) .ags-c-form .wpcf7-submit,
  :where(body.ags-page-contact) .ags-c-form input[type="submit"] {
    padding: var(--ags-btn-solid-pad-y, var(--ags-btn-pad-y-fluid)) var(--ags-btn-solid-pad-x, calc(34 * var(--ags-px))); font-size: calc(14 * var(--ags-px));
  }
}
@media (max-width: 1023px) {
  /* 0. Release the desktop min-width:1024 lock (body.elementor-page, ungated, far above).
     The homepage only releases it for body.ags-redesign (front-page-only), so on interior
     pages the body would stay 1024px wide → horizontal overflow at every phone width. Release
     it for every Elementor page at ≤1023 (that's the whole purpose of the mobile region);
     desktop keeps the lock (this is inside the max-width gate). VERIFIED: this is the primary
     overflow fix — without it scrollWidth pins to 1024 at 390/360/320. */
  /* COHESIVE SECTION (Simon 2026-08-17): §4 statement + §5 photo carousel are ONE cream section, so the
     cream must run continuously behind the carousel with NO white gap. The photo row is cream; the earlier
     margin-block:32px was TRANSPARENT (showed the white page bg above/below → a break). Use margin:0 so the
     section abuts the cream statement above and the red numgrid below, and put the space BELOW the photo as
     PADDING (cream). Keep padding-TOP:0 — the progress bar is absolute:top:12px from the root, so a top pad
     would push the in-flow photo down while the bar stayed put (they'd detach); the space ABOVE the photo
     comes from the statement's own cream padding-bottom. */
  /* EDGE PADDING (Simon 2026-08-17): at 768–1023 Elementor drops the section side-padding to 0 (the
     phone 20px only applies ≤767), so the "What's included" heading + the numgrid eyebrow/cards touch
     the page edge on wider phones. Give both sections explicit side padding at ALL mobile widths
     (≈20px @390, growing gently) so nothing sits flush. padding-inline (0,3,0) beats Elementor's
     per-element padding; only the left/right is set, so the vertical padding is untouched. */

  /* §7 NUMBERED GRID → HORIZONTAL side-scroll (Simon 2026-08-17, ref Image #8): NOT a 2-up grid but a
     single swipeable row of number+label cards, ~1.5 cards visible so the next one peeks in at the right
     edge. The grid scrolls INTERNALLY (overflow-x on the grid, NOT the page) so document scrollWidth
     stays 390 — no page-level horizontal overflow. SHARED across the numgrid-bearing pages (GT + VIP). */
  /* HORIZONTAL-SCROLL AFFORDANCE (Simon, ref Image #26): a long arrow at the bottom-right of the
     numgrid section signalling the card row scrolls sideways — the peeking-next-card cue alone wasn't
     obvious enough, especially at VIP's 12 items. A sibling of the (overflow-x) grid, so it doesn't
     scroll away. margin-left:auto right-aligns it (works whether the section is block or flex-column);
     cream stroke reads on the maroon "What's included" band. SHARED across the numgrid pages (GT + VIP). */
  /* The items are Elementor .e-con containers → their flex sizing is driven by Elementor's per-element
     --flex-* consumer (0,2,0), which beats a bare .ags-c-numgrid__item (0,1,0) and shrinks them to ~21px.
     Assert the CARD WIDTH at page-type scope (0,3,0) to win — same --flex-* trap as the hero photo-col. */
  /* Kanji eyebrow (旅行/後退 + label) is CENTRED above the grid in the mockup; the desktop left-gutter
     positioning is page-scoped + gated ≥1024, so mobile inherits Elementor's per-element flex-start.
     Page-type scope (0,4,0) beats it. SHARED across the numgrid-bearing pages (GT + VIP). */
  /* POLISH: size the §7 eyebrow kanji to match the mockup (glyph ≈54px @390, same as the §1 hero kanji).
     It otherwise falls back to the small Kit eyebrow global (live ≈36px). Mirrors the hero-kanji clamp
     above. SHARED across the numgrid pages (GT + VIP). :first-child = the kanji heading (not the label). */
  /* Item "1." / "2." numbers — mockup ≈ 24px; the reflow leaves them at the ~13px label global. */

  /* §9 ACCORDION → single column; beach photo below the list (flex-direction per-element from
     Elementor (0,2,0) → scope to page type to win). */

  /* §CONTACT — mobile. The form column is already width-agnostic (max-width:720px caps well above
     any phone width, so it's effectively 100% here); just tighten the hero title + keep the field
     type comfortable. box-sizing:border-box (set above) keeps the full-width inputs off the x-axis. */
  .ags-c-form .wpcf7-submit,
  .ags-c-form input[type="submit"] { width: 100%; }
}
/* =============================================================================
   POST BODY — the blog post body (Pro Post Content widget) styled to the doc pages, reading the V4 Variables.
   (Regrouped 2026-10-02 from the old 18 partials by scripts/rebuild/regroup_css.js.)
   ========================================================================== */
/* Photos are wired by attachment ID into the existing ags-c-* Image widgets; this block only sets
   per-slot proportions/pacing, mirroring the Group-Tours finalize (components.css ~3718-4269),
   retargeted to body.ags-page-vip-karate-retreat. Portable slug scope ONLY — never page-id-N /
   .elementor-N, never body.ags-redesign. Appendable at end-of-file (self-contained, balanced).
   GREYSCALE: none needed — the VIP feature source is already B&W; the photo-row, overlay, accordion
   and hero photos are all COLOUR in the Figma. (The shared >=1024 base rule bakes grayscale onto the
   overlay photo for GT, so it is explicitly reset to `none` below.) Desktop-only (1024->ultrawide). */
/* Slug: karate-experiences (post 19556). Scope: body.ags-page-karate-experiences.
   Section stack matches the Figma: Hero(経験) → Marquee → Feature(dojo floor) →
   Experience List(animated ags-c-explist) → Overlay Split(Book as Group / Individual) → CTA.
   Only the components this page HAS are tuned here (hero photo, feature band, explist
   composition, overlay viewport-fill). Numgrid / photorow / accordion are OMITTED (not on
   this page). Greyscale: NONE baked here — the §Overlay photo is greyscaled by the existing
   UNSCOPED `.ags-c-overlay__photo img { filter: grayscale(1) }` component rule; the hero source
   (exp-1) is already B&W, and the feature + edge photos are COLOUR in the reference. The overlay
   editorial composition (corner headings, centre divider, captions, lists, cover photo) is also
   supplied by the UNSCOPED .ags-c-overlay* rules already in components.css — we only pin height. */
/* Slug: karate-history-tour (post 19555). Scope: body.ags-page-karate-history-tour ONLY —
   never page-id-N / .elementor-N, never body.ags-redesign (portable-scope rule; linter
   scripts/qa/check_portable_scope.py). Photos are wired by attachment ID into the existing
   ags-c-* Image widgets (wire_history_tour_photos.php); this block only sets per-slot
   proportions / pacing, mirroring the VIP + Group-Tours finalize blocks above, retargeted here.

   Section stack (matches the Figma desktop mockup — the middle "What's included / Who is this
   for" is a SINGLE editorial overlay, no blue title-band / numbered grid):
     1 Hero(経験·Experience) · 2 Marquee · 3 Feature(B&W kata) · 4 Statement ·
     5 Overlay "What's included / Who is this for" (B&W monk + dune) ·
     6 Locations Title (RED band) · 7 Locations Photo Row (maroon 4-card filmstrip, 3rd WIDE) ·
     8 Accordion (pinned belt photo + maroon dividers) · 9 Closing Overlay (約 centred, colour) · 10 CTA.

   GREYSCALE: the §5 overlay source is a B&W dune/monk (leave the shared grayscale(1) baked; add a
   dark scrim so the cream text is legible over the pale sand). The §9 CLOSING photo is a
   teal/blue rain frame that MUST keep its colour → explicitly reset filter:none (the shared
   >=1024 base rule `.ags-c-overlay__photo img{filter:grayscale(1)}` would otherwise bake it grey).
   The §3 feature source is already B&W. Desktop-only (1024 -> ultrawide). */
/* =============================================================================
   POST CARD (.ags-c-postcard) — SHARED, reusable across pages, NOT page-scoped.
   Born on About's "Find your journey" carousel but built to be reused by a future
   blog listing grid, blog-post cards, and a WP posts loop, so its INTRINSIC styling
   (photo/tags/title/more) lives here rather than under body.ags-page-about. Scoped
   `.elementor-page [data-elementor-type="wp-page"] .ags-c-postcard*` — same
   page-type-scope convention as the interior ags-c-* components above (beats Kit
   globals on headings/text; see the CONVENTION note ~4787). Any page-specific
   CONTEXT the card sits in (carousel width/snap, grid columns, etc.) is that page's
   own concern — e.g. body.ags-page-about .ags-c-journey__card below, which carries
   both classes on the same element (.ags-c-postcard for the intrinsic half,
   .ags-c-journey__card for the carousel-item half).
   Markup: .ags-c-postcard > .ags-c-postcard__thumb (position context) >
   .ags-c-postcard__img (square photo) + .ags-c-postcard__tags > .ags-c-postcard__tag
   ×N (overlaid pills), then .ags-c-postcard__title (heading) + .ags-c-postcard__more
   (link).
   ============================================================================= */

/* =============================================================================
   ARTICLE CARD (.ags-c-articlecard) — SHARED Loop Item, NOT page-scoped.
   -----------------------------------------------------------------------------
   "C · Article Card" (elementor_library post, _elementor_template_type=loop-item) — the
   overlay card from Desktop (3): a photo fills the card; tag pills (post_tag terms, dynamic,
   unlinked → one bare <span> per term) sit top-right; a date pill (dynamic Post Date) sits
   top-left; a bottom scrim carries a title/excerpt/READ-MORE stack anchored to the card's
   BOTTOM edge (so a longer/shorter excerpt never repositions the title or button). Built by
   scripts/build_articlecard.py, applied by scripts/apply_articlecard.php (Task 3, sub-project
   #2 "Blog page build" — supersedes Task 2's photo+title-only stub). Reused by the /articles
   Loop Grid (body.ags-page-articles, doc type wp-page), the About "Find your journey" grid
   (wp-page), and the single-post template's "Keep exploring" grid (doc type single-post, Task
   8) — three DIFFERENT enclosing page types, so scoping to the PAGE's own `[data-elementor-
   type]` value (as this block did originally, `.elementor-page [data-elementor-type="wp-page"]`)
   breaks on any page type that isn't "wp-page" (confirmed: the single-post template's `<body>`
   never even gets a bare `.elementor-page` class). Scoped instead to the LOOP-ITEM's OWN wrapper
   attributes — `[data-elementor-type="loop-item"][data-elementor-post-type="elementor_library"]`
   — which are present unconditionally, on EVERY page this card is ever rendered on, because
   `.ags-c-articlecard` only ever renders AS a loop-item (never as a direct page widget). Never a
   page slug. `data-type`/`data-tags` on the card ROOT come from a PHP
   render filter (functions.php ags_articlecard_data_attrs(), keyed to the root's literal id
   "ac-root"), not from any rule here — Elementor's own dynamic tags can't emit taxonomy SLUGS
   (only display names), see that function's comment.
   Markup: .ags-c-articlecard (root — position context, clip + round, the bottom-scrim ::after)
   > .ags-c-articlecard__img (fills, absolute) + __tags (pills, abs top-right) + __date (pill,
   abs top-left) + __body (abs bottom-anchored: __title + __excerpt + __more).
   Aspect ratio 405/442 is the mockup's own card measured at its 1280 export — a scale-free
   ratio (like .ags-c-postcard__img's 1/1 above), so it holds at any grid column width without
   needing --ags-px. Everything ELSE here (pill/text sizes, insets, gaps) IS in --ags-px so it
   enlarges with the rest of the reference-frame composition on ultrawide.
   ============================================================================= */

/* `.is-hidden` state, JS-toggled (ageshio-articles.js, /articles Task 4 filter) — unscoped
   by page/breakpoint like the rest of .ags-c-articlecard (SHARED, not page-scoped): a plain
   visibility switch, not a layout rule, so it doesn't need --ags-px or the ≥1024 guard above. */
/* =============================================================================
   ===== ABOUT PAGE (body.ags-page-about) =====
   -----------------------------------------------------------------------------
   Slug: about (post 19830). Scope: body.ags-page-about ONLY — portable slug hook,
   never page-id-N / .elementor-N (linter: scripts/qa/check_portable_scope.py).
   New/collapsed sections only — the REUSED components (marquee, both maroon
   .ags-c-statement bands, the §8 .ags-c-overlay "Who we serve / Why Ageshio",
   the blue .ags-c-accordion, footer) already render via the shared rules above;
   this region does not touch them except the one Task-4 divider tweak at the end.

   Section stack (DOM order, confirmed live): 1 Photo hero (+ eyebrow) · 2 Marquee
   (shared) · 3 Intro (2-col para) · 4 Approach (4 numbered cards) · 5 Mission
   statement (shared, maroon) · 6 What we do (dark cqw stage) · 7 Based in Okinawa
   (blue map band) · 8 Connection to Grand Masters intro · 9 Masters filmstrip
   (shared .ags-masters, static-ported like History) · 10 Overlay (shared) ·
   11 Statement #2 (shared, maroon) · 12 Accordion (shared, blue variant) ·
   13 Find your journey (search bar + 3 cards) · 14 Final CTA (.ags-c-cta3, NEW —
   maroon band, centred heading + 3-pill row) · 15 Footer (shared).

   Placeholders: every unsourced photo is a real Image widget pointing at
   Elementor's placeholder.png, painted neutral by the shared `.ags-c-ph` rule
   (components.css ~4784) — no image sourcing happens here. Colour is DB-owned
   (Global Colors on each widget/section) — this file is layout/position/size only;
   the one exception is the Task-4 accordion divider (visibility, not brand colour).

   RHYTHM PASS (2026-09-17, design-feedback round 3 — SUPERSEDES the two notes below): the
   round-2 "every section is a 100svh floor" treatment floated content mid-section and left an
   empty cream tail on sections whose content was shorter than one viewport (Approach read as
   "two sections" because of it; the mission statements' eyebrow→headline gap also collapsed
   under vertical centring). Simon: match the OTHER interior pages' rhythm instead — content-
   height sections, `--ags-sec-pad` top+bottom, content top-anchored, NOT forced full-viewport.
   `.ags-c-intro`, `.ags-c-approach`, `.ags-c-statement--maroon`, `.ags-c-mapband`,
   `.ags-c-masters-intro` (+ its paired `.ags-masters` filmstrip below) and `.ags-c-journey`
   are now `min-height:0` (content-height) with `justify-content:normal`, matching GT's
   `.ags-c-statement` model. `.ags-c-photohero`, `.ags-c-whatwedo`, `.ags-c-overlay` (full-bleed
   100svh stages), `.ags-c-accordion--blue` (100svh, matches GT's accordion) and `.ags-c-cta3`
   (compact band) are UNCHANGED — those correctly fill/are full-bleed. The card/photo/filmstrip
   SIZE reductions from the round-2 "VIEWPORT FIT PASS" (below each section) are kept as-is —
   only the section-level height/justify/pad wrapper changed, not the content sizing.

   VIEWPORT FIT PASS (2026-09-17, round 2 — sizing kept, framing above superseded): the four
   flagged sections (approach / mapband / journey / the masters-intro+filmstrip pair) ran
   taller than one viewport at the 1440x766 reference because their CONTENT (photo/card/
   filmstrip heights, or a title font size) was bigger than the reference band, not because of
   the pads/edges — those are untouched throughout. Each is now reduced (see the inline note at
   each section) so content+pads fit ≤766 at 1440, expressed in --ags-px so the fit holds at
   every size. Those content-size reductions remain valid under round 3's content-height model. */

/* ---- Clear Elementor's stray per-level 10px padding on every WRAPPER container
   introduced by this page (the "clear it at every level" rule — see the pre-flight
   checklist in docs/COMPOSITION-METHODOLOGY.md §4). Width-agnostic so it also holds
   under 1024 if these wrappers are ever reused there. ---- */
/* =============================================================================
   ===== ARTICLES LISTING PAGE (body.ags-page-articles) =====
   -----------------------------------------------------------------------------
   Slug: articles. /articles — "Search Articles" listing (sub-project #2 "Blog page
   build", Task 2). Scope: body.ags-page-articles ONLY — portable slug hook, never
   page-id-N / .elementor-N (linter: scripts/qa/check_portable_scope.py).

   Section stack: 1 Search + tag pills + Loop Grid (ONE section, ags-c-articles-header —
   content-height rhythm, --ags-sec-pad top/bottom, matches every other interior page's
   model, NOT a 100svh floor) · 2 CTA (shared .ags-c-cta, UNSCOPED — needs no rules here,
   see components.css ~5498) · 3 Footer (theme-builder condition, auto-renders sitewide —
   no rules here either).

   The search bar re-declares About's "Find your journey" search idiom (caret bar + faded-
   placeholder input, components.css ~7473 body.ags-page-about .ags-c-journey__*) under a
   NEW class prefix (ags-c-articles-header__*) rather than reusing ags-c-journey__* directly
   — that rule is scoped body.ags-page-about only, so copying the ~8 declarations keeps each
   page's CSS independently portable (no cross-page class coupling).

   The Loop Grid's card is `.ags-c-articlecard` — the SHARED overlay card (Task 3), rules
   above under "ARTICLE CARD (.ags-c-articlecard) — SHARED Loop Item" (~6871), not here (it's
   reused by other pages too, so it isn't page-scoped). ---- */
/* =============================================================================
   ===== SINGLE POST — POST BODY (body.single-post .ags-c-postbody__content) =====
   -----------------------------------------------------------------------------
   The Single Post template (Theme Builder 20663) is native V4 except the body: the
   Pro "Post Content" widget renders each post's own WordPress HTML (the_content),
   which Global Classes can't reach. These rules style that HTML to match the doc
   pages (Terms, FAQ, Privacy: doc-col / doc-h2 / doc-h3 / doc-text / doc-table) and
   read the V4 Variables, so changing a Variable in Elementor (Site settings →
   Variables) restyles every post too:
     column  820 × --space-stage-unit-display (as doc-col)   text  --font-body 300, --text-body, --color-ink
     h2      --font-display, --text-h4, --color-brand        h3–h6 --font-display, 19 × unit (as doc-h3)
     links   underlined, ink; hover --color-brand            tables as doc-table (key cell tinted)
   Photo runs are grouped into `.ags-c-contentrow` by ageshio-content-photorows.js
   (3 per row; one per line ≤1023); broken old images are hidden by
   ageshio-img-fallback.js. The hero/meta/keep-exploring/CTA sections are V4 Classes
   (posthero*, postbody*, keep*) — nothing for them here.
   Content inventory (87 posts, 2026-10-01): img, h2, a, table (key/value rows), h6,
   strong, iframe (YouTube), figure, h3, ul. ============================================================================= */
body.single-post .ags-c-postbody__content { width: 100%; }
body.single-post .ags-c-postbody__content .elementor-widget-container {
  max-width: calc(820 * var(--space-stage-unit-display)); margin: 0 auto;
  color: var(--color-ink); font-family: var(--font-body), sans-serif; font-weight: 300;
  font-size: var(--text-body); line-height: 1.65;
}

/* ---- Headings ---- */
body.single-post .ags-c-postbody__content .elementor-widget-container h2 {
  font-family: var(--font-display), sans-serif; font-weight: 400; font-size: var(--text-h4);
  line-height: 1.15; letter-spacing: -0.03em; color: var(--color-brand); text-align: left;
  margin: calc(48 * var(--space-stage-unit-display)) 0 calc(16 * var(--space-stage-unit-display));
}
body.single-post .ags-c-postbody__content .elementor-widget-container h3, body.single-post .ags-c-postbody__content .elementor-widget-container h4, body.single-post .ags-c-postbody__content .elementor-widget-container h5, body.single-post .ags-c-postbody__content .elementor-widget-container h6 {
  font-family: var(--font-display), sans-serif; font-weight: 400;
  font-size: calc(19 * var(--space-stage-unit-display));
  line-height: 1.3; letter-spacing: -0.02em; color: var(--color-brand); text-align: left;
  margin: calc(32 * var(--space-stage-unit-display)) 0 calc(12 * var(--space-stage-unit-display));
}
body.single-post .ags-c-postbody__content .elementor-widget-container :is(h3, h4, h5, h6) {
  font-family: var(--font-display), sans-serif; font-weight: 400;
  font-size: calc(19 * var(--space-stage-unit-display));
  line-height: 1.3; letter-spacing: -0.02em; color: var(--color-brand); text-align: left;
  margin: calc(32 * var(--space-stage-unit-display)) 0 calc(12 * var(--space-stage-unit-display));
}
body.single-post .ags-c-postbody__content .elementor-widget-container > :first-child { margin-top: 0; }

/* ---- Text, links, lists ---- */
body.single-post .ags-c-postbody__content .elementor-widget-container p {
  margin: 0 0 calc(16 * var(--space-stage-unit-display));
}
body.single-post .ags-c-postbody__content .elementor-widget-container ul, body.single-post .ags-c-postbody__content .elementor-widget-container ol {
  margin: 0 0 calc(16 * var(--space-stage-unit-display)); padding-left: 1.4em;
}
body.single-post .ags-c-postbody__content .elementor-widget-container :is(ul, ol) {
  margin: 0 0 calc(16 * var(--space-stage-unit-display)); padding-left: 1.4em;
}
body.single-post .ags-c-postbody__content .elementor-widget-container li { margin-bottom: 0.4em; }
/* The migrated posts carry old inline styles (blue links, #000/#2b2b2b spans, stray fonts and
   backgrounds). An inline style beats any stylesheet rule, so these resets need !important. */
body.single-post .ags-c-postbody__content .elementor-widget-container p, body.single-post .ags-c-postbody__content .elementor-widget-container li, body.single-post .ags-c-postbody__content .elementor-widget-container span, body.single-post .ags-c-postbody__content .elementor-widget-container strong, body.single-post .ags-c-postbody__content .elementor-widget-container b, body.single-post .ags-c-postbody__content .elementor-widget-container em, body.single-post .ags-c-postbody__content .elementor-widget-container u {
  color: var(--color-ink) !important; font-family: var(--font-body), sans-serif !important;
  background-color: transparent !important;
}
body.single-post .ags-c-postbody__content .elementor-widget-container :is(p, li, span, strong, b, em, u) {
  color: var(--color-ink) !important; font-family: var(--font-body), sans-serif !important;
  background-color: transparent !important;
}
body.single-post .ags-c-postbody__content .elementor-widget-container li, body.single-post .ags-c-postbody__content .elementor-widget-container span, body.single-post .ags-c-postbody__content .elementor-widget-container strong, body.single-post .ags-c-postbody__content .elementor-widget-container b, body.single-post .ags-c-postbody__content .elementor-widget-container em, body.single-post .ags-c-postbody__content .elementor-widget-container u {
  font-size: inherit !important;
}
body.single-post .ags-c-postbody__content .elementor-widget-container :is(li, span, strong, b, em, u) {
  font-size: inherit !important;
}
body.single-post .ags-c-postbody__content .elementor-widget-container strong, body.single-post .ags-c-postbody__content .elementor-widget-container b { font-weight: 600 !important; }
body.single-post .ags-c-postbody__content .elementor-widget-container :is(strong, b) { font-weight: 600 !important; }
body.single-post .ags-c-postbody__content .elementor-widget-container a {
  color: var(--color-ink) !important; font-family: inherit !important; font-size: inherit !important;
  background-color: transparent !important;
  -webkit-text-decoration: underline !important;
  text-decoration: underline !important; text-underline-offset: 0.15em; transition: color 0.2s;
}
body.single-post .ags-c-postbody__content .elementor-widget-container a:hover { color: var(--color-brand) !important; }
/* a link inside a heading keeps the heading's colour */
body.single-post .ags-c-postbody__content .elementor-widget-container h2 a, body.single-post .ags-c-postbody__content .elementor-widget-container h3 a, body.single-post .ags-c-postbody__content .elementor-widget-container h4 a, body.single-post .ags-c-postbody__content .elementor-widget-container h5 a, body.single-post .ags-c-postbody__content .elementor-widget-container h6 a {
  color: inherit !important;
}
body.single-post .ags-c-postbody__content .elementor-widget-container :is(h2, h3, h4, h5, h6) a {
  color: inherit !important;
}

/* ---- Photos, captions, embeds ---- */
body.single-post .ags-c-postbody__content .elementor-widget-container img { max-width: 100%; height: auto; }
body.single-post .ags-c-postbody__content .elementor-widget-container figure img {
  display: block; margin: 0 auto; border-radius: var(--radius-field);
}
/* a photo on its own, or after a line of text inside the same paragraph */
body.single-post .ags-c-postbody__content .elementor-widget-container > p > img {
  display: block; margin: calc(16 * var(--space-stage-unit-display)) auto; border-radius: var(--radius-field);
}
body.single-post .ags-c-postbody__content .elementor-widget-container > p > img:only-child { margin: 0 auto; }
body.single-post .ags-c-postbody__content .elementor-widget-container > p:has(> img:only-child),
body.single-post .ags-c-postbody__content .elementor-widget-container figure {
  margin: calc(28 * var(--space-stage-unit-display)) 0;
}
body.single-post .ags-c-postbody__content .elementor-widget-container figcaption {
  margin-top: calc(8 * var(--space-stage-unit-display));
  font-size: var(--text-small); color: var(--color-muted); text-align: center;
}
/* WordPress expands a bare YouTube URL into a fixed-size iframe; the JS sets each one's real
   aspect-ratio from its width/height attributes — 16/9 is the no-JS fallback. */
body.single-post .ags-c-postbody__content .elementor-widget-container iframe {
  display: block; width: 100%; max-width: 100%; height: auto; aspect-ratio: 16 / 9;
  margin: calc(28 * var(--space-stage-unit-display)) 0; border: 0; border-radius: var(--radius-field);
}
/* photo rows (ageshio-content-photorows.js) */
body.single-post .ags-c-contentrow {
  display: flex; flex-wrap: wrap; gap: calc(20 * var(--space-stage-unit-display));
  margin: calc(28 * var(--space-stage-unit-display)) 0;
}
body.single-post .ags-c-contentrow__item {
  /* grow 0: a leftover photo on its own line stays the same size as its row-mates */
  flex: 0 1 calc((100% - 2 * 20 * var(--space-stage-unit-display)) / 3); min-width: 0;
  margin: 0; display: flex; flex-direction: column; gap: calc(10 * var(--space-stage-unit-display));
}
body.single-post .ags-c-contentrow__item img {
  width: 100%; height: auto; display: block; margin: 0; border-radius: var(--radius-field);
}
body.single-post .ags-c-contentrow__cap {
  margin: 0; font-size: var(--text-small); font-weight: 600; line-height: 1.4; text-align: center;
}
body.single-post .ags-c-contentrow__cap a { color: inherit !important; }

/* ---- Info tables (key/value rows: <th scope=row> + <td>) — as doc-table ---- */
body.single-post .ags-c-postbody__content .elementor-widget-container table {
  width: 100%; margin: calc(28 * var(--space-stage-unit-display)) 0;
  border-collapse: separate; border-spacing: 0; overflow: hidden;
  border: 1px solid var(--color-brand); border-radius: 8px; background: transparent;
}
body.single-post .ags-c-postbody__content .elementor-widget-container th, body.single-post .ags-c-postbody__content .elementor-widget-container td {
  padding: calc(12 * var(--space-stage-unit-display)) calc(18 * var(--space-stage-unit-display));
  font-family: var(--font-body), sans-serif; font-weight: 300; font-size: var(--text-body);
  line-height: 1.5; color: var(--color-ink); text-align: left; vertical-align: top;
  background: transparent; border: 0; border-bottom: 1px solid rgba(142, 30, 0, 0.14);
}
body.single-post .ags-c-postbody__content .elementor-widget-container :is(th, td) {
  padding: calc(12 * var(--space-stage-unit-display)) calc(18 * var(--space-stage-unit-display));
  font-family: var(--font-body), sans-serif; font-weight: 300; font-size: var(--text-body);
  line-height: 1.5; color: var(--color-ink); text-align: left; vertical-align: top;
  background: transparent; border: 0; border-bottom: 1px solid rgba(142, 30, 0, 0.14);
}
body.single-post .ags-c-postbody__content .elementor-widget-container th {
  font-family: var(--font-display), sans-serif; font-weight: 400; color: var(--color-brand);
  background: rgba(142, 30, 0, 0.08);
}
body.single-post .ags-c-postbody__content .elementor-widget-container tr:last-child > th {
  border-bottom: 0;
}
body.single-post .ags-c-postbody__content .elementor-widget-container tr:last-child > td {
  border-bottom: 0;
}
body.single-post .ags-c-postbody__content .elementor-widget-container tr:last-child > :is(th, td) {
  border-bottom: 0;
}

/* ---- Tablet + mobile (as doc-col / doc-cell / doc-h3 at these widths) ---- */
@media (max-width: 1023px) {
  body.single-post .ags-c-postbody__content .elementor-widget-container { max-width: 100%; }
  /* fixed spacing here: the stage unit gets too small on phones (doc pages do the same) */
  body.single-post .ags-c-postbody__content .elementor-widget-container h2 { margin: 40px 0 14px; }
  body.single-post .ags-c-postbody__content .elementor-widget-container h3, body.single-post .ags-c-postbody__content .elementor-widget-container h4, body.single-post .ags-c-postbody__content .elementor-widget-container h5, body.single-post .ags-c-postbody__content .elementor-widget-container h6 {
    font-size: 17px; margin: 26px 0 10px;
  }
  body.single-post .ags-c-postbody__content .elementor-widget-container :is(h3, h4, h5, h6) {
    font-size: 17px; margin: 26px 0 10px;
  }
  body.single-post .ags-c-postbody__content .elementor-widget-container p, body.single-post .ags-c-postbody__content .elementor-widget-container ul, body.single-post .ags-c-postbody__content .elementor-widget-container ol { margin-bottom: 14px; }
  body.single-post .ags-c-postbody__content .elementor-widget-container :is(p, ul, ol) { margin-bottom: 14px; }
  body.single-post .ags-c-postbody__content .elementor-widget-container > :first-child { margin-top: 0; }
  body.single-post .ags-c-postbody__content .elementor-widget-container table, body.single-post .ags-c-postbody__content .elementor-widget-container iframe, body.single-post .ags-c-postbody__content .elementor-widget-container figure, body.single-post .ags-c-postbody__content .elementor-widget-container > p:has(> img:only-child), body.single-post .ags-c-contentrow { margin: 24px 0; }
  body.single-post .ags-c-postbody__content .elementor-widget-container :is(table, iframe, figure),
  body.single-post .ags-c-postbody__content .elementor-widget-container > p:has(> img:only-child),
  body.single-post .ags-c-contentrow { margin: 24px 0; }
  body.single-post .ags-c-contentrow__item { gap: 8px; }
  body.single-post .ags-c-postbody__content .elementor-widget-container > p > img:not(:only-child) { margin: 14px auto; }
  body.single-post .ags-c-postbody__content .elementor-widget-container figcaption { margin-top: 8px; }
  body.single-post .ags-c-postbody__content .elementor-widget-container th, body.single-post .ags-c-postbody__content .elementor-widget-container td { padding: 10px 14px; }
  body.single-post .ags-c-postbody__content .elementor-widget-container :is(th, td) { padding: 10px 14px; }
  body.single-post .ags-c-contentrow { flex-direction: column; flex-wrap: nowrap; gap: 20px; }
  body.single-post .ags-c-contentrow__item { flex: 1 1 100%; width: 100%; }
}
@media (max-width: 767px) {
  body.single-post .ags-c-postbody__content .elementor-widget-container { font-size: 15.5px; }
  body.single-post .ags-c-postbody__content .elementor-widget-container h2 { font-size: 26px; }
  body.single-post .ags-c-postbody__content .elementor-widget-container th, body.single-post .ags-c-postbody__content .elementor-widget-container td { font-size: 15.5px; }
  body.single-post .ags-c-postbody__content .elementor-widget-container :is(th, td) { font-size: 15.5px; }
  /* key/value rows stack (key on top), as doc-row on mobile; the posts' inline 30%/70% widths go */
  body.single-post .ags-c-postbody__content .elementor-widget-container table, body.single-post .ags-c-postbody__content .elementor-widget-container tbody, body.single-post .ags-c-postbody__content .elementor-widget-container tr, body.single-post .ags-c-postbody__content .elementor-widget-container th, body.single-post .ags-c-postbody__content .elementor-widget-container td {
    display: block; width: 100% !important; height: auto !important;
  }
  body.single-post .ags-c-postbody__content .elementor-widget-container :is(table, tbody, tr, th, td) {
    display: block; width: 100% !important; height: auto !important;
  }
  body.single-post .ags-c-postbody__content .elementor-widget-container tr {
    border-bottom: 1px solid rgba(142, 30, 0, 0.14);
  }
  body.single-post .ags-c-postbody__content .elementor-widget-container tr:last-child { border-bottom: 0; }
  body.single-post .ags-c-postbody__content .elementor-widget-container th, body.single-post .ags-c-postbody__content .elementor-widget-container td { border-bottom: 0; }
  body.single-post .ags-c-postbody__content .elementor-widget-container :is(th, td) { border-bottom: 0; }
}
/* =============================================================================
   TABLET (641–1023px) — review pass 2026-09-21.
   -----------------------------------------------------------------------------
   The mobile reflow (@media max-width:1023px) is tuned at ~390px; at tablet widths
   the text-heavy pages ran full-width (single-post prose hit ~110 chars/line at 1023)
   and the article cards went one-per-row (oversized, lots of scrolling). This block
   overrides the mobile rules for 641–1023 ONLY (it comes after the mobile block, so it
   wins on source order at equal specificity): cap the prose measure + centre it, and
   drop the two card grids to 2-up. Full-bleed elements (hero/body photos, band
   backgrounds) are deliberately NOT capped — only the reading columns. Below 641px the
   phone layout is unchanged. ============================================================ */
@media (min-width: 641px) and (max-width: 1023px) {
  /* single-post: comfortable reading measure, centred (was full-width) */
  body.single-post .ags-c-postbody__content .elementor-widget-container { max-width: 680px; margin-left: auto; margin-right: auto; }
}

/* ---- BLOCK-EDITOR BLOCKS (guide review 2026-10-02, item 12) — new posts written with the block editor.
   The editor offers only these blocks for posts (inc/single-post.php ags_post_allowed_blocks). Paragraph,
   headings, lists, tables, images and embeds use the rules above; these cover the rest, from the V4 Variables.
   Separate Image blocks in a row still become a photo row (js/ageshio-content-photorows.js); a Gallery block
   keeps its own grid. ---- */
body.single-post .ags-c-postbody__content .elementor-widget-container .wp-block-gallery {
  display: grid !important; grid-template-columns: repeat(3, minmax(0, 1fr));
  grid-gap: calc(20 * var(--space-stage-unit-display));
  gap: calc(20 * var(--space-stage-unit-display)); margin: calc(28 * var(--space-stage-unit-display)) 0;
}
body.single-post .ags-c-postbody__content .elementor-widget-container .wp-block-gallery > figure {
  width: auto !important; margin: 0 !important;
}
body.single-post .ags-c-postbody__content .elementor-widget-container .wp-block-gallery img {
  display: block; width: 100%; height: 100%; aspect-ratio: 4 / 3; -o-object-fit: cover; object-fit: cover; margin: 0;
  border-radius: var(--radius-field);
}
body.single-post .ags-c-postbody__content .elementor-widget-container blockquote {
  margin: calc(28 * var(--space-stage-unit-display)) 0; padding: 0 0 0 calc(24 * var(--space-stage-unit-display));
  border: 0; border-left: 3px solid var(--color-brand);
  font-family: var(--font-display), sans-serif; font-size: calc(var(--text-body) * 1.25); font-weight: 400;
  line-height: 1.35; color: var(--color-brand);
}
body.single-post .ags-c-postbody__content .elementor-widget-container blockquote p { margin: 0; font: inherit; color: inherit; }
body.single-post .ags-c-postbody__content .elementor-widget-container blockquote cite {
  display: block; margin-top: 10px; font-family: var(--font-body), sans-serif; font-size: var(--text-small);
  font-style: normal; color: var(--color-muted);
}
body.single-post .ags-c-postbody__content .elementor-widget-container .wp-block-buttons {
  display: flex; flex-wrap: wrap; gap: 12px; margin: calc(28 * var(--space-stage-unit-display)) 0;
}
body.single-post .ags-c-postbody__content .elementor-widget-container .wp-block-button__link {
  display: inline-flex; align-items: center; justify-content: center;
  padding: var(--space-btn-pad-block) var(--space-btn-pad-inline); border: 0; border-radius: var(--radius-pill);
  font-family: var(--font-ui), sans-serif; font-size: var(--text-button); font-weight: 700; line-height: 1.4;
  color: var(--color-cream) !important; background: var(--color-brand) !important; -webkit-text-decoration: none !important; text-decoration: none !important;
  transition: background-color .2s;
}
body.single-post .ags-c-postbody__content .elementor-widget-container .wp-block-button__link:hover { background: var(--color-brand-dark) !important; }
body.single-post .ags-c-postbody__content .elementor-widget-container hr {
  width: 100%; height: 0; margin: calc(40 * var(--space-stage-unit-display)) 0; border: 0;
  border-top: 1px solid var(--color-rule); opacity: 1; background: none;
}
@media (max-width: 1023px) {
  body.single-post .ags-c-postbody__content .elementor-widget-container .wp-block-gallery { grid-template-columns: 1fr; gap: 20px; margin: 24px 0; }
  body.single-post .ags-c-postbody__content .elementor-widget-container .wp-block-gallery img { height: auto; aspect-ratio: auto; }
  body.single-post .ags-c-postbody__content .elementor-widget-container blockquote:not(.does-not-exist), body.single-post .ags-c-postbody__content .elementor-widget-container .wp-block-buttons { margin: 24px 0; }
  body.single-post .ags-c-postbody__content .elementor-widget-container :is(blockquote, .wp-block-buttons) { margin: 24px 0; }
  body.single-post .ags-c-postbody__content .elementor-widget-container blockquote { padding-left: 16px; }
  body.single-post .ags-c-postbody__content .elementor-widget-container hr { margin: 32px 0; }
}
