/* ==========================================================================
   Sage Kitchen Cabinetry — Editorial homepage
   Front-page only: hero, sections, grids, process steps, CTA.

   The header, mobile nav, .sage-wrap, .sage-eyebrow and .sage-arrow now live
   in header.css, which loads on EVERY page — the editorial header is sitewide.
   Everything is prefixed .sage- so it cannot leak into the UX Builder pages.
   Tokens come from tokens.css.
   ========================================================================== */

#sageMain {
  background: var(--bone);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--text-base);
  line-height: var(--leading-body);
  overflow-x: hidden;
}

#sageMain h1,
#sageMain h2,
#sageMain h3 {
  font-family: var(--font-display);
  font-weight: 400;
  letter-spacing: var(--tracking-display);
  margin: 0;
  text-wrap: balance;
}

#sageMain p { margin: 0; }
#sageMain img { max-width: 100%; display: block; }

/* ==========================================================================
   HERO — the big picture, running up behind the header
   ========================================================================== */

/* The fallback ground behind the photo was --olive-900: a near-black flash
   before the hero image decodes, and the visible ground on any frame the
   image does not cover. Bone matches the light scrim that now sits over it,
   so the hero loads light and stays light. */
.sage-hero {
  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  display: flex;
  align-items: center;
  padding: 180px 0 6rem;
  overflow: hidden;
  background: var(--bone);
}

.sage-hero-art { position: absolute; inset: 0; }

.sage-hero-img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 60% 60%;
}

/* ==========================================================================
   The hero veil — inverted 2026-09-02, dark scrim -> light scrim
   ==========================================================================
   This was the single darkest element on the site: a near-black gradient at
   58% over an already low-key interior, with the image pushed to
   brightness(1.32) underneath to stop the cabinetry going to mud. Two
   corrections fighting each other, and the net effect was a dark first
   impression on every visit.

   Now the scrim is BONE, not black, and the copy is dark-on-light. The
   photograph reads as daylight instead of dusk, which is the "light designer
   feel" the brief asked for, and the cabinetry the hero exists to sell is
   finally visible at its own values.

   Directional, not flat: densest on the left where the copy sits, clearing to
   fully transparent on the right so the photograph is untouched where nothing
   overlaps it. Same geometry as the dark version it replaces — only the
   colour and the direction of the contrast changed.

   The stops are deliberately high (0.94 at the copy edge). A light scrim has
   to be MORE opaque than a dark one to buy the same contrast, because it is
   working against a mid-luminance photograph rather than with it. Anything
   below ~0.90 here and --ink drops under 4.5:1 on the brighter frames. */
/* Pulled back 2026-10-12, when the hero became a real photograph of an
   installed rift-cut white oak kitchen rather than a CGI room scene. The
   earlier stops (94/88/55/12) were tuned to rescue type from a low-key
   render; over real photography they washed the oak out to the point that
   the thing the image was chosen for -- the grain -- stopped reading.

   Measured against the shipped crop at 1440x900, sampling the page ground
   between glyphs for each text run:

       eyebrow --olive-700   7.11:1
       h1      --olive-900  11.62:1
       h1 em   --gold-700    3.95:1   large text, threshold 3.0
       lede    --stone       4.84:1   <- the binding one
       ticks   --ink        16.22:1

   The lede is what caps this. A further pull to 82/70/22 was rendered and
   rejected: it measures 4.58:1, which passes by 0.08 and leaves no margin
   for a future image swap. Do not darken these stops without re-measuring
   the lede -- and measure against the nearest PAINTED ancestor, not the
   transparent .sage-hero-art above it. */
.sage-hero-veil {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(100deg,
      rgba(253, 251, 246, 0.88) 0%,
      rgba(253, 251, 246, 0.78) 34%,
      rgba(253, 251, 246, 0.34) 60%,
      rgba(253, 251, 246, 0.06) 82%,
      rgba(253, 251, 246, 0.00) 100%);
}

/* The brightness boost is gone. It existed only to rescue the photo from the
   dark veil that is no longer there; leaving it would blow out the highlights
   now that nothing is suppressing them.

   The residual brightness(1.06) went too, 2026-10-12. It was a 6% lift kept
   to blend a render into the bone ground; against a real photograph it only
   flattened the oak, and removing it changed the measured contrast by at most
   0.05:1 at every text run -- i.e. it was costing tone and buying nothing.
   saturate(1.02) stays: it offsets the veil's desaturating wash. */
.sage-hero-img {
  filter: saturate(1.02);
}

/* The copy block must start on the same optical line as the logo, so the
   width limit goes on the text itself — capping .sage-hero-inner instead
   would shrink the centred container and push everything inward. */
.sage-hero-inner {
  position: relative;
  z-index: 2;
  width: 100%;
}
/* min() so the cap never exceeds the viewport — a fixed rem max-width wider
   than a phone screen overflows instead of wrapping. */
.sage-hero-inner > * { max-width: min(34rem, 100%); }
/* The site's own eyebrow ("Quality Cabinetry · Design Planning · Expert
   Installation") is longer than the 34rem column and wrapped mid-phrase.
   It is one tracked line by design, so give it room and let it wrap only
   when the viewport genuinely cannot hold it. */
.sage-hero-inner > .sage-eyebrow { max-width: min(46rem, 100%); }
.sage-hero-inner > .sage-hero-sub { max-width: min(31rem, 100%); }
/* The veil above is BONE, not the black scrim this eyebrow was coloured for.
   --gold-300 champagne measures ~1.2:1 against it — the same failure that took
   --gold-100 off the H1 em and --gold-300 off the trust ticks below, and missed
   here only because this colour comes from a modifier class in header.css
   rather than from the .sage-on-dark context the band swap fixed.

   --olive-700 is the palette's body-safe green (8.17:1) and is already the
   colour of the ticks 44px below, so the two read as one system. Gold is NOT an
   option at this size: --gold-700 is 4.14:1, large-text-only per tokens.css,
   and would put gold twice within 20px of the H1 accent.

   Do NOT fix this by editing header.css:30 or the --gold-300 token — champagne
   on --olive-800 is 8.19:1 and correct on every interior .sage-on-dark band.

   Typography: at 0.24em the tracking was so open the phrase read as loose
   letters rather than words. 0.18em is still an editorial small-cap but holds
   together; 14px/600 restores the presence the lighter colour would cost. */
.sage-hero .sage-eyebrow--gold {
  color: var(--olive-700);
  font-size: 0.875rem;
  font-weight: 600;
  letter-spacing: 0.18em;
}
/* The gap under the eyebrow has to be set HERE, on the eyebrow, and it has to
   out-specify an ID. `#sageMain h1 { margin: 0 }` near the top of this file is
   (1,0,1); `.sage-hero h1 { margin: 1.4rem 0 0 }` is (0,1,1) and has therefore
   NEVER applied — measured 0px on staging. The eyebrow and the H1 were
   separated only by the H1's line-height leading, which is a large part of why
   this block read as cramped.

   Fixed on the eyebrow's own margin-bottom rather than by raising the h1 rule
   to `#sageMain .sage-hero h1`: margin-bottom on a block sibling collapses
   against nothing here (the h1 margin is 0), so the value is exactly the gap
   you get. Do not "tidy" this onto the h1 — it will silently stop working. */
#sageMain .sage-hero .sage-eyebrow--gold { margin-bottom: 1.15rem; }
/* Dark-on-light now — see the veil block above. --olive-900 rather than --ink:
   at display size the green reads as brand rather than as plain black text,
   and it clears 15:1 on the scrim. */
/* margin-top here is INERT — `#sageMain h1 { margin: 0 }` above is an ID
   selector and wins. Kept at 0 so the file does not claim spacing it does not
   produce; the eyebrow's margin-bottom above is what actually separates them. */
.sage-hero h1 {
  font-size: clamp(2.6rem, 6.4vw, 4.6rem);
  line-height: 1.04;
  color: var(--olive-900);
  margin: 0;
}
/* "Cabinetry Showroom" was champagne --gold-100, which is 1.1:1 on a bone
   scrim — it would have vanished completely. --gold-700 is the deepest bronze
   in the ramp and the only gold with any presence on light ground; at 4.14:1
   it clears AA for large text, which an H1 is by definition (>=24px). It is
   NOT usable at body size — see the warning in tokens.css. */
.sage-hero h1 em { font-style: normal; color: var(--gold-700); }
.sage-hero-sub {
  margin-top: 1.5rem;
  font-size: 1.0625rem;
  line-height: 1.75;
  color: var(--stone);
  max-width: 31rem;
}
/* --- hero trust bullets --- */
/* Wider than the 34rem copy cap: three phrases on one row need the room, and
   they wrap to a stack on their own when the viewport cannot hold them. */
.sage-hero-inner > .sage-hero-trust { max-width: min(44rem, 100%); }
.sage-hero-trust {
  margin: 2rem 0 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem 1.75rem;
  font-size: 0.875rem;
  color: var(--ink);
}
.sage-hero-trust li {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}
/* Checkmark as generated content — decorative, and it keeps the tick out of
   the accessible name so screen readers read the phrase, not "check". */
/* Was --gold-300 (1.4:1 on the light scrim — gone). --olive-500 was tried and
   also rejected: computed against the scrim composited over a mid-luminance
   photo it lands at 3.09:1, below the 4.5:1 body threshold at every stop.
   The tick is generated content and aria-hidden, so it is decoration rather
   than text and does not strictly bind — but a checkmark that washes out over
   a bright frame breaks the list visually, which is the same practical
   failure. --olive-700 is the palette's body-safe green and clears 4.5:1
   against every stop over both bright and mid photography. */
.sage-hero-trust li::before {
  content: "\2713";
  color: var(--olive-700);
  font-weight: 700;
}

