/* ============================================================
   DreamLux USA — shared stylesheet for the internal pages.

   The homepage (index.html) is a bundled x-dc document whose styling is
   entirely inline, so its values are duplicated here as real tokens. If a
   colour, font or spacing value changes on the homepage, change it here too.
   Values were read directly off the homepage, not re-invented:
     bg #0A0A0E · alt #101319 · deep #050609 · gold #C9A227 · gold-lt #E8C766
     ink #FAFAF8 · hairline rgba(255,255,255,.07) · radius 2px
     headings Cormorant Garamond · body Jost · section padding 150px/64px
   ============================================================ */

:root {
  --bg:        #0A0A0E;
  --bg-alt:    #101319;
  --bg-deep:   #050609;
  --gold:      #C9A227;
  --gold-lt:   #E8C766;
  --gold-dk:   #8A6A1C;
  --ink:       #FAFAF8;
  --ink-70:    rgba(250,250,248,0.68);
  --ink-60:    rgba(250,250,248,0.60);
  --ink-45:    rgba(250,250,248,0.45);
  --hairline:  rgba(255,255,255,0.07);
  --hairline-2:rgba(255,255,255,0.15);
  --radius:    2px;
  --pad-x:     64px;
  /* height of the sticky nav: 20px padding x2 + 52px logo + 1px border */
  --navh:      93px;
  --serif:     'Cormorant Garamond', Georgia, serif;
  --sans:      'Jost', system-ui, -apple-system, sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--gold); text-decoration: none; }
a:hover { color: var(--gold-lt); }
img, video { max-width: 100%; }

/* ---------- reveal animations (uploads/dl-anim.js) ----------
   Mirrors the homepage's inline copy. Hidden states are scoped to .dl-js,
   which the script puts on <html>, so a script failure never leaves text
   invisible. */
.dl-ln { display: block; overflow: hidden; padding-bottom: .18em; margin-bottom: -.18em; }
.dl-w  { display: inline-block; white-space: nowrap; }
.dl-c  { display: inline-block; will-change: transform; }
/* An inline-block is a block container, so it inherits text-indent and
   re-applies it INSIDE its own box — inflating every word by the indent width
   on any indented paragraph. Neutralise it on the word/char boxes; the visual
   indent belongs to the line wrapper. */
.dl-w, .dl-c { text-indent: 0; }
.dl-js .dl-fade,.dl-js .dl-h { opacity: 0; transform: translateY(20px); }

@keyframes dlVideoGrow {
  from { clip-path: inset(46% 46% 46% 46%); transform: scale(1.2); }
  to   { clip-path: inset(0 0 0 0);         transform: scale(1); }
}
.dl-video { animation: dlVideoGrow 1.9s cubic-bezier(.16,1,.3,1) both; }

@media (prefers-reduced-motion: reduce) {
  .dl-video { animation: none; }
  .dl-c, .dl-w { transform: none !important; }
  .dl-js .dl-fade,.dl-js .dl-h { opacity: 1; transform: none; }
}

/* ---------- top nav ---------- */
.nav {
  position: sticky; top: 0; z-index: 50;
  display: flex; align-items: center; justify-content: space-between;
  gap: 24px; padding: 20px var(--pad-x);
  background: rgba(10,10,14,0.72);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border-bottom: 1px solid var(--hairline);
}
/* flex:none + max-width:none because the logo is a flex item with the default
   shrink:1, and the global `img { max-width: 100% }` then squeezed it
   horizontally against its fixed 52px height whenever the nav ran short of
   room — the wordmark rendered visibly narrowed on tablet widths, just above
   the burger breakpoint. Fixed 2026-08-20. */
.nav__logo { flex: none; }
.nav__logo img { height: 52px; width: auto; max-width: none; display: block; }
/* caps, light weight, 16px — matching the homepage nav */
.nav__links {
  display: flex; gap: 30px; font-size: 18px; font-weight: 300;
  letter-spacing: .14em; text-transform: uppercase;
}
.nav__links { align-self: stretch; align-items: stretch; }
.nav__item { position: relative; display: flex; align-items: center; }
/* invisible bridge across the nav's bottom padding + border, so the pointer
   never leaves the item on its way to the panel */
.nav__item--has-mega::after {
  content: ''; position: absolute; left: 0; right: 0; top: 100%; height: 34px;
}
.nav__links > .nav__item > a { color: var(--ink); display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }
.nav__links > .nav__item > a:hover { color: rgba(250,250,248,.6); }
.nav__links > .nav__item > a[aria-current="page"] { color: rgba(250,250,248,.55); }

/* ---------- mega menu ----------
   Full-width panel of image cards, opened purely on :hover / :focus-within so
   it needs no JS. It hangs off the nav (which is sticky/fixed), is pinned to the
   viewport edges rather than the item, and is click-through-proof while closed
   (visibility + pointer-events, not just opacity). */
.nav__chev {
  width: 6px; height: 6px; border-right: 1px solid currentColor;
  border-bottom: 1px solid currentColor; transform: rotate(45deg) translateY(-2px);
  transition: transform .25s;
}
.nav__item--has-mega:hover .nav__chev { transform: rotate(225deg) translateY(-2px); }
.mega__wrap {
  position: fixed; left: 0; right: 0; top: var(--navh);
  background: rgba(10,10,14,.96);
  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
  border-bottom: 1px solid var(--hairline);
  padding: 40px var(--pad-x) 44px;
  /* Client (Jonathan, 2026-08-26): "you arent able to scroll down on the
     dropdown menu". The panel is position:fixed with no height cap, so as soon
     as .mega--8 wraps to two rows -- which it does at any width under 1400px,
     where the grid steps down to 4 columns -- it stands ~900px tall. On a
     laptop viewport the second row fell below the fold with nothing to scroll:
     the panel does not scroll itself, and scrolling the page cannot move a
     fixed panel. Cap it to the space left under the nav and let it scroll. */
  max-height: calc(100vh - var(--navh));
  max-height: calc(100dvh - var(--navh));
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(250,250,248,.28) transparent;
  opacity: 0; visibility: hidden; pointer-events: none;
  transform: translateY(-8px);
  transition: opacity .25s ease, transform .3s ease, visibility .25s;
  z-index: 60;
}
.nav__item--has-mega:hover .mega__wrap,
.nav__item--has-mega:focus-within .mega__wrap {
  opacity: 1; visibility: visible; pointer-events: auto; transform: none;
}
/* While a panel is open, the nav strip itself goes solid to match it —
   otherwise its translucent blur keeps showing the hero image through,
   which reads as a seam against the panel's near-opaque fill below it. */
.nav:has(.nav__item--has-mega:hover),
.nav:has(.nav__item--has-mega:focus-within) {
  background: rgba(10,10,14,.96);
}
.mega { display: grid; gap: 20px; max-width: 1500px; margin: 0 auto; }
.mega--4 { grid-template-columns: repeat(4, 1fr); }
.mega--8 { grid-template-columns: repeat(8, 1fr); }
.mega__card { display: block; color: inherit; }
.mega__media {
  display: block; position: relative; overflow: hidden;
  border-radius: var(--radius); background: var(--bg-deep);
}
.mega__media img, .mega__media video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
  transition: transform .8s cubic-bezier(.22,1,.36,1);
}
.mega__card:hover .mega__media img { transform: scale(1.07); }
.mega__label {
  display: block; margin-top: 12px;
  font-size: 15px; font-weight: 300; letter-spacing: .14em;
  text-transform: uppercase; color: var(--ink);
}
.mega__card:hover .mega__label { color: rgba(250,250,248,.6); }
@media (max-width: 1400px) { .mega--8 { grid-template-columns: repeat(4, 1fr); } }
/* The one filled button on the site: the nav's primary action (client asked for
   it "more solid"). Everything else stays an outline button.
   Weight is 500, not 300 like the nav links beside it — dark text on this
   light fill reads visibly THINNER than the same weight in light-on-dark
   (a real optical effect: light-on-dark text irradiates/looks heavier at
   equal weight), so 300 here looked mismatched against the nav links even
   though the CSS numbers matched. 500 compensates so they read as one
   family of type at a glance. */
.nav__cta {
  border: 1px solid var(--ink); background: var(--ink); color: var(--bg);
  font-family: var(--sans); font-size: 14px; font-weight: 400; letter-spacing: .1em;
  text-transform: uppercase; padding: 11px 22px; border-radius: var(--radius);
  cursor: pointer; white-space: nowrap; transition: background .2s, color .2s;
}
.nav__cta:hover {
  background: rgba(250,250,248,.14); border-color: #FFFFFF; color: #FFFFFF;
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
}
.nav__burger { display: none; background: none; border: 0; cursor: pointer; padding: 6px; }
.nav__burger span {
  display: block; width: 22px; height: 1.5px; background: var(--ink); margin: 5px 0;
  transition: transform .3s cubic-bezier(.22,1,.36,1), opacity .2s;
}
/* open = an X, matching the homepage burger */
.nav.is-open .nav__burger span:nth-child(1) { transform: translateY(6.5px) rotate(45deg); }
.nav.is-open .nav__burger span:nth-child(2) { opacity: 0; }
.nav.is-open .nav__burger span:nth-child(3) { transform: translateY(-6.5px) rotate(-45deg); }

/* ---------- interior hero ----------
   One treatment for every internal page — same height, scrim and inset — so
   the pages read as a set. Only the media behind it changes. */
.hero {
  position: relative; overflow: hidden; background: var(--bg-deep);
  min-height: 88vh; display: flex; align-items: flex-end;
}
.hero__media { position: absolute; inset: 0; }
.hero__media img, .hero__media video { width: 100%; height: 100%; object-fit: cover; display: block; }
.hero__scrim {
  position: absolute; inset: 0;
  background: linear-gradient(0deg, rgba(10,10,14,.92) 4%, rgba(10,10,14,.45) 48%, rgba(10,10,14,.55) 100%);
}
.hero__inner { position: relative; padding: 150px var(--pad-x) 76px; width: 100%; }

/* ---------- sections ---------- */
.sec { padding: 65px var(--pad-x); background: var(--bg); }
.sec--alt  { background: var(--bg-alt); }
.sec--deep { background: var(--bg-deep); }
.sec--tight { padding-top: 45px; padding-bottom: 45px; }
.wrap { max-width: 1500px; margin: 0 auto; }
.narrow { max-width: 760px; }
.mid { max-width: 1140px; }   /* narrow + 50% */

.eyebrow { display: flex; align-items: center; gap: 10px; margin-bottom: 18px; }
/* the leading gold dash was dropped in v3 — the <i> is gone from the templates,
   but keep it hidden in case any hand-written markup still carries one */
.eyebrow i { display: none; }
.eyebrow span {
  font-size: 14px; font-weight: 300; letter-spacing: .2em;
  color: var(--gold); text-transform: uppercase;
}

