/* =========================================================================
   EVERLUX — "Maison" design system
   -------------------------------------------------------------------------
   Derived from a computed-style crawl of cartier.com (home / category / PLP).
   The findings that drive every rule below:

     · RADIUS   556 elements at 0px. One at 100%. There is no in-between.
     · COLOR    4 total. #1D1C1C ink, #FFF, #F9F9F9 whisper, + one accent
                used ONCE per page. No mid-gray ramp at all.
     · TYPE     h1 is 32px. Not 48. The luxury signal is SMALL type, uppercase,
                POSITIVE tracking (0.4–1.5px) — never large type.
     · MOTION   ~every transition is `opacity` @ .2–.5s ease-out, plus a slow
                `background-size`/scale image breathe. Zero translateY lifts.
     · DEPTH    No ambient shadows. Separation comes from whitespace and a
                single hairline. Nothing floats.

   The discipline is subtractive. Everything you'd normally add — a shadow, a
   radius, a gradient, a second accent colour — is the thing to remove.
   ========================================================================= */

:root {
	/* ---- Colour. Five. That's the whole palette. ------------------------ */
	--ink:      #1d1c1c;  /* Cartier's exact body ink. Warm near-black, not #000. */

	/* The ground is CREAM, not white — the "jewelry store" move. Pure #fff read
	   as blinding/clinical: Cartier gets away with white because their pages are
	   photograph-dense, and ours have bare stretches. Gallery-cream intensity —
	   warm enough that returning to a white site feels cold, quiet enough to
	   read as linen rather than yellow.
	   NOTE: the packshots stay baked on pure white and still work — they render
	   with mix-blend-mode: multiply, and white multiplies to whatever is behind
	   it, so the boards float on cream for free. Do not regenerate them. */
	--paper:    #f8f5ee;
	--whisper:  #f0ebe1;  /* the tile / alt-section ground. Deeper cream, not grey. */
	--hairline: #e3dcce;  /* borders. warm, so it reads as paper, not chrome. */

	/* Dark grounds only — the CTA bands, the footer, the lightbox backdrop.
	   One step warmer than the text ink so dark sections read velvet-lined
	   rather than charcoal. Text stays --ink; this is never a type colour. */
	--espresso: #221d18;

	/* Type-only tints. Deliberately NOT a ramp — two steps, no more. */
	--ink-soft: #6b6866;  /* captions, meta, disabled */
	--on-dark:  #f2f0ed;

	/* The green survives in the logo only. It is not a UI colour.
	   Kept as a token purely so nothing hardcodes it back in by accident. */
	--brand-legacy: #149150;

	/* ---- Type ---------------------------------------------------------- */
	--display: "Cormorant Garamond", "Times New Roman", Times, serif;
	--sans:    "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;

	/* The UI voice: small, uppercase, wide-tracked. Used for nav, buttons,
	   eyebrows, labels, prices — every non-prose string on the site. */
	--ui-size:   0.6875rem; /* 11px */
	--ui-weight: 600;
	--ui-track:  0.14em;

	/* ---- Metrics ------------------------------------------------------- */
	--radius: 0;            /* load-bearing. do not raise this. */
	--gutter: clamp(1.25rem, 4vw, 3rem);
	--bleed:  clamp(1.25rem, 3vw, 4.5rem);  /* full-bleed page inset */
	--container: 1440px;
	--measure: 34rem;       /* prose max width */

	/* Vertical rhythm — generous. Whitespace is the only luxury signal that
	   costs nothing and can't be faked. */
	--space-section: clamp(5rem, 10vw, 9rem);
	--space-tight:   clamp(3rem, 6vw, 5rem);

	/* ---- Motion -------------------------------------------------------- */
	/* Cartier's actual values, verbatim from the crawl. */
	--fade:  .45s ease-out;      /* opacity, the default transition */
	--quick: .2s  ease-in-out;   /* hover state changes */
	--breathe: 1.2s cubic-bezier(.22,.61,.36,1); /* the slow image scale */
}

/* ---- Reset ------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }
html.lenis, html.lenis body { height: auto; }
.lenis.lenis-smooth { scroll-behavior: auto !important; }
.lenis.lenis-stopped { overflow: hidden; }

html { overflow-x: clip; }

/* Scroll lock — set by maison.js while the mobile nav or an overlay is open.
   Lenis has its own .lenis-stopped, but that only exists when Lenis runs; this
   class also covers native/touch scrolling and reduced-motion users. */
/* Lock on <html> ONLY. Putting overflow:hidden on body as well (html already
   non-visible) turns body into its own scroll container — the same trap noted
   on body below — and the sticky .site-header then sticks to body (scrollTop 0)
   instead of the viewport, so opening the mobile nav mid-page snapped the
   header back up to the top of the document, out of view. */
html.is-locked { overflow: hidden; }
body {
	margin: 0;
	/* Backstop against horizontal scroll, NOT the fix for it — the real causes
	   get fixed at the source (the .values inline-style bug was one). `clip`
	   rather than `hidden`: hidden turns body into a scroll container, which
	   breaks position:sticky descendants; clip just truncates. */
	overflow-x: clip;
	font-family: var(--sans);
	font-size: 0.9375rem; /* 15px — Cartier body sits at 14–16 */
	line-height: 1.65;
	color: var(--ink);
	background: var(--paper);
	-webkit-font-smoothing: antialiased;
	text-rendering: optimizeLegibility;
}

img { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }
button { font: inherit; cursor: pointer; background: none; border: 0; color: inherit; }

/* Nothing on this site has a radius or a shadow. Enforced, not suggested. */
* { border-radius: var(--radius); }

.screen-reader-text {
	position: absolute; width: 1px; height: 1px;
	overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}
.skip-link { position: absolute; left: -9999px; }
.skip-link:focus {
	left: 1rem; top: 20%; background: var(--ink); color: var(--paper);
	padding: .75rem 1.25rem; z-index: 999;
}

/* Focus: a hairline, not a glow. */
:focus-visible { outline: 1px solid var(--ink); outline-offset: 3px; }

/* Selecting text inverts to ink-on-cream — the same pair, flipped. */
::selection { background: var(--ink); color: var(--paper); }

/* =========================================================================
   TYPE PRIMITIVES
   ========================================================================= */

/* The display serif. Light weight, tight leading, NEVER large.
   Cartier's h1 is 32px — the restraint IS the signal. */
h1, h2, h3, h4 {
	font-family: var(--display);
	font-weight: 300;
	line-height: 1.12;
	letter-spacing: 0;
	color: var(--ink);
	margin: 0 0 .6em;
}

h1 { font-size: clamp(1.875rem, 3.2vw, 2.75rem); }  /* 30 → 44px. Capped low on purpose. */
h2 { font-size: clamp(1.625rem, 2.6vw, 2.25rem); }  /* 26 → 36px */
h3 { font-size: clamp(1.25rem, 1.6vw, 1.5rem); }
h4 { font-size: 1.125rem; }

/* The UI voice. Every non-prose string wears this. */
.ui, .eyebrow, .btn, .link-rule, .nav-link, .facet__legend, .pcard__name, .price {
	font-family: var(--sans);
	font-size: var(--ui-size);
	font-weight: var(--ui-weight);
	letter-spacing: var(--ui-track);
	text-transform: uppercase;
	line-height: 1.5;
}

.eyebrow {
	display: block;
	color: var(--ink-soft);
	margin: 0 0 1.5rem;
}
/* No decorative dash before the eyebrow. It was chrome. It's gone. */

.lede {
	font-family: var(--sans);
	font-size: 1rem;
	line-height: 1.7;
	color: var(--ink-soft);
	max-width: var(--measure);
	margin: 0;
}

/* The statement block — the breath between the hero and the first panel.
   Centred, narrow, and surrounded by far more space than the words need. That
   ratio IS the message: a brand confident enough to stop talking. */
.statement { text-align: center; }
.statement .serif-copy {
	margin-inline: auto;
	font-size: clamp(1.25rem, 1.9vw, 1.6rem);
	line-height: 1.5;
	color: var(--ink);
	max-width: 40rem;
}