.sage-hero-cta {
  margin-top: 2.2rem;
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
}

/* --- hero buttons --- */
/* The spec's #D4AF37 on near-black is a 9:1 pairing, but it is not a logo
   colour. --gold-300 is the palette's champagne-side brass and clears 10:1
   against --olive-900 text, so the button keeps the intended contrast while
   staying on-brand. */
.sage-btn {
  display: inline-block;
  padding: 0.95rem 1.75rem;
  border-radius: var(--radius-sm);
  font-size: 0.9375rem;
  font-weight: 600;
  line-height: 1;
  text-decoration: none;
  transition: background 0.2s ease, border-color 0.2s ease,
              color 0.2s ease, transform 0.2s ease;
}
/* Inverted for the light hero. The champagne-on-dark button was legible only
   because the ground behind it was near-black; on a bone scrim, gold-300 on
   white is 1.6:1 and the button disappears into the page.

   Solid olive is the right answer rather than an outlined gold one: on a light
   airy hero the primary action should be the single darkest mark on the
   screen, which is what makes it read as the obvious next step without
   needing size or shout. White on --olive-800 is 9.41:1. */
.sage-btn-primary {
  background: var(--olive-800);
  color: #fff;
  border: 1px solid var(--olive-800);
}
.sage-btn-primary:hover,
.sage-btn-primary:focus-visible {
  background: var(--olive-900);
  border-color: var(--olive-900);
  color: #fff;
  transform: translateY(-2px);
}
/* The ghost button keeps its outlined treatment but in ink rather than white.
   --greige for the resting border is too faint against a photograph, so it
   takes a visible olive hairline instead. */
.sage-btn-ghost {
  background: transparent;
  color: var(--olive-800);
  border: 1px solid var(--olive-500);
}
.sage-btn-ghost:hover,
.sage-btn-ghost:focus-visible {
  background: var(--sage-100);
  border-color: var(--olive-800);
  color: var(--olive-900);
  transform: translateY(-2px);
}
/* Gold-100 focus ring was tuned for a dark ground and is invisible on light. */
.sage-btn:focus-visible {
  outline: 2px solid var(--olive-700);
  outline-offset: 3px;
}
@media (prefers-reduced-motion: reduce) {
  .sage-btn { transition: background 0.2s ease, border-color 0.2s ease; }
  .sage-btn:hover, .sage-btn:focus-visible { transform: none; }
}

/* --- google trust badge --- */
/* ==========================================================================
   TRUST BAR — its own band under the hero (2026-09-03)
   ==========================================================================
   The rating badge used to float over the hero photograph. It has its own
   section now: over the photo it competed with the headline and the CTAs for
   the one composition that should be calm, and every style decision had to
   survive an unknown image behind it — which is what produced the old
   semi-transparent pill.

   The band is deliberately shallow. It is one line of proof, not a content
   section; at the .sage-sec rhythm (11rem) it would read as more important
   than the services below it. Linen so it separates from the bone section
   that follows without introducing a third surface colour. */
.sage-trustbar {
  background: var(--linen);
  border-bottom: 1px solid var(--greige);
  padding: clamp(1.5rem, 3vw, 2.25rem) 0;
}
.sage-trustbar .sage-wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1.25rem 2.5rem;
}

/* --- the rating badge ---
   Two columns divided by a hairline: the score at display size on the left,
   source and count on the right. Was one cramped row — an 18px Google G, five
   14px stars and two runs of text at the same optical weight inside 0.5rem of
   padding, i.e. a perfect rating styled as a footnote.

   The whole badge is the anchor to #reviews. Previously only "(9 Reviews)"
   was clickable — a ~90px target for the element whose entire job is sending
   people to the proof. */
.sage-trust-badge {
  display: inline-flex;
  align-items: stretch;
  gap: 0;
  padding: 0;
  border-radius: var(--radius-md);
  /* Opaque bone, and no backdrop-filter. Both existed only to cope with a
     photograph showing through; on a flat linen band the blur cost a
     compositor layer for no visible effect, and the alpha was pointless. */
  background: var(--bone);
  border: 1px solid var(--greige);
  box-shadow: var(--shadow-sm);
  text-decoration: none;
  overflow: hidden;
  transition: box-shadow var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
.sage-trust-badge:hover,
.sage-trust-badge:focus-visible {
  border-color: var(--sage-200);
  box-shadow: var(--shadow-md);
  transform: translateY(-2px);
}
@media (prefers-reduced-motion: reduce) {
  .sage-trust-badge { transition: none; }
  .sage-trust-badge:hover,
  .sage-trust-badge:focus-visible { transform: none; }
}

/* --- left column: the score --- */
.sage-trust-score {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.3rem;
  padding: 0.7rem 1.05rem;
  background: var(--sage-50);
  border-right: 1px solid var(--greige);
}
/* The serif face is what makes this read as a considered mark rather than a
   UI chip — same display font as the headings it sits under. */
.sage-trust-num {
  font-family: var(--font-display);
  font-size: 1.75rem;
  line-height: 1;
  color: var(--olive-900);      /* 14.6:1 on --sage-50 */
}

/* --- right column: source + count --- */
.sage-trust-text {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 0.2rem;
  padding: 0.7rem 1.15rem;
  font-size: 0.875rem;
  color: var(--ink);
  white-space: nowrap;
}
.sage-trust-src {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
}
.sage-trust-src strong {
  font-weight: 600;
  font-size: 0.8125rem;
  letter-spacing: 0.01em;
  color: var(--ink);
}
.sage-trust-count {
  font-size: 0.8125rem;
  color: var(--stone);          /* 5.52:1 on bone */
}
.sage-g-icon { width: 16px; height: 16px; flex: none; }

/* --- credentials, beside the rating ---
   Flatsome styles every <li> through .entry-content ul — indent plus a
   decorative left border that renders as a gold rule between items. Zeroed on
   the component's own class, same as the door grid. */
.sage-trust-creds {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.75rem;
  margin: 0;
  padding: 0;
  list-style: none;
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  color: var(--stone);          /* 5.05:1 on linen */
}
.sage-trust-creds li {
  margin: 0;
  padding: 0;
  border: 0;
  list-style: none;
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}
.sage-trust-creds li::marker { content: ""; }
/* A small olive lozenge rather than a checkmark — the hero checklist already
   uses ticks, and repeating them here would read as the same list twice. */
.sage-trust-creds li::before {
  content: "";
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--olive-500);
  flex: none;
  transform: translateY(-1px);
}

/* Stack on narrow screens: the badge takes the full row, credentials below.
   space-between with two children leaves a ragged gap once they wrap, so
   switch to a start-aligned column instead. */
@media (max-width: 860px) {
  .sage-trustbar .sage-wrap {
    flex-direction: column;
    align-items: flex-start;
    gap: 1.1rem;
  }
}
.sage-trust-stars { display: inline-flex; gap: 1px; }
/* #FFB800 as specified — a review star is a Google convention, not a brand
   mark, so it stays the familiar amber rather than taking the palette gold. */