h1, h2, h3, h4 { font-family: var(--serif); color: var(--ink); margin: 0; letter-spacing: -.01em; }
.h1 { font-size: clamp(44px, 5.5vw, 78px); font-weight: 300; line-height: 1.02; }
.h1--italic { font-style: italic; font-weight: 300; color: var(--gold); font-size: clamp(26px, 2.6vw, 40px); line-height: 1.15; margin-top: 6px; }
.h2 { font-size: clamp(32px, 3.2vw, 46px); font-weight: 300; }
/* Floor raised 24px -> 28px (client, 2026-08-28): on a phone the collection
   pages' "The weave" heading read barely above the 16px body copy under it.
   Only the floor moved, so it affects phones and tablets; from ~1170px up the
   2.4vw track takes over and desktop is unchanged. */
.h3 { font-size: clamp(28px, 2.4vw, 34px); font-weight: 300; line-height: 1.14; }
.h4 { font-size: 21px; font-weight: 300; }

.lede { font-size: 17px; line-height: 1.65; color: var(--ink-70); max-width: 700px; }
.body { font-size: 15px; line-height: 1.75; color: var(--ink-60); max-width: 640px; }
.kicker { font-size: 14px; font-weight: 300; letter-spacing: .06em; color: var(--gold); text-transform: uppercase; }
.mt-8 { margin-top: 8px; }  .mt-16 { margin-top: 16px; } .mt-24 { margin-top: 24px; }
.mt-32 { margin-top: 32px; } .mt-48 { margin-top: 48px; } .mt-64 { margin-top: 64px; }
.mb-16 { margin-bottom: 16px; } .mb-24 { margin-bottom: 24px; }
.mb-48 { margin-bottom: 48px; } .mb-64 { margin-bottom: 64px; } .mb-80 { margin-bottom: 80px; }

/* ---------- buttons ---------- */
.btn {
  display: inline-block; font-family: var(--sans); font-size: 14px; font-weight: 300;
  letter-spacing: .08em; text-transform: uppercase; padding: 17px 32px;
  border-radius: var(--radius); cursor: pointer; border: 1px solid transparent;
  transition: background .2s, border-color .2s, color .2s, box-shadow .2s;
}
/* v3, 2026-08-13: every button on the site is an outline button — no fills at
   all, white or otherwise. `.btn--gold` keeps its name (every template already
   asks for it as the primary action) but is now a stroke button too. Primary and
   secondary are still told apart where they sit side by side — as they do on
   every collection page — by border opacity rather than by a fill: primary gets
   a full-opacity white edge, secondary a 30% one. Hover is a faint white wash,
   never a solid fill. */
.btn--gold { background: transparent; color: var(--ink); border-color: var(--ink); }
.btn--gold:hover { background: rgba(250,250,248,.12); color: var(--ink); }
.btn--ghost { background: transparent; color: var(--ink); border-color: rgba(250,250,248,.3); }
.btn--ghost:hover { background: rgba(250,250,248,.12); border-color: var(--ink); color: var(--ink); }
.btn-row { display: flex; flex-wrap: wrap; gap: 16px; align-items: center; }

/* prev / all-collections / next nav on collection detail pages — big cards,
   each filled edge-to-edge by a photo of what it links to, label sitting on
   a bottom scrim (mirrors .tile/.tile__scrim used for the gallery grids). */
.collnav {
  position: relative; overflow: hidden; display: flex; align-items: flex-end;
  min-height: 340px; padding: 30px 34px; border: 1px solid var(--hairline);
  border-radius: var(--radius); background: var(--bg-deep);
  font-family: var(--sans); font-weight: 300; font-size: 20px; letter-spacing: .01em;
  color: var(--ink); transition: border-color .2s;
}
.collnav:hover { border-color: var(--gold); }
.collnav--prev { justify-content: flex-start; }
.collnav--all  { justify-content: center; text-align: center; }
.collnav--next { justify-content: flex-end; text-align: right; }
.collnav__bg { position: absolute; inset: 0; }
.collnav__bg img { width: 100%; height: 100%; object-fit: cover; display: block; }
.collnav__scrim {
  position: absolute; inset: 0;
  background: linear-gradient(0deg, rgba(10,10,14,.9) 0%, rgba(10,10,14,.08) 60%);
}
.collnav__label {
  position: relative; font-family: var(--serif); font-weight: 300;
  font-size: clamp(23px, 2vw, 29px); line-height: 1.15; letter-spacing: 0;
}
/* Cormorant's latin subset has no U+2190/2192, so the arrows would silently
   fall back to a system serif mid-word. Keep them in the sans stack on
   purpose, sized down, so the pairing is deliberate rather than accidental. */
.collnav__arw { font-family: var(--sans); font-size: .72em; vertical-align: .06em; }
@media (max-width: 520px) {
  .collnav { min-height: 240px; padding: 24px 26px; font-size: 17px; }
}

/* ---------- grids & cards ---------- */
.grid { display: grid; gap: 24px; }
.grid--4 { grid-template-columns: repeat(4, 1fr); }
.grid--3 { grid-template-columns: repeat(3, 1fr); }
.grid--2 { grid-template-columns: repeat(2, 1fr); }

.card { display: block; cursor: pointer; transition: transform .25s; color: inherit; }
.card:hover { transform: translateY(-6px); color: inherit; }
.card__media {
  position: relative; overflow: hidden; border: 1px solid var(--hairline);
  border-radius: var(--radius); margin-bottom: 18px; background: var(--bg-deep);
  aspect-ratio: 4 / 5;
}
.card__media img, .card__media video { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .6s cubic-bezier(.22,1,.36,1); }
.card:hover .card__media img { transform: scale(1.04); }
.card__title { font-family: var(--serif); font-size: 24px; font-weight: 300; color: var(--ink); margin-bottom: 4px; }
.card__sub { font-size: 14px; color: var(--ink-45); }

/* Plain framed photo in a grid. Source photos range from 3:2 landscape to
   1:2 portrait, so grids must impose their own ratio or the rows go ragged. */
.shot {
  position: relative; overflow: hidden; aspect-ratio: 4 / 5;
  border: 1px solid var(--hairline); border-radius: var(--radius);
  background: var(--bg-deep);
}
.shot--wide { aspect-ratio: 3 / 2; }
.shot img, .shot video { width: 100%; height: 100%; object-fit: cover; display: block; }

/* ---------- collections: pinned copy left, cards scrolling past on the right ----------
   The aside is sticky, so the copy holds while the grid moves — the fluid.glass
   pattern. Falls back to a plain stacked layout below 900px. */
/* No `align-items: start` here: it would shrink the aside to its content
   height, and a sticky child can only travel inside its parent's box — so the
   copy would scroll away instead of pinning. Stretch keeps the aside as tall as
   the card column, which is the distance we want it to hold for. */
/* Collections landing: eight cards in two rows of four, same side padding and
   gutter as the homepage's .dlrow. Row gap > column gap so the captions have
   room before the next row starts. */
.crow4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 48px 24px; }
.crow4 .ccard__title { padding: 0 20px; }
@media (max-width: 900px) { .crow4 { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 860px) { .crow4 { gap: 40px 16px; } }
@media (max-width: 520px) { .crow4 { grid-template-columns: 1fr; } }

.ccard { display: block; color: inherit; }
.ccard__media {
  position: relative; overflow: hidden; border-radius: var(--radius);
  /* 3:4 matches the client's new card photography and the homepage rows */
  aspect-ratio: 3 / 4; background: var(--bg-deep); margin-bottom: 14px;
}
.ccard__media img, .ccard__media video {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: transform .8s cubic-bezier(.22,1,.36,1), filter .4s;
}
/* Zoom is the whole hover — no dim, no centred VIEW pill (both removed
   2026-08-13), matching the homepage's .dlc cards. */
.ccard:hover .ccard__media img, .ccard:focus-visible .ccard__media img,
.ccard:hover .ccard__media video, .ccard:focus-visible .ccard__media video { transform: scale(1.07); }
/* title set like the top menu: caps, light, same size and tracking */
.ccard__title {
  font-family: var(--sans); font-size: 18px; font-weight: 300;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink);
}


/* tile with the label sitting on the image (applications).
   Hover deliberately mirrors the homepage's collection cards (.dlc there):
   the card lifts, the photo zooms + dims, and a View pill fades in. */
.tile { color: inherit; position: relative; overflow: hidden; border: 1px solid var(--hairline); border-radius: var(--radius); display: flex; align-items: flex-end; padding: 22px; aspect-ratio: 4/5; transition: border-color .2s, transform .25s; }
.tile:hover { border-color: var(--gold); transform: translateY(-6px); }
.tile > img, .tile > video { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; transition: transform .8s cubic-bezier(.22,1,.36,1), filter .4s; }
.tile:hover > img, .tile:hover > video { transform: scale(1.04); filter: brightness(.72); }
.tile__scrim { position: absolute; inset: 0; background: linear-gradient(0deg, rgba(10,10,14,.88) 0%, rgba(10,10,14,.1) 62%); }
.tile__view {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%) scale(.94);
  padding: 13px 26px; border-radius: var(--radius);
  font-family: var(--sans); font-size: 14px; font-weight: 300;
  letter-spacing: .14em; text-transform: uppercase; color: var(--ink);
  white-space: nowrap;
  background: rgba(10,10,14,.28); border: 1px solid rgba(250,250,248,.85);
  backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
  opacity: 0; pointer-events: none;
  transition: opacity .3s, transform .45s cubic-bezier(.22,1,.36,1);
}
.tile:hover .tile__view, .tile:focus-visible .tile__view { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.tile__cap { position: relative; }
.tile__cap b { display: block; font-family: var(--serif); font-weight: 300; font-size: 23px; line-height: 1.15; color: var(--ink); }
.tile__cap a, .tile__cap em { display: block; font-size: 14px; font-style: normal; color: var(--gold); margin-top: 3px; }

/* ---------- editorial media rows (mirrors the homepage 3-step rhythm) ---------- */
.rows { display: flex; flex-direction: column; gap: 150px; }
.row { display: flex; align-items: center; gap: 110px; }
.row--flip { flex-direction: row-reverse; }
/* A fixed aspect keeps rows from stretching when the source photo is a very
   tall portrait (several client files are 1:2 or taller). */
.row__media { flex: none; width: 46%; aspect-ratio: 3/2; border: 1px solid var(--hairline); border-radius: var(--radius); overflow: hidden; }
.row__media > * { width: 100%; height: 100%; }
.row__media img, .row__media video { width: 100%; height: 100%; object-fit: cover; display: block; }
.row__media--tall { width: 34%; aspect-ratio: 3/4; }
/* the fabric selector's stage: square rather than the row's default 3:2 band,
   so a portrait fabric shot reads at a useful height instead of a thin strip */
.row__media--fab { width: 46%; aspect-ratio: 1/1; }
.row__copy { flex: 1; min-width: 0; }

/* ---------- editorial two-column copy block ----------
   Large mixed sans/italic-serif statement on the left, smaller supporting
   copy dropped lower on the right. */
.split { display: grid; grid-template-columns: 1.4fr .78fr; gap: clamp(40px, 5vw, 110px); align-items: start; }
.split__lead {
  margin: 0; font-family: var(--sans); font-weight: 300;
  font-size: clamp(25px, 2.9vw, 44px); line-height: 1.2; color: var(--ink);
  text-indent: 2.4em;
}
.split__lead em { font-family: var(--serif); font-style: italic; font-weight: 300; }
/* dl-anim turns each visual line into a block-level .dl-ln, and a block-level
   child re-applies the paragraph's text-indent to EVERY line. Keep it on the
   first line only. */
.split__lead > .dl-ln { text-indent: 0; }
.split__lead > .dl-ln:first-child { text-indent: 2.4em; }
.split__body { padding-top: clamp(24px, 11vw, 200px); }
.split__body p { margin: 0; font-size: 16.5px; line-height: 1.72; color: var(--ink-70); max-width: 520px; }
.split__body p + p { margin-top: 26px; }

/* ---------- big thin stat numerals ---------- */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); border-top: 1px solid var(--hairline); }
.stat { padding: 46px 36px 8px; border-left: 1px solid var(--hairline); }
.stat:first-child { border-left: 0; padding-left: 0; }
.stat__n {
  font-family: var(--serif); font-weight: 300; letter-spacing: -.02em;
  font-size: clamp(54px, 7.4vw, 124px); line-height: .88; color: var(--ink);
}
.stat__l {
  margin-top: 18px; font-size: 14px; font-weight: 300; letter-spacing: .18em;
  text-transform: uppercase; color: var(--ink-45); line-height: 1.5;
}

