/* ═══════════════════════════════════════════════════════════════════════════
   KR SITE-WIDE PRESENTATION STANDARDS
   GENERATED by apply_kr_sitewide_standards.py — do not hand-edit.

   Linked last on every page, so it consolidates the per-page layers below it
   rather than competing with them.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1 · HERO TINT PARITY ──────────────────────────────────────────────────
   One wash for every hero on the site. The values are the About page's
   computed treatment, which was already the site reference:

       linear-gradient(to bottom, rgba(14,21,32,0.10), rgba(14,21,32,0.45))

   Before this rule nine selectors carried their own gradient — Projects at
   0.02→0.30, the home hero at 0.15/0.30/0.75, Solutions inverted to `to top`,
   the Sosoares manufacturer page at 0.62/0.38/0.68, and the product-detail
   washes with six stops. Hero titles stay legible because the bottom stop is
   unchanged from the reference and the existing title text-shadow is retained
   where a page already carried one.
   ------------------------------------------------------------------------- */
:root {
  --kr-hero-wash-top: rgba(14, 21, 32, 0.10);
  --kr-hero-wash-bottom: rgba(14, 21, 32, 0.45);
  --kr-hero-wash: linear-gradient(to bottom,
                    var(--kr-hero-wash-top),
                    var(--kr-hero-wash-bottom));
}

/* Dark theme keeps the identical wash: the reference treatment is already
   theme-neutral (a cool near-black at low alpha over photography), and About
   does not change it between themes. Declared explicitly so a future theme
   token cannot drift one page away from the rest. */
[data-theme="dark"] {
  --kr-hero-wash-top: rgba(14, 21, 32, 0.10);
  --kr-hero-wash-bottom: rgba(14, 21, 32, 0.45);
}

.page-hero-overlay,
.page-hero--image-only .page-hero-overlay,
#page-projects .page-hero-overlay,
#page-sosoares-overview .page-hero-overlay,
.hero-overlay,
.page-hero .hero-overlay,
.tp-prod-hero-wash,
.tp-page-detail .tp-prod-hero-wash,
.tp-page-detail .tp-prod-hero--light .tp-prod-hero-wash,
#page-as-fd-75 .tp-prod-hero-wash {
  background: var(--kr-hero-wash) !important;
}

/* ── 2 · COMPACT, RIGHT-ALIGNED CAPABILITY BANDS ───────────────────────────
   The locked KR tile anatomy is unchanged: 48px frame, Switzer SemiBold
   9.5pt title, Switzer Medium 7.5pt data line, data line omitted entirely
   where there is no stat.

   What changes is the track sizing. The bands previously used
   `repeat(6, minmax(0,1fr))`, which stretched six equal columns across the
   full container and left the tiles floating far apart. They are now
   content-sized and packed to the right-hand edge, which is what the brand
   asks for and also what makes a 9- or 10-tile product still fit one row.
   ------------------------------------------------------------------------- */
:root {
  --kr-cap-gap-x-tight: 0.5rem;   /* 8px — "closely spaced" */
  --kr-cap-gap-y-tight: 1.25rem;
  --kr-cap-col: 5.25rem;          /* 84px — fits the longest brand label */
}

.tp-capsband .tp-panel--caps .tp-caps-grid,
.tp-page-detail .tp-capsband .tp-caps-grid,
.tp-page-listing .tp-panel--caps > .tp-caps-grid,
#page-windows-doors .tp-capsband .tp-caps-grid,
#page-test-as-fd-75 .tp-panel--caps > .tp-caps-grid,
.tp-capsband:not([data-kr-cap-density="curated"]) .tp-panel--caps .tp-caps-grid,
#page-windows-doors .tp-capsband:not([data-kr-cap-density="curated"]) .tp-caps-grid {
  display: grid !important;
  grid-auto-flow: column !important;
  grid-template-columns: none !important;
  grid-auto-columns: minmax(0, var(--kr-cap-col)) !important;
  justify-content: end !important;      /* right-aligned on desktop */
  gap: var(--kr-cap-gap-y-tight) var(--kr-cap-gap-x-tight) !important;
  row-gap: var(--kr-cap-gap-y-tight) !important;
  column-gap: var(--kr-cap-gap-x-tight) !important;
  align-items: start !important;
}

/* Mobile safety. Column auto-flow cannot wrap, so below the desktop
   breakpoint the bands return to explicit wrapped rows and are centred —
   a right-aligned partial row reads as a mistake on a narrow screen. */