.sage-trust-stars svg { width: 12px; height: 12px; fill: #FFB800; }

/* The gold hairline that closes the hero. Re-weighted for a light ground: the
   old ramp peaked at --gold-100 (champagne) in the middle, which was the
   brightest point against a dark hero and is now the *faintest* against a
   bone one — the rule visibly broke in half. Peaks at --gold-500 instead and
   falls away to the deeper bronze at the edges.

   This is decoration, not text, so it is a legitimate place for gold on light
   — the one role gold keeps in this palette now. */
.sage-hero-rule {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  height: 1px;
  z-index: 3;
  background: linear-gradient(90deg, transparent, var(--gold-700) 18%,
    var(--gold-500) 50%, var(--gold-700) 82%, transparent);
}

@media (max-width: 760px) {
  /* Stack the bullets: three phrases side by side wrap into an unreadable
     ragged block well before they actually overflow. */
  .sage-hero-trust { flex-direction: column; gap: 0.55rem; }
  /* Full-width CTAs so the tap targets are unambiguous. */
  .sage-hero-cta { gap: 0.75rem; }
  .sage-btn { display: block; width: 100%; text-align: center; }
  /* The badge is a two-column card now, not a single-line pill. Its content is
     two short labels stacked in the right column, so it fits a 320px screen
     without needing to wrap — and allowing a wrap here would break "Rated on
     Google" across lines away from its logo. Keep nowrap, and shrink the
     padding instead, which is what actually buys the room. */
  .sage-trust-score { padding: 0.6rem 0.9rem; }
  .sage-trust-text { padding: 0.6rem 0.95rem; }
  .sage-trust-num { font-size: 1.55rem; }
}

@media (max-width: 900px) {
  .sage-hero { padding-top: 84px; min-height: 88vh; }
  /* Vertical on mobile: the copy stacks over the middle of the photo rather
     than sitting in a left column, so the gradient runs top-to-bottom.
     Inverted to bone in step with the desktop veil above.

     Denser than desktop and denser at the BOTTOM, which is the opposite of
     the old dark version: the copy block sits low on a phone (headline, proof,
     buttons), so that is where the scrim has to do its work. The top stays
     lighter to leave the photograph legible above the headline. */
     The top stop is 0.86, not the 0.72 first tried: computed against a
     mid-luminance photo, 0.72 put --stone at 4.03:1 and the checklist tick at
     3.09:1, both under the 4.5:1 body minimum. On a phone the copy block
     starts higher up the screen than on desktop, so the "safe" transparent
     zone at the top is much smaller than it looks. */
  .sage-hero-veil {
    background: linear-gradient(180deg,
      rgba(253, 251, 246, 0.86) 0%,
      rgba(253, 251, 246, 0.94) 42%,
      rgba(253, 251, 246, 0.96) 100%);
  }
}

/* ==========================================================================
   SECTIONS
   ========================================================================== */

/* Air. Was clamp(5rem, 10vw, 8.5rem).
   Whitespace is the cheapest luxury signal there is, and the homepage was the
   densest thing on the site — nine sections at 8.5rem meant the eye never got
   a rest between one pitch and the next. The floor moves too (5 -> 5.5rem):
   most visitors are on a phone, where the clamp never reaches its ceiling, so
   raising only the max would have changed nothing for them. */
.sage-sec { padding: clamp(5.5rem, 12vw, 11rem) 0; }

/* Sections that are purely photography get more room still — the image is
   doing the talking and needs silence on both sides of it. */
.sage-sec--showcase { padding: clamp(6rem, 14vw, 13rem) 0; }

/* The header is fixed, so anything scrolled to by anchor (or a refresh that
   restores mid-page) would land underneath it. Reserve the solid bar's height
   plus a little breathing room. */
#sageMain [id],
.sage-sec[id] {
  scroll-margin-top: 150px;
}
@media (max-width: 900px) {
  #sageMain [id],
  .sage-sec[id] { scroll-margin-top: 84px; }
}
.sage-sec-alt { background: var(--linen); }
.sage-on-dark { background: var(--olive-800); color: #fff; }
.sage-on-dark h2, .sage-on-dark h3 { color: #fff; }

/* ==========================================================================
   LIGHT ACCENT BAND — replaces the olive band on the homepage (2026-09-02)
   ==========================================================================
   The brief was "the page should breathe, and I don't want the dark colours."
   The homepage carried three heavy dark moments — the hero veil, the Service
   Areas band and the closing CTA — so the visitor's first AND last impression
   were both dark, with a third slab in the middle.

   .sage-on-sage is the light replacement: a pale sage ground that keeps the
   brand green present and keeps the page's alternating rhythm, without the
   weight. Everything on it uses the ordinary dark-on-light tokens, so there
   are no light-on-dark overrides to get wrong — which is precisely the class
   of bug that bit this redesign three times (list pills at 1.00:1, the nested
   split heading at 1.34:1, GF's red (Required) at 1.66:1). A light band
   cannot produce that failure mode at all.

   .sage-on-dark is deliberately NOT deleted: it still styles the footer and
   is available to interior pages. This is an additional class, not a rename. */
.sage-on-sage { background: var(--sage-100); }

/* No colour overrides needed for headings or body — they inherit the same
   --olive-900 / --stone they use on bone and linen, and both clear AA here
   (8.00:1 and 4.69:1 measured). Stated explicitly anyway so that a future
   edit copying the .sage-on-dark block above does not "helpfully" add white
   text to a light band. */
.sage-on-sage h2,
.sage-on-sage h3 { color: var(--olive-900); }
.sage-on-sage .sage-sec-head h2 { color: var(--olive-900); }
.sage-on-sage .sage-split h2,
.sage-on-sage .sage-split h3 { color: var(--olive-900); }
.sage-on-sage .sage-split-body,
.sage-on-sage .sage-sec-intro { color: var(--stone); }

/* The tick rule: gold is 2.31:1 on this ground and fails as *text*, but a 1px
   decorative rule carries no contrast minimum. --gold-700, the deepest bronze,
   is used rather than --gold-500 so it still reads as metal against pale sage
   instead of washing out. */
.sage-on-sage .sage-tick { background: var(--gold-700); }

.sage-sec-head {
  text-align: center;
  max-width: 44rem;
  margin: 0 auto clamp(3rem, 6vw, 4.5rem);
}
.sage-sec-head h2 {
  font-size: clamp(1.9rem, 3.6vw, 2.9rem);
  line-height: 1.15;
  margin-top: 0.9rem;
  color: var(--olive-900);
}
.sage-on-dark .sage-sec-head h2 { color: #fff; }
.sage-tick {
  width: 44px; height: 1px;
  background: var(--olive-500);
  margin: 1.6rem auto 0;
}
.sage-on-dark .sage-tick { background: var(--gold-400); }
/* Section intro sits under the rule, not above it — the site's own section
   copy is a sentence of context, not a second heading. */
.sage-sec-intro {
  margin-top: 1.6rem;
  font-size: var(--text-base);
  line-height: var(--leading-body);
  color: var(--stone);
}
.sage-on-dark .sage-sec-intro { color: var(--white-a80); }

/* --- split -------------------------------------------------------------- */
.sage-split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(2.5rem, 6vw, 5rem);
  align-items: center;
}
.sage-split h2 {
  font-size: clamp(1.75rem, 3.3vw, 2.5rem);
  line-height: 1.18;
  margin-top: 0.9rem;
  color: var(--olive-900);
}
.sage-split-body { margin-top: 1.4rem; color: var(--stone); line-height: 1.8; }

/* A split laid over a dark band: the heading and body still carried their
   light-background colours — olive-900 on olive-800 measured 1.34:1 and stone
   measured 2.65:1, both unreadable. The .sage-on-dark h2 rule above only
   matches a direct heading, not one nested inside .sage-split.            */
.sage-on-dark .sage-split h2,
.sage-on-dark .sage-split h3 { color: var(--bone); }
.sage-on-dark .sage-split-body { color: var(--white-a80); }
.sage-split-more { margin-top: 2.2rem; }
.sage-split-media { position: relative; }
.sage-split-img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: var(--radius-sm);
}

.sage-feat {
  margin-top: 2.2rem;
  padding-top: 2.2rem;
  border-top: 1px solid var(--greige);
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
}
.sage-feat-row { display: flex; gap: 1.1rem; align-items: flex-start; }
.sage-feat-dash {
  width: 30px; height: 1px;
  background: var(--gold-500);
  flex-shrink: 0;
  margin-top: 0.85rem;
}
.sage-feat h3 {
  font-size: 0.8125rem;
  text-transform: uppercase;
  letter-spacing: 0.13em;
  font-weight: 600;
  font-family: var(--font-body);
  color: var(--ink);
  margin: 0;
}
.sage-feat p { margin-top: 0.3rem; font-size: 0.9375rem; color: var(--stone); line-height: 1.65; }

@media (max-width: 860px) { .sage-split { grid-template-columns: 1fr; } }

/* --- style + gallery grids ---------------------------------------------- */
.sage-grid-2 {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(1.25rem, 2.5vw, 2rem);
}
.sage-grid-3 {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.25rem, 2.5vw, 2rem);
  margin-top: clamp(1.25rem, 2.5vw, 2rem);
}
/* The four services, matching the site's own four-up service grid. */
.sage-grid-4 {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(1.25rem, 2.5vw, 2rem);
  margin-top: clamp(1.25rem, 2.5vw, 2rem);
}

.sage-style-card { text-decoration: none; color: inherit; display: block; }
.sage-style-img {
  position: relative;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--olive-700);
}
.sage-style-img--tall { aspect-ratio: 3 / 4; }
.sage-style-img img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.7s cubic-bezier(0.2, 0.7, 0.3, 1);
}
.sage-style-card:hover .sage-style-img img { transform: scale(1.03); }
.sage-style-card h3 {
  margin-top: 1rem;
  font-size: 0.75rem;
  font-family: var(--font-body);
  text-transform: uppercase;
  letter-spacing: 0.15em;
  font-weight: 600;
  color: var(--ink);
}
.sage-meta { font-size: 0.8125rem; color: var(--stone); margin-top: 0.2rem; }

.sage-gallery { margin-top: 0; }
.sage-gal-item {
  margin: 0;
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--radius-sm);
  background: var(--linen);
}
.sage-gal-item img {
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.7s cubic-bezier(0.2, 0.7, 0.3, 1);
}
.sage-gal-item:hover img { transform: scale(1.03); }

.sage-center-more { text-align: center; margin-top: clamp(3rem, 6vw, 4.5rem); }

/* --- reviews ------------------------------------------------------------ */
/* A horizontal scroll-snap rail: three cards visible, the rest scrolled to.
   Scroll-snap rather than a JS slider — native momentum on touch, real
   keyboard scrolling, and it degrades to a plain scroller if JS never runs.
   The arrow buttons are progressive enhancement on top (homepage.js). */
/* --- Google summary bar, above the rail ---------------------------------
   The 5.0 and the review count previously existed ONLY in the hero badge, two
   thirds of a page away from the reviews themselves — so the section whose
   whole job is credibility never stated its own headline number, and the
   cards read as unsourced testimonials.

   Deliberately echoes the hero badge's construction (display-serif number,
   Google mark, small-caps support) so the two register as the same claim
   restated, not two different ratings. */