/* ---------- brand statement ---------- */
.statement { text-align: center; }
.statement__a { font-family: var(--serif); font-weight: 300; font-size: clamp(34px, 5vw, 76px); line-height: 1.05; color: var(--ink); }
.statement__b { font-family: var(--serif); font-style: italic; font-weight: 300; font-size: clamp(26px, 3.6vw, 54px); line-height: 1.15; color: var(--gold); margin-top: 10px; }

/* ---------- collection rows with a centred stroke button ---------- */
.collrows { position: relative; --cr-gap: 10px; }
/* One gap value drives both axes so the space between cards in a row matches
   the space between the two rows exactly. */
.crtrack { display: flex; align-items: stretch; gap: var(--cr-gap); }
.crtrack + .crtrack { margin-top: var(--cr-gap); }
.crcard {
  position: relative; flex: 1 1 0; min-width: 0; overflow: hidden;
  aspect-ratio: 16 / 10; border: 1px solid var(--hairline); display: block;
  background: var(--bg-deep);
}
.crcard img, .crcard video { width: 100%; height: 100%; object-fit: cover; display: block; transition: transform .9s cubic-bezier(.22,1,.36,1); }
.crcard:hover img, .crcard:hover video { transform: scale(1.06); }
/* darken the middle band so the overlaid button stays legible */
.collrows__veil { position: absolute; inset: 0; pointer-events: none; background: radial-gradient(ellipse 46% 42% at 50% 50%, rgba(10,10,14,.72) 0%, rgba(10,10,14,0) 70%); }
/* Spans the full width and centres its inline-block child, rather than
   left:50% + translateX(-50%). That old pairing looked fine on desktop but
   was broken on mobile: an absolutely-positioned box with `left:50%` and no
   width gets only (container − 50%) to shrink-wrap into, so at phone widths
   the label had ~190px to work with and wrapped into a narrow, tall box.
   left:0/right:0 removes the constraint entirely. Fixed 2026-08-20. */
.collrows__cta { position: absolute; left: 0; right: 0; top: 50%; transform: translateY(-50%); z-index: 5; text-align: center; }
.btn--stroke {
  border: 1px solid rgba(250,250,248,.85); color: var(--ink);
  background: rgba(10,10,14,.28); backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px);
  padding: clamp(18px,2vw,28px) clamp(28px,4vw,60px);
  font-size: clamp(13px,1.15vw,17px); letter-spacing: .14em;
  white-space: nowrap;
}
/* hover was a solid gold fill; aligned to the same faint wash as every other
   button so no button on the site fills on hover */
.btn--stroke:hover { background: rgba(250,250,248,.16); border-color: var(--ink); color: var(--ink); }

@media (max-width: 1200px) {
  .stats { grid-template-columns: repeat(2, 1fr); }
  .stat { padding: 34px 24px 8px; }
  .stat:nth-child(3) { border-left: 0; padding-left: 0; }
  .stat:nth-child(n+3) { border-top: 1px solid var(--hairline); margin-top: 8px; }
}
@media (max-width: 860px) {
  .split { grid-template-columns: 1fr; gap: 34px; }
  .split__lead { text-indent: 1.6em; }
  .split__body { padding-top: 0; }
  .crtrack { display: grid; grid-template-columns: 1fr 1fr; gap: var(--cr-gap); }
  /* client, 2026-08-19: keep the button centred over the images on mobile
     too, same as desktop — it was previously pulled out below the grid. */
  .btn--stroke { background: rgba(10,10,14,.28); backdrop-filter: blur(7px); -webkit-backdrop-filter: blur(7px); }
}
@media (max-width: 520px) {
  .stats { grid-template-columns: 1fr; }
  .stat { border-left: 0; padding-left: 0; }
  .stat:nth-child(n+2) { border-top: 1px solid var(--hairline); margin-top: 8px; }
}

/* ---------- interior hero: static full-bleed media ----------
   v3 replaced the pinned scroll-expand block (media grew from a centred
   rectangle to full-bleed as you scrolled) with a plain full-bleed background.
   No scroll-driven zoom, so site.js's [data-pvideo] loop finds nothing and
   bails. One treatment on every internal page, so they still read as a set.

   Height fills the first screen minus the sticky nav, so the title lands on
   screen rather than a nav-height below the fold. Halved on touch/narrow
   viewports only — see the [reduced-height] block further down. */
.pvwrap {
  position: relative; height: calc(100vh - var(--navh)); min-height: 560px;
  overflow: hidden; background: var(--bg-deep);
  display: flex; flex-direction: column; justify-content: flex-end;
}
.pvstage { position: static; display: contents; }
.pvframe { position: absolute; inset: 0; }
/* An iframe cannot be object-fit, so the embed is sized to the larger of the
   two cover dimensions and centred — same visual result as cover. */
.pvframe iframe {
  position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
  width: max(100%, 177.78vh); height: max(100%, 56.25vw); border: 0;
  /* background, not a player: no clicks, no hover chrome, not tab-reachable */
  pointer-events: none;
}
.pvframe img, .pvframe video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
}

/* Hero copy sits 76px above the hero's bottom edge, as on every other page. */
.pvhead {
  position: relative; z-index: 2;
  padding: 0 var(--pad-x) 76px;
  pointer-events: none;
}

/* sound toggle for the embedded video */
.pvsound {
  position: absolute; right: var(--pad-x); bottom: 76px;
  z-index: 3; display: inline-flex; align-items: center; gap: 10px;
  padding: 11px 18px; border-radius: var(--radius); cursor: pointer;
  font-family: var(--sans); font-size: 14px; font-weight: 300; letter-spacing: .08em;
  text-transform: uppercase; color: var(--ink);
  background: rgba(10,10,14,.42); border: 1px solid rgba(255,255,255,.22);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  transition: border-color .2s, background .2s, color .2s;
}
.pvsound:hover { border-color: var(--gold); color: var(--gold-lt); }
.pvsound:focus-visible { outline: 2px solid var(--gold); outline-offset: 3px; }
.pvsound svg { width: 19px; height: 19px; fill: none; stroke: currentColor; stroke-width: 1.2; }
.pvsound svg polygon { fill: currentColor; stroke: none; }
/* muted = slash shown, waves hidden; unmuted = the reverse */
.pvsound .pvsound__waves { opacity: 0; }
.pvsound .pvsound__slash { opacity: 1; }
.pvsound[aria-pressed="true"] .pvsound__waves { opacity: 1; }
.pvsound[aria-pressed="true"] .pvsound__slash { opacity: 0; }
.pvhead::before {
  content: ''; position: absolute; inset: -220px 0 0 0; z-index: -1;
  background: linear-gradient(0deg, rgba(10,10,14,.92) 6%, rgba(10,10,14,0) 100%);
}

/* On phones the hero copy runs the full column width, so the button's
   bottom-right corner is not free — it sat on top of the lede (client,
   2026-08-28). Below 860px it leaves the corner and stacks under the text
   block instead. .pvstage is display:contents, so the button and .pvhead are
   both flex children of .pvwrap and `order` can put the button second. The
   scrim is extended past .pvhead's box so the wash continues behind it. */
@media (max-width: 860px) {
  .pvsound {
    position: static; order: 2; align-self: flex-start;
    margin: 0 var(--pad-x) 54px;
  }
  .pvhead { order: 1; padding-bottom: 18px; }
  .pvhead::before { inset: -200px 0 -120px 0; }
}

/* ---------- process: support, full-bleed with copy in three corners ---------- */
.psupport { position: relative; min-height: 100vh; overflow: hidden; background: var(--bg-deep); }
.psupport__media { position: absolute; inset: 0; }
.psupport__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* lighter than before so the photograph reads through */
.psupport__veil {
  position: absolute; inset: 0;
  background:
    radial-gradient(ellipse 70% 50% at 50% 50%, rgba(10,10,14,.26) 0%, rgba(10,10,14,.04) 70%),
    linear-gradient(180deg, rgba(10,10,14,.52) 0%, rgba(10,10,14,.10) 38%, rgba(10,10,14,.58) 100%);
}
.psupport__grid {
  position: relative; min-height: 100vh;
  /* top padding clears the sticky nav so the eyebrow never tucks under it */
  padding: 130px var(--pad-x) calc(var(--pad-x) + 20px);
  display: grid; grid-template-rows: auto 1fr; gap: 40px;
}
/* eyebrow and heading are one top-left block, heading on a single line */
.psupport__top { justify-self: start; align-self: start; }
.psupport__h {
  font-family: var(--serif); font-weight: 300; white-space: nowrap;
  font-size: clamp(34px, 4.6vw, 78px); line-height: 1.04;
  text-shadow: 0 2px 30px rgba(10,10,14,.55);
}
/* body sits toward the middle of the page, lower half */
.psupport__body {
  justify-self: center; align-self: center; text-align: left; margin: 0;
  /* nudged right of dead centre so it sits under the heading's weight */
  transform: translateX(clamp(40px, 9vw, 170px));
  max-width: 480px; font-size: 16px; line-height: 1.75; color: var(--ink-70);
  text-shadow: 0 2px 20px rgba(10,10,14,.7);
}