/* Serif body — Cartier uses their serif for short editorial copy at 16px. */
.serif-copy {
	font-family: var(--display);
	font-size: 1.25rem;
	line-height: 1.55;
	font-weight: 300;
	color: var(--ink);
	max-width: var(--measure);
}

.prose { max-width: var(--measure); }
.prose p { color: var(--ink); margin: 0 0 1.4em; }

/* =========================================================================
   LAYOUT
   ========================================================================= */
.container { width: min(100% - (var(--bleed) * 2), var(--container)); margin-inline: auto;     display: grid;
    grid-template-columns: 1fr;
    gap: 1rem;
    align-items: start;}

	.container:has(.form-wrap){
		    grid-template-columns: 1fr 1fr;
	}
.bleed     { width: calc(100% - (var(--bleed) * 2)); margin-inline: auto; }
.full      { width: 100%; }

.section        { padding-block: var(--space-section); }
.section--tight { padding-block: var(--space-tight); }
.section--whisper { background: var(--whisper); }
.section--ink   { background: var(--espresso); color: var(--on-dark); }
.section--ink h1, .section--ink h2, .section--ink h3 { color: var(--paper); }
.section--ink .eyebrow { color: rgba(242,240,237,.55); }

/* Section headers are LEFT-aligned and narrow. Centered headers with a big
   serif line is the "premium template" look we're specifically avoiding. */
.section-head { max-width: 40rem; margin-bottom: clamp(2.5rem, 5vw, 4rem); }
.section-head h2 { margin-bottom: .5rem; }
.section-head--split {
	max-width: none;
	display: flex; align-items: flex-end; justify-content: space-between;
	gap: 2rem; flex-wrap: wrap;
}

/* =========================================================================
   BUTTONS — two kinds. Filled black, or text-with-a-rule. Nothing else.
   No radius. No shadow. No lift on hover. Colour inverts, that's all.
   ========================================================================= */
.btn {
	display: inline-flex; align-items: center; gap: .6rem;
	padding: 1.0625rem 2.25rem;
	border: 1px solid var(--ink);
	background: var(--ink);
	color: var(--paper);
	transition: background var(--quick), color var(--quick), border-color var(--quick);
	white-space: nowrap;
}
.btn:hover { background: transparent; color: var(--ink); }

.btn--ghost { background: transparent; color: var(--ink); }
.btn--ghost:hover { background: var(--ink); color: var(--paper); }

/* On dark backgrounds, invert the pair. */
.section--ink .btn, .btn--on-dark {
	background: var(--paper); color: var(--ink); border-color: var(--paper);
}
.section--ink .btn:hover, .btn--on-dark:hover {
	background: transparent; color: var(--paper);
}

/* The workhorse link: a word and a rule that draws itself in.
   This replaces every arrow-pill CTA in the old build. */
.link-rule {
	display: inline-block;
	position: relative;
	padding-bottom: .5rem;
	color: var(--ink);
}
.link-rule::after {
	content: "";
	position: absolute; left: 0; bottom: 0;
	width: 100%; height: 1px;
	background: currentColor;
	transform: scaleX(1);
	transform-origin: left;
	transition: transform var(--breathe);
}
.link-rule:hover::after { transform: scaleX(0); transform-origin: right; }

/* =========================================================================
   HEADER — centred, two-tier. This is the single most recognisable move on
   cartier.com, and the fastest way to stop looking like a materials company:
   logo-left/nav-right is the default every competitor ships.
   ========================================================================= */
.site-header {
	position: sticky; top: 0; z-index: 100;
	background: var(--paper);
	border-bottom: 1px solid var(--hairline);
	transition: background var(--fade), border-color var(--fade);
}

/* No transparent-over-hero variant. The bar is always solid and always above
   the photograph — see the note in header.php. */

/* Tier 1 — utility rail + centred wordmark */
.header-top {
	position: relative;
	display: flex; align-items: center; justify-content: space-between;
	padding: 1.125rem var(--bleed);
}
.header-util { display: flex; gap: 1.75rem; }
.header-util a { font-family: var(--sans); font-size: var(--ui-size); font-weight: 500; letter-spacing: var(--ui-track); text-transform: uppercase; opacity: .75; transition: opacity var(--quick); }
.header-util a:hover { opacity: 1; }

.brand {
	position: absolute; left: 50%; top: 50%;
	transform: translate(-50%, -50%);
	display: flex; flex-direction: column; align-items: center;
	line-height: 1;
}
/* The real Everlux letterforms, extracted from the supplied badge (see the note
   in header.php). Targeted as a descendant `img` rather than by class, because
   the WebP plugin rewraps every <img> in a <picture> and a class on the img
   would land on the wrapper instead. */
.brand img {
	display: block;
	height: 22px;
	width: auto;
}
.brand__sub {
	font-family: var(--sans);
	font-size: .5625rem; font-weight: 500;
	letter-spacing: .34em;   /* wide enough to optically match the wordmark's width */
	text-transform: uppercase;
	color: currentColor; opacity: .7;
	margin-top: .3rem; margin-right: -.34em; /* kill the trailing letter-space */
}

.header-actions { display: flex; align-items: center; gap: 1.25rem; }
.icon-btn { display: inline-flex; opacity: .8; transition: opacity var(--quick); }
.icon-btn:hover { opacity: 1; }
.icon-btn svg { width: 19px; height: 19px; }
.cart-count {
	font-family: var(--sans); font-size: .625rem; font-weight: 600;
	letter-spacing: .04em; margin-left: .3rem;
}

/* Tier 2 — centred primary nav */
.header-nav {
	border-top: 1px solid var(--hairline);
	background-color: var(--whisper);
	display: flex; align-items: center; justify-content: space-between;
	gap: 2.5rem;
	padding: .5rem var(--bleed) .5rem;
}
.nav-link {
	position: relative;
	padding-bottom: .25rem;
	transition: opacity var(--quick);
}
.nav-link::after {
	content: "";
	position: absolute; left: 0; bottom: 0;
	width: 100%; height: 1px;
	background: currentColor;
	transform: scaleX(0); transform-origin: right;
	transition: transform .4s cubic-bezier(.22,.61,.36,1);
}
.nav-link:hover::after { transform: scaleX(1); transform-origin: left; }

/* The duplicated utility links inside the nav exist for the mobile panel only —
   on desktop the utility rail already shows them. */
.header-nav__util { display: none; }

.nav-toggle { display: none; flex-direction: column; gap: 4px; padding: .5rem; }
.nav-toggle span {
	display: block; width: 20px; height: 1px; background: currentColor;
	transition: transform var(--quick), opacity var(--quick);
}
/* Open state: the bars become the close glyph. The one place a transform is
   allowed to move something — a hamburger that doesn't become an X leaves
   people hunting for how to close the menu. */
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(5px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-5px) rotate(-45deg); }

/* The old .hero (text + buttons overlaid on a full-bleed image, with a scrim to
   keep them legible) is gone. It has been replaced by .panel--hero below, which
   is the same photograph with nothing on top of it. The scrim went with it —
   once no type sits on the image, nothing needs darkening. */

/* =========================================================================
   PAGE HERO — the plain title bar, for pages not yet converted to blocks.
   Deliberately austere: a rule and a name. It is a placeholder, and it should
   look like one rather than pretending to be a designed header.
   ========================================================================= */
.page-hero {
	padding: clamp(4rem, 8vw, 7rem) 0 clamp(2.5rem, 4vw, 3.5rem);
	border-bottom: 1px solid var(--hairline);
}
.page-hero__title { margin: 0; }

/* =========================================================================
   EDITORIAL PANELS — the spine of the homepage.
   -------------------------------------------------------------------------
   This is cartier.com's actual homepage structure, and it is almost entirely
   photographs. Four near-full-screen images stacked down the page, each one
   followed by a SMALL centred caption on white — a tracked uppercase label, a
   line or two of serif, and one underlined link. Then, and only then, a single
   product grid.

   The rule that makes it work, and the one everybody breaks: THERE IS NO TEXT
   ON THE PHOTOGRAPH. Cartier's hero carries no headline at all. The image is
   allowed to be an image, and the words wait their turn underneath it. Putting
   a headline and two buttons over the hero is precisely the move that makes a
   site look like it is selling you something.
   ========================================================================= */
