/* ==========================================================================
   Sage Kitchen Cabinetry — Editorial header, sitewide
   Split out of homepage.css so every page gets the same header, not just the
   front page. homepage.css keeps the hero/section/grid rules it alone needs.

   Loaded on every singular page (see sage_editorial_assets in functions.php).
   Tokens come from tokens.css.
   ========================================================================== */

/* --- shared chrome ------------------------------------------------------
   .sage-wrap and the arrow link are used by the header AND the homepage
   sections, so they live here — the file that always loads.               */

.sage-wrap {
  max-width: var(--site-width, 1360px);
  margin: 0 auto;
  padding-inline: clamp(1.25rem, 4vw, 2.5rem);
}

/* --- eyebrow: the connective device across every section ----------------- */
.sage-eyebrow {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.24em;
  font-weight: 500;
  color: var(--stone);
  margin: 0;
}
.sage-eyebrow--gold,
.sage-on-dark .sage-eyebrow { color: var(--gold-300); }

/* --- arrow link, in place of buttons ------------------------------------- */
.sage-arrow {
  display: inline-flex;
  align-items: center;
  gap: 0.7rem;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.2em;
  font-weight: 500;
  text-decoration: none;
  color: var(--ink);
  padding-bottom: 0.4rem;
  position: relative;
}
.sage-arrow::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: var(--greige);
  transition: background var(--dur-base) var(--ease-out);
}
.sage-arrow:hover::after { background: var(--olive-500); }
.sage-arrow svg { transition: transform var(--dur-base) var(--ease-out); }
.sage-arrow:hover svg { transform: translateX(4px); }
.sage-on-dark .sage-arrow,
.sage-hero .sage-arrow { color: #fff; }
.sage-on-dark .sage-arrow::after,
.sage-hero .sage-arrow::after { background: var(--gold-100-a40); }
.sage-on-dark .sage-arrow:hover::after,
.sage-hero .sage-arrow:hover::after { background: var(--gold-100); }

#sageMain a:focus-visible,
.sage-hdr a:focus-visible,
.sage-burger:focus-visible {
  outline: 2px solid var(--gold-500);
  outline-offset: 4px;
}

/* ==========================================================================
   HEADER — transparent over the hero, condensing on scroll
   ========================================================================== */

.sage-hdr {
  position: fixed;
  top: 0; left: 0; right: 0;
  z-index: 400;
  background: transparent;
  border-bottom: 1px solid transparent;
  /* backdrop-filter is deliberately NOT transitioned: animating a filter
     forces a re-blur of everything behind the bar on every frame. Only the
     cheap paint properties animate. */
  transition: background var(--dur-base) var(--ease-out),
              border-color var(--dur-base) var(--ease-out);
  /* Own compositor layer so the fixed bar is not repainted against the
     hero scrolling underneath it. */
  will-change: background-color;
  transform: translateZ(0);
}
/* Fully opaque, deliberately.
   At 0.92 the hero copy showed straight through the bar while scrolling past
   it — and because that copy is styled white-on-photo, it turned near-invisible
   against bone with the logo sitting on top of it. It read as a rendering
   fault. An opaque bar means content passes cleanly underneath.
   No backdrop-filter either: there is nothing left to blur once the background
   is solid, and it was the most expensive property in this header. */
.sage-hdr.is-solid {
  background: var(--bone);
  border-bottom-color: var(--greige);
}

.sage-hdr-in {
  max-width: var(--site-width, 1360px);
  margin: 0 auto;
  padding-inline: clamp(1.25rem, 4vw, 2.5rem);
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 180px;
  transition: height 0.4s var(--ease-out);
}
/* Condensed bar (owner, 2026-10-28: "too tall when you start scrolling").
   Was 130px with an 84px logo -- barely a condense from 180. 88px keeps the
   logo's tagline legible at 60px and the nav and CTA comfortably centred. */
.sage-hdr.is-solid .sage-hdr-in { height: 88px; }

/* Two real logo files, cross-faded. No filters — the gold has to survive. */
.sage-hdr-logo {
  display: block;
  position: relative;
  height: 116px;
  /* Matches the padded viewBox (316 x 309.254). The source SVG's tagline
     runs to within ~3px of the canvas edge, so "KITCHEN CABINETRY" lost the
     K's stem and the Y's arm to subpixel rounding at small sizes; the file
     now carries 8 units of side padding and this ratio has to track it. */
  aspect-ratio: 316 / 309;
  flex-shrink: 0;
  transition: height 0.4s var(--ease-out);
}
.sage-hdr.is-solid .sage-hdr-logo { height: 60px; }
.sage-hdr-logo img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  transition: opacity 0.35s var(--ease-out);
}
.sage-hdr-logo .on-photo { opacity: 1; }
.sage-hdr-logo .on-solid { opacity: 0; }
.sage-hdr.is-solid .sage-hdr-logo .on-photo { opacity: 0; }
.sage-hdr.is-solid .sage-hdr-logo .on-solid { opacity: 1; }