/* ---------- process: at scale, 50% taller with a looping project strip ---------- */
/* taller on desktop (client, 2026-08-20) — the looping project strip fills the
   band's full height, so the band's height IS how big those photos read */
.pscale { position: relative; overflow: hidden; padding: 150px var(--pad-x); background: var(--bg); }
/* the running strip occupies the right half only */
.pscale__bg { position: absolute; inset: 0 0 0 46%; display: flex; align-items: center; }
.pscale__track {
  display: flex; gap: 14px; height: 100%; align-items: center;
  width: max-content; animation: pMarquee 80s linear infinite;
}
.pscale__track img { height: 100%; width: auto; object-fit: cover; }
/* the strip is duplicated in the markup, so -50% is one seamless cycle */
@keyframes pMarquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
/* Overlay only along the top, left and bottom edges — the right stays clear so
   the strip is visible, while the left keeps the copy legible. */
.pscale__veil {
  position: absolute; inset: 0; pointer-events: none;
  background:
    linear-gradient(180deg, var(--bg) 0%, rgba(10,10,14,.55) 12%, rgba(10,10,14,0) 26%),
    linear-gradient(0deg,   var(--bg) 0%, rgba(10,10,14,.55) 12%, rgba(10,10,14,0) 26%),
    linear-gradient(90deg,  var(--bg) 0%, var(--bg) 34%, rgba(10,10,14,.72) 50%, rgba(10,10,14,0) 68%);
}
.pscale__inner { position: relative; }
.pscale__copy { text-align: left; max-width: 560px; }
.pscale__cta { margin-top: 36px; }

@media (prefers-reduced-motion: reduce) { .pscale__track { animation: none; } }

/* ---------- [reduced-height] tablet + mobile ----------
   The full-screen hero and Support band are right on a desktop but far too
   tall on a phone or tablet, so they are halved here and ONLY here (client,
   2026-08-20 — an earlier pass put the reduction in the base rule, which
   shrank desktop too).

   Two triggers, because neither alone is enough:
     max-width:1100px          — phones, portrait tablets, and a desktop
                                 browser narrowed down to test responsively.
     hover:none + pointer:coarse — any touch device at ANY width. This is what
                                 catches the cases a width query keeps missing:
                                 an iPad Pro in landscape is 1366px wide, and
                                 in desktop-mode Safari it reports ~1488px, so
                                 both sail past every sane width breakpoint
                                 while still very much being tablets.
   A real desktop with a mouse matches neither, so it keeps full height. */
@media (max-width: 1100px), (hover: none) and (pointer: coarse) {
  /* clamped, not just halved: a portrait tablet is ~1900px tall, where half is
     still ~950px. min-height (not height) on the Support band so it can grow
     with its copy instead of being clipped by its own overflow:hidden. */
  .pvwrap { height: calc((100vh - var(--navh)) / 2); min-height: 340px; max-height: 620px; }
  .psupport, .psupport__grid { min-height: clamp(360px, 50vh, 620px); }
  .psupport__grid { padding-top: clamp(56px, 7vw, 130px); }
  .pscale { padding: 96px var(--pad-x); }
}

@media (max-width: 900px) {
  /* a phone wants a lower floor and ceiling than a tablet does */
  .pvwrap { min-height: 300px; max-height: 460px; }
  .psupport__grid { gap: 26px; }
  .psupport__h { white-space: normal; }
  /* transform:none — the desktop rule nudges this copy right of dead centre
     with translateX(clamp(40px,9vw,170px)) so it sits under the heading's
     weight. That transform was never reset for the stacked layout, so on a
     phone the paragraph sat ~50px right of the heading instead of flush with
     it. Client, 2026-08-20. */
  .psupport__body { justify-self: start; text-align: left; transform: none; }
  .pscale__bg { inset: 0; opacity: .5; }
  .pscale { padding: 90px var(--pad-x); }   /* was 120px — larger than the tablet value above it */
  .pscale__inner { flex-direction: column; align-items: flex-start; }
}

/* ---------- process: the five steps, all visible in one row ----------
   v3 (client, 2026-08-13) replaced a crossfading one-at-a-time slider with a
   static row: no icons, no glass card, no arrows or autoplay ring — just a thin
   number, the step name and the copy, five across. The old .pslider/.pglass/
   .pnav rules were deleted with it; site.js's [data-pslider] loop now matches
   nothing and bails, so no JS needed removing. */
.psteps { display: grid; grid-template-columns: repeat(5, 1fr); gap: clamp(20px, 2.4vw, 44px); }
.pstep { padding-top: 24px; border-top: 1px solid var(--hairline-2); }
.pstep__n {
  font-family: var(--serif); font-weight: 300; font-size: clamp(36px, 3.4vw, 56px);
  line-height: 1; color: var(--gold); margin-bottom: 16px;
  /* the numerals are the one place a hairline weight has to hold at large size */
  font-variant-numeric: lining-nums;
}
/* Step name: nudged up a step (client, 2026-08-28) — at the old 19px floor it
   read barely larger than the 15px body copy under it. The .psteps--stack
   variant on the product pages sets its own size and is unaffected. */
.pstep__t {
  font-family: var(--serif); font-weight: 300; font-size: clamp(21px, 1.75vw, 28px);
  line-height: 1.18; color: var(--ink); margin: 0 0 12px;
}
.pstep__b { margin: 0; font-size: 15px; line-height: 1.7; color: var(--ink-60); }

@media (max-width: 1150px) { .psteps { grid-template-columns: repeat(3, 1fr); row-gap: 44px; } }
@media (max-width: 820px)  { .psteps { grid-template-columns: repeat(2, 1fr); row-gap: 38px; } }
@media (max-width: 520px)  { .psteps { grid-template-columns: 1fr; row-gap: 32px; } }

/* ---------- spec / fact list ---------- */
/* margin:0 — a <dl> carries the UA default `margin: 1em 0`, which nothing
   here reset, so the table sat 16px lower than the heading margin intended
   and 16px above the data-sheet card. */
.facts { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 1px; background: var(--hairline); border: 1px solid var(--hairline); margin: 0; }
/* single-column variant: a narrow "at a glance" sidebar rather than the
   full-width specifications table (project case-study pages). */
.facts--stack { grid-template-columns: 1fr; }

/* project case-study overview: copy column stays wide, the facts sidebar is
   a fixed narrow width instead of stretching across the other half of a
   50/50 grid — otherwise its content sits in a mostly-empty column. */
.proverview { display: grid; grid-template-columns: minmax(0,1fr) 320px; gap: 48px; align-items: start; }
@media (max-width: 860px) { .proverview { grid-template-columns: 1fr; } }
.facts > div { background: var(--bg); padding: 26px 30px; }
.facts dt { font-size: 14px; font-weight: 300; letter-spacing: .1em; text-transform: uppercase; color: var(--ink-45); margin-bottom: 8px; }
.facts dd { margin: 0; font-size: 15px; line-height: 1.6; color: var(--ink); }

/* Specifications band. It carried `sec--tight` (45px desktop / 32px mobile)
   while the "In place" section right below it uses the standard .sec padding
   (65px / 46px), so the two bands' spacing never matched — the gap under the
   data-sheet card read visibly smaller than the one above "IN PLACE". It now
   takes the standard .sec padding, so the two are equal at every breakpoint;
   the class remains as a hook for the tablet rule below. Client, 2026-08-20.

   The :last-child reset is separate: on the two collection pages with no PDF
   the band ends with a <p> whose UA default bottom margin was pushing the
   bottom gap ~14px past the top one. */
.specsec > .wrap > :last-child { margin-bottom: 0; }

/* downloadable data-sheet row (Specifications section on collection pages) */
.dsheet {
  display: flex; align-items: center; justify-content: center; gap: 20px; margin-top: 26px;
  padding: 22px 26px; border: 1px solid var(--hairline);
  border-radius: var(--radius); background: var(--bg); transition: border-color .2s;
}
.dsheet:hover { border-color: var(--hairline-2); }
.dsheet__icon { flex: none; display: block; }
.dsheet__icon svg {
  width: 30px; height: 30px; display: block;
  fill: none; stroke: var(--gold); stroke-width: 1.2;
  stroke-linecap: round; stroke-linejoin: round;
}
.dsheet__meta { flex: 0 1 auto; min-width: 0; }
.dsheet__meta b {
  display: block; font-family: var(--sans); font-weight: 300;
  font-size: 16px; color: var(--ink);
}
.dsheet__spec {
  display: block; margin-top: 5px; font-size: 13px; letter-spacing: .05em;
  text-transform: uppercase; color: var(--ink-45);
}
.dsheet__cta {
  flex: none; margin-left: 56px; font-family: var(--sans); font-size: 13px; font-weight: 400;
  letter-spacing: .12em; text-transform: uppercase; color: var(--bg); white-space: nowrap;
  background: var(--gold); border: 1px solid var(--gold); padding: 10px 18px;
  border-radius: var(--radius); transition: background .2s, border-color .2s, color .2s;
}
.dsheet__cta:hover { background: transparent; border-color: var(--gold); color: var(--gold); }
/* Stacked on phones: icon, then title/spec line, then the button — each on its
   own row and centred. The desktop `margin-left: 56px` on the CTA has to be
   reset, otherwise it counts toward the centred row and the button lands ~28px
   right of centre. */
@media (max-width: 520px) {
  .dsheet { flex-wrap: wrap; gap: 14px; padding: 20px; }
  .dsheet__meta { flex: 1 1 100%; order: 1; text-align: center; }
  .dsheet__cta { order: 2; margin-left: 0; }
}

/* ---------- contact page: form beside a "larger project" card ---------- */
.cgrid { display: grid; grid-template-columns: minmax(0,1fr) 360px; gap: clamp(32px, 4vw, 64px); align-items: start; }
.cgrid .form { max-width: none; }
.bigproj {
  position: relative; overflow: hidden; display: block;
  border: 1px solid var(--hairline); border-radius: var(--radius);
  background: var(--bg-deep); padding: 34px 32px;
}
.bigproj__bg { position: absolute; inset: 0; }
.bigproj__bg img { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Two layers: a flat wash to knock the photo back far enough for body copy to
   pass contrast, plus a vertical gradient so the top of the card stays darkest
   where the heading sits. */
.bigproj__scrim {
  position: absolute; inset: 0; background:
    linear-gradient(180deg, rgba(5,6,9,.86) 0%, rgba(5,6,9,.62) 52%, rgba(5,6,9,.8) 100%),
    rgba(5,6,9,.12);
}
.bigproj__body { position: relative; }
@media (max-width: 980px) {
  .cgrid { grid-template-columns: 1fr; }
  .bigproj { max-width: 560px; }
}

.chips { display: flex; flex-wrap: wrap; gap: 10px; }
.chip { font-size: 14px; padding: 8px 14px; border: 1px solid var(--hairline-2); border-radius: 40px; color: var(--ink-70); }
.chip--gold { border-color: rgba(201,162,39,.5); color: var(--gold); }

/* swatch strip on the collection pages */
.swatches { display: flex; flex-wrap: wrap; gap: 14px; align-items: center; }
.sw { width: 40px; height: 40px; border-radius: 50%; border: 2px solid var(--bg); cursor: pointer; padding: 0; }
.sw[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--bg), 0 0 0 4px var(--gold), 0 0 16px rgba(201,162,39,.6); }