@media (max-width: 960px) {
  .tp-capsband .tp-panel--caps .tp-caps-grid,
.tp-page-detail .tp-capsband .tp-caps-grid,
.tp-page-listing .tp-panel--caps > .tp-caps-grid,
#page-windows-doors .tp-capsband .tp-caps-grid,
#page-test-as-fd-75 .tp-panel--caps > .tp-caps-grid,
.tp-capsband:not([data-kr-cap-density="curated"]) .tp-panel--caps .tp-caps-grid,
#page-windows-doors .tp-capsband:not([data-kr-cap-density="curated"]) .tp-caps-grid {
    grid-auto-flow: row !important;
    grid-template-columns: repeat(4, minmax(0, 1fr)) !important;
    grid-auto-columns: auto !important;
    justify-content: center !important;
    gap: 1.35rem 0.75rem !important;
    row-gap: 1.35rem !important;
    column-gap: 0.75rem !important;
  }
}
@media (max-width: 640px) {
  .tp-capsband .tp-panel--caps .tp-caps-grid,
.tp-page-detail .tp-capsband .tp-caps-grid,
.tp-page-listing .tp-panel--caps > .tp-caps-grid,
#page-windows-doors .tp-capsband .tp-caps-grid,
#page-test-as-fd-75 .tp-panel--caps > .tp-caps-grid,
.tp-capsband:not([data-kr-cap-density="curated"]) .tp-panel--caps .tp-caps-grid,
#page-windows-doors .tp-capsband:not([data-kr-cap-density="curated"]) .tp-caps-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr)) !important;
  }
}
@media (max-width: 400px) {
  .tp-capsband .tp-panel--caps .tp-caps-grid,
.tp-page-detail .tp-capsband .tp-caps-grid,
.tp-page-listing .tp-panel--caps > .tp-caps-grid,
#page-windows-doors .tp-capsband .tp-caps-grid,
#page-test-as-fd-75 .tp-panel--caps > .tp-caps-grid,
.tp-capsband:not([data-kr-cap-density="curated"]) .tp-panel--caps .tp-caps-grid,
#page-windows-doors .tp-capsband:not([data-kr-cap-density="curated"]) .tp-caps-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr)) !important;
    gap: 1.1rem 0.5rem !important;
    row-gap: 1.1rem !important;
    column-gap: 0.5rem !important;
  }
}

/* The kr-variant band keeps its own locked treatment. */
.kr-variant .tp-panel.tp-panel--caps.kr-variant-caps > .tp-caps-grid--variant {
  grid-auto-flow: row !important;
  justify-content: center !important;
}

/* Long labels are allowed to wrap rather than overflow the 84px track. */
.tp-capsband .tp-cap .tp-cap-name,
.tp-capsband .tp-cap .tp-cap-value {
  overflow-wrap: break-word;
  hyphens: auto;
  max-width: 100%;
}

/* ── 3 · WINDOWS & DOORS SECTION SURFACES ──────────────────────────────────
   Window Systems and Door Systems are the two halves of the page and were
   visually identical. Each header now sits on its own approved-palette
   surface: Windows on Surface alt (#F2F0EC light / #221E1A dark) with a
   bronze rule, Doors on Surface offset (#ECEAE5 / #1C1916) with a charcoal
   rule. Body text stays --color-text on both, so contrast remains AA.
   ------------------------------------------------------------------------- */
#wd-windows > .container > .wd-range-header,
#wd-doors > .container > .wd-range-header {
  padding: 2rem clamp(1rem, 4vw, 3rem);
  border-radius: 8px;
  margin-bottom: 2.5rem;
  border: 1px solid var(--color-border, #D0CCBF);
}
#wd-windows > .container > .wd-range-header {
  background: var(--color-surface-alt, #F2F0EC);
  border-left: 3px solid var(--color-accent, #8C6E45);
}
#wd-doors > .container > .wd-range-header {
  background: var(--color-surface-offset, #ECEAE5);
  border-left: 3px solid var(--color-primary, #2A2520);
}
[data-theme="dark"] #wd-windows > .container > .wd-range-header {
  background: #221E1A;
  border-color: #35302B;
  border-left-color: #8C6E45;
}
[data-theme="dark"] #wd-doors > .container > .wd-range-header {
  background: #1C1916;
  border-color: #35302B;
  border-left-color: #E8E4DC;
}
#wd-windows > .container > .wd-range-header .section-title,
#wd-doors > .container > .wd-range-header .section-title {
  color: var(--color-text);
  margin-bottom: 0.75rem;
}

/* ── 4 · UNIFIED COMPARISON SECTIONS ───────────────────────────────────────
   Every comparison CTA band previously carried an inline `background:#1A1714`
   plus its own padding, and the titles were 1.1rem — smaller than the body
   copy beside them. One surface, one enlarged title.
   ------------------------------------------------------------------------- */