/* position:relative is load-bearing — .panel__overlay is absolutely positioned
   and anchors to this. Without it the overlay escapes to the initial containing
   block: on the homepage the hero is ~100vh so it landed correctly by accident,
   but a 52vh page header threw its title hundreds of pixels down the page, on
   top of the next section. */
.panel { position: relative; display: block; }

.panel__media {
	position: relative;
	width: 100%;
	height: 72vh;
	min-height: 420px;
	overflow: hidden;
	background: var(--whisper);
}
.panel--hero .panel__media { height: 84vh; min-height: 520px; }

/* The hero — and ONLY the hero — carries type on the image: an eyebrow, a line,
   and one call to action. Every panel below it keeps its words underneath the
   photograph, which is what stops the page reading as a sales pitch.
   One button, not two. The second CTA is always the one that kills the mood. */
.panel__overlay {
	position: absolute;
	left: 0; right: 0; bottom: 0;
	padding: 0 var(--bleed) clamp(3rem, 6vw, 5rem);
	color: var(--paper);
	max-width: 40rem;
}
.panel__overlay .eyebrow { color: rgba(255,255,255,.72); margin-bottom: 1rem; }
.panel__overlay h1 { color: var(--paper); margin-bottom: 1rem; }
.panel__overlay p {
	font-family: var(--sans);
	font-size: .9375rem; line-height: 1.7;
	color: rgba(255,255,255,.82);
	max-width: 26rem;
	margin: 0 0 2rem;
}

/* Corner triangle behind the overlay label.
   A right triangle anchored to the panel's bottom-left corner, with the long
   edge running from top-left to bottom-right. Its two short sides are a multiple
   of the overlay's own box (the label text), so the text always fits inside the
   triangle however long the heading is. Sharp edges, no radius, so it matches
   the rest of the system. */
.panel__overlay {
	isolation: isolate;  /* keeps the z-index:-1 triangle behind the text but above the photo */
	--tri-color: rgba(29, 28, 28, .5);  /* the ink tone the scrims already use */
	--tri-x: 1.25;  /* horizontal side = 1.9 × overlay width */
	--tri-y: 2;  /* vertical side = 2.1 × overlay height */
	--tri-soft: 7rem;  /* width of the fade across the diagonal edge */
}

.panel--hero > .panel__overlay{
	--tri-x: 2;
	--tri-y: 2;
}

.panel__overlay::before {
	content: "";
	position: absolute;
	left: 0; bottom: 0;
	z-index: -1;
	width: calc(100% * var(--tri-x));
	height: calc(100% * var(--tri-y));
	background: linear-gradient(
		to top right,
		var(--tri-color) calc(50% - var(--tri-soft)),
		transparent      50%
	);
	pointer-events: none;
}

/* Keep a tall triangle (long headings on small screens) from spilling above the
   photo. `clip` rather than `hidden`, so .panel doesn't become a scroll container. */
.panel--hero,
.panel--pagehead { overflow: clip; }

/* A scrim only where the type actually sits. The header is solid and above the
   image, so the top of the photograph needs no darkening at all. */
.panel--hero .panel__media::after {
	content: "";
	position: absolute; inset: 0;
	background: linear-gradient(to top, rgba(29,28,28,.6) 0%, rgba(29,28,28,.18) 40%, transparent 68%);
	pointer-events: none;
}

.panel__media img {
	width: 100%; height: 100%;
	object-fit: cover;
	transform: scale(1.04);
	transition: transform 3s cubic-bezier(.22,.61,.36,1);
}
.is-ready .panel--hero .panel__media img { transform: scale(1); }
.panel:hover .panel__media img { transform: scale(1); }

/* ---- Hero tone explorer -------------------------------------------------
   The tone frames stack over the default photograph and FADE — the sanctioned
   crossfade, nothing slides. They sit under the scrim (::after paints above
   the children), so the overlay type stays legible on every tone. */
.hero-frame {
	position: absolute; inset: 0;
	opacity: 0;
	transition: opacity var(--fade);
	pointer-events: none;
}
.hero-frame.is-active { opacity: 1; }
.hero-frame img { width: 100%; height: 100%; object-fit: cover; }

/* The chip row: the gallery's word-and-rule control, re-grounded for the dark
   scrim it sits on. Small on purpose — it's an invitation, not a toolbar. */
.hero-tones {
	display: flex; align-items: baseline; flex-wrap: wrap;
	gap: 1.5rem;
	margin-top: 2rem;
}
.hero-tones__label {
	font-family: var(--sans);
	font-size: var(--ui-size); font-weight: var(--ui-weight);
	letter-spacing: var(--ui-track); text-transform: uppercase;
	color: rgba(255,255,255,.6);
}
.hero-tones .gfilter__btn { color: rgba(255,255,255,.72); }
.hero-tones .gfilter__btn:hover,
.hero-tones .gfilter__btn.is-active { color: var(--paper); }
.hero-tones .gfilter__btn::after { background: var(--paper); bottom: 0; }
/* Without JS the chips are dead controls — the hero stays the plain photograph. */
html:not(.js) .hero-tones { display: none; }

/* The caption. Centred, narrow, and quiet — it sits UNDER the image, never on
   it. Cartier gives this block roughly a third of the vertical space it gives
   the photograph, which is the whole hierarchy in one ratio. */
.panel__caption {
	text-align: center;
	max-width: 34rem;
	margin-inline: auto;
	padding: clamp(2.75rem, 5vw, 4.25rem) var(--bleed) clamp(3.5rem, 6vw, 5.5rem);
}
.panel__caption .eyebrow { margin-bottom: 1rem; }
.panel__caption h1,
.panel__caption h2 { margin-bottom: 1.125rem; }
.panel__caption p {
	font-family: var(--display);
	font-size: 1.0625rem;
	line-height: 1.6;
	font-weight: 300;
	color: var(--ink-soft);
	margin: 0 0 1.75rem;
}

/* =========================================================================
   PRODUCT GRID — chromeless. THE SKU ANSWER.
   -------------------------------------------------------------------------
   Cartier lists hundreds of SKUs. Their product "card" has no card: no border,
   no shadow, no radius, no background box. Image sits on a whisper tile, then
   name / variant / price stack beneath in centred UI type. The density comes
   free because each item carries ZERO chrome — 12 items read calmer than 4 of
   our old shadowed cards. This is what lets a 200-SKU catalogue stay quiet.
   ========================================================================= */
.pgrid {
	display: grid;
	grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
	gap: clamp(2rem, 3.5vw, 3.5rem) clamp(1.5rem, 2.5vw, 2.5rem);
}
.pgrid--4 { grid-template-columns: repeat(4, 1fr); }

.pcard { display: flex; flex-direction: column; }
.pcard__media {
	position: relative;
	display: block;
	aspect-ratio: 1 / 1;
	background: var(--whisper);   /* the tile — separation without a border */
	overflow: hidden;
	margin-bottom: 1.25rem;
}
.pcard__media img {
	width: 100%; height: 100%; object-fit: cover;
	transition: opacity var(--fade), transform var(--breathe);
}

/* Packshot tiles — the plank/panel renders.
   `multiply` knocks the white out against the whisper tile and keeps the
   render's soft contact shadow, so the board floats rather than sitting in a
   visible photo box. (A rembg cutout would be worse: it takes the silhouette
   and discards the shadow, and the plank reads as pasted-on. Only reach for a
   real cutout if we ever float a packshot on a DARK section, where multiply
   fails.)

   These point at uploads/everlux-packshots/, which are normalised copies — the
   originals each sat on a 2000² canvas with the board occupying a different box
   (1749x844, 1626x751, 1371x844 …), so the dead margin was baked into the
   pixels and NO uniform CSS scale could line the grid up. Every normalised file
   places its board at the same width, optically centred, so the tiles need no
   transform at all. Regenerate with scratchpad/normalize-packshots.mjs. */