/* on/off light toggle */
.toggle { display: flex; align-items: center; gap: 16px; }
.toggle__track { width: 48px; height: 26px; border-radius: 13px; border: 0; cursor: pointer; position: relative; background: var(--hairline-2); transition: background .2s; padding: 0; }
.toggle__track[aria-pressed="true"] { background: var(--gold); }
.toggle__knob { position: absolute; top: 2px; left: 2px; width: 22px; height: 22px; border-radius: 50%; background: var(--ink); transition: transform .2s; }
.toggle__track[aria-pressed="true"] .toggle__knob { transform: translateX(22px); }
.toggle__label { font-size: 14px; font-weight: 300; letter-spacing: .04em; }
.lit { transition: filter .45s ease; }
.lit[data-lit="off"] { filter: brightness(.34) saturate(.65); }

/* ---------- filter bar ---------- */
.filters { display: flex; flex-wrap: wrap; gap: 10px; }
.filter {
  font-family: var(--sans); font-size: 14px; font-weight: 300; letter-spacing: .02em;
  padding: 9px 16px; border-radius: 40px; cursor: pointer;
  background: transparent; border: 1px solid var(--hairline-2); color: var(--ink-70);
  transition: all .2s;
}
.filter:hover { border-color: var(--gold); color: var(--gold); }
.filter[aria-pressed="true"] { background: var(--gold); border-color: var(--gold); color: var(--bg); font-weight: 300; }
/* Keyed off [data-gallery], not a .gallery class — the container is
   `<div class="grid grid--4" data-gallery>` and never had one, so the old
   `.gallery > .is-hidden` selector matched nothing and the applications filter
   silently did nothing at all despite the JS running correctly. */
[data-gallery] > .is-hidden { display: none; }

/* ---------- FAQ ---------- */
.faq { border-top: 1px solid var(--hairline); }
.faq details { border-bottom: 1px solid var(--hairline); }
.faq summary {
  cursor: pointer; list-style: none; padding: 26px 0; display: flex;
  justify-content: space-between; align-items: center; gap: 24px;
  font-family: var(--serif); font-size: 23px; font-weight: 300; color: var(--ink);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary::after { content: '+'; color: var(--gold); font-family: var(--sans); font-size: 22px; flex: none; transition: transform .25s; }
.faq details[open] summary::after { transform: rotate(45deg); }
.faq p { margin: 0 0 26px; font-size: 15px; line-height: 1.75; color: var(--ink-60); max-width: 780px; }

/* ---------- form ---------- */
.form { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 22px; max-width: 860px; }
.field { display: flex; flex-direction: column; gap: 8px; }
.field--full { grid-column: 1 / -1; }
.field label { font-size: 14px; font-weight: 300; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-45); }
.field input, .field select, .field textarea {
  font-family: var(--sans); font-size: 15px; color: var(--ink);
  background: rgba(255,255,255,.03); border: 1px solid var(--hairline-2);
  border-radius: var(--radius); padding: 14px 16px; width: 100%;
}
.field textarea { min-height: 140px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus { outline: 0; border-color: var(--gold); background: rgba(201,162,39,.05); }
.field select { appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--gold) 50%), linear-gradient(135deg, var(--gold) 50%, transparent 50%); background-position: calc(100% - 20px) 50%, calc(100% - 14px) 50%; background-size: 6px 6px, 6px 6px; background-repeat: no-repeat; }

/* ---------- closing CTA (matches the homepage's glow block) ---------- */
/* The section above the CTA always shares its background, so the two
   paddings read as a single gap: the perceived space above the heading was
   prev-bottom + 70px — 115px on collection pages, 135px on Projects, 160px
   on product pages — against 70px below the button. Unequal, and a different
   value on every page. Zeroing the bottom padding of a same-background
   section that runs into the CTA makes the CTA's own symmetric padding the
   whole gap, so it is identical above and below on every page and at every
   breakpoint. Client, 2026-08-20. */
.cta { position: relative; padding: 90px var(--pad-x); text-align: center; background: var(--bg); }
.sec:not(.sec--alt):not(.sec--deep):has(+ .cta) { padding-bottom: 0; }
.cta__glow {
  position: absolute; left: 0; right: 0; top: -180px; height: 900px; pointer-events: none;
  background: radial-gradient(ellipse 720px 400px at 50% 50%,
    rgba(201,162,39,.20) 0%, rgba(201,162,39,.07) 45%, rgba(201,162,39,0) 78%);
}
.cta__inner { position: relative; }

/* ---------- footer ---------- */
.foot__rule { height: 3px; background: linear-gradient(90deg, var(--bg-alt), var(--gold-dk), var(--gold-lt), transparent); }
.foot { padding: 72px var(--pad-x) 40px; background: var(--bg-alt); }
.foot__cols { display: grid; grid-template-columns: 1.4fr repeat(4, 1fr); gap: 44px; margin-bottom: 56px; }
.foot__logo { height: 42px; width: auto; display: block; margin-bottom: 20px; }
.foot__blurb { font-size: 16px; line-height: 1.65; color: rgba(250,250,248,.5); max-width: 280px; margin: 0 0 24px; }
.foot h5 { font-family: var(--sans); font-size: 16px; font-weight: 300; letter-spacing: .1em; text-transform: uppercase; color: rgba(250,250,248,.38); margin: 0 0 14px; }
.foot__col a { display: block; font-size: 16px; line-height: 2.1; color: rgba(250,250,248,.62); transition: color .2s; }
.foot__col a:hover { color: var(--gold); }
/* .foot__col--mob is the Products column. It went in as a phone-only column
   (2026-08-28) and is shown at every width as of the same day's second round
   of client notes, so it carries no display rule now — the modifier stays on
   the markup of all 53 pages purely as a hook, it no longer means "phones
   only". The footer is a five-column grid because of it. */
.foot__tbc { display: block; font-size: 16px; line-height: 2.1; color: rgba(250,250,248,.45); }
.foot__social { display: flex; gap: 10px; }
/* Real links now, not <span>s: these were decorative placeholders until the
   client confirmed they were meant to be social buttons (2026-08-28). */
.foot__social a {
  width: 34px; height: 34px; border-radius: 50%; border: 1px solid rgba(255,255,255,.2);
  display: flex; align-items: center; justify-content: center;
  font-size: 14px; font-weight: 300; color: rgba(250,250,248,.6);
  text-decoration: none;
  transition: border-color .2s, color .2s;
}
.foot__social a:hover,
.foot__social a:focus-visible { border-color: var(--gold); color: var(--gold); }
.foot__base { border-top: 1px solid rgba(255,255,255,.08); padding-top: 26px; font-size: 16px; color: rgba(250,250,248,.4); display: flex; justify-content: space-between; gap: 24px; flex-wrap: wrap; }

/* a quiet marker for things awaiting client data, so a prototype never
   reads as if placeholder content were final */
.tbc { font-size: 14px; color: var(--ink-45); font-style: italic; }

/* ---------- tablet ----------
   Above the 1100px burger breakpoint the nav is still horizontal but the six
   items, the logo and the CTA are all competing for width, so the type steps
   down here to keep every label on one line (client, 2026-08-20). */
@media (min-width: 1101px) and (max-width: 1320px) {
  .nav__links { font-size: 15px; gap: 20px; }
  .nav__cta { font-size: 13px; padding: 10px 16px; }
}
/* Tablet-only rules (client, 2026-08-20). Scoped with min-width so they don't
   override the phone rules set at 900px and below.
   The .pvwrap and .psupport height overrides that used to live here are gone:
   both are now halved in their base rules, at every width. A tablet-scoped
   copy was not just redundant, it was the bug — it only matched up to 1200px,
   so a portrait or desktop-mode tablet wider than that fell through to the
   full-height rule and the band was never reduced at all. */
@media (min-width: 901px) and (max-width: 1200px) {
  /* collection pages: halve the gap between the weave row and Specifications.
     :has() picks out only that one boundary — the weave section is the .sec
     immediately followed by the Specifications band — so no other section
     spacing on the page changes. */
  .sec:has(+ .specsec) { padding-bottom: 32px; }
  .specsec { padding-top: 32px; }
}

/* Nav gets its own breakpoint, above the layout one: at 18px the links plus the
   logo and CTA need ~1070px of content width, so below ~1150px they collide. */
@media (max-width: 1100px) {
  .nav__links, .nav__cta { display: none; }
  .nav__burger { display: block; }
  /* burger sits beside the logo, above the panel it opens */
  .nav { flex-wrap: wrap; }
  .nav__logo { order: 1; } .nav__burger { order: 2; }
  .nav__links { order: 3; } .nav__cta { order: 4; }
  /* .nav__item is a row flex (for desktop centring against the fixed-position
     mega panel); in burger mode the mega panel goes position:static and
     joins that same row, so the "Collections"/"Products" label ends up
     BESIDE the image grid instead of above it. Stack this item instead. */
  .nav__item--has-mega { flex-direction: column; align-items: stretch; }
  /* the hover panels would be unreachable on touch — flatten them.
     max-height/overflow are reset too: the desktop panel is a fixed, self-
     scrolling box, but in the drawer it is just content in the flow, and
     leaving the cap on would nest a second scroller inside the drawer. */
  .mega__wrap {
    position: static; opacity: 1; visibility: visible; pointer-events: auto;
    transform: none; background: none; backdrop-filter: none; border: 0;
    padding: 14px 0 4px; width: 100%;
    max-height: none; overflow: visible;
  }
  /* The open drawer: declared HERE, at the breakpoint that shows the burger.
     It used to live only in the 860px block, so between 861px and 1100px the
     burger was visible but .nav__links stayed display:none — tapping it did
     nothing at all. Fixed 2026-08-26. */
  .nav.is-open .nav__links {
    display: flex; flex-direction: column; gap: 18px; width: 100%;
    padding: 18px 0 6px;
  }
  .nav.is-open .nav__cta { display: block; width: 100%; }
  /* Same client note as .mega__wrap above (Jonathan, 2026-08-26). The open
     drawer stacks 6 nav items plus the flattened 8-card Collections grid and
     4-card Products grid inside a position:sticky nav — far taller than a
     phone viewport, and a sticky box pinned at top:0 never scrolls its own
     overflow into view. Cap the nav to the viewport and let it scroll. The
     background goes fully opaque only in this state, because page content now
     travels behind the drawer instead of below it. */
  .nav.is-open {
    /* Anchored to the viewport, not the document. The closed nav is sticky,
       which is fine, but a sticky box is still in flow: if the page scrolls
       underneath an open drawer the whole nav travels with it and the bottom of
       the list leaves the screen with no way back. Fixed removes that failure
       mode entirely (Ziad, 2026-08-27: "it appears, but you can't scroll to see
       it all the way to the bottom"). */
    position: fixed;
    top: 0; left: 0; right: 0;
    /* Three height fallbacks, least to most correct — the last one a browser
       understands wins. 100vh is wrong on iOS (it is the LARGE viewport, so the
       drawer's last rows sit behind the browser chrome and cannot be reached
       even at the end of the scroll); -webkit-fill-available and then dvh fix
       exactly that. */
    max-height: 100vh;
    max-height: -webkit-fill-available;
    max-height: 100dvh;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
    background: var(--bg);
    scrollbar-width: thin;
    scrollbar-color: rgba(250,250,248,.28) transparent;
  }
  /* always 4 across on mobile/tablet, per client request */
  .mega, .mega--4, .mega--8 { grid-template-columns: repeat(4, 1fr); gap: 10px; }
  .mega__label { font-size: 11px; letter-spacing: .08em; margin-top: 6px; }
  .nav__chev { display: none; }
}