.sage-rev-summary {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-wrap: wrap;
  gap: 0.5rem 1.5rem;
  margin: 0 auto clamp(2.5rem, 5vw, 3.5rem);
  padding-bottom: clamp(1.5rem, 3vw, 2rem);
  border-bottom: 1px solid var(--greige);
}
.sage-rev-summary-score {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
}
.sage-rev-score-num {
  font-family: var(--font-display);
  font-size: clamp(2rem, 4vw, 2.6rem);
  line-height: 1;
  color: var(--olive-900);
}
.sage-rev-stars-lg { display: inline-flex; gap: 2px; }
.sage-rev-stars-lg svg { width: 19px; height: 19px; fill: #FFB800; }
.sage-rev-summary-meta {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9375rem;
  color: var(--stone);
}
.sage-rev-summary-meta strong { color: var(--ink); font-weight: 600; }

.sage-reviews-rail { position: relative; }
.sage-reviews {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: calc((100% - 2 * var(--rev-gap)) / 3);
  --rev-gap: clamp(1.25rem, 2.4vw, 2rem);
  gap: var(--rev-gap);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* Cards carry a shadow and a focus ring; padding keeps both from being
     clipped by the scroll container's overflow. */
  padding: 4px;
  margin: -4px;
  scrollbar-width: none;
}
.sage-reviews::-webkit-scrollbar { display: none; }
.sage-reviews:focus-visible {
  outline: 2px solid var(--olive-500);
  outline-offset: 3px;
}
/* ==========================================================================
   REVIEW CARD — rebuilt 2026-09-03
   ==========================================================================
   Was: five identical amber stars, then the quote, then a hairline, then the
   name. Nine cards in a row opened with the same graphic, so the rail had no
   variety and the person — the thing that makes a testimonial credible — was
   the last thing read.

   Now the attribution leads: monogram, name, project, stars. Each card opens
   with a different pair of letters, which is what gives the rail rhythm.

   The quote sits below and is the visual weight of the card. */
.sage-review {
  scroll-snap-align: start;
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: clamp(1.6rem, 2.8vw, 2.15rem);
  background: var(--bone);
  border: 1px solid var(--greige);
  border-radius: var(--radius-md);
  transition: box-shadow var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
/* A quiet lift on hover. The rail is horizontally scrollable and this is the
   affordance that says the cards are objects rather than a flat block of
   text — it does not imply they are clickable, since nothing here is a link. */
.sage-review:hover {
  border-color: var(--sage-200);
  box-shadow: var(--shadow-md);
  transform: translateY(-3px);
}
@media (prefers-reduced-motion: reduce) {
  .sage-review { transition: none; }
  .sage-review:hover { transform: none; }
}

/* --- card head: monogram + name + stars --- */
.sage-review-head {
  display: grid;
  grid-template-columns: auto 1fr;
  grid-template-areas:
    "avatar id"
    "avatar stars";
  column-gap: 0.85rem;
  row-gap: 0.2rem;
  align-items: center;
  margin: 0 0 1.15rem;
  padding: 0;
  border: 0;
}

/* The monogram. Reviewer profile photos are the reviewers' copyright (see
   front-page.php), so the avatar is typographic — initials we render, which
   carry no third-party licence. Pale sage ground keeps it as a quiet marker
   rather than a coloured dot competing with the Google stars beside it. */
.sage-review-avatar {
  grid-area: avatar;
  width: 46px;
  height: 46px;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: var(--sage-100);
  border: 1px solid var(--sage-200);
  color: var(--olive-700);            /* 6.72:1 on --sage-100 */
  font-family: var(--font-display);
  font-size: 1.0625rem;
  line-height: 1;
  letter-spacing: 0.01em;
}

.sage-review-id { grid-area: id; min-width: 0; }
.sage-review-stars { grid-area: stars; }

/* Pushes the quote to fill the card so cards of unequal quote length still
   align top and bottom across the rail. */
.sage-review blockquote { flex: 1 0 auto; }

/* --- rail arrows --- */
.sage-rev-nav {
  position: absolute;
  top: 50%;
  transform: translateY(-50%);
  z-index: 2;
  width: 44px; height: 44px;
  display: flex; align-items: center; justify-content: center;
  padding: 0;
  border: 1px solid var(--greige);
  border-radius: 50%;
  background: var(--bone);
  color: var(--olive-800);
  box-shadow: 0 2px 10px rgba(28, 28, 24, 0.10);
  cursor: pointer;
  transition: background 0.2s ease, color 0.2s ease, opacity 0.2s ease;
}
.sage-rev-nav svg { width: 20px; height: 20px; }
.sage-rev-nav:hover { background: var(--olive-800); color: var(--bone); }
.sage-rev-nav:focus-visible {
  outline: 2px solid var(--olive-500);
  outline-offset: 2px;
}
.sage-rev-prev { left: 0; transform: translate(-50%, -50%); }
.sage-rev-next { right: 0; transform: translate(50%, -50%); }
/* Hidden until JS wires them up, and at each end of the rail. */
.sage-rev-nav[hidden],
.sage-reviews-rail:not(.is-ready) .sage-rev-nav { display: none; }
.sage-rev-nav[disabled] { opacity: 0; pointer-events: none; }

/* No room to overhang the container below ~1180px — tuck the arrows inside. */
@media (max-width: 1180px) {
  .sage-rev-prev { left: 4px; transform: translateY(-50%); }
  .sage-rev-next { right: 4px; transform: translateY(-50%); }
}
.sage-review-img {
  margin: calc(-1 * clamp(1.5rem, 2.6vw, 2rem));
  margin-bottom: clamp(1.25rem, 2vw, 1.5rem);
  aspect-ratio: 4 / 3;
  overflow: hidden;
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
  background: var(--linen);
}
.sage-review-img img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* Amber, not palette gold: a review star is a Google convention and reads as
   borrowed credibility precisely because it is the familiar colour. Same
   reasoning as the hero badge. */
.sage-review-stars { display: flex; gap: 0.1rem; }
.sage-review-stars svg { width: 14px; height: 14px; fill: #FFB800; }

/* The quote is the card. Larger and looser than the 0.9375rem it was — at
   that size nine cards of small grey text read as a wall, which is the
   "wordy" problem in miniature. */
.sage-review blockquote {
  margin: 0;
  padding: 0;
  border: 0;
  font-size: 1rem;
  line-height: 1.72;
  color: var(--ink);
}

/* The old figcaption rule put a top border and margin on the attribution
   block, which was correct when it sat at the card floor. It now leads the
   card, so both are zeroed in .sage-review-head above; this override stops
   the generic rule reapplying them. */
.sage-review figcaption {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
}
.sage-review-name {
  display: block;
  font-weight: 600;
  font-size: 0.9375rem;
  color: var(--olive-900);
  /* Long names must not force the card wider than its grid column. */
  overflow-wrap: anywhere;
}
.sage-review-proj {
  display: block;
  margin-top: 0.1rem;
  font-size: 0.8125rem;
  color: var(--stone);
}
.sage-reviews-src {
  margin-top: clamp(2rem, 4vw, 2.75rem);
  text-align: center;
  font-size: 0.8125rem;
  color: var(--stone);
}
.sage-reviews-src a { color: var(--olive-700); }

/* 3 cards -> 2 -> ~1.15. The fractional last step deliberately leaves a sliver
   of the next card visible on phones, which is what signals "this scrolls"
   when the arrows are hidden. */
@media (max-width: 900px) {
  .sage-reviews { grid-auto-columns: calc((100% - var(--rev-gap)) / 2); }
}
@media (max-width: 600px) {
  .sage-reviews { grid-auto-columns: 86%; }
  .sage-rev-nav { display: none; }
}

@media (max-width: 760px) {
  .sage-grid-2 { grid-template-columns: 1fr; }
  .sage-grid-3 { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 480px) {
  .sage-grid-3 { grid-template-columns: 1fr; }
}

/* --- process ------------------------------------------------------------ */
.sage-steps {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(2rem, 5vw, 4rem);
}
.sage-step { border-top: 1px solid var(--gold-100-a22); padding-top: 1.9rem; }
.sage-num { font-size: 0.6875rem; letter-spacing: 0.2em; color: var(--gold-300); font-weight: 600; }
.sage-step h3 { font-size: 1.2rem; margin-top: 0.65rem; }
.sage-step p { margin-top: 0.7rem; font-size: 0.9375rem; color: var(--white-a72); line-height: 1.75; }

@media (max-width: 760px) { .sage-steps { grid-template-columns: 1fr; } }

/* --- closing CTA -------------------------------------------------------- */
.sage-cta { text-align: center; max-width: 40rem; }
.sage-cta h2 { font-size: clamp(2rem, 4.2vw, 3.1rem); line-height: 1.12; margin-top: 0.9rem; }
.sage-lead { margin-top: 1.6rem; color: var(--white-a80); line-height: 1.8; }

/* The closing CTA on a light band. .sage-lead and .sage-tel were written for
   an olive ground and carry white-alpha colours, which would land at ~1.2:1
   on pale sage — invisible, the same failure this redesign hit three times
   before. They are restated here rather than made conditional, so the dark
   variant keeps working wherever it is still used. */
.sage-on-sage .sage-cta h2 { color: var(--olive-900); }
.sage-on-sage .sage-lead { color: var(--stone); }
.sage-on-sage .sage-tel { color: var(--stone); }
.sage-on-sage .sage-tel a {
  color: var(--olive-700);            /* 6.72:1 — gold would be 2.31:1 */
  border-bottom-color: var(--sage-200);
}
.sage-on-sage .sage-tel a:hover { color: var(--olive-900); }
.sage-cta-row {
  margin-top: 2.6rem;
  display: flex;
  gap: 2.5rem;
  justify-content: center;
  flex-wrap: wrap;
}
.sage-tel { margin-top: 2.8rem; font-size: 0.875rem; color: var(--white-a62); }
.sage-tel a {
  color: var(--gold-100);
  text-decoration: none;
  border-bottom: 1px solid var(--gold-100-a35);
  padding-bottom: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .sage-hdr,
  .sage-hdr-in,
  .sage-hdr-logo,
  .sage-hdr-logo img,
  .sage-hdr nav a,
  .sage-style-img img,
  .sage-gal-item img,
  .sage-arrow svg,
  .sage-arrow::after {
    transition-duration: 0.01ms !important;
  }
}

/* Desktop shows the full paragraph; the short one is mobile-only. */
.sage-hero-sub--short { display: none; }

/* ==========================================================================
   MOBILE HERO — give it room to breathe
   ==========================================================================
   Measured at 412x780: the hero was 904px tall in a 780px viewport, so it did
   not fit its own screen, and the primary CTA sat at 631px -- below the fold
   once the browser's address bar is showing. Seven elements were stacked over
   the photograph: eyebrow, four-line H1, 45-word paragraph, three-item
   checklist, two buttons and a review badge.

   The fix is subtraction, not shrinking. Scaling six competing elements down
   still leaves six competing elements, and the photograph -- the reason a
   showroom has a hero at all -- stays buried. So on a phone the redundant
   layers are removed and the rest is given space.

   Nothing is deleted from the markup: every element still ships for desktop,
   for search engines and for screen readers on the interior pages. This is a
   viewport-scoped decision about what earns room on a 412px screen. */
@media (max-width: 760px) {
  /* The eyebrow says "Quality Cabinetry · Design Planning · Expert
     Installation"; the checklist below says "Custom 3D Design Planning /
     Licensed & Expert Installation / Direct Showroom Pricing". The same three
     promises, twice, 44px apart. The checklist is the more specific of the two,
     so the eyebrow goes and the checklist stays. */
  .sage-hero .sage-eyebrow--gold { display: none; }

  /* 41.6px over four lines is 173px of headline. It reads as shouting rather
     than as confidence, and it is what pushes everything else off screen.
     clamp() keeps it commanding on a 600px tablet and sane on a 360px phone. */
  .sage-hero h1 {
    font-size: clamp(2rem, 8.5vw, 2.4rem);
    line-height: 1.12;
    margin-bottom: 0.85rem;
  }

  /* The lede is 45 words defining what a kitchen showroom is -- to someone who
     has just read "Las Vegas Kitchen & Bath Cabinetry Showroom". It is good
     copy for a search snippet and for desktop, and it is the fourth thing
     competing for a first impression on a phone. Clipped to two lines rather
     than hidden: the opening clause still sets the location and the trade,
     which is the part that does work here. */
  .sage-hero-sub--long { display: none; }
  .sage-hero-sub--short {
    display: block;
    margin-bottom: 1.15rem;
    font-size: 0.95rem;
    line-height: 1.5;
  }

  /* Breathing room between the three groups that remain: headline, proof,
     action. */
  .sage-hero-trust { gap: 0.5rem; margin-bottom: 1.4rem; font-size: 0.9rem; }

  /* The badge used to be reordered here so proof sat above the two CTAs
     rather than below them. It has left the hero entirely (2026-09-03) and
     is now its own .sage-trustbar band immediately beneath — which achieves
     the same thing structurally, and for desktop as well as mobile. The
     ordering rule that used to sit here has been removed rather than left
     targeting an element that is no longer inside .sage-hero-inner. */
  .sage-hero-cta { order: 3; margin-top: 0.25rem; }
  .sage-hero-inner { display: flex; flex-direction: column; }
  /* No ordering rule for .sage-eyebrow--gold: it is display:none in this same
     block (see above), so ordering it was inert. Removed rather than left
     pointing at an element that does not render here. */
  .sage-hero-inner > h1 { order: 0; }
  .sage-hero-inner > .sage-hero-sub { order: 1; }
  .sage-hero-inner > .sage-hero-trust { order: 1; }

  /* svh, not vh: vh is the viewport with browser chrome hidden, which is the
     one state that does not overflow. The hero should fill the screen it is
     actually shown on, not the idealised one. */
  .sage-hero { min-height: 100svh; padding-top: 76px; padding-bottom: 2rem; }
}

/* Very small phones. The second CTA is the phone number, which is already the
   button in the fixed header on every screen -- so on the tightest screens it
   is a third copy of a link the visitor can already see. */
@media (max-width: 400px) {
  .sage-hero h1 { font-size: clamp(1.85rem, 8vw, 2.1rem); }
  .sage-hero-cta .sage-btn-ghost { display: none; }
}

/* ---------------------------------------------------------------------------
   The catalogue band (#catalogue)

   Four collection cards on the bone band between #brands and #designer.

   list-style and the padding/margin zeroing are not defensive boilerplate:
   Flatsome styles every <li> through .entry-content ul with an indent plus a
   decorative left border that renders as a gold rule between grid cells. Same
   tell as the zoom pill -- a computed value that appears nowhere in this file
   is the parent theme. Zero it on the component's own class.

   Four fixed columns, not auto-fill with a minmax floor: a small floor gave
   seven 158px columns at 1440px on the door grid, and these cards are taller.
   Step 4 -> 2 -> 1 explicitly.
   --------------------------------------------------------------------------- */
.sage-cat-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(1rem, 2vw, 1.75rem);
  margin: clamp(2.5rem, 5vw, 4rem) 0 0;
  padding: 0;
  list-style: none;
}
.sage-cat-grid > li {
  margin: 0;
  padding: 0;
  border: 0;
  list-style: none;
}
.sage-cat-link {
  display: flex;
  flex-direction: column;
  height: 100%;
  border-radius: var(--radius-md);
  background: var(--bone);
  border: 1px solid var(--greige);
  box-shadow: var(--shadow-sm);
  text-decoration: none;
  overflow: hidden;
  transition: box-shadow var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out);
}
.sage-cat-link:hover,
.sage-cat-link:focus-visible {
  border-color: var(--olive-600);
  box-shadow: var(--shadow-md, var(--shadow-sm));
  transform: translateY(-2px);
}
/* aspect-ratio on the wrapper, not the img: the card plates are 3:2 and a
   height derived from the full width is what made PIL pad crops with black. */
.sage-cat-art {
  display: block;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background: var(--greige);
}
.sage-cat-img {
  display: block;
  width: 100%;
  height: 100%;
  max-width: 100%;
  object-fit: cover;
}
.sage-cat-body {
  display: block;
  padding: clamp(1rem, 2vw, 1.5rem);
}
/* Spans need display:block -- .sage-door-line and .sage-door-meta ran together
   as "Dover Bedford" until told otherwise. */
.sage-cat-name {
  display: block;
  font-family: var(--font-display);
  font-size: 1.25rem;
  color: var(--olive-800);
  margin-bottom: 0.4rem;
}
/* --olive-600 measures 6.78:1 on bone. Not gold: --gold-700 is 4.14:1 there,
   under the 4.5 minimum, and reads as the safe end of the ramp when it is not. */
.sage-cat-blurb {
  display: block;
  font-size: 0.875rem;
  line-height: 1.6;
  color: var(--stone);
}
.sage-cat-more {
  margin: clamp(2rem, 4vw, 3rem) 0 0;
  text-align: center;
}

@media (max-width: 900px) {
  .sage-cat-grid { grid-template-columns: repeat(2, 1fr); }
}
@media (max-width: 520px) {
  .sage-cat-grid { grid-template-columns: 1fr; }
}

/* ==========================================================================
   EDITORIAL PASS — 2026-10-12
   ==========================================================================
   The owner asked for a page that reads as luxury and could not say what was
   missing. Measured: 12 sections, 11,000px, 1,566 words, every section the
   same shape (eyebrow / serif H2 / paragraph / boxed image), and the one
   image reused three times. Nothing changed scale, so nothing read as a
   moment.

   Five reference sites were screenshotted at 1440px and measured the same
   way -- Plain English, deVOL, Poggenpohl, Boffi, Henrybuilt. What they share:

     - 200-500 words for the whole page (we had 1,566)
     - photography edge-to-edge, square corners, NO card chrome: no borders,
       no radii, no shadows, no boxed captions
     - one serif statement line standing alone in white space
     - section labels as small tracked caps, not a heading stack
     - asymmetric splits where the copy side is almost empty
     - lists of places / categories as a single tracked-caps line
       (Plain English: LONDON  NEW YORK  LOS ANGELES)

   Three of the five are light sites, which matters: the owner's 2026-09-02
   brief was "I don't want the dark colours", so the olive band stays off.
   Luxury here is scale and restraint, not darkness.

   Everything below is additive and overrides earlier rules by cascade order.
   The sections it replaces (trustbar, split features, services grid, brands,
   catalogue cards, service areas, bath, projects) keep their CSS for the
   interior pages that still use those classes.
   ========================================================================== */

/* Square corners. Every reference site; the radius read as "template". */
#sageMain img,
#sageMain .sage-split-img,
#sageMain .sage-style-img,
#sageMain .sage-gal-item { border-radius: 0; }

/* Quieter section heads: label + one serif line, no rule, no intro. */
.sage-sec-head--quiet { margin-bottom: clamp(2.5rem, 5vw, 3.5rem); }
.sage-sec-head--quiet h2 { font-size: clamp(1.6rem, 2.8vw, 2.3rem); }

/* --- statement ---------------------------------------------------------- */
/* One sentence in the display face, then the credentials and the four
   service links as tracked caps. Replaces the trust bar AND the "Designs
   Without Limits" split with its four filler bullets -- same facts, 70
   fewer words. Every claim in the line already appears on this page or the
   site: the showroom address, "3D design" (hero tick + two reviews), "our own
   crew" (the brands intro). Nothing new is promised. */
.sage-state { text-align: center; }
.sage-state-line {
  font-family: var(--font-display);
  font-size: clamp(1.5rem, 3vw, 2.4rem);
  line-height: 1.3;
  color: var(--olive-900);
  max-width: 36ch;
  margin: 0 auto;
  text-wrap: balance;
}
.sage-state-creds {
  list-style: none;
  margin: clamp(2.25rem, 4.5vw, 3.25rem) auto 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.6rem 2.4rem;
  font-size: 0.75rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--stone);              /* 6.12:1 on bone */
}
.sage-state-creds li { margin: 0; padding: 0; border: 0; list-style: none; }
.sage-state-creds a { color: inherit; text-decoration: none; border-bottom: 1px solid var(--greige); padding-bottom: 2px; }
.sage-state-creds a:hover { color: var(--olive-900); border-bottom-color: var(--gold-700); }
.sage-state-nav {
  margin-top: clamp(1.75rem, 3.5vw, 2.5rem);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.5rem 2.25rem;
}
.sage-state-nav a {
  font-family: var(--font-display);
  font-size: clamp(1.1rem, 1.6vw, 1.3rem);
  color: var(--olive-800);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 2px;
}
.sage-state-nav a:hover { border-bottom-color: var(--gold-700); }

/* --- collection tiles --------------------------------------------------- */
/* Plain English's grid: photograph, one label over it, nothing else. The four
   tiles are still generated from data/cnc-specs.php (never hand-authored);
   only the blurbs are gone -- the catalogue page carries them. */
.sage-tiles {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(0.75rem, 1.5vw, 1.25rem);
  list-style: none;
  margin: 0;
  padding: 0;
}
.sage-tiles > li { margin: 0; padding: 0; border: 0; list-style: none; }
.sage-tile {
  position: relative;
  display: block;
  aspect-ratio: 1 / 1;              /* square from 3:2 plates: a centred 800x800 crop keeps the island */
  overflow: hidden;
  background: var(--linen);
  text-decoration: none;
}
.sage-tile img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 1.4s cubic-bezier(0.22, 1, 0.36, 1);
}
.sage-tile:hover img,
.sage-tile:focus-visible img { transform: scale(1.04); }
/* The label is white on the photograph. The scrim is what makes that legible
   -- measure it against the brightest plate, not by eye. First pass was
   0.62 at the foot and three of the four labels measured 3.41-4.06:1 against
   the worst-case ground (the Luxor plate's pale floor). 0.82 at the foot,
   held to 0.55 through the label's own band, is what clears 4.5 on all four;
   it fades out by 55% so the top two thirds of the photograph stay untouched. */