.wd-specs-section {
  background: #1A1714 !important;
  padding-block: 2.75rem !important;
}
.wd-specs-section .wd-cmp-cta-title {
  font-size: clamp(1.5rem, 2.4vw, 2rem) !important;
  line-height: 1.2 !important;
  color: #E8E4DC !important;
  margin-bottom: 0.5rem;
}
.wd-specs-section .wd-cmp-cta-sub {
  color: #C6C0B8 !important;   /* 8.4:1 on #1A1714 — AA at body size */
}

/* ── 5 · PINNED ACTION CHEVRONS ────────────────────────────────────────────
   The chevron is a pinned KR sprite symbol, never a glyph character, and it is
   aria-hidden so the accessible name of every action is unchanged.

   Size. The standard is 18px, chosen after visual testing at 12, 14, 16,
   18 and 20. The previous 12px chevron was optically weak beside the 20px
   semantic action icon it sits next to; 16px was still faint; 20px read as a
   second icon rather than a direction cue. 18px is the clear step.

   Frameless. The symbol referenced here is `kr-i-chevron-right-bare`, derived from
   the pinned chevron with the KR icon-tile frame removed and the 82% artwork
   inset dropped, so the glyph fills its box. The declarations below additionally
   guarantee no border, background, rounded container or shadow can be inherited
   from any action or icon layer. The locked 48px capability tiles are untouched:
   they use the framed symbols through `.tp-cap-icon`, which this block never
   matches.
   ------------------------------------------------------------------------- */
:root {
  --kr-act-chev-size: 18px;
  --kr-touch-min: 44px;
}

/* `assets/icons/kr-icons.css` sizes every action icon with
   `.tp-act svg.kr-icon { width: 20px; height: 20px }` — specificity (0,2,1),
   which outranks a bare `.kr-act-chev` (0,1,0) however late this sheet is
   linked. Matching the same shape at (0,2,1) from this last-linked sheet makes
   the pinned size win in every container without enumerating them. */
.kr-act-chev,
svg.kr-act-chev.kr-icon,
.tp-act svg.kr-act-chev,
.btn svg.kr-act-chev,
.card-link svg.kr-act-chev,
.sys-link svg.kr-act-chev,
.mobile-link svg.kr-act-chev,
.kr-carousel-btn svg.kr-act-chev {
  flex: 0 0 auto;
  width: var(--kr-act-chev-size);
  height: var(--kr-act-chev-size);
  margin-right: 0.42em;
  opacity: 0.85;
  transform: translateY(0.01em);
  /* frameless, unconditionally */
  border: 0 !important;
  border-radius: 0 !important;
  background: none !important;
  background-color: transparent !important;
  box-shadow: none !important;
  padding: 0 !important;
  outline: 0;
}
/* Nothing may draw a frame around or behind the chevron. */
.kr-act-chev::before,
.kr-act-chev::after {
  content: none !important;
}
a:hover > .kr-act-chev,
button:hover > .kr-act-chev,
a:focus-visible > .kr-act-chev,
button:focus-visible > .kr-act-chev {
  opacity: 1;
}
.mobile-link > .kr-act-chev { margin-right: 0.5em; opacity: 0.7; }

/* ── 5b · TOUCH TARGETS ────────────────────────────────────────────────────
   Control-style actions were measuring 34px tall on desktop and 32px on mobile,
   below the 44px minimum. They are brought up to 44px here so the larger
   chevron is never achieved at the cost of a smaller hit area.

   Genuinely inline text links inside prose (`.tp-textlink` in a paragraph) are
   deliberately excluded: forcing a 44px box on a link inside a sentence breaks
   line rhythm, and inline links in a block of text are exempt from the target
   size criteria. They are recorded as an exemption in the QA guard rather than
   silently skipped.
   ------------------------------------------------------------------------- */
.tp-act,
.btn,
.kr-cmp-toggle,
.tp-doc-link,
.card-link,
.sys-link,
.mobile-link,
.sys-specs-toggle,
.kr-cmp-tray-go,
.kr-cmp-tray-clear,
.kr-cmp-enquire,
.tp-comparebar-open {
  min-height: var(--kr-touch-min);
  display: inline-flex;
  align-items: center;
}
.tp-act { align-items: center; }
/* Card actions stretch across their card, so keep them block-level flex. */
.card-link, .sys-link { display: flex; }

/* ── 6 · SUPPORT FOR THE CONVERTED F&V / O&I OVERVIEW CARDS ────────────────
   The converted cards carry a feature list and a text brand label in place of
   the Sosoares logo image. Both need to match the existing card rhythm.
   ------------------------------------------------------------------------- */
