/* frontend/styles/main.css -- Styled by Sarvi design system (FROZEN shared foundation)
   Order: tokens -> reset -> base type -> components -> motion -> a11y -> responsive.
   Edited only in Phase 1; frozen for Phase 2. Page-specific CSS lives in styles/pages/. */

/* ============================================================
   TOKENS
   ============================================================ */
:root {
  /* ===== BRAND PALETTE (exact hex -- Sarvi's swatch) ===== */
  --c-onyx:        #1E1F20;  /* all body + heading text */
  --c-sage:        #50554F;  /* secondary accent, links on canvas, CTA fill, dark bands */
  --c-blush:       #BC9C9B;  /* FILL/SURFACE/PHOTO-MAT ONLY -- never text-on-light, never border-on-light */
  --c-alabaster:   #CDCBCE;  /* heavier section surface tone; never text, never border */

  /* ===== CANVAS + SURFACES (warm near-white; NOT pure #FFF) ===== */
  --c-canvas:           #FAF9F7;  /* page background */
  --c-surface:          #F1F0EE;  /* faint warm alternating-band surface (Onyx 14.5:1) */
  --c-surface-alabaster:#CDCBCE;  /* Alabaster as a heavier band (Onyx 10.24:1) */
  --c-surface-blush:    #EDE2E1;  /* desaturated blush tint surface (Onyx 13.02:1) */

  --c-sage-deep:   #3D413C;  /* sage hover/active depth */

  /* ===== SEMANTIC TEXT ROLES ===== */
  --t-body:          var(--c-onyx);       /* 15.69:1 on canvas */
  --t-muted:         var(--c-sage);       /* 7.25:1 on canvas ONLY */
  --t-on-dark:       var(--c-canvas);     /* light text on sage/onyx bands */
  --t-on-dark-muted: var(--c-alabaster);  /* secondary text on dark -- LARGE only */

  /* ===== LINES / BORDERS / FOCUS (clear 3:1 -- Onyx-based, never Blush/Alabaster) ===== */
  --line-hairline: rgba(30,31,32,0.12);   /* dividers, card edges on light */
  --line-strong:   rgba(30,31,32,0.24);   /* hover border on light */
  --focus-ring:    var(--c-onyx);
  --line-on-dark:  rgba(250,249,247,0.20);

  /* ===== CTA ===== */
  --accent-cta-bg:    var(--c-sage);
  --accent-cta-text:  var(--c-canvas);    /* 7.25:1 */
  --accent-cta-hover: var(--c-sage-deep);

  /* ===== ADDED ACCENT (the one bold pop colour, ~10% coverage) =====
     BLACK CHERRY #3A0E1E (JR + Sarvi approved 2026-06-03) -- ONE warm-red accent at
     TWO values: cherry on LIGHT surfaces, its light twin Antique Blush #BC9C9B on DARK blocks
     (cherry is invisible on onyx/sage). Emerald is OUT except the hard-coded footer line.
     Swappable alternates via html[data-accent="terracotta|gold|forest|teal"] kept below. */
  --c-accent:        #3A0E1E;             /* black-cherry fill; canvas label on it 15.8:1 */
  --c-accent-deep:   #2A0A16;             /* cherry hover/active -- a touch deeper (canvas label 17.3:1) */
  --c-accent-on:     var(--c-canvas);     /* text that sits ON the cherry fill -- canvas, high contrast */
  --t-accent:        #3A0E1E;             /* black-cherry AS text on light (prices, numbers, kickers) -- AAA every light band */
  --accent-rule:     #3A0E1E;             /* 2-4px graphical rules on light -- cherry, >=10:1 */
  --accent-on-dark:  #BC9C9B;             /* Antique Blush on onyx/sage blocks -- rose-on-charcoal (literal, NOT var(--c-blush): de-aliased so surface-blush and accent-blush stay independent) */

  /* ===== TYPE ===== */
  --font-serif: "Cormorant Garamond", Georgia, "Times New Roman", serif;
  --font-sans:  "Mulish", system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  --fs-kicker:  0.8125rem;                                 /* 13px */
  --fs-body-sm: 1rem;                                      /* 16px floor */
  --fs-body:    1.125rem;                                  /* 18px DEFAULT */
  --fs-lead:    1.25rem;                                   /* 20px */
  --fs-h4:      clamp(1.5rem, 1.2rem + 1.2vw, 1.75rem);    /* 24-28px */
  --fs-h3:      clamp(1.875rem, 1.4rem + 2vw, 2.5rem);     /* 30-40px */
  --fs-h2:      clamp(2.5rem, 1.8rem + 3.2vw, 3.75rem);    /* 40-60px */
  --fs-h1:      clamp(3.25rem, 2rem + 6vw, 5.5rem);        /* 52-88px */
  --lh-display: 1.05; --lh-heading: 1.15; --lh-body: 1.6; --lh-lead: 1.55;
  --ls-display: -0.01em; --ls-heading: 0; --ls-body: 0.005em; --ls-caps: 0.14em;

  /* ===== SPACING (8-pt grid, 4px half-step) ===== */
  --sp-xs: 4px; --sp-sm: 8px; --sp-md: 16px; --sp-lg: 24px; --sp-xl: 32px;
  --sp-2xl: 48px; --sp-3xl: 64px; --sp-4xl: 96px; --sp-5xl: 128px;

  /* ===== LAYOUT ===== */
  --container-max: 1200px; --container-narrow: 720px;
  --gutter: clamp(24px, 5vw, 80px);
  --header-h: 76px;            /* reserved header height -- prevents CLS on partial inject */
  --radius: 2px; --radius-pill: 9999px;

  /* ===== MOTION ===== */
  --ease-out: cubic-bezier(0.33,1,0.68,1);
  --ease-in:  cubic-bezier(0.42,0,1,1);
  --dur-micro: 180ms; --dur-enter: 360ms; --dur-exit: 240ms;
}

/* ============================================================
   ACCENT VARIANTS -- alternates for previewing. The :root default IS BLACK CHERRY (approved).
   Set on <html>: data-accent="terracotta|gold|forest|teal|emerald" to preview an alternate.
   Each hex is contrast-checked for its exact role over Sarvi's base.

   TERRACOTTA reads on light AND dark -- it can tint text, fills, and rules
   anywhere. GOLD is invisible as text on cream (color.md: yellow/gold text on
   white is below 2:1) -- so in the gold variant the bright gold lives on FILLS
   (dark text on it) and on the DARK value-blocks (where brass-on-charcoal sings
   at ~8:1); gold-as-text on light falls back to a dark-brass that still clears
   4.5:1. The tokens absorb that asymmetry so page CSS never branches. */