.sage-tile::after {
  content: "";
  position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(28, 28, 24, 0.82) 0%, rgba(28, 28, 24, 0.55) 22%, rgba(28, 28, 24, 0.18) 40%, rgba(28, 28, 24, 0) 55%);
  pointer-events: none;
}
.sage-tile-label {
  position: absolute;
  left: 0; right: 0;
  bottom: clamp(1.1rem, 2vw, 1.6rem);
  z-index: 1;
  text-align: center;
  color: #fff;
  font-size: 0.75rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  font-weight: 500;
}
.sage-tile:focus-visible { outline: 2px solid var(--olive-500); outline-offset: 3px; }

/* Dealer marks under the tiles: logos and names only. The 40-word blurbs
   belong on /brands/ when it exists, not on the front page. */
.sage-dealer-row {
  margin-top: clamp(3.5rem, 7vw, 6rem);
  padding-top: clamp(2.25rem, 4vw, 3rem);
  border-top: 1px solid var(--greige);
}
.sage-dealer-row > .sage-eyebrow { text-align: center; margin-bottom: clamp(1.5rem, 3vw, 2.25rem); }
.sage-dealer-row .sage-dealer-blurb { display: none; }

/* --- editorial split (asymmetric, edge-bleed) ---------------------------- */
/* Poggenpohl's device: the photograph runs to the viewport edge on its side
   and the copy side is mostly air -- a label, one line, one link. The grid
   deliberately does NOT sit in .sage-wrap; that is what lets the image bleed. */
