/* =========================================================================
   ZOLA WINDOWS — Design System
   One stylesheet for the whole site. Brand-critical values are CSS variables
   at the top, so they can be changed in one place.
   Breakpoints used throughout: 1080 / 991 / 767 / 479.
   ========================================================================= */

/* Real brand fonts (Myriad Pro Black + Futura), replacing the Aboreto/
   Urbanist Google Fonts placeholder used earlier in the rebuild. Self-hosted
   from assets/fonts/ -- real licensed files from Zola's own Dropbox (used
   across their print/marketing collateral), not hotlinked or pulled from a
   free-font site. */
@font-face { font-family: "Myriad Pro Black"; src: url("../fonts/MyriadProBlack.otf") format("opentype"); font-weight: 900; font-style: normal; font-display: swap; }
@font-face { font-family: "Futura"; src: url("../fonts/FuturaStdLight.otf") format("opentype"); font-weight: 300; font-style: normal; font-display: swap; }
@font-face { font-family: "Futura Book"; src: url("../fonts/FuturaStdBook.otf") format("opentype"); font-weight: 400; font-style: normal; font-display: swap; }
@font-face { font-family: "Futura"; src: url("../fonts/FuturaStdBold.otf") format("opentype"); font-weight: 700; font-style: normal; font-display: swap; }

:root {
  /* ---- Brand color ----
     --blue is the single Zola brand blue. Change this one value to retune
     the whole site. Replace with the exact hex from the Zola brand guide. */
  --blue:        #003d76;
  --blue-700:    #00305e;
  --blue-900:    #001e3a;
  --blue-050:    #e7eef6;

  /* ---- Ink & neutrals ---- */
  --ink:         #16191d;   /* headings + primary text */
  --body:        #3c4248;   /* body copy */
  --muted:       #6c747d;   /* secondary / captions */
  --line:        #e4e7ea;   /* hairline borders */
  --line-strong: #cfd4d9;
  --paper:       #ffffff;
  --paper-alt:   #f5f6f8;   /* alternating section background */
  --paper-warm:  #f3f1ec;   /* occasional warm panel */
  --ink-soft:    #0b1d33;   /* deep navy section bg */
  --silver:      #929496;   /* brand silver */
  --dark-grey:   #4e4e50;   /* brand dark grey */

  /* ---- Type ----
     Heading = Myriad Pro Black (only one real weight, 900 -- every
     heading renders at that weight regardless of font-weight requested).
     Body/UI = Futura; "Futura Book" is the real body-copy weight (its own
     family name, matching how live registers it), "Futura" carries the
     real Light(300)/Bold(700) faces for UI text that asks for those
     weights explicitly. */
  --display: "Futura", "Helvetica Neue", Arial, sans-serif;
  --body-font: "Futura Book", "Helvetica Neue", Arial, sans-serif;
  --heading: "Myriad Pro Black", "Helvetica Neue", Arial, sans-serif;

  /* ---- Layout ---- */
  --maxw: 1240px;
  --maxw-wide: 1600px;
  --maxw-narrow: 880px;
  --gutter: clamp(20px, 5vw, 64px);
  --header-h: 94px;

  /* ---- Radius & shadow ---- */
  --r-sm: 4px;
  --r:    8px;
  --r-lg: 16px;
  --shadow-sm: 0 1px 2px rgba(16,25,33,.06), 0 2px 8px rgba(16,25,33,.05);
  --shadow:    0 6px 24px rgba(16,25,33,.10);
  --shadow-lg: 0 24px 60px rgba(16,25,33,.18);

  /* ---- Motion ---- */
  --ease: cubic-bezier(.22,.61,.36,1);
}

/* Fix typo'd token fallback above without breaking older refs */
:root { --blue-600: var(--blue); }

/* -------------------------------------------------------------------------
   Reset / base
   ------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after { animation-duration: .001ms !important; transition-duration: .001ms !important; }
}

body {
  font-family: var(--body-font);
  color: var(--body);
  background: var(--paper);
  line-height: 1.65;
  font-size: 17px;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

img, svg, video { display: block; max-width: 100%; height: auto; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; border: none; background: none; color: inherit; }
ul { list-style: none; padding: 0; }

:focus-visible {
  outline: 3px solid var(--blue);
  outline-offset: 2px;
  border-radius: 2px;
}

.skip-link {
  position: absolute; left: 12px; top: -60px; z-index: 2000;
  background: var(--blue); color: #fff; padding: 10px 16px; border-radius: var(--r-sm);
  transition: top .2s var(--ease);
}
.skip-link:focus { top: 12px; }

/* -------------------------------------------------------------------------
   Typography
   ------------------------------------------------------------------------- */
h1, h2, h3, h4, h5 { font-family: var(--heading); color: var(--ink); line-height: 1.14; font-weight: 400; letter-spacing: 0; }

h1 { font-size: clamp(2.2rem, 5.4vw, 4.2rem); font-weight: 400; }
/* font-size/weight match live's real computed values (checked at
   479/991/1440px, same fixed-px-per-breakpoint pattern as the hero
   headings -- live doesn't use a continuous clamp() here either). */
h2 { font-size: 32px; font-weight: 900; }
@media (max-width: 991px) { h2 { font-size: 30px; } }
@media (max-width: 479px) { h2 { font-size: 24px; } }
h3 { font-size: clamp(1.3rem, 2.2vw, 1.8rem); }
h4 { font-size: 1.18rem; }
p  { margin: 0 0 1.1em; }
p:last-child { margin-bottom: 0; }

/* Matches live's real body-copy size (16px/25px), not a distinct larger
   "lead" emphasis size -- live doesn't visually distinguish lead paragraphs
   from regular body text by size. */
.lead { font-size: 16px; color: var(--body); line-height: 1.5625; }
@media (max-width: 479px) { .lead { font-size: 14.88px; line-height: 1.5; } }

/* Matches live's real eyebrow style: title case (not uppercase/small-caps),
   light weight, black, 18px -- confirmed on clad-wood.html's "Best Of Both
   Worlds" section and again independently on the homepage pull-quote area,
   so applied as the base style rather than a one-off. This is a shared
   class used on 24 pages. */
.eyebrow {
  font-family: var(--display);
  text-transform: none;
  letter-spacing: normal;
  font-size: 18px;
  font-weight: 300;
  color: #333;
  display: inline-block;
  margin-bottom: 1rem;
}
.eyebrow--muted { color: var(--muted); }
.eyebrow--light { color: rgba(255,255,255,.8); }

.text-center { text-align: center; }
.measure { max-width: 62ch; }
.measure-center { max-width: 64ch; margin-left: auto; margin-right: auto; }

/* -------------------------------------------------------------------------
   Layout primitives
   ------------------------------------------------------------------------- */
.container { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: var(--gutter); }
.container--narrow { max-width: var(--maxw-narrow); }
.container--wide { max-width: var(--maxw-wide); }
/* Matches live's real container-large (1440px cap, flat 24px gutter) --
   scoped to clad-wood.html only for now, not applied sitewide yet. */
.container--live { max-width: 1440px; padding-inline: 24px; }

.section { padding-block: clamp(56px, 8vw, 120px); }
.section--tight { padding-block: clamp(40px, 5vw, 72px); }
/* For a section that opens with a full-bleed image instead of its usual
   top padding — the image should touch the section's top edge. */
.section--flush-top { padding-top: 0; }
/* For a pair of sections meant to sit close together (e.g. tilt-turn's
   "How It Works" video into the gallery right below it) -- live's real
   gap there is ~62px total, not the usual ~200px two stacked section
   paddings would give. */
.section--flush-bottom { padding-bottom: 0; }
.section--tight-top { padding-top: clamp(40px, 5vw, 72px); }
/* Same idea, but matching live's real flat (non-responsive) 56px gap
   into breezepanel's gallery. */