.sage-hdr nav {
  display: flex;
  align-items: center;
  gap: clamp(1.3rem, 1.8vw, 2.1rem);
}
/* Eight top-level items now (Visualizer and Showroom promoted, 2026-10-13).
   Without nowrap the two-word labels broke onto a second line at 1280 AND
   1440 -- "BROWSE / CABINETS" stacked under itself -- which is worse than
   any amount of tightening. Below ~1380px eight items cannot fit between
   the logo and the Call button at this tracking, so Home is hidden there:
   the logo is the home link, and none of the five reference sites carries a
   Home item at all. The row is still in the DOM and in the mobile panel. */
.sage-hdr nav a {
  font-size: 13px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  font-weight: 500;
  white-space: nowrap;
  text-decoration: none;
  color: var(--white-a92);
  transition: letter-spacing var(--dur-base) var(--ease-out),
              color var(--dur-base) var(--ease-out);
  text-shadow: 0 1px 12px rgba(20, 22, 12, 0.5);
}
.sage-hdr nav a:hover { color: var(--gold-100); }
/* WordPress stamps .menu-item-home on the item that points at the front
   page, so this targets Home by what it is, not by its position. */
@media (max-width: 1380px) and (min-width: 901px) {
  .sage-hdr nav li.menu-item-home { display: none; }
}
.sage-hdr.is-solid nav a {
  color: var(--olive-700);
  letter-spacing: 0.14em;
  text-shadow: none;
}
.sage-hdr.is-solid nav a:hover { color: var(--olive-900); }

/* ==========================================================================
   LIGHT HERO — header colours over a bone scrim (2026-09-02)
   ==========================================================================
   The transparent header state above is written for a DARK hero photo: white
   nav links with a drop shadow, a champagne CTA, and the on-dark logo. The
   homepage hero is now a light bone scrim, so every one of those is somewhere
   between low-contrast and invisible — white-a92 on the scrim measures ~1.1:1.

   Rather than restyle each one again, this reuses the .is-solid palette that
   already exists for the scrolled bar: it is the same problem (header chrome
   on a light ground) and it is already contrast-checked.

   Scoped to body.sage-editorial-home so INTERIOR pages with a dark hero are
   untouched -- theirs are still dark photos with dark="true" (33 of 36 pages),
   and the white transparent state remains correct there.

   Widened 2026-09-17 with body.sage-light-top, which functions.php adds to any
   singular page whose FIRST section is light. The catalogue pages opened with a
   bone section and inherited the dark-hero palette, so their nav read at about
   1.1:1 -- invisible rather than merely low-contrast. This is the "widen the
   selector rather than duplicating it" path the note below already called for.

   The bar itself stays transparent — only the ink on it changes. The solid
   background still arrives on scroll via .is-solid, so the condensing
   behaviour is unchanged.

   The homepage is a light-topped page too (sage_has_light_top() says so
   since 2026-10-13). Do NOT try to include it here by prepending
   `body.sage-editorial-home .sage-hdr:not(.is-solid),` to these selector
   lists: as a separate selector that targets the whole bar, and eight such
   lines painted the header olive on load for six weeks. */
body.sage-light-top .sage-hdr:not(.is-solid) nav a {
  color: var(--olive-700);            /* 7.90:1 on bone */
  text-shadow: none;
}
body.sage-light-top .sage-hdr:not(.is-solid) nav a:hover {
  color: var(--olive-900);
}

/* The CTA was champagne-on-transparent with a gold hairline: 1.1:1 and a
   border nobody could see. Filled olive instead — it is the header's one
   action and should read as such against a pale hero. */
body.sage-light-top .sage-hdr:not(.is-solid) .sage-hdr-cta {
  background: var(--olive-800);
  border-color: var(--olive-800);
  color: var(--bone);
}
body.sage-light-top .sage-hdr:not(.is-solid) .sage-hdr-cta:hover,
body.sage-light-top .sage-hdr:not(.is-solid) .sage-hdr-cta:focus-visible {
  background: var(--olive-900);
  border-color: var(--olive-900);
  color: var(--bone);
}