html[data-accent="terracotta"]{
  --c-accent:       #A0451F;  /* clay fill -- canvas label 5.3:1 */
  --c-accent-deep:  #8A3A18;  /* hover + text-on-canvas 6.8:1 */
  --c-accent-on:    var(--c-canvas);  /* light text on the fill */
  --t-accent:       #8A3A18;  /* terracotta AS text on canvas */
  --accent-rule:    #8A3A18;  /* rules on light */
  --accent-on-dark: #E0926A;  /* light clay on onyx/sage = ~6:1 */
}
html[data-accent="gold"]{
  --c-accent:       #B8862B;  /* brass-gold fill -- ONYX label on it 5.8:1 */
  --c-accent-deep:  #6E5214;  /* dark brass -- gold-as-text/rule on light 6:1 */
  --c-accent-on:    var(--c-onyx);   /* DARK text on the gold fill */
  --t-accent:       #6E5214;  /* dark-brass text on canvas (bright gold fails here) */
  --accent-rule:    #6E5214;  /* rules on light */
  --accent-on-dark: #DBB45C;  /* bright gold on onyx/sage = ~8:1 (brass plaque) */
}
/* GREEN family (Sarvi-direction, exploring). All three are DARK like terracotta:
   canvas label on the fill, dark-green text on light, and a LIGHTER green for marks
   on the dark blocks (the token absorbs the light/dark asymmetry). They share the
   cool/green family with Dark Sage, so the pop is SATURATION + value, not hue --
   each is pushed saturated enough to read as a distinct accent, not "more sage". */
html[data-accent="forest"]{      /* deep pine -- most cohesive, quietest pop */
  --c-accent:       #245C43;  /* canvas label on fill ~7.4:1 */
  --c-accent-deep:  #1B4A35;  /* hover + text-on-light ~9:1 */
  --c-accent-on:    var(--c-canvas);
  --t-accent:       #1F5239;  /* forest text on canvas ~8:1 */
  --accent-rule:    #1F5239;
  --accent-on-dark: #8FB8A0;  /* soft sage-mint on onyx ~7.5:1 */
}
html[data-accent="emerald"]{     /* jewel green -- richer, pops harder, still clearly green */
  --c-accent:       #1E7A52;  /* canvas label on fill ~5.1:1 */
  --c-accent-deep:  #16653F;  /* hover + text-on-light ~7:1 */
  --c-accent-on:    var(--c-canvas);
  --t-accent:       #136B45;  /* emerald text on canvas ~6:1 */
  --accent-rule:    #136B45;
  --accent-on-dark: #4FBE8A;  /* bright emerald on onyx ~8:1 (jewel on charcoal) */
}
html[data-accent="teal"]{        /* deep petrol teal -- green's bluer cousin, most distinct pop */
  --c-accent:       #1A6A72;  /* canvas label on fill ~5.8:1 */
  --c-accent-deep:  #14545B;  /* hover + text-on-light ~7.5:1 */
  --c-accent-on:    var(--c-canvas);
  --t-accent:       #155C63;  /* teal text on canvas ~6.5:1 */
  --accent-rule:    #155C63;
  --accent-on-dark: #54BFC9;  /* bright teal on onyx ~8.5:1 */
}

/* ============================================================
   RESET + BASE
   ============================================================ */
* { box-sizing: border-box; }
/* overflow-x:clip contains the full-bleed .floral-window's 100vw breakout (100vw
   includes the scrollbar width -> a hairline horizontal scroll otherwise). clip,
   NOT hidden -- it does not establish a scroll container, so it does not affect
   page scrolling or sticky positioning. */
html { -webkit-text-size-adjust: 100%; overflow-x: clip; }
body {
  margin: 0; font-family: var(--font-sans); font-size: var(--fs-body);
  line-height: var(--lh-body); letter-spacing: var(--ls-body);
  color: var(--t-body); background: var(--c-canvas);
  -webkit-font-smoothing: antialiased; text-rendering: optimizeLegibility;
  overflow-x: clip;   /* with html's clip, fully contains the full-bleed 100vw breakout */
}
img { max-width: 100%; display: block; }
a { color: var(--c-sage); }
.skip-link { position: absolute; left: -9999px; top: 0; background: var(--c-onyx); color: var(--c-canvas);
  padding: var(--sp-sm) var(--sp-md); z-index: 100; }
.skip-link:focus { left: var(--sp-md); top: var(--sp-md); }
.container { max-width: var(--container-max); margin-inline: auto; padding-inline: var(--gutter); }
.container--narrow { max-width: var(--container-narrow); }
.section { padding-block: var(--sp-4xl); }            /* 96px desktop */
@media (max-width: 768px){ .section { padding-block: var(--sp-2xl); } } /* 48px mobile */
p { max-width: 66ch; margin-block-end: 1em; }

/* ============================================================
   BASE TYPE
   "bold" = scale + UPPERCASE + tracking at regular weight -- never heavy cuts.
   Headings only >=24px (Cormorant 400-500); body never below 16px, default 18px.
   Never text-align: justify.
   ============================================================ */
h1,.h1{font-family:var(--font-serif);font-weight:500;font-size:var(--fs-h1);line-height:var(--lh-display);letter-spacing:var(--ls-display);color:var(--t-body);margin:0 0 var(--sp-md);}
h2,.h2{font-family:var(--font-serif);font-weight:500;font-size:var(--fs-h2);line-height:var(--lh-heading);margin:0 0 var(--sp-md);}
h3,.h3{font-family:var(--font-serif);font-weight:500;font-size:var(--fs-h3);line-height:var(--lh-heading);margin:0 0 var(--sp-sm);}
h4,.h4{font-family:var(--font-serif);font-weight:600;font-size:var(--fs-h4);line-height:var(--lh-heading);margin:0 0 var(--sp-sm);}

.kicker{font-family:var(--font-sans);font-size:var(--fs-kicker);font-weight:500;text-transform:uppercase;
  letter-spacing:var(--ls-caps);color:var(--c-sage);margin:0 0 var(--sp-sm);}
.lead{font-size:var(--fs-lead);line-height:var(--lh-lead);}
small,.text-fine{font-size:var(--fs-body-sm);}
.wordmark{font-family:var(--font-serif);font-weight:500;font-size:1.75rem;color:var(--c-onyx);text-decoration:none;letter-spacing:.01em;}

/* ============================================================
   COMPONENTS
   ============================================================ */

/* --- Layout primitives -- the shared asymmetric-layout building blocks.
   Every page composes its asymmetric layouts FROM these; page CSS may tweak
   spacing but never redefines the split ratios.

   TWO-CHILD CONTRACT (.split):
     .split has exactly two children --
       .split__media  = the image/figure child
       .split__body   = the text child
     .split--reverse swaps their VISUAL order via 'order' (source order unchanged,
       so the DOM/reading order stays media-then-body for a11y).
     At <=768px the grid collapses to ONE column and stacks media first, then body.
   .stack-asymmetric is the 2fr/1fr grid (wider primary column + narrower aside). */