.pcard__media--packshot {
	display: grid; place-items: center;
	padding: clamp(1rem, 2vw, 2rem);
	/* The vitrine: a hairline frame around each finish tile — the display-case
	   glass. Products get cases; photographs of rooms do not, so this stays off
	   the gallery/lifestyle tiles. */
	border: 1px solid var(--hairline);
}
.pcard__media--packshot img {
	mix-blend-mode: multiply;
	object-fit: contain;
}
.pcard:hover .pcard__media--packshot img { transform: scale(1.05); }
/* The hover-swap survives from the old build, but it FADES — it doesn't slide. */
.pcard__media .swap-back {
	position: absolute; inset: 0;
	opacity: 0;
	transition: opacity var(--fade);
}
.pcard:hover .swap-back { opacity: 1; }
.pcard:hover .pcard__media img { transform: scale(1.04); }  /* the slow breathe */
/* On a packshot tile the hover image is a ROOM, not a board — it must not
   inherit the multiply blend (a photograph multiplied onto cream goes muddy)
   or the contain fit, and it fills right over the tile's vitrine padding. */
.pcard__media--packshot .swap-back img {
	mix-blend-mode: normal;
	object-fit: cover;
	width: 100%; height: 100%;
}

.pcard__body { text-align: center; }
.pcard__name {
	display: block;
	color: var(--ink);
	margin-bottom: .5rem;
}
/* The variant line is the serif — Cartier's "Yellow gold" line. For us that's
   the finish name, which is exactly the thing a flooring buyer scans for. */
.pcard__variant {
	display: block;
	font-family: var(--display);
	font-size: 1rem;
	font-weight: 400;
	text-transform: none;
	letter-spacing: 0;
	color: var(--ink-soft);
	margin-bottom: .5rem;
}
/* The price is a serif figure — a handwritten tag, not a barcode. It opts out
   of the tracked-caps UI voice it would otherwise inherit from the .price rule
   in the type-primitives block, so every property that voice sets is unset
   here, not just some. (Tabular contexts — the accessory list, the qty-tier
   table — keep sans for scannability.) */
.price {
	display: block;
	font-family: var(--display);
	font-size: 1.125rem;
	font-weight: 400;
	letter-spacing: 0;
	text-transform: none;
	color: var(--ink);
}
.price__unit {
	font-family: var(--sans);
	font-size: .6875rem;
	text-transform: none; letter-spacing: .02em;
	color: var(--ink-soft); font-weight: 400;
}
/* "sold by the box · $61.93" — the transactional truth under the comparison
   figure. Quiet on purpose; the per-sqft number is what shoppers compare. */
.pcard__boxnote {
	display: block;
	font-family: var(--sans);
	font-size: .6875rem;
	letter-spacing: .03em; text-transform: none;
	color: var(--ink-soft);
	margin-top: .35rem;
}
.pcard__boxnote .amount { color: var(--ink-soft); }

/* The editorial interruption. Cartier drops a lifestyle tile INTO the product
   grid every ~8 items so a long list never reads as a spreadsheet. This is the
   pressure-release valve that makes high SKU counts survivable. */
.pgrid__feature {
	grid-column: span 2;
	display: flex; flex-direction: column;
}
/* Drop the fixed aspect and let the media stretch to whatever height the row's
   1:1 packshot tiles set. A hard 16/11 here made the feature taller than its
   neighbours and dragged the whole row out of square. */
.pgrid__feature .pcard__media {
	aspect-ratio: auto;
	flex: 1;
	min-height: 0;
}
.pgrid__feature .pcard__body { text-align: center; }
.pgrid__feature h3 { margin-bottom: .75rem; }

@media (max-width: 900px) {
	.panel__overlay{
		--tri-x: 1;
    --tri-y: 2;}
	.lightbox .lightbox__nav{
		top: auto;
	}
	.lightbox .lightbox__figure{
		padding: 0;
	}
	.pgrid--4 { grid-template-columns: repeat(2, 1fr); }
	.pgrid__feature { grid-column: span 2; }
}

/* =========================================================================
   FACETED RAIL — the other half of the SKU answer.
   Plain checkboxes. Uppercase legends. No pills, no chips, no accordions with
   chrome. Cartier filters jewellery by metal / stone / shape; you filter by
   finish / width / room / collection. Same shape of problem, same solution.
   ========================================================================= */
.plp { display: grid; grid-template-columns: 220px 1fr; gap: clamp(2rem, 5vw, 5rem); }

.facets { position: sticky; top: 140px; align-self: start; }
.facet { border-bottom: 1px solid var(--hairline); padding: 1.5rem 0; }
.facet:first-child { padding-top: 0; }
.facet__legend {
	display: flex; align-items: center; justify-content: space-between;
	width: 100%;
	color: var(--ink);
	margin-bottom: 1.125rem;
}
.facet__legend svg { width: 12px; height: 12px; opacity: .5; }
.facet__list { list-style: none; margin: 0; padding: 0; display: grid; gap: .875rem; }
.facet__item { display: flex; align-items: center; gap: .75rem; }
/* A real square. Not a rounded toggle, not a pill. */
.facet__box {
	appearance: none;
	width: 13px; height: 13px;
	border: 1px solid var(--ink-soft);
	background: transparent;
	flex: none;
	cursor: pointer;
	transition: background var(--quick), border-color var(--quick);
}
.facet__box:checked { background: var(--ink); border-color: var(--ink); }
.facet__label {
	font-family: var(--sans); font-size: .75rem; font-weight: 400;
	letter-spacing: .06em; text-transform: uppercase;
	color: var(--ink); cursor: pointer;
}
.facet__more {
	font-family: var(--sans); font-size: .75rem; letter-spacing: .06em;
	text-transform: uppercase; color: var(--ink-soft);
	margin-top: 1rem; text-decoration: underline; text-underline-offset: 3px;
}

.plp__bar {
	display: flex; align-items: baseline; justify-content: space-between;
	padding-bottom: 1.5rem; margin-bottom: 2.5rem;
	border-bottom: 1px solid var(--hairline);
}
.plp__count { font-family: var(--sans); font-size: .75rem; letter-spacing: .08em; text-transform: uppercase; color: var(--ink-soft); }

/* The count beside a facet — present, but never competing with the label. */
.facet__n { color: var(--ink-soft); font-weight: 400; margin-left: .15rem; }

.facet__clear {
	font-family: var(--sans); font-size: .6875rem; font-weight: 600;
	letter-spacing: var(--ui-track); text-transform: uppercase;
	color: var(--ink);
	padding: 1.5rem 0 0;
	text-decoration: underline; text-underline-offset: 4px;
}

.plp__empty {
	font-family: var(--display);
	font-size: 1.25rem; font-weight: 300;
	color: var(--ink-soft);
	padding: 4rem 0;
}

/* A hidden card must not leave a hole in the grid — `hidden` alone is defeated
   by the grid item's display. Same for the feature tile, which steps out of
   the way while a collection tone filter is active. */
.pgrid .pcard[hidden] { display: none; }
.pgrid .pgrid__feature[hidden] { display: none; }

/* =========================================================================
   ACCESSORIES — trims and mouldings. Present, findable, out of the way.
   A plain list of rules. These are $10 parts; dressing them up as products is
   what made the old shop grid read as a warehouse.
   ========================================================================= */
.accessories {
	list-style: none; padding: 0;
	/*columns: 2; column-gap: clamp(2rem, 5vw, 4rem); */
	margin: 0 auto;
    max-width: 60rem;
	border-top: 1px solid var(--hairline);
}
.accessory {
	display: flex; align-items: baseline; justify-content: space-between;
	gap: 1.5rem;
	padding: 1rem 0;
	border-bottom: 1px solid var(--hairline);
	break-inside: avoid;
}
.accessory__name {
	font-family: var(--sans); font-size: .8125rem;
	letter-spacing: .04em;
	color: var(--ink);
	transition: opacity var(--quick);
}
.accessory__name:hover { opacity: .6; }
.accessory__price {
	font-family: var(--sans); font-size: .75rem; font-weight: 600;
	letter-spacing: .06em;
	color: var(--ink-soft);
	white-space: nowrap;
}
.accessory__price .amount { color: var(--ink); }

@media (max-width: 760px) {
	.accessories { columns: 1; }
}

@media (max-width: 900px) {
	.plp { grid-template-columns: 1fr; }
	.facets { position: static; }
}