.section--tight-top-sm { padding-top: 56px; }
.section--alt { background: var(--paper-alt); }
.section--blue { background: var(--blue); color: #fff; }
.section--ink { background: var(--ink-soft); color: #fff; }
.section--blue h1, .section--blue h2, .section--blue h3,
.section--ink h1, .section--ink h2, .section--ink h3 { color: #fff; }
.section--ink p, .section--ink .lead { color: rgba(255,255,255,.82); }
.section--ink .link-arrow { color: #fff; }

/* An image meant to run edge to edge, ignoring the section's own container. */
.full-bleed { display: block; width: 100%; }

/* A narrow heading beside a wider block of supporting text/links. */
.text-split { display: grid; grid-template-columns: 1fr 1.6fr; gap: clamp(24px, 4vw, 56px); align-items: start; }
@media (max-width: 767px) { .text-split { grid-template-columns: 1fr; } }

.section-head { max-width: 760px; margin-bottom: clamp(32px, 4vw, 56px); }
.section-head.center { margin-inline: auto; text-align: center; }
.section-head > .btn { margin-top: 1.2rem; }
/* Matches live: this section's heading/lead run the full section width,
   left-aligned, instead of the usual 760px readable-measure cap -- scoped
   to clad-wood.html for now, same as .container--live above. */
.section-head--live { max-width: none; }
/* firewall.html's intro uses an h1 here instead of the usual page-hero
   h1 -- matches live's real size for this specific instance (38px/44px),
   much smaller than the global clamp() h1 meant for a full hero headline. */
.section-head--live h1 { font-size: 38px; line-height: 44px; }

.grid { display: grid; gap: clamp(20px, 3vw, 40px); }
.grid-2 { grid-template-columns: repeat(2, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
/* A bare <img> dropped directly into .grid (no .card wrapper) won't
   stretch to fill its column on its own -- replaced elements keep their
   intrinsic size under grid's default stretch. */
.grid > img { width: 100%; }

/* Glass-overview: live shows one large flat comparison image on desktop
   (cropped to a fixed-height band, matching live's own object-fit:cover
   treatment) and swaps to 4 separate square images stacked in a grid on
   mobile, where the wide image would be illegibly small. */
.glass-overview__wide { width: 100%; height: auto; }
.glass-overview__mobile { display: none; }
@media (max-width: 767px) {
  .glass-overview__wide { display: none; }
  .glass-overview__mobile { display: grid; }
}

/* Split media + text */
.split {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: clamp(28px, 5vw, 72px);
  align-items: center;
}
.split--wide-media { grid-template-columns: 1.2fr 1fr; }
/* Narrow text aside beside a video. Matches live's real edge-to-edge
   layout for this section: no .container, the aside gets 25px of left
   padding and the video area gets 50px on each side instead of a
   shared centered gutter. */
.split--video-aside { grid-template-columns: 320px 1fr; align-items: start; gap: 0; }
.split--video-aside .split__body { padding-left: 25px; }
.split--video-aside .split__media { padding-inline: 50px; }
.split--video-aside .split__body h3 { font-weight: 700; }
/* Matches live's real "Get a quote" intro on request-a-quote.html: a
   noticeably larger heading (46px, vs the usual 32px split__body h2) and
   Futura Light body copy at #333 instead of the default --body-font/
   --body token pair. Scoped to this page -- not a general split__body
   convention (checked against clad-wood's equivalent intro, which is a
   plain 32px h2). */
.split--quote .split__body h2 { font-size: 46px; }
.split--quote .split__body p { font-family: var(--display); font-weight: 300; color: #333; }
/* Matches live's real "View our Idea Book" intro: stays at the usual 32px
   split__body h2 size, but both heading and paragraph are set in Futura
   (var(--display)) at #333 -- not the default Myriad-900/--ink heading or
   the default --body-font/--body paragraph pair. Also equal 1fr/1fr
   columns, not the 1.2fr/1fr .split--wide-media ratio (live's text and
   image columns measured equal at 480px/480px in a 1440px container). */
.split--book .split__body h2 { font-family: var(--display); font-weight: 700; color: #333; }
.split--book .split__body p { font-family: var(--display); font-weight: 300; color: #333; }
@media (max-width: 991px) {
  .split--video-aside { grid-template-columns: 1fr; padding-inline: var(--gutter); gap: 32px; }
  .split--video-aside .split__body, .split--video-aside .split__media { padding: 0; }
}
/* photo-heavy sections give the image column 3x the text column's width
   and stretch the image to match the text column's height via
   object-fit:cover (matching live, and Ellie's explicit request that the
   photo span the full height of the text block). Our type scale still
   runs bigger than live's for this component, so the text column -- and
   therefore the photo -- ends up taller relative to its width than the
   photo's own natural aspect ratio, meaning some real cropping is
   inherent to this approach; .container--wide (see above) keeps that
   from being as severe as it would be in the narrower default container. */
.split--photo-heavy { grid-template-columns: 1fr 3fr; align-items: stretch; }
/* .reverse on this modifier also mirrors which column is wide, so the
   wide side can sit on the left with the narrow side on the right --
   .reverse alone only reorders which element goes where, not the
   track sizes, so a photo-first layout needs the ratio flipped too. */
.split--photo-heavy.reverse { grid-template-columns: 3fr 1fr; }
@media (max-width: 991px) { .split--photo-heavy, .split--photo-heavy.reverse { grid-template-columns: 1fr; } }
.split__media img { width: 100%; border-radius: var(--r); box-shadow: var(--shadow); }
.split--photo-heavy .split__media { height: 100%; }
.split--photo-heavy .split__media img { height: 100%; object-fit: cover; border-radius: 0; box-shadow: none; }
@media (max-width: 991px) { .split--photo-heavy .split__media { height: auto; } .split--photo-heavy .split__media img { height: auto; } }
/* Opt-out of the full-height crop above: some photo-heavy splits (e.g. the
   historic page's real live layout) keep the photo at its own natural
   aspect ratio, centered in a column that still matches the text's height,
   instead of cropping to fill it edge to edge. */
.split--photo-heavy .split__media--natural { display: flex; align-items: center; justify-content: center; }
.split--photo-heavy .split__media--natural img { height: auto; width: 100%; object-fit: contain; }
.split__media--flat img { border-radius: 0; box-shadow: none; }
.split__body > .btn { margin-top: 1.6rem; }
/* .split's DOM order already alternates text/photo per section (see index.html);
   .reverse marks a section whose media comes first in the DOM. Pushing the
   body to order:2 keeps that DOM order on screen instead of re-flattening it
   back to text-left every time. */
.split.reverse .split__body { order: 2; }

/* .split__body--live: match live's own type for this block rather than the
   house defaults. On shipping-delivery.html live renders the heading as its
   .section-title (Myriad Pro 46/44, regular, #333, 10px below) and the body
   copy as FuturaStd Light 16/25 #333 -- a bigger, lighter heading and lighter
   body than our site defaults. Values measured off live at 1280/960/390.
   Scoped to this modifier so the other 17 .split__body pages are unaffected.
   On the heading's weight: live's CSS asks for font-weight 400, but the only
   face it registers under that family is the Black otf, so live renders Black
   too -- the same file we ship at assets/fonts/MyriadProBlack.otf. Verified by
   measuring the glyph run "We'll do the rest!" at 46px on both: 347.72px
   either side. So this heading already matches live exactly; no lighter
   Myriad Pro file is needed. */
.prose.split__body--live h2 { font-size: 46px; line-height: 44px; color: #333; margin-top: 0; margin-bottom: 10px; }
.prose.split__body--live p { font-family: var(--display); font-weight: 300; font-size: 16px; line-height: 25px; color: #333; }
.split__body--live strong { font-weight: 700; }
@media (max-width: 991px) { .prose.split__body--live h2 { font-size: 31px; line-height: 30px; } }
@media (max-width: 479px) {
  .prose.split__body--live h2 { font-size: 25px; line-height: 26px; }
  .prose.split__body--live p { font-size: 14.88px; line-height: 22.32px; }
}

/* .split--live: live stretches this photo to the height of the text column and
   crops it with background-size:cover anchored right-centre, so the bay window
   and sofa show rather than the fireplace. Our markup uses a real <img> (for the
   alt text), so the same crop is object-fit:cover + object-position:100% 50%.
   Below 768 live stacks the columns and gives the photo a fixed height instead
   of stretching. Heights measured off live at 1280/960/767/480/390:
   >=768 stretches to the row, <=767 is 350px, <=479 is 140px; radius 4px. */
.split--live { align-items: stretch; }
.split--live .split__media img {
  display: block; width: 100%; height: 100%;
  object-fit: cover; object-position: 100% 50%;
  border-radius: 4px; box-shadow: none;
}
@media (max-width: 767px) { .split--live .split__media img { height: 350px; } }
@media (max-width: 479px) { .split--live .split__media img { height: 140px; } }

/* -------------------------------------------------------------------------
   Buttons
   ------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: .55em;
  font-family: var(--display);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .09em;
  font-size: .82rem;
  padding: 14px 26px;
  border-radius: 2px;
  transition: transform .25s var(--ease), background-color .25s var(--ease), color .25s var(--ease), box-shadow .25s var(--ease);
  white-space: nowrap;
}
.btn svg { width: 1em; height: 1em; }
/* Body buttons are uniform: live's .button-1 dark -- transparent on a 1px
   #333 border, 14px/700, 9px 30px, square. .btn--primary keeps its name (87
   pages use it) but now renders this outline; .btn--ghost is the same spec.
   Ellie chose white over blue for every body button, 2026-09-03. */
.btn--primary, .btn--ghost { background: transparent; color: #333; border: 1px solid #333; border-radius: 0; padding: 9px 30px; font-size: 14px; letter-spacing: .06em; }
.btn--primary:hover, .btn--ghost:hover { background: #333; color: #fff; }
/* Live's real "outline" button (Request Quote, View Our Idea Book, etc.) is
   much tighter than our earlier guess: square corners, a slim #333 border,
   and a 10px label -- confirmed identical across the homepage hero, the
   glass-doors Highlights CTA, and the homepage Idea Book link. */

.btn--light { background: #fff; color: var(--ink); }
.btn--light:hover { transform: translateY(-2px); box-shadow: var(--shadow); }
.btn--on-blue { background: #fff; color: var(--blue); }
.btn--on-blue:hover { transform: translateY(-2px); box-shadow: var(--shadow-lg); }
/* Light variant of the same live button (white border/text), used on dark
   or photo backgrounds where the dark #333 outline wouldn't show up. */
.btn--outline-light { background: transparent; color: #fff; border: 1px solid #fff; border-radius: 0; padding: 9px 14px; font-size: 10px; letter-spacing: .1em; }
.btn--outline-light:hover { background: #fff; color: #333; }
.btn--lg { padding: 17px 34px; font-size: .9rem; }
.btn--block { display: flex; width: 100%; justify-content: center; }

.link-arrow {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  letter-spacing: .08em; font-size: .8rem; color: var(--blue);
  display: inline-flex; align-items: center; gap: .4em;
}
.link-arrow::after { content: "→"; transition: transform .25s var(--ease); }
.link-arrow:hover::after { transform: translateX(4px); }

/* =========================================================================
   HEADER / NAVIGATION
   ========================================================================= */
.site-header {
  position: sticky; top: 0; z-index: 1000;
  background: rgba(255,255,255,.94);
  backdrop-filter: saturate(140%) blur(10px);
  border-bottom: 1px solid var(--line);
}
/* gap and .nav__link's horizontal padding (below) are trimmed from their
   original values -- at the old spacing, the 8 flat nav links plus the
   always-visible hamburger didn't actually fit in the ~75px just above the
   1080px collapse breakpoint, pushing the hamburger off-screen (invisible,
   unclickable) in that band. */
.nav {
  display: flex; align-items: center; gap: 20px;
  height: var(--header-h);
  padding-inline: clamp(16px, 4vw, 40px);
}
/* The logomark overhangs the header and bleeds onto the hero, matching the
   legacy site. Deliberately no background/border/shadow here — the mark's own
   square is the brand shape; wrapping it in a card reads as a floating tab. */
.nav__logo {
  flex: 0 0 auto; align-self: flex-start;
  height: calc(var(--header-h) + 36px);
  display: flex; align-items: center;
  position: relative; z-index: 2;
}
/* Legibility over dark heroes comes from the white bleed baked into the asset,
   not from a filter here. */
.nav__logo img { height: 120px; width: auto; display: block; }

.nav__menu { display: flex; align-items: stretch; gap: 4px; margin-right: 8px; height: 100%; }
.nav__spacer { flex: 1 1 auto; }
/* flex-shrink:0 -- without it, .nav__actions (and the always-visible
   hamburger inside it) got squeezed toward 0 width in the band just above
   the 1080px collapse breakpoint, where the full nav__menu barely fits.
   The menu itself can shrink; the toggle button never should. */
.nav__actions { display: flex; align-items: center; gap: 14px; flex-shrink: 0; }
.nav__phone {
  font-family: var(--display); font-weight: 700; font-size: .85rem;
  color: var(--ink); letter-spacing: .02em; display: none;
}
@media (min-width: 1180px){ .nav__phone { display: inline-flex; } }

.nav__item { position: relative; display: flex; align-items: center; }
.nav__link {
  font-family: var(--body-font);
  font-weight: 400;
  font-size: 15px;
  text-transform: uppercase;
  letter-spacing: normal;
  color: #000;
  padding: 10px 8px;
  display: inline-flex; align-items: center; gap: .35em;
  white-space: nowrap;
  border-radius: var(--r-sm);
  transition: color .2s var(--ease);
}
.nav__link:hover, .nav__item:hover .nav__link { color: var(--blue); }
.nav__link .chev { width: 9px; height: 9px; transition: transform .25s var(--ease); }
.nav__item:hover .nav__link .chev, .nav__item.open .nav__link .chev { transform: rotate(180deg); }

/* Mega dropdown */
.mega {
  position: absolute; top: 100%; left: 0;
  margin-top: 10px;
  background: #fff; border: 1px solid var(--line);
  border-radius: var(--r); box-shadow: var(--shadow-lg);
  padding: 22px; min-width: 280px;
  opacity: 0; visibility: hidden; transform: translateY(6px); pointer-events: none;
  transition: opacity .2s var(--ease), transform .2s var(--ease), visibility .2s;
  z-index: 1001;
}
/* transparent bridge so moving the cursor from the link to the panel never crosses a dead gap */
.mega::before { content: ""; position: absolute; left: 0; right: 0; top: -18px; height: 18px; }
.nav__item:hover .mega, .nav__item:focus-within .mega, .nav__item.open .mega { opacity: 1; visibility: visible; transform: translateY(0); pointer-events: auto; }
.mega--wide { min-width: 620px; }
.mega--wide.mega--center, .mega--center { left: 0; right: auto; }

.mega__grid { display: grid; gap: 4px 28px; }
.mega--wide .mega__grid { grid-template-columns: 1fr 1fr; }
.mega__col-title {
  font-family: var(--display); text-transform: uppercase; letter-spacing: .14em;
  font-size: .68rem; color: var(--muted); font-weight: 700;
  padding: 6px 10px; margin-top: 6px;
}
.mega a {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 10px; border-radius: var(--r-sm);
  font-size: .93rem; color: var(--body); font-weight: 500;
  transition: background .18s var(--ease), color .18s var(--ease);
}
.mega a:hover { background: var(--blue-050); color: var(--blue); }
.mega a .ico { width: 26px; height: 26px; flex: 0 0 auto; opacity: .8; }
.mega a:hover .ico { opacity: 1; }

/* Hamburger — always visible, alongside the flat nav links, matching the live site */
.nav__toggle { display: flex; width: 44px; height: 44px; align-items: center; justify-content: center; }
.nav__toggle span, .nav__toggle span::before, .nav__toggle span::after {
  content: ""; display: block; width: 24px; height: 2px; background: var(--ink);
  transition: transform .3s var(--ease), opacity .2s var(--ease);
}
.nav__toggle span::before { transform: translateY(-7px); }
.nav__toggle span::after  { transform: translateY(5px); }
body.menu-open .nav__toggle span { background: transparent; }
body.menu-open .nav__toggle span::before { transform: rotate(45deg); }
body.menu-open .nav__toggle span::after  { transform: rotate(-45deg) translateY(-1px); }

/* Mobile drawer -- a right-aligned panel, not full-width, matching live.
   Live's real width is a viewport-relative percentage that steps at the
   same breakpoints as the rest of the site: 25vw above 1080px, 50vw down
   to 480px, 65vw below that (narrow phones need the wider share just to
   fit the nav labels). */
.mobile-drawer {
  position: fixed; top: var(--header-h); right: 0; bottom: 0; left: auto; z-index: 999;
  width: 25vw; max-width: 400px;
  background: #fff; overflow-y: auto;
  border-left: 1px solid var(--line-strong);
  padding: 8px 0 40px;
  display: none;
}
/* Dim the page behind the open drawer. Live's .navigation-underlay is a
   full-viewport black at 80% (a #000c gradient, animated 0 -> 1); ours fades
   the same value. z-index sits below the drawer (999) and the header (1000),
   so the drawer and the close toggle stay clear of the dim -- live darkens
   its navbar too, but that would put our close button behind the overlay. */
.nav-underlay {
  position: fixed; inset: 0; z-index: 900;
  background: rgba(0,0,0,.8);
  opacity: 0; pointer-events: none;
  transition: opacity .3s var(--ease);
}
body.menu-open .nav-underlay { opacity: 1; pointer-events: auto; }
body.menu-open .mobile-drawer { display: block; }
/* The header's main nav disappears below 1080px and was not repeated in the
   drawer, so on a phone Clad Wood, Aluminum, uPVC, Historic, Firewall, Why
   Zola, Resources and Portfolio had no route at all. Live leads its drawer
   with the same primary destinations before the accordion groups. Rows match
   .m-acc__head's metrics so the list reads as one continuous menu. */
.m-nav a {
  display: block; padding: 20px 25px; border-bottom: 1px solid var(--line);
  font-family: var(--body-font); font-weight: 400; font-size: 18px; color: #333;
}
.m-nav a:hover { color: var(--blue); }
/* Only needed while the header's own nav is hidden. Above 1080px .nav__menu
   is on screen, so repeating the same eight links in the drawer just
   duplicates what the visitor can already see. */
@media (min-width: 1081px) { .m-nav { display: none; } }
.m-acc { border-bottom: 1px solid var(--line); }
/* Padding below is a fixed 20px/25px pair, not var(--gutter) -- live's real
   drawer rows use a small fixed inset, not the page-section gutter clamp
   (which grows to 64px+ at wide viewports and made the drawer look
   oversized). Confirmed against live at a 1920px viewport. */
.m-acc__head {
  width: 100%; display: flex; justify-content: space-between; align-items: center;
  padding: 20px 25px; font-family: var(--body-font); font-weight: 400;
  text-transform: none; letter-spacing: normal; font-size: 18px; color: #333;
}
.m-acc__head .chev { width: 12px; transition: transform .25s var(--ease); }
.m-acc.open .m-acc__head .chev { transform: rotate(180deg); }
/* Matches live's real expanded-accordion background -- a specific blue
   (#1269b0, already used elsewhere as .config-highlights__tab--overview's
   background), not the site's main --blue token (#003d76) or a plain
   gray fill. */
.m-acc__panel { max-height: 0; overflow: hidden; transition: max-height .3s var(--ease); background: #1269b0; }
.m-acc.open .m-acc__panel { max-height: 720px; }
.m-acc__panel a { display: block; padding: 10px 0 10px 40px; font-size: 14px; color: #fff; }
.m-acc__panel a:hover { color: rgba(255,255,255,.75); }

/* Content FAQ / accordion (used on knowledge & info pages) */
.faq { border-top: 1px solid var(--line); }
.faq__item { border-bottom: 1px solid var(--line); }
.faq__head {
  width: 100%; display: flex; justify-content: space-between; align-items: center; gap: 20px;
  padding: 20px 4px; font-family: var(--display); font-weight: 700; font-size: 1.05rem;
  color: var(--ink); text-align: left; cursor: pointer; background: none;
}
.faq__head:hover { color: var(--blue); }
.faq__head .chev { width: 13px; flex: 0 0 13px; transition: transform .25s var(--ease); color: var(--blue); }
.faq__item.open .faq__head .chev { transform: rotate(180deg); }
.faq__panel { max-height: 0; overflow: hidden; transition: max-height .3s var(--ease); }
.faq__item.open .faq__panel { max-height: 520px; }
.faq__panel p { padding: 0 4px 22px; color: var(--body); max-width: 70ch; }
.m-link { display: block; padding: 20px 25px; font-family: var(--body-font); font-weight: 400; text-transform: none; letter-spacing: normal; font-size: 18px; color: #333; border-bottom: 1px solid var(--line); }
.mobile-drawer .btn { margin: 24px var(--gutter) 0; }
/* Logo + award badge at the foot of the drawer, matching live's real
   spacing (measured at a 1280px viewport: ~38px above the logo, ~26px
   between logo and badge, both centered, fixed pixel widths). */
.drawer-foot { margin-top: 38px; text-align: center; padding-inline: var(--gutter); }
/* The images compute display:block, which text-align:center cannot centre;
   auto inline margins centre them at any drawer width. */
.drawer-foot img { width: 230px; max-width: 100%; margin-inline: auto; }
.drawer-foot__badge { width: 210px; margin-top: 26px; }

/* =========================================================================
   HERO
   ========================================================================= */
.hero {
  position: relative;
  min-height: 100vh;
  min-height: 100dvh;
  /* Live's header overlays the hero (hero spans the full viewport, header
     floats on top of its first ~94px) rather than pushing it down, so a
     100vh hero's bottom edge lands exactly at the bottom of the viewport.
     Our header is sticky and in normal flow, adding its own height on top
     of the hero's -- without this, the hero (and anything anchored to its
     bottom, like .hero__awards) would run header-height's-worth past the
     bottom of the screen on first load. Pull the hero up under the header
     by the same amount instead of touching the header's own positioning,
     which is shared by every page. */
  margin-top: calc(-1 * var(--header-h));
  display: flex; align-items: center;
  color: #fff; overflow: hidden; text-align: center;
}
.hero__bg { position: absolute; inset: 0; z-index: 0; background: var(--ink-soft); }
.hero__bg img { width: 100%; height: 100%; object-fit: cover; }
/* No overlay: live's .hero_section paints the photo bare, and the dark
   gradient this used to add is exactly why our hero looked darker than
   live with the identical image file. Heading is #333 like live's. */
.hero__inner { position: relative; z-index: 2; width: 100%; }
/* font-size/line-height match live's real computed values, same fixed-px-
   per-breakpoint values as .page-hero h1 (checked separately -- both hero
   headings measure identically on live). */
.hero h1 { color: #333; max-width: 30ch; margin-inline: auto; font-size: 38px; line-height: 44px; font-weight: 700; }
.hero__sub { color: rgba(255,255,255,.9); font-size: clamp(1.05rem,1.6vw,1.35rem); max-width: 46ch; margin: 1rem auto 0; }
@media (max-width: 991px) { .hero h1 { font-size: 30px; line-height: 44px; } }
@media (max-width: 479px) { .hero h1 { font-size: 24px; line-height: 29px; } }
.hero__cta { margin-top: 2rem; display: flex; gap: 14px; flex-wrap: wrap; justify-content: center; }
.hero__awards { position: absolute; left: clamp(16px,4vw,48px); bottom: clamp(20px,4vw,40px); z-index: 2; display: flex; align-items: flex-end; gap: 14px; }
.hero__awards img { height: clamp(58px, 7vw, 84px); width: auto; filter: drop-shadow(0 3px 12px rgba(0,0,0,.35)); }
.hero__credit { position: absolute; right: clamp(16px,4vw,48px); bottom: clamp(20px,5vw,40px); z-index: 2; text-align: right; line-height: 1.35; }
.hero__credit strong { display: block; font-family: var(--display); text-transform: uppercase; letter-spacing: .12em; font-size: .72rem; color: #fff; }
.hero__credit span { font-family: var(--display); font-size: .7rem; color: rgba(255,255,255,.7); }
/* Now one span per credit, so draw the divider between them. */
.hero__credit span + span { margin-left: 8px; padding-left: 8px; border-left: 1px solid rgba(255,255,255,.5); }
@media (max-width: 767px) {
  /* These were hidden on phones because, pinned over the photo, they landed
     on the headline. With the hero stacked they have somewhere to go: straight
     under the photo, styled like the .photo-credit rows used further down the
     same pages -- project name in bold, each credit divided by a rule. */
  /* Direct children of a hero only. historic.html reuses .hero__credit
     inside a .quote-band, where the credit still sits over a photo and must
     stay white and absolutely placed -- the unscoped version turned it dark
     and dropped it into the flow on top of the picture. */
  /* Centred and on ONE line. At a flat .8rem the landing page's three
     credits ran wider than a phone and wrapped, dropping "Indigo Builders"
     onto a second line ragged-right. The size now scales with the viewport
     so the row shrinks to fit instead of breaking, and nowrap keeps it
     honest -- if it ever cannot fit it will overflow visibly rather than
     silently wrap. Clamped at .8rem so nothing grows past the old size. */
  .page-hero > .hero__credit, .hero > .hero__credit {
    position: static; display: flex; flex-wrap: nowrap; justify-content: center;
    align-items: baseline; gap: 8px; margin: 8px 0 0; padding: 0 12px;
    text-align: center; font-size: clamp(.56rem, 2.6vw, .8rem);
    color: var(--ink); line-height: 1.35;
  }
  .page-hero > .hero__credit strong, .hero > .hero__credit strong { display: inline; font-family: var(--display); text-transform: none; letter-spacing: normal; font-size: inherit; font-weight: 700; color: var(--ink); white-space: nowrap; }
  .page-hero > .hero__credit span, .hero > .hero__credit span { font-size: inherit; color: var(--ink); padding-left: 8px; border-left: 1px solid var(--ink); white-space: nowrap; }
  .page-hero > .hero__credit span + span, .hero > .hero__credit span + span { margin-left: 0; padding-left: 8px; border-left-color: var(--ink); }

  /* The award badges are pinned bottom-left over the photo on desktop. With
     the hero stacked they sit under the CTA on a white ground, where being
     jammed against the left edge reads as a mistake -- centre them.
     The drop-shadow goes with them: it exists to lift the badges off a dark
     photo, and against white it renders as a grey halo with a visible edge,
     which is the "shadow that doesn't blend" Ellie spotted on the Architizer
     hexagon (its transparent corners make the halo obvious). */
  .page-hero > .hero__awards, .hero > .hero__awards {
    position: static; order: 3; justify-content: center;
    margin: 4px 0 4px; padding: 0 12px; inset: auto;
  }
  .page-hero > .hero__awards img, .hero > .hero__awards img { filter: none; }
}

/* Right-aligned, single-line, pipe-separated photo credit directly under
   an inline (non-hero) image -- matches live's real .image-credit
   component exactly (font sizes, weights, and the border-left divider). */
.photo-credit { display: flex; justify-content: flex-end; flex-wrap: wrap; gap: 10px; margin-top: 8px; font-size: .8rem; color: var(--ink); }
.photo-credit strong { font-weight: 700; }
/* var(--body-font) ("Futura Book") only has a single weight-400 face
   registered, so it can't actually render a 300 (light) face -- switching
   to var(--display) ("Futura"), which has a real 300 face, so this reads
   as genuinely light like live instead of falling back to regular weight. */
.photo-credit span { font-family: var(--display); font-weight: 300; padding-left: 10px; border-left: 1px solid var(--ink); }
/* Groups an image with a .photo-credit below it as one block, so a flex
   row parent (like .split__media--natural, which centers a single item)
   doesn't lay the image and its credit out side by side instead of stacked. */
.split__media-inner { width: 100%; }

/* page hero (shorter, for interior pages) */
.page-hero {
  position: relative; color: #fff; overflow: hidden;
  min-height: 100vh;
  min-height: 100dvh;
  display: flex; align-items: center;
}
.page-hero__bg { position: absolute; inset: 0; background: var(--ink-soft); }
.page-hero__bg img, .page-hero__bg video { width: 100%; height: 100%; object-fit: cover; }
.page-hero::after { content:""; position:absolute; inset:0; background: rgba(0,0,0,.4); }
/* The 40% scrim above exists only so white heading text stays readable on
   top of a photo. The portfolio project heroes, the portfolio index hero
   and the firewall hero video carry no text at all, so it just darkened
   the picture -- live has no scrim, filter or opacity change on any of
   them (checked on fairmont-passive-house, /portfolio and /firewall). */
.page-hero--image-only::after { display: none; }
/* Firewall's hero video carries its message inside the frame, so cropping it
   cuts the words off -- worst on a phone, where a 16:9 video stretched to
   fill a 100vh band loses most of its width. Live never crops this one: the
   band is 16:9 at every size (1280x720 at desktop, 375x211 at 375), so the
   whole frame always shows. Only this hero gets it -- the configuration
   pages' videos sit behind our own headline, where filling the viewport is
   the point and cropping costs nothing. */
.page-hero--video { min-height: 0; aspect-ratio: 16 / 9; }

/* Phone: let a hero photo be as tall as it actually is instead of cropping
   it into a full-viewport band. At 375px a typical 1.6-ratio photo had to
   scale to ~1300px wide to cover an 812px-tall hero, so under a third of
   the picture was visible. Making __bg static and the image height:auto
   sizes each hero to its own photo, so every one fits exactly with no crop
   and no letterboxing, whatever its shape.
   Live crops these on a phone too (measured 31% on a project page), so this
   is a deliberate improvement rather than a parity fix.
   Applied to the text-free heroes only -- the project pages and the
   portfolio index. Heroes with a headline over the photo are left alone
   here; shrinking those means deciding where the text goes. */
@media (max-width: 767px) {
  .page-hero--image-only { min-height: 0; height: auto; }
  .page-hero--image-only .page-hero__bg { position: static; }
  .page-hero--image-only .page-hero__bg img { width: 100%; height: auto; object-fit: initial; display: block; }
}
.page-hero__inner { position: relative; z-index: 2; padding-block: 48px; }
/* Centers the h1 + CTA as a block (text itself stays left-started, matching
   live's real hero: a max-width column centered in the hero, not centered
   text) -- opt-in per page, e.g. aluminum.html. */
.page-hero__inner--center { display: flex; flex-direction: column; align-items: center; }
/* font-size/line-height match live's real computed values (checked at
   479/991/1440px -- live holds a fixed px size per breakpoint here rather
   than scaling continuously with clamp()). */
.page-hero h1 { color: #fff; max-width: 20ch; font-weight: 700; font-size: 38px; line-height: 44px; text-shadow: 0 2px 16px rgba(0,0,0,.45); }
.page-hero p { color: rgba(255,255,255,.9); max-width: 52ch; margin-top: 1rem; font-size: 1.1rem; text-shadow: 0 1px 10px rgba(0,0,0,.4); }
@media (max-width: 991px) { .page-hero h1 { font-size: 30px; line-height: 44px; } }
@media (max-width: 479px) { .page-hero h1 { font-size: 24px; line-height: 29px; } }
/* For hero headings that carry an explicit <br> (aluminum): the 20ch cap
   above would force a second wrap mid-line, so let the <br> alone set the
   break. Must sit after .page-hero h1 -- equal specificity, order decides. */
.page-hero h1.h1-manual-break { max-width: none; }
/* Live's material heroes put the heading and CTA in a 550px column
   (.home-hero_content left) with the button filling the column, so the button
   is exactly as wide as the heading text block. Must sit AFTER .page-hero h1,
   whose 20ch max-width it overrides at equal specificity. */
.hero-col { max-width: 550px; }
.hero-col h1 { max-width: none; }
.hero-col .hero__cta .btn { width: 100%; justify-content: center; }
.page-hero--plain { background: var(--ink-soft); min-height: 0; }
/* The two bare form confirmations (contact us, request-a-quote form) have no
   hero banner on live -- .raq-success there is a full-viewport centred block.
   Matched with generous vertical padding so the message is not stranded at
   the top of an otherwise empty page. */
.success-plain { padding-block: clamp(96px, 16vw, 200px); }
.success-plain h1 { font-size: clamp(28px, 3.9vw, 50px); line-height: 1.15; }
.page-hero--plain::after { display: none; }
.page-hero--plain .page-hero__inner { padding-block: clamp(48px, 8vw, 96px); }

/* Shorter, centered-text hero (resources.html) -- live's version isn't a
   full-viewport hero, it's a fixed ~548px band with a two-line centered
   h1 (a light "kicker" line, then the real title) and a .5 dark overlay
   rather than the default .4. */
.page-hero--short { min-height: clamp(320px, 43vw, 560px); }
.page-hero--short::after { background: rgba(0,0,0,.5); }
/* Matches live's real style for this shorter/utility-page hero (request-
   a-quote, contact-us, idea-book, newsletter-signup, resources): a
   lighter body-weight face, not the bold Myriad Pro Black used by the
   full-size .page-hero on every other interior page. */
.page-hero--short h1 { max-width: none; font-size: clamp(28px, 3.9vw, 50px); line-height: 1.15; font-family: var(--body-font); font-weight: 400; text-shadow: none; }
.page-hero__kicker { display: block; font-family: var(--heading); font-weight: 400; font-size: clamp(18px, 2.1vw, 27px); color: rgba(255,255,255,.7); margin-bottom: 4px; }

/* Configuration pages (fixed-windows.html etc.) -- a near-full-height hero
   with the title+subtext and an "Available In" material-tile row both
   anchored to the bottom edge, side by side (matching live's real
   .configuration-hero layout: title bottom-left-ish/centered-in-its-own
   column, materials bottom-right, same baseline). */
/* Live's header overlays this hero, so its 100vh bottom edge lands exactly
   at the bottom of the screen and the title + material row sit just above
   the fold. Ours is sticky and in normal flow, so a 100vh hero ran a
   header's-worth (94px) past the bottom and pushed the text off screen --
   Ellie's screenshot showed the title clipped by the viewport edge. Pull
   the hero up under the header by the same amount, exactly as .hero
   already does for the home/material heroes, rather than changing the
   header's positioning, which every page shares. */
.page-hero--config { align-items: flex-end; margin-top: calc(-1 * var(--header-h)); }
.page-hero__config-row { display: flex; flex-wrap: wrap; justify-content: space-between; align-items: flex-end; gap: 32px; width: 100%; padding-inline: 30px; padding-bottom: 18px; }
.page-hero__text { text-align: left; }

.hero-materials { display: flex; align-items: flex-end; }
/* No margin-bottom here -- with align-items: flex-end this label's own
   margin box has to end exactly where .hero-materials__item's does (0),
   or the label sits above the material name text instead of inline
   with it. */
.hero-materials__label { color: #fff; font-size: 1rem; font-weight: 300; margin-right: 12px; }
.hero-materials__item { display: flex; flex-direction: column; align-items: center; text-align: center; padding-inline: 10px; }
.hero-materials__item img { width: 72px; height: 72px; }
.hero-materials__item span { color: #fff; font-size: .875rem; margin-top: 10px; }

@media (max-width: 767px) {
  /* Heroes that carry a headline get the same fit, with the text moved below
     the photo. Measured at 375px: the photo gives 197-250px of height while
     the headline blocks need 99-420px, so only about half would fit as an
     overlay -- Wood needs 420px against a 197px photo. Rather than have
     these behave differently page to page, all of them stack.
     The white type and the dark scrim exist only for text sitting ON a
     photo, so below it the type goes dark and the scrim goes away.
     .page-hero--video (firewall) is excluded: it has no text and its own
     16:9 rule already fits it. */
  .page-hero:not(.page-hero--plain):not(.page-hero--video), .hero { min-height: 0; height: auto; display: flex; flex-direction: column; margin-top: 0; }
  /* photo, then its credit, then the headline -- DOM order puts the credit
     last, which would strand it under the button. */
  .page-hero__bg, .hero__bg { order: 0; }
  .hero__credit { order: 1; }
  .page-hero__inner, .hero__inner { order: 2; }
  .page-hero:not(.page-hero--plain):not(.page-hero--video) .page-hero__bg, .hero__bg { position: static; }
  .page-hero:not(.page-hero--plain):not(.page-hero--video) .page-hero__bg img,
  .hero__bg img { width: 100%; height: auto; object-fit: initial; display: block; }
  .page-hero:not(.page-hero--plain):not(.page-hero--video)::after, .hero::after { display: none; }
  .page-hero__inner, .hero__inner { padding: 24px var(--gutter); width: 100%; }
  .page-hero h1, .hero h1 { color: var(--ink); text-shadow: none; max-width: none; }
  .page-hero p, .hero__inner p { color: #333; text-shadow: none; }
  .page-hero__kicker { color: #666; }
  /* The hero CTA is .btn--outline-light: white text on a white outline,
     built for sitting on a dark photo. Below the photo it was invisible --
     a blank gap where the button should be. Give it the dark body-button
     treatment used everywhere else on a white ground. */
  .page-hero .btn--outline-light, .hero .btn--outline-light { color: #333; border-color: #333; }
  .page-hero .btn--outline-light:hover, .hero .btn--outline-light:hover { background: #333; color: #fff; }
  /* Config pages: the title and the material row stack under the photo. */
  .page-hero--config { align-items: stretch; }
  .page-hero__config-row { flex-direction: column; align-items: flex-start; padding-inline: 0; padding-bottom: 0; gap: 16px; }
  .hero-materials__label, .hero-materials__item span { color: #333; }
  /* These material swatches are light shapes drawn to sit on a dark photo --
     the uPVC one is a plain white square. Stacked below the photo on a white
     ground they vanished, so give them the same hairline outline the RAL
     swatch grids use for exactly this reason. */
  .hero-materials__item img, .ph-hero__frame img { border: .5px solid var(--ink); box-sizing: border-box; }
  .page-hero__config-row { flex-direction: column; align-items: flex-start; text-align: left; }
  .hero-materials { flex-wrap: wrap; justify-content: flex-start; }
  .hero-materials__label { width: 100%; text-align: left; margin: 0 0 8px; }
  .hero-materials__item img { width: 56px; height: 56px; }
}

/* Configuration "Highlights" + tabs section (fixed-windows.html,
   tilt-turn-windows.html, etc.) Matches live's real two-part layout: a
   full-width intro block (kicker + heading + copy + Request Quote), then
   a row with a tab menu on the left and the active tab's content on the
   right. The menu is a real 2-column grid (not a single stack) -- live
   wraps a 3rd+ tab to a second row rather than spanning it full-width,
   confirmed via computed grid-column ("auto" on every button, not
   span-2). Tab color is NOT an active/inactive state -- live's own
   Overview button is blue on every page regardless of which tab is
   selected, other tabs are dark; confirmed on tilt-turn-windows.html
   where the currently-active "Sizing" tab and the inactive "Popular
   Configurations" tab are both #333 while the inactive "Overview" tab is
   blue. Wired up with the site's existing [data-tabs] JS. */
/* Live's real container here is the same container-large pattern used for
   the galleries (1440px cap, flat 24px gutter, centered) -- confirmed via
   careful re-measurement at both 1280px and 1920px viewports; an earlier
   "max-width: none" reading only looked uncapped because it was taken
   below the 1440px breakpoint, where a capped and an uncapped container
   render identically. Without the cap, content hugs the left edge with a
   growing empty gap on the right at wide viewports instead of staying
   centered like live. */
.config-highlights .container { max-width: 1440px; padding-inline: 24px; }

.config-highlights__top { max-width: none; }
/* Live's copy text here uses the Light(300) face of the dual-weight
   "Futura" family (var(--display)), not "Futura Book" -- var(--body-font)
   is a different, heavier-looking face live doesn't use for this kind of
   copy. Color is a flat #333, not the lighter --muted/--body tokens. */
.config-highlights__label { display: block; font-size: 1.1rem; font-family: var(--display); font-weight: 300; color: #333; }
.config-highlights__top h2 { font-size: 1.5rem; margin: 4px 0 10px; }
.config-highlights__top p { font-family: var(--display); font-weight: 300; color: #333; margin-bottom: 1.4rem; }

.config-highlights__row { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 50px; margin-top: clamp(32px, 4vw, 48px); }
/* Live's menu grid always reserves the full 2-column width (2x tab
   width), even when only one tab actually exists -- on a single-tab
   page the second column sits empty, which is what pushes the pane
   content that extra distance to the right. A flex:0 0 auto menu would
   just shrink to fit whatever tabs exist, losing that reserved gap. */
.config-highlights__menu { flex: 0 0 300px; display: grid; grid-template-columns: auto auto; }
/* white-space: normal is deliberate. These tiles also carry .tabs__btn,
   which is set to nowrap below 767px for the horizontally-scrolling tab
   strips; on a fixed 150px tile that pushed 'Popular Configurations'
   (185px of text) straight out of its box on a phone. The compound
   selector outranks that rule, so the label wraps to two lines here,
   which is what it already does on desktop. */
.config-highlights__tab, .config-highlights__tab.tabs__btn, .config-highlights__tab.tabs__btn.is-active { display: block; width: 150px; text-align: center; padding: 15px 10px; background: #333; color: #fff; font-size: 1.1rem; font-weight: 700; border: none; cursor: pointer; font-family: var(--body-font, inherit); white-space: normal; }
.config-highlights__tab--overview, .config-highlights__tab--overview.tabs__btn, .config-highlights__tab--overview.tabs__btn.is-active { background: #1269b0; }
/* Selected-tile outline, matching live's real active tab (a 5px black
   inset box-shadow, not a border -- a border would add to the tile's box
   size and shift the layout, same reasoning as .lines-tile.is-active). */
.config-highlights__tab.is-active { box-shadow: inset 0 0 0 5px #000; }
.config-highlights__pane { flex: 1 1 480px; }
.config-highlights__pane-text { max-width: 520px; margin-bottom: clamp(24px, 3vw, 40px); }
.config-highlights__pane-text h3 { font-size: 1.5rem; margin-bottom: 10px; font-weight: 700; }
.config-highlights__pane-text p { font-family: var(--display); font-weight: 300; color: #333; }

/* Overview: the product image sits to the LEFT of the callout list
   (DOM order: img then ul -- flex default places first child left). */
.config-highlights__media { display: flex; align-items: center; gap: 40px; flex-wrap: wrap; }
/* Live caps this image with BOTH max-width and max-height (350px, or
   500px at wide viewports -- same breakpoint as before). Landscape
   product photos (windows) are bound by the width cap same as before;
   portrait ones (doors) are bound by the height cap instead, which
   width-only sizing let grow far too tall relative to the 100px
   callout circles beside it. width/height: auto (not 100%) lets the
   browser's native replaced-element sizing honor whichever cap binds. */
.config-highlights__media > img { width: auto; height: auto; max-width: 350px; max-height: 350px; }
@media (min-width: 1440px) { .config-highlights__media > img { max-width: 500px; max-height: 500px; } }

/* flex:1 so this list stretches to fill the row's remaining width
   (beside the image) instead of hugging its own content width -- on
   live the label text wraps to use that full available width, which a
   non-stretched list has no room to do, leaving a wide empty gap on
   wide screens. */
.config-callouts { display: flex; flex-direction: column; gap: 20px; flex: 1 1 260px; min-width: 0; }
.config-callouts__item { display: flex; align-items: center; gap: 0; }
.config-callouts__icon { width: 100px; height: 100px; border-radius: 50%; overflow: hidden; border: 1px solid var(--line-strong); flex-shrink: 0; }
.config-callouts__icon img { width: 100%; height: 100%; object-fit: cover; }
/* The height-maximum callout was a bitmap of the characters "8'" -- the
   circle around it is this CSS rule, not part of the image. Changing the
   figure meant either recreating that artwork in a typeface we cannot match
   exactly, or setting it as real text; real text is sharper, scales, is
   readable to a screen reader, and makes the next change a one-line edit. */
.config-callouts__icon--measure {
  display: flex; align-items: center; justify-content: center;
  background: var(--paper);
  font-family: var(--display); font-weight: 700; font-size: 26px;
  color: #3d3d3d; letter-spacing: -.01em; line-height: 1;
}
.config-callouts__line { width: 50px; height: 2px; background: var(--line-strong); flex-shrink: 0; }
.config-callouts__label { flex: 1 1 auto; min-width: 0; font-size: 1rem; color: var(--body); padding-left: 16px; }

/* "Popular Configurations"-style tab: a grid of labeled diagram icons.
   Real icon size measured on live: 200x200px, 50px gap both axes. */
.config-icon-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 50px; }
.config-icon-grid__item { display: flex; flex-direction: column; align-items: center; text-align: center; gap: 14px; }
.config-icon-grid__item img { width: 200px; max-width: 100%; height: 200px; object-fit: contain; }
.config-icon-grid__item span { font-size: .78rem; letter-spacing: .04em; text-transform: uppercase; color: var(--body); }

/* Architizer award callout, page-specific to pocketing-lift-slides.html --
   live's real Highlights section for this product leads with an award
   badge before the regular kicker/heading/intro block. Font sizes
   measured on live: label 18px (matches .config-highlights__label
   exactly), heading 24px/900/Myriad (matches the default h1-h5 rule,
   just pinned to a fixed size instead of the default h3 clamp), body
   16px/300/Futura/#333 (the same pattern used everywhere else). */
.pls-award { margin-bottom: 2rem; }
.pls-award h3 { font-size: 24px; margin: 4px 0 10px; }
.pls-award p { font-family: var(--display); font-weight: 300; color: #333; margin-bottom: 1.4rem; }
.pls-award__badge { width: 110px; height: auto; display: block; margin-bottom: 1.4rem; }

/* Per-tab visual for pocketing-lift-slides.html's Highlights pane --
   live's real layout here is a single centered column (animated GIF,
   then a smaller SVG/PNG configuration diagram, then text, then a large
   lightbox photo), not the side-by-side image+callout-list layout every
   other configuration page uses. Column stacking and the diagram's ~51%
   width relative to the GIF measured on live at a 1440px viewport. */
.pls-visual { display: flex; flex-direction: column; align-items: center; gap: 10px; }
.pls-visual__gif { width: 100%; }
.pls-visual__diagram { width: 51%; }
.pls-visual__text { width: 100%; margin: 10px 0; }
.pls-visual__text p { font-family: var(--display); font-weight: 300; color: #333; }
.pls-visual__photo { display: block; width: 100%; cursor: zoom-in; }
.pls-visual__photo img { width: 100%; display: block; }

/* Before/after compare slider (e.g. liftslide-doors' "Standard vs.
   OpenView" tab) -- a single image area with a draggable divider between
   the two real photos, plus labeled side rails, matching live's real
   Juxtapose-style widget. JS in main.js drives --pos on .compare-slider__stage. */
.compare-slider { display: flex; align-items: stretch; border-radius: var(--r); overflow: hidden; }
.compare-slider__side { flex: 0 0 56px; background: var(--ink-soft); display: flex; align-items: center; justify-content: center; }
.compare-slider__side span { writing-mode: vertical-rl; transform: rotate(180deg); color: #fff; font-family: var(--display); letter-spacing: .08em; font-weight: 700; font-size: .95rem; white-space: nowrap; }
.compare-slider__stage { position: relative; flex: 1 1 auto; aspect-ratio: 3 / 2; overflow: hidden; touch-action: pan-y; --pos: 50%; }
.compare-slider__img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; pointer-events: none; user-select: none; }
.compare-slider__img--after { clip-path: inset(0 0 0 var(--pos)); }
.compare-slider__handle { position: absolute; top: 0; bottom: 0; left: var(--pos); width: 2px; background: #fff; transform: translateX(-50%); cursor: ew-resize; }
.compare-slider__handle::after {
  content: "";
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: 40px; height: 40px; border-radius: 50%;
  background: var(--blue) url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M8 7l-5 5 5 5M16 7l5 5-5 5'/%3E%3C/svg%3E") center / 20px no-repeat;
  box-shadow: 0 2px 10px rgba(0,0,0,.35);
}
@media (max-width: 479px) { .compare-slider__side { flex-basis: 40px; } .compare-slider__side span { font-size: .8rem; } }

/* "Sizing"-style tab: same 3-column grid as Popular Configurations --
   the diagram+labels group spans 2 columns, the note sits in the 3rd,
   aligned to the bottom of the row (matches live's real layout: the
   Smallest/Largest captions sit directly below the combined diagram,
   split evenly under each half; the note is off to the right). */
.config-sizing { display: grid; grid-template-columns: repeat(3, 1fr); gap: 50px; align-items: end; }
.config-sizing__diagram { grid-column: span 2; max-width: 500px; }
.config-sizing__img { display: block; width: 100%; max-width: 500px; height: auto; }
.config-sizing__labels { display: flex; }
.config-sizing__labels span { flex: 1; text-align: center; font-size: .78rem; letter-spacing: .04em; text-transform: uppercase; color: var(--body); font-weight: 700; }
.config-sizing__note { font-size: .875rem; color: var(--body); }

@media (max-width: 767px) {
  .config-highlights .container { padding-inline: var(--gutter); }
  .config-highlights__row { flex-direction: column; gap: 24px; }
  .config-highlights__menu { flex-basis: auto; }
  .config-highlights__media { flex-direction: column; }
  .config-icon-grid { grid-template-columns: repeat(2, 1fr); }
  .config-icon-grid__item img { width: 140px; height: 140px; }
  .config-sizing { grid-template-columns: 1fr; }
  .config-sizing__diagram { grid-column: span 1; }
}

/* Project credit caption (Zola signature) */
.credit {
  display: flex; flex-direction: column; gap: 2px;
  font-size: .8rem; letter-spacing: .03em;
}
.credit strong { font-family: var(--display); text-transform: uppercase; letter-spacing: .1em; font-weight: 700; font-size: .72rem; }
.credit span { color: var(--muted); }
.media-band { position: relative; background: var(--ink-soft); }
.media-band img { width: 100%; height: clamp(360px, 60vh, 680px); object-fit: cover; }
.media-band__credit {
  position: absolute; left: clamp(16px,4vw,48px); bottom: clamp(16px,4vw,40px);
  color: #fff; z-index: 2;
}
.media-band__credit .credit span { color: rgba(255,255,255,.85); }
.media-band::after { content:""; position:absolute; inset:0; background: linear-gradient(180deg, transparent 55%, rgba(0,0,0,.45)); }

/* Full-bleed, full-viewport-height photo band with a diagonal dark-to-
   transparent gradient (left side darker for text contrast) and a
   bottom-right project credit -- matches live's real "to-america"
   section on tilt-turn-windows.html. Background image + gradient are
   set inline per page since the photo differs per project. */
.hero-cta-band {
  position: relative;
  min-height: 100vh; min-height: 100dvh;
  background-size: cover; background-position: center;
  display: flex; align-items: center;
  padding-inline: clamp(24px, 8vw, 128px);
  padding-block: clamp(32px, 6vw, 64px);
  color: #fff;
}
.hero-cta-band__text { max-width: 320px; }
.hero-cta-band__text h2 { color: #fff; font-size: clamp(24px, 2.6vw, 32px); font-weight: 800; line-height: 1.2; margin-bottom: .9rem; }
.hero-cta-band__text p { font-size: 1rem; line-height: 1.5; margin-bottom: 1.5rem; }
.hero-cta-band__credit {
  position: absolute; right: clamp(24px, 6vw, 64px); bottom: clamp(24px, 4vw, 40px);
  color: rgba(255,255,255,.85); font-size: .8rem; letter-spacing: .03em; text-align: right;
}
/* Phone: this band is min-height:100vh above, so on a 375x812 screen its
   1.9-ratio photo was squeezed into a 0.46 slot -- 24% of the picture
   visible, against the 84% the desktop band shows at its 1.6 ratio. Sizing
   it to that same 1.6 (62.5vw) restores desktop's framing as far as the
   text allows: the text block needs 297px here, so the band settles there
   and shows 66%. Must sit after the base rule -- equal specificity, order
   decides, and the first attempt at this lost by being declared earlier. */
@media (max-width: 767px) {
  .hero-cta-band { min-height: 62.5vw; }
  /* The band's own gradient is angled and fades out by 47%, which works
     where the text sits in a 320px column on the left. On a phone the text
     spans nearly the full width and ran onto the bright window, so add a
     flat scrim across the whole band. It goes on ::before rather than in
     background-image because the photo URL is set inline per page and
     overriding background-image here would drop it. */
  .hero-cta-band::before { content: ""; position: absolute; inset: 0; background: rgba(0,0,0,.45); }
  .hero-cta-band__text { position: relative; z-index: 1; }
  /* The credit is anchored bottom-right and at this width lands on top of
     the copy and the button. Hidden, matching .hero__credit, which is
     already display:none on phones for the same collision. */
  .hero-cta-band__credit { display: none; }
}

/* =========================================================================
   CARDS
   ========================================================================= */
.card { background: #fff; border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; transition: transform .3s var(--ease), box-shadow .3s var(--ease); }
.card:hover { transform: translateY(-4px); box-shadow: var(--shadow); }
.card__media { aspect-ratio: 16/10; overflow: hidden; background: var(--paper-alt); }
.card__media img { width: 100%; height: 100%; object-fit: cover; transition: transform .6s var(--ease); }
.card:hover .card__media img { transform: scale(1.05); }
.card__body { padding: 22px 24px 26px; }
.card__body h3 { font-size: 18px; font-weight: 700; margin-bottom: .5rem; }
.card__body p { font-size: .96rem; color: var(--muted); }
.card__kicker { font-family: var(--display); text-transform: uppercase; letter-spacing: .12em; font-size: .68rem; color: var(--blue); font-weight: 700; margin-bottom: .6rem; }
/* Matches live's real portfolio project cards -- scoped rather than
   changed on the shared .card/.card__kicker classes, which are also used
   for unrelated card types (testimonials, blog previews) on 22 other
   pages that weren't checked against this specific live styling. */
.card--live .card__body { text-align: center; }
/* Square corners on the portfolio project cards, per Ellie. Scoped to
   .card--live so the rounded look stays on the other card types. */
.card--live { border-radius: 0; }
.card--live .card__kicker { text-transform: none; letter-spacing: normal; font-size: 14px; font-weight: 300; color: #000; margin-bottom: .3rem; }
.card--live .card__body h3 { font-family: var(--display); font-size: 16px; font-weight: 700; color: #000; }

/* Config (window-type) cards with icon */
.config-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(220px,1fr)); gap: 18px; }
.config-card {
  background: #fff; border: 1px solid var(--line); border-radius: var(--r);
  padding: 26px 22px; display: flex; flex-direction: column; gap: 14px; min-height: 230px;
  transition: transform .3s var(--ease), box-shadow .3s var(--ease), border-color .3s var(--ease);
}
.config-card:hover { transform: translateY(-4px); box-shadow: var(--shadow); border-color: transparent; }
.config-card__icon { height: 64px; }
.config-card__icon img { height: 100%; width: auto; }
.config-card h3 { font-size: 1.12rem; }
.config-card p { font-size: .9rem; color: var(--muted); flex: 1; }
.config-card .link-arrow { font-size: .72rem; }

/* Material/icon link chips */
.chip-grid { display: flex; flex-wrap: wrap; gap: 12px; }
.chip {
  display: inline-flex; align-items: center; gap: 10px;
  border: 1px solid var(--line-strong); border-radius: 50px;
  padding: 10px 20px 10px 14px; font-family: var(--display); font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em; font-size: .8rem; color: var(--ink);
  transition: all .25s var(--ease);
}
.chip img { width: 26px; height: 26px; }
.chip:hover { border-color: var(--blue); color: var(--blue); background: var(--blue-050); }

/* icon tile grid (configs available, simplified). Flex, not grid, so a
   partial last row (e.g. 2 tiles left over from 13) centers itself instead
   of hugging the left edge under the full-width columns above it. */
.icon-tiles { display: flex; flex-wrap: wrap; justify-content: center; gap: 14px; }
.icon-tile {
  flex: 0 1 150px;
  display: flex; flex-direction: column; align-items: center; gap: 12px; text-align: center;
  padding: 22px 14px; border: 1px solid var(--line); border-radius: var(--r); background: #fff;
  font-family: var(--display); font-weight: 600; font-size: .85rem; color: var(--ink);
  transition: all .25s var(--ease);
}
.icon-tile img { height: 52px; }
.icon-tile:hover { border-color: var(--blue); color: var(--blue); box-shadow: var(--shadow-sm); transform: translateY(-3px); }
/* Plain variant -- no card look, just an icon and a label. Matches live's
   real "Effortless Efficiency" section on the homepage (both the materials
   swatches and the configuration icons), where these tiles have no
   border, background, or padding at all. */
.icon-tiles--plain .icon-tile { border: none; background: none; padding: 0; font-family: var(--body-font); font-weight: 400; font-size: 1.09rem; color: #333; }
.icon-tiles--plain .icon-tile:hover { box-shadow: none; }
.icon-tiles--plain .icon-tile img { height: 80px; }
/* Larger tile variant -- for illustrative cross-sections (e.g. Cladding
   Styles) rather than small icon+label swatches. */
.icon-tiles--lg { flex-wrap: nowrap; }
.icon-tiles--lg .icon-tile { flex: 0 1 280px; padding: 24px; gap: 16px; font-size: 1rem; }
.icon-tiles--lg .icon-tile img { height: 220px; max-width: 100%; }
@media (max-width: 767px) { .icon-tiles--lg { flex-wrap: wrap; } }

/* Small color-chip variant: live's real RAL/Tactile/Anodized swatches are
   bare ~40x26px color rectangles with no visible label at all -- the name
   only shows in the hover preview (already built into .swatch-tile below).
   Kept .icon-tile's card look for Wood Stains (real photo thumbnails,
   Ellie's own call to keep those larger) and everywhere else that reuses
   .icon-tile -- this variant is scoped to the RAL/Tactile/Anodized grids. */
.icon-tiles--swatch { gap: 6px; }
.icon-tiles--swatch .icon-tile { flex: 0 0 40px; width: 40px; height: 26px; padding: 0; border: .5px solid var(--ink); border-radius: 0; background: none; }
.icon-tiles--swatch .icon-tile img { width: 100%; height: 100%; object-fit: cover; }
.icon-tiles--swatch .icon-tile > span { display: none; }

/* Taller variant, for grids whose swatches are the full 330x500 sample
   photographs rather than the 55x35 -icon chips. clad-wood references the
   full images in all four of its grids; aluminum and upvc reference the
   icons, which are landscape and already sit in the 40x26 box at close to
   their own ratio.

   In that landscape box object-fit:cover took a thin horizontal band out of
   the middle of a tall photo -- the swatch was unreadable, which is what
   Ellie reported. These keep the photo's own portrait shape, sized by height
   so the width follows from it, and are bigger so the finish is legible.

   Must come after .icon-tiles--swatch above: same specificity, so order is
   what decides it. */
.icon-tiles--swatch-tall { gap: 8px; }
.icon-tiles--swatch-tall .icon-tile { flex: 0 0 auto; width: auto; height: 84px; }
.icon-tiles--swatch-tall .icon-tile img { width: auto; height: 100%; object-fit: contain; }

/* Swatch tile: hovering a small stain/color tile reveals a larger preview
   of the same photo, floating above it -- matches live's real interaction. */
.swatch-tile { position: relative; }
.swatch-preview {
  position: absolute; left: 50%; bottom: calc(100% + 12px); transform: translateX(-50%) translateY(6px) scale(.96);
  width: 200px; background: #fff; border: 1px solid var(--line); border-radius: var(--r); box-shadow: var(--shadow);
  padding: 10px; text-align: center; opacity: 0; pointer-events: none; transition: all .18s var(--ease); z-index: 6;
}
.swatch-preview img { width: 100%; height: auto; border-radius: var(--r-sm); display: block; }
.swatch-preview span { display: block; margin-top: 8px; font-family: var(--display); font-weight: 600; font-size: .82rem; color: var(--ink); line-height: 1.3; }
.swatch-preview::after {
  content: ""; position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  border: 7px solid transparent; border-top-color: #fff;
}
.swatch-tile:hover .swatch-preview { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }

/* Option row: a small product photo beside a name + description, repeated
   in a 2-up grid (handles, wood species) -- matches live's real layout,
   which wraps pairs of these left-to-right rather than stacking a single
   full-width column. */
.option-rows { display: flex; flex-wrap: wrap; }
.option-row { flex: 0 1 50%; display: flex; gap: 16px; align-items: flex-start; padding: 14px 14px 24px; box-sizing: border-box; }
.option-row__img { width: 90px; height: 90px; object-fit: contain; flex: 0 0 auto; }
.option-row__body h4 { margin-bottom: 4px; }
.option-row__body p { font-size: .92rem; color: var(--muted); margin: 0; }
/* Wood Species photos are real project photography, not icons -- live shows
   them as a taller portrait crop instead of a square icon box. */
[data-panel="species"] .option-row__img { height: 170px; object-fit: cover; }
@media (max-width: 767px) { .option-row { flex: 0 1 100%; } }

/* Handle colour swatches (the Handles panel on all four product pages).
   These replaced the old .option-row list: nine colours of the same handle,
   so a photo and a name carry it and per-colour paragraphs would just repeat
   the name nine times.

   Sized by HEIGHT, not width. The source photos are a tall 420x1508 (1:3.6),
   so a width-based grid would make them enormously tall; a fixed height keeps
   every handle the same physical size, which is the comparison the panel is
   for. width:auto then follows from the intrinsic ratio.

   The pale colours -- Traffic White above all -- are nearly the tone of the
   section behind them, so each swatch gets a white panel to sit on and a
   hairline edge. That reads as a deliberate product tile and stops white and
   silver from dissolving into the background. */
.handle-swatches {
  --hs-gap: 16px;
  display: flex; flex-wrap: wrap; gap: var(--hs-gap);
  justify-content: center;
  margin-top: 8px;
}
/* Nine tiles would wrap 6+3 at desktop width. Ellie asked for 5 then 4, laid
   out like the Wood Stains swatches. A zero-height full-width flex item forces
   the line to end after the fifth, and justify-content:center then centres
   both rows independently -- which a grid could not do, since its second row
   would sit under the first four columns rather than centred. */
.handle-swatches__break { display: none; }
@media (min-width: 768px) {
  .handle-swatches__break { display: block; flex: 0 0 100%; height: 0; margin: 0; padding: 0; border: 0; }
}
.handle-swatch {
  flex: 0 0 auto; box-sizing: border-box;
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  width: 132px; padding: 18px 10px 14px;
  background: var(--paper); border: 1px solid var(--line);
}
.handle-swatch img { height: 210px; width: auto; display: block; object-fit: contain; }
.handle-swatch span {
  font-family: var(--body-font); font-size: 13px; line-height: 1.3;
  color: #333; text-align: center;
}
/* Five per row is a share of the panel, not a fixed 132px. This panel sits
   beside the option tiles, so its width tracks the layout rather than the
   viewport -- at a 900px viewport it is only 548px wide, and five fixed tiles
   (644px with gaps) wrapped to 4+1 before the forced break, giving 4/1/4. A
   proportional basis fits five at any panel width; max-width stops them
   ballooning on a wide one, and the row stays centred either way. */
@media (min-width: 768px) {
  .handle-swatch { flex: 0 0 calc((100% - 4 * var(--hs-gap)) / 5); width: auto; max-width: 150px; }
}
/* Between 768 and 991 the panel is only ~448px wide, and a flex item's
   default min-width:auto floors each tile at its min-content -- the word
   "Anthracite" plus padding, about 79px. Five of those plus 16px gaps
   overflowed by a hair and wrapped to 4+1. Tightening the gap and side
   padding in this band buys back the few pixels so five still fit. */
@media (min-width: 768px) and (max-width: 991px) {
  .handle-swatches { --hs-gap: 8px; }
  .handle-swatch { padding-inline: 6px; }
}
@media (max-width: 991px) { .handle-swatch img { height: 180px; } }
@media (max-width: 767px) {
  /* Two clear columns beat five cramped ones on a phone -- at 116px the
     handle is thinner than a fingertip and the name starts wrapping. */
  .handle-swatches { gap: 12px; }
  /* 0 0, not 1 1: with nine tiles the last one is alone on its row, and a
     growable basis stretched it to the full width -- one giant handle under
     four normal ones. */
  .handle-swatch { flex: 0 0 calc(50% - 6px); width: auto; max-width: none; }
}

/* Materials + full configuration list side by side (homepage) */
.selection-layout { display: grid; grid-template-columns: minmax(240px, 1fr) 2.6fr; gap: clamp(32px, 5vw, 64px); align-items: start; }
.selection-layout__materials { max-width: 300px; margin-top: 28px; }
.selection-layout__materials .icon-tile { flex: 0 1 calc(50% - 7px); }
@media (max-width: 991px) {
  .selection-layout { grid-template-columns: 1fr; }
  .selection-layout__materials { max-width: 340px; }
}

/* =========================================================================
   PRODUCT LINES + SPEC STATS
   ========================================================================= */
.lines-table { border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.lines-table__row { display: grid; grid-template-columns: 1fr auto; gap: 16px; align-items: center; padding: 16px 22px; border-bottom: 1px solid var(--line); }
.lines-table__row:last-child { border-bottom: none; }
.lines-table__row:nth-child(even) { background: var(--paper-alt); }
.lines-table__name { font-family: var(--display); font-weight: 700; color: var(--ink); font-size: 1.05rem; }
.lines-table__r { font-family: var(--display); font-weight: 800; color: var(--blue); font-size: 1.4rem; letter-spacing: -.02em; }

/* .spec-grid -- sound-control.html's two data tables, matched to live exactly.
   Live builds these as a 2-column CSS grid of flat cells with a 2px white gutter
   (the page background showing through), NOT as bordered rows, so every value is
   measured off live at 1280 and 390:
     grid    2 equal columns, 2px gap
     header  #bbb, #333 text, uppercase, padding 20px 10px
     data    #1269b0, white text, padding 10px
     text    Futura Book 14/20 regular, centred; sub-label 14 -> 12px
     corners square -- live has no radius on these cells
   #1269b0 is deliberately literal, not var(--blue): the brand blue in this
   stylesheet is #003d76, and live's table blue is a distinctly lighter one.
   Row heights come from content plus padding, as they do on live -- no fixed
   heights, so the grid equalises each row itself. */
.spec-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; }
.spec-grid__cell {
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; padding: 10px; border-radius: 0;
  background: #1269b0; color: #fff;
  font-family: var(--body-font); font-size: 14px; line-height: 20px; font-weight: 400;
}
.spec-grid__cell--head { background: #bbb; color: #333; text-transform: uppercase; padding: 20px 10px; }
.spec-grid__sub { font-size: 12px; line-height: 20px; }

/* .awards-list -- awards.html's year-by-year list, matched to live.
   Live lays each block out as a 1/3 + 2/3 row: the year on the left, then a
   section label ("Awards" / "Press") and its entries on the right. A year that
   has both Awards and Press is two rows, the second sitting close under the
   first. Values measured off live at 1280:
     row      1fr 2fr; 76px between year groups, 20px between a year's two rows
     year     Futura 28/18 bold, #1269b0 (live's lighter blue, as the tables)
     label    Futura Book 15/18 regular, #333, uppercase, 14px below
     entry    Futura 14/20 light, #838383; publication bold and title italic
              inside the link, both #333, no underline
   Live's publication is weight 600; the Futura we ship has 300 and 700 only,
   so 700 is the closest available. Stacks to one column below 767, where live
   stacks it too. */
.awards-row { display: grid; grid-template-columns: 1fr 2fr; column-gap: 20px; margin-top: 76px; }
.awards-row:first-child { margin-top: 0; }
.awards-row--tight { margin-top: 20px; }
.awards-row__year { font-family: var(--display); font-weight: 700; font-size: 28px; line-height: 18px; color: #1269b0; }
.awards-row__title { font-family: var(--body-font); font-weight: 400; font-size: 15px; line-height: 18px; color: #333; text-transform: uppercase; letter-spacing: 0; margin: 0 0 14px; }
.awards-entry { font-family: var(--display); font-weight: 300; font-size: 14px; line-height: 20px; color: #333; margin: 0; }
.awards-entry a { color: #333; text-decoration: none; }
.awards-entry a:hover { text-decoration: underline; text-underline-offset: 3px; }
.awards-entry strong { font-weight: 700; color: #333; }
.awards-entry em { font-weight: 300; font-style: italic; color: #333; }

/* careers.html's "Currently no openings available." line. Live renders this
   from an empty CMS list in Futura Book 14/20 #333, not as body copy. */
.careers-empty { font-family: var(--body-font); font-weight: 400; font-size: 14px; line-height: 20px; color: #333; }

@media (max-width: 767px) {
  .awards-row { grid-template-columns: 1fr; row-gap: 10px; margin-top: 48px; }
  .awards-row--tight { margin-top: 20px; }
}

/* .split--swap: media sits in the LEFT column on desktop, but the DOM stays in
   reading order so the stacked mobile layout keeps each heading above its own
   content. Used by sound-control.html, where .reverse would otherwise push a
   ten-row table above the heading that explains it on a phone. */
@media (min-width: 768px) { .split--swap .split__media { order: -1; } }

/* =========================================================================
   PROJECT DETAIL (individual portfolio project pages)
   Layout matches live's real project-detail template: full-bleed hero with
   no text, then a narrow name+description+CTA column beside a wide column
   (location/materials + people fields flowing in a wrapping row, with the
   window/door type icons directly beneath), then a photo grid. Live's own
   left/right split is a 2-span/4-span 6-col grid -- 1fr/2fr reproduces the
   same ratio. Reused by all project-*.html pages.
   ========================================================================= */
.project-detail__info { display: grid; grid-template-columns: 1fr 2fr; gap: clamp(28px, 5vw, 70px); align-items: start; padding-block: clamp(40px, 6vw, 72px); }
.project-detail__name { font-size: clamp(28px, 3.2vw, 38px); margin-bottom: 10px; }
.project-detail__desc { color: var(--body); line-height: 1.6; margin-bottom: 1.7rem; }
.project-detail__left .btn { margin-top: 4px; }

.project-detail__fields { display: flex; flex-wrap: wrap; gap: 26px 32px; }
.project-detail__field { display: flex; flex-direction: column; gap: 5px; min-width: 90px; }
.project-detail__field-label { font-size: .8rem; color: var(--muted); }
.project-detail__field-label::after { content: ":"; }
.project-detail__field-value { font-size: .92rem; color: var(--ink); font-weight: 600; }
.project-detail__field-value a { color: var(--ink); }
.project-detail__field-value a:hover { color: var(--blue); }

.project-detail__loc { font-size: .92rem; font-weight: 600; color: var(--ink); }
/* Each product line on its own line (matches live's real markup: a
   plain vertical list, not comma-joined) -- also narrows this field
   enough that Photographer fits on the same row as the rest instead of
   wrapping to its own line. */
.project-detail__materials { display: flex; flex-direction: column; gap: 2px; margin-top: 5px; }
.project-detail__material { font-size: .92rem; color: var(--ink); }

.project-detail__types { margin-top: clamp(28px, 4vw, 40px); }
.icon-row { display: flex; flex-wrap: wrap; gap: 24px; }
.icon-row__item { display: flex; flex-direction: column; align-items: center; gap: 8px; width: 84px; text-align: center; }
.icon-row__item img { width: 48px; height: 48px; }
.icon-row__item span { font-size: .78rem; color: var(--muted); }

@media (max-width: 767px) { .project-detail__info { grid-template-columns: 1fr; } }

/* Project photo gallery -- Glo Fenestration-style alternating rows: a
   "mixed" row of 2-3 images (a portrait+landscape pair, three portraits,
   or two landscapes) followed by a single full-width landscape row,
   repeating. Each row is its own small grid so row shapes don't have to
   share column tracks. Images crop to their slot via object-fit except
   the full-width row, which shows the photo at its own natural aspect
   (matching the reference site's real behavior). Lightbox-enabled via
   the existing [data-lightbox] wiring in main.js. */
/* Flat 16px gap matches live's real .image-grid gap exactly (verified on
   both fixed-windows and tilt-turn's live galleries) -- not responsive. */
.proj-gallery { display: flex; flex-direction: column; gap: 16px; }
.proj-gallery__row { display: grid; gap: inherit; }
.proj-gallery__row--pair { grid-template-columns: 1fr 2fr; }
.proj-gallery__row--trio { grid-template-columns: repeat(3, 1fr); }
.proj-gallery__row--duo { grid-template-columns: repeat(2, 1fr); }
.proj-gallery__row--full { grid-template-columns: 1fr; }
/* A lone trailing image left over after chunking -- same 3-up track as
   --trio so it sits at one-third width, unstretched, rather than
   filling the row (matches live's real behavior for an odd leftover). */
.proj-gallery__row--single { grid-template-columns: repeat(3, 1fr); }
/* ...but let it keep the photo's own shape rather than the 3/4 portrait
   crop the other tiles use. A trailing tile has no neighbour to line its
   height up with, so the uniform box buys nothing, and cropping a
   landscape shot into a tall third-width sliver is what Ellie flagged.
   Live agrees: its equivalent trailing tile renders 448x299 (1.50) on a
   1377px grid, i.e. the photo's own ratio. aspect-ratio:auto also means a
   genuinely portrait-shot photo still shows portrait, which is the
   exception she asked for -- no per-image markup needed. */
.proj-gallery__row--single .proj-gallery__item { aspect-ratio: auto; }
.proj-gallery__row--single .proj-gallery__item img { height: auto; object-fit: initial; }

.proj-gallery__item { display: block; overflow: hidden; cursor: zoom-in; aspect-ratio: 3 / 4; }
.proj-gallery__item img { width: 100%; height: 100%; object-fit: cover; display: block; }

.proj-gallery__row--pair .proj-gallery__item:last-child,
.proj-gallery__row--duo .proj-gallery__item { aspect-ratio: 3 / 2; }

.proj-gallery__row--full .proj-gallery__item { aspect-ratio: auto; }
.proj-gallery__row--full .proj-gallery__item img { height: auto; object-fit: initial; }

@media (max-width: 767px) {
  .proj-gallery__row--pair,
  .proj-gallery__row--trio,
  .proj-gallery__row--duo,
  .proj-gallery__row--single { grid-template-columns: 1fr; }
  /* These rows are a single stacked column on a phone, so there is no
     neighbour to line up with and no reason to force a uniform box. The
     old 4/3 here cropped every portrait photo to about half its frame
     (a 0.67 source in a 1.33 box). Letting each image keep its own shape
     shows all of them whole -- same treatment --full already gets. */
  .proj-gallery__row--pair .proj-gallery__item,
  .proj-gallery__row--trio .proj-gallery__item,
  .proj-gallery__row--duo .proj-gallery__item,
  .proj-gallery__row--single .proj-gallery__item { aspect-ratio: auto; }
  .proj-gallery__row--pair .proj-gallery__item img,
  .proj-gallery__row--trio .proj-gallery__item img,
  .proj-gallery__row--duo .proj-gallery__item img,
  .proj-gallery__row--single .proj-gallery__item img { height: auto; object-fit: initial; }
}

/* No card border/background here or on .product-line__media/.spec-strip
   below -- matches live, which has no outline or gray fill around this
   product-line card or its stats row at all. */
.product-line { }
/* Three columns matching live's real layout: cross-section image, detail
   text, and a narrow "Visible Sash" column, all the same height. */
/* Values measured off live's own panel (see PR #57): image / copy / sash at
   350 / flexible / 131 with a 25.6px gutter and no per-column padding. Live
   styles this component identically on every page that carries it, so these
   are the base rules; reviewed on passive-house first, then rolled out. */
/* Two columns since 2026-09-03: the Visible Sash tile now sits inline at the
   end of the Available In icon row (Ellie's call, diverging from live's third
   131px column). */
.product-line__top { display: grid; grid-template-columns: minmax(0, 350px) minmax(0, 1fr); gap: 25.6px; align-items: start; }
.product-line__media { display: flex; align-items: center; justify-content: center; padding: 0; }
.product-line__media img { width: 100%; height: auto; max-height: 350px; object-fit: contain; }
.product-line__intro { padding: 0; }
.product-line__intro h3 { font-size: 24px; line-height: 30px; font-weight: 600; margin-bottom: 10px; }
.product-line__intro p { font-family: var(--display); font-size: 16px; line-height: 25px; font-weight: 300; color: #333; margin-bottom: 10px; }

/* Clad wood lines: a colored tile per line (darker = higher R-value)
   beside the detail card, instead of a plain row of tab buttons. */
.lines-layout { display: grid; grid-template-columns: minmax(220px, 280px) 1fr; gap: 24px; align-items: start; }
/* Square corners, as live has them: its tile grid computes border-radius 0
   and overflow visible. The rounding here came from a radius on the grid
   plus overflow:hidden to clip the tiles into it -- both removed, since the
   overflow existed only to serve the radius. */
.lines-tiles { display: grid; grid-template-columns: repeat(2, 1fr); gap: 2px; }
.lines-tile { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; padding: 20px 16px; min-height: 150px; color: #fff; text-align: center; border: 2px solid transparent; }
.lines-tile__name { font-family: var(--display); font-weight: 600; font-size: .92rem; line-height: 1.2; }
.lines-tile__r { font-family: var(--display); font-weight: 800; font-size: 1.3rem; }
/* Live's real active-tile indicator is a 5px inset box-shadow, not a
   border -- a border would get clipped by .lines-tiles .tabs__btn's
   border-bottom:none reset below, but an inset shadow doesn't. */
.lines-tile.is-active { box-shadow: inset 0 0 0 5px #000; }
.lines-tile--r11 { background: #004d80; }
.lines-tile--r9  { background: #006eb7; }
.lines-tile--r8  { background: #2e88c4; }
.lines-tile--r7  { background: #5ca2d1; }
.lines-tile--r6  { background: #8bbdde; }
.lines-tile--r4  { background: #b7d4e8; }
/* Fractional R-value tiers (aluminum's lines land on .5 values) --
   interpolated between the existing whole-number shades above. */
.lines-tile--r75 { background: #4595ca; }
.lines-tile--r65 { background: #73afd7; }
.lines-tile--r45 { background: #b7d4e8; }
@media (max-width: 767px) { .lines-layout { grid-template-columns: 1fr; } }

/* "Options to Fit Your Home" reuses the same tile-nav pattern, but each
   tile's background is a real material color/photo per category, matching
   live -- not a color gradient like the Lines tiles above. */
.opt-tile--handles  { background: #2c2c2c; }
.opt-tile--species  { background: #999 url("https://cdn.prod.website-files.com/5cb62ed8577ebe3b6d03de1a/5cca3a4755351252ebefa945_Screen%20Shot%202019-05-01%20at%205.29.59%20PM.webp") center / cover no-repeat; text-shadow: 0 1px 4px rgba(0,0,0,.6); }
.opt-tile--cladding { background: #fff url("https://cdn.prod.website-files.com/5cb62ed8577ebe3b6d03de1a/5cd25b87bd7d8b7221ea5200_Screen%20Shot%202019-05-07%20at%209.30.34%20PM.webp") center / 125px repeat; }
.opt-tile--stains   { background: #333 url("https://cdn.prod.website-files.com/5cb62ed8577ebe3b6d03de1a/5cca3a49f1988ce9dfa785d6_Screen%20Shot%202019-05-01%20at%205.30.48%20PM.webp") center / cover no-repeat; text-shadow: 0 1px 4px rgba(0,0,0,.6); }
.opt-tile--colors   { background: #868686; }
.opt-tile--doors    { background: #333; }

/* "Available In" as plain icons with a real hover tooltip (a small blue
   label bubble), matching live -- not the labeled pill list used elsewhere
   on this page. */
/* "Available In:" label -- matches live's real .available-in-text. */
.avail-icons-label { display: block; font-family: var(--display); font-weight: 300; font-size: 1rem; color: #333; margin: 0 0 10px; }
.avail-icons + .avail-icons-label, .avail-icons-label + .avail-icons { margin-top: 0; }
/* No boxed/bordered icons here -- matches live, which shows plain icons
   with no border or background, just a hover tooltip. */
/* Five configuration icons per line, at any screen width. Live's icon list
   sits in a fixed ~390px column where exactly five fit (56px items, 16px
   gaps); ours is in a column that grows with the viewport, so a flex-wrap
   row fitted ten across on a wide screen. A five-track grid holds the count
   steady instead of letting it depend on the container's width.
   The sixth track exists only for the Visible Sash tile, which is placed
   into it explicitly below -- grid places definitely-positioned items
   first, so the icons then auto-flow through tracks 1-5 only. */
.avail-icons { display: flex; align-items: flex-start; gap: 16px; }
/* The icons live in their own five-track grid so the count per line is fixed
   rather than depending on how wide the panel happens to be. The sash tile is
   a sibling of that grid, not a cell in it -- same structure live uses, and
   the reason it stays put while the icons wrap beneath it. */
.avail-icons__grid { display: grid; grid-template-columns: repeat(5, max-content); align-items: center; gap: 16px; }
/* Pushed to the far right of the icon row, as live spaces it -- the wide gap
   is what stops it reading as another configuration option. */
.avail-sash--inline { padding: 0; margin-left: auto; padding-left: 24px; gap: 6px; }
.avail-sash--inline span { font-size: .82rem; }
.avail-icons img { width: 44px; height: 44px; }
.avail-icons a { position: relative; display: flex; align-items: center; justify-content: center; }
.avail-icons a .icon-tip {
  position: absolute; bottom: calc(100% + 8px); left: 50%; transform: translateX(-50%);
  width: auto; height: auto; padding: 4px 10px; border: none; border-radius: 5px;
  background: var(--blue); color: #fff; font-size: .72rem; font-weight: 600; line-height: 1.4;
  white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity .15s var(--ease);
  box-shadow: 0 2px 8px rgba(0,0,0,.18); z-index: 1;
}
.avail-icons a .icon-tip::after {
  content: ""; position: absolute; top: 100%; left: 50%; transform: translateX(-50%);
  border: 5px solid transparent; border-top-color: var(--blue);
}
.avail-icons a:hover .icon-tip, .avail-icons a:focus-visible .icon-tip { opacity: 1; }
/* Visible Sash feature -- its own narrow column at the end of
   .product-line__top on desktop (real sizes: a 75px bordered icon box,
   16px label), matching live. */
.avail-sash { display: flex; flex-direction: column; align-items: flex-start; justify-content: center; gap: 10px; padding: 0; }
.avail-sash img { width: 75px; height: 75px; border: 1px solid var(--ink); padding: 14px; box-sizing: border-box; }
.avail-sash span { font-size: 1rem; color: var(--ink); white-space: nowrap; }
/* Stacked panel below 991: this block must sit AFTER the .avail-sash base
   rule above, or the base's flex-direction:column outranks it by order. */
@media (max-width: 991px) {
  .product-line__top { grid-template-columns: 1fr; gap: 20px; }
}
/* Fewer icon tracks on small screens so five 44px icons plus the sash tile
   cannot overflow a phone-width panel. */
@media (max-width: 767px) { .avail-icons__grid { grid-template-columns: repeat(4, max-content); } }
@media (max-width: 479px) { .avail-icons__grid { grid-template-columns: repeat(3, max-content); } }
.avail { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 1.2rem; }
.avail__label { width: 100%; font-size: .72rem; text-transform: uppercase; letter-spacing: .12em; color: var(--muted); font-family: var(--display); font-weight: 700; margin-bottom: 2px; }
.avail a { font-size: .78rem; padding: 6px 12px; border: 1px solid var(--line-strong); border-radius: 50px; color: var(--body); transition: all .2s var(--ease); }
.avail a:hover { border-color: var(--blue); color: var(--blue); }
/* Same pill, but for a configuration that doesn't have its own page yet
   (e.g. Casement) -- no link, no hover state. */
.avail__unlinked { font-size: .78rem; padding: 6px 12px; border: 1px dashed var(--line-strong); border-radius: 50px; color: var(--muted); }

.spec-strip { display: grid; grid-template-columns: repeat(6,1fr); gap: 16px; padding-top: 30px; color: var(--ink); }
.spec { padding: 22px 16px; text-align: center; }
.spec__num { font-family: var(--display); font-weight: 800; font-size: 1.9rem; color: var(--blue); line-height: 1; }
.spec__label { font-size: .68rem; text-transform: uppercase; letter-spacing: .08em; color: var(--ink); margin-top: 8px; line-height: 1.3; }

/* Stat row (light) */
.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px,1fr)); gap: 20px; }
.stat { text-align: center; }
.stat__num { font-family: var(--display); font-weight: 800; font-size: clamp(2rem,4vw,3rem); color: var(--blue); line-height: 1; letter-spacing: -.02em; }
.stat__label { font-size: .8rem; color: var(--muted); margin-top: 8px; text-transform: uppercase; letter-spacing: .06em; }

/* =========================================================================
   TABS (options sections)
   ========================================================================= */
.tabs__nav { display: flex; flex-wrap: wrap; gap: 4px; border-bottom: 1px solid var(--line); margin-bottom: 32px; }
/* Portfolio's filter row is centred to sit under its centred heading. Live
   left-aligns both, but this page no longer has the hero image above them
   that anchored that alignment.

   "safe" matters here: below 768 this nav is a nowrap overflow-x scroller,
   and plain `center` on an overflowing scroller pushes the leading items to
   a negative offset that scrolling cannot reach -- "All", the default filter,
   ended up 200px off the left edge and unreachable. `safe center` falls back
   to flex-start whenever the row overflows, and browsers that do not know the
   keyword drop the declaration and get the same left-aligned result. */
#portfolio-filter { justify-content: safe center; }
/* Matches live's real portfolio filter nav -- the only page using this
   base (unmodified) tabs__btn look; every other .tabs__nav usage adds a
   more specific modifier (lines-tiles, subtabs) that overrides this. */
.tabs__btn {
  font-family: var(--body-font); font-weight: 400; font-size: 14px; color: #333;
  padding: 14px 18px; border-bottom: 2px solid transparent; transition: all .2s var(--ease);
}
.tabs__btn:hover { color: var(--ink); }
.tabs__btn.is-active { color: #333; border-bottom-color: transparent; text-decoration: underline; }
/* The clad-wood-lines tile nav reuses .tabs__btn for its click/active
   behavior, but needs its own look -- white text throughout (even
   active), no underline. Beats the rules above on specificity. */
.lines-tiles.tabs__nav { display: grid; grid-template-columns: repeat(2, 1fr); gap: 2px; border-bottom: none; margin-bottom: 0; }
.lines-tiles .tabs__btn { padding: 0; border-bottom: none; white-space: normal; flex: initial; }
.lines-tiles .tabs__btn, .lines-tiles .tabs__btn:hover, .lines-tiles .tabs__btn.is-active { color: #fff !important; }
.tabs__panel { display: none; }
.tabs__panel.is-active { display: block; animation: fade .35s var(--ease); }
@keyframes fade { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: none; } }

/* Sub-tabs: a second, smaller pill-style toggle nested inside a tabs__panel
   (e.g. Modern/Classic door styles, or a color-tier picker). */
.subtabs__nav { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 24px; }
.subtabs__btn { font-family: var(--display); font-weight: 600; font-size: .85rem; padding: 10px 18px; border: 1px solid var(--line-strong); border-radius: 50px; color: var(--body); background: #fff; transition: all .2s var(--ease); }
.subtabs__btn:hover { border-color: var(--blue); color: var(--blue); }
.subtabs__btn.is-active { background: var(--ink); border-color: var(--ink); color: #fff; }
/* Resources page only: square corners on the two drawing-map tab buttons,
   per Ellie. The pill shape stays on the shared subtabs used by the
   aluminum/clad-wood/historic option selectors. */
/* Resources page only: square corners and a black 1px border on the two
   drawing-map buttons. Both live and ours are 1px, so what read as a
   lighter line weight was the colour -- ours inherited the shared pale
   grey (--line-strong, #cfd4d9) while live draws them in black. The pill
   subtabs on aluminum/clad-wood/historic keep the grey. */
#detail-sets .subtabs__btn { border-radius: 0; border-color: #000; }
.subtabs__panel { display: none; }
.subtabs__panel.is-active { display: block; animation: fade .35s var(--ease); }

.swatch-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(120px,1fr)); gap: 16px; }
.swatch { text-align: center; }
.swatch__chip { aspect-ratio: 1; border-radius: var(--r); overflow: hidden; border: 1px solid var(--line); box-shadow: var(--shadow-sm); }
.swatch__chip img { width: 100%; height: 100%; object-fit: cover; }
.swatch__name { font-size: .82rem; font-weight: 600; color: var(--ink); margin-top: 8px; }
.swatch__code { font-size: .72rem; color: var(--muted); }

.option-card { text-align: center; }
.option-card__img { aspect-ratio: 1; border-radius: var(--r); overflow: hidden; border: 1px solid var(--line); background: var(--paper-alt); }
.option-card__img img { width: 100%; height: 100%; object-fit: contain; padding: 10px; }
.option-card h4 { font-size: 1rem; margin-top: 12px; }
.option-card p { font-size: .85rem; color: var(--muted); margin-top: 4px; }

/* =========================================================================
   GALLERY + LIGHTBOX
   ========================================================================= */
.gallery { columns: 3; column-gap: 16px; }
.gallery img { width: 100%; border-radius: var(--r); margin-bottom: 16px; cursor: zoom-in; transition: filter .25s var(--ease), transform .25s var(--ease); break-inside: avoid; }
.gallery img:hover { filter: brightness(1.04); }

.gallery-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px,1fr)); gap: 16px; grid-auto-flow: dense; }
.gallery-grid figure { position: relative; overflow: hidden; border-radius: var(--r); cursor: zoom-in; }
.gallery-grid img { width: 100%; height: 100%; object-fit: cover; aspect-ratio: 4/3; transition: transform .5s var(--ease); }
.gallery-grid figure:hover img { transform: scale(1.05); }
.gallery-grid .tall { grid-row: span 2; }
.gallery-grid .tall img { aspect-ratio: 3/4; }
.gallery-grid figcaption { position: absolute; left: 0; right: 0; bottom: 0; padding: 28px 18px 14px; color: #fff; font-size: .82rem; background: linear-gradient(transparent, rgba(0,0,0,.6)); opacity: 0; transition: opacity .25s var(--ease); }
.gallery-grid figure:hover figcaption { opacity: 1; }

/* Fixed-order gallery layout (3 small / 1 wide / 2 half / 3 small), for
   pages that need to match a specific live row order rather than
   .gallery-grid's auto-packed masonry. Currently used on historic.html. */
.gallery-fixed { display: grid; grid-template-columns: repeat(6, 1fr); gap: 16px; }
.gallery-fixed figure { position: relative; overflow: hidden; border-radius: var(--r); grid-column: span 2; margin: 0; cursor: zoom-in; }
.gallery-fixed img { width: 100%; height: 100%; object-fit: cover; aspect-ratio: 4/3; display: block; transition: transform .5s var(--ease); }
.gallery-fixed figure:hover img { transform: scale(1.05); }
.gallery-fixed .wide { grid-column: span 6; }
.gallery-fixed .wide img { aspect-ratio: 21/9; }
.gallery-fixed .half { grid-column: span 3; }
@media (max-width: 767px) {
  .gallery-fixed { grid-template-columns: repeat(2, 1fr); }
  .gallery-fixed figure, .gallery-fixed .wide, .gallery-fixed .half { grid-column: span 2; }
}

.lightbox { position: fixed; inset: 0; z-index: 3000; background: rgba(10,12,16,.92); display: none; align-items: center; justify-content: center; padding: 4vw; }
.lightbox.open { display: flex; }
.lightbox img { max-width: 92vw; max-height: 88vh; border-radius: var(--r); box-shadow: var(--shadow-lg); }
.lightbox__close { position: absolute; top: 20px; right: 24px; width: 48px; height: 48px; color: #fff; font-size: 2rem; line-height: 1; }
.lightbox__nav { position: absolute; top: 50%; transform: translateY(-50%); width: 56px; height: 56px; color: #fff; font-size: 2.4rem; background: rgba(255,255,255,.08); border-radius: 50%; display: flex; align-items: center; justify-content: center; }
.lightbox__nav:hover { background: rgba(255,255,255,.16); }
.lightbox__prev { left: 18px; } .lightbox__next { right: 18px; }

/* =========================================================================
   TESTIMONIAL / QUOTE
   ========================================================================= */
/* Full-width photo-background quote band. Values matched to live's computed
   styles: border-radius 14px, dark overlay rgba(0,0,0,.5) by default, white
   32px heading, white italic 21px blockquote. The blockquote font-family is
   var(--body-font) ("Futura Book", a real weight-400 face) rather than
   var(--display) ("Futura", which only has 300/700 registered) -- requesting
   weight 400 on --display made the browser fall back to the 300 face, which
   read noticeably lighter than live's real text. */
.quote-band { position: relative; border-radius: var(--r-lg); overflow: hidden; display: flex; align-items: center; justify-content: center; }
.quote-band__bg { position: absolute; inset: 0; z-index: 0; background: var(--ink-soft); }
.quote-band__bg img { width: 100%; height: 100%; object-fit: cover; }
.quote-band::after { content: ""; position: absolute; inset: 0; background: rgba(0,0,0,.5); z-index: 1; }
/* Darker overlay variant -- live's real WaPo pull-quote on the homepage
   measures rgba(0,0,0,.64), a bit heavier than the .5 default above. Live
   also crops that photo from the top (background-position 0 0), not
   centered, so this variant pins the img the same way. */
.quote-band--dark::after { background: rgba(0,0,0,.64); }
.quote-band--dark .quote-band__bg img { object-position: top; }
.quote-band__inner { position: relative; z-index: 2; max-width: 780px; text-align: center; padding: clamp(56px,8vw,100px) clamp(24px,5vw,60px); }

.quote-band__inner { position: relative; }
.quote-band__mark {
  position: absolute; top: clamp(20px, 4vw, 46px); left: clamp(24px, 5vw, 60px);
  font-size: 2.5em; line-height: 1; color: #fff; opacity: .5;
  font-family: var(--heading); pointer-events: none;
}
.quote-band__inner h2 { color: #fff; font-size: 32px; margin-bottom: 24px; }
.quote-band__inner blockquote { font-family: var(--body-font); font-weight: 400; font-style: italic; font-size: 21px; line-height: 1.5; color: #fff; margin: 0 0 20px; }
.quote-band__inner .credit { display: block; margin-bottom: 1.5rem; color: rgba(255,255,255,.85); }
/* Phone: .quote-band is a flex row and its __inner shrank to fit content,
   settling at 228px inside a 327px band -- which broke the heading onto
   four lines and the quote into three-word ribbons. Give it the full width
   so the centred text uses the whole picture. */
/* The quote band's photo credit lives just outside the band now, so that on a
   phone it can sit under the picture instead of over it -- pinned to the
   bottom-right it ran across the View Our Idea Book button. The wrapper is
   the positioning context that keeps desktop's overlay exactly as it was. */
.quote-band-wrap { position: relative; }
@media (max-width: 767px) {
  .quote-band__inner { width: 100%; padding: 40px 20px; }
  .quote-band__inner h2 { font-size: 26px; }
  .quote-band__inner blockquote { font-size: 17px; }
  /* Below the picture, in the page's own dark text, matching the hero
     credits and the .photo-credit rows elsewhere. */
  .quote-band-wrap > .hero__credit {
    position: static; display: flex; flex-wrap: wrap; justify-content: flex-end;
    gap: 10px; margin: 8px 0 0; padding: 0; text-align: left;
    font-size: .8rem; color: var(--ink); line-height: 1.35;
  }
  .quote-band-wrap > .hero__credit strong { display: inline; font-family: var(--display); text-transform: none; letter-spacing: normal; font-size: .8rem; font-weight: 700; color: var(--ink); }
  .quote-band-wrap > .hero__credit span { font-size: .8rem; color: var(--ink); padding-left: 10px; border-left: 1px solid var(--ink); margin-left: 0; }
}

/* Full-bleed photo band with a text panel in the corner (no dark overlay --
   matched to live, where the panel just sits over the sky in the photo).
   Used on firewall.html's UV Protection section.
   Background-image at native size (not object-fit:cover on an <img>) --
   the real photo is 2100x1400 (1.5:1), narrower than this band's own
   1280x700-ish box (~1.83:1), so object-fit:cover always fits the full
   image width with zero horizontal crop room, which left the left edge
   (and the curtain on it) visible regardless of object-position. Live
   crops with a plain oversized background-image instead, which actually
   has room to crop horizontally. */
/* Live puts this banner inside a centered 1440px container (.container-large),
   not full-bleed -- at 1920 that leaves 240px margins each side. Below 1440
   it spans the full width, same as live's no-padding wrapper. */
.uv-band { position: relative; height: 700px; overflow: hidden; background-repeat: no-repeat; background-position: 70% 50%; background-size: auto; max-width: 1440px; margin-inline: auto; }
/* Matches live's real measured offsets (27px/24px/290px), tighter to the
   corner than the earlier clamp()-based guess. */
.uv-band__panel { position: absolute; top: 27px; right: 24px; max-width: 290px; }
.uv-band__panel h2 { margin-bottom: .5rem; }
.uv-band__panel p { margin-bottom: 1.25rem; }
@media (max-width: 767px) {
  /* background-size:auto paints this 2100px-wide photo at full size, so a
     375px phone showed roughly 18% of its width -- a zoomed slice that put
     a window mullion straight behind the text. On a phone the band now
     reserves exactly the photo's own height (padding-top 66.667% = 1400/2100
     of the width) and scales the photo to fit it, so the whole frame shows
     uncropped, and the text sits below the picture instead of on top of it,
     where at this width it covered most of it anyway. */
  .uv-band { height: auto; padding-top: 66.667%; background-size: contain; background-position: 50% 0; }
  .uv-band__panel { position: static; padding: 24px; max-width: none; }
}

/* Dark full-bleed photo blog cards (why-zola.html's "From the Blog") --
   distinct from the plain .card "From the Blog" grid used elsewhere:
   title/summary sit directly on the photo with a gradient scrim, on a
   pure black section (#000, not --ink-soft's navy -- matched to live). */
.blog-band { background: #000; }
.blog-band h2 { color: #fff; }
.blog-card { position: relative; display: block; border-radius: var(--r); overflow: hidden; aspect-ratio: 4/3; }
.blog-card img { width: 100%; height: 100%; object-fit: cover; transition: transform .5s var(--ease); }
.blog-card:hover img { transform: scale(1.05); }
.blog-card::after { content: ""; position: absolute; inset: 0; background: linear-gradient(transparent 40%, rgba(0,0,0,.85)); }
.blog-card__body { position: absolute; left: 0; right: 0; bottom: 0; z-index: 1; padding: 22px 20px; }
.blog-card__body h3 { color: #fff; font-size: 1.05rem; margin-bottom: 6px; }
.blog-card__body p { color: rgba(255,255,255,.85); font-size: .85rem; line-height: 1.4; }

.testimonial-card { background: #fff; border: 1px solid var(--line); border-radius: var(--r-lg); padding: clamp(28px,3vw,44px); }
.testimonial-card p { font-size: 1.15rem; font-style: italic; color: var(--ink); line-height: 1.5; }
.testimonial-card cite { display: block; margin-top: 1.2rem; font-style: normal; font-weight: 700; font-family: var(--display); color: var(--blue); font-size: .9rem; }

/* Video testimonial cards */
.vid-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px,1fr)); gap: 20px; }
/* Quotes run different numbers of lines per card. The grid's default
   align-items:stretch already makes every .vid-card the same height (the
   tallest card in the row); flex:1 on the quote lets it absorb that extra
   space instead of the frame, so every frame's top edge still lines up. */
.vid-card { display: flex; flex-direction: column; }
.vid-card__frame { position: relative; aspect-ratio: 16/10; border-radius: var(--r); overflow: hidden; background: #000; }
.vid-card__frame iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.vid-card__quote { flex: 1; font-family: var(--display); font-weight: 600; font-style: italic; font-size: 1.02rem; color: var(--ink); margin-bottom: 14px; line-height: 1.3; }

/* =========================================================================
   SHOWROOM CARDS
   ========================================================================= */
.showroom__media img { width: 100%; border-radius: var(--r); box-shadow: var(--shadow); aspect-ratio: 4/3; object-fit: cover; }
.showroom__place { font-family: var(--display); text-transform: uppercase; letter-spacing: .08em; color: var(--blue); font-weight: 700; font-size: .8rem; margin-bottom: .5rem; }
.showroom__addr { font-family: var(--display); font-weight: 700; color: var(--ink); font-size: 1.05rem; margin-top: 1rem; }
.showroom__addr a { color: var(--ink); }

/* =========================================================================
   FORMS
   ========================================================================= */
.form { display: grid; gap: 18px; }
.form-row { display: grid; gap: 18px; grid-template-columns: 1fr 1fr; }
.field { display: flex; flex-direction: column; gap: 7px; }
.field label { font-family: var(--display); font-weight: 600; font-size: .82rem; color: var(--ink); letter-spacing: .02em; }
.field .req { color: var(--blue); }
.field input, .field select, .field textarea {
  font: inherit; font-size: .98rem; padding: 13px 15px;
  border: 1px solid var(--line-strong); border-radius: var(--r-sm); background: #fff; color: var(--ink);
  transition: border-color .2s var(--ease), box-shadow .2s var(--ease);
  width: 100%;
}
.field input:focus, .field select:focus, .field textarea:focus { outline: none; border-color: var(--blue); box-shadow: 0 0 0 3px var(--blue-050); }
.field textarea { resize: vertical; min-height: 130px; }
.form__note { font-size: .85rem; color: var(--muted); }
.form-success { display: none; padding: 18px 20px; border-radius: var(--r); background: var(--blue-050); border: 1px solid var(--blue); color: var(--blue-900); font-weight: 600; }
.form-success.show { display: block; }

/* =========================================================================
   CTA BANDS
   ========================================================================= */
.cta-band { text-align: center; }
.cta-band h2 { max-width: 18ch; margin-inline: auto; }
.cta-band p { max-width: 52ch; margin: 1rem auto 2rem; }
.cta-band__actions { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; }
.cta-split { display: grid; grid-template-columns: 1.1fr 1fr; align-items: center; gap: clamp(28px,5vw,72px); }

/* simple feature list */
.feature-list { display: grid; gap: 22px; }
.feature { display: flex; gap: 16px; }
.feature__icon { flex: 0 0 44px; width: 44px; height: 44px; border-radius: 50%; background: var(--blue-050); color: var(--blue); display: flex; align-items: center; justify-content: center; }
.feature__icon svg { width: 22px; height: 22px; }
.feature h4 { margin-bottom: .3rem; }
.feature p { font-size: .95rem; color: var(--muted); }

/* logo / value strip */
.value-strip { display: grid; grid-template-columns: repeat(auto-fit,minmax(180px,1fr)); gap: 1px; background: var(--line); border: 1px solid var(--line); border-radius: var(--r); overflow: hidden; }
.value-strip > div { background: #fff; padding: 28px 24px; text-align: center; }
.value-strip__num { font-family: var(--display); font-weight: 800; font-size: 2.2rem; color: var(--blue); }
.value-strip__label { font-size: .85rem; color: var(--muted); margin-top: 6px; }

/* =========================================================================
   FOOTER
   ========================================================================= */
/* Footer ground is pure black to match live (.footer on zolawindows.com is
   rgb(0,0,0), a single flat colour with no inner panels). Deliberately not
   var(--blue) -- that is the brand navy #003d76 this footer used to use. */
.site-footer { background: #000; color: #fff; padding-block: clamp(48px,7vw,84px) 32px; }
.footer-layout { display: flex; align-items: flex-start; gap: clamp(24px, 4vw, 48px); }
.footer-logo { flex: 0 0 auto; }
.footer-logo img { width: 90px; height: auto; display: block; }
/* Badge is the footer's right-hand column, as on live (.footer-luxury-logo:
   ~200px, flex sibling after the content). Self-aligned to the row's end so
   it sits low, per Ellie. */
.footer-badge { flex: 0 0 auto; width: 200px; max-width: 100%; border-radius: 10px; align-self: flex-end; }
@media (max-width: 767px) { .footer-badge { align-self: center; width: 140px; } }
.footer-main { flex: 1 1 auto; min-width: 0; }
.footer-cta { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 20px; padding-block: 24px; border-bottom: 1px solid rgba(255,255,255,.18); }
.footer-cta__text { color: #fff; font-size: 1rem; margin: 0; text-align: right; }
.footer-bottom { display: flex; flex-wrap: wrap; gap: 24px; justify-content: space-between; align-items: center; padding-top: 32px; }
.footer-nav { display: flex; flex-wrap: wrap; gap: 8px 26px; }
.footer-nav a { font-family: var(--display); font-weight: 600; text-transform: uppercase; letter-spacing: .06em; font-size: .76rem; color: rgba(255,255,255,.9); }
.footer-nav a:hover { color: #fff; text-decoration: underline; text-underline-offset: 3px; }
/* Centred at the very bottom, live-style: plain 25px white glyphs at a 25px
   gap (live's .footer-link_wrapper / .footer-social_link-block), not the old
   bordered circles on the right of the nav row. */
.footer-social { display: flex; justify-content: center; gap: 25px; margin-top: 40px; }
.footer-social a { width: 25px; height: 25px; color: #fff; display: flex; align-items: center; justify-content: center; transition: opacity .25s var(--ease); }
.footer-social a:hover { opacity: .7; }
.footer-social svg { width: 25px; height: 25px; }
.footer-meta { width: 100%; margin-top: 24px; padding-top: 20px; border-top: 1px solid rgba(255,255,255,.14); display: flex; flex-wrap: wrap; gap: 16px; justify-content: space-between; font-size: .76rem; color: rgba(255,255,255,.7); }
.footer-meta a { color: rgba(255,255,255,.7); }
.footer-meta a:hover { color: #fff; }

/* =========================================================================
   MISC
   ========================================================================= */
.divider { height: 1px; background: var(--line); border: 0; margin-block: clamp(40px,6vw,80px); }
.pill { display:inline-block; padding: 5px 13px; border-radius: 50px; background: var(--blue-050); color: var(--blue); font-size: .74rem; font-weight: 700; font-family: var(--display); text-transform: uppercase; letter-spacing: .08em; }
.note-card { background: var(--blue-050); border-left: 3px solid var(--blue); border-radius: var(--r-sm); padding: 18px 22px; font-size: .95rem; }
.prose p { margin-bottom: 1.2em; }
.prose h2 { margin-top: 1.6em; margin-bottom: .5em; }
.prose h3 { margin-top: 1.4em; margin-bottom: .4em; }
.prose ul { display: grid; gap: .5em; margin: 0 0 1.2em; padding-left: 1.2em; list-style: disc; }
.prose ul li { padding-left: .2em; }

/* Guide/document pages (e.g. adjustment-care.html): a bold lead-in paragraph,
   then sections each separated by a hairline above. Mirrors the live site's
   "top-line-block" layout for these plain maintenance/reference documents. */
.doc-intro { font-size: 18px; line-height: 24px; font-weight: 700; color: var(--ink); }
.doc-block { border-top: 1px solid var(--line); margin-top: 35px; padding-top: 40px; }
.doc-block > h2:first-child { margin-top: 0; }

/* showrooms.html -- live lays each location out as a 1/4 + 3/4 row: a square
   200px photo with a 20px radius, then an uppercase location title and its
   details. Blocks are separated by the same hairline as .doc-block (live reuses
   its own top-line-block here), except the first, which live marks "no-line".
   Values measured off live at 1280. Stacks below 767, as live does. */
.doc-block--no-line { border-top: 0; margin-top: 0; padding-top: 0; }
.showroom { display: grid; grid-template-columns: 240px 1fr; column-gap: 0; align-items: start; }
.showroom__photo img { width: 200px; height: 200px; object-fit: cover; border-radius: 20px; display: block; }
.showroom__title { font-family: var(--body-font); font-weight: 400; font-size: 15px; line-height: 18px; color: #333; text-transform: uppercase; letter-spacing: 0; margin: 0 0 14px; }
.showroom__body p { font-family: var(--display); font-weight: 300; font-size: 16px; line-height: 25px; color: #333; margin: 0 0 1em; }
.showroom__body p:last-child { margin-bottom: 0; }
.showroom__body a { color: #333; text-decoration: underline; text-underline-offset: 3px; }
/* Addresses and phone numbers are <strong> in live's copy, but the surrounding
   paragraph is Futura Light 300, so the browser's default "bolder" only lands on
   400 -- which renders as the 300 face and looks unbolded. Live computes these
   at 600; the Futura we ship has 300 and 700, so 700 is the closest face. */
.showroom__body strong { font-weight: 700; }
@media (max-width: 767px) {
  .showroom { grid-template-columns: 1fr; row-gap: 20px; }
}

/* about-us.html -- live alternates five two-column blocks, each a 538px image
   beside its text, 64px gutter, images cover at 6px radius. Subheads inside a
   block (Innovation / Expertise / Service, and the four under "What You Get")
   are Futura Book 24/30 semibold. The closing CTA uses live's lighter blue
   (#1269b0), not the brand navy var(--blue) the rest of our buttons use. */
.about-block { margin-top: clamp(48px, 6vw, 80px); }
.about-block:first-of-type { margin-top: 0; }
.about-block .split__media img { height: auto; border-radius: 6px; }
/* Deliberate divergence from live. Live stretches each of these photos to the
   height of its text column and crops the overflow with object-fit:cover, so
   the two longest blocks ("How We Do It", "What You Get") squeeze a 3:2 photo
   into a 538x619 and a 538x754 box -- taller than they are wide, showing a
   narrow vertical slice of the shot. Ellie asked for them uncropped.

   So the photo keeps its own proportions and is centred against the text
   instead of stretched to it. That leaves white space beside the longer
   blocks, which is the trade for showing the whole photograph.

   height:auto also has to beat .split--live's height:100%, and aspect-ratio
   has to be cleared, or the image is given a definite height again and
   object-fit:cover starts cropping. Below 768 live lets each photo keep its
   own proportions already, and the fixed heights .split--live applies on
   shipping-delivery must not leak in here. */
@media (min-width: 768px) {
  .about-block .split__media { align-self: center; }
  .about-block .split__media img { height: auto; aspect-ratio: auto; }
}
.prose.split__body--live h3 { font-family: var(--body-font); font-weight: 700; font-size: 24px; line-height: 30px; color: #333; margin: 20px 0 10px; }
.prose.split__body--live h3:first-of-type { margin-top: 0; }
.about-cta { margin-top: clamp(48px, 6vw, 80px); text-align: center; }


/* passive-house.html -- pieces live has that the shared vocabulary lacked:
   a photo credit under each image, the PHI/PHIUS certification logos, the
   pull-quote, and the five Passive House Method principles (live writes each
   as a bold name followed by an italic description). */
/* passive-house.html hero. Live centres a 500px content column in the hero
   (text left-aligned inside it, the CTA spanning its full width) and pins the
   "Available In:" material tiles to the bottom right of the image. Those tiles
   use the white "Alternate" icon cut, which is what that cut is for -- they sit
   over a dark photo here. Measured off live at 1280: column 500px, h1 38px,
   label 16px/300 white, each tile 92px wide with a 72x102 icon frame. */
/* .page-hero is a flex row, so its __inner shrinks to content width; the hero
   column can only centre against the full hero once the inner fills it. */
.page-hero--ph .page-hero__inner { width: 100%; }
.ph-hero__content { width: 500px; max-width: 100%; margin-inline: auto; }
.ph-hero__content h1 { max-width: none; }
.ph-hero__content p { max-width: none; }
.ph-hero__content .hero__cta { margin-top: 1.4rem; }
.ph-hero__content .hero__cta .btn { width: 100%; justify-content: center; }
.ph-hero__materials { position: absolute; z-index: 3; right: clamp(16px, 3vw, 40px); bottom: clamp(16px, 2.5vw, 32px); display: flex; align-items: flex-end; gap: 14px; }
.ph-hero__avail { font-family: var(--display); font-weight: 300; font-size: 16px; color: #fff; padding-bottom: 6px; white-space: nowrap; }
.ph-hero__tiles { display: flex; gap: 6px; }
.ph-hero__tiles a { display: flex; flex-direction: column; align-items: center; gap: 6px; width: 92px; color: #fff; font-family: var(--display); font-weight: 300; font-size: .82rem; text-align: center; }
.ph-hero__tiles a:hover { opacity: .8; }
.ph-hero__frame { display: flex; align-items: center; justify-content: center; width: 72px; height: 102px; }
.ph-hero__frame img { max-width: 100%; max-height: 100%; }
@media (max-width: 991px) {
  /* Stacked under the hero copy: label on its own line and the tiles wrapping,
     otherwise the label's nowrap plus four tiles overflows the hero at 390.
     .page-hero is a flex ROW, so the materials block has to be told to stack
     or it lands beside the hero content instead of under it. */
  .page-hero--ph { flex-direction: column; justify-content: center; }
  /* Stacked below the photo now, so the white type these use over the
     image would be invisible on the page ground. */
  .ph-hero__materials { position: static; flex-direction: column; align-items: flex-start; gap: 10px; padding: 0 var(--gutter) 28px; }
  .ph-hero__avail { padding-bottom: 0; color: #333; }
  .ph-hero__tiles a { color: #333; }
  .ph-hero__tiles { flex-wrap: wrap; justify-content: center; gap: 6px 10px; }
  .ph-hero__tiles a { width: 76px; }
  .ph-hero__frame { width: 58px; height: 82px; }
}
.ph-figure { margin: 0; }

/* Configurations row: live draws no card around each item -- just the line
   drawing and its label. Measured off live at 1280: each item 181x155 with a
   125px icon and a 14px label, the row centred and wrapping. */
/* Live puts this block on a light grey ground and centres the row, so the
   four items on the second line sit under the middle of the seven above them.
   That centring is why this is flex rather than a 7-column grid -- a grid
   left-aligns an incomplete final row. Sizing each item to exactly a seventh
   of the row keeps live's 7-then-4 break without relying on tile width. */
/* This page alternates white and a light grey down the page; live's grey is
   #f5f5f5, not our --paper-alt (#f5f6f8). Applied per section rather than
   with .section--alt so the sequence is explicit in the markup. */
.ph-alt { background: #f5f5f5; }

/* The lines panel spacing itself now lives in the base .product-line rules --
   reviewed on passive-house (PR #57), rolled out to all five pages after.
   .ph-configs itself also now applies to the Configurations sections on
   clad-wood, wood, aluminum and upvc -- same live layout on every product
   page, so the smaller icon-tiles--plain treatment there was replaced. */
.ph-configs { gap: 0; justify-content: center; }
.ph-configs .icon-tile { flex: 0 1 181px; padding: 0; border: 0; border-radius: 0; background: none; gap: 8px; font-weight: 400; font-size: 14px; }
/* Seven across on desktop as live has it; fewer columns below that so the
   181px tiles do not drop to one per row on a phone. Explicit widths rather
   than flex-basis: a percentage basis did not override the 181px above it. */
@media (min-width: 1100px) { .ph-configs .icon-tile { flex: 0 0 auto; width: calc(100% / 7); } }
@media (max-width: 1099px) { .ph-configs .icon-tile { flex: 0 0 auto; width: calc(100% / 4); } }
@media (max-width: 767px)  { .ph-configs .icon-tile { width: calc(100% / 3); } }
@media (max-width: 479px)  { .ph-configs .icon-tile { width: calc(100% / 2); } }
.ph-configs .icon-tile img { height: 125px; }
/* Live's config row is full-bleed (its wrapper spans the whole viewport, no
   container cap) and steps the icons up to 175px from 1440px wide -- measured
   off live aluminum at 1920: wrapper 1905px, tiles 272px (1/7), icons 175x175.
   The row now sits OUTSIDE .container in the markup on all five product
   pages, so only the icon bump lives here. */
@media (min-width: 1440px) { .ph-configs .icon-tile img { height: 175px; } }
.ph-configs .icon-tile:hover { border-color: transparent; box-shadow: none; transform: none; color: var(--blue); }

/* Section headings on this page are 32/36 on live, not the 46/44 that
   .split__body--live applies for shipping-delivery's .section-title. The lines
   selector's own heading is smaller again at 24/30. */
.page-ph .prose.split__body--live h2 { font-size: 32px; line-height: 36px; }
.ph-lines-head h2 { font-size: 24px; line-height: 30px; }

/* These photos are shown whole on live, not cropped. .split--live's
   right-anchored cover crop is more specific, so it has to be named out
   explicitly; height:auto lets the box follow the image's own proportions so
   nothing is cut off and nothing is stretched. Live does stretch these
   slightly (a 1.78 image in a 2.01 box via object-fit:fill); ours keeps the
   photo's real shape instead of reproducing that distortion. */
.split--live .split__media.ph-figure img,
/* The ZNC illustration belongs in the same exception. It is a labelled
   cutaway, so a crop does not just lose scenery -- it cuts the labels off.
   Desktop was fine by luck (the text column happens to land within a pixel
   of the artwork's own 1.02 ratio), but .split--live's fixed phone heights
   forced the 1500x1476 square into a 350x140 letterbox and cover kept only a
   thin strip of its right edge. */
.split--live .split__media--natural img,
.ph-figure img { height: auto; object-fit: fill; object-position: 50% 50%; }
.split--live .ph-figure { align-self: start; }
/* Live renders each credit as one row of parts separated by a 10px gap --
   project name bold, collaborators light, all 12.8px black, no uppercase and
   no pipe characters despite the "pipes" class name in its markup. */
.ph-credit { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 10px; line-height: 1.35; }
.ph-credit span { font-family: var(--display); font-weight: 300; font-size: 12.8px; color: #000; }
.ph-credit span.ph-credit__project { font-weight: 700; }
.ph-cert-logos { display: flex; align-items: center; gap: 20px; margin: 20px 0; }
.ph-cert-logos img { height: 60px; width: auto; }
.ph-quote { margin: 0 0 .4em; padding: 0; border: 0; font-family: var(--display); font-weight: 300; font-size: 20px; line-height: 30px; color: #333; font-style: italic; }
.ph-quote__by { font-weight: 700; }
/* Live writes these as plain disc bullets at 16/20 -- and its own copy only
   pairs the first principle with its description; the rest are separate
   bullets. Reproduced as live has it rather than tidied into pairs. */
.ph-principles { margin: 0 0 1.2em; padding-left: 1.2em; list-style: disc; }
.ph-principles li { font-family: var(--display); font-weight: 300; font-size: 16px; line-height: 20px; color: #333; margin-bottom: .55em; }
.ph-principles em { font-style: italic; }

/* The Request Quote that closes the lines section, centred under the specs. */
.ph-lines-cta { margin-top: clamp(28px, 4vw, 44px); text-align: center; }

/* "Feel the Zola Window difference": live runs the infrared photo full bleed at
   its own proportions, then a dark navy band (#0e1046) under it carrying the
   heading and copy in white across a 1/3 + 2/3 row. Measured off live at 1280:
   band padding 28px 0, heading 32/36 semibold, copy 16/25 light. Live's heading
   is Futura Book at 600; that face only ships at 400 here, so this uses Futura
   at 700 -- the same substitution already made for the showroom addresses. */
.ph-difference__img { display: block; width: 100%; height: auto; }
.ph-difference__band { background: #0e1046; color: #fff; padding: 28px 0; }
.ph-difference__row { display: grid; grid-template-columns: 1fr 2fr; gap: 32px; align-items: start; }
.ph-difference__row h2 { font-family: var(--display); font-size: 32px; line-height: 36px; font-weight: 700; color: #fff; margin: 0; }
.ph-difference__row p { font-family: var(--display); font-size: 16px; line-height: 25px; font-weight: 300; color: #fff; margin: 0; }

/* Blog index, matched to live at 1280. The featured slider is 500px with
   centred white date + 32/36 title over the photo and full-height 80px arrow
   zones. Cards are the photo itself (background, cover) with white title 18/24
   bold and excerpt 12/16 light at the bottom over a dark gradient; grid is
   three 250px-tall columns at a 16px gap, with the lead card spanning 2x2. */
.bp-title { margin-bottom: .4em; }
.bp-subtitle { margin-bottom: 18px; }
/* 'Featured Posts' sits on the slider photo, not above it -- live overlays it
   at the top of the 500px band. Its own class, not .bp-subtitle, because
   'All Posts' further down keeps the plain heading treatment.
   Live: absolute, inset 0 0 auto, margin-top 40px, z-index 5, white,
   FuturaStd 700 at 32px, centred. */
.bp-featured-heading { position: absolute; inset: 0 0 auto; margin-top: 40px; z-index: 5; color: #fff; text-align: center; font-family: var(--display); font-weight: 700; font-size: 32px; }
.bp-slider { position: relative; height: 500px; background: #ddd; overflow: hidden; }
.bp-slide { position: absolute; inset: 0; display: none; align-items: center; justify-content: center; background-size: cover; background-position: 50% 50%; color: #fff; text-align: center; }
.bp-slide.is-active { display: flex; }
.bp-slide__content { max-width: 640px; padding: 0 90px; }
.bp-slide__date { font-family: var(--body-font); font-size: 14px; margin-bottom: 6px; }
.bp-slide h3 { font-size: 32px; line-height: 36px; color: #fff; margin: 0; }
.bp-slide__by { font-family: var(--body-font); font-size: 14px; margin-top: 8px; }
.bp-slider__arrow { position: absolute; top: 0; bottom: 0; width: 80px; border: 0; background: none; color: #fff; font-size: 40px; cursor: pointer; z-index: 2; }
.bp-slider__arrow--prev { left: 0; }
.bp-slider__arrow--next { right: 0; }
.bp-slider__arrow:hover { background: rgba(0,0,0,.12); }
.bp-cats { display: flex; flex-wrap: wrap; gap: 6px 22px; margin-bottom: 24px; }
.bp-cats a { font-family: var(--body-font); font-size: 14px; color: #333; }
.bp-cats a.is-active, .bp-cats a:hover { color: var(--blue); text-decoration: underline; text-underline-offset: 3px; }
.bp-empty { font-family: var(--body-font); font-size: 14px; color: #333; padding: 40px 0 120px; }
.bp-featgrid, .bp-grid { display: grid; grid-template-columns: repeat(3, 1fr); grid-auto-rows: 250px; gap: 16px; }
.bp-featgrid { margin-bottom: 16px; }
.bp-card { position: relative; display: block; overflow: hidden; background-size: cover; background-position: 50% 50%; }
.bp-card--big { grid-column: 1 / 3; grid-row: 1 / 3; }
.bp-card::after { content: ""; position: absolute; inset: 0; background: linear-gradient(transparent 40%, rgba(0,0,0,.85)); }
.bp-card__body { position: absolute; left: 0; right: 0; bottom: 0; z-index: 1; padding: 14px 12px; }
.bp-card__body h4 { font-size: 18px; line-height: 24px; font-weight: 700; color: #fff; margin: 0 0 4px; }
.bp-card__body p { font-family: var(--display); font-size: 12px; line-height: 16px; font-weight: 300; color: #fff; margin: 0; }

/* Blog article bodies (the 18 posts transcribed from live): responsive video
   embeds and in-body figures. */
.post-body figure { margin: 1.6em 0; }
.post-body figure img { max-width: 100%; height: auto; display: block; }
.post-body blockquote { margin: 1.4em 0; padding-left: 1.1em; border-left: 3px solid var(--line-strong); font-style: italic; }
.post-video { position: relative; aspect-ratio: 16 / 9; margin: 1.6em 0; }
.post-video iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
@media (max-width: 767px) {
  .bp-featgrid, .bp-grid { grid-template-columns: 1fr; }
  .bp-card--big { grid-column: auto; grid-row: auto; }
  .bp-slider { height: 380px; }
  .bp-slide h3 { font-size: 24px; line-height: 30px; }
  .bp-slide__content { padding: 0 56px; }
}
@media (max-width: 767px) { .ph-difference__row { grid-template-columns: 1fr; gap: 14px; } }
.reveal { opacity: 0; transform: translateY(24px); transition: opacity .7s var(--ease), transform .7s var(--ease); }
.reveal.in { opacity: 1; transform: none; }

/* =========================================================================
   RESPONSIVE
   ========================================================================= */
/* Breakpoints follow the source site's framework (Webflow): 991 / 767 / 479. */

/* Nav collapses at 1080 (this nav carries six top-level items + a CTA, so it
   needs more room than a default Webflow navbar before going to the drawer). */
@media (max-width: 1080px) {
  .nav__menu { display: none; }
  .nav__toggle { display: flex; }
  .nav__phone { display: none; }
  .mobile-drawer { width: 50vw; }

  /* Below this breakpoint the header is the only thing pinned to the top of
     a small screen, and at desktop's 94px with a 120px logomark it was eating
     a large share of a phone viewport for the whole scroll. Halving the mark
     and shrinking the bar to match buys back ~40px of every screen. Desktop
     keeps live's proportions -- the oversized overhanging mark is the brand
     shape there, and there is no shortage of vertical room for it.

     Set on :root so everything derived from --header-h follows: the drawer's
     top offset, and the negative margins that let the heroes run under the
     header (.hero, .page-hero--config). */
  :root { --header-h: 54px; }
  .nav__logo { height: calc(var(--header-h) + 18px); }
  .nav__logo img { height: 60px; }

  /* Ellie asked for a lot more of the menu on screen at once. Rows were
     18px text in 20px/25px padding -- about 62px each, so only a handful of
     destinations fit a phone before scrolling. At 15px in 13px/20px they are
     ~46px, roughly a third more rows visible, and still comfortably above the
     ~44px minimum for a reliable tap target. */
  .m-nav a,
  .m-link,
  .m-acc__head { padding: 13px 20px; font-size: 15px; }
  .m-acc__head .chev { width: 10px; }
  .m-acc__panel a { padding: 9px 0 9px 34px; font-size: 13.5px; }
  .m-acc.open .m-acc__panel { max-height: 560px; }
  .mobile-drawer { padding: 4px 0 28px; }
  .mobile-drawer .btn { margin: 16px var(--gutter) 0; }
  .drawer-foot { margin-top: 24px; }
  .drawer-foot img { width: 170px; }
  .drawer-foot__badge { width: 150px; margin-top: 18px; }
}

/* ---------- Tablet (≤991px): cards go 2-up ---------- */
@media (max-width: 991px) {
  .grid-3, .grid-4 { grid-template-columns: 1fr 1fr; }
  .spec-strip { grid-template-columns: repeat(3,1fr); }
  .section { padding-block: clamp(48px, 7vw, 96px); }
}

/* ---------- Mobile landscape (≤767px): single-column stacking ---------- */
@media (max-width: 767px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .split, .cta-split, .product-line__top { grid-template-columns: 1fr; }
  .split.reverse .split__media { order: 0; }
  .product-line__media { min-height: 220px; }
  .gallery { columns: 2; }
  .section { padding-block: clamp(40px, 9vw, 72px); }
  .section-head { margin-bottom: 28px; }
  .hero { min-height: min(74vh, 600px); }
  .tabs__nav { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
  .tabs__nav::-webkit-scrollbar { display: none; }
  .tabs__btn { white-space: nowrap; flex: 0 0 auto; }
}

/* ---------- Mobile portrait (≤479px) ---------- */
@media (max-width: 479px) {
  body { font-size: 16px; }
  .nav__actions .btn { display: none; }
  .mobile-drawer { width: 65vw; }
  .form-row { grid-template-columns: 1fr; }
  .spec-strip { grid-template-columns: repeat(2,1fr); }
  .gallery { columns: 1; }
  .footer-layout { flex-direction: column; align-items: center; text-align: center; }
  .footer-cta { justify-content: center; text-align: center; }
  .footer-bottom { flex-direction: column; align-items: flex-start; }
  .footer-nav { gap: 14px 22px; }
  .footer-nav a { padding: 4px 0; }
  .hero { min-height: min(78vh, 540px); }
  .hero__cta { width: 100%; }
  .hero__cta .btn, .page-hero__cta .btn { flex: 1 1 100%; justify-content: center; }
  .cta-band__actions { width: 100%; }
  .cta-band__actions .btn { flex: 1 1 100%; justify-content: center; }
}

/* Centers the Detail Set Map / Typical Install Detail Map toggle, scoped
   to this page's tab nav rather than the shared .subtabs__nav (which is
   left-started elsewhere, e.g. the Wood Stains subtabs). */
#detail-sets .subtabs__nav { justify-content: center; }

/* ---------- Technical drawings download table ----------
   Grid table with a real System/Product Line/Downloads header row and
   the system name spanning its whole row group (grid-row:span N, N set
   inline per system in the markup). Values (header blue #1269b0, row
   divider #bbb, system-name 33.6px/400/#333, button border/padding/
   letter-spacing) matched to live's real computed styles. */
.dl-table { display: grid; grid-template-columns: minmax(160px,220px) 1fr auto; border: 1px solid var(--ink); border-radius: 0; overflow: hidden; }
.dl-table__head { display: contents; }
.dl-table__head > div { background: #1269b0; color: #f8f8f8; font-family: var(--display); font-weight: 700; padding: 16px 24px; font-size: 1.05rem; }
.dl-table__head > div:first-child { font-size: 1.2rem; }
.dl-system { display: contents; }
.dl-system__name { display: flex; align-items: center; padding: 24px; font-family: var(--heading); font-weight: 400; font-size: clamp(24px,2.4vw,34px); color: #333; border-right: 1px solid var(--line-strong); border-bottom: 1px solid var(--line-strong); }
/* Both panels' System column (header word + row content) reads centered. */
.dl-table__head > div:first-child,
.dl-system__name { justify-content: center; text-align: center; }
/* Typical Install Detail Map's system names are longer (e.g. "Wood
   (ThermoWood)") and were wrapping/clipping in the shared 220px cap --
   widen just this panel's System column. */
[data-panel="install-detail"] .dl-table { grid-template-columns: minmax(240px,320px) 1fr auto; }
.dl-row { display: contents; }
.dl-row__name { display: flex; align-items: center; padding: 18px 24px; border-bottom: 1px solid var(--line-strong); font-weight: 500; color: var(--ink); }
.dl-row__links { display: flex; align-items: center; gap: 12px; padding: 14px 24px; border-bottom: 1px solid var(--line-strong); }
.dl-link {
  font-family: var(--display); font-size: .74rem; font-weight: 700;
  letter-spacing: .06em; text-transform: uppercase;
  padding: 9px 22px; border: 1px solid var(--line-strong); border-radius: 0;
  color: var(--ink); white-space: nowrap;
  transition: background .18s var(--ease), color .18s var(--ease), border-color .18s var(--ease);
}
.dl-link:hover { background: var(--ink); color: #fff; }
@media (max-width: 767px) {
  .dl-table { display: block; border: none; }
  .dl-table__head { display: none; }
  .dl-system { display: block; margin-bottom: 24px; border: 1px solid var(--ink); border-radius: var(--r); overflow: hidden; }
  .dl-system__name { border-right: none; }
  .dl-row { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; padding: 14px 16px; border-bottom: 1px solid var(--line); }
  .dl-row__name, .dl-row__links { padding: 0; border-bottom: none; }
}

/* =========================================================================
   ANATOMY DIAGRAM (anatomy-of-a-super-window.html)
   The leader lines are PAINTED INTO the artwork (anatomy__window.avif) --
   they are permanent, always blue, and run from each dot out to under its
   label. Live draws no lines of its own: no SVG, no pseudo-elements, no
   borders. An earlier pass here rebuilt them as an SVG polyline layer that
   greyed out on inactive components; that layer sat on top of the image's
   own blue lines and washed them out, which is what looked broken. Only
   three things overlay the image: the ten dots, the five labels, and the
   description panel. Clicking a label only recolours label text (#bbb ->
   #000) and swaps the visible description -- nothing about the lines or
   dots moves or changes colour on live.

   Every value below is copied from live's stylesheet, not measured off a
   screenshot: the box is a fixed max-width 800px (720 at <=991, 90%/500
   at <=767), dots are a 10px box scaled to 0.7 anchored by their own
   top-left corner (no centring transform), and labels are anchored by
   bottom/right. Positions live in CSS rather than inline styles so the
   narrow breakpoints can override them.
   ========================================================================= */
.anatomy { position: relative; max-width: 800px; margin-inline: auto; }
/* The img is wrapped in <picture> to swap in a line-free copy on phones; the
   wrapper is the flex item at phone width, so it has to lay out like the img. */
.anatomy picture { display: block; }
.anatomy__img { width: 100%; display: block; }

.anatomy__detail { position: absolute; top: 3%; right: 0; width: 44%; max-width: 420px; }
.anatomy__detail-item { display: none; }
.anatomy__detail-item.is-active { display: block; height: 330px; overflow: auto; }
.anatomy__detail-item h3 { font-size: 24px; line-height: 30px; font-weight: 900; margin-bottom: 10px; }
.anatomy__detail-item p { font-family: var(--display); font-weight: 300; color: #333; font-size: .9rem; line-height: 1.7; }

.anatomy__dot { position: absolute; width: 10px; height: 10px; border-radius: 50%; background: #1269b0; transform: scale(.7); }
.anatomy__dot[data-component="triple-pane"] { top: 53.5%; left: 47.5%; }
.anatomy__dot[data-component="triple-pane"].anatomy__dot--2 { left: 50%; }
.anatomy__dot[data-component="triple-pane"].anatomy__dot--3 { left: 52.5%; }
.anatomy__dot[data-component="concealed"] { top: 49.4%; left: 23%; }
.anatomy__dot[data-component="warm-edge"] { bottom: 24%; left: 49%; }
.anatomy__dot[data-component="warm-edge"].anatomy__dot--2 { bottom: 24.7%; left: 52%; }
.anatomy__dot[data-component="triple-seal"] { bottom: 15.5%; left: 47.5%; }
.anatomy__dot[data-component="triple-seal"].anatomy__dot--2 { bottom: 16.4%; left: 50%; }
.anatomy__dot[data-component="triple-seal"].anatomy__dot--3 { bottom: 14.4%; left: 55.5%; }
.anatomy__dot[data-component="insulated-frames"] { bottom: 9%; left: 47%; }

.anatomy__label {
  position: absolute; text-align: left; background: none; border: none; padding: 0;
  font-family: var(--body-font); font-weight: 400; font-size: 1.05rem; line-height: 1.2;
  color: #bbb; cursor: pointer; transition: color .2s;
}
.anatomy__label.is-active { color: #000; }
.anatomy__label[data-component="triple-pane"] { bottom: 41%; right: 2%; }
.anatomy__label[data-component="concealed"] { top: 67%; right: 2%; }
.anatomy__label[data-component="warm-edge"] { bottom: 21.5%; right: 13.5%; }
.anatomy__label[data-component="triple-seal"] { bottom: 12%; right: 4%; }
.anatomy__label[data-component="insulated-frames"] { bottom: 5.3%; right: 17%; }

@media (max-width: 991px) {
  .anatomy { max-width: 720px; }
  .anatomy__label { font-size: 1rem; }
  .anatomy__detail { width: 41%; max-width: 350px; right: 1%; }
}

@media (max-width: 767px) {
  /* Phone layout is Ellie's (PR #119), not live's. Live keeps the overlay
     diagram at phone width, but that means 10px labels sitting on the
     artwork -- barely readable. Instead the labels stack underneath as a
     full-width list, and the image crops to the window: the source is
     1764x1620 and only its left 1015px is the window, the remaining 42%
     being lines reaching toward where the labels sit on DESKTOP. With the
     labels stacked, that strip is lines pointing at nothing, so it is
     cropped away and the window fills the width instead.

     The labels are positioned by [data-component] above, so the resets
     here have to match that specificity to win. */
  .anatomy { display: flex; flex-direction: column; gap: 20px; width: auto; max-width: 800px; }
  .anatomy picture { order: 1; }
  /* The phone <source> is already cropped to the window, so no CSS crop here.
     It replaces an aspect-ratio/object-fit cover crop of the desktop artwork,
     which trimmed the lines' right-hand run but left them crossing the window
     and exiting the frame. */
  /* Bounded by viewport HEIGHT, not just width. The phone artwork is
     857x1531 -- taller than it is wide -- so sizing it off width alone made
     it ~600px on a 812px-tall phone: three quarters of the screen for one
     image. Capping the height and letting width follow the same ratio keeps
     the whole graphic, and the labels under it, on screen together. */
  .anatomy__img {
    width: 100%;
    max-width: calc(52vh * 857 / 1531);
    /* svh, NOT dvh. dvh tracks the CURRENT viewport, so Chrome's shrinking
       address bar resized the diagram continuously as you scrolled -- Ellie
       saw it visibly rescale. svh is the smallest (address-bar-expanded)
       height and never changes, so the diagram is stable through a scroll. */
    max-width: calc(52svh * 857 / 1531);
    margin-inline: auto;
  }
  .anatomy__dot { display: none; }
  /* Picker before the text it controls. The description sat above the list,
     so you met a screen-tall paragraph before the labels it belongs to and
     had to scroll back up after choosing one -- the list and its text were
     never on screen together. Order: window, list, description. */
  .anatomy__labels { order: 2; display: flex; flex-direction: column; border-top: 1px solid var(--line); }
  .anatomy__detail { position: static; width: auto; max-width: none; padding-right: 0; order: 3; }
  .anatomy__label[data-component] {
    position: static; inset: auto; display: block; width: 100%;
    white-space: normal; padding: 14px 0; font-size: 1rem; max-width: none;
    border-bottom: 1px solid var(--line);
  }
  /* As a list, the selected row needs more than a colour shift to read as
     selected -- on the diagram that job is done by the highlighted dot. */
  .anatomy__label.is-active { font-weight: 700; }
  .anatomy__detail-item.is-active { height: auto; overflow: visible; }
  .anatomy__detail-item h3 { font-size: 1.2rem; line-height: 1.3; }
}

/* ---------- Short viewports (phone held in landscape) ---------- */
/* THE GATE MATTERS MORE THAN THE LAYOUT. Three earlier attempts at this all
   appeared to change nothing on Ellie's actual phone, because they were gated
   on `min-width: 768px`. A landscape iPhone is not reliably that wide: an SE
   is 667px, and iOS Display Zoom shrinks the CSS viewport on any model, so the
   rule simply never matched and the portrait treatment kept applying. Height
   is the thing that actually characterises a landscape phone, so that is what
   is tested; the min-width is only there to keep two columns from being
   attempted on something genuinely tiny, and is set well below any phone in
   landscape. Do not put a 768px gate back.

   Layout: window artwork on the left sized to the height available, with the
   label list and the description in a readable column beside it. The desktop
   overlay is not usable here -- its labels sit at percentages of a box that is
   taller than it is wide, so at any size that fits a 390px-tall screen the type
   is around 7px. This keeps the type readable and spends the axis a landscape
   phone actually has. */
@media (max-height: 600px) and (min-width: 560px) {
  .anatomy {
    display: grid; max-width: none; gap: 0 26px; margin-inline: 0;
    grid-template-columns: auto minmax(0, 1fr); grid-template-rows: auto 1fr;
    align-items: start; container-type: normal;
  }
  .anatomy picture { grid-column: 1; grid-row: 1 / span 2; order: 0; }
  .anatomy__img {
    width: auto; max-width: none; margin: 0;
    height: 72vh;
    height: 72svh;
  }
  .anatomy__dot, .anatomy__lines { display: none; }
  .anatomy__labels {
    grid-column: 2; grid-row: 1; order: 0;
    display: flex; flex-direction: column; border-top: 1px solid var(--line);
  }
  .anatomy__label[data-component] {
    position: static; inset: auto; display: block; width: 100%; max-width: none;
    white-space: normal; padding: 8px 0; font-size: .8rem; line-height: 1.3;
    border-bottom: 1px solid var(--line);
  }
  .anatomy__label.is-active { font-weight: 700; }
  .anatomy__detail {
    grid-column: 2; grid-row: 2; order: 0; position: static;
    width: auto; max-width: none; padding: 12px 0 0;
  }
  .anatomy__detail-item.is-active { height: auto; overflow: visible; }
  .anatomy__detail-item h3 { font-size: 1rem; line-height: 1.3; margin-bottom: 6px; }
  .anatomy__detail-item p { font-size: .78rem; line-height: 1.55; }
}

/* ---------- Short viewports, site-wide ---------- */
/* Same gate as the anatomy block above, for the same reason: a landscape phone
   is characterised by its height, not its width, and a min-width of 768px
   silently excludes an SE and any phone with Display Zoom on.

   Videos first. A 16:9 frame sized off the WIDTH of a landscape phone is
   taller than the screen -- on the firewall page the hero measured 120% of the
   viewport height and the fire-test video 113%, so neither could ever be seen
   whole. Cap them by height and let the width follow the ratio. The box stays
   16:9, so nothing is cropped: Ellie deliberately un-cropped the firewall hero
   in e0584da, and a cap that reintroduced cropping would undo that. The hero
   therefore letterboxes -- narrower than the screen, centred -- rather than
   running off the bottom of it.

   Then the general "everything is a bit too big" feeling: desktop-scale
   section padding and a 30px h2 in a 390px-tall window. Tightened modestly,
   not to phone-portrait sizes -- there is still a lot of width here. */
@media (max-height: 600px) and (min-width: 560px) {
  .page-hero--video {
    max-height: 76vh; width: calc(76vh * 16 / 9);
    max-height: 76svh; width: calc(76svh * 16 / 9);
    max-width: 100%; margin-inline: auto;
  }
  .vid-card__frame {
    max-width: calc(72vh * 16 / 9);
    max-width: calc(72svh * 16 / 9);
    margin-inline: auto;
  }
  .section { padding-block: clamp(28px, 7vh, 48px); }
  .section--tight { padding-block: clamp(24px, 5vh, 36px); }
  .section-head { margin-bottom: 20px; }
  h2 { font-size: 24px; }
  .lead { font-size: 15px; }

  /* Images. Every image on the firewall page was taller than a landscape
     phone's screen -- wildfire diagram 89% of the viewport height, skyline
     photo 107%, the glass overview 119%, each blog card 73% -- because the
     tablet breakpoints stack things into one or two columns and let each
     image take the full width. A landscape phone has the WIDTH of a tablet
     but a third of the height, so the answer is columns, not smaller pages:
     restore the desktop column counts and let the images shrink to fit them.

     Glass overview: below 767px the single wide image is swapped for four
     square ones stacked in a grid, and above it the wide image returns. At
     landscape-phone height the wide image is unreadable AND taller than the
     screen, so use the four -- but in one row, as Ellie asked, since there is
     room for that here and stacking them would be four screens of scrolling. */
  .glass-overview__wide { display: none; }
  .glass-overview__mobile { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
  .glass-overview__mobile img { width: 100%; height: auto; }

  /* Three blog cards side by side instead of two: they are fixed 4:3, so the
     narrower column is what brings the image down (378x283 -> ~245x184). */
  .grid-3 { grid-template-columns: repeat(3, 1fr); }
  .grid-4 { grid-template-columns: repeat(4, 1fr); }

  /* The skyline photo: the 991px rules stack this split into one column and
     let the image be its natural height. Put the desktop layout back --
     image beside the text at the desktop 1:3 ratio, filling the row height
     with a cover crop, which is exactly how it renders on desktop -- and add
     a height cap as a backstop so a long paragraph cannot drag it tall. */
  .split--photo-heavy { grid-template-columns: 1fr 3fr; align-items: stretch; }
  .split--photo-heavy.reverse { grid-template-columns: 3fr 1fr; }
  .split--photo-heavy .split__media { height: 100%; }
  .split--photo-heavy .split__media img { height: 100%; max-height: 62vh; max-height: 62svh; object-fit: cover; }

  /* The wildfire assembly diagram sits in its own column already; it is just
     drawn at the full column width. Let it be as wide as the height allows. */
  .split__media--flat img { width: auto; max-width: 100%; height: auto; max-height: 60vh; max-height: 60svh; display: block; margin-inline: auto; }

  /* The UV band is a fixed 700px on desktop, matching live: the photo is
     painted at natural size and the text panel is pinned in its top-right
     corner, where the photo happens to have a white wall. Two landscape
     attempts overlaid the panel on a height-capped photo and both failed --
     scaled to cover, the wall is gone and the text lands on the window and
     the hillside. So no overlay at all: the photo takes the left column at
     its full uncropped height (auto 100%, anchored left, no-repeat leaves the
     rest of the section white) and the text sits beside it on white. Height
     is the smaller of 76svh and the height at which the photo would exceed
     55% of the width, so the text column is never squeezed below ~45%.

     Restated in full because on a phone under 768px wide the portrait rules
     match here too (padding-top 66%, static panel); everything they touch is
     set back explicitly. A section background, not an <img>, so the image
     rules above do not reach it. */
  .uv-band {
    display: grid; align-items: center;
    height: min(76vh, 36.7vw); height: min(76svh, 36.7vw);
    grid-template-columns: calc(min(76vh, 36.7vw) * 1.5) minmax(0, 1fr);
    grid-template-columns: calc(min(76svh, 36.7vw) * 1.5) minmax(0, 1fr);
    padding-top: 0;
    background-size: auto 100%; background-position: left center; background-repeat: no-repeat;
  }
  .uv-band__panel { grid-column: 2; position: static; padding: 0 20px 0 24px; max-width: none; }
  .uv-band__panel h2 { font-size: 22px; }
  .uv-band__panel p { font-size: 15px; }

  /* Footer. Desktop's three-column flex (logo | main | badge) survives down
     to 480px, and on a landscape phone it left the middle column ~317px wide:
     the button and tagline stacked with the tagline ragged-right, the four
     links wrapped onto two rows, and the badge floated mid-height off to the
     side -- 462px of footer, 123% of the screen, looking loose. With the width
     a landscape phone has, three short rows fit instead: logo, button and
     tagline together; the four links across one row; the legal line with the
     badge at the right. .footer-main is display:contents so its rows can be
     placed on the footer grid directly. */
  .site-footer { padding-block: 24px 18px; }
  .footer-layout { display: grid; grid-template-columns: auto 1fr auto; align-items: center; gap: 0 22px; text-align: left; }
  .footer-logo { grid-column: 1; grid-row: 1; }
  .footer-logo img { width: 64px; }
  .footer-main { display: contents; }
  .footer-cta { grid-column: 2 / -1; grid-row: 1; flex-wrap: nowrap; justify-content: flex-start; align-items: center; gap: 22px; padding-block: 0; border-bottom: 0; }
  .footer-cta__text { text-align: left; font-size: .9rem; line-height: 1.35; }
  .footer-bottom { grid-column: 1 / -1; grid-row: 2; padding-top: 14px; margin-top: 16px; border-top: 1px solid rgba(255,255,255,.18); }
  .footer-nav { gap: 8px 22px; }
  .footer-meta { grid-column: 1 / 3; grid-row: 3; margin-top: 12px; padding-top: 10px; }
  .footer-badge { grid-column: 3; grid-row: 3; align-self: center; justify-self: end; width: 110px; margin-top: 12px; }
  .footer-social { margin-top: 14px; }
}