.split{display:grid;grid-template-columns:1.1fr 0.9fr;gap:var(--sp-3xl);align-items:center;}
.split--reverse{}  /* swaps visual order -- see media query */
.stack-asymmetric{display:grid;grid-template-columns:2fr 1fr;gap:var(--sp-2xl);}
.split--reverse .split__media{order:2;}  /* visual-order swap, source order unchanged */
@media (max-width: 768px){
  .split,.stack-asymmetric{grid-template-columns:1fr;}
}

/* --- Buttons -- targets >=44px (use 52px); on this light theme hover/active darken. */
.btn-primary{display:inline-flex;align-items:center;justify-content:center;min-height:52px;padding:0 var(--sp-xl);
  font-family:var(--font-sans);font-size:var(--fs-body-sm);font-weight:600;text-transform:uppercase;letter-spacing:var(--ls-caps);
  color:var(--c-accent-on);background:var(--c-accent);border:1px solid transparent;border-radius:var(--radius);
  cursor:pointer;text-decoration:none;transition:background-color var(--dur-micro) var(--ease-out);}
.btn-primary:hover{background:var(--c-accent-deep);}
.btn-primary:active{background:var(--c-accent-deep);}
.btn-primary:focus-visible{outline:3px solid var(--focus-ring);outline-offset:3px;}
.btn-primary:disabled{opacity:.45;cursor:not-allowed;}

.btn-secondary{display:inline-flex;align-items:center;justify-content:center;min-height:52px;padding:0 var(--sp-xl);
  font-weight:600;font-size:var(--fs-body-sm);font-family:var(--font-sans);text-transform:uppercase;letter-spacing:var(--ls-caps);
  color:var(--c-onyx);background:transparent;border:1px solid var(--line-strong);border-radius:var(--radius);text-decoration:none;
  transition:border-color var(--dur-micro) var(--ease-out),background-color var(--dur-micro) var(--ease-out);}
.btn-secondary:hover{border-color:var(--c-onyx);background:rgba(30,31,32,.04);}
.btn-secondary:focus-visible{outline:3px solid var(--focus-ring);outline-offset:3px;}

/* On sage/onyx dark bands the sage button vanishes -- use this inverted variant there: */
.btn-on-dark{display:inline-flex;align-items:center;justify-content:center;min-height:52px;padding:0 var(--sp-xl);
  font-weight:600;font-size:var(--fs-body-sm);font-family:var(--font-sans);text-transform:uppercase;letter-spacing:var(--ls-caps);
  color:var(--c-onyx);background:var(--c-canvas);border-radius:var(--radius);text-decoration:none;
  transition:background-color var(--dur-micro) var(--ease-out);}
.btn-on-dark:hover{background:var(--c-alabaster);}
.btn-on-dark:focus-visible{outline:3px solid var(--c-canvas);outline-offset:3px;}
/* Primary CTA on a DARK surface: the cherry fill is invisible on onyx/sage, so the button
   takes the on-dark accent (Antique Blush) fill + onyx text -- the dark-surface twin of the
   cherry button (onyx-on-blush 6.6:1, hover 5.2:1). Light cards (floral-window, blush/cream
   bands) keep the cherry fill. This restores the visible accent button emerald used to give. */