/* =========================================================================
   EDITORIAL SPLIT — image + copy. No card, no shadow, no radius.
   ========================================================================= */
.split {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: clamp(2rem, 6vw, 6rem);
	align-items: center;
}
.split + .split { margin-top: var(--space-section); }
.split--reverse .split__media { order: 2; }
.split__media { overflow: hidden; }
.split__media img {
	width: 100%; aspect-ratio: 4 / 5; object-fit: cover;
	/* Bias the crop low. These are landscape interior renders squeezed into a
	   portrait frame, and a centred crop hands half the frame to the ceiling —
	   on a flooring site, the bottom of the photograph is the product. */
	object-position: center 72%;
	transition: transform 1.6s cubic-bezier(.22,.61,.36,1);
}
.split:hover .split__media img { transform: scale(1.03); }
.split__body { max-width: 30rem; }
.split__cta { margin: 2rem 0 0; }

/* The dark CTA band. Centred — it's the one block on the page that is allowed
   to ask for something directly, so it stops pretending to be editorial. */
.cta-block { text-align: center; grid-template-columns: 1fr; gap: 1rem;}
.cta-block__action { margin: 2.5rem 0 0; }

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

/* =========================================================================
   DUET — two images at offset heights. The luxury-editorial pair.
   Unequal columns, unequal aspects, and the second image dropped — level
   two-ups read as comparison tables; offset pairs read as spreads.
   ========================================================================= */
.duet {
	display: grid;
	grid-template-columns: 7fr 5fr;
	gap: clamp(1.5rem, 4vw, 4rem);
	align-items: start;
}
.duet__a img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }
.duet__b { margin-top: clamp(2.5rem, 7vw, 6rem); }
.duet__b img { width: 100%; aspect-ratio: 4 / 5; object-fit: cover; }
.duet__caption {
	font-family: var(--sans);
	font-size: .6875rem; letter-spacing: .12em; text-transform: uppercase;
	color: var(--ink-soft);
	text-align: center;
	margin: 0 0 clamp(1.5rem, 3vw, 2.5rem) 0;
}
@media (max-width: 860px) {
	.duet { grid-template-columns: 1fr; }
	.duet__b { margin-top: 0; }
}

/* =========================================================================
   VALUES — a hairline row. No icon circles, no boxes, no shadows.
   The old build had 4 shadowed cards with green icon chips. All chrome. Gone.
   ========================================================================= */
.values {
	display: grid;
	/* --value-cols comes from the template as a custom property (never an inline
	   grid-template-columns — inline outranks the mobile media query below, and
	   that exact mistake forced 4 columns onto phones and gave the site
	   horizontal scroll). */
	grid-template-columns: repeat(var(--value-cols, 4), 1fr);
}
.value {
	padding: 0 2rem 0 0;
	border-right: 1px solid var(--hairline);
	text-align: center;
}
.value:last-child { border-right: 0; }
.value:not(:first-child) { padding-left: 2rem; }
.value h3 {
	font-family: var(--sans);
	font-size: var(--ui-size); font-weight: var(--ui-weight);
	letter-spacing: var(--ui-track); text-transform: uppercase;
	margin-bottom: .875rem;
}
.value p { font-size: .875rem; line-height: 1.7; color: var(--ink-soft); margin: 0; }
/* When the column is a route, say so — the rule draws itself under the heading. */
.value__link { position: relative; display: inline-block; padding-bottom: .35rem; font-size: .75rem; }
.value__link::after {
	content: "";
	position: absolute; left: 0; bottom: 0;
	width: 100%; height: 1px;
	background: currentColor;
	transform: scaleX(0); transform-origin: right;
	transition: transform .4s cubic-bezier(.22,.61,.36,1);
}
.value__link:hover::after { transform: scaleX(1); transform-origin: left; }

@media (max-width: 860px) {
	.values { grid-template-columns: repeat(2, 1fr); }
	.value:nth-child(2n) { border-right: 0; }
}

/* Small screens: one column, and unlinked headings become disclosure buttons
   (injected by maison.js — see initValues; linked headings stay links and
   their columns stay open). The marker is a content swap, not a rotation. */
@media (max-width: 700px) {
	.values { grid-template-columns: 1fr; }
	.value { border-right: 0; padding: 1.5rem 0 0; }
	.value:not(:first-child) { padding-left: 0; }

	.value__toggle {
		display: flex; align-items: baseline; justify-content: space-between;
		width: 100%;
		gap: 1rem;
		padding: 0;
		text-align: left;
		font: inherit;
		letter-spacing: inherit;
		text-transform: inherit;
	}
	.value__toggle::after { content: "+"; font-weight: 400; color: var(--ink-soft); }
	.is-open .value__toggle::after { content: "–"; }

	/* Only columns that actually got a toggle collapse — linked columns have no
	   toggle and stay open. No :has support ⇒ nothing hides, which is the
	   correct degrade (everything readable, button still works as a no-op). */
	.js [data-values] .value:has(.value__toggle):not(.is-open) p { display: none; }
	.js [data-values] .value.is-open p { animation: ev-fade var(--fade) both; }
}

/* =========================================================================
   PAGE HEADER — the inner-page opener. Shorter than the hero.
   ========================================================================= */
.panel--pagehead .panel__media { height: 52vh; min-height: 340px; }
.panel--pagehead .panel__media::after {
	content: "";
	position: absolute; inset: 0;
	background: linear-gradient(to top, rgba(29,28,28,.25) 0%, rgba(29,28,28,.2) 50%, rgba(29,28,28,.05) 100%);
	pointer-events: none;
}
.panel--pagehead .panel__overlay p { margin-bottom: 0; }

/* =========================================================================
   STATS — numbers in the serif. No cards, no boxes, no coloured digits: the
   figure is large because it is a serif at size, not because it's been dressed.
   ========================================================================= */
.stats { display: grid; gap: 2rem; grid-template-columns: repeat(var(--stat-cols, 4), 1fr); }
.stat { border-left: 1px solid var(--hairline); padding-left: 1.75rem; }
.stat:first-child { border-left: 0; padding-left: 0; }
.stat__n {
	font-family: var(--display);
	font-size: clamp(2.5rem, 4vw, 3.5rem);
	font-weight: 300;
	line-height: 1;
	color: var(--ink);
	margin-bottom: .75rem;
}
.stat__l {
	font-family: var(--sans);
	font-size: .75rem; font-weight: 500;
	letter-spacing: .1em; text-transform: uppercase;
	color: var(--ink-soft);
	line-height: 1.6;
}
@media (max-width: 760px) {
	/* No !important needed anymore — the column count is a custom property, not
	   an inline declaration, so this rule wins by normal cascade. */
	.stats { grid-template-columns: repeat(2, 1fr); gap: 2.5rem 1.5rem; }
	.stat:nth-child(odd) { border-left: 0; padding-left: 0; }
}

/* =========================================================================
   FEATURE TABS — the layered-construction explainer.
   The list is a column of rules, not a row of pills. Selection is shown by the
   rule going solid and the text going to ink — no filled chip, no background.
   ========================================================================= */
.tabs { display: grid; grid-template-columns: 320px 1fr; gap: clamp(2rem, 5vw, 4.5rem); align-items: start; }

.tabs__list { display: flex; flex-direction: column; border-top: 1px solid var(--hairline); }
.tabs__btn {
	position: relative;
	text-align: left;
	padding: 1.125rem 0;
	border-bottom: 1px solid var(--hairline);
	font-family: var(--sans);
	font-size: .75rem; font-weight: 500;
	letter-spacing: .08em; text-transform: uppercase;
	line-height: 1.5;
	color: var(--ink-soft);
	transition: color var(--quick);
}
.tabs__btn::before {
	content: "";
	position: absolute; left: 0; bottom: -1px;
	width: 0; height: 1px;
	background: var(--ink);
	transition: width .5s cubic-bezier(.22,.61,.36,1);
}
.tabs__btn:hover { color: var(--ink); }
.tabs__btn.is-active { color: var(--ink); font-weight: 600; }
.tabs__btn.is-active::before { width: 100%; }

.tabs__panels { position: relative; }
/* No JS: every panel shows, and the page reads as a list of layers. The JS
   below collapses it to one at a time. */