/* The burger's three bars are white over a photo by default. */
body.sage-light-top .sage-hdr:not(.is-solid) .sage-burger span {
  background: var(--olive-800);
}

/* The logo is a genuine cross-fade between two FILES (on-dark and light), not
   a filter — see header-sage.php. The on-dark variant is built to sit on a
   dark photo and disappears on bone, so the pair has to swap with the rest of
   the chrome. Opacity, matching how .is-solid does it, so the transition on
   scroll stays a fade rather than a pop. */
body.sage-light-top .sage-hdr:not(.is-solid) .sage-hdr-logo .on-photo { opacity: 0; }
body.sage-light-top .sage-hdr:not(.is-solid) .sage-hdr-logo .on-solid { opacity: 1; }

/* Flatsome's menu markup arrives as <li> — strip the list chrome.
   display:contents alone leaves the ::marker behind in Safari/Firefox, so
   clear list-style on the items too and zero the indent. */
.sage-hdr nav ul,
.sage-mobile-nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
  display: contents;
}
.sage-hdr nav li,
.sage-mobile-nav li {
  list-style: none;
  margin: 0;
  padding: 0;
}
.sage-hdr nav li::marker,
.sage-mobile-nav li::marker { content: ""; }

/* --- header call CTA ----------------------------------------------------- */
/* Two states like the nav links: outlined over the hero photo, filled once the
   bar goes solid. Champagne on olive is the palette's on-dark text pair
   (8.19:1); the solid state inverts to olive-on-champagne rather than
   thinning the contrast. */
.sage-hdr-cta {
  display: inline-block;
  margin-left: 1.5rem;
  padding: 0.55rem 1.1rem;
  border: 1px solid var(--gold-100-a40);
  border-radius: var(--radius-sm);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--gold-100);
  text-decoration: none;
  transition: background 0.2s var(--ease-out), border-color 0.2s var(--ease-out),
              color 0.2s var(--ease-out);
}
.sage-hdr-cta:hover,
.sage-hdr-cta:focus-visible {
  background: var(--gold-100);
  border-color: var(--gold-100);
  color: var(--olive-900);
}
.sage-hdr.is-solid .sage-hdr-cta {
  background: var(--olive-800);
  border-color: var(--olive-800);
  color: var(--bone);
}
.sage-hdr.is-solid .sage-hdr-cta:hover,
.sage-hdr.is-solid .sage-hdr-cta:focus-visible {
  background: var(--olive-900);
  border-color: var(--olive-900);
  color: #fff;
}
.sage-hdr-cta:focus-visible {
  outline: 2px solid var(--gold-300);
  outline-offset: 2px;
}

/* --- mobile ------------------------------------------------------------- */
.sage-burger {
  display: none;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 44px; height: 44px;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
}
.sage-burger span {
  display: block;
  height: 1.5px;
  width: 24px;
  background: #fff;
  transition: background var(--dur-base) var(--ease-out),
              transform var(--dur-base) var(--ease-out),
              opacity var(--dur-fast) var(--ease-out);
}
.sage-hdr.is-solid .sage-burger span { background: var(--olive-800); }

/* Three bars -> X: the middle one fades, the outer two meet in the centre.
   gap is 5px and each bar is 1.5px, so a bar travels 6.5px to reach the
   middle. */
.sage-hdr.is-open .sage-burger span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
.sage-hdr.is-open .sage-burger span:nth-child(2) { opacity: 0; transform: scaleX(0.4); }
.sage-hdr.is-open .sage-burger span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }

.sage-mobile-nav {
  display: none;
  flex-direction: column;
  gap: 0;
  padding: 0.5rem clamp(1.25rem, 4vw, 2.5rem) 1.5rem;
  background: var(--bone-a98);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  border-top: 1px solid var(--greige);
}
.sage-mobile-nav a {
  display: block;
  padding: 0.9rem 0;
  font-size: 0.8125rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  font-weight: 500;
  text-decoration: none;
  color: var(--olive-800);
  border-bottom: 1px solid var(--greige);
}
.sage-mobile-tel {
  margin-top: 0.5rem;
  color: var(--olive-700) !important;
  border-bottom: 0 !important;
}