/* ---------- responsive ---------- */
@media (max-width: 1200px) {
  :root { --pad-x: 40px; }
  .grid--4 { grid-template-columns: repeat(3, 1fr); }
  .row { gap: 60px; }
  /* three across, not two: with Products added there are five columns, and at
     two across the last one sat alone on a row of its own. Two rows of three
     leave the single gap at the end of the block, where it reads as the end of
     the list rather than as a hole. */
  .foot__cols { grid-template-columns: repeat(3, 1fr); }
}
@media (max-width: 860px) {
  :root { --pad-x: 24px; }
  /* .nav.is-open drawer rules moved up to the 1100px burger breakpoint — they
     were unreachable on tablets while the burger was already showing. */
  /* was larger than the desktop value (80px/90px vs 65px/70px) — backwards
     for mobile, where screen-filling padding pushes content needlessly far
     down. Sections now step down, not up. */
  .sec { padding: 46px var(--pad-x); }
  .sec--tight { padding-top: 32px; padding-bottom: 32px; }
  /* Heading-to-content gaps were set for desktop and overshot the tighter
     mobile section padding — on the Specifications band the 48px under the
     heading was larger than the 32px above the eyebrow, so the block read
     lopsided. Scaled down so the rhythm inside a section is even. */
  .mb-48 { margin-bottom: 28px; }
  .mb-64 { margin-bottom: 34px; }
  .mb-80 { margin-bottom: 40px; }
  .hero__inner { padding: 110px var(--pad-x) 54px; }
  .grid--4, .grid--3, .grid--2 { grid-template-columns: repeat(2, 1fr); }
  .rows { gap: 64px; }
  .row, .row--flip { flex-direction: column; gap: 28px; }
  .row__media, .row__media--tall { width: 100%; }
  /* collection pages with a fabric selector: client, 2026-08-19 wants the
     stage image UNDER the color-group/LED picker on mobile, not above it. */
  .row:has(.row__media--fab) .row__copy { order: 1; }
  .row:has(.row__media--fab) .row__media--fab { order: 2; }
  .facts, .form { grid-template-columns: 1fr; }
  .cta { padding: 56px var(--pad-x); }
  .step { grid-template-columns: 54px 1fr; gap: 18px; }
  .step__n { font-size: 32px; }
  /* Projects page gallery (the only grid--4): 1 big full-width tile, then
     pairs of two, repeating every 5 — instead of the plain 2-up grid every
     other grid--N uses. Scoped to [data-gallery] so grid--3/--2 elsewhere
     (product/collection pages) are untouched. */
  [data-gallery] { gap: 16px; }
  [data-gallery] .tile:nth-child(5n+1) { grid-column: 1 / -1; aspect-ratio: 16 / 10; }
  /* Logo block takes its own full-width row and the four link columns pair up
     2x2 under it — no odd column left over at any width from here down. */
  .foot__cols { grid-template-columns: 1fr 1fr; }
  .foot__cols > div:first-child { grid-column: 1 / -1; }
}
@media (max-width: 520px) {
  .grid--3, .grid--2 { grid-template-columns: 1fr; }
  [data-gallery] { gap: 12px; }
  /* .foot__cols only had a 2-column mobile step (from 4), which still
     overflowed a phone width — Logo and Company share column 1, so both
     scrolled off horizontally, leaving only Collections/Contact (column 2)
     visible. Fixed to 2 real columns: Logo spans the full width on its own
     row, and Collections/Company — now a tall 8-item list since each
     collection got its own line — sit side by side instead of stacking, so
     Company isn't pushed far down the page.

     Contact used to span the full width too. Since the Products column arrived
     it left a half-empty row, so the two now pair up (client, 2026-08-28).

     The two tracks are deliberately uneven and the column gap is down from
     24px to 16px: info@dreamluxusa.com is ~160px wide and wrapped onto two
     lines in an even half-column (client, 2026-08-28). Nothing in the left
     column is wider than "Digital Pattern" (~112px), so the width comes off a
     column that was not using it. That holds the address on one line down to
     ~340px of viewport; overflow-wrap is the last-resort guard below that, so
     a very narrow phone wraps the address instead of scrolling sideways. */
  .foot__cols { grid-template-columns: minmax(0,1fr) minmax(0,1.25fr); gap: 28px 16px; }
  .foot__col a { overflow-wrap: anywhere; }
}


/* ---------- media: 3-column grid of real client moments ---------- */
.mgrid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 40px 28px; }
.mcard { display: block; color: inherit; }
.mcard__media {
  position: relative; aspect-ratio: 4 / 3; overflow: hidden;
  border-radius: var(--radius); background: var(--bg-deep); margin-bottom: 18px;
}
.mcard__media img, .mcard__media video {
  position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; display: block;
  transition: transform .9s cubic-bezier(.22,1,.36,1);
}
.mcard:hover .mcard__media img, .mcard:hover .mcard__media video { transform: scale(1.06); }
.mcard__meta {
  font-size: 14px; font-weight: 300; letter-spacing: .16em;
  text-transform: uppercase; color: var(--gold); margin-bottom: 10px;
}
.mcard__title { font-family: var(--serif); font-weight: 300; font-size: 26px; line-height: 1.2; margin: 0 0 10px; }
.mcard__body { margin: 0; font-size: 15px; line-height: 1.7; color: var(--ink-60); }
/* the card is now an <a> to its article page (client, 2026-08-20) */
.mcard { text-decoration: none; }
.mcard__more {
  display: inline-block; margin-top: 14px; font-size: 14px; letter-spacing: .08em;
  text-transform: uppercase; color: var(--gold); transition: color .2s;
}
.mcard:hover .mcard__title { color: var(--gold-lt); }
.mcard__title { transition: color .2s; }
/* an article's photo tiles have no caption, so the label sits centred */
.tile--plain { align-items: center; justify-content: center; }
@media (max-width: 1000px) { .mgrid { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 620px)  { .mgrid { grid-template-columns: 1fr; } }

/* ---------- blog article page (client, 2026-08-25) ----------
   The article now reads as one continuous column: title, date, then body copy
   with its photographs and video set into the measure between paragraphs,
   rather than a block of text followed by a separate gallery band. */
.artdate {
  font-family: var(--sans); font-size: 14px; letter-spacing: .12em;
  text-transform: uppercase; color: var(--ink-45); margin: 0;
}
/* Sits in the hero under the h1, so it needs the light-on-dark treatment and
   the same entrance delay the lede uses. */
.pvhead .artdate { margin-top: 14px; color: rgba(250,250,248,.62); }

/* A figure inside the 760px text measure. Breaking out to ~1.18x keeps the
   photograph dominant without becoming a full-bleed band, which would split
   the article back into sections — the thing this layout is undoing. */
.artfig { margin: 44px 0; }
.artfig img, .artfig video {
  display: block; width: 100%; height: auto; border-radius: var(--radius);
  background: var(--bg-deep);
}
.artfig figcaption {
  margin-top: 12px; font-size: 14px; line-height: 1.6; color: var(--ink-45);
  font-style: italic;
}
@media (min-width: 900px) {
  .artfig--wide { width: 118%; margin-left: -9%; }
}
.artbody > p + .artfig { margin-top: 40px; }
.artbody > .artfig + p { margin-top: 40px; }

/* ---------- product pages ---------- */
/* auto-fit rather than a fixed 3, so a 4-item set (Curtains/Surfaces'
   Activation Techniques, Furniture's 4 named pieces) reflows cleanly
   instead of leaving a lone 4th card on its own row */
.psub { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 28px; }
.psub__n {
  font-family: var(--serif); font-weight: 300; font-size: 40px; line-height: 1;
  color: var(--gold); margin-bottom: 14px;
}
.psub__t { font-family: var(--serif); font-weight: 300; font-size: 24px; margin: 0 0 10px; text-transform: uppercase; letter-spacing: .06em; }
.psub__b { margin: 0; font-size: 15px; line-height: 1.7; color: var(--ink-60); }
@media (max-width: 860px) { .psub { grid-template-columns: 1fr; gap: 32px; } }

/* distinctive-features checklist (Curtains, Surfaces) */
.flist { list-style: none; margin: 0; padding: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr)); gap: 16px 32px; }
.flist__item { display: flex; align-items: flex-start; gap: 12px; font-size: 15px; line-height: 1.6; color: var(--ink-70); }
.flist__item svg { flex: none; width: 18px; height: 18px; margin-top: 2px; fill: none; stroke: var(--gold); stroke-width: 1.6; }

/* ---------- product pages: rebuilt 2026-08-20, matching the homepage's own
   "who we are" language (.dlstate / .dlwa in index.html) rather than
   inventing a new visual system ---------- */
/* centred serif opening statement — same recipe as the homepage's .dlstate */
.plead { font-family: var(--serif); font-weight: 300; font-size: clamp(23px, 2.35vw, 35px); line-height: 1.32; letter-spacing: -.005em; color: var(--ink); text-align: center; max-width: 768px; margin: 0 auto; }
/* The statement's own band. The homepage gives this block 150px above and
   130px below (.dlwasec + .dlstate's margin); it was sitting in a `sec--tight`
   45px band here, which read cramped against the hero — client, 2026-08-20. */