.tabs__panel { display: grid; gap: 1.75rem; }
.tabs__panel + .tabs__panel { margin-top: 3rem; }
.tabs__media { background: var(--whisper); overflow: hidden; }
.tabs__media img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; }
.tabs__body h3 { margin-bottom: .5rem; }
.tabs__body p { color: var(--ink-soft); margin: 0; }

/* JS on: one panel at a time, cross-faded. */
.js .tabs__panel { display: none; }
.js .tabs__panel + .tabs__panel { margin-top: 0; }
.js .tabs__panel.is-active { display: grid; animation: ev-fade var(--fade) both; }
@keyframes ev-fade { from { opacity: 0; } to { opacity: 1; } }

@media (max-width: 900px) {
	.tabs { grid-template-columns: 1fr; }
}

/* =========================================================================
   FORMS — square, hairline, no glow. Gravity Forms fights back, hence the
   specificity; this is the minimum needed to stop it shipping rounded inputs
   and a blue focus ring into a site that has neither.
   ========================================================================= */
.form-wrap { max-width: 44rem; }

/* Form beside copy. The aside carries the caveats and the contact details;
   the form does the work. */
.form-split {
	display: grid;
	grid-template-columns: 1fr 1fr;
	gap: clamp(2.5rem, 10vw, 5.5rem);
	align-items: start;
}
.form-split .form-wrap { max-width: none; }
.form-split__aside .prose p { font-size: .9375rem; color: var(--ink-soft); }
.form-split__aside .prose strong { color: var(--ink); font-weight: 600; }

/* Contact details — a list of rules, not a row of icon cards in circles.
   The old build had exactly that; it was chrome. */
.contact-list {
	list-style: none; margin: 2.5rem 0 0; padding: 0;
	border-top: 1px solid var(--hairline);
	max-width: 75%;
}
.contact {
	display: grid;
	grid-template-columns: 9rem 1fr;
	gap: 1rem;
	padding: 1.125rem 0;
	border-bottom: 1px solid var(--hairline);
}
.contact__label {
	font-family: var(--sans);
	font-size: var(--ui-size); font-weight: var(--ui-weight);
	letter-spacing: var(--ui-track); text-transform: uppercase;
	color: var(--ink-soft);
}
.contact__value { font-size: .9375rem; color: var(--ink); line-height: 1.6; }
.contact__value a { border-bottom: 1px solid var(--hairline); transition: border-color var(--quick); }
.contact__value a:hover { border-bottom-color: var(--ink); }

@media (max-width: 900px) {
	.form-split { grid-template-columns: 1fr; }
	.contact { grid-template-columns: 1fr; gap: .25rem; }
}
.form-wrap .gform_wrapper.gravity-theme input[type="text"],
.form-wrap .gform_wrapper.gravity-theme input[type="email"],
.form-wrap .gform_wrapper.gravity-theme input[type="tel"],
.form-wrap .gform_wrapper.gravity-theme input[type="number"],
.form-wrap .gform_wrapper.gravity-theme select,
.form-wrap .gform_wrapper.gravity-theme textarea {
	border: 1px solid var(--hairline);
	border-radius: 0;
	box-shadow: none;
	padding: .875rem 1rem;
	font-family: var(--sans);
	font-size: .9375rem;
	color: var(--ink);
	background: var(--paper);
	transition: border-color var(--quick);
}
.form-wrap .gform_wrapper.gravity-theme input:focus,
.form-wrap .gform_wrapper.gravity-theme select:focus,
.form-wrap .gform_wrapper.gravity-theme textarea:focus {
	border-color: var(--ink);
	outline: 1px solid var(--ink);
	outline-offset: -2px;
}
.form-wrap .gform_wrapper.gravity-theme .gfield_label {
	font-family: var(--sans);
	font-size: var(--ui-size);
	font-weight: var(--ui-weight);
	letter-spacing: var(--ui-track);
	text-transform: uppercase;
	color: var(--ink);
}
.form-wrap .gform_wrapper.gravity-theme .gform_footer input[type="submit"],
.form-wrap .gform_wrapper.gravity-theme .gform_button {
	font-family: var(--sans);
	font-size: var(--ui-size);
	font-weight: var(--ui-weight);
	letter-spacing: var(--ui-track);
	text-transform: uppercase;
	background: var(--ink);
	color: var(--paper);
	border: 1px solid var(--ink);
	border-radius: 0;
	box-shadow: none;
	padding: 1.0625rem 2.25rem;
	cursor: pointer;
	transition: background var(--quick), color var(--quick);
	margin: 0 auto;
}
.form-wrap .gform_wrapper.gravity-theme .gform_footer input[type="submit"]:hover,
.form-wrap .gform_wrapper.gravity-theme .gform_button:hover {
	background: transparent;
	color: var(--ink);
}

/* ---- Validation and submit states, in the system voice -------------------
   GF ships red-on-pink error chrome. Red is not in the palette and does not
   get an exception: the error signal here is the border going full ink and
   the message wearing the tracked-caps voice. .ev-touched-invalid is the
   same treatment applied on blur by maison.js, before GF ever posts. */
.form-wrap .gform_wrapper.gravity-theme .gfield_error input,
.form-wrap .gform_wrapper.gravity-theme .gfield_error select,
.form-wrap .gform_wrapper.gravity-theme .gfield_error textarea,
.form-wrap .gform_wrapper.gravity-theme .gfield.ev-touched-invalid input,
.form-wrap .gform_wrapper.gravity-theme .gfield.ev-touched-invalid select,
.form-wrap .gform_wrapper.gravity-theme .gfield.ev-touched-invalid textarea {
	border-color: var(--ink);
	background: var(--whisper);
}
.form-wrap .gform_wrapper.gravity-theme .gfield_validation_message,
.form-wrap .gform_wrapper.gravity-theme .validation_message {
	font-family: var(--sans);
	font-size: var(--ui-size); font-weight: var(--ui-weight);
	letter-spacing: var(--ui-track); text-transform: uppercase;
	color: var(--ink);
	background: none; border: 0;
	padding: .5rem 0 0; margin: 0;
}
.form-wrap .gform_wrapper.gravity-theme .gform_validation_errors {
	background: var(--paper);
	border: 1px solid var(--ink);
	border-radius: 0;
	box-shadow: none;
	padding: 1.25rem 1.5rem;
	margin-bottom: 2rem;
}
.form-wrap .gform_wrapper.gravity-theme .gform_validation_errors > h2 {
	font-family: var(--display);
	font-size: 1.125rem; font-weight: 300;
	color: var(--ink);
	margin: 0;
}
.form-wrap .gform_wrapper.gravity-theme .gform_validation_errors > h2 .gform-icon { display: none; }
.form-wrap .gform_wrapper.gravity-theme .gfield_required,
.form-wrap .gform_wrapper.gravity-theme .gfield_required_text { color: var(--ink-soft); }

/* The confirmation reads like a statement — serif, air, no green banner. */
.form-wrap .gform_confirmation_message {
	font-family: var(--display);
	font-size: clamp(1.25rem, 1.9vw, 1.6rem);
	line-height: 1.5;
	font-weight: 300;
	color: var(--ink);
	padding: 2rem 0;
}

/* While GF's iframe round-trip is in flight. The re-rendered DOM clears it. */
.gform_wrapper.is-submitting .gform_footer input[type="submit"],
.gform_wrapper.is-submitting .gform_button {
	opacity: .5;
	cursor: wait;
}

/* =========================================================================
   GALLERY
   -------------------------------------------------------------------------
   A column layout, not a hard 4-up of cropped squares. These are photographs of
   rooms; cropping a room to a square to make a tidy grid throws away the room.
   Portraits stay portrait, landscapes stay landscape.
   ========================================================================= */