@media (max-width: 900px) {
  /* Fixed bar height on mobile — no height animation at all.
     Animating `height` triggers layout every frame, which desktop absorbs but
     phones do not. The mobile delta was only 12px (84->72) anyway, so the
     transition cost far outweighed the visual payoff and read as jank right
     at the start of a scroll. The bar now just fades in its background.       */
  .sage-hdr-in,
  .sage-hdr.is-solid .sage-hdr-in {
    height: 84px;
    transition: none;
  }
  .sage-hdr-logo,
  .sage-hdr.is-solid .sage-hdr-logo {
    height: 60px;
    transition: none;
  }

  /* Phones condense too (2026-10-28), but with transforms only -- no layout.
     The bar slides up 20px, so 64px of it shows; the logo scales to 0.72 and
     it drops 10px (the burger 18px) to sit centred in what remains. Nothing
     changes size in layout, so this runs on the compositor and cannot jank
     the way the height animation above did. The mobile menu panel lives
     inside the header, so it moves with the bar and stays flush beneath it. */
  .sage-hdr { transition: background var(--dur-base) var(--ease-out),
                          border-color var(--dur-base) var(--ease-out),
                          transform 0.35s var(--ease-out); }
  .sage-hdr.is-solid { transform: translateY(-20px); }
  .sage-hdr-logo,
  .sage-burger { transition: transform 0.35s var(--ease-out); }
  .sage-hdr-logo { transform-origin: left center; }
  .sage-hdr.is-solid .sage-hdr-logo { transform: translateY(10px) scale(0.72); }
  /* 18px, not 10: the burger sits top-aligned with the logo at rest (12px
     down, 44px tall), not centred, so it needs more to land on the logo's
     centre line in the 64px bar. Measured: both centre at y=32. */
  .sage-hdr.is-solid .sage-burger { transform: translateY(18px); }
  @media (prefers-reduced-motion: reduce) {
    .sage-hdr, .sage-hdr-logo, .sage-burger { transition: none; }
  }

  /* (The solid bar is opaque and blur-free for every breakpoint now — see the
     base .sage-hdr.is-solid rule. Nothing mobile-specific left to override.) */
  .sage-hdr nav { display: none; }
  /* The bar is only 84px tall on mobile and the logo already claims most of
     it — the CTA would crowd the burger. The mobile panel carries its own
     call link, so nothing is lost. */
  .sage-hdr-cta { display: none; }
  /* The 44px button centres its 24px bars, so 10px of the button is empty on
     each side. Against the bar's 20px padding-inline that put the bars ~30px
     from the edge while the logo sat at ~20px, reading as though the burger
     were inset. Pulling the button out by its own dead margin lines the bars
     up with the logo's edge; the 44px tap target is untouched, it just
     overhangs the padding rather than sitting inside it. */
  .sage-burger {
    display: flex;
    margin-right: -10px;
  }
  .sage-hdr.is-open { background: var(--bone-a98); }
  .sage-hdr.is-open .sage-burger span { background: var(--olive-800); }
  .sage-hdr.is-open .sage-hdr-logo .on-photo { opacity: 0; }
  .sage-hdr.is-open .sage-hdr-logo .on-solid { opacity: 1; }
  .sage-mobile-nav[data-open="true"] { display: flex; }
}

/* ==========================================================================
   INTERIOR PAGES — clearing the fixed header
   The homepage hero handles this itself (100vh, its own padding). Every other
   page opens with a UX Builder section: `.section.corner.nomar.dark` carrying
   a `.section-bg` photo at min-height 350px. That is already a dark photo, so
   the transparent bar sits on it correctly — it just needs room, or the bar
   lands on top of the H1.

   Scoped to the FIRST section only, and only on pages using the editorial
   header, so no other section on the page is affected.
   ========================================================================== */

body.sage-editorial-header:not(.sage-editorial-home) #content > .section:first-of-type {
  /* Clears the 180px un-condensed bar with breathing room below it. */
  padding-top: 210px !important;
  /* 350px of hero minus a 180px bar reads as a strip; give the copy room. */
  min-height: 470px !important;
}

@media (max-width: 900px) {
  body.sage-editorial-header:not(.sage-editorial-home) #content > .section:first-of-type {
    padding-top: 116px !important;
    min-height: 360px !important;
  }
}

/* Anchor targets must not land under the fixed bar. The homepage sets this for
   #sageMain; interior pages need it for the UX Builder section ids. */
body.sage-editorial-header [id] { scroll-margin-top: 150px; }
@media (max-width: 900px) {
  body.sage-editorial-header [id] { scroll-margin-top: 84px; }
}

