/* ==========================================================================
   EC Carpets Rugs — DESIGN TOKENS  ·  v9b ("Gallery" → Revised)
   ==========================================================================
   Synced VERBATIM from the v9b mockup css/tokens.css (:root block). This is the
   single control surface; the shell and every block sheet consume these names.
   Parity rule (docs/DESIGN-SYNC.md): a token change in the mockup is synced by
   re-copying this block. Do NOT invent theme-only names for tokenised things.

   BASELINE SYNC (2026-07-25): design source of truth moved from v4b to v9b.
   Headline v9b changes carried in below: --surface flips bone → WHITE; the
   Woodland Sage brand green is the interactive/highlight/default-button + footer
   colour; a serif DISPLAY face (--font-serif, IvyPresto Display via Typekit) is
   introduced for h2 + banner headings while body/nav/labels stay Gordita; and
   the floating-glass chrome geometry tokens are new.

   GOLD RETIREMENT (2026-08-16 resync): the earlier sync left gold as the ROOT
   accent and swapped it to sage only in the folded override layer in
   pb-shell.css. It is now retired AT THE ROOT: --accent, --accent-hover,
   --focus-ring-inverse, --cta-accent and --text-accent all read the Woodland
   Sage ramp, which grows to FOUR steps (pale / base / deep / ink) because the
   base mid tone cannot carry the small-text and on-dark jobs the light gold
   used to. The two gold definitions survive UNREFERENCED, as a record — see the
   RETIRED note in the palette. Nothing should be re-pointed at them.

   FONTS: Gordita is self-hosted (@font-face below); the src paths are repointed
   to the theme's assets/fonts/ (this file lives in assets/styles/parts/, so
   ../../fonts/ from here — the mockup used ../assets/fonts/). IvyPresto Display
   is loaded per-page from Typekit (enqueued in functions.php:
   https://use.typekit.net/fex7lpz.css) and resolved by --font-serif below.
   ========================================================================== */