.gfilter {
	display: flex; flex-wrap: wrap; gap: 2rem;
	padding-bottom: 1.25rem;
	margin-bottom: clamp(2rem, 4vw, 3rem);
	border-bottom: 1px solid var(--hairline);
}
/* Not pills. A word and a rule — the same language as every other control. */
.gfilter__btn {
	position: relative;
	padding-bottom: .5rem;
	font-family: var(--sans);
	font-size: var(--ui-size); font-weight: var(--ui-weight);
	letter-spacing: var(--ui-track); text-transform: uppercase;
	color: var(--ink-soft);
	transition: color var(--quick);
}
.gfilter__btn::after {
	content: "";
	position: absolute; left: 0; bottom: -1.3rem;
	width: 100%; height: 1px;
	background: var(--ink);
	transform: scaleX(0); transform-origin: left;
	transition: transform .45s cubic-bezier(.22,.61,.36,1);
}
.gfilter__btn:hover { color: var(--ink); }
.gfilter__btn.is-active { color: var(--ink); }
.gfilter__btn.is-active::after { transform: scaleX(1); }

.ggrid {
	columns: 3;
	column-gap: clamp(1rem, 2vw, 1.75rem);
}
.gitem {
	break-inside: avoid;
	margin: 0 0 clamp(1rem, 2vw, 1.75rem);
	background: var(--whisper);
	overflow: hidden;
}
.gitem[hidden] { display: none; }
.gitem__btn { display: block; width: 100%; padding: 0; cursor: zoom-in; }
.gitem__btn img {
	width: 100%; height: auto;
	transition: transform var(--breathe), opacity var(--fade);
}
.gitem__btn:hover img { transform: scale(1.03); }

.ggrid__empty {
	font-family: var(--display); font-size: 1.25rem; font-weight: 300;
	color: var(--ink-soft); padding: 3rem 0;
}

@media (max-width: 1100px) { .ggrid { columns: 2; } }
@media (max-width: 700px)  { .ggrid { columns: 1; } }

/* ---- Lightbox ----------------------------------------------------------
   A <dialog>: the browser supplies the modal semantics, the focus trap, the
   backdrop and Escape-to-close. A hand-rolled overlay div gets all four subtly
   wrong, and the last one not at all. */
.lightbox {
	border: 0; padding: 0;
	max-width: 100vw; max-height: 100vh;
	width: 100%; height: 100%;
	background: transparent;
	overflow: hidden;
}
.lightbox::backdrop { background: rgba(34,29,24,.94); } /* espresso, matching the dark grounds */
.lightbox[open] { display: grid; place-items: center; }

.lightbox__figure {
	margin: 0;
	display: grid; place-items: center; gap: 1.25rem;
	padding: clamp(3rem, 6vw, 5rem);
	max-width: 100%; max-height: 100%;
}
.lightbox__figure img {
	max-width: min(92vw, 1400px);
	max-height: 78vh;
	width: auto; height: auto;
	object-fit: contain;
}
.lightbox__caption {
	font-family: var(--sans); font-size: .75rem;
	letter-spacing: .1em; text-transform: uppercase;
	color: rgba(242,240,237,.7);
	text-align: center;
}
/* "3 / 14" — where you are in the set. Same voice as the caption, quieter. */
.lightbox__count {
	font-family: var(--sans); font-size: .6875rem;
	letter-spacing: .12em;
	color: rgba(242,240,237,.45);
	text-align: center;
}

.lightbox__close,
.lightbox__nav {
	position: fixed;
	color: var(--paper);
	opacity: .6;
	transition: opacity var(--quick);
	line-height: 1;
	z-index: 1;
}
.lightbox__close:hover,
.lightbox__nav:hover { opacity: 1; }
.lightbox__close { top: 1.5rem; right: 2rem; font-size: 2.25rem; }
.lightbox__nav { top: 50%; transform: translateY(-50%); font-size: 3rem; padding: 1rem; }
.lightbox__nav--prev { left: 1rem; }
.lightbox__nav--next { right: 1rem; }

/* =========================================================================
   DOWNLOADS — rules and type. No cards, no icon chips in circles.
   ========================================================================= */
.docs {
	list-style: none; margin: 0; padding: 0;
	border-top: 1px solid var(--hairline);
	margin: 0 auto;
	max-width: 60rem;
}
.doc { border-bottom: 1px solid var(--hairline); }
.doc__link {
	display: flex; align-items: baseline; justify-content: space-between;
	gap: 2rem; flex-wrap: wrap;
	padding: 1.5rem 0;
	transition: opacity var(--quick);
}
.doc__link:hover { opacity: .6; }
.doc__title {
	font-family: var(--display);
	font-size: clamp(1.125rem, 1.5vw, 1.375rem);
	font-weight: 300;
	color: var(--ink);
}
.doc__meta { display: flex; align-items: baseline; gap: 1.5rem; }
.doc__spec {
	font-family: var(--sans); font-size: .6875rem;
	letter-spacing: .1em; text-transform: uppercase;
	color: var(--ink-soft);
}
.doc__action {
	font-family: var(--sans); font-size: var(--ui-size); font-weight: var(--ui-weight);
	letter-spacing: var(--ui-track); text-transform: uppercase;
	color: var(--ink);
	border-bottom: 1px solid var(--ink);
	padding-bottom: .2rem;
	white-space: nowrap;
}

/* Filter-as-you-type over the list. The input is the facet voice: a hairline
   underline, no box, no glow. Explicit [hidden] because the li is a flex row. */
.doc[hidden] { display: none; }
.docs-search {
	display: flex; align-items: baseline; gap: 1.5rem;
	max-width: 60rem;
	margin: 0 auto;
	margin-bottom: 1.75rem;
}
.docs-search input {
	flex: 1;
	max-width: 22rem;
	border: 0; border-bottom: 1px solid var(--hairline);
	background: transparent;
	padding: .5rem 0;
	font-family: var(--sans); font-size: .875rem;
	letter-spacing: .04em;
	color: var(--ink);
	transition: border-color var(--quick);
}
.docs-search input:focus { border-bottom-color: var(--ink); outline: none; }
.docs-search input::placeholder { color: var(--ink-soft); }
.docs-search__count {
	font-family: var(--sans); font-size: .6875rem;
	letter-spacing: .1em; text-transform: uppercase;
	color: var(--ink-soft);
}
.docs__empty {
	font-family: var(--display); font-size: 1.25rem; font-weight: 300;
	color: var(--ink-soft); padding: 3rem 0;
}

/* =========================================================================
   FAQ — questions on hairline rules. Native <details>; the open answer FADES
   in (opacity only) while the height change stays instant — animating the
   height is the slide the motion law bans, wearing a different property.
   ========================================================================= */
.faqs {
	border-top: 1px solid var(--hairline);
	max-width: 60rem;
}
.faq { border-bottom: 1px solid var(--hairline); }
.faq__q {
	list-style: none;
	display: flex; align-items: baseline; justify-content: space-between;
	gap: 2rem;
	padding: 1.5rem 0;
	cursor: pointer;
	font-family: var(--display);
	font-size: clamp(1.125rem, 1.5vw, 1.375rem);
	font-weight: 300;
	color: var(--ink);
	transition: opacity var(--quick);
}
.faq__q::-webkit-details-marker { display: none; }
.faq__q:hover { opacity: .6; }
/* The marker: a content swap, not a rotation. */
.faq__q::after {
	content: "+";
	font-family: var(--display);
	font-size: 1.375rem; font-weight: 300;
	color: var(--ink-soft);
	flex: none;
}
.faq[open] .faq__q::after { content: "–"; }
.faq__a {
	max-width: var(--measure);
	padding: 0 0 1.75rem;
}
.faq__a p { font-size: .9375rem; line-height: 1.7; color: var(--ink-soft); margin: 0 0 1em; }
.faq__a p:last-child { margin-bottom: 0; }
.faq[open] .faq__a { animation: ev-fade var(--fade) both; }

/* =========================================================================
   SEARCH OVERLAY — the finish palette. A full-viewport <dialog> on paper: a
   reading surface, not a photo viewer, so it takes the page ground rather
   than the espresso backdrop. It FADES open. Nothing slides down.
   ========================================================================= */
.search-overlay {
	border: 0; padding: 0;
	max-width: 100vw; max-height: 100vh;
	width: 100%; height: 100%;
	background: var(--paper);
	color: var(--ink);
}
.search-overlay::backdrop { background: rgba(34,29,24,.4); }
.search-overlay[open] { animation: ev-fade var(--fade) both; }