.plead-sec { padding-top: clamp(64px, 9vw, 150px); padding-bottom: clamp(56px, 8vw, 130px); }

/* 50/50 image+copy row — same recipe as the homepage's .dlwa/.dlwa--flip:
   photo fills one half edge to edge, copy sits in a centred measure in the
   other half, sides alternate via --flip. */
/* min-height and copy padding both match the homepage's .dlwa/.dlwa__copy
   (700px / 88px) — which is also exactly the +25% row height the client
   asked for on the Fashion and Furniture slider rows, 2026-08-20. */
.prow { display: grid; grid-template-columns: 1fr 1fr; align-items: center; min-height: 700px; gap: 0; }
.prow__media { align-self: stretch; position: relative; overflow: hidden; background: var(--bg-deep); }
.prow__media > img, .prow__media .pslider { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block; }
.prow__copy { max-width: 620px; margin: 0 auto; padding: 88px 24px; text-align: left; }
/* wider measure for the Applications column — a numbered list with a
   description per row needs more room than a plain paragraph does */
.prow__copy--wide { max-width: 760px; }

/* Product-page vertical rhythm (client, 2026-08-20). Every boundary between
   two product bands is one value on each side, so all the gaps match:
     .psec          padded text bands (Applications, Techniques, Built on)
     .prow-sec      full-bleed image rows — flush by design, like the
                    homepage's .dlwa rows
     .prow-sec--gap a full-bleed row that ends a run and therefore needs to
                    contribute its half of the next boundary */
.psec { padding-top: 90px; padding-bottom: 90px; }
.prow-sec { padding: 0; }
.prow-sec--gap { padding-bottom: 90px; }

/* The Applications row sits inside a .wrap so its copy column starts on
   exactly the same left grid line as every other section's heading — it was
   centred inside its own half-column before, which put "APPLICATIONS" ~60px
   left of "HOW IT WORKS" below it (client, 2026-08-20). Zeroing the padding
   on the side that faces the page edge is what makes the text line up; the
   inner side keeps a gutter against the image. */
.prow--apps .prow__copy { max-width: none; margin: 0; }
.prow--apps.prow--flip .prow__copy { padding-left: 0; padding-right: clamp(24px, 3vw, 56px); }
.prow--apps:not(.prow--flip) .prow__copy { padding-right: 0; padding-left: clamp(24px, 3vw, 56px); }
.prow__h { font-family: var(--serif); font-weight: 300; font-size: clamp(26px, 2.7vw, 40px); line-height: 1.22; margin: 0 0 18px; }
/* caps headings on the product pages (client, 2026-08-20) */
.prow__h--caps, .h2--caps { text-transform: uppercase; letter-spacing: .06em; }
/* the source pages give each section a short italic line under the heading */
.prow__tag { font-family: var(--serif); font-style: italic; font-weight: 300; font-size: 20px; line-height: 1.4; color: var(--gold); margin: 0 0 18px; }
.prow--flip .prow__media { order: 2; }
.prow--flip .prow__copy { order: 1; }
/* spec table under a Furniture piece — the source page's own field list */
.pspec { margin: 24px 0 0; border-top: 1px solid var(--hairline-2); }
.pspec > div { display: flex; gap: 18px; padding: 10px 0; border-bottom: 1px solid var(--hairline); }
.pspec dt { flex: none; width: 132px; font-size: 13px; font-weight: 300; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-45); }
.pspec dd { margin: 0; font-size: 15px; line-height: 1.5; color: var(--ink-70); }
@media (max-width: 820px) {
  .prow { grid-template-columns: 1fr; min-height: 0; }
  .prow__media, .prow--flip .prow__media { order: 1; height: 475px; }
  /* The .prow sections carry `padding:0` inline so the photo can run
     edge to edge, which means the copy column has to supply the page's side
     margin itself. It was 4px — effectively none — so headings sat flush
     against the screen edge on Surfaces/Curtains/Fashion/Furniture.
     Fixed 2026-08-20. */
  .prow__copy, .prow--flip .prow__copy { order: 2; max-width: none; padding: 40px var(--pad-x); }
  /* the desktop grid-line alignment is meaningless once the row stacks */
  .prow--apps .prow__copy, .prow--apps.prow--flip .prow__copy,
  .prow--apps:not(.prow--flip) .prow__copy { padding: 40px var(--pad-x); }
  .psec { padding-top: 46px; padding-bottom: 46px; }
  .prow-sec--gap { padding-bottom: 46px; }
  .pspec > div { flex-direction: column; gap: 4px; }
  .pspec dt { width: auto; }
}

/* Image slider inside a .prow__media slot. Slides crossfade; site.js owns
   which one is active and advances on the progress bar's animationend, so
   the bar IS the timer — pausing the animation pauses the slider, with no
   separate JS clock to keep in sync. */
/* one place to tune how long each image holds before advancing */
.pslider { position: relative; width: 100%; height: 100%; overflow: hidden; --pslide-dwell: 7s; }
.pslide { position: absolute; inset: 0; opacity: 0; transition: opacity .9s ease; }
.pslide.is-active { opacity: 1; }
.pslide img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pslider__nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 3;
  width: 42px; height: 42px; border-radius: 50%; cursor: pointer;
  border: 1px solid rgba(250,250,248,.3); background: rgba(10,10,14,.45);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  display: flex; align-items: center; justify-content: center;
  opacity: 0; transition: opacity .25s, border-color .2s, background .2s;
}
.pslider:hover .pslider__nav, .pslider__nav:focus-visible { opacity: 1; }
.pslider__nav:hover { border-color: var(--ink); background: rgba(10,10,14,.72); }
.pslider__nav svg { width: 18px; height: 18px; fill: none; stroke: var(--ink); stroke-width: 1.5; }
.pslider__nav--prev { left: 14px; } .pslider__nav--next { right: 14px; }
/* one bar per image: count at a glance, and the fill shows time to the next */
.pslider__bars { position: absolute; left: 16px; right: 16px; bottom: 16px; z-index: 3; display: flex; gap: 6px; }
.pslider__bar {
  flex: 1 1 0; height: 3px; padding: 0; border: 0; cursor: pointer;
  background: rgba(250,250,248,.28); border-radius: 2px; overflow: hidden;
}
.pslider__fill { display: block; width: 100%; height: 100%; background: var(--ink); transform: scaleX(0); transform-origin: left center; }
.pslider__bar.is-active .pslider__fill { animation: pslideFill var(--pslide-dwell, 7s) linear forwards; }
@keyframes pslideFill { from { transform: scaleX(0); } to { transform: scaleX(1); } }
/* hovering the image pauses the bar, and therefore the slider itself */
.pslider:hover .pslider__bar.is-active .pslider__fill { animation-play-state: paused; }
@media (prefers-reduced-motion: reduce) {
  .pslide { transition: none; }
  .pslider__bar.is-active .pslider__fill { animation: none; transform: scaleX(1); }
  .pslider__nav { opacity: 1; }
}
@media (max-width: 620px) { .pslider__nav { opacity: 1; width: 36px; height: 36px; } }

/* The auto-scrolling .pmarquee strip that used to sit under Applications was
   removed on 2026-08-20: the client asked for the same .pslider used on the
   Furniture/Fashion rows there instead, so nothing referenced it any more. */

/* Applications with more than five entries (Curtains lists seven contexts)
   wrap onto a second row instead of squeezing into five columns. */