.sage-edit { padding: clamp(2.5rem, 5vw, 4rem) 0; }
.sage-edit + .sage-edit { padding-top: 0; }
.sage-edit-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  align-items: center;
  gap: clamp(2rem, 5vw, 5rem);
}
.sage-edit-media { margin: 0; }
.sage-edit-media img { width: 100%; height: auto; display: block; }
.sage-edit-copy {
  max-width: 24rem;
  margin-inline: auto;
  padding-inline: clamp(1.25rem, 4vw, 2.5rem);
}
.sage-edit-copy h2 {
  font-size: clamp(1.9rem, 3.2vw, 2.7rem);
  line-height: 1.15;
  margin-top: 0.9rem;
  color: var(--olive-900);
}
.sage-edit-copy > p:not(.sage-eyebrow) {
  margin-top: 1.4rem;
  color: var(--stone);
  line-height: 1.75;
}
.sage-edit-copy .sage-arrow { margin-top: 2rem; }
.sage-edit--flip .sage-edit-media { order: 2; }

/* --- reviews, editorial ------------------------------------------------- */
/* One quote at display size, attributed in tracked caps, above the rail.
   Chosen because it is the most specific sentence a client wrote -- a wall
   removed, a sink moved -- which is what luxury copy sounds like. */
.sage-pull {
  margin: 0 auto clamp(2.5rem, 5vw, 3.5rem);
  max-width: 46rem;
  text-align: center;
  padding: 0;
  border: 0;
}
.sage-pull p {
  font-family: var(--font-display);
  font-size: clamp(1.35rem, 2.6vw, 2rem);
  line-height: 1.35;
  color: var(--olive-900);
  text-wrap: balance;
}
.sage-pull footer {
  margin-top: 1.4rem;
  font-size: 0.75rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--stone);
}
.sage-rev-line {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9375rem;
  color: var(--stone);
  margin: 0 0 clamp(2.25rem, 4.5vw, 3.25rem);
}
.sage-rev-line strong { color: var(--ink); font-weight: 600; }
/* The rail cards lose their chrome. Same markup and ids, so homepage.js keeps
   driving the arrows and snap points. */
.sage-review,
.sage-review:hover {
  border: 0;
  border-radius: 0;
  background: transparent;
  box-shadow: none;
  transform: none;
  padding: 0 clamp(0.25rem, 1vw, 1rem);
}
.sage-review-avatar { display: none; }
.sage-review-head {
  grid-template-columns: 1fr;
  grid-template-areas: "id" "stars";
  margin-bottom: 1rem;
}
.sage-review-name { font-family: var(--font-display); font-weight: 400; font-size: 1.05rem; }
.sage-review-proj { font-size: 0.75rem; letter-spacing: 0.14em; text-transform: uppercase; margin-top: 0.3rem; }
.sage-review blockquote { font-size: 1rem; line-height: 1.7; color: var(--ink); }

/* --- cities -------------------------------------------------------------- */
/* The Service Areas H2 and its link carry local-SEO weight and are kept
   verbatim (see the old .sage-on-sage note). What goes is the paragraph and
   the installation stock photo; the six cities become the tracked-caps line. */
.sage-cities { text-align: center; }
.sage-cities h2 {
  font-size: clamp(1.6rem, 2.8vw, 2.3rem);
  margin: 0.9rem auto 0;
  max-width: 28ch;
}
.sage-city-nav {
  margin-top: clamp(2rem, 4vw, 3rem);
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.85rem 2.75rem;
}
.sage-city-nav a {
  font-size: 0.8125rem;
  letter-spacing: 0.28em;
  text-transform: uppercase;
  font-weight: 500;
  color: var(--olive-800);           /* 9.41:1 on bone */
  text-decoration: none;
  border-bottom: 1px solid transparent;
  padding-bottom: 3px;
}
.sage-city-nav a:hover { color: var(--olive-900); border-bottom-color: var(--gold-700); }
.sage-cities .sage-center-more { margin-top: clamp(2rem, 4vw, 2.75rem); }

/* --- closing CTA, trimmed ------------------------------------------------ */
.sage-on-sage .sage-lead { font-size: 1rem; margin-top: 1.25rem; }

/* --- responsive ----------------------------------------------------------- */
@media (max-width: 900px) {
  .sage-tiles { grid-template-columns: repeat(2, 1fr); }
  .sage-edit-grid { grid-template-columns: 1fr; gap: 2rem; }
  .sage-edit--flip .sage-edit-media { order: 0; }
  .sage-edit-copy { max-width: 34rem; }
}
@media (max-width: 520px) {
  .sage-state-creds { gap: 0.55rem 1.4rem; }
  .sage-city-nav { gap: 0.75rem 1.5rem; }
}
@media (prefers-reduced-motion: reduce) {
  .sage-tile img { transition: none; }
  .sage-tile:hover img { transform: none; }
}

/* --- spacing that has to out-specify the ID resets --------------------------
   `#sageMain p { margin: 0 }` and `#sageMain h2 { margin: 0 }` near the top of
   this file are (1,0,1). Every margin above written on a bare class is
   (0,1,0) and therefore NEVER applies -- the same trap the hero eyebrow
   comment documents. Measured on the first push: the statement and the
   cities H2 rendered left-anchored (their `margin: 0 auto` lost), eyebrows
   sat flush on headings, and "Read all reviews" collapsed into the tallest
   review card once the card borders were gone.

   Restated here with the ID, in one place, so the next rule added above
   does not quietly fail the same way. */
#sageMain .sage-state-line { margin: 0 auto; }
#sageMain .sage-coll .sage-sec-head h2,
#sageMain .sage-sec-head--quiet h2 { margin-top: 0.9rem; }
#sageMain .sage-dealer-row > .sage-eyebrow { margin-bottom: clamp(1.5rem, 3vw, 2.25rem); }
#sageMain .sage-edit-copy h2 { margin-top: 0.9rem; }
#sageMain .sage-edit-copy > p:not(.sage-eyebrow) { margin-top: 1.4rem; }
#sageMain .sage-pull p { margin: 0; }
#sageMain .sage-rev-line { margin: 0 0 clamp(2.25rem, 4.5vw, 3.25rem); }
#sageMain .sage-reviews-src { margin-top: clamp(2.5rem, 5vw, 3.5rem); }
#sageMain .sage-cities h2 { margin: 0.9rem auto 0; }
#sageMain .sage-cities .sage-center-more { margin-top: clamp(2rem, 4vw, 2.75rem); }
#sageMain .sage-cta h2 { margin-top: 0.9rem; }
#sageMain .sage-cta .sage-lead { margin-top: 1.25rem; }