.search-overlay__inner {
	width: min(100% - (var(--bleed) * 2), 44rem);
	margin-inline: auto;
	padding: clamp(3rem, 8vh, 6rem) 0 4rem;
}

.search-overlay__form {
	display: flex; align-items: center; gap: 1.5rem;
	border-bottom: 1px solid var(--ink);
	margin-bottom: 1rem;
}
/* The query is the user's own words — the one place largish type is earned. */
.search-overlay__input {
	flex: 1;
	border: 0; background: transparent;
	padding: .75rem 0 1rem;
	font-family: var(--display);
	font-size: clamp(1.375rem, 2.4vw, 1.75rem);
	font-weight: 300;
	color: var(--ink);
}
.search-overlay__input:focus { outline: none; }
.search-overlay__input::placeholder { color: var(--ink-soft); }
.search-overlay__input::-webkit-search-cancel-button { display: none; }
.search-overlay__close {
	font-family: var(--display);
	font-size: 2rem; font-weight: 300;
	line-height: 1;
	color: var(--ink);
	opacity: .6;
	transition: opacity var(--quick);
	padding: 0 0 .5rem;
}
.search-overlay__close:hover { opacity: 1; }

.search-overlay__count {
	min-height: 1.5em;
	font-family: var(--sans); font-size: .6875rem;
	letter-spacing: .1em; text-transform: uppercase;
	color: var(--ink-soft);
	margin: 0 0 1.5rem;
}

.search-overlay__group { margin-bottom: 2.5rem; }
.search-overlay__group .eyebrow { margin-bottom: .75rem; }
.search-results {
	list-style: none; margin: 0; padding: 0;
	border-top: 1px solid var(--hairline);
}
.search-hit {
	display: flex; align-items: baseline; justify-content: space-between;
	gap: 2rem;
	padding: .875rem 0;
	border-bottom: 1px solid var(--hairline);
	transition: opacity var(--quick);
}
.search-hit:hover, .search-hit.is-active { opacity: .55; }
.search-hit__name {
	font-family: var(--display);
	font-size: 1.0625rem; font-weight: 300;
	color: var(--ink);
}
.search-hit__meta {
	font-family: var(--sans); font-size: .6875rem;
	letter-spacing: .08em; text-transform: uppercase;
	color: var(--ink-soft);
	white-space: nowrap;
}
.search-overlay__empty {
	font-family: var(--display); font-size: 1.25rem; font-weight: 300;
	color: var(--ink-soft);
	padding: 2rem 0;
	margin: 0;
}

/* =========================================================================
   REVEAL — opacity only. This is the rule people get wrong.
   Cartier's elements FADE. They do not slide up 30px, they do not scale in.
   A translateY reveal is the single loudest "startup landing page" tell.
   ========================================================================= */
[data-reveal] { opacity: 0; transition: opacity .8s ease-out; }
[data-reveal].is-in { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
	*, *::before, *::after {
		animation-duration: .01ms !important;
		transition-duration: .01ms !important;
	}
	[data-reveal] { opacity: 1; }
	.panel__media img { transform: none; }
}

/* =========================================================================
   FOOTER
   ========================================================================= */
.site-footer { background: var(--espresso); color: var(--on-dark); padding: 2.5rem 0 2.5rem; border-top: 1px solid rgba(242,240,237,.14); }
.footer-grid {
	display: grid; grid-template-columns: 1.4fr repeat(3, 1fr);
	gap: 3rem;
	padding-bottom: 4rem;
	border-bottom: 1px solid rgba(242,240,237,.14);
}
.footer-col h4 {
	font-family: var(--sans); font-size: var(--ui-size); font-weight: var(--ui-weight);
	letter-spacing: var(--ui-track); text-transform: uppercase;
	color: var(--paper); margin-bottom: 1.5rem;
}
.footer-col ul { list-style: none; margin: 0; padding: 0; display: grid; gap: .875rem; }
.footer-col a { font-size: .875rem; color: rgba(242,240,237,.7); transition: color var(--quick); }
.footer-col a:hover { color: var(--paper); }
.footer-bottom {
	display: flex; justify-content: space-between; flex-wrap: wrap; gap: 1rem;
	padding-top: 2rem;
}
.footer-bottom p,
.footer-bottom a {
	font-family: var(--sans); font-size: .6875rem; letter-spacing: .1em;
	text-transform: uppercase; color: rgba(242,240,237,.5); margin: 0;
}
/* Scoped to .site-footer on purpose — do NOT flatten this back to a bare
   `.footer-wordmark`. WooCommerce's woocommerce-layout.css ships
   `.woocommerce img, .woocommerce-page img { height: auto }`, and since
   `.woocommerce-page` sits on <body> for every shop/category/product page, that
   rule matches EVERY image on those pages. Its specificity (0,1,1) beats a lone
   class (0,1,0), so the wordmark loses its height and balloons to 438x101 — but
   only on Woo pages, which is what makes it look like a phantom. Two classes
   (0,2,0) settles it. */

.woocommerce ul.products li.product, ul.products li.product {
	justify-content: flex-end;
}

.site-footer .footer-wordmark { height: 20px; width: auto; margin-bottom: 1.5rem; opacity: .9; }
.footer-blurb {
	font-size: .875rem; line-height: 1.7;
	color: rgba(242,240,237,.7); max-width: 20rem; margin: 0 0 1.75rem;
}
.footer-contact { list-style: none; margin: 0; padding: 0; display: grid; gap: .5rem; }
.footer-contact li,
.footer-contact a,
.footer-contact span {
	font-family: var(--sans); font-size: .8125rem; letter-spacing: .02em;
	color: rgba(242,240,237,.7);
}
.footer-contact a:hover { color: var(--paper); }
.footer-logos { display: flex; align-items: center; gap: 1.5rem; }
/* The partner marks are dark-on-light PNGs — invert them onto the ink footer. */
.footer-logos img { height: 24px; width: auto; filter: brightness(0) invert(1); opacity: .55; }

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

/* =========================================================================
   RESPONSIVE HEADER
   ========================================================================= */
@media (max-width: 1000px) {
	.gform_wrapper.gravity-theme .gform_footer > input{margin: 0 auto;}
	.header-util { display: none; }
	.nav-toggle { display: flex; }
	.brand { position: static; transform: none; }
	.header-top { justify-content: space-between; }

	/* The mobile menu. The toggle JS was always flipping .is-open — the bug was
	   that no CSS existed for the open state, so it toggled a class on an
	   element that stayed display:none. A full-width panel under the bar:
	   column of rules, cream ground, and the utility links appended at the foot.
	   Anchored to .site-header (sticky ⇒ a containing block for absolutes), so
	   it drops from under the bar and scrolls with it. */
	.header-nav { display: none; }
	.header-nav.is-open {
		display: flex;
		flex-direction: column;
		align-items: stretch;
		gap: 0;
		position: absolute;
		top: 100%; left: 0; right: 0;
		background: var(--paper);
		border-bottom: 1px solid var(--hairline);
		padding: .25rem var(--bleed) 1.5rem;
		/* The page behind is locked, so a panel taller than the screen (landscape
		   phones) must scroll itself or its last links are unreachable. */
		max-height: calc(100dvh - 100%);
		overflow-y: auto;
		overscroll-behavior: contain;
		animation: ev-fade var(--fade) both;
	}
	.header-nav.is-open .nav-link {
		padding: 1rem 0;
		border-bottom: 1px solid var(--hairline);
	}
	/* The rule-draw hover underline is a desktop affordance — on the panel every
	   link already has a rule under it. */
	.header-nav.is-open .nav-link::after { display: none; }

	.header-nav.is-open .header-nav__util {
		display: flex;
		flex-direction: column;
		margin-top: 1.25rem;
	}
	.header-nav.is-open .header-nav__util .nav-link {
		color: var(--ink-soft);
		font-weight: 500;
	}
	.header-nav.is-open .header-nav__util .nav-link:last-child { border-bottom: 0; }

	.stats{
		    display: flex;
    flex-direction: column;
    justify-content: center;
	}

	.stats .stat{
		padding-left: 0;
	}

	.container{
		    grid-template-columns: 1fr;
    gap: 0;
	}
}