.onyx-peak .btn-primary,.block--dark .btn-primary,.block--sage .btn-primary,
.sage-block .btn-primary,.photo-band .btn-primary,.cta-band .btn-primary,
.services-card--featured .btn-primary{background:var(--accent-on-dark);color:var(--c-onyx);}
.onyx-peak .btn-primary:hover,.block--dark .btn-primary:hover,.block--sage .btn-primary:hover,
.sage-block .btn-primary:hover,.photo-band .btn-primary:hover,.cta-band .btn-primary:hover,
.services-card--featured .btn-primary:hover{background:#A8888A;}

/* --- Header / nav --- */
/* The [data-include] partial wrappers are made boxless so the sticky header's containing
   block is the viewport, NOT the 85px-tall include <div> that wrapped it (a header-height
   parent gave position:sticky zero range -> it scrolled away). display:contents removes the
   wrapper box without touching the header, so the header keeps its backdrop-filter (the
   containing block the mobile-nav fixed overlay relies on). overflow-x:clip on html/body
   pairs with overflow-y:visible, so it does NOT create a scroll container -> the viewport
   stays the sticky scrollport. */
[data-include]{display:contents;}
.site-header{position:sticky;top:0;z-index:50;min-height:var(--header-h);display:flex;align-items:center;
  justify-content:space-between;gap:var(--sp-lg);padding:var(--sp-md) var(--gutter);
  background:color-mix(in srgb,var(--c-canvas) 88%,transparent);backdrop-filter:blur(8px);
  border-bottom:1px solid var(--line-hairline);}

/* --- Auto-hiding header (Headroom pattern, ALL widths) ---
   The header HIDES on scroll-DOWN and REVEALS on scroll-UP, at every width
   (desktop AND mobile). It stays position:sticky; only a transform animates
   (compositor-only, motion.md: "animate transform/opacity only"). nav-autohide
   logic lives in include-partials.js (attached after the header is injected +
   wired); it toggles .site-header--hidden. Transition TRANSFORM only, --dur-exit
   (240ms) ease-out (motion.md duration bucket "Deliberate"/snappy; exits
   shorter than entries). The guards (always-visible near top, reveal on
   scroll-up, reveal on focusin, never hide while the mobile menu is open) all
   live in the JS. The base rule applies at all widths so mobile auto-hides too;
   the backdrop-filter on .site-header (the mobile-nav fixed-overlay containing
   block) stays on the base rule above, untouched. */
.site-header{transition:transform var(--dur-exit) var(--ease-out);will-change:transform;}
.site-header--hidden{transform:translateY(-100%);}

/* REDUCED MOTION: auto-hide is DISABLED in JS (the class is never added),
   so the header stays always-visible. Belt-and-suspenders here -- if a
   .site-header--hidden class ever lands, neutralize it and drop the
   transform transition. Substitute, not strip (motion.md): the persistent
   nav stays fully usable, it simply never auto-hides. */
@media (prefers-reduced-motion: reduce){
  .site-header{transition:none;}
  .site-header--hidden{transform:none;}
}
.site-nav{display:flex;align-items:center;gap:var(--sp-md);}
.nav-link{font:500 var(--fs-kicker)/1 var(--font-sans);text-transform:uppercase;letter-spacing:var(--ls-caps);
  color:var(--c-onyx);text-decoration:none;padding:var(--sp-sm);min-height:44px;display:inline-flex;align-items:center;}
.nav-link:hover{color:var(--c-sage);}
.nav-link[aria-current="page"]{border-bottom:1.5px solid var(--c-sage);}
.nav-cta{margin-left:var(--sp-sm);}
.nav-toggle{display:none;}            /* shown at <=768px (see responsive) */

/* --- Service / price card -- asymmetric, near-sharp, hairline border, no heavy shadow, no translateY lift. */
.service-card{background:var(--c-canvas);border:1px solid var(--line-hairline);border-radius:var(--radius);
  padding:var(--sp-xl);box-shadow:0 1px 2px rgba(0,0,0,.04);transition:border-color 200ms var(--ease-out);}
.service-card:hover{border-color:var(--line-strong);}
.service-card .kicker{color:var(--c-sage);}
.service-card .price{font:500 var(--fs-h3)/1 var(--font-serif);color:var(--t-accent);}
.service-card__tag{display:inline-block;font:600 var(--fs-kicker)/1 var(--font-sans);text-transform:uppercase;
  letter-spacing:var(--ls-caps);color:var(--c-onyx);background:var(--c-surface-blush);padding:var(--sp-xs) var(--sp-sm);border-radius:var(--radius);}
.service-card ul{padding-left:1.1em;}
.service-card li{margin-block-end:var(--sp-xs);}

/* --- Testimonial --- */
.testimonial{background:var(--c-surface-blush);padding:var(--sp-5xl) var(--gutter);text-align:center;}
.testimonial blockquote{font:italic 400 var(--fs-h3)/1.3 var(--font-serif);color:var(--c-onyx);max-width:22ch;margin:0 auto var(--sp-lg);}
.testimonial cite{font:500 var(--fs-kicker)/1 var(--font-sans);text-transform:uppercase;letter-spacing:var(--ls-caps);color:var(--c-sage);font-style:normal;}

/* --- Figures / image bands -- swap-ready aspect boxes so the real shoot drops in without reflow. */
.figure{margin:0;}
.figure img{width:100%;height:100%;object-fit:cover;display:block;border-radius:var(--radius);}
.figure--bleed{width:100vw;margin-inline:calc(50% - 50vw);}
.figure--mat{background:var(--c-surface-blush);padding:var(--sp-xl);}
.figure figcaption{font-size:var(--fs-body-sm);color:var(--c-sage);margin-top:var(--sp-sm);}
.ratio-4x5{aspect-ratio:4/5;} .ratio-3x2{aspect-ratio:3/2;} .ratio-1x1{aspect-ratio:1/1;}

/* --- CTA band + footer --- */
.cta-band{background:var(--c-sage);color:var(--c-canvas);text-align:center;padding:var(--sp-5xl) var(--gutter);}
.cta-band h2{color:var(--c-canvas);} .cta-band .kicker{color:var(--c-canvas);} /* canvas (7.25:1), NOT Alabaster: 13px kicker is not large text, Alabaster-on-sage 4.73:1 fails */
/* A thin EMERALD rule caps the footer -- the SINGLE intentional cool brand-note in an
   otherwise all-warm cherry+blush accent system (JR-LOCKED here, and ONLY here). A value/hue
   BREAK so an Onyx CTA peak above it does not merge into the Onyx footer (Phase 3), and a quiet
   brand line closing every page. HARD-CODED #4FBE8A (7.13:1 on onyx), independent of
   --accent-on-dark (now blush) -- do NOT re-couple it to the accent token. */
/* Onyx SUEDE TEXTURE (JR direction): the colour stays exactly --c-onyx. A neutral
   (single-channel grayscale, mean mid-grey) seamless grain is blended over the onyx
   with background-blend-mode:soft-light, so it ONLY modulates luminance -- a faint
   light/dark suede grain -- and never shifts the hue. Text is unaffected (blend is on
   the background layer only). Drops to flat onyx in forced-colors / no-webp. */
.site-footer{background-color:var(--c-onyx);
  background-image:url('/assets/images/onyx-grain.webp');
  background-size:480px;background-repeat:repeat;background-blend-mode:soft-light;
  color:var(--c-canvas);padding:var(--sp-3xl) var(--gutter) var(--sp-xl);
  border-top:2px solid #4FBE8A;}  /* LOCKED emerald footer line -- hard-coded, NOT var(--accent-on-dark) (now blush) */
@media (forced-colors: active){ .site-footer{background-image:none;} }
.site-footer a{color:var(--c-canvas);text-decoration:none;}
.site-footer__inner{display:flex;flex-wrap:wrap;align-items:flex-start;gap:var(--sp-2xl);max-width:var(--container-max);margin-inline:auto;}
.site-footer__inner > .btn-on-dark{margin-left:auto;align-self:center;color:var(--c-onyx);}
.site-footer__nav{display:flex;flex-direction:column;gap:var(--sp-sm);}
.site-footer__legal{border-top:1px solid #4FBE8A;margin-top:var(--sp-2xl);padding-top:var(--sp-md); /* LOCKED emerald divider -- hard-coded, NOT the accent token */
  display:flex;gap:var(--sp-lg);max-width:var(--container-max);margin-inline:auto;font-size:var(--fs-body-sm);}
.wordmark--footer{font-size:1.5rem;color:var(--c-canvas);margin:0 0 var(--sp-sm);}
.site-footer__tagline{font-family:var(--font-serif);font-style:italic;color:var(--c-alabaster);}

/* --- Forms --- */
.form-field{margin-block-end:var(--sp-lg);}
.form-field label{display:block;font-weight:600;font-size:var(--fs-body-sm);margin-block-end:var(--sp-xs);color:var(--c-onyx);}
.form-field .helper{font-size:var(--fs-body-sm);color:var(--c-sage);margin-block-end:var(--sp-xs);}
.form-field input,.form-field select,.form-field textarea{width:100%;font-family:var(--font-sans);
  font-size:max(1rem,16px);                 /* >=16px stops iOS zoom */
  padding:var(--sp-md);border:1px solid var(--line-strong);border-radius:var(--radius);background:var(--c-canvas);color:var(--c-onyx);}
.form-field input:focus-visible,.form-field select:focus-visible,.form-field textarea:focus-visible{outline:3px solid var(--focus-ring);outline-offset:2px;}
.form-field textarea{min-height:120px;resize:vertical;}
.form-field--error input,.form-field--error textarea{border-color:#7A2E2E;}
.field-error{display:flex;align-items:center;gap:var(--sp-xs);color:#7A2E2E;font-size:var(--fs-body-sm);margin-block-start:var(--sp-xs);}
.field-error::before{content:"\26A0";} /* warning glyph -- icon + text, never colour alone */
.form-status{margin-block-start:var(--sp-md);font-weight:600;}
.checkbox-field{display:flex;gap:var(--sp-sm);align-items:flex-start;}
.checkbox-field input{width:auto;min-height:24px;min-width:24px;}

/* ============================================================
   VALUE BLOCKS + FLORAL WINDOW  (the value-pop redesign primitives)
   ------------------------------------------------------------
   Pop in a deliberately-muted palette comes from VALUE drama (a hard jump to a
   dark block) + one bold ACCENT + the floral at full strength -- never from
   washes/gradients over close tones (color.md: saturation is the volume knob,
   value structure first). These three primitives supply exactly that.
   ============================================================ */

/* Flat dark value-blocks -- a hard jump from near-white canvas to a solid field.
   NO gradient (close-tone gradients drift through drab mid-values). Full-bleed
   band (the section spans edge-to-edge); .container re-centres the content. */
.block--dark{background:var(--c-onyx);color:var(--c-canvas);}
.block--sage{background:var(--c-sage);color:var(--c-canvas);}
.block--dark h1,.block--dark h2,.block--dark h3,.block--dark h4,.block--dark p,.block--dark li,
.block--sage h1,.block--sage h2,.block--sage h3,.block--sage h4,.block--sage p,.block--sage li{color:var(--c-canvas);}
/* On a dark block the kicker, rule, and price carry the accent -- now Antique Blush
   #BC9C9B (rose-on-charcoal), the on-dark twin of the on-light black cherry. */
.block--dark .kicker{color:var(--accent-on-dark);}  /* blush on onyx = 6.6:1, passes AA */
.block--sage .kicker{color:var(--c-canvas);}  /* blush on sage = 3.0:1 FAILS AA for 13px text; canvas 7.25:1. Blush stays on the rule below (graphical 3:1). */
.block--dark .accent-rule,.block--sage .accent-rule{background:var(--accent-on-dark);}
.block--dark .price,.block--sage .price{color:var(--accent-on-dark);}

/* A short accent rule -- a focal mark, 3px x 48px. On light it uses --accent-rule
   (dark enough for 3:1); the dark-block override above swaps it to the on-dark accent. */
.accent-rule{display:block;width:48px;height:3px;background:var(--accent-rule);
  border:0;margin:0 0 var(--sp-md);border-radius:var(--radius);}

/* ------------------------------------------------------------
   NAMED SURFACE UTILITIES (DESIGN.md §6 value-rhythm vocabulary)
   ------------------------------------------------------------
   Every page alternates light -> dark -> light and shows ALL FOUR of her colours
   as REAL surfaces (C2 [LOCKED]). These are the named bands the §6 section map
   references. Onyx stays text + ONE dark peak per page; black cherry lands only on
   focal points (C3). All contrast figures are Onyx-on-light unless noted.

   `cream`  -- the page BASE (#FAF9F7). It is the body default; the .cream class is
              a no-op marker so a section can be tagged cream explicitly when the
              §6 map calls for it (the base ground, ~60%). */
.cream{background:var(--c-canvas);color:var(--c-onyx);}

/* `blush-band` -- Antique Blush TINT surface (#EDE2E1). FILL only (her blush fails
   as text/border on light: 2.39:1). Onyx text on it = 13.02:1. A warm light band. */
.blush-band{background:var(--c-surface-blush);color:var(--c-onyx);}

/* `alabaster-band` -- Alabaster surface (#CDCBCE). FILL only. The cooler / heavier
   light band. Onyx text on it = 10.24:1.
   CLAY TEXTURE (D7, JR-directed): a colour-locked REAL-clay surface -- a top-down
   troweled-clay photo (CC0 Poly Haven "Clay Floor 001") desaturated + re-tinted so the
   MEAN colour stays exactly #CDCBCE and only light/dark RELIEF varies (hue unchanged --
   never reintroduces the rejected terracotta). Sibling of the onyx footer suede.
   4 DISTINCT crops, ONE per alabaster band (--clay-img per section below) so the image
   is never stretched/repeated. cover hero, NOT a tile. Subtle (darkest crack >=4.9:1 vs
   Onyx text). Drops to flat alabaster in forced-colors / if the webp fails to load
   (background-color base is always present). */
.alabaster-band{
  background-color:var(--c-surface-alabaster);
  background-image:var(--clay-img,none);
  background-size:cover;background-position:var(--clay-pos,center);background-repeat:no-repeat;
  color:var(--c-onyx);}
@media (forced-colors: active){ .alabaster-band{background-image:none;} }
/* one distinct clay version per alabaster band (D7) -- no repetition across the site;
   calmer crops on the text-dense bands, busiest on the map slot (least text). */
.home-faq     {--clay-img:url('/assets/images/clay-alabaster-1.webp');}
.about-pov    {--clay-img:url('/assets/images/clay-alabaster-3.webp');}
.services-know{--clay-img:url('/assets/images/clay-alabaster-4.webp');}
.contact-map  {--clay-img:url('/assets/images/clay-alabaster-2.webp');}

/* `sage-block` -- Dark Sage surface (#50554F) with canvas text (7.25:1) and the
   on-dark Antique Blush #BC9C9B for the rule. The MAIN dark value-block (her green). Alias of
   the existing .block--sage (kept; HTML uses both names interchangeably). */
.sage-block{background:var(--c-sage);color:var(--c-canvas);}
.sage-block h1,.sage-block h2,.sage-block h3,.sage-block h4,.sage-block p,.sage-block li{color:var(--c-canvas);}
.sage-block .kicker{color:var(--c-canvas);}  /* blush on sage 3.0:1 FAILS AA text; canvas 7.25:1. Blush lands on the rule (graphical 3:1). */
.sage-block .accent-rule{background:var(--accent-on-dark);}
.sage-block .price{color:var(--accent-on-dark);}

/* `onyx-peak` -- Onyx surface (#1E1F20) with canvas text. The ONE darkest peak per
   page (C2: Onyx reserved for text + 1-2 dark peaks, never the default block).
   Alias of the existing .block--dark (kept; HTML uses both names interchangeably). */
.onyx-peak{background:var(--c-onyx);color:var(--c-canvas);}
.onyx-peak h1,.onyx-peak h2,.onyx-peak h3,.onyx-peak h4,.onyx-peak p,.onyx-peak li{color:var(--c-canvas);}
.onyx-peak .kicker{color:var(--accent-on-dark);}
.onyx-peak .accent-rule{background:var(--accent-on-dark);}
.onyx-peak .price{color:var(--accent-on-dark);}

/* `photo-band` -- a full-bleed denim photo band with a dark scrim so canvas text /
   an Antique Blush kicker stay legible over the image. The image is set per-page (the
   denim shot is a swappable placeholder, C11); this utility supplies the bleed +
   dark scrim + canvas text contract. Onyx 0.72 scrim -> canvas text safe.
   (.figure--bleed remains the bare full-bleed image box; photo-band is the
   text-over-photo band.) */
.photo-band{position:relative;isolation:isolate;color:var(--c-canvas);
  background-color:var(--c-onyx);background-size:cover;background-position:center;background-repeat:no-repeat;}
.photo-band::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:linear-gradient(160deg, rgba(30,31,32,0.78) 0%, rgba(30,31,32,0.62) 100%);}
.photo-band > *{position:relative;z-index:1;}
.photo-band h1,.photo-band h2,.photo-band h3,.photo-band h4,.photo-band p,.photo-band li{color:var(--c-canvas);}
.photo-band .kicker{color:var(--accent-on-dark);}
.photo-band .accent-rule{background:var(--accent-on-dark);}

/* FLORAL WINDOW -- the pivot move, FULL-BLEED floral treatment (D2/F2 [LOCKED]).
   REPLACES the old side-by-side pane+card SPLIT (the WRONG build per DESIGN.md
   D2/F2 -- a split is not occlusion).

   The composition (composition.md: occlusion is the STRONGEST 2D depth cue;
   negative space frames the focal subject; asymmetric balance; Rule-of-One focal
   point from visual-hierarchy.md):
     1. .floral-window is a FULL-BLEED floral section -- her floral at FULL strength
        as the BACKGROUND, spanning the ENTIRE section: full width (edge-to-edge,
        breaking out of the .container) AND full height. NO aperture frame, NO
        border-radius, NO contained-box look -- the floral IS the section field.
     2. A SOLID OPAQUE cream card (.floral-window__card) floats ON the floral,
        pinned lower-left within the CONTENT column (asymmetric), OCCLUDING a region
        of it -- occlusion IS the depth (the single Rule-of-One focal point). Text
        lives ONLY on this opaque card (C4: never text on the floral).
     3. A small Dark Sage kicker chip (.floral-window__chip) layers ABOVE the card
        (z-index) -- brings Sage in as a real surface (C2), occluding the card's corner.

   The card breaks out to 100vw but RE-INSETS its content column via padding-inline,
   so the card + chip stay on the same readable measure / gutter as the rest of the
   site (never jammed against the raw viewport edge on wide screens). CSS-only: the
   page HTML only removes the old tag span; the card stays inside its .container.

   The floral layer is DECORATIVE: mark it aria-hidden in the page HTML.
   ------------------------------------------------------------------------------- */

/* The full-bleed floral field: breaks out of .container to span the whole section
   width (100vw) AND a generous full height. padding-inline rebuilds the content
   column (gutter when narrow; grows to centre the content at --container-max when
   wide) so the floral fills edge-to-edge while the card stays on the readable
   measure, never the raw viewport edge. */
.floral-window{position:relative;min-height:620px;overflow:hidden;
  width:100vw;margin-inline:calc(50% - 50vw);          /* full-bleed breakout of .container */
  padding-inline:max(var(--gutter), calc((100vw - var(--container-max)) / 2 + var(--gutter)));
  background-image:url('/assets/images/sarviWebBG-web.jpg');
  background-size:cover;background-position:center 42%;background-repeat:no-repeat;}
/* A whisper cream vignette settles the floral so the floating card reads as
   foreground (atmospheric depth cue, composition.md). Decorative; carries no text.
   NOT a frame -- a soft directional settle under the card's lower-left anchor. */
.floral-window::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;
  background:radial-gradient(120% 90% at 16% 88%, rgba(250,249,247,.30), rgba(250,249,247,0) 50%);}

/* The SOLID opaque card -- the occluding foreground plane, text lives ONLY here.
   Asymmetric: pinned lower-left of the CONTENT column. abspos offsets resolve from
   the padding box, so `left` carries the SAME content-column expression as the
   parent's padding-inline -- the card lands on the site's readable measure, not the
   raw viewport edge. Generous floral breathing to the right + top (composition.md
   negative space; C7 whitespace). margin-top leaves room for the sage chip to
   overlap above it. */
.floral-window__card{position:absolute;z-index:1;left:max(var(--gutter), calc((100vw - var(--container-max)) / 2 + var(--gutter)));bottom:var(--gutter);
  width:min(560px, calc(100% - var(--gutter) * 2));margin-top:46px;
  background:var(--c-canvas);                 /* SOLID OPAQUE -- text contrast always guaranteed */
  padding:var(--sp-2xl) var(--sp-2xl) var(--sp-xl);border-radius:var(--radius);
  box-shadow:0 26px 54px -26px rgba(30,31,32,.55),0 8px 22px -16px rgba(30,31,32,.40);}

/* The Dark Sage kicker chip -- a real Sage SURFACE (C2), anchored TO THE CARD.
   The chip lives INSIDE .floral-window__card (its first child); the card is
   position:absolute, so it is the chip's containing block. The chip straddles
   the card's top-left corner -- top:-16px lifts it half ABOVE the card (onto the
   floral) and half OVER the card, occluding the card's corner. occlusion = the
   STRONGEST 2D depth cue (composition.md "Depth cues in 2D: Overlap (occlusion)
   -- Strongest depth cue in 2D"), and the chip casts its existing shadow onto the
   card to seal the layered, foreground read. Canvas text on sage = 7.25:1;
   on-dark Antique Blush rule. Hosts the .kicker + an optional .accent-rule. */
.floral-window__chip{position:absolute;z-index:2;top:-16px;left:var(--sp-lg);
  background:var(--c-sage);color:var(--c-canvas);
  padding:var(--sp-md) var(--sp-lg);border-radius:var(--radius);
  box-shadow:0 18px 34px -16px rgba(30,31,32,.55),0 4px 10px -6px rgba(30,31,32,.5);}
.floral-window__chip .kicker{color:var(--c-canvas);margin:0;white-space:nowrap;}
.floral-window__chip .accent-rule{background:var(--accent-on-dark);margin:var(--sp-sm) 0 0;width:46px;}

/* FLEXIBLE CONTENT SLOT -- the card body works for three shapes:
   (a) numbered steps with black cherry serif numerals; (b) a single paragraph;
   (c) a heading/line + a button. */

/* (a) numbered steps -- black cherry serif numerals (text-on-light #3A0E1E, >=4.5:1).
   Each <li> is a 2-column grid: the numeral via ::before in column 1, and a single
   .floral-step-body wrapper (title + note) in column 2 -- the wrapper keeps title and
   note in the SAME grid cell (without it, the second span wraps under the numeral). */
.floral-window__card .floral-steps{list-style:none;counter-reset:fwstep;padding:0;margin:var(--sp-md) 0 0;}
.floral-window__card .floral-steps li{counter-increment:fwstep;display:grid;
  grid-template-columns:48px 1fr;column-gap:var(--sp-md);align-items:start;padding:var(--sp-sm) 0;}
.floral-window__card .floral-steps li + li{border-top:1px solid var(--c-surface-blush);} /* warm hairline group */
.floral-window__card .floral-steps li::before{content:counter(fwstep);
  font-family:var(--font-serif);font-weight:600;font-size:var(--fs-h4);line-height:1.1;
  color:var(--t-accent);font-feature-settings:"lnum" 1;}
.floral-window__card .floral-step-body{min-width:0;}
.floral-window__card .floral-step-title{display:block;font-weight:600;font-size:var(--fs-body);
  line-height:1.3;color:var(--c-onyx);}
.floral-window__card .floral-step-note{display:block;font-size:var(--fs-body);line-height:1.4;
  color:var(--c-onyx);margin-top:2px;}

/* (b) single paragraph + (c) heading + button -- the card heading is sized to the
   CARD context (the global h2/.h2 clamp reaches 60px, too large inside a 560px card);
   here a fixed Cormorant display at card scale, impact from scale not weight (C5). */
.floral-window__card h2,.floral-window__card .h2,
.floral-window__card h3,.floral-window__card .h3{
  font-family:var(--font-serif);font-weight:500;font-size:clamp(1.75rem,1.4rem+1.4vw,2.125rem);
  line-height:var(--lh-heading);letter-spacing:var(--ls-display);color:var(--c-onyx);
  margin:var(--sp-xs) 0 var(--sp-md);}
.floral-window__card p{margin-block-end:var(--sp-md);}
.floral-window__card .btn-primary,.floral-window__card .btn-secondary{margin-top:var(--sp-sm);}

/* RESPONSIVE (<=768px): the absolute-card composition cannot hold on a phone.
   The full-bleed floral stays full-bleed (still edge-to-edge, top-anchored so the
   blooms show), and the card flows full-width at the BOTTOM of that field as a flex
   item over the floral (graceful stack, card occludes the lower band). padding-block
   keeps the blooms visible above the card; padding-inline (from the base rule) holds
   the card on the gutter. */
@media (max-width: 768px){
  .floral-window{display:flex;flex-direction:column;justify-content:flex-end;
    min-height:520px;background-position:center top;
    padding-block:var(--sp-2xl) var(--sp-2xl);
    box-shadow:0 20px 44px -28px rgba(30,31,32,.42);}
  .floral-window__card{position:static;width:100%;margin-top:0;
    left:auto;bottom:auto;padding:var(--sp-lg);}
  /* Chip lives inside the card now -- on a phone it flows as a static label at
     the TOP of the card content (no absolute straddle; the overlap composition
     does not hold at this width). */
  .floral-window__chip{position:static;top:auto;left:auto;display:inline-block;margin-bottom:var(--sp-md);}
  /* Narrow the numeral column + gap so the step body keeps its line on a phone
     (the desktop 48px col + 16px gap squeezes "Free consult" into a wrap at 390px). */
  .floral-window__card .floral-steps li{grid-template-columns:32px 1fr;column-gap:var(--sp-sm);}
  .floral-window__card .floral-steps li::before{font-size:var(--fs-h4);}
}

/* ============================================================
   MOTION
   Animate only transform/opacity; entrances 180-400ms; no bounce/parallax/autoplay;
   one element animates at a time; reduced-motion substitutes a fade (does not kill).
   ============================================================ */
.reveal{opacity:0;transform:translateY(16px);transition:opacity var(--dur-enter) var(--ease-out),transform var(--dur-enter) var(--ease-out);}
.reveal.is-visible{opacity:1;transform:none;}
@media (prefers-reduced-motion: reduce){
  .reveal{transition:opacity var(--dur-enter) var(--ease-out);transform:none;}
  *,*::before,*::after{animation-duration:.01ms !important;scroll-behavior:auto !important;}
}

/* ============================================================
   FLORAL ATMOSPHERE SYSTEM  (shared -- her sarviWebBG floral, web-optimized)
   ------------------------------------------------------------
   The "scrolling background": her painterly floral (blush blooms + sage
   leaves + alabaster petals + onyx shadow -- HER exact palette, no new hues)
   sits behind a section while the content scrolls OVER it. Deploys MORE of
   her floral + MORE blush as atmosphere, never as wallpaper -- 1-2 spots per
   page, restraint is the brief.

   Image: /assets/images/sarviWebBG-web.jpg (368KB, 1920px -- the WEB weight;
   the 9.4MB PNG is the master, never referenced in CSS).

   CONTRAST CONTRACT (WCAG-verified over the BUSIEST floral region, scrim 0.85 stop):
     .floral--scrim-light -> Onyx body text   = 11.1:1 worst case. Safe.
                          -> Sage 13px kicker  = 5.14:1 worst case. Safe (clears 4.5:1).
     .floral--scrim-dark  -> Canvas text. Onyx wash 0.70 floor, worst case
        (near-white petal showing through) = 5.79:1. Safe.
   Antique Blush stays FILL/IMAGERY only -- it is never introduced as text or
   a border here (2.39:1 on light fails); all blush is wash + the floral art.

   MOVEMENT: a slow Ken-Burns drift on the floral layer only -- transform +
   opacity, ~40s, ease, no bounce/pan-jerk (motion.md: calm, 80/20 rule,
   one thing moves). FULLY disabled under prefers-reduced-motion (the layer
   goes static -- the floral atmosphere is preserved, only the drift stops).
   ============================================================ */

/* Base: a sectioned floral backdrop the content scrolls over.
   background-attachment:fixed gives the "scrolling background" feel on
   desktop (the floral holds still, content slides over it). */
.floral{position:relative;isolation:isolate;
  background-image:url('/assets/images/sarviWebBG-web.jpg');
  background-size:cover;background-position:center;background-repeat:no-repeat;
  background-attachment:fixed;}
/* Content above the scrim. */
.floral > *{position:relative;z-index:1;}

/* Scrim variants -- the contrast guarantee. The ::before paints the wash
   ABOVE the floral (which is the element background) but BELOW content. */
.floral::before{content:"";position:absolute;inset:0;z-index:0;pointer-events:none;}
/* Light wash. Slight diagonal so the floral reads a touch more on one edge
   without ever dropping text contrast. Measured over the BUSIEST floral region
   (darkest floral pixel under the least-opaque 0.85 stop):
     Onyx body  = 11.1:1   Sage 13px kicker = 5.14:1   (both clear AA 4.5:1).
   The least-opaque stop is 0.85 (was 0.80, which put the Sage kicker at 4.53:1 --
   real AA, razor-thin; 0.85 buys genuine headroom without touching her hex). */
.floral--scrim-light::before{
  background:linear-gradient(160deg, rgba(250,249,247,0.90) 0%, rgba(250,249,247,0.85) 100%);}
/* Dark wash -> protects Canvas text on a floral band (5.79:1 worst case). */
.floral--scrim-dark::before{
  background:linear-gradient(160deg, rgba(30,31,32,0.74) 0%, rgba(30,31,32,0.70) 100%);}
.floral--scrim-dark{color:var(--c-canvas);}
.floral--scrim-dark h1,.floral--scrim-dark h2,.floral--scrim-dark h3,
.floral--scrim-dark .kicker,.floral--scrim-dark p{color:var(--c-canvas);}

/* Drift: the gentle Ken-Burns. Applied to a dedicated floral LAYER (::after)
   so the scrim wash and the content never move -- only the imagery breathes.
   Long, eased, sub-perceptual scale (1.0 -> 1.06) + a few px of travel. */
.floral--drift::after{content:"";position:absolute;inset:0;z-index:-1;pointer-events:none;
  background-image:url('/assets/images/sarviWebBG-web.jpg');
  background-size:cover;background-position:center;background-repeat:no-repeat;
  transform-origin:center;will-change:transform;
  animation:floral-kenburns 42s var(--ease-out) infinite alternate;}
/* On a drift section the base layer is hidden so the moving ::after is the
   only floral -- avoids a static + moving copy ghosting at different speeds
   (motion.md vestibular anti-pattern). The scrim ::before still sits above it. */
.floral--drift{background-image:none;}
@keyframes floral-kenburns{
  from{transform:scale(1) translate3d(0,0,0);}
  to{transform:scale(1.06) translate3d(-1.2%,-0.8%,0);}
}

/* MOBILE: background-attachment:fixed is janky / unsupported on iOS Safari and
   most touch browsers -- fall back to a STATIC, contained floral so the section
   still carries her atmosphere without the jitter. Drift also stops here
   (mid-tier GPUs + the fixed-image cost). */
@media (max-width: 768px){
  .floral{background-attachment:scroll;}
  .floral--drift::after{animation:none;}
  .floral--drift{background-image:url('/assets/images/sarviWebBG-web.jpg');}
}

/* REDUCED MOTION: kill the drift entirely; keep the floral + scrim static.
   Meaning is preserved (motion.md: substitute, don't strip the surface). */
@media (prefers-reduced-motion: reduce){
  .floral--drift::after{animation:none;}
  .floral--drift{background-image:url('/assets/images/sarviWebBG-web.jpg');}
  .floral{background-attachment:scroll;}  /* fixed can itself feel like motion to sensitive users */
}

/* MORE BLUSH -- a reusable blush-wash band + tints, all FILL only.
   Blush as the 10-30% accent (color.md 60-30-10): the wash carries the warmth,
   text stays Onyx. Authored in OKLCH so the blush->canvas blend never muddies
   (color.md: sRGB midpoints go grey; oklch holds the hue). Onyx text over the
   blush surface measures 13.02:1. */
.blush-wash{background:linear-gradient(155deg,
    var(--c-surface-blush) 0%,
    color-mix(in oklch, var(--c-surface-blush) 55%, var(--c-canvas)) 45%,
    var(--c-canvas) 100%);}
/* A softer top-edge blush wash for section headers that want a touch of warmth. */
.blush-wash--soft{background:linear-gradient(180deg,
    color-mix(in oklch, var(--c-surface-blush) 70%, var(--c-canvas)) 0%,
    var(--c-canvas) 60%);}

/* ============================================================
   ACCESSIBILITY FLOOR (non-negotiable)
   ============================================================ */
/* Plain links + summary fall back to the UA ring otherwise -- give them the explicit Onyx ring (completes plan 2.6). */
a:focus-visible,summary:focus-visible{outline:3px solid var(--focus-ring);outline-offset:3px;}
/* On Onyx/sage dark bands an Onyx ring is invisible -- invert to canvas there. */
.site-footer a:focus-visible,.cta-band a:focus-visible,[class*="denim__content"] a:focus-visible{outline-color:var(--c-canvas);}
@media (forced-colors: active){
  .btn-primary:focus-visible,.btn-secondary:focus-visible,.btn-on-dark:focus-visible,
  .form-field input:focus-visible,.form-field select:focus-visible,.form-field textarea:focus-visible,
  a:focus-visible,button:focus-visible{outline:3px solid Highlight;outline-offset:3px;}
}

/* ============================================================
   RESPONSIVE
   Mobile menu: full-screen overlay panel (not a cramped dropdown); Esc dismisses;
   focus-trapped while open (include-partials.js). Opening sets body scroll-lock and
   moves focus to first nav link; closing restores focus to .nav-toggle + unlocks scroll.
   ============================================================ */
@media (max-width: 768px){
  .nav-toggle{display:inline-flex;align-items:center;min-height:44px;padding:var(--sp-sm) var(--sp-md);
    background:transparent;border:1px solid var(--line-strong);border-radius:var(--radius);
    font:500 var(--fs-kicker)/1 var(--font-sans);text-transform:uppercase;letter-spacing:var(--ls-caps);color:var(--c-onyx);cursor:pointer;}
  /* Full-screen overlay. The .site-header has backdrop-filter, which makes it the
     containing block for this position:fixed child -- so inset:0 would size to the
     76px header, not the viewport. width:100% / height:100dvh size to the screen
     regardless of the containing block, WITHOUT the scrollbar gutter that 100vw
     would add (100vw is scrollbar-inclusive and overflowed the content box by ~15px
     even while closed -- the source of the mobile horizontal scroll). top/left:0
     anchor at the header's top-left (the sticky header sits at viewport 0,0 and
     scroll is locked while the menu is open). */
  .site-nav{position:fixed;top:0;left:0;width:100%;height:100vh;height:100dvh;
    background:var(--c-canvas);flex-direction:column;justify-content:center;
    gap:var(--sp-lg);transform:translateY(-8px);opacity:0;pointer-events:none;
    transition:opacity var(--dur-enter) var(--ease-out),transform var(--dur-enter) var(--ease-out);z-index:60;}
  .site-nav.is-open{opacity:1;transform:none;pointer-events:auto;}
  .site-nav .nav-link{font-size:var(--fs-h4);}
  .nav-cta{margin-top:var(--sp-md);}
}
@media (prefers-reduced-motion: reduce){ .site-nav{transition:opacity var(--dur-enter) var(--ease-out);} }