.tp-card .tp-feat-list {
  list-style: none;
  margin: 1rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.5rem;
}
.tp-card .tp-feat-list > li {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  font-size: 0.94rem;
  line-height: 1.5;
  color: var(--color-text);
}
.tp-card .tp-feat-list > li > .kr-icon {
  flex: 0 0 auto;
  width: 15px;
  height: 15px;
  margin-top: 0.2em;
  opacity: 0.8;
}
.tp-card-brand {
  display: block;
  margin-bottom: 0.35rem;
  color: var(--color-text-muted, #6B6B6B);
}
.tp-award-list {
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1.25rem;
}
.tp-award-list > li {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  font-size: 0.9rem;
  color: var(--color-text-muted, #6B6B6B);
}
.tp-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 1rem;
  margin-top: 1.5rem;
}
.tp-gallery .tp-gal-item { margin: 0; }
.tp-gallery .tp-gal-item img {
  width: 100%;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 6px;
}
.tp-doc-list { list-style: none; margin: 1.25rem 0 0; padding: 0; display: grid; gap: 0.6rem; }
.tp-plan-list { list-style: none; margin: 1.5rem 0 0; padding: 0; display: grid; gap: 0.7rem; }
.tp-plan-list > .tp-plan-item { display: flex; align-items: flex-start; gap: 0.6rem; line-height: 1.55; }
.tp-plan-list > .tp-plan-item > .kr-icon { flex: 0 0 auto; margin-top: 0.2em; opacity: 0.8; }
.tp-sec-subtitle { margin-top: 2rem; font-size: 1.15rem; }

/* ── 8 · ACCESSIBLE, ON-BRAND TEXT AND LINK COLOUR ───────────────────────
   Two computed-contrast failures, both measured against the true composited
   background rather than the nominal token pairing.

   (a) `.tp-doc-link` had NO `color` declaration anywhere in the site, so it
       fell through to the user-agent default `#0000EE`. On the dark background
       `#141210` that is 1.99:1 against a 4.5:1 requirement at 18px — below AA
       even for large text — and browser-default blue is not a KR colour at all.
       These are the outbound product-document links ("Renson Product Page",
       "View Full BIPV System"), so they are exactly the links a specifier
       clicks.

       Fixed with the accent ramp the KR system already defines for links and
       CTAs. The ramp is theme-aware, so one declaration is correct in both
       themes and hover moves the right way in each — darker on light, brighter
       on dark:

         rest         var(--color-accent-hover)   light #7A5E38  5.62:1
                                                  dark  #AE8E65  6.10:1
         hover/focus  var(--color-accent-active)  light #664E2E  7.28:1
                                                  dark  #BE9E75  7.42:1

       Worst case over every surface such a link sits on is 5.01:1 (light
       surface-offset) and 4.95:1 (dark charcoal panel) — AA with margin
       everywhere. Note that `--color-accent` itself is only 4.42:1 on the light
       background, which is why the hover rung is the correct resting value for
       body-sized link text rather than the base accent.

       The underline, the external-link `target`/`rel` and the pinned chevron are
       untouched: colour is not the only affordance.

   (b) `.tp-attr` inherits `--color-text-muted`, which is designed against the
       page background `#F8F7F4` (4.97:1) but lands on the darker surface-offset
       `#ECEAE5` inside a `.tp-panel`, giving 4.43:1 at 11.84px — a marginal but
       real AA failure. Darkening the global muted token would touch every
       secondary text run on the site, so the override is scoped to `.tp-attr`
       and light mode only; dark mode already passes (5.07:1 on the charcoal
       panel) and is left alone.

       The value is derived from KR tokens rather than introduced: 90% muted
       mixed toward the primary text token resolves to `#636362` and 5.00:1 on
       surface-offset, a 4/255 shift that keeps the muted hierarchy intact while
       clearing AA with a margin. It also passes on every other light surface
       (5.28–6.01:1), so scoping by surface is unnecessary.
   ------------------------------------------------------------------------- */
.tp-doc-link {
  color: var(--color-accent-hover);
  text-decoration: underline;
  text-underline-offset: 2px;
}
.tp-doc-link:hover,
.tp-doc-link:focus-visible {
  color: var(--color-accent-active);
}
.tp-doc-link:focus-visible {
  outline: 2px solid var(--color-accent-active);
  outline-offset: 3px;
  border-radius: 2px;
}
[data-theme='light'] .tp-attr,
:root:not([data-theme]) .tp-attr {
  /* 90% --color-text-muted mixed toward --color-text; emitted as the resolved
     value rather than a live color-mix() so every engine and every audit tool
     reads the same colour. 5.00:1 on surface-offset. */
  color: #636362;
}

/* ── 9 · THE F&V / O&I BREADCRUMB AND HERO CAPTION ───────────────────────
   The 19 `fvoi-*.html` pages ship a *second* breadcrumb component,
   `.tp-crumbs > .tp-crumb-list`, that no stylesheet ever targeted. It therefore
   rendered as a raw numbered list — "1. Home  2. Solutions  3. …" stacked
   vertically — with browser-default underlined blue links at 1.99:1 on the dark
   background. The 28 Schüco and Sosoares detail pages use `.tp-crumbband-path`
   instead, which *is* styled, so the two families of detail page did not match.
   These rules bring the F&V / O&I component to the same treatment rather than
   inventing a third: flex trail, `/` separators, `--tp-accent-ink` links
   (`#7A5E38` light 5.62:1, `#AC8E65` dark 6.06:1) and the current page in body
   text.

   The same pages put `.tp-prod-hero-brand` and `.tp-prod-hero-eyebrow` in the
   dark hero panel while leaving them at the light-theme body colour `#1E1A16`,
   giving **1.14:1** — effectively invisible text, confirmed by screenshot. The
   hero is dark in both themes, so the caption takes the same near-white the
   Schüco and Sosoares heroes already use.
   ------------------------------------------------------------------------- */
.tp-crumbs .tp-crumb-list {
  margin: 0;
  padding: 0;
  max-width: none;
  list-style: none;
  color: var(--color-text-muted);
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0 var(--space-2, 0.5rem);
  font-size: 0.9rem;
}
.tp-crumbs .tp-crumb-list > li {
  display: flex;
  align-items: baseline;
  gap: var(--space-2, 0.5rem);
}
/* Separator is generated, so it is never announced by a screen reader. */
.tp-crumbs .tp-crumb-list > li + li::before {
  content: '/';
  opacity: 0.45;
  color: var(--color-text-muted);
}
.tp-crumbs .tp-crumb-list a {
  color: var(--tp-accent-ink, #7A5E38);
  text-decoration: none;
  border-bottom: 1px solid transparent;
}
.tp-crumbs .tp-crumb-list a:hover {
  border-bottom-color: currentColor;
}
.tp-crumbs .tp-crumb-list a:focus-visible {
  outline: 2px solid var(--tp-accent-ink, #7A5E38);
  outline-offset: 3px;
  border-radius: 2px;
}
.tp-crumbs .tp-crumb-current {
  color: var(--color-text);
}
/* The hero panel is dark in both themes, so this is not theme-switched. */
.page-hero .tp-prod-hero-brand,
.page-hero .tp-prod-hero-eyebrow {
  color: rgba(255, 255, 255, 0.95);
}
/* The footer column headings are 14px/700 — not "large text" under WCAG, so
   they need 4.5:1 — and were set in Bronze `#8C6E45`. The footer surface is
   `--color-primary`, which is Warm Charcoal `#2A2520` in light but Bronze
   `#8C6E45` in dark, so the heading was 3.20:1 on light and **bronze on bronze,
   1:1, completely invisible** in dark. The colour must therefore be
   theme-aware; a single value cannot serve both surfaces.

   One value now serves both, because standard 10 below replaces the bronze dark
   footer base with an approved charcoal surface:

     light, on `#2A2520`  #BE9E75  6.02:1
     dark,  on `#221E1A`  #BE9E75  6.57:1 */
.site-footer .footer-col-title,
footer .footer-col-title {
  color: #BE9E75;
}

/* ── 10 · DARK-MODE FOOTER SURFACE, BUTTONS AND THE DARK CAPSBAND ──────────
   (a) `style.css` gives the footer `background: var(--color-primary)`. In light
       that is Warm Charcoal `#2A2520`, which is correct: a darker band under a
       cream page. In dark the same token is **Bronze `#8C6E45`**, so the whole
       footer became a bronze base — and no colour in the KR palette reaches
       4.5:1 on it, so *every* text run there failed (headings, copy, links, ABN,
       and any outline button sitting on it). Bronze is the accent, not a base
       surface, so the surface is the defect.

       The dark footer now uses `--color-surface-alt` `#221E1A`, an approved
       dark-palette token that reads as a distinct band against the `#141210`
       page exactly as `#2A2520` does against the cream page. Every footer value
       then clears AA comfortably:

         cream copy   `#E8E4DC` 13.05:1      muted    `#9A9490` 5.53:1
         heading      `#BE9E75`  6.57:1      accent   `#AE8E65` 5.40:1

       Bronze stays the interactive accent on top of it, which is what the brand
       actually specifies. Light mode is untouched, and because both surfaces now
       take the same heading colour, the theme-specific heading override this
       replaces is no longer needed.

       `.hero` and `.section--dark` take `var(--color-primary)` from the same
       stylesheet and so flipped to bronze in dark mode too, which is why a
       transparent outline button sitting on them measured 4.42:1. They are
       corrected with the footer: one token flip, three base surfaces, and bronze
       is no longer a full-bleed base anywhere.

   (b) `.btn-primary` takes `background: var(--color-primary)` too, so in dark it
       became Bronze with `#1A1510` text: 3.82:1 at 14px/600. 14px is never
       “large text” under WCAG, so the 4.5:1 threshold applies and there is no
       waiver — the guide's nominal “Bronze background, dark text” pairing simply
       does not pass at button text sizes. The dark primary button keeps bronze
       as its interactive colour but drops to the darker `#7A5E38` rung with
       cream text, which is 5.62:1 and is already the pairing 160 other dark
       primary buttons on the site use. `.tp-skip` had the identical defect from
       the same token and takes the same pairing.

   (c) `.tp-capsband[data-tp-treatment="B"]` is the near-black `#1E1A16`
       specification panel; treatment `A` is transparent and inherits the light
       page. That attribute is therefore an exact, surface-accurate
       discriminator, which earlier class-only attempts were not: scoping by
       `.tp-capsband` alone regressed the light-page instances of these classes
       from 4.97:1 to 2.80:1 and from 5.62:1 to 2.86:1. Scoped to treatment B,
       `.kr-cap-note` and `.tp-textlink` take the dark-palette values on the dark
       panel in both themes, and every instance outside that panel is left
       exactly as it was.
   ------------------------------------------------------------------------- */
[data-theme='dark'] .site-footer,
[data-theme='dark'] footer.site-footer,
[data-theme='dark'] .hero,
[data-theme='dark'] .section--dark {
  background: var(--color-surface-alt, #221E1A);
}
/* The light Accent button is Bronze `#8C6E45` with `#1A1510` text: 3.82:1 at
   14px/600, so it fails for the same reason the dark primary button did. It
   keeps bronze but drops to the `#7A5E38` rung with cream text, 5.62:1 -- the
   same pairing used above, so both interactive variants now agree. */
.btn-accent,
.btn-accent:hover {
  background: #7A5E38;
  color: #F8F7F4;
}
[data-theme='dark'] .btn-accent,
[data-theme='dark'] .btn-accent:hover {
  background: #9E7E55;
  /* `--color-primary-active`, an approved token, rather than the `#1A1510`
     near-black that appears nowhere in the KR palette. */
  color: #151210;
}
[data-theme='dark'] .btn-primary,
[data-theme='dark'] .btn-primary:hover,
[data-theme='dark'] .tp-skip {
  background: #7A5E38;
  color: #F8F7F4;
}
.tp-capsband[data-tp-treatment="B"] .kr-cap-note {
  color: #9A9490;
}
.tp-capsband[data-tp-treatment="B"] .tp-textlink {
  color: #AC8E65;
}

/* (d) `--color-accent` `#8C6E45` is the base Bronze, and it clears 4.5:1 on NO
       KR surface at label or body sizes: 4.42:1 on the cream page, 3.69:1 on
       Surface, 3.20:1 on Warm Charcoal. Eyebrow labels and inline "Explore"
       affordances used it directly. The accent ramp already provides the right
       rung for each surface, so each is used where it belongs -- the same
       correction already applied to `.tp-doc-link`:

         on light surfaces  `#7A5E38`  5.62:1 on `#F8F7F4`
         on dark surfaces   `#AE8E65`  5.71:1 on `#1C1916`, 4.95:1 on `#2A2520`

       These failures are only visible once scroll-reveal animations are settled;
       the probe now neutralises them before measuring, because elements resting
       at `opacity: 0` were being skipped as invisible and the pages below the
       fold were never actually measured. */
.section-label,
.kr-explore-text {
  color: #7A5E38;
}
.section--dark .section-label,
.section--dark .kr-explore-text,
.hero .section-label,
.hero .kr-explore-text,
.site-footer .section-label,
.site-footer .kr-explore-text,
[data-theme='dark'] .section-label,
[data-theme='dark'] .kr-explore-text {
  color: #AE8E65;
}
/* Per-page layers set these labels with a higher specificity than a two-class
   selector can reach, so the dark-surface rung is pinned. Narrow and explicit
   rather than a blanket important. */
section.section--dark .section-label,
section.section--dark .kr-explore-text {
  color: #AE8E65 !important;
}
/* Correcting standard 10(a) for its own consequence: `.section--dark` headings
   were `#141210` -- dark ink chosen for the BRONZE base that (a) removed. On the
   charcoal surface that is 1.13:1, so the heading takes the cream text token.
   Caught only because the probe now settles scroll-reveal animations. */
[data-theme='dark'] section.section--dark .section-title,
[data-theme='dark'] section.section--dark .section-subtitle,
[data-theme='dark'] section.section--dark p {
  color: var(--color-text, #E8E4DC);
}

/* ── Standard 12 · legibility of tile content over uncontrolled photography ─
   The Solutions tiles place a bronze "Explore" affordance, its chevron and a
   cream title directly over editorial photography. `.solution-tile-overlay`
   scrims only `rgba(14,21,32,.55)` at the very bottom, fading out by 60%, which
   is not enough on a bright frame: measured against the ACTUAL painted pixels
   behind each glyph, the three tiles ran 1.95:1 / 2.48:1 / 2.50:1 in light and
   2.17:1 / 2.96:1 / 2.99:1 in dark -- all under the 3:1 that WCAG 1.4.11 asks of
   a graphical control, and the O&I tile's paving frame is visibly marginal.

   The scrim is deepened and extended rather than recolouring the accent, because
   the title and label sit on the same backdrop and have the same problem; one
   controlled backdrop fixes the whole tile foot. Brand colours are untouched --
   bronze remains the interactive accent, the photograph remains the hero. */
.solution-tile .solution-tile-overlay,
.mfr-tile .solution-tile-overlay {
  background: linear-gradient(to top,
              rgba(20, 18, 16, 0.88) 0%,
              rgba(20, 18, 16, 0.72) 22%,
              rgba(20, 18, 16, 0.34) 48%,
              rgba(20, 18, 16, 0) 78%);
}
/* The scrim alone only reached 2.34-3.00:1, because base Bronze `#8C6E45` is a
   mid-luminance hue: it is low-contrast against a bright photo AND against a
   deep scrim. The tile foot therefore takes the lightest approved accent rung,
   `#BE9E75` (the dark-mode accent-active token), which keeps the bronze identity
   of the affordance while clearing 3:1 against the controlled backdrop. The
   glyph and its label are set together so they stay optically matched. */
.solution-tile .solution-tile-explore,
.mfr-tile .solution-tile-explore,
.solution-tile .solution-tile-arrow,
.mfr-tile .solution-tile-arrow {
  color: #BE9E75;
}
.solution-tile .solution-tile-arrow,
.mfr-tile .solution-tile-arrow {
  border-top-color: rgba(190, 158, 117, 0.55);
}

/* ── Standard 13 · the stats bar must not force horizontal scroll at 320px ──
   `.stats-grid` is a fixed `repeat(4, 1fr)` with a `--space-8` gap at every
   width. At 320px that is 4 columns plus 3 gaps inside a 288px content box, and
   the long stat labels pushed the document to 323px -- a 3px horizontal scroll
   on the narrowest supported viewport. Two columns below 400px removes the
   overflow and keeps the numerals legible instead of shrinking them. */
@media (max-width: 400px) {
  .stats-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: var(--space-6);
  }
}

/* ── Standard 11 · minimum target size for chevron text actions ────────────
   WCAG 2.5.8 (AA) requires a 24x24 CSS px target; WCAG 2.5.5 (AAA) asks 44x44.
   Discrete controls -- link-box CTAs, buttons, tile actions -- already measure
   >=44px and are untouched here. The `.tp-textlink` family measured 19-27px, so
   the ones under 24px failed even the AA floor. Vertical padding lifts every
   such link to the 24px AA minimum without moving surrounding copy, because the
   padding is absorbed by the existing line box.

   These are text links, not buttons: WCAG 2.5.8 explicitly excepts a target in
   a sentence, so 44px is not imposed on inline prose links -- doing so would
   force 44px line boxes through running text. The AA floor is met everywhere and
   the exception is relied on only where it genuinely applies. */
.tp-textlink {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  min-height: 24px;
  padding-block: 2px;
}
/* A back-navigation link is a discrete control, not prose: it gets the full
   44px AAA target. */
.tp-textlink[data-kr-chev-back] {
  min-height: 44px;
}


/* NOT overridden here, deliberately. `.kr-cap-note` (3.24:1) and `.tp-textlink`
   (2.87:1) fail on the near-black `#1E1A16` capsband panel, but both classes also
   render on the light page background where the light tokens are correct, and
   `.tp-capsband` is NOT uniformly dark across pages. Every container-level
   override tested regressed a light instance -- `.kr-cap-note` from 4.97:1 to
   2.80:1, `.tp-textlink` from 5.62:1 to 2.86:1 -- which is worse than the defect
   it removed. Both are recorded as residual advisories: the durable remedy is to
   give the dark capsband variant its own surface-scoped token set, so the colour
   follows the surface instead of being guessed from a class name. */


/* ── 12 · HOMEPAGE ABOUT GALLERY (three photographs, two rows) ──────────────
   GENERATED by kr_homepage_media.css_block(). See that module for why each
   rule exists. Slot focal points, orientations and frame ratios all come from
   home-media/manifest.json, so the layout follows the photographs.

   Order and occupancy, top to bottom:
     1 anchor    — glass-bridge-stair (portrait, 0.667)
                   top row, left — frame 0.5667
     2 companion — waterfront-residence-interior (landscape, 1.500)
                   top row, right — frame 1.2750
     3 feature   — commercial-glazed-facade-dusk (landscape, 1.714)
                   full content width — frame 2.0168

   One governing rule: every frame keeps 85% of the dimension it crops.
   The top row's columns are therefore split 30.77% / 69.23% (the ratio of the
   two photographs' own aspect ratios) and the feature's frame is its own
   1.7143 divided by that retention — a 2.017:1 cinematic band, so the
   photograph's full width reaches the screen and only its height is trimmed. */
.benchmarks-mosaic picture,
.benchmarks-feature picture {
  display: block;
  width: 100%;
  height: 100%;
}
.benchmarks-mosaic-left img { object-position: 45% 45%; }
.benchmarks-mosaic-top img { object-position: 58% center; }

/* Top row. No fixed height: the anchor's ratio sets the row, the companion
   stretches to match it, and the gap is taken out of the split so the two
   frames keep the same share of their own width at every width. */
.benchmarks-mosaic {
  grid-template-columns: calc((100% - var(--space-2)) * 0.3077) 1fr;
  align-items: stretch;
  height: auto;
}
.benchmarks-mosaic-left {
  height: auto;
  aspect-ratio: 0.5667;
}
.benchmarks-mosaic-top {
  flex: none;
  height: 100%;
  border-radius: 0 var(--radius-lg) var(--radius-lg) 0;
}

/* The feature. A sibling of the two-column About row, so `width: 100%` here is
   the section's content width. */
.benchmarks-feature {
  margin-top: var(--space-8);
  width: 100%;
  aspect-ratio: 2.0168;
  overflow: hidden;
  border-radius: var(--radius-lg);
}
.benchmarks-feature img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 42% 30%;
  display: block;
  transition: transform 800ms cubic-bezier(0.16, 1, 0.3, 1);
}
.benchmarks-feature:hover img { transform: scale(1.04); }

@media (max-width: 768px) {
  /* One image per row, each row shaped to its own photograph. No side-by-side
     slivers, no horizontal scroll, no desktop crop leaking onto a phone. */
  .benchmarks-mosaic {
    display: grid;
    grid-template-columns: 1fr;
    gap: var(--space-2);
    height: auto;
    overflow: visible;
    border-radius: 0;
  }
  .benchmarks-mosaic-left {
    height: auto;
    aspect-ratio: 3 / 4;
    border-radius: var(--radius-lg);
  }
  .benchmarks-mosaic-top {
    height: auto;
    aspect-ratio: 3 / 2;
    border-radius: var(--radius-lg);
  }
  .benchmarks-feature {
    margin-top: var(--space-2);
    aspect-ratio: 12 / 7;
    border-radius: var(--radius-lg);
  }
}


/* ── 13 · WINDOWS & DOORS HERO FOCAL POINT ─────────────────────────────────
   GENERATED by kr_site_restructure.hero_css_block(). Was `center bottom`,
   which showed source rows 480-1080 of the 1080-row frame — the furniture and
   floor half. `center 20%` shows rows 96-696, giving the upper half of the
   frame priority so the mullions, door heads, soffit and glazed corners survive
   the crop. See the module for the frames this was measured against.

   Applies to the video, its poster and the inner <img> still alike: one crop for
   the moving and the frozen state means no reflow between them, including under
   prefers-reduced-motion. Below 768px the cover crop is horizontal (the full
   frame height is already on screen), so this value is inert there by
   construction and needs no breakpoint override. */
#page-windows-doors .page-hero-bg img,
#page-windows-doors .page-hero-bg video {
  object-position: center 20%;
}


/* ── 14 · NARROW-PHONE SPLIT COLLAPSE ──────────────────────────────────────
   GENERATED by kr_site_restructure.narrow_split_css_block(). Inline-styled
   `grid-template-columns:1fr 1fr` editorial splits overflow a 700 px
   viewport by ~54px; below the breakpoint they stack instead. `!important` is
   required because the competing declaration is an inline style attribute. */
@media (max-width: 699px) {
  .container > [style*="grid-template-columns:1fr 1fr"] {
    grid-template-columns: 1fr !important;
    gap: var(--space-8) !important;
  }
}