/* Long unbroken strings (phone numbers, URLs) must not open a horizontal
   scrollbar on narrow screens. Cheap insurance; the layout itself measures
   clean (scrollWidth == innerWidth at every breakpoint, verified 2026-08-17). */
body.sage-editorial-header #content h1,
body.sage-editorial-header #content h2,
body.sage-editorial-header #content h3 {
  overflow-wrap: break-word;
}

/* ==========================================================================
   AUTHORIZED DEALER ROW
   --------------------------------------------------------------------------
   This lives in header.css, NOT homepage.css, and that is load-bearing.
   homepage.css is gated to the front page by sage_is_editorial_home(), but the
   row also renders on /cabinets/ (and the Stage 2 brand pages) via the
   [sage_dealers] shortcode. Styling it from homepage.css would leave every
   interior copy completely unstyled while the homepage looked perfect.

   For the same reason nothing below may depend on .sage-sec, .sage-sec-head,
   .sage-grid-* or .sage-style-card — all of those are front-page-only.

   LIGHT BANDS ONLY — see the contrast note on sage_render_dealers().
   ========================================================================== */

.sage-dealers {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: clamp(1.5rem, 4vw, 3.5rem);
  margin-top: clamp(2.5rem, 5vw, 4rem);
  align-items: start;
}

.sage-dealer {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  text-decoration: none;
  color: inherit;
}

/* --- optical normalisation -----------------------------------------------
   The three marks have very different ratios: 173:62 (2.79), 533:299 (1.78)
   and 754:114 (6.61). Scaling them to a common HEIGHT renders them 112 / 71 /
   265px wide — Top Knobs 3.7x the width of MSI. Scaling to a common WIDTH
   gives heights of 57 / 90 / 24px. Both read as broken.

   Perceived size tracks AREA, not height, so each mark instead gets one shared
   box and a per-brand cap that equalises ink area at roughly 4,200 sq px.
   Keyed on data-brand rather than [src*=] so renaming an upload cannot
   silently revert a mark to the generic cap.                                */
.sage-dealer-mark {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 76px;
  margin-bottom: 1.5rem;
}
.sage-dealer-mark img,
#sageMain .sage-dealer-mark img {   /* see the ID note on the per-brand caps */
  max-height: 100%;
  max-width: min(100%, 190px);
  width: auto;
  height: auto;
  object-fit: contain;
  /* Load-bearing, not decoration. The theme's lazy-loader swaps src for an
     inline SVG placeholder, and because WordPress cannot read intrinsic
     dimensions out of an SVG it emits viewBox="0 0 100 100" with NO width or
     height attrs. Both SVG marks therefore inherit a 1:1 ratio and render
     square (CNC measured 62x62 on staging) until the real file loads — and
     with height:auto they never recover. --mark-ratio carries the true viewBox
     ratio from sage_dealer_brands(), so the box is correct from first paint.
     The PNG is unaffected (it ships real width/height) but shares the rule
     harmlessly. */
  aspect-ratio: var(--mark-ratio, auto);
}
/* Per-mark width caps, derived rather than guessed. With aspect-ratio pinned
   above, width alone determines the rendered box, so each cap solves for equal
   *ink* area — box area weighted by how much of each viewBox the artwork
   actually fills (CNC ~62%, MSI ~42%, Top Knobs ~34%, which is why the widest
   mark also gets the most width instead of being punished for it).
   Renders 153x55 / 135x76 / 200x30 at desktop. Re-measure with the headless
   audit in the plan if any mark is replaced. */
/* Each cap is written twice — once plain, once behind #sageMain — and that
   duplication is deliberate.
   homepage.css:31 declares `#sageMain img { max-width: 100% }`. An ID selector
   scores (1,0,1) and beats any chain of classes, so on the FRONT PAGE ONLY the
   caps below were silently dropped: Top Knobs rendered 389px there against a
   correct 200px on /cabinets/, which has no #sageMain wrapper.
   Matching the ID is the narrow fix. The alternative — !important — would be
   heavier and would fight the shared rule everywhere instead of just here.
   Verified by reading computedMaxWidth in a headless browser, not by eye. */
.sage-dealer-mark[data-brand="cnc-cabinetry"] img,
#sageMain .sage-dealer-mark[data-brand="cnc-cabinetry"] img { max-width: 153px; }
.sage-dealer-mark[data-brand="msi-surfaces"] img,
#sageMain .sage-dealer-mark[data-brand="msi-surfaces"] img { max-width: 135px; }
.sage-dealer-mark[data-brand="top-knobs"] img,
#sageMain .sage-dealer-mark[data-brand="top-knobs"] img { max-width: 200px; }