/* ---- Self-hosted brand type: Gordita, weights 300/400/500 ONLY. -------- */
@font-face {
  font-family: "Gordita";
  src: url("../../fonts/Gordita-Light.woff2") format("woff2"),
       url("../../fonts/Gordita-Light.woff") format("woff");
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Gordita";
  src: url("../../fonts/Gordita-Regular.woff2") format("woff2"),
       url("../../fonts/Gordita-Regular.woff") format("woff");
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: "Gordita";
  src: url("../../fonts/Gordita-Medium.woff2") format("woff2"),
       url("../../fonts/Gordita-Medium.woff") format("woff");
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

:root {
  /* ======================================================================
     1. PALETTE
     ====================================================================== */

  /* Raw brand hues (unchanged VALUES — reference the semantic roles below, so a
     re-theme only touches this block). */
  --bone:        #F5F4F1;  /* dominant light surface                        */
  --bone-deep:   #EDEBE6;  /* recessed surface / placeholder field base     */
  --slate:       #546067;  /* primary text                                  */
  --slate-deep:  #3D4B51;  /* dark anchor — now INK / tiny-chip use ONLY     */
  --sage:        #A3A190;  /* muted secondary accent                        */
  --sage-tint:   #C9C8BC;  /* hairlines / very quiet lines on bone          */
  /* Clay — an OFFICIAL brand secondary (brand doc 11), documented in
     styleguide.html as "not used in this skin". That changes here: the shop
     card's "Most Popular" flag is the first thing in v9b to use it. It is a MID
     tone like Woodland Sage, so it takes DARK ink, not white — white on it is
     2.39:1 (fail), --near-black is 6.17:1. No deeper step is added: nothing yet
     needs one. */
  --clay:        #BFA19C;  /* official brand secondary — card flags          */
  /* RETIRED (v9b, client review): gold is no longer used anywhere in this
     variation — every consumer now reads the Woodland Sage ramp below. The two
     definitions are KEPT, unreferenced, because the other variations (v4b, v8b)
     share this design language and the hues are part of the written record.
     Do not re-point anything at them. */
  --gold:        #D3B574;  /* RETIRED — was the primary interactive accent  */
  --gold-deep:   #B8965A;  /* RETIRED — was accent hover / on-bone text     */
  --brick:       #C44D48;  /* alert / sale / sparing emphasis only          */
  --white:       #FFFFFF;
  --near-black:  #23292C;  /* deepest text on bone when slate is too soft   */

  /* Woodland Sage — the v9b brand accent (Pantone 414 C, EC brand guidelines).
     NEW tokens; deliberately NOT a repurpose of --sage (which stays on
     hairlines / swatches). This is the interactive/brand green that in v9b
     drives the button fills, the footer field and EVERY place v4 used gold.
     Deep step is the hover/pressed tone. AA note: sage #A3A393 is a mid
     tone — white text on it is ~2.0:1 (large/UI only, not body), slate-deep ink
     on it is ~4.0:1; the footer + buttons pick inks accordingly below.

     A FOUR-STEP RAMP, not two. Retiring gold handed Woodland Sage two jobs the
     base tone cannot do, because #A3A393 is a MID tone (relative luminance
     0.36) and gold #D3B574 was a LIGHT one (0.48):
       • small accent TEXT on the white/bone/sand page — base sage is ~2.2:1
         there, a clear AA fail, and was exactly why gold needed the deep bronze
         --text-accent in the first place;
       • small accent text ON a dark chip or over dark photography — base sage
         is 3.5:1 on slate-deep, WORSE than the gold it replaces (4.58:1).
     So the ramp gains a PALE step (ink on dark grounds) and an INK step (text
     on light grounds). Both sit on Woodland Sage's own hue axis — R == G with
     B a step lower, exactly the relationship #A3A393 has — so they read as the
     same colour darkened / lightened, not as new hues. Contrasts measured with
     the mockup's accent-contrast script on rendered pixels, not arithmetic. */
  --woodland-sage-pale: #D5D5C8;  /* accent INK on dark chips / dark imagery
                                     (6.1:1 on slate-deep — gold was 4.58:1)   */
  --woodland-sage:      #A3A393;  /* v9b brand/interactive green — FILLS/lines */
  --woodland-sage-deep: #8B8B7D;  /* hover / pressed / deeper step; also the
                                     tone for THIN lines + marks on light      */
  --woodland-sage-ink:  #515134;  /* small accent TEXT on light grounds
                                     (white 8.1:1 · bone 7.4:1 · sand-deep 6.0:1
                                     — the retired bronze managed 6.3:1 on bone) */

  /* Warm SAND surfaces — v4 keeps two quiet sand tones (inherited hues from v3)
     but uses them SPARINGLY. In v3 the wash flooded every formerly-dark band; in
     v4 the page is one bone ground and these sand tones appear only where a beat
     genuinely needs its own surface: the closing CTA band (sand-deep) and the
     rare recessed well. The bright warm sand also seats the rounded media frames
     softly against the bone. AA is documented against both tones throughout. */
  --sand:            #EFE7DA;  /* soft warm sand — recessed wells only            */
  --sand-deep:       #E7DCC6;  /* one step deeper — the CTA closing surface        */
  /* (v3 alias kept so any stray consumer resolves; v4 no longer floods with it) */
  --beige-wash:      var(--sand);
  --beige-wash-deep: var(--sand-deep);

  /* --- Semantic surface roles ---
     THE load-bearing v4 change: --surface-inverse (which drives the utility
     strip, the footer, and every .section--inverse band — Home Three-Simple-
     Steps / CTA / Designers-Edit, DE Trick-of-the-Trade / Edit-Intro / close,
     FAQ Quick-Links, PDP Warmth band) is re-pointed to the BONE ground. That
     flattens all of them into the single light page at once; theme.css then
     lets SPACE + hairlines carry the separation those bands used to get from a
     surface change (v4 POV: separate with air, not colour). The lone exception
     is the CTA band, which keeps its own distinct surface via --cta-* below. */
  --surface:            var(--white);       /* v9b: pure white page ground (was bone) */
  --surface-recessed:   var(--bone-deep);   /* the rare quiet well            */
  --surface-inverse:    var(--white);       /* v9b: flattened bands ride white too
                                               (v4 pointed this at bone)       */
  --surface-raised:     var(--white);       /* the rare true-white lift      */
  /* Closing CTA band surface + inks (Home §9b) — the ONE band that keeps a
     distinct surface (standing user rule). v4 chooses a quiet RECESSED SAND
     field (--sand-deep #E7DCC6) with dark ink + a soft rounded media language,
     NOT v3's saturated gold slab — a calm, considered close in keeping with the
     whitespace POV, still clearly its own beat between the (bone) FAQ's above
     and the (bone) footer below. The accent (Woodland Sage since gold was
     retired) is reserved for the button fill + a hairline accent, never a full
     field here. AA on --sand-deep #E7DCC6:
     near-black #23292C 10.6:1 (title/aside), slate-deep #3D4B51 6.5:1 (eyebrow/
     body), slate #546067 4.66:1 (all ≥ 4.5). The primary CTA keeps N1's two-
     state spirit re-inked for the sand ground: slate-deep OUTLINE at rest →
     solid slate-deep FILL with bone ink on hover/focus (bone on slate-deep
     9.1:1). */
  --cta-surface:        var(--sand-deep);
  --cta-ink:            var(--slate-deep);
  --cta-ink-strong:     var(--near-black);
  --cta-eyebrow:        var(--slate-deep);
  --cta-muted:          var(--slate-deep);
  --cta-btn-ink:        var(--slate-deep);
  --cta-btn-fill:       var(--slate-deep);
  --cta-btn-fill-ink:   var(--bone);
  --cta-accent:         var(--woodland-sage); /* the single hairline accent (was
                                                 gold). No consumer reads this
                                                 today; kept so a future CTA rule
                                                 lands on sage, not on the
                                                 retired hue.                   */

  /* --- Semantic text roles ---
     In v4 the on-inverse family sits on the BONE ground (surface-inverse → bone),
     so it stays DARK ink exactly as on the default page — the former "inverse"
     bands are just the page now. All the AA-safe inks v3 tuned for bone + beige
     still hold (bone is the easier ground of the two), so the values carry over.

     MUTED-TEXT AA (carried from v3): --text-muted is a darker WARM TAUPE (not the
     decorative --sage, which is only ~2:1 on light). AA: bone 5.91:1. --sage is
     UNCHANGED so the decorative hairline-strong rule + palette swatch keep their
     hue — only text consumers use the taupe. */
  --sage-ink:           #5F5847;            /* warm muted TEXT ink (AA on light) */
  --text:               var(--slate);       /* body                          */
  --text-strong:        var(--near-black);  /* headings that need weight      */
  --text-muted:         var(--sage-ink);    /* captions, meta, placeholder cap — warm taupe, AA-safe on bone + sand */
  --text-on-inverse:    var(--slate);       /* on the bone ground now — dark body */
  --text-on-inverse-mut:#4E5456;            /* AA muted on bone (6.5:1) + sand    */
  /* ACCENT-TEXT AA: --text-accent is the DEEP step of the accent, so small accent
     TEXT (body links, faq eyebrows, help links, hover states) clears AA on light.
     v9b re-points it from the retired gold's bronze (#6F5628) to Woodland Sage's
     own ink step — the whole point of a deep step is that the mid accent, gold or
     sage, is ~2.2-2.5:1 as small text on the light page and cannot carry copy.
     Base Woodland Sage stays on FILLS / RAILS / RINGS / underline lines. */
  --text-accent:        var(--woodland-sage-ink);  /* accent TEXT ink — AA-safe on white, bone + sand */

  /* --- Interactive roles ---
     accent-contrast is the RESTING outline ink/line of the primary CTA (shell
     N1) as well as the ink notionally sitting on the accent fill. It stays
     slate-deep and must NOT follow the accent to sage: at rest the button is
     transparent, so a sage line/label would be ~2.2:1 on the white page. The ink
     that actually sits ON the sage fill is set to white by the folded [K] block.
     focus-ring stays the dark anchor for a high-contrast ring on the light page
     (8.6:1). focus-ring-inverse (the ring over dark imagery — in practice only
     the mega promo tiles; the folded [C2] block relaxes the rest back to the dark
     ring) moves from gold to Woodland Sage. Note sage is DARKER than gold was
     (luminance 0.36 vs 0.48), so the ring loses a little contrast against a dark
     tile — 6.6:1 on near-black where gold gave 8.6:1 — comfortably clear of the
     3:1 a non-text indicator owes, and verified over the real promo photography. */
  --accent:             var(--woodland-sage);
  --accent-hover:       var(--woodland-sage-deep);
  --accent-contrast:    var(--slate-deep);  /* resting CTA line/ink; NOT on-fill */
  --focus-ring:         var(--slate-deep);  /* high-contrast focus outline   */
  --focus-ring-inverse: var(--woodland-sage); /* focus ring over dark imagery */

  /* ======================================================================
     2. HAIRLINE & BORDER TREATMENT
     The operational form of "space and hairlines, never boxes" (plan §2.3).
     Default separator is a 1px low-contrast rule — reach for this before any
     bordered container.
     ====================================================================== */
  --hairline-width: 1px;
  --hairline:        var(--sage-tint);            /* on bone                 */
  --hairline-strong: var(--sage);                 /* when a rule must read   */
  --hairline-inverse: rgba(61,75,81,0.18);        /* WAS white-18% → slate-deep
                                                     translucent, so the rule
                                                     reads on the beige wash    */
  --rule:            var(--hairline-width) solid var(--hairline);
  --rule-strong:     var(--hairline-width) solid var(--hairline-strong);
  --rule-inverse:    var(--hairline-width) solid var(--hairline-inverse);

  /* ======================================================================
     3. TYPOGRAPHY
     One family (Gordita placeholder). Hierarchy is built from SIZE / SPACING
     / COLOUR / WEIGHT-within-300-500 — never fake-bold (plan §4.9).
     Display font is aliased so the incoming brand font swaps in one line.
     ====================================================================== */
  --font-display: "Gordita", "Helvetica Neue", Arial, sans-serif; /* [SWAP-READY] */
  --font-body:    "Gordita", "Helvetica Neue", Arial, sans-serif;
  --font-mono:    ui-monospace, "SFMono-Regular", "Menlo", monospace; /* dims/price read-outs only */
  /* v9b (brief A2/A3): IvyPresto Display (Typekit, loaded per page head). The
     serif DISPLAY face for section h2s + banner headings. Base weight is Regular
     400 (see theme.css [J]); the family also carries 100/600/700 + italics for
     the occasional tasteful accent. Body/nav/labels stay Gordita. */
  --font-serif:   "ivypresto-display", "Times New Roman", Georgia, serif;

  --w-light:  300;
  --w-regular:400;
  --w-medium: 500;   /* the heaviest weight the system permits */

  /* Fluid type scale — clamps so a single file serves desktop + mobile with
     no breakpoint juggling. V4 lifts the DISPLAY + H1 ceilings a step and lets
     headings breathe larger against the open bone ground (the whitespace POV
     wants confident, generous display type, not sparse-feeling small heads);
     H2–H4 + body are held at the round-1 values so the reading rhythm and the
     configurator/PDP density are unchanged. Ratio ~1.25 (major third), looser at
     display. min / preferred(vw) / max, in rem. */
  --fs-display: clamp(3rem, 1.5rem + 6.4vw, 6rem);        /* hero headline — larger */
  --fs-h1:      clamp(2.5rem, 1.6rem + 3.9vw, 4rem);      /* page H1 — a step up     */
  --fs-h2:      clamp(1.75rem, 1.3rem + 1.9vw, 2.5rem);    /* section head    */
  --fs-h3:      clamp(1.375rem, 1.15rem + 0.9vw, 1.75rem); /* sub-section     */
  --fs-h4:      clamp(1.125rem, 1.0rem + 0.5vw, 1.3125rem);/* card/tile title */
  --fs-lead:    clamp(1.125rem, 1.0rem + 0.55vw, 1.4375rem);/* intro paragraph — a touch larger */
  --fs-body:    1rem;                                      /* 16px base       */
  --fs-sm:      0.875rem;                                  /* 14px meta       */
  --fs-xs:      0.75rem;                                   /* 12px micro      */
  --fs-eyebrow: 0.75rem;                                   /* tracked labels  */

  --lh-tight:   1.05;   /* display / large headlines — tighter at v4's big sizes */
  --lh-heading: 1.15;   /* h2–h4 — a hair tighter for the lighter weight        */
  --lh-snug:    1.35;   /* leads, tile titles                   */
  --lh-body:    1.6;    /* body copy — room to breathe          */
  --lh-loose:   1.7;    /* long-form editorial                  */

  /* Letter-spacing. Wide tracking on eyebrow capitals echoes the logo's
     "EST · 1963" lockup — a deliberate brand tie. V4 tightens the display track
     a touch more so the larger, lighter headings read crisp, not loose. */
  --ls-display:  -0.028em;  /* large type tightens — v4 goes a step further      */
  --ls-heading:  -0.015em;
  --ls-body:      0;
  --ls-eyebrow:   0.24em;   /* the signature tracked micro-label — v4 opens it   */
  --ls-caps:      0.12em;   /* buttons / nav caps                */

  /* ======================================================================
     4. SPACING — 8px grid (plan §2.4). Every gap/pad references these.
     ====================================================================== */
  --space-3xs: 0.25rem;  /*  4px */
  --space-2xs: 0.5rem;   /*  8px */
  --space-xs:  0.75rem;  /* 12px */
  --space-sm:  1rem;     /* 16px */
  --space-md:  1.5rem;   /* 24px */
  --space-lg:  2rem;     /* 32px */
  --space-xl:  3rem;     /* 48px */
  --space-2xl: 4rem;     /* 64px */
  --space-3xl: 6rem;     /* 96px */
  --space-4xl: 8rem;     /*128px */

  /* Section rhythm — the vertical space between page sections. v9b tightens the
     pacing NOTABLY (brief A5: ~40–50% less than v4): the revision wants a denser,
     more contemporary rhythm than v4's very airy gallery spacing. Reduced ~46%
     at every stop (min/pref/max) so the compression holds across mobile→desktop.
     v4 values kept in comments for reference.
       was --section-gap:   clamp(3.5rem, 2.5rem + 4.5vw, 7rem)
       was --section-gap-s: clamp(2.5rem, 2rem + 2.5vw, 4rem) */
  --section-gap:   clamp(1.9rem, 1.35rem + 2.45vw, 3.75rem);
  --section-gap-s: clamp(1.375rem, 1.1rem + 1.35vw, 2.25rem);

  /* ======================================================================
     5. LAYOUT
     ====================================================================== */
  --container-max: 1360px;  /* editorial max width                          */
  --container-wide:1600px;  /* near-full-bleed sections                     */
  --measure:       68ch;    /* readable long-form column                    */
  /* v9b (brief A13 → S9): --gutter is the PAGE-EDGE side padding (the .container
     side pad, the .image-band viewport inset, the sticky-atc bar pad). Round 1 set
     it to a flat 10px with the rest of the "10px motif"; S9 finds that too tight at
     the page edge and grows it to 20px. The 10px motif LIVES ON in --gap below (grid/
     carousel BETWEEN-item gaps) and in --chrome-inset (the floating-bar inset), which
     both explicitly stay 10px per S9. (v4 was a fluid clamp(1.25rem,0.5rem+3vw,3rem).) */
  --gutter:        20px;    /* page side padding — v9b (S9: was 10px)        */
  /* The 10px "motif" gutter for BETWEEN-item gaps: product/range/easy grid gaps,
     carousel slide gaps, and the peek-rail edge inset + inter-card gap. Split out
     from --gutter in S9 so the page-edge padding can grow to 20px while every
     between-item gap holds at the tuned 10px. */
  --gap:           10px;    /* grid / carousel between-item gap — 10px motif */
  --tap-min:       44px;    /* WCAG minimum touch target (plan §7)           */

  /* ======================================================================
     6. RADII (ARCHITECTURAL) — HELD AT ROUND-1 VALUES ON PURPOSE.
     These drive functional geometry in the configurator (css/range.css): the
     rug's corner-style step, the corner dots and the round-shape preview all
     read --radius-* to PREVIEW real corner radii. Inflating them would make the
     configurator lie, so v4's rounded-imagery language rides the SEPARATE
     --radius-media family in §6b instead. Buttons get their own soft radius
     (--radius-btn) so they can round without touching the architectural scale.
     ====================================================================== */
  --radius-none: 0;
  --radius-xs:   2px;
  --radius-sm:   4px;
  --radius-md:   8px;
  --radius-pill: 999px;
  --radius-circle: 50%;
  --radius-btn:  10px;   /* v4 buttons — softly rounded, not a hard pill        */

  /* ======================================================================
     6b. RADII (MEDIA) — the v4 rounded-imagery scale. The load-bearing shape
     change: every photo / media frame on the site rounds to one of these, so
     the imagery reads as a considered gallery of soft-cornered frames against
     the bone ground. Applied EXPLICITLY by shell.css + page CSS to the media
     primitives (.product-tile__media, .trio__media, .two-up__media, .image-band,
     .mega__promo, .faq-entry, .edit-hero, hero .ph, DE cards …), never to the
     configurator's rug geometry. Three steps so small tiles, standard media and
     large hero/feature frames stay in proportion.
     ====================================================================== */
  --radius-media-sm:  10px;  /* small tiles, chips-of-image, swatched cells      */
  --radius-media:     18px;  /* the standard media frame (tiles, two-up, stage)  */
  --radius-media-lg:  28px;  /* large hero / feature / gallery frames            */

  /* ======================================================================
     7. ELEVATION — used sparingly and softly. Prefer hairlines to shadows;
     these exist only for overlays (menu panel, info pop-up, sticky bar).
     ====================================================================== */
  --shadow-panel: 0 8px 40px -12px rgba(35,41,44,0.28);
  --shadow-pop:   0 4px 24px -8px rgba(35,41,44,0.22);
  --shadow-sticky:0 -4px 20px -10px rgba(35,41,44,0.22);
  --scrim:        rgba(35,41,44,0.44);   /* menu / overlay backdrop          */
  /* v9b floating-glass chrome (brief A11): a soft lift under each floating bar so
     it reads as a card hovering over the page, not a flat strip. */
  --shadow-glass: 0 6px 24px -10px rgba(35,41,44,0.28);

  /* ======================================================================
     7b. FLOATING GLASS CHROME GEOMETRY  (v9b, brief A11/A12)
     The utility strip + header become two SEPARATE floating glass bars, inset
     from the viewport edges, with a visible gap between them. These tokens are
     the single source of truth for that geometry so shell.css (bar placement),
     home.css (hero pull-up) and nav.js (--mega-top / scroll swap) all agree.
     ====================================================================== */
  --chrome-inset:  10px;   /* inset from top/left/right viewport edges (10px motif) */
  --chrome-gap:    7px;    /* visible gap BETWEEN the strip and the header bar      */
  --ustrip-h:      40px;   /* utility strip bar height (one rotating line)          */
  --header-h:      68px;   /* header bar height (was an inline fallback in home.css)*/
  --chrome-radius: 14px;   /* rounded corners on both glass bars (12–16px band)     */
  /* Convenience: the y-offset of the header bar top, and the total height the
     whole chrome occupies from the viewport top (used for body offset + hero). */
  --header-top:    calc(var(--chrome-inset) + var(--ustrip-h) + var(--chrome-gap));
  --chrome-bottom: calc(var(--header-top) + var(--header-h));

  /* ======================================================================
     8. Z-LAYERS — one ladder for the whole set so overlays never fight.
     ====================================================================== */
  --z-base:        0;
  --z-raised:      10;
  --z-sticky-bar:  200;   /* mobile sticky add-to-cart                       */
  --z-header:      400;   /* main header / utility strip                     */
  --z-scrim:       500;   /* menu backdrop                                   */
  --z-menu:        600;   /* mega-menu panel (above scrim)                   */
  --z-popover:     700;   /* per-step info pop-ups                           */
  --z-toast:       800;

  /* ======================================================================
     9. MOTION — restraint (plan §7). Short, calm, transform/opacity only.
     Durations/easings are tokens so reduced-motion can zero them globally.
     ====================================================================== */
  --dur-fast:  120ms;
  --dur-base:  220ms;
  --dur-slow:  360ms;   /* panel slide / hero cross-fade                     */
  --ease-standard: cubic-bezier(0.4, 0, 0.2, 1);
  --ease-out:      cubic-bezier(0.16, 1, 0.3, 1);   /* decisive entrance     */
  --ease-in:       cubic-bezier(0.4, 0, 1, 1);

  /* ======================================================================
     10. PLACEHOLDER FIELDS
     Tasteful stand-ins for lifestyle photography — a flat tone / subtle
     gradient with a discreet caption of the intended shot. NEVER a grey box
     with an X (plan brief, planning/05). Page CSS composes these.
     ====================================================================== */
  --ph-tone-1: linear-gradient(135deg, #E7E4DD 0%, #D8D4CB 100%); /* warm    */
  --ph-tone-2: linear-gradient(135deg, #D9DBD6 0%, #C4C7C0 100%); /* cool    */
  --ph-tone-3: linear-gradient(160deg, #E9E2D4 0%, #D5C9B2 100%); /* sand    */
  /* Empty-slot tones only ever show before real photography drops in; every .ph
     on the delivered pages carries an <img>, so these are fallbacks. Kept in the
     warm sand family so any bare slot reads light + warm on the bone ground
     (consistent with v4's sand accents). */
  --ph-tone-dark: linear-gradient(150deg, #EFE7DA 0%, #E3D6BE 100%); /* warm sand */
  --ph-caption:  var(--slate);
}

/* ==========================================================================
   REDUCED MOTION — honour the OS preference everywhere (plan §7). Zero the
   motion tokens and neutralise any transition/animation the pages declare,
   so no component has to remember to handle this itself.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  :root {
    --dur-fast: 0ms;
    --dur-base: 0ms;
    --dur-slow: 0ms;
  }
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
/* NB: the mobile --ustrip-h: 36px override lives in the shell's utility-strip
   media query (pb-shell.css §2), matching the v9b mockup's placement. */