/* --- review rail: clipping is visible now the card chrome is gone ------------
   With borders, a card half off-screen read as a card going off-screen. With
   no border it reads as text cut off at the edge -- on a phone after one
   swipe, and on desktop where the prev arrow (44px, half overhanging the
   container) sat on the first card's second line. Two fixes:

   Desktop: cards get inner padding back (no border, no background), so text
   starts clear of the arrows' inner half.

   Phones (<600px, where the arrows are already hidden): no carousel at all.
   The first three cards stack, the rest are hidden, and "Read all reviews"
   carries the other nine. Nothing can be clipped. homepage.js still runs
   -- it only sets is-ready and wires arrows that are display:none here. */
.sage-review,
.sage-review:hover { padding: 0 2rem; }   /* measured: 1.5rem left 2px under the arrow */
@media (max-width: 600px) {
  .sage-reviews {
    grid-auto-flow: row;
    grid-auto-columns: auto;
    grid-template-columns: 1fr;
    gap: 2.5rem;
    overflow: visible;
    scroll-snap-type: none;
    padding: 0;
    margin: 0;
  }
  .sage-review,
  .sage-review:hover { padding: 0; }
  .sage-review:nth-child(n+4) { display: none; }
}

/* --- tiles: plain-English line first, CNC name second (2026-10-12) ---------
   "Bedford" tells a customer nothing; "The starting point" does. The scrim
   stops move up to cover the second line -- re-measured after the change. */
.sage-tile::after {
  background: linear-gradient(to top, rgba(28, 28, 24, 0.84) 0%, rgba(28, 28, 24, 0.6) 30%, rgba(28, 28, 24, 0.2) 48%, rgba(28, 28, 24, 0) 60%);
}
.sage-tile-label { display: flex; flex-direction: column; gap: 0.35rem; align-items: center; }
.sage-tile-plain {
  font-family: var(--font-display);
  font-size: clamp(1.05rem, 1.5vw, 1.3rem);
  letter-spacing: 0;
  text-transform: none;
  font-weight: 400;
  line-height: 1.15;
}
.sage-tile-name {
  font-size: 0.6875rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  opacity: 0.85;
}
#sageMain .sage-sec-head--quiet .sage-sec-intro { margin-top: 1.1rem; }
.sage-sec-head--quiet .sage-sec-intro a { color: var(--olive-700); }

/* ==========================================================================
   MOBILE HERO — stack, don't overlay (2026-10-13)
   ==========================================================================
   The veil is tuned for a desktop text column on the left third. On a phone
   the column IS the screen, so the veil covered ~80% of the photograph and
   the hero read as a faded backdrop with six things stacked on it -- the
   rift oak it was chosen for sat under the tick list.

   deVOL's pattern instead: the photograph full-strength, edge to edge, with
   the transparent header floating over it; then the headline and one button
   on bone beneath. No veil, so no contrast maths, and the photo gets to be
   the entrance. The lede and ticks still ship (desktop, crawlers, the
   statement section one screen down restates them) but are not shown here.

   Everything in this block is viewport-scoped and overrides the September
   mobile rules above by cascade order. */
@media (max-width: 760px) {
  .sage-hero {
    display: block;
    min-height: 0;
    padding: 0 0 2.5rem;
  }
  /* The art becomes a block of its own height rather than a backdrop. 62svh
     leaves the headline's first line visible under it on an 844px phone. */
  .sage-hero-art {
    position: relative;
    inset: auto;
    height: 62svh;
    min-height: 380px;
  }
  .sage-hero-img {
    /* Frame the range, the marble and the oak -- the right two-thirds of the
       16:9 plate. Measured: 60% 60% showed the island and a blank wall. */
    object-position: 78% 56%;
  }
  .sage-hero-veil { display: none; }

  .sage-hero-inner { padding-top: 2rem; }
  .sage-hero h1 {
    font-size: clamp(2.1rem, 9vw, 2.6rem);
    line-height: 1.08;
    margin-bottom: 0;
  }
  .sage-hero-sub--short,
  .sage-hero-sub--long,
  .sage-hero-trust { display: none; }
  .sage-hero-cta { margin-top: 1.6rem; }
  .sage-hero-cta .sage-btn-ghost { display: none; }
  .sage-hero-cta .sage-btn-primary { width: 100%; text-align: center; }
  .sage-hero-rule { display: none; }
}

/* --- entrance drift ------------------------------------------------------ */
/* A slow push-in on the photograph, 1.0 -> 1.05 over nine seconds, once. The
   one piece of motion luxury sites allow themselves; at this speed it reads
   as the room settling rather than an effect. .sage-hero is overflow:hidden
   and the art box on mobile is too, so the scale never escapes its frame. */
.sage-hero-art { overflow: hidden; }
@keyframes sage-hero-drift {
  from { transform: scale(1); }
  to   { transform: scale(1.05); }
}
.sage-hero-img {
  transform-origin: 62% 55%;
  animation: sage-hero-drift 9s cubic-bezier(0.22, 0.61, 0.36, 1) forwards;
}
@media (prefers-reduced-motion: reduce) {
  .sage-hero-img { animation: none; }
}

/* --- top scrim for the floating header ---------------------------------- */
/* The header is transparent over the photograph now, and its olive links sit
   on the brightest part of the image (ceiling, upper doors): measured 3.26:1
   on the worst link; at 0.86/0.62 the links over the plaster hood still
   measured 4.12. 0.92/0.74 clears every link. A bone gradient over the top 240px -- the veil's own
   device, run vertically -- lifts that above 4.5 and gives the logo a quiet
   ground. It is on .sage-hero-art, not the veil, so it also applies on
   phones where the veil is off and the header floats over the stacked photo.
   The drift animation scales the <img>, not this layer, so the scrim holds. */
.sage-hero-art::after {
  content: "";
  position: absolute;
  left: 0; right: 0; top: 0;
  height: 240px;
  background: linear-gradient(to bottom,
    rgba(253, 251, 246, 0.92) 0%,
    rgba(253, 251, 246, 0.74) 45%,
    rgba(253, 251, 246, 0.00) 100%);
  pointer-events: none;
  z-index: 1;
}
@media (max-width: 760px) {
  .sage-hero-art::after { height: 170px; }
}

/* --- mobile section rhythm (2026-10-13) ----------------------------------
   .sage-sec pads clamp(5.5rem, 12vw, 11rem) top AND bottom; on a 412px phone
   that is 88px + 88px between every pair of sections -- a void the owner
   read as broken. Desktop keeps its air; phones get a rhythm that matches
   the reference sites' mobile views (~50px between blocks).

   The four service links wrapped 3 + 1 with "Showers" orphaned on its own
   line. A 2 x 2 grid reads as a menu rather than a broken sentence. */
@media (max-width: 760px) {
  .sage-sec { padding: 3.25rem 0; }
  .sage-sec--showcase { padding: 3.5rem 0; }
  .sage-state-line { font-size: clamp(1.4rem, 6.2vw, 1.7rem); }
  .sage-state-creds { margin-top: 1.75rem; gap: 0.7rem 1.4rem; }
  .sage-state-nav {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.9rem 1rem;
    max-width: 19rem;
    margin: 1.75rem auto 0;
  }
  .sage-state-nav a { text-align: center; }
}

/* The statement and the collections both sit on bone, so nothing marked the
   boundary but air -- less of it on phones since 2026-10-13. A hairline, the
   same one the dealer row and the tier section use. Full width, like the
   old trust bar's, not clipped to the wrap. */
.sage-coll { border-top: 1px solid var(--greige); }

/* --- tiles on phones: caption below, not over (2026-10-13) --------------
   At two tiles across, the serif label wraps to two lines and climbs out of
   the foot scrim into the brightest part of the plate -- chairs, island,
   marble. It measured but it did not read (owner: "hard to see what it
   says"). deVOL's pattern instead: photograph, then the caption on bone
   beneath it. Desktop keeps the in-photo label; there it sits on one line
   inside the scrim. */
@media (max-width: 900px) {
  .sage-tile { aspect-ratio: auto; display: block; }
  .sage-tile img { aspect-ratio: 4 / 3; height: auto; }
  .sage-tile::after { display: none; }
  .sage-tile-label {
    position: static;
    align-items: flex-start;
    text-align: left;
    gap: 0.25rem;
    padding: 0.7rem 0 0;
    color: var(--olive-900);
  }
  .sage-tile-plain { font-size: 1.15rem; }
  .sage-tile-name { color: var(--olive-600); opacity: 1; letter-spacing: 0.24em; }
}

/* ==========================================================================
   OWNER PUNCH LIST — 2026-10-13
   ==========================================================================
   Nine of the eleven items from the owner's phone review, in order. The two
   not here need material that does not exist in this repo (real photographs
   of Shlomo, the showroom and installed work) or an owner decision (one
   primary identity across logo, H1 and the service-areas H2). */

/* 1. First screen: smaller photo, value prop under the H1, button on screen. */
@media (max-width: 760px) {
  .sage-hero-art { height: 44svh; min-height: 300px; }
  .sage-hero-sub--short {
    display: block;
    margin-top: 1rem;
    font-size: 1rem;
    line-height: 1.6;
    color: var(--stone);
  }
  .sage-hero-cta { margin-top: 1.4rem; }
  /* The same sentence now opens the hero on phones, so it is not repeated
     one screen down. Desktop shows the long lede in the hero and this line
     here, as before. */
  .sage-state-line { display: none; }
  #sageMain .sage-state-creds { margin-top: 0; }
}