.sage-dealer-name {
  font-family: var(--font-body);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.15em;
  font-weight: 600;
  color: var(--ink);
  margin: 0;
}
.sage-dealer-cap {
  display: block;
  margin-top: 0.35rem;
  font-size: 0.8125rem;
  color: var(--olive-700);   /* 8.17:1 on bone. Never gold here — 2.81:1 fails. */
}
.sage-dealer-blurb {
  display: block;
  margin-top: 0.75rem;
  font-size: 0.9375rem;
  line-height: 1.65;
  /* --stone used to be 3.55:1 on --bone, failing AA for 15px body text, so
     this rule carried the darkened value inline. The token itself is now that
     value, so the workaround is gone and this follows the rest of the site
     again. */
  color: var(--stone);
  max-width: 24rem;
}

a.sage-dealer .sage-dealer-mark img {
  transition: opacity var(--dur-base) var(--ease-out);
}
a.sage-dealer:hover .sage-dealer-mark img { opacity: 0.72; }

/* Collapse to one column early: three logos plus blurbs get cramped well
   before phone widths. Note .sage-grid-4 in homepage.css ships NO breakpoint
   and stays 4-up on a 375px screen — do not copy that pattern here. */
@media (max-width: 860px) {
  .sage-dealers { grid-template-columns: 1fr; gap: 2.5rem; }
  /* Taller than the desktop box would suggest: stacked one-per-row there is
     plenty of vertical room, and at 60px the 1.78:1 MSI lockup was the only
     mark hitting the height cap, which shrank it against the other two. */
  .sage-dealer-mark { height: 72px; }
  .sage-dealer-blurb { max-width: 32rem; }
}

@media (prefers-reduced-motion: reduce) {
  a.sage-dealer .sage-dealer-mark img { transition-duration: 0.01ms !important; }
}

/* --------------------------------------------------------------------------
   Light cards inside a dark band
   --------------------------------------------------------------------------
   A bone card sitting on the olive band is the site's standard "three steps"
   pattern. Flatsome paints everything inside a section[dark] with light text,
   and that rule does not stop at the card edge -- so the card's own text came
   out bone-on-bone at exactly 1.00:1. Invisible, not merely low contrast, and
   the fourth instance of this failure mode on the project (list pills,
   .sage-split headings, Gravity Forms' required marker).

   It cannot be fixed by selector alone: UX Builder puts the card background on
   .col-inner through a generated #row-<id> rule, leaving no inline style or
   class to key off. So the row opts in with class="sage-light-cards", and this
   lives in header.css (sitewide) so any page adding the pattern can use it.
   -------------------------------------------------------------------------- */
.dark .sage-light-cards > .col > .col-inner { color: var(--ink); }
.dark .sage-light-cards > .col > .col-inner :is(h1, h2, h3, h4, h5, h6) {
  color: var(--olive-900);
}
.dark .sage-light-cards > .col > .col-inner :is(p, li, span, strong) {
  color: var(--ink);
}

/* --------------------------------------------------------------------------
   Door-style grid  ([sage_door_styles])
   --------------------------------------------------------------------------
   Sixteen doors as pictures rather than a list of names. The swatches are tall
   (roughly 1:2 -- a drawer front above a door), so the tiles are narrow and the
   grid runs wide: four across on desktop, two on a phone. One across would put
   the last door four screens down.

   auto-fill with a minmax floor rather than a fixed count, because the same
   shortcode is used filtered to one collection, where four columns of two tiles
   would leave half a row of dead space.
   -------------------------------------------------------------------------- */
/* Flatsome styles every ul/li on the site -- indent, bullets, and a decorative
   left border that renders as a gold rule between grid cells. It beats this
   file on specificity through `.entry-content ul`, so the reset is stated on
   the component's own classes and the list properties are zeroed explicitly.
   Same lesson as the zoom pill and Gravity Forms: a computed value that appears
   nowhere in this file is the parent theme. */
.sage-doorgrid {
  display: grid;
  /* Four across at desktop width. auto-fill with a small floor gave seven
     158px columns at 1440px -- technically "responsive", visually a contact
     sheet. These tiles are ~1:2, so a narrow column is a very tall thin door. */
  grid-template-columns: repeat(4, 1fr);
  gap: clamp(1.25rem, 2.5vw, 2rem);
  /* A 1:2 swatch in a 300px column is 600px tall, and sixteen of those is four
     screens of doors. Capping the grid keeps a tile near 200x400 -- big enough
     to read a bevel, small enough that the whole range is two screens. */
  max-width: 60rem;
  margin: clamp(2rem, 4vw, 3rem) auto 0;
  padding: 0;
  list-style: none;
}