.psteps--many { grid-template-columns: repeat(4, 1fr); row-gap: 40px; }
@media (max-width: 1150px) { .psteps--many { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 820px)  { .psteps--many { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 520px)  { .psteps--many { grid-template-columns: 1fr; } }

/* Applications inside a .prow__copy half-column: one item per row with the
   numeral set beside the label instead of above it, so a 560px measure can
   carry seven contexts (Curtains) or five with descriptions (Surfaces). */
.psteps--stack { display: block; }
.psteps--stack .pstep { display: grid; grid-template-columns: 46px 1fr; column-gap: 18px; padding-top: 16px; margin-top: 16px; }
.psteps--stack .pstep:first-child { margin-top: 0; }
.psteps--stack .pstep__n { font-size: 26px; margin-bottom: 0; align-self: start; }
.psteps--stack .pstep__t { font-size: 19px; margin: 0; align-self: center; }
.psteps--stack .pstep__b { grid-column: 2; margin-top: 6px; }
@media (max-width: 520px) {
  .psteps--stack .pstep { grid-template-columns: 36px 1fr; column-gap: 12px; }
  .psteps--stack .pstep__n { font-size: 22px; }
}

/* ---------- contact: world map with location pins ----------
   The map is a separate SVG (Natural Earth 110m land, public domain) so the page
   stays light; pins are real elements positioned by percentage, which keeps them
   correct at any rendered width because the projection is equirectangular. */
.wmap { position: relative; width: 100%; }
.wmap__img { display: block; width: 100%; height: auto; opacity: .85; }
.wmap__pin { position: absolute; transform: translate(-50%, -50%); }
.wmap__dot {
  display: block; width: 9px; height: 9px; border-radius: 50%;
  background: var(--gold); box-shadow: 0 0 0 4px rgba(201,162,39,.22);
  position: relative; z-index: 2;
}
/* the slow pulse reads as "live", and stops for reduced-motion users */
.wmap__dot::after {
  content: ''; position: absolute; inset: -4px; border-radius: 50%;
  border: 1px solid var(--gold); opacity: .7;
  animation: wpulse 2.8s ease-out infinite;
}
@keyframes wpulse { 0% { transform: scale(.6); opacity: .8 } 100% { transform: scale(2.6); opacity: 0 } }
.wmap__tag {
  position: absolute; left: 16px; top: 50%; transform: translateY(-50%);
  white-space: nowrap; font-size: 13px; line-height: 1.35; color: var(--ink-70);
  background: rgba(10,10,14,.72); border: 1px solid var(--hairline);
  padding: 6px 10px; border-radius: var(--radius); backdrop-filter: blur(6px);
  opacity: 0; pointer-events: none; transition: opacity .25s;
}
.wmap__tag b { display: block; color: var(--ink); font-weight: 300; letter-spacing: .1em; text-transform: uppercase; font-size: 12px; }
/* pins near the right edge label leftwards so the tag never leaves the map */
.wmap__pin--flip .wmap__tag { left: auto; right: 16px; }
.wmap__pin:hover .wmap__tag { opacity: 1; }
@media (prefers-reduced-motion: reduce) { .wmap__dot::after { animation: none; } }
@media (max-width: 700px) {
  .wmap__tag { display: none; }
  .wmap__dot { width: 7px; height: 7px; }
}

/* ---------- collection fabric selector ----------
   Matches the client's close-up mockup: a plain tab row for the colour GROUP
   sitting directly on top of a bordered panel that holds the LED colours, each
   a separate dark card with a swatch, and the selected one outlined in white.
   Groups differ per collection and no group has every LED colour, so the grid
   renders only what the library actually holds. */
.fabsel { margin-top: 34px; }
.fabsel__lbl {
  font-size: 12px; font-weight: 300; letter-spacing: .14em; text-transform: uppercase;
  color: var(--ink-45); margin: 0 0 10px;
}
.fabsel__lbl--in { margin: 0 0 12px; }

/* tab row — no chrome of its own; the active tab is a lighter fill that reads as
   attached to the panel below it */
.fabsel__groups { display: flex; }
.fabgrp {
  flex: 1 1 0; padding: 14px 12px; cursor: pointer;
  background: transparent; border: 0;
  font-family: var(--sans); font-size: 15px; font-weight: 300; letter-spacing: .1em;
  text-transform: uppercase; color: var(--ink); transition: background .2s;
}
.fabgrp:hover { background: rgba(250,250,248,.06); }
.fabgrp[aria-selected="true"] { background: rgba(250,250,248,.11); }

.fabsel__panel { border: 1px solid rgba(250,250,248,.5); padding: 14px; }
.fabsel__leds { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.fabled {
  display: flex; align-items: center; gap: 12px; padding: 8px 10px; cursor: pointer;
  background: rgba(250,250,248,.055); border: 1px solid transparent; text-align: left;
  font-family: var(--sans); font-size: 14px; font-weight: 300; color: var(--ink);
  transition: background .2s, border-color .2s;
}
.fabled[hidden] { display: none; }
.fabled img { width: 40px; height: 40px; object-fit: cover; display: block; flex: none; }
.fabled:hover { background: rgba(250,250,248,.1); }
.fabled[aria-selected="true"] { border-color: var(--ink); background: rgba(250,250,248,.07); }

/* The one non-LED option in a group (the fabric's own colour with no fiber lit) —
   same size as the LED swatches, but ordered last and gold-bordered so it
   still doesn't read as just another colour choice. */
.fabled--off { order: 99; border-style: dashed; border-color: var(--gold); }
.fabled--off span { font-style: italic; color: var(--gold); }
.fabled--off[aria-selected="true"] { border-color: var(--gold-lt); background: rgba(201,162,39,.12); }

.fabsel__stage { position: relative; width: 100%; height: 100%; overflow: hidden; border-radius: var(--radius); background: var(--bg-deep); }
.fabsel__stage img {
  position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; display: block;
  transition: opacity .35s ease;
}
.fabsel__note { margin-top: 14px; }
@media (max-width: 620px) {
  .fabsel__leds { grid-template-columns: repeat(2, 1fr); }
  .fabgrp { font-size: 13px; padding: 12px 8px; }
}

/* ---------- collection "In place" mosaic ----------
   Client mockup: one tall tile on the left beside a 2x2 block. Degrades by tile
   count, because collections carry between 0 and 5 distinct applications. */
.mosaic { display: grid; gap: 20px; grid-template-columns: repeat(3, 1fr); }
.mosaic .tile { aspect-ratio: 4 / 3; }
/* 5 tiles: tall left spanning both rows, then 2x2 */
.mosaic--5 { grid-template-rows: repeat(2, 1fr); }
.mosaic--5 .tile:first-child { grid-row: 1 / span 2; aspect-ratio: auto; }
/* 4 tiles: plain 2x2, all tiles equal. (Previously a tall-left tile spanning
   both rows next to a nth-child(4) tile spanning 2 columns — that mixed
   single-width and double-width tiles into the SAME pair of 1fr row tracks,
   whose content-based height comes out uneven between the two rows on an
   auto-height grid, and it rendered as a blank gap where the wide tile's
   row should have been. A uniform 2x2 has no such mismatch.) */
.mosaic--4 { grid-template-columns: repeat(2, 1fr); }
.mosaic--3 { grid-template-columns: repeat(3, 1fr); }
.mosaic--2 { grid-template-columns: repeat(2, 1fr); }
.mosaic--1 { grid-template-columns: 1fr; }
.mosaic--1 .tile { aspect-ratio: 16 / 7; }
@media (max-width: 860px) {
  .mosaic, .mosaic--5, .mosaic--4 { grid-template-columns: repeat(2, 1fr); grid-template-rows: none; }
  .mosaic--5 .tile:first-child { grid-row: auto; aspect-ratio: 4 / 3; }
}
@media (max-width: 520px) {
  .mosaic, .mosaic--5, .mosaic--4, .mosaic--3, .mosaic--2 { grid-template-columns: 1fr; }
}

/* ---------- project lightbox ----------
   Clicking a mosaic tile whose GALLERY entry carries `project` opens this
   instead of navigating to the filtered Projects page. Plain <a href> stays
   underneath as the no-JS / no-match fallback. */
.lbx {
  position: fixed; inset: 0; z-index: 200;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; visibility: hidden; transition: opacity .25s ease, visibility .25s;
}
.lbx[data-open] { opacity: 1; visibility: visible; }
.lbx__scrim { position: absolute; inset: 0; background: rgba(5,6,9,.92); backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px); }
.lbx__body {
  position: relative; z-index: 1; width: min(1100px, 92vw); max-height: 88vh;
  display: flex; flex-direction: column; align-items: center;
  transform: scale(.97); transition: transform .25s ease;
}
.lbx[data-open] .lbx__body { transform: none; }
.lbx__figure { margin: 0; max-width: 100%; max-height: 74vh; display: flex; align-items: center; justify-content: center; }
.lbx__img { max-width: 100%; max-height: 74vh; width: auto; height: auto; display: block; border-radius: var(--radius); box-shadow: 0 30px 90px rgba(0,0,0,.6); }
/* Anchored to .lbx__body (not the figure) so it shares the next arrow's
   right edge and the two line up on one vertical axis — client, 2026-08-20.
   The maths: .lbx__nav is 52px at right:-20px, so its centre sits 6px inside
   the body's right edge; a 38px button needs right:-13px to land on the same
   centre. .lbx__body is a column flex whose first in-flow child is the
   figure, so top:-14px still reads as "just above the image's top edge". */
.lbx__close {
  position: absolute; top: -14px; right: -13px; width: 38px; height: 38px;
  border: 1px solid rgba(250,250,248,.3); border-radius: 50%; background: rgba(10,10,14,.7);
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  color: var(--ink); font-size: 22px; line-height: 1; cursor: pointer;
  display: flex; align-items: center; justify-content: center; transition: border-color .2s, color .2s;
}
.lbx__close:hover { border-color: var(--ink); color: var(--gold-lt); }
.lbx__nav {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 52px; height: 52px; border-radius: 50%; cursor: pointer;
  border: 1px solid rgba(250,250,248,.3); background: rgba(10,10,14,.4);
  display: flex; align-items: center; justify-content: center;
  transition: border-color .2s, background .2s;
}
.lbx__nav:hover { border-color: var(--ink); background: rgba(10,10,14,.7); }
.lbx__nav svg { width: 20px; height: 20px; fill: none; stroke: var(--ink); stroke-width: 1.4; }
.lbx__nav--prev { left: -20px; }
.lbx__nav--next { right: -20px; }
.lbx__foot {
  display: flex; align-items: center; justify-content: space-between; gap: 24px;
  width: 100%; margin-top: 18px;
  font-family: var(--sans); font-size: 14px; color: var(--ink-60);
}
.lbx__title { font-family: var(--serif); font-size: 18px; color: var(--ink); }
.lbx__count { letter-spacing: .06em; white-space: nowrap; }
@media (max-width: 720px) {
  .lbx__nav { width: 42px; height: 42px; }
  .lbx__nav--prev { left: 6px; } .lbx__nav--next { right: 6px; }
  .lbx__close { top: 8px; right: 8px; background: rgba(10,10,14,.5); }
  .lbx__foot { flex-direction: column; align-items: flex-start; gap: 6px; margin-top: 12px; }
}

/* ---------- studio comments widget ----------
   The review widget pins its button cluster to the top-right corner
   (#ddc-c-cluster { position:fixed; top:16px; right:16px; z-index:999998 } in
   /srv/studio/shared-assets/comments-widget.js) — exactly where this site puts
   its nav. At mobile widths it sat squarely on top of the burger, so the menu
   icon looked missing entirely (Ziad, 2026-08-27); at desktop widths it covers
   part of "Start an Inquiry". Moved to the bottom-right, which clears the nav
   at every width. !important because the widget injects its own stylesheet at
   runtime, after this file has loaded. Review-tier only — the widget tag is
   never on the production site. */
#ddc-c-cluster { top: auto !important; bottom: 16px !important; }


/* While the mobile drawer is open the page behind it must not scroll: on touch
   the gesture otherwise moves the document instead of the drawer, which reads
   as "the menu won't scroll". Set by assets/site.js alongside .nav.is-open. */
html.nav-locked, html.nav-locked body { overflow: hidden; }

/* ---------- contact form: required markers + spam check ----------
   Name and Email are mandatory (client, 2026-08-27); the asterisk is
   aria-hidden because the inputs carry aria-required, so a screen reader
   announces the requirement once rather than reading "star". */
.form .req { color: var(--gold); font-weight: 400; }

/* A self-contained arithmetic challenge, sitting below the form's fields and
   above Submit. Deliberately not reCAPTCHA/hCaptcha: those need site keys and a
   third-party script, and this tier is a prototype — see the note under the
   form. The markup is plain enough to swap out without touching the layout. */
/* .cap sits on a .field wrapper, so the label and input already inherit the
   form's styling — only the question/answer row needs laying out. */
.cap__row { display: flex; align-items: center; gap: 10px; }
.cap__q {
  font-size: 15px; color: var(--ink-70); white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.cap__in { max-width: 120px; }
.cap__new {
  background: none; border: 1px solid var(--hairline-2); color: var(--ink-60);
  border-radius: var(--radius); width: 34px; height: 34px; cursor: pointer;
  font-size: 15px; line-height: 1; transition: border-color .2s, color .2s;
}
.cap__new:hover { border-color: var(--gold); color: var(--gold); }
.cap__err { color: #E8766A; font-size: 13px; margin: 8px 0 0; }

/* Stop the page rubber-banding past its own content. On iOS a fling at the end
   of the document keeps scrolling into empty background below the footer, which
   reads as the page being taller than it is (Ziad, 2026-08-27: "it shouldn't be
   able to scroll down more than footer"). Chromium measures no overflow — the
   document ends exactly at the footer — so this targets the browser's overscroll
   effect rather than a layout bug. Note it also disables pull-to-refresh. */
html { overscroll-behavior-y: none; }

/* Keep the review widget off the footer's copyright line. It was moved out of
   the nav's corner earlier; bottom-right then landed it on the credit, so it
   sits a little higher than the default 16px. */
#ddc-c-cluster { bottom: 64px !important; }