/* 2. Sticky mobile bar. z-index under the header (400) and the mobile nav. */
.sage-cta-bar { display: none; }
@media (max-width: 760px) {
  .sage-cta-bar {
    position: fixed;
    left: 0; right: 0; bottom: 0;
    z-index: 390;
    display: grid;
    grid-template-columns: auto 1fr;
    align-items: center;
    gap: 0 1.25rem;
    padding: 0.6rem 1.25rem calc(0.6rem + env(safe-area-inset-bottom, 0px));
    /* Solid, not frosted: a translucent bar let the copy behind it show
       through as a smudge beside "Call". */
    background: var(--bone);
    border-top: 1px solid var(--greige);
    transform: translateY(110%);
    transition: transform 0.3s var(--ease-out);
  }
  body.sage-cta-bar-on .sage-cta-bar { transform: none; }
  /* Room for the bar goes on the footer's black bottom strip, not on body:
     body padding computed to 72px and the page still could not scroll the
     last 72px (Flatsome's wrapper decides the scroll range), so the address
     line stayed under the bar. Padding inside .absolute-footer is black
     under black -- invisible -- and does extend the page. */
  body.sage-cta-bar-on #footer .absolute-footer { padding-bottom: calc(0.75rem + 4.5rem); }
  /* One line, centred, lighter. The old 1fr/1.4fr grid wrapped "Book an
     appointment" onto two lines; the grid then stretched both cells to that
     height and "Call" sat at the top of an empty box. nowrap + auto/1fr
     cannot wrap at any phone width (narrowest 320px leaves the button ~190px
     for ~165px of text). */
  .sage-cta-bar .sage-btn {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 46px;
    padding: 0 1rem;
    font-size: 0.875rem;
    font-weight: 500;
    letter-spacing: 0.03em;
    white-space: nowrap;
    border-radius: 2px;
  }
  .sage-cta-bar .sage-btn:hover,
  .sage-cta-bar .sage-btn:focus-visible { transform: none; }
  .sage-cta-call {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 46px;                    /* full tap target, no box */
    padding: 0 0.25rem;
    color: var(--olive-800);             /* 9.41:1 on bone */
    font-size: 0.875rem;
    font-weight: 500;
    letter-spacing: 0.03em;
    text-decoration: none;
  }
  .sage-cta-call svg {
    width: 17px;
    height: 17px;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.4;
    stroke-linejoin: round;
  }
  .sage-cta-call:focus-visible { outline: 2px solid var(--olive-700); outline-offset: 2px; }
  @media (prefers-reduced-motion: reduce) { .sage-cta-bar { transition: none; } }
}

/* 3. Tracked caps stay on section labels (.sage-eyebrow) and the small CNC
      name under a tile. Everything else goes sentence case and darker. */
#sageMain .sage-arrow {
  text-transform: none;
  letter-spacing: 0;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--olive-800);            /* 9.41:1 on bone */
}
#sageMain .sage-arrow::after { background: var(--olive-500); }
#sageMain .sage-state-creds {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  font-size: 0.875rem;
  color: var(--stone);                /* 6.12:1 on bone */
  /* Baseline, not center: the score is set larger in the display face, so
     centring the boxes left the licence ~3px off the rating's text line. */
  align-items: baseline;
  gap: 0.75rem 0;
}
#sageMain .sage-state-creds li { display: flex; align-items: baseline; }
/* Hairline between the rating and the licence, in place of a gap. */
#sageMain .sage-state-creds li + li::before {
  content: "";
  align-self: center;
  width: 1px;
  height: 1em;
  margin: 0 1.6rem;
  background: var(--greige);
}
.sage-state-creds a { border-bottom: 0; }
#sageMain .sage-review-proj { text-transform: none; letter-spacing: 0; font-size: 0.875rem; }
#sageMain .sage-pull footer { text-transform: none; letter-spacing: 0; font-size: 0.9375rem; color: var(--ink); }
#sageMain .sage-pull footer::before { content: "\2014\00a0"; color: var(--stone); }

/* 4. Google rating: a line of type, not a widget (owner, 2026-10-28).
      No box, no chevron, no Google yellow -- #FFB800 was the only saturated
      colour on the page and is what review plugins look like. */
.sage-trust-chip {
  display: inline-flex;
  align-items: baseline;
  gap: 0.7rem;
  color: inherit;
  text-decoration: none;
}
.sage-trust-chip strong {
  font-family: var(--font-display);
  font-weight: 400;
  font-size: 1.1rem;
  color: var(--olive-800);            /* 9.41:1 on bone */
  margin-right: 0.15em;
}
.sage-trust-chip-text {
  border-bottom: 1px solid transparent;
  padding-bottom: 1px;
  transition: border-color 0.2s ease;
}
.sage-trust-chip:hover .sage-trust-chip-text,
.sage-trust-chip:focus-visible .sage-trust-chip-text { border-bottom-color: var(--gold-700); }
.sage-trust-chip-stars { display: inline-flex; gap: 2px; align-self: center; }
/* Bronze, not brass: --gold-700 is 4.14:1 on bone, over the 3:1 that a
   meaningful graphic needs. (It fails as body TEXT -- these are not text,
   and the score is stated in words beside them.) */
.sage-trust-chip-stars svg { width: 11px; height: 11px; fill: var(--gold-700); }
@media (prefers-reduced-motion: reduce) { .sage-trust-chip-text { transition: none; } }
/* Narrow phones: the two items stack, so the hairline has nothing to divide. */
@media (max-width: 480px) {
  #sageMain .sage-state-creds { flex-direction: column; }
  #sageMain .sage-state-creds li + li::before { display: none; }
}

/* 5. Brands: one compact row of marks, one sentence, no repeated names. */
.sage-dealer-line {
  text-align: center;
  color: var(--stone);
  font-size: 0.9375rem;
  margin: 0 0 1.5rem;
}
#sageMain .sage-dealer-row { margin-top: clamp(2.5rem, 5vw, 3.5rem); padding-top: clamp(1.75rem, 3vw, 2.25rem); }
#sageMain .sage-dealer-row .sage-dealers {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 1.25rem clamp(1.75rem, 5vw, 4rem);
  max-width: none;
}
#sageMain .sage-dealer-row .sage-dealer { display: block; text-align: center; }
/* The mark is width:100% in header.css, which is 100% of nothing inside a
   flex row -- measured 0px wide on all three. Width from the mark's own
   --mark-ratio (set inline by sage_render_dealers) at the row height. */
#sageMain .sage-dealer-row .sage-dealer-mark {
  height: 32px;
  width: calc(32px * var(--mark-ratio, 3));
  max-width: 180px;
  margin: 0 auto;
}
#sageMain .sage-dealer-row .sage-dealer-mark img { max-width: 100%; max-height: 100%; }
#sageMain .sage-dealer-row .sage-dealer-name,
#sageMain .sage-dealer-row .sage-dealer-cap,
#sageMain .sage-dealer-row .sage-dealer-blurb { display: none; }

/* 6. Service cards. */
.sage-state-nav {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: clamp(0.75rem, 1.5vw, 1.25rem);
  max-width: 64rem;
  margin: clamp(1.75rem, 3.5vw, 2.5rem) auto 0;
}
.sage-svc-card {
  display: block;
  text-decoration: none;
  color: var(--olive-900);
  background: var(--bone);
  border: 1px solid var(--greige);
  transition: border-color 0.2s ease, transform 0.25s var(--ease-out);
}
.sage-svc-card:hover,
.sage-svc-card:focus-visible { border-color: var(--olive-500); transform: translateY(-2px); }
.sage-svc-img { display: block; aspect-ratio: 4 / 3; overflow: hidden; background: var(--linen); }
.sage-svc-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sage-svc-label {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.75rem 0.85rem;
  font-family: var(--font-display);
  font-size: 1.1rem;
  border-top: 1px solid var(--greige);
}
.sage-svc-label svg { width: 18px; height: 18px; flex: none; color: var(--olive-700); }
@media (max-width: 760px) {
  .sage-state-nav { grid-template-columns: 1fr 1fr; gap: 0.75rem; max-width: none; }
  .sage-svc-label { font-size: 1rem; padding: 0.6rem 0.7rem; }
}

/* 7. Collection tiles on phones: a horizontal swipe, one card mostly in
      view with the next peeking, captions below (already). Bleeds to the
      screen edge so the first card starts at the gutter. */
@media (max-width: 760px) {
  .sage-tiles {
    display: flex;
    gap: 0.9rem;
    overflow-x: auto;
    scroll-snap-type: x mandatory;
    padding: 0 clamp(1.25rem, 4vw, 2.5rem) 0.5rem;
    margin: 0 calc(-1 * clamp(1.25rem, 4vw, 2.5rem));
    scrollbar-width: none;
    overscroll-behavior-x: contain;
  }
  .sage-tiles::-webkit-scrollbar { display: none; }
  .sage-tiles > li { flex: 0 0 76%; scroll-snap-align: start; }
}

/* 10. Reviews and cities, tightened. The quote is DM Serif Display, which
       has no italic -- the browser was synthesising one. */
#sageMain .sage-pull p { font-style: normal; }
.sage-pull-stars { display: inline-flex; gap: 2px; margin-bottom: 1rem; }
.sage-pull-stars svg { width: 18px; height: 18px; fill: #FFB800; }
#sageMain .sage-reviews-src { display: flex; flex-wrap: wrap; justify-content: center; align-items: baseline; gap: 0.75rem 2rem; font-size: 0.9375rem; }
.sage-reviews-leave { color: var(--stone); }
@media (max-width: 600px) {
  .sage-reviews-rail { display: none; }
  #sageMain .sage-reviews-src { margin-top: 0.5rem; }
}
.sage-city-line {
  max-width: 40rem;
  margin: 1.5rem auto 0;
  font-size: 1.0625rem;
  line-height: 1.75;
  color: var(--ink);
}
.sage-city-line a {
  color: var(--olive-800);
  text-decoration: none;
  border-bottom: 1px solid var(--greige);
}
.sage-city-line a:hover { border-bottom-color: var(--olive-500); }
#sageMain .sage-city-line { margin-top: 1.5rem; }
#sageMain .sage-cities .sage-center-more { margin-top: 1.75rem; }