.sage-doorgrid > .sage-door {
  display: flex;
  flex-direction: column;
  margin: 0;
  padding: 0;
  border: 0;
  list-style: none;
}

.sage-doorgrid > .sage-door::before,
.sage-doorgrid > .sage-door::marker { content: none; }

/* The swatch sits on bone, not white. These are studio shots on a white sweep,
   and on a #FDFBF6 section a white-backed image reads as a brighter rectangle
   floating on the band -- the same coldness the redesign removed everywhere
   else. The hairline gives the pale doors (White, Sleek White, Snow) an edge;
   without it they dissolve into the section. */
.sage-door-img {
  display: block;
  overflow: hidden;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  background: var(--bone);
}

.sage-door-img img {
  display: block;
  width: 100%;
  height: auto;
}

/* Woodbourne and Alvic have no honest swatch yet. A dashed well keeps the tile
   the same shape as its neighbours instead of collapsing the grid row, and
   reads as "nothing here yet" rather than as a failed image. aspect-ratio
   matches the real swatches (560x1120). */
.sage-door-img--none {
  aspect-ratio: 1 / 2;
  border-style: dashed;
  background: transparent;
}

/* The style term leads. This is the word people search for -- "shaker",
   "slab" -- and the whole reason the grid exists. */
.sage-door-style {
  margin: 0.75rem 0 0;
  font-family: var(--font-body);
  font-size: var(--text-sm);
  font-weight: 600;
  line-height: var(--leading-tight);
  letter-spacing: 0;
  color: var(--color-heading);
  text-transform: none;
}

/* These are <span>s, so they need an explicit block: left inline they ran
   together as "Dover Bedford" on one line and wrapped mid-phrase. */
.sage-door-line,
.sage-door-meta,
.sage-door-note {
  display: block;
}

/* CNC's own name, kept because it is what the quote and the order will say. */
.sage-door-line {
  margin-top: 0.15rem;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

.sage-door-meta {
  margin-top: 0.1rem;
  font-size: var(--text-xs);
  line-height: 1.45;
  color: var(--color-text-muted);
  text-wrap: pretty;
}

.sage-door-note {
  margin-top: 0.35rem;
  font-size: var(--text-xs);
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--olive-700);
}

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

@media (max-width: 600px) {
  /* Two across, never one: these tiles are twice as tall as they are wide, so a
     single column pushes the sixteenth door absurdly far down the page. */
  .sage-doorgrid { grid-template-columns: repeat(2, 1fr); gap: 1rem; }
}

/* ==========================================================================
   FOOTER — compact on phones (2026-10-13)
   ==========================================================================
   The footer is reusable block 21, a Flatsome layout in the database that
   every page embeds, so it is restyled here rather than re-authored: four
   columns (brand, Services, Service Areas, Company) that stack full-width on
   a phone into ~1,440px of olive -- a 176px logo, 36px link rows with
   dividers, a 105px Google badge. Owner: "really clunky".

   Columns are addressed by position inside the row (the block's ids are
   generated). If the block is ever re-ordered, these four selectors are the
   thing to update. Desktop is left as it is. */
@media (max-width: 760px) {
  #footer .section { padding-top: 2.25rem !important; padding-bottom: 1.5rem !important; }
  #footer .row > .col { padding-bottom: 0.75rem; }
  #footer .row { display: flex; flex-wrap: wrap; }

  /* Brand column: small logo, tagline, the two contact icons, the licence. */
  #footer .row > .col:nth-child(1) .img { max-width: 132px; margin-bottom: 0.4rem; }
  #footer .row > .col:nth-child(1) .img-inner img { width: 100%; height: auto; }
  #footer .row > .col:nth-child(1) p { font-size: 0.9375rem; line-height: 1.5; }
  #footer .row > .col:nth-child(1) .gap-element { padding-top: 0 !important; height: 6px !important; }
  #footer .row > .col:nth-child(1) .social-icons { margin: 0.25rem 0 0.5rem; }
  #footer .row > .col:nth-child(1) .text.is-xsmall { font-size: 0.75rem; opacity: 0.85; }

  /* Services and Company share a row; Service Areas becomes one wrapped line. */
  #footer .row > .col:nth-child(2),
  #footer .row > .col:nth-child(4) { flex: 0 0 50%; max-width: 50%; }
  #footer .row > .col:nth-child(3) { order: 5; flex: 0 0 100%; max-width: 100%; }
  #footer .row > .col:nth-child(4) .col-inner { text-align: left; }

  #footer .menu-title { margin-bottom: 0.25rem; }
  #footer .menu-title p { font-size: 0.6875rem; letter-spacing: 0.18em; margin: 0; }
  #footer .is-divider { margin: 0.35rem 0 0.5rem; max-width: 2rem; height: 1px; }
  #footer .ux-menu-link,
  #footer .ux-menu-link__link { min-height: 0; border: 0 !important; }
  #footer .ux-menu-link__link { padding: 0.25rem 0; font-size: 0.9375rem; }
  #footer .row > .col:nth-child(3) .ux-menu.stack-col { flex-direction: row; flex-wrap: wrap; gap: 0 1.1rem; }
  #footer .row > .col:nth-child(3) .ux-menu-link { width: auto; }

  #footer .row > .col:nth-child(4) .gap-element { height: 8px !important; padding: 0 !important; }
}

/* --- footer bottom line (2026-10-28) ---------------------------------------
   Flatsome paints `.absolute-footer, html { background: #000 }` from the
   footer_bottom_color mod, so the copyright line sat in a black strip under
   the olive footer -- read by the owner as a second footer, repeating the
   phone and address (blanked in functions.php; only the copyright remains).
   It becomes the olive footer's last line: same ground, a champagne
   hairline, smaller and quieter type.
   `html` too, or overscroll on a phone still flashes black. */
html,
#footer .absolute-footer { background-color: var(--olive-800) !important; }
#footer .absolute-footer {
  padding: 1.25rem 0 1.5rem;
  border-top: 1px solid rgba(254, 235, 170, 0.16);    /* --gold-100, faint */
}
#footer .absolute-footer .container {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.25rem 1.5rem;
}
#footer .absolute-footer .footer-primary { float: none; margin: 0; }
#footer .absolute-footer .footer-secondary { display: none; }   /* emptied */
#footer .absolute-footer .footer-text,
#footer .absolute-footer .copyright-footer {
  padding: 0;
  font-size: 0.75rem;
  line-height: 1.6;
  letter-spacing: 0.03em;
  color: rgba(253, 251, 246, 0.72);                    /* --bone @ 72% */
}

/* "Review us on Google": a champagne text link, not Google's white sticker.
   The sticker was the one off-palette object in the footer. External, so ↗. */
/* Left, flush with the Company links above it; the column centres by default. */
#footer .sage-footer-review { text-align: left; margin: 0 !important; padding: 0 !important; }
#footer .sage-footer-review p { margin: 0; }
#footer .sage-footer-review a {
  color: var(--gold-100);                              /* 8.19:1 on olive-800 */
  font-size: 0.875rem;
  letter-spacing: 0.02em;
  text-decoration: none;
  border-bottom: 1px solid rgba(254, 235, 170, 0.4);
  padding-bottom: 2px;
  transition: border-color 0.2s ease;
}
#footer .sage-footer-review a::after { content: " \2197"; }
#footer .sage-footer-review a:hover,
#footer .sage-footer-review a:focus-visible { border-bottom-color: var(--gold-100); }

/* --- showroom-by-appointment note ----------------------------------------
   One wording, defined in visit.php, printed wherever a page invites a visit.
   Colour is inherited so it sits on any band -- bone, linen, the pale sage
   closing band and the olive footer -- and only the lead is emphasised. It
   never goes below body size on the full variant: this is the sentence that
   stops someone driving over to a locked door. */
.sage-visit-note { font-size: 0.9375rem; line-height: 1.55; margin: 1rem 0 0; }
.sage-visit-note strong { font-weight: 600; }
.sage-visit-hours { display: block; margin-top: 0.35rem; font-size: 0.75rem; letter-spacing: 0.08em; text-transform: uppercase; }
.sage-visit-note--short { font-size: 0.8125rem; margin-top: 0.75rem; }
.sage-cta .sage-visit-note { margin-left: auto; margin-right: auto; max-width: 34rem; }
.sage-pd-cta .sage-visit-note { flex-basis: 100%; margin: 0; }
.sage-visit-note--footer { font-size: 0.8125rem; line-height: 1.65; margin: 0; }
.sage-visit-note--footer a { color: inherit; }
