/* =============================================================================
   LEOSMART PREMIUM — design system

   1. Tokens        6. Hero             11. Footer
   2. Base          7. Cards            12. Product detail
   3. Layout        8. Grids            13. Certificates
   4. Buttons       9. Tables           14. Responsive
   5. Header / nav  10. Forms           15. Signature, utilities & motion
                                        16. Contact
                                        17. About (Our Craft)

   Rule of the house: components read from the tokens below and never invent
   their own navy, gold, spacing or radius. If a value is not here, it does not
   exist. The palette is the logo's — navy #0B2447, gold #E0A62B, and the cream
   plate #FEFDF1 they sit on — and the ground stays light everywhere.
   ========================================================================== */

/* 1 ── Tokens ------------------------------------------------------------- */
:root {
    color-scheme: light;

    /* Ground — the cream plate the logo lockup sits on */
    --white:      #FFFFFF;   /* page ground */
    --cream:      #FBF8EE;   /* alternating band, inset surfaces */
    --panel:      #FFFFFF;   /* card surface */
    --panel-2:    #FEFCF4;   /* card surface, tinted for hover */

    /* Hairlines — navy, so a rule reads as ink rather than grey */
    --line:           rgba(11, 36, 71, .14);
    --line-soft:      rgba(11, 36, 71, .07);
    --line-gold:      rgba(223, 166, 43, .34);
    --line-gold-soft: rgba(223, 166, 43, .16);

    /* Navy — the logo's wordmark and emblem ink (#0B2447, hue 215). It carries
       the structure of the site: headings, primary actions, the footer ground. */
    --navy:      #0B2447;
    --navy-deep: #061A36;   /* footer ground, pressed states */
    --navy-soft: #16407A;   /* hover lift on navy surfaces */
    --on-navy:   #FEFDF1;   /* type on navy — the logo plate, one shade cooler */
    --navy-a08:  rgba(11, 36, 71, .08);
    --navy-a16:  rgba(11, 36, 71, .16);
    --navy-a50:  rgba(11, 36, 71, .50);

    /* Gold — the logo's emblem (#E6A92B → #F3C449). --gold is decoration only:
       fills, rules, icons. Text set in gold takes --gold-deep (large) or
       --gold-ink (small), the two steps that clear contrast on a light ground. */
    --gold:       #E0A62B;
    --gold-soft:  #F0C24A;
    --gold-deep:  #A87C18;
    --gold-ink:   #8A6410;
    --champagne:  #F5E4A8;
    --gold-a08:   rgba(223, 166, 43, .08);
    --gold-a16:   rgba(223, 166, 43, .16);
    --gold-a50:   rgba(223, 166, 43, .50);
    --gold-a80:   rgba(223, 166, 43, .80);

    /* Quiet navy-greys — every value here clears 4.5:1 on --white */
    --muted:      #56637A;
    --muted-2:    #6B7789;
    --danger:     #C4513A;
    --success:    #2E7D4F;

    /* WhatsApp — the one accent this site does not own. It marks the single
       fastest route to a human and is spent nowhere else, so the green still
       reads as "this is WhatsApp" instead of as a second brand colour. */
    --wa-green:      #25D366;
    --wa-green-deep: #1DA851;   /* hover / pressed */
    --wa-a50:        rgba(37, 211, 102, .50);

    /* Paper ground — the document mode used by the certified page */
    --paper:      #FBF8EE;
    --paper-ink:  #0B2447;

    /* Type roles: display for voice, sans for reading, mono for data */
    --display: "Playfair Display", "Cormorant Garamond", "Iowan Old Style", Georgia, "Times New Roman", serif;
    --sans: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    --mono: "IBM Plex Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

    /* Type scale — every size on the site comes from these six steps */
    --fs-display:  clamp(2.35rem, 6.2vw, 4.6rem);
    --fs-h2:       clamp(1.95rem, 4vw, 3.05rem);
    --fs-h3:       clamp(1.18rem, 2vw, 1.5rem);
    --fs-body:     1rem;
    --fs-sm:       .875rem;
    --fs-label:    .66rem;
    --track-label: .26em;

    /* Space scale — multiples of 4px */
    --space-1: .25rem;  --space-2: .5rem;   --space-3: .75rem;  --space-4: 1rem;
    --space-5: 1.25rem; --space-6: 1.5rem;  --space-8: 2rem;    --space-10: 2.5rem;
    --space-12: 3rem;   --space-16: 4rem;   --space-20: 5rem;

    /* Layout */
    --container: 1240px;
    /* The page's outer margin, and the reason it is fluid: a fixed 15px against
       a 1200px plate is a Bootstrap default, and it left the content 15px from
       the edge of a 1024px window while the desktop plate kept its own air.
       Now the gutter is the same proportion of the window at every width, the
       content keeps the width it had on a desktop, and a tablet gets a real
       margin instead of a hairline one. */
    --gutter: clamp(20px, 3.5vw, 36px);
    /* The band rhythm: the air above and below every band's contents, and the
       only place it is set — a band never overrides it. Held at 100px a desktop
       ran two bands 200px apart, which read as a hole rather than as a pause; it
       now tops out at 60px and floors at 40px on a phone, which is air the page
       can still be read across. */
    --section-y: clamp(2.5rem, 5vw, 3.75rem);

    /* Radii — near-square. One value per job, nothing in between. */
    --radius-sm: 2px;
    --radius: 6px;
    --radius-lg: 12px;
    --radius-pill: 999px;

    /* Depth — navy-tinted, never black. A black shadow on a cream ground
       greys the whole panel; a navy one reads as light falling off the edge. */
    --shadow-sm:   0 2px 10px -4px rgba(11, 36, 71, .10);
    --shadow-md:   0 24px 60px -30px rgba(11, 36, 71, .18);
    --shadow-lg:   0 40px 90px -40px rgba(11, 36, 71, .22);
    --shadow-gold: 0 18px 44px -22px rgba(223, 166, 43, .28);
    --shadow-navy: 0 22px 48px -26px rgba(11, 36, 71, .32);
    --shadow: var(--shadow-md);

    /* Motion */
    --ease:        cubic-bezier(.22, .61, .36, 1);
    --ease-out:    cubic-bezier(.16, .84, .44, 1);
    --ease-spring: cubic-bezier(.34, 1.4, .64, 1);
    --dur-fast: .22s;
    --dur:      .34s;
    --dur-slow: .6s;

    /* Signature — the bonded waistband, read as a warp stripe. It appears on
       landmark edges only: under every section heading and along the footer
       seam. It is the one motif that repeats on this site; add no others. */
    --tape: repeating-linear-gradient(115deg, var(--gold-a80) 0 1px, transparent 1px 5px);
    /* The same weave at punctuation weight, laid over a solid gold ribbon so a
       64–72px heading rule reads as a woven edge rather than a dotted line.
       Same material, sized for the scale it is drawn at. */
    --rule-tape:
        repeating-linear-gradient(115deg, rgba(255, 255, 255, .42) 0 2px, transparent 2px 6px),
        linear-gradient(90deg, var(--gold) 0%, var(--gold-soft) 50%, var(--gold) 100%);
}

/* 2 ── Base --------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
    margin: 0;
    background: var(--white);
    color: var(--navy);
    font-family: var(--sans);
    font-size: 16px;
    line-height: 1.65;
    font-weight: 400;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    overflow-x: hidden;
}

h1, h2, h3, h4 {
    font-family: var(--display);
    font-weight: 600;
    line-height: 1.1;
    letter-spacing: -.015em;
    margin: 0 0 .6em;
    color: var(--navy);
    text-wrap: balance;
}

/* Display type is set tight on purpose — Playfair at 4rem with default
   tracking reads loose and generic. */
h1 { font-size: var(--fs-display); line-height: 1.04; letter-spacing: -.022em; }
h2 { font-size: var(--fs-h2);      line-height: 1.08; letter-spacing: -.02em; }
h3 { font-size: var(--fs-h3);      line-height: 1.25; letter-spacing: -.012em; }
h4 { font-size: 1rem;              line-height: 1.35; letter-spacing: .01em; }

p { margin: 0 0 1.1em; color: var(--muted); text-wrap: pretty; }
p:last-child { margin-bottom: 0; }

a { color: var(--gold-deep); text-decoration: none; transition: color .25s var(--ease); }
a:hover { color: var(--gold-ink); }

img { max-width: 100%; display: block; }

hr { border: 0; border-top: 1px solid var(--line-soft); margin: 3rem 0; }

::selection { background: var(--gold); color: var(--navy); }

.icon { width: 1.35em; height: 1.35em; flex: none; }

.skip-link {
    position: absolute; left: -9999px; top: 0; z-index: 200;
    background: var(--navy); color: var(--on-navy); padding: .75rem 1.25rem;
}
.skip-link:focus { left: 0; }

:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 3px; }

/* 3 ── Layout ------------------------------------------------------------- */
.container { width: 100%; max-width: var(--container); margin-inline: auto; padding-inline: var(--gutter); }
.container--narrow { max-width: 800px; }

/* Every band on every page uses one vertical rhythm, so pages feel like the
   same document. Do not add per-page padding overrides. */
.section { padding-block: var(--section-y); position: relative; }
/* Stacked bands share one gap. Two bands back to back would otherwise add their
   leading edges up to twice the rhythm between their contents, which reads as a
   hole rather than air — so the lower band gives up its leading edge. The first
   band under a page head keeps the full lead, and the last one keeps its full
   foot, because nothing is stacked against it. */
.section--joined { padding-top: calc(var(--section-y) * .55); }
.section--panel { background: var(--cream); border-block: 1px solid var(--line-soft); }
.section--panel + .section--panel { border-top: 0; }

/* Everything is announced on the centre axis of the page — the masthead and
   every section heading. Sections are
   full-width bands here, so a centred head is aligned with the content it
   introduces rather than hanging off its left edge. If a section ever puts its
   heading beside something instead, use .section-head--start. */
.section-head { max-width: 640px; margin-inline: auto; margin-bottom: clamp(1.75rem, 3.5vw, 2.75rem); text-align: center; }

/* Section headings carry no rule of their own. The tape is the masthead's —
   one woven edge under a page's title — and repeating it under every band on a
   page spent it as texture rather than as a signature. */
.section-head h2 { margin-bottom: 0; }
.section-head .lead { margin: var(--space-5) auto 0; }

/* Escape hatch: an asymmetric band with its heading against the left rail. */
.section-head--start { margin-inline: 0; text-align: start; }
.section-head--start .lead { margin-inline: 0; }

/* On a desktop the head takes the room its own line needs. Held at the 640px
   measure it broke nearly every heading — and the lead under it — onto a second
   line, and a band's announcement is the one block on a page that should read as
   one statement. The measure stays in place below 981px, where the container is
   inside the text's own width and wrapping is the layout, not the cap. */
@media (min-width: 981px) {
    .section-head { max-width: none; }
    /* The standfirst is capped at 62ch for the reading measure, which broke most
       of them onto a second line inside a head that now has the room for one.
       Same rule as above: on a desktop it runs to its head's width. */
    .section-head .lead, .page-head .lead { max-width: none; }
}

/* Micro-label. Mono, so every label on the site reads as instrument data
   rather than decoration — and with no ornamental dash competing with it. */
.eyebrow {
    display: inline-flex; align-items: center;
    font-family: var(--mono);
    font-size: var(--fs-label); font-weight: 500;
    letter-spacing: var(--track-label); text-transform: uppercase;
    color: var(--gold-ink); margin-bottom: var(--space-4);
}

.lead { font-size: 1.06rem; color: var(--muted); max-width: 62ch; }

.gold { color: var(--gold-deep); }
/* Centring a band means centring its eyebrow, its standfirst and its button row
   too. These three carry that, so no section patches its own children. */
.text-center { text-align: center; }
.text-center .eyebrow { justify-content: center; }
.text-center .lead { margin-inline: auto; }
.text-center .btn-row { justify-content: center; }

/* 4 ── Buttons ------------------------------------------------------------ */
/* One button system, four weights, one box:
     .btn                     navy fill  — the page's primary action
     .btn--gold / .btn-gold   gold fill  — the loudest action on a screen. Both
                              names for one variant: .btn-gold is what the hero
                              carousel calls it.
     .btn--ghost /.btn-outline-w  hairline — a second path out of a section
     .btn--outline            gold hair — a quiet action on a cream band
   Every variant fills in the same box: navy for structure, gold for emphasis,
   and a white sweep that crosses the face on hover. */
/* Bootstrap's stylesheet is loaded ahead of this one (the carousel needs it),
   and it ships a `.btn:hover` that outranks the fill declared below it: it sets
   background-color from its own --bs-btn-hover-bg, which our markup never
   defines, so the property lands on unset and the button goes transparent —
   navy type on the page ground, which reads as a button that vanishes under the
   pointer. Handing Bootstrap's variables our own values makes its rule resolve
   to this component's colours instead of to nothing, and the two rules beside
   it (`.btn-check+.btn:hover` and the disabled state) resolve with it. */
.btn,
.btn-gold,
.btn-outline-w {
    --btn-bg: var(--navy);
    --btn-fg: var(--on-navy);
    --btn-bd: var(--navy);
    --btn-hov-bg: var(--navy-deep);
    --btn-hov-fg: var(--on-navy);
    --btn-hov-bd: var(--gold);
    --btn-shade: rgba(255, 255, 255, .18);
    --btn-min: 48px;

    --bs-btn-color: var(--btn-fg);
    --bs-btn-bg: var(--btn-bg);
    --bs-btn-border-color: var(--btn-bd);
    --bs-btn-hover-color: var(--btn-hov-fg);
    --bs-btn-hover-bg: var(--btn-hov-bg);
    --bs-btn-hover-border-color: var(--btn-hov-bd);
    --bs-btn-box-shadow: none;
    --bs-btn-disabled-opacity: .45;
    position: relative; overflow: hidden; isolation: isolate;
    display: inline-flex; align-items: center; justify-content: center; gap: .6rem;
    min-height: var(--btn-min);
    padding: .95rem 1.9rem;
    font-family: var(--sans);
    font-size: .74rem; font-weight: 600; letter-spacing: .16em; text-transform: uppercase;
    color: var(--btn-fg);
    background-color: var(--btn-bg);
    background-image: linear-gradient(180deg, var(--btn-shade), transparent 62%);
    border: 1px solid var(--btn-bd); border-radius: var(--radius);
    cursor: pointer; text-align: center;
    transition: transform var(--dur) var(--ease), background-color var(--dur) var(--ease),
                border-color var(--dur) var(--ease), color var(--dur) var(--ease),
                box-shadow var(--dur) var(--ease);
}
/* The sweep. It sits on a negative layer, so it crosses the fill without ever
   touching the label, and it is written as a transform so it stays on the
   compositor. */
.btn::after,
.btn-gold::after,
.btn-outline-w::after {
    content: ""; position: absolute; z-index: -1; top: -20%; bottom: -20%; left: -65%; width: 45%;
    background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .38), transparent);
    transform: translateX(0) skewX(-18deg);
    transition: transform .8s var(--ease-out);
    pointer-events: none;
}
.btn:hover::after,
.btn-gold:hover::after,
.btn-outline-w:hover::after { transform: translateX(340%) skewX(-18deg); }
.btn:hover,
.btn-gold:hover,
.btn-outline-w:hover {
    --btn-bg: var(--btn-hov-bg); --btn-fg: var(--btn-hov-fg); --btn-bd: var(--btn-hov-bd);
    background-color: var(--btn-hov-bg);
    border-color: var(--btn-hov-bd);
    color: var(--btn-hov-fg); transform: translateY(-2px); box-shadow: var(--shadow-navy);
}
.btn:active, .btn-gold:active, .btn-outline-w:active { transform: translateY(0); }

/* Bootstrap also hangs a box-shadow and a focus ring on every .btn. Neither is
   this design's, so both are written out of the component. */
.btn:focus-visible,
.btn-gold:focus-visible,
.btn-outline-w:focus-visible { box-shadow: 0 0 0 3px var(--gold-a50); outline: none; }

/* Gold fill — reserved for the one action a screen is built around. */
.btn--gold,
.btn-gold { --btn-bg: var(--gold); --btn-fg: var(--navy); --btn-bd: var(--gold);
             --btn-hov-bg: var(--gold-soft); --btn-hov-fg: var(--navy); --btn-hov-bd: var(--gold-soft);
             --btn-shade: rgba(255, 255, 255, .34); }
.btn--gold:hover,
.btn-gold:hover { box-shadow: var(--shadow-gold); }

/* Hairline — navy ink on the page ground. */
.btn--ghost,
.btn-outline-w {
    --btn-bg: transparent; --btn-fg: var(--navy); --btn-bd: var(--line);
    --btn-hov-bg: var(--cream); --btn-hov-fg: var(--navy); --btn-hov-bd: var(--line-gold);
    --btn-shade: rgba(255, 255, 255, 0); background-image: none;
}
.btn--ghost:hover,
.btn-outline-w:hover { box-shadow: var(--shadow-sm); }

/* Gold hairline — the quiet action, for cream bands. */
.btn--outline {
    --btn-bg: transparent; --btn-fg: var(--gold-deep); --btn-bd: var(--line-gold);
    --btn-hov-bg: var(--navy); --btn-hov-fg: var(--on-navy); --btn-hov-bd: var(--navy);
    --btn-shade: rgba(255, 255, 255, 0); background-image: none;
}
.btn--outline:hover { box-shadow: var(--shadow-navy); }

.btn--block { width: 100%; }
.btn--sm { --btn-min: 44px; padding: .7rem 1.25rem; font-size: .68rem; }
.btn[disabled] { opacity: .45; cursor: not-allowed; transform: none; box-shadow: none; }

.btn-row { display: flex; flex-wrap: wrap; gap: .9rem; }
/* A stacked button column — used where the buttons are the last thing in a
   narrow panel and full width reads better than a wrapped row. */
.btn-row--stack { flex-direction: column; }

/* 5 ── Header / nav ------------------------------------------------------- */

/* Top Strip — contact detail rather than navigation, so it leaves the sticky bar
   as the page moves down past it and returns when the page moves back up
   (driven by data-strip-hidden, set in assets/js/main.js).

   The fold is a transform on the header, never a height collapse. A sticky
   header that changes height reflows the whole document, and the browser
   answers that reflow by adjusting scrollY — which this very scroll handler
   then reads back as a direction change. Hide, adjust, unhide, adjust: that
   loop is what made the bar flicker. Translating the header leaves the document
   height untouched, so there is nothing to feed back into the handler.

   --strip-h is stated once and read twice: the band's height, and the offset the
   header travels. */
.primary-header { --strip-h: 45px; }
.primary-header[data-strip-hidden="true"] { transform: translateY(calc(-1 * var(--strip-h))); }

.header-top-strip {
    height: var(--strip-h);
    display: flex;
    align-items: center;
    background: var(--cream);
    border-bottom: 1px solid var(--line-soft);
    transition: opacity var(--dur-fast) var(--ease), visibility var(--dur) var(--ease);
}
/* Out of sight also means out of the tab order — visibility leaves the layout
   alone, which is the whole point here. */
.primary-header[data-strip-hidden="true"] .header-top-strip { opacity: 0; visibility: hidden; }
.top-strip-content {
    display: flex; align-items: center; justify-content: space-between;
}
.header-contact-info {
    display: flex; align-items: center; gap: 1.5rem;
}
.header-contact-info a {
    display: inline-flex; align-items: center; gap: .45rem;
    font-family: var(--mono);
    font-size: .68rem; font-weight: 400; letter-spacing: .1em;
    color: var(--muted); transition: color var(--dur) var(--ease);
}
.header-contact-info a:hover { color: var(--gold-ink); }
.header-contact-info .icon { width: 14px; height: 14px; }
.header-social-links {
    display: flex; align-items: center; gap: .75rem;
}
.header-social-links a {
    display: grid; place-items: center; width: 28px; height: 28px;
    border-radius: 50%; color: var(--muted-2);
    transition: all .3s var(--ease);
}
.header-social-links a:hover {
    color: var(--gold-deep); background: var(--gold-a16);
    transform: translateY(-1px);
}
.header-social-links .icon { width: 14px; height: 14px; }

/* Main Navigation */
.primary-header {
    position: sticky; top: 0; z-index: 90;
    background: rgba(255, 255, 255, .92);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border-bottom: 1px solid var(--line-soft);
    transition: transform var(--dur) var(--ease),
                border-color .3s var(--ease), background .3s var(--ease);
}
.primary-header[data-scrolled="true"] {
    background: rgba(255, 255, 255, .97);
    border-bottom-color: var(--line-gold);
    box-shadow: 0 12px 34px -26px rgba(11, 36, 71, .45);
}

.main-navigation {
    position: relative;
}
.nav-wrapper {
    display: flex; align-items: center; justify-content: space-between;
    gap: 1.5rem; min-height: 72px;
}

/* Brand */
.brand { display: inline-flex; align-items: center; gap: .85rem; color: var(--navy); text-decoration: none; }
.brand:hover { color: var(--navy); }
/* The fallback monogram is the logo lockup in miniature: gold on navy. */
.brand-mark {
    width: 38px; height: 38px; flex: none;
    display: grid; place-items: center;
    border: 1px solid var(--navy); border-radius: var(--radius-sm);
    font-family: var(--display); font-size: 1rem; letter-spacing: .04em;
    color: var(--gold-soft); background: var(--navy);
    transition: transform var(--dur) var(--ease-spring), box-shadow var(--dur) var(--ease);
}
.brand:hover .brand-mark { transform: translateY(-1px); box-shadow: var(--shadow-sm); }
.brand-mark--logo {
    position: relative; overflow: hidden;
    border-color: transparent; background: none;
}
.brand-mark--logo img {
    position: absolute; inset: 0; width: 100%; height: 100%;
    object-fit: cover; transform: scale(1.95); transform-origin: 50% 44%;
    border-radius: var(--radius-sm);
}
.brand-text { display: flex; flex-direction: column; line-height: 1.15; }
.brand-name { font-family: var(--display); font-size: 1.16rem; letter-spacing: .16em; color: var(--navy); }
/* The second line of the lockup. It is two words now ("Premium Underwear"), so
   it is set smaller and tighter than the one-word line it used to be: tracked at
   .34em it ran wider than the name above it and pushed the header nav around at
   around 1000px. Held at or under the name's own width — and never wrapped — the
   lockup keeps the column it has always had. */
.brand-sub {
    font-family: var(--mono); font-size: .5rem; letter-spacing: .2em;
    text-transform: uppercase; color: var(--gold-ink); white-space: nowrap;
}

/* Desktop Menu */
.desktop-menu-container { display: flex; align-items: center; }
.primary-menu-list {
    display: flex; align-items: center; gap: .25rem;
    list-style: none; margin: 0; padding: 0;
}
.menu-item-link {
    position: relative;
    display: inline-flex; align-items: center; gap: .3rem;
    min-height: 44px;
    font-size: .74rem; font-weight: 500; letter-spacing: .16em;
    text-transform: uppercase; color: var(--muted);
    padding: .5rem .85rem; border-radius: var(--radius);
    transition: color var(--dur) var(--ease), background var(--dur) var(--ease);
}
/* The nav reads navy, not gold: at 12px uppercase the gold cannot carry text
   contrast, so the underline carries the brand instead. */
.menu-item-link::after {
    content: ""; position: absolute; left: .85rem; right: .85rem; bottom: .25rem;
    width: 0; height: 2px; border-radius: var(--radius-pill); background: var(--gold);
    transition: width .35s var(--ease-out);
}
.menu-item-link:hover {
    color: var(--navy);
    background: var(--navy-a08);
}
.menu-item-link:hover::after { width: calc(100% - 1.7rem); }
.menu-item.active .menu-item-link,
.menu-item-link[aria-current="page"] {
    color: var(--navy);
}
.menu-item.active .menu-item-link::after,
.menu-item-link[aria-current="page"]::after {
    width: calc(100% - 1.7rem);
}

/* CTA Button */
/* The section never gives up width. It used to be shrinkable, and between the
   980px drawer breakpoint and about 1070px the bar is short of room, so the
   browser paid for the row by squeezing the order button: "Order Now" broke
   onto three lines under a label icon and the plate grew to 77px, taller than
   the 72px bar. An order button that has to say two words stays one line — the
   band media query below is what finds it the room instead. */
.header-cta-section { display: flex; align-items: center; flex: none; }
.header-cta-button {
    display: inline-flex; align-items: center; gap: .5rem;
    white-space: nowrap;
    min-height: 44px; padding: .6rem 1.35rem;
    font-size: .7rem; font-weight: 600; letter-spacing: .16em;
    text-transform: uppercase; text-decoration: none;
    color: #fff; background-color: var(--navy);
    border: 1px solid var(--navy); border-radius: var(--radius);
    transition: background-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.header-cta-button:hover {
    background-color: var(--gold);
    border-color: var(--gold);
    color: var(--navy);
    box-shadow: var(--shadow-gold);
    transform: translateY(-1px);
}
.header-cta-button .icon { width: 14px; height: 14px; }
.header-cta-button:hover .icon { transform: translateX(3px); }

/* Mobile Menu Toggle */
.mobile-menu-toggle {
    display: none;
    flex-direction: column; justify-content: center; gap: 5px;
    width: 36px; height: 36px; padding: 0;
    background: none; border: 1px solid var(--line); border-radius: var(--radius);
    cursor: pointer; transition: border-color .3s var(--ease);
}
.mobile-menu-toggle:hover { border-color: var(--line-gold); }
.toggle-bar {
    display: block; width: 18px; height: 1.5px; margin: 0 auto;
    background: var(--navy); border-radius: 1px;
    transition: all .3s var(--ease);
    transform-origin: center;
}
.mobile-menu-toggle[aria-expanded="true"] .toggle-bar:nth-child(1) {
    transform: translateY(6.5px) rotate(45deg);
}
.mobile-menu-toggle[aria-expanded="true"] .toggle-bar:nth-child(2) {
    opacity: 0; transform: scaleX(0);
}
.mobile-menu-toggle[aria-expanded="true"] .toggle-bar:nth-child(3) {
    transform: translateY(-6.5px) rotate(-45deg);
}

/* Mobile Menu Overlay */
.mobile-menu-overlay {
    position: fixed; inset: 0; z-index: 100;
    background: var(--navy-a50);
    backdrop-filter: blur(4px); -webkit-backdrop-filter: blur(4px);
    opacity: 0; visibility: hidden;
    /* The drawer parks itself off-screen with translateX(100%). A transform
       still contributes to scrollable overflow, so without this clip the
       closed drawer added its own 380px to the document width on EVERY page
       and desktop got a horizontal scrollbar with a dead strip down the
       right. Clipping here keeps the off-canvas trick off the layout. */
    overflow: hidden;
    transition: opacity .35s var(--ease), visibility .35s var(--ease);
}
.mobile-menu-overlay[aria-hidden="false"] {
    opacity: 1; visibility: visible;
}
.mobile-menu-container {
    position: absolute; top: 0; right: 0; bottom: 0;
    width: min(380px, 88vw); max-width: 100%;
    background: var(--cream);
    border-left: 1px solid var(--line-gold);
    display: flex; flex-direction: column;
    transform: translateX(100%);
    transition: transform .35s var(--ease);
    box-shadow: -20px 0 60px var(--navy-a16);
}
.mobile-menu-overlay[aria-hidden="false"] .mobile-menu-container {
    transform: translateX(0);
}
.mobile-menu-header {
    display: flex; align-items: center; justify-content: space-between;
    padding: 1.25rem 1.5rem;
    border-bottom: 1px solid var(--line-soft);
}
.mobile-menu-close {
    display: grid; place-items: center; width: 36px; height: 36px;
    background: none; border: 1px solid var(--line); border-radius: var(--radius);
    color: var(--navy); cursor: pointer;
    transition: all .3s var(--ease);
}
.mobile-menu-close:hover {
    border-color: var(--line-gold); color: var(--gold-deep);
}
.mobile-menu-close .icon { width: 18px; height: 18px; }

.mobile-navigation {
    flex: 1; display: flex; flex-direction: column;
    padding: 1rem 0; overflow-y: auto;
}
.mobile-menu-list {
    list-style: none; margin: 0; padding: 0; flex: 1;
}
.mobile-menu-item {
    border-bottom: 1px solid var(--line-soft);
}
.mobile-menu-link {
    display: flex; align-items: center; gap: .75rem;
    padding: 1rem 1.5rem;
    font-size: .85rem; font-weight: 500; letter-spacing: .1em;
    text-transform: uppercase; color: var(--muted);
    text-decoration: none;
    transition: all .3s var(--ease);
}
.mobile-menu-link:hover {
    color: var(--navy); background: var(--navy-a08);
    padding-left: 2rem;
}
.mobile-menu-item.active .mobile-menu-link {
    color: var(--navy); border-left: 3px solid var(--gold); background: var(--gold-a08);
}

.mobile-menu-footer {
    padding: 1.25rem 1.5rem;
    border-top: 1px solid var(--line-soft);
    display: flex; flex-direction: column; gap: .75rem;
}
.mobile-cta-button {
    display: flex; align-items: center; justify-content: center; gap: .5rem;
    padding: .85rem 1.5rem;
    font-size: .78rem; font-weight: 600; letter-spacing: .1em;
    text-transform: uppercase; text-decoration: none;
    color: var(--navy); background: var(--gold);
    border: none; border-radius: var(--radius);
    transition: all .3s var(--ease);
}
.mobile-cta-button:hover {
    background: var(--gold-soft);
    box-shadow: 0 8px 24px var(--gold-a16);
}
.mobile-cta-button .icon { width: 16px; height: 16px; }
.mobile-cta-outline {
    background: transparent; color: var(--navy);
    border: 1px solid var(--line);
}
.mobile-cta-outline:hover {
    border-color: var(--line-gold); color: var(--gold-ink);
    background: var(--gold-a08);
}

body[data-mobile-menu-open="true"] { overflow: hidden; }

/* Responsive */
@media (max-width: 980px) {
    /* No strip to fold away, so the header must not travel either. */
    .header-top-strip { display: none; }
    .primary-header { --strip-h: 0px; }
    .desktop-menu-container { display: none; }
    .header-cta-section { display: none; }
    .mobile-menu-toggle { display: flex; }
}

/* The bar's tight band: the drawer's breakpoint up to where the full bar fits
   again. Brand, five nav items and the order button need about 1070px, so below
   that the nav gives the room back rather than the button — the same five items,
   a little less tracking and a little less padding. Measured, the row then fits
   from 981px up with ~50px to spare, and the band runs 20px past the point it
   stops being needed so a wider substitute font cannot push the order button
   back into a wrap. The underline offsets move with the padding, or it would
   overhang its own label at this size. */
@media (min-width: 981px) and (max-width: 1090px) {
    .nav-wrapper { gap: 1rem; }
    .primary-menu-list { gap: .1rem; }
    .menu-item-link { padding: .5rem .5rem; letter-spacing: .08em; }
    .menu-item-link::after { left: .5rem; right: .5rem; }
    .menu-item-link:hover::after { width: calc(100% - 1rem); }
}

/* Hero Carousel (Bootstrap) */
.hero-carousel {
    position: relative;
}

/* The band is the rest of the first screen — header plus hero make one page,
   which is the proportion the reference hero has — a 92vh copy band
   under a short header, the copy centred in it). Its header is about a third of
   ours, so the header is *subtracted* rather than guessed at: the two measured
   states are 118px above 980px (strip 45 + nav 73) and 73px at or below it,
   where the strip is not shown. Subtracting them is what keeps the indicators
   above the fold — the old flat 92vh did not, which is why it was removed. The
   copy is centred by `align-items`, so the air above and below it is whatever
   the band has left over; --hero-gap below is the guaranteed floor for a short
   viewport, not the whole of that air. */
.slide-item {
    --hero-head: 118px;
    min-height: calc(100vh - var(--hero-head));
    /* svh where it is supported: on a phone the small viewport is the one that
       makes the band and the header add up to a single screen. */
    min-height: calc(100svh - var(--hero-head));
    background-size: cover;
    background-position: center;
    position: relative;
    display: flex;
    align-items: center;
}

/* The strip is hidden from 980px down (see the header section), so the band
   gets 45px more of the screen there. Same one number, same reason. */
@media (max-width: 980px) {
    .slide-item { --hero-head: 73px; }
}

/* Directional overlay — warm charcoal that preserves photograph depth
   on the right while keeping copy fully legible on the left. */
.slide-item::before {
    content: '';
    position: absolute;
    inset: 0;
    background:
        linear-gradient(100deg, rgba(11, 36, 71, .92) 0%, rgba(11, 36, 71, .78) 35%, rgba(11, 36, 71, .38) 65%, rgba(11, 36, 71, .12) 100%),
        linear-gradient(0deg, rgba(11, 36, 71, .5) 0%, rgba(11, 36, 71, 0) 50%);
}

/* Each slide's photograph is the SITE_IMAGES slot named for its position in the
   carousel — hero-1, hero-2, hero-3 — and is painted on inline by
   background_style() in includes/functions.php, so no image path is written into
   this file. The .slide-N class stays as the hook for per-slide styling and for
   anything that needs to name a slide. A slot with nothing filed leaves the band
   on the ink ground the overlay above is built for. */

/* The copy block. --hero-measure is the one width the hero invents: it is the
   measure every line in the block is written against, and the bound that lets
   the title balance instead of running the full width of the plate. Everything
   else below is the site's own space and type scale — no raw pixels. */
.slide-content {
    --hero-measure: 660px;
    /* The copy's own air: the reference hero's padding, measured 80px/80px at
       1440x900. Inside the full-height band this is the floor the
       copy keeps when the screen is too short to centre it — on a tall screen
       the centring gives more, which is the same thing the reference does with
       its 80px inside a 92vh band. The rest of the rhythm lives in the margin
       steps further down: the space *inside* the stack is what the eye reads as
       the block's air, so that is where the air belongs. */
    --hero-gap: clamp(2.5rem, 9vh, 5.5rem);
    position: relative;
    z-index: 2;
    color: #fff;
    max-width: min(var(--hero-measure), 100%);
    padding-block: var(--hero-gap);
}

/* The hero label follows the site's micro-label system — the old bordered
   pill was the most template-looking element on the page. */
.slide-eyebrow {
    display: inline-flex;
    align-items: center;
    gap: .5rem;
    font-family: var(--mono);
    font-size: var(--fs-label);
    font-weight: 500;
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: var(--gold-soft);
    margin-bottom: var(--space-5);
}
/* Gold on navy — the logo's own pairing, borrowed for the label above a title,
   and the one place on the page where the accent is the first thing read. */
.slide-eyebrow::before {
    content: '';
    width: 24px;
    height: 1px;
    background: var(--gold-a80);
}

.slide-title {
    font-family: var(--display);
    /* Carried near the display scale and set tight — Playfair at hero size with
       default tracking reads loose and generic. The markup holds no <br>: the
       block's measure plus text-wrap: balance (from the h1–h4 base) breaks the
       same sentence sensibly at every width instead of at one hard point. 3.3rem
       is measured, not guessed — see the note below the rule. */
    font-size: clamp(1.9rem, 4.6vw, 3.3rem);
    font-weight: 700;
    line-height: 1.03;
    letter-spacing: -.03em;
    margin-bottom: var(--space-6);
    color: #fff;
    text-shadow: 0 2px 24px rgba(0, 0, 0, .3);
}

.slide-title em,
.slide-title span {
    font-style: italic;
    color: var(--gold-soft);
    text-shadow: 0 0 34px var(--gold-a50);
    /* The accent is always one word or two, and a hyphen inside it must not be
       a break opportunity: "re-engineered" splitting to "re-" / "engineered"
       across two lines is the one thing that undoes an otherwise clean break. */
    white-space: nowrap;
}

/* Line breaking, measured rather than guessed. At a 660px measure and 3.3rem
   every slide's title lands on two lines (widest line 647px of the 660px
   available) and the rag stays inside the measure; at 3.6rem the three slides
   disagree — one takes three lines while the other two take two — and a hero
   whose height changes slide to slide reads as broken. Guard both numbers
   together if either moves. */

/* Fluid, so the two breakpoints below never have to restate a size. The
   measure keeps the lead inside the dark half of the overlay, where it clears
   4.5:1 against the photograph even at its brightest. */
.slide-desc {
    font-size: clamp(.95rem, .3vw + .87rem, 1.05rem);
    line-height: 1.75;
    max-inline-size: 46ch;
    margin-bottom: var(--space-10);
    color: rgba(255, 255, 255, .82);
    text-shadow: 0 1px 10px rgba(0, 0, 0, .28);
}

/* Slide meta — eyebrow and slide index share one hairline, so the copy block
   opens on a ruled line instead of a floating label. */
.slide-meta {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    max-width: min(460px, 100%);
    /* The stack runs on two gaps: label and headline belong to each other, so
       the meta and title steps are one token apart (20px / 24px of margin),
       the lead keeps its own wider rhythm before the action (40px). Measured
       gaps between the boxes are 20 / 24 / 40 — the space moved *in* here, out
       of --hero-gap, which is the whole point of the two numbers. */
    margin-bottom: var(--space-5);
}
.slide-meta::after {
    content: "";
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, var(--gold-a50), transparent);
}
.slide-meta .slide-eyebrow { margin-bottom: 0; }
.slide-index {
    font-family: var(--mono);
    font-size: var(--fs-label);
    font-weight: 500;
    letter-spacing: var(--track-label);
    text-transform: uppercase;
    color: rgba(255, 255, 255, .5);
    white-space: nowrap;
}

/* Entrance — only the active slide animates, and each part of the copy rises a
   beat after the one above it. Reduced-motion users get the copy at rest. */
@keyframes hero-rise {
    from { opacity: 0; transform: translateY(16px); }
    to   { opacity: 1; transform: none; }
}
.carousel-item.active .slide-content > * { animation: hero-rise .7s var(--ease-out) both; }
.carousel-item.active .slide-content > *:nth-child(2) { animation-delay: .12s; }
.carousel-item.active .slide-content > *:nth-child(3) { animation-delay: .22s; }
.carousel-item.active .slide-content > *:nth-child(4) { animation-delay: .32s; }
@media (prefers-reduced-motion: reduce) {
    .carousel-item.active .slide-content > * { animation: none; }
}

/* Bootstrap Carousel Controls.
   Bootstrap holds these at half opacity and lifts them on hover; that is kept,
   but written here explicitly so the arrow is always more visible under the
   pointer than away from it — never less. */
.hero-carousel .carousel-control-prev,
.hero-carousel .carousel-control-next {
    width: 48px;
    height: 48px;
    top: 50%;
    bottom: auto;
    margin: 0 24px;
    transform: translateY(-50%);
    /* Set here so it can be lifted on hover: Bootstrap's own halves the arrow,
       and a control that dims under the pointer is a control that looks broken. */
    opacity: .72;
    background: rgba(255, 255, 255, .08);
    border: 1px solid rgba(255, 255, 255, .18);
    border-radius: var(--radius);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    transition: all var(--dur) var(--ease);
}

.hero-carousel .carousel-control-prev:hover,
.hero-carousel .carousel-control-next:hover {
    opacity: 1;
    background: rgba(255, 255, 255, .18);
    border-color: rgba(255, 255, 255, .4);
    transform: translateY(-50%) scale(1.05);
}

.hero-carousel .carousel-control-prev-icon,
.hero-carousel .carousel-control-next-icon {
    width: 18px;
    height: 18px;
}

/* Bootstrap Carousel Indicators — the active bar is the brand's gold, so the
   slide you are on is marked in the same colour as everything else. */
.hero-carousel .carousel-indicators [data-bs-target] {
    width: 32px;
    height: 3px;
    border-radius: var(--radius);
    background: rgba(255, 255, 255, .3);
    border: none;
    transition: all 0.4s cubic-bezier(.22, .61, .36, 1);
}

.hero-carousel .carousel-indicators .active {
    background: var(--gold);
    width: 48px;
    box-shadow: 0 0 12px var(--gold-a50);
}

/* Slide CTA — one action per slide. It is the only control on the photograph,
   so it is cut larger than the page buttons: same token system as section 4,
   bigger box, and the arrow steps forward on hover. */
/* No margin here: the lead's own bottom space sets the gap, so one value owns
   the whole stack instead of two margins collapsing into each other. The row is
   a flex box rather than a plain line of inline-level content, because an
   inline-level button leaves the line box's descender space underneath it — a
   few pixels that would otherwise show up as the bottom gap measuring short of
   the top one. */
.slide-cta { display: flex; }
.slide-cta .btn-hero {
    --btn-min: 56px;
    padding: 1.15rem 2.6rem;
    font-size: .75rem;
    font-weight: 600;
    letter-spacing: .2em;
    text-transform: uppercase;
    background: var(--gold);
    color: var(--navy);
    border: none;
    box-shadow: 0 6px 26px rgba(6, 26, 54, .4);
    transition: all var(--dur) var(--ease);
}
/* Over the photograph the CTA inverts instead of darkening: gold out, navy in. */
.slide-cta .btn-hero:hover {    background: var(--navy); color: var(--on-navy);
    transform: translateY(-2px);
    box-shadow: 0 12px 34px rgba(6, 26, 54, .55);
}
.slide-cta .btn-hero .icon {
    width: 1.05em;
    height: 1.05em;
    transition: transform var(--dur) var(--ease);
}
.slide-cta .btn-hero:hover .icon { transform: translateX(4px); }

/* Carousel responsive */
@media (max-width: 900px) {
    .hero-carousel .carousel-control-prev,
    .hero-carousel .carousel-control-next { width: 40px; height: 40px; margin: 0 12px; }
    .hero-carousel .carousel-control-prev-icon,
    .hero-carousel .carousel-control-next-icon { width: 16px; height: 16px; }
}
/* Below this width the label wraps, and a hairline with nothing left to span
   collapses into a stub between the two halves of it. The rule goes, the slide
   index keeps the right edge, and the label reads as two clean lines. */
@media (max-width: 560px) {
    .slide-meta { align-items: flex-start; flex-wrap: wrap; row-gap: var(--space-2); }
    .slide-meta::after { display: none; }
    .slide-index { margin-inline-start: auto; }
}

@media (max-width: 480px) {
    .slide-cta .btn-hero { --btn-min: 52px; padding: 1rem 1.8rem; letter-spacing: .16em; font-size: .7rem; }
    .hero-carousel .carousel-control-prev,
    .hero-carousel .carousel-control-next { display: none; }
    .hero-carousel .carousel-indicators { bottom: 1rem; gap: .6rem; }
    .hero-carousel .carousel-indicators [data-bs-target] { width: 24px; }
    .hero-carousel .carousel-indicators .active { width: 36px; }
}

/* Product visual placeholder (swapped for real photography when added) */
.visual {
    position: relative; aspect-ratio: 4 / 5; width: 100%;
    border: 1px solid var(--line-gold); border-radius: var(--radius-lg);
    background:
        radial-gradient(120% 90% at 50% 0%, var(--gold-a08), transparent 62%),
        linear-gradient(160deg, var(--cream), var(--panel));
    display: grid; place-items: center; overflow: hidden;
    transition: border-color var(--dur-slow) var(--ease), box-shadow var(--dur-slow) var(--ease);
}
.visual:hover {
    border-color: var(--line-gold);
    box-shadow: 0 10px 34px rgba(11, 36, 71, .10);
}
.visual img { width: 100%; height: 100%; object-fit: cover; }
.visual-monogram { font-family: var(--display); font-size: clamp(3rem, 9vw, 5.5rem); color: rgba(223, 166, 43, .12); letter-spacing: .06em; }
.visual::after {
    content: ""; position: absolute; inset: 12px; border: 1px solid var(--line-gold-soft);
    border-radius: calc(var(--radius-lg) - 4px); pointer-events: none;
}
/* Ticker */
.ticker { border-block: 1px solid var(--line-soft); background: var(--cream); overflow: hidden; padding-block: 1.15rem; }
.ticker-track { display: flex; gap: 3.5rem; width: max-content; animation: ticker 42s linear infinite; }
.ticker span {
    display: inline-flex; align-items: center; gap: .7rem; white-space: nowrap;
    font-size: .7rem; letter-spacing: .22em; text-transform: uppercase; color: var(--muted);
}
.ticker span::before { content: ""; width: 4px; height: 4px; background: var(--gold); border-radius: 50%; }
@keyframes ticker { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) { .ticker-track { animation: none; } }

/* 7 ── Cards -------------------------------------------------------------- */
.card {
    position: relative; display: flex; flex-direction: column; overflow: hidden;
    background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-lg);
    padding: clamp(1.5rem, 3vw, 2.25rem);
    transition: border-color var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease),
                background var(--dur-slow) var(--ease), box-shadow var(--dur-slow) var(--ease);
}
/* A gold hairline draws in across the top edge as the card is picked up — the
   same ribbon as the section rules, at card scale. */
.card::before {
    content: ""; position: absolute; inset: 0 0 auto 0; height: 2px;
    background: linear-gradient(90deg, transparent, var(--gold), transparent);
    transform: scaleX(0); transform-origin: 50% 50%;
    transition: transform .55s var(--ease-out);
}
.card:hover::before { transform: scaleX(1); }
.card:hover {
    border-color: var(--line-gold); transform: translateY(-5px);
    background: var(--panel-2); box-shadow: var(--shadow-navy);
}
.card h3 { margin-bottom: .5rem; }
.card p { font-size: .93rem; margin-bottom: 0; }
.card-icon {
    width: 46px; height: 46px; display: grid; place-items: center; margin-bottom: var(--space-5);
    border: 1px solid var(--line-gold-soft); border-radius: var(--radius); color: var(--gold-deep);
    background: var(--gold-a08);
    transition: background var(--dur) var(--ease), color var(--dur) var(--ease),
                border-color var(--dur) var(--ease), transform var(--dur) var(--ease-spring);
}
.card:hover .card-icon {
    background: var(--navy); border-color: var(--navy); color: var(--gold-soft);
    transform: translateY(-2px) rotate(-4deg);
}
/* Compact card — the companion block that sits beside a column of copy, and the
   card used inside lists of terms or standards. */
.card--tight { padding: 1.35rem 1.5rem; }

/* A card that is itself a link — the whole panel is the target, so the type
   inside keeps the card's colours instead of the link's. */
.card--link { color: inherit; text-decoration: none; }
.card--link:hover { color: inherit; }
.card--link:hover h3 { color: var(--gold-ink); }

/* Icon and copy on one line. The card then reads as a specification row rather
   than a panel, so a stack of them never competes with the heading above. */
.card--split { display: flex; gap: 1.1rem; align-items: flex-start; padding: 1.35rem 1.55rem; }
.card--split .card-icon { margin: 0; width: 42px; height: 42px; flex: none; }
.card--split strong { display: block; color: var(--navy); font-size: .97rem; margin-bottom: .2rem; }
.card--split p { color: var(--navy); font-size: .93rem; margin: 0; }
.card--split .small { color: var(--muted); }

/* A heading inside a card — the step titles on the order and enquiry forms.
   Card-sized, so it introduces its fields without competing with the page. */
.card-heading { font-size: clamp(1.3rem, 2.4vw, 1.55rem); line-height: 1.2; margin-bottom: 0; }

/* The card's opening line: the mark on the left, the card's index on the right,
   so the two read as one specification header instead of two stacked blocks.
   The number is set at display scale as the card's own watermark — outlined at
   rest, so it stays furniture and does not compete with the heading below it,
   then filled with the house gold and drawn across in a sheen as the card is
   picked up. */
.card-top {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 1rem; margin-bottom: var(--space-5);
}
.card-top .card-icon { margin-bottom: 0; }
/* The tinted fill is the fallback: where a stroke cannot be drawn, the number
   still reads as a watermark rather than disappearing. */
.card-num {
    font-family: var(--display); font-weight: 700; font-variant-numeric: tabular-nums;
    font-size: clamp(2rem, 4.6vw, 2.85rem); line-height: .82;
    letter-spacing: -.04em; white-space: nowrap; display: block; align-self: flex-start;
    padding-right: .06em; color: var(--gold-a16);
    transition: color var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease-spring);
}
.card:hover .card-num { color: var(--gold-deep); transform: translateY(-3px); }
@supports (-webkit-text-stroke: 1px currentColor) {
    .card-num { color: transparent; -webkit-text-stroke: 1.2px var(--line-gold); }
    .card:hover .card-num { -webkit-text-stroke-color: transparent; }
}
/* The fill as it is picked up: the emblem's gold read as a metal, one band
   travelling across the numerals rather than a static flat colour. */
@supports (-webkit-background-clip: text) {
    .card:hover .card-num {
        background: linear-gradient(100deg, var(--gold-deep) 0%, var(--gold-soft) 32%,
                    var(--champagne) 50%, var(--gold) 68%, var(--gold-deep) 100%);
        background-size: 260% 100%;
        -webkit-background-clip: text; background-clip: text;
        -webkit-text-fill-color: transparent;
        animation: num-sheen 2.6s var(--ease) infinite;
    }
}
@keyframes num-sheen { from { background-position: 100% 0; } to { background-position: -160% 0; } }

/* Product card
   ────────────────────────────────────────────────────────────────────────
   Image-first, and deliberately short. Three rules hold it together:
   1. The frame is a 4:3 landscape rectangle and a photograph fills it edge to
      edge. A frame with no photograph carries the piece's own accent as a
      tint and its mark as a watermark, so an unphotographed range still reads
      as a colour range rather than as twelve missing images.
   2. Below the frame are exactly three things: the name, the price and one
      action. Everything else the piece has to say belongs on the product page.
   3. Price and action are pinned to the card's floor, so a row lines up no
      matter how long any one name runs.
   ──────────────────────────────────────────────────────────────────────── */
.product-card {
    position: relative; display: flex; flex-direction: column; height: 100%; overflow: hidden;
    border: 1px solid var(--line); border-radius: var(--radius-lg); background: var(--panel);
    box-shadow: var(--shadow-sm);
    transition: border-color var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease),
                box-shadow var(--dur-slow) var(--ease);
}
/* The same gold ribbon as .card, drawn along this card's top edge as it is
   picked up. This card is the larger object, so it gets the slower draw. */
.product-card::before {
    content: ""; position: absolute; z-index: 4; inset: 0 0 auto 0; height: 2px;
    background: linear-gradient(90deg, transparent, var(--gold), transparent);
    transform: scaleX(0); transform-origin: 50% 50%;
    transition: transform .55s var(--ease-out);
}

/* The frame's own link to the piece. It covers the frame rather than wrapping
   it, because the price plate has to sit beside a link that is hidden from
   screen readers — a price inside an aria-hidden link is a price nobody hears.
   One tab stop per card is still the card's rule: this one is tabindex="-1" and
   aria-hidden, and the name below is the stop. */
.product-card-link { position: absolute; inset: 0; z-index: 1; display: block; text-decoration: none; color: inherit; }

/* ── Media ─────────────────────────────────────────────────────────────── */
.product-card-visual {
    position: relative; display: grid; place-items: center; overflow: hidden;
    /* The card is the image. A 4:3 landscape frame gives the piece the largest
       share of the card while keeping the row compact, so a grid of twelve
       reads as a catalogue rather than as a wall of text. */
    aspect-ratio: 4 / 3;
    /* One studio-cream ground for every piece. A photograph sits on it when
       there is one; the piece's own accent tint stands in for it when there is
       not, which is what keeps twelve unphotographed cards from looking like
       twelve missing images. */
    background:
        radial-gradient(72% 64% at 50% 42%, var(--card-glow, rgba(223, 166, 43, .14)), transparent 70%),
        linear-gradient(160deg, var(--cream) 0%, var(--panel-2) 100%);
    border-bottom: 1px solid var(--line-soft);
}
/* The mark at display size, set into the ground as a watermark rather than
   printed on it — the same two letters the monogram in front spells, at a
   weight low enough to read as a studio backdrop instead of as content. */
.product-card-visual::before {
    content: attr(data-mark); position: absolute; inset: 0; z-index: 0;
    display: grid; place-items: center;
    font-family: var(--display); font-weight: 700; font-size: 11rem; line-height: 1;
    letter-spacing: -.02em; color: rgba(11, 36, 71, .05);
    pointer-events: none;
}
/* A photograph is the whole subject, so the watermark steps out of its way.
   The ground itself no longer changes between the two states — it is the same
   studio cream either way. */
.product-card-visual--photo::before { display: none; }

/* The monogram, set in the piece's own accent, so an unphotographed range still
   reads as a colour range rather than as twelve identical placeholders. */
.product-card-visual .visual-monogram {
    position: relative; z-index: 1;
    font-size: clamp(2.75rem, 6vw, 4rem); font-weight: 700; letter-spacing: .06em;
    color: var(--card-accent, var(--gold-deep));
    transition: transform var(--dur-slow) var(--ease-spring);
}
/* A single pass of light across the frame on hover. Nothing is announced by it
   — it only tells the eye the card has been picked up. */
.product-card-visual::after {
    content: ""; position: absolute; z-index: 2; top: -30%; bottom: -30%; left: -55%; width: 40%;
    background: linear-gradient(100deg, transparent, var(--gold-a50), transparent);
    opacity: 0; transform: skewX(-18deg); pointer-events: none;
}
@media (hover: hover) {
    .product-card:hover .product-card-visual::after,
    .product-card:focus-within .product-card-visual::after {
        animation: card-sheen 1.05s var(--ease-out) forwards;
    }
}
.product-card-visual .visual {
    position: absolute; inset: 0; aspect-ratio: auto; border: 0; border-radius: 0;
    /* The card frame owns the rectangle, so the panel's own border, radius,
       aspect-ratio and wash are all switched off here and the frame's ground
       shows through; the twill below is the only thing over it. */
    background: none;
    /* The frame's row track must be definite or the photo's 100% height has
       nothing to resolve against: it falls back to the image's own 1:1 ratio,
       overflows the frame and clips the top and bottom of the product. */
    grid-template-rows: 100%;
}
/* cover — the frame is a 4:3 rectangle and the supplied photography is square,
   so the piece fills the frame edge to edge and crops from the top and bottom
   rather than sitting letterboxed inside it. */
.product-card-visual .visual img {
    width: 100%; height: 100%; object-fit: cover;
    transition: transform var(--dur-slow) var(--ease-out);
}
/* The woven twill — the section tape's 115deg warp, drawn at texture weight so
   a frame this large still reads as cloth rather than as a flat colour. */
.product-card-visual .visual::before {
    content: ""; position: absolute; inset: 0;
    background: repeating-linear-gradient(115deg, rgba(11, 36, 71, .03) 0 1px, transparent 1px 7px);
    pointer-events: none;
}
.product-card-visual--photo .visual::before { display: none; }
/* A hairline inset marks the frame of an unphotographed piece. A photograph
   fills the rectangle edge to edge, so the hairline steps aside for it. */
.product-card-visual .visual::after { inset: 12px; border-radius: var(--radius-sm); border-color: var(--line-gold-soft); }
.product-card-visual--photo .visual::after { display: none; }

/* One chip per plate, top-left, in the logo plate's ivory — the accent is
   already carrying the monogram and the price, so the chip stays quiet. */
.product-card-badge {
    position: absolute; top: 1rem; left: 1.05rem; z-index: 3;
    background: var(--navy); color: var(--on-navy);
    border: 0;
    font-family: var(--mono);
    font-size: .56rem; font-weight: 500; letter-spacing: .16em; text-transform: uppercase;
    padding: .38rem .7rem; border-radius: var(--radius-sm);
    box-shadow: 0 4px 14px rgba(6, 26, 54, .26);
}


/* ── Price ────────────────────────────────────────────────────────────────
   The price rides the frame's corner. It is the figure a shopper looks for
   first, so it is on the card before the copy is read at all — and because it
   sits over the photograph, it is the one element here that has to survive both
   a dark garment and a light studio ground: a navy plate with the numerals in
   the emblem's gold, which the site already uses for exactly this (see the
   enquiry band and the review cards).

   It is set opposite the badge — badge top-left, plate bottom-right — so the
   two never collide on a narrow card, and it is outside the frame's link so a
   screen reader hears the price once. */
.product-card-tag {
    position: absolute; right: .8rem; bottom: .8rem; z-index: 4;
    display: flex; align-items: baseline; gap: .5rem;
    margin: 0;
    background: var(--navy); color: var(--on-navy);
    border-radius: var(--radius); padding: .5rem .75rem;
    box-shadow: 0 6px 20px rgba(6, 26, 54, .3);
}
.product-card-tag-now {
    font-family: var(--display); font-size: clamp(1rem, 1.4vw, 1.15rem);
    font-weight: 600; line-height: 1; color: var(--gold-soft);
    font-variant-numeric: tabular-nums;
}
.product-card-tag-was {
    font-size: .74rem; line-height: 1; color: rgba(254, 253, 241, .55);
    text-decoration: line-through;
}

/* ── Body ──────────────────────────────────────────────────────────────── */
/* Four things and no more: name, two lines of the piece's own description, the
   sizes-and-colours line, and the actions. A grid of twelve cards is scanned
   rather than read, so the fabric, the specification, the gallery and the care
   card all wait on the product page — which the name and View Details both link
   to. */
.product-card-body {
    display: flex; flex-direction: column; gap: .7rem; flex: 1;
    padding: 1.15rem 1.25rem 1.25rem;
}
.product-card-name { margin: 0; font-size: clamp(1.02rem, 1.3vw, 1.16rem); line-height: 1.25; }
.product-card-name a { color: var(--navy); text-decoration: none; transition: color var(--dur) var(--ease); }
.product-card-name a:hover { color: var(--gold-ink); }

/* Two lines of the description and an ellipsis. A card line is a shop window,
   not the paragraph: the full text is one click away, and the clamp is what
   keeps a 400-character description from setting the height of every card in the
   row. The heading above it is never clamped — a piece's name is how a shopper
   tells it from the next one. */
.product-card-desc {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
    overflow: hidden;
    margin: 0; font-size: .88rem; line-height: 1.5; color: var(--muted);
}

/* The two questions a card is actually asked, answered from the catalogue: which
   sizes, and how many colours. Separated by the site's own interpunct rather
   than by borders, so the line reads as one caption. */
.product-card-meta {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: .3rem .55rem; margin: 0;
    font-family: var(--mono); font-size: .6rem; font-weight: 500;
    letter-spacing: .12em; text-transform: uppercase; color: var(--muted-2);
}
.product-card-meta span + span::before {
    content: "·"; margin-right: .55rem; color: var(--gold);
}

/* ── Action ────────────────────────────────────────────────────────────────
   One action, full width, 44px tall. A gold hairline rather than a fill: twelve
   navy slabs in one grid would shout louder than the pieces they sell, and the
   hair is the button system's own "quiet action on a cream band". It still
   fills navy on hover, so the primary read is never lost. */
/* `margin-top: auto` pins the actions to the card's floor, so a piece with no
   description or no sizes still ends its card on the same line as its
   neighbours in the row. */
.product-card-actions { display: flex; flex-wrap: wrap; gap: .5rem; flex: none; margin-top: auto; }
/* `auto` basis with the default `min-width: auto` is what stops a label breaking
   in half: a flex item never shrinks below its own text, so a card too narrow for
   the pair wraps the buttons onto two full-width lines — the right answer on a
   phone — instead of setting "View Details" on two lines. The pair measures
   139 + 112 + the 8px gap, and the body carries 20px either side, so 300px of
   card is where the one-line read begins. That is why the product grids hold a
   300px floor of their own (section 8). */
.product-card-details,
.product-card-order { flex: 1 1 auto; }

/* Hover and focus move together, and only where a pointer can hover */
@media (hover: hover) {
    .product-card:hover,
    .product-card:focus-within {
        border-color: var(--line-gold); transform: translateY(-6px); box-shadow: var(--shadow-navy);
    }
    .product-card:hover::before,
    .product-card:focus-within::before { transform: scaleX(1); }
    .product-card:hover .product-card-visual .visual img,
    .product-card:focus-within .product-card-visual .visual img { transform: scale(1.045); }
    /* The monogram steps forward on a frame with no photograph — the only piece
       of frame furniture that answers the pointer there. */
    .product-card:hover .product-card-visual .visual-monogram { transform: scale(1.06); }
}

/* 7b ── Reviews — the quote rail ------------------------------------------
   The review card is the site's own card at quote scale: the same white plate,
   the same hairline, the same gold ribbon that draws in along the top edge as
   the card is picked up. The star row and the display quotation mark take the
   place of the icon-and-number line the fabric cards open with, so the two
   bands read as one family rather than as two ideas.

   The rail is a scroll-snap track, not a slideshow: every review is a real
   element at every width, the pointer can drag it, and a phone gets exactly one
   card per view because half a card beside half a card is unreadable. The
   padding is hover room (the card lifts) and the negative margin gives it back,
   so the row still lines up with the heading above it; scroll-padding is the
   same measure, which is what makes a snapped card land on that line.
   ------------------------------------------------------------------------- */
.quotes { --quote-gap: 1.4rem; }
/* Flex, not grid-auto-columns: a percentage track there is sized against the
   implicit grid and Chrome shrinks it to fit the box, which quietly turns six
   cards into six slivers and leaves nothing to scroll. A flex basis with
   flex-shrink held at 0 cannot be talked out of its width. */
.quote-rail {
    display: flex; gap: var(--quote-gap);
    overflow-x: auto; overflow-y: hidden;
    scroll-snap-type: x mandatory;
    scroll-padding-inline: .5rem;
    padding: .5rem; margin: -.5rem;
    /* The scrollbar is the one piece of chrome the rail does without: the card
       that peeks in at the edge, the arrows and the indicators all say the row
       keeps going, and a grey bar under a cream band says "unfinished". */
    scrollbar-width: none;
}
.quote-rail::-webkit-scrollbar { display: none; }
@media (prefers-reduced-motion: no-preference) {
    .quote-rail { scroll-behavior: smooth; }
}
/* One tab stop for the rail itself, so the arrow keys can walk it card by card
   before either button is reached. The ring is the site's own (:focus-visible
   in section 2), which is why nothing is drawn here. */

.quote-card {
    position: relative; overflow: hidden;
    display: flex; flex-direction: column; min-width: 0;
    flex: 0 0 100%;
    scroll-snap-align: start;
    background: var(--panel); border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    padding: clamp(1.35rem, 2.6vw, 1.75rem);
    transition: border-color var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease),
                background var(--dur-slow) var(--ease), box-shadow var(--dur-slow) var(--ease);
}
.quote-card::before {
    content: ""; position: absolute; inset: 0 0 auto 0; height: 2px;
    background: linear-gradient(90deg, transparent, var(--gold), transparent);
    transform: scaleX(0); transform-origin: 50% 50%;
    transition: transform .55s var(--ease-out);
}
@media (hover: hover) {
    .quote-card:hover {
        border-color: var(--line-gold); transform: translateY(-5px);
        background: var(--panel-2); box-shadow: var(--shadow-navy);
    }
    .quote-card:hover::before { transform: scaleX(1); }
}
/* One card per view on a phone, two on a tablet, three on a desk. The basis is
   taken off the same gap the track is, so the columns and the step a swipe
   makes can never disagree. */
@media (min-width: 700px) {
    .quote-card { flex-basis: calc((100% - var(--quote-gap)) / 2); }
}
@media (min-width: 1040px) {
    .quote-card { flex-basis: calc((100% - var(--quote-gap) * 2) / 3); }
}

/* The card's opening line — the stars on the left, the quotation mark on the
   right, which is the specification header the fabric cards use with different
   furniture. The mark is drawn as a watermark: outlined at rest so it stays
   decoration and never competes with the quote under it, filled with the house
   gold as the card is picked up. Same device as .card-num, no second animation
   — this band adds a rail, not another loop. */
.quote-top {
    display: flex; align-items: flex-start; justify-content: space-between;
    gap: 1rem; margin-bottom: var(--space-4);
}
.quote-rating { display: inline-flex; align-items: center; gap: .2rem; margin: 0; }
.quote-star { display: block; }
.quote-star .icon { display: block; width: .95rem; height: .95rem; }
.quote-star.is-on { color: var(--gold); }
.quote-star.is-off { color: var(--line-gold); }
.quote-mark {
    font-family: var(--display); font-weight: 700;
    font-size: clamp(2.2rem, 4.4vw, 2.9rem); line-height: .62;
    color: var(--gold-a16); margin-top: .1rem;
    transition: color var(--dur-slow) var(--ease);
}
@supports (-webkit-text-stroke: 1px currentColor) {
    .quote-mark { color: transparent; -webkit-text-stroke: 1.2px var(--line-gold); }
}
@media (hover: hover) {
    .quote-card:hover .quote-mark { color: var(--gold); -webkit-text-stroke-color: transparent; }
    @supports (-webkit-text-stroke: 1px currentColor) {
        .quote-card:hover .quote-mark { -webkit-text-stroke-width: 0; }
    }
}

/* The quote itself, at reading size rather than display size: this is the only
   paragraph on the page a visitor is meant to take at face value, so it is set
   as prose and left to the measure the card gives it. */
.quote-body { margin: 0 0 var(--space-5); }
.quote-body p {
    font-size: clamp(.96rem, 1.5vw, 1.05rem); line-height: 1.6;
    color: var(--navy); margin: 0;
}

/* Who said it, and what they bought. Pinned to the card's floor with the ruled
   row above it, so the attribution of every card in a row sits on one line no
   matter how long any quote runs — the same rule the product card's price and
   action follow. */
.quote-by {
    margin-top: auto; display: flex; align-items: center; gap: .85rem;
    padding-top: var(--space-5); border-top: 1px solid var(--line-soft);
}
.quote-media { position: relative; flex: none; width: 46px; height: 46px; margin: 0; }
.quote-media img {
    width: 100%; height: 100%; object-fit: cover;
    border-radius: var(--radius-pill); border: 1px solid var(--line-gold-soft);
}
/* The monogram stands in for a portrait that was never filed. */
.quote-initial {
    width: 100%; height: 100%; display: grid; place-items: center;
    border-radius: var(--radius-pill); border: 1px solid var(--line-gold-soft);
    background: var(--gold-a08); color: var(--gold-ink);
    font-family: var(--display); font-weight: 700; font-size: 1.1rem; line-height: 1;
}
/* Verified — the check rides the portrait's corner, the smallest possible mark
   that still says the purchase is real. Gold rather than the green a review
   widget usually reaches for: this site has one accent and it is not adopting a
   second for a badge. */
.quote-verified {
    position: absolute; right: -3px; bottom: -3px;
    width: 19px; height: 19px; display: grid; place-items: center;
    background: var(--navy); color: var(--gold-soft);
    border-radius: var(--radius-pill); border: 2px solid var(--panel);
}
.quote-verified .icon { width: .62rem; height: .62rem; }
.quote-who { min-width: 0; }
.quote-who strong { display: block; font-size: .93rem; line-height: 1.25; color: var(--navy); }
.quote-who span { display: block; font-size: .78rem; line-height: 1.4; color: var(--muted-2); }

/* Controls — the hero carousel's, in the light palette: 46px plates with the
   house hairline, and bars rather than dots for the position, so the two
   carousels on the page are recognisably the same control. The arrows and the
   bars move the rail; they own nothing, which is why the rail is complete and
   scrollable with JavaScript switched off. */
.quote-controls {
    position: relative;
    display: flex; align-items: center; justify-content: center; gap: 1rem;
    margin-top: clamp(1.35rem, 3vw, 2rem);
}
.quote-btn {
    flex: none; width: 46px; height: 46px; display: grid; place-items: center;
    background: var(--panel); border: 1px solid var(--line-gold-soft);
    border-radius: var(--radius); color: var(--gold-deep); cursor: pointer;
    transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
                color var(--dur) var(--ease);
}
.quote-btn .icon { width: 1.05rem; height: 1.05rem; }
.quote-btn--prev .icon { transform: rotate(180deg); }
.quote-btn:hover:not(:disabled) { background: var(--gold-a08); border-color: var(--line-gold); }
.quote-btn:disabled { opacity: .32; cursor: default; }

/* The bars are 3px of paint with a 31px hit area behind them: the visible rule
   stays a rule, and the thing a thumb aims at is still a target. */
.quote-indicators { display: flex; align-items: center; gap: .45rem; }
.quote-indicators button {
    position: relative; width: 30px; height: 3px; padding: 0; border: 0; cursor: pointer;
    background: var(--gold-a16); border-radius: var(--radius);
    transition: background var(--dur) var(--ease), width var(--dur) var(--ease);
}
.quote-indicators button::after { content: ""; position: absolute; inset: -14px -3px; }
.quote-indicators button.is-active { width: 46px; background: var(--gold-deep); }
/* A bar the rail cannot reach is a bar that lies about how much is left. */
.quote-indicators button[hidden] { display: none; }

/* The position, printed — and the live region that announces it. It sits on the
   rail's right edge so the three controls stay optically centred, and on a
   narrow screen it goes to screen readers only rather than into the row. */
.quote-index {
    position: absolute; right: 0; top: 50%; transform: translateY(-50%);
    font-family: var(--mono); font-size: var(--fs-label); font-weight: 500;
    letter-spacing: var(--track-label); text-transform: uppercase;
    color: var(--muted-2); font-variant-numeric: tabular-nums;
}
@media (max-width: 900px) {
    .quote-index {
        width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
        overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
    }
}

/* 8 ── Grids --------------------------------------------------------------
   Every card grid on the site is one of these four. Because the columns are
   auto-fit, a section with two products lays out as two columns and a section
   with six cards as three — the grid heals itself as the catalogue changes, so
   a row never leaves an empty cell sitting beside it.
   ------------------------------------------------------------------------- */
.grid { display: grid; gap: 1.4rem; }
.grid--2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }
/* The product grids set their own floor instead of borrowing one: a card has to
   hold its two actions on a single line (300px — see the action block in section
   7), and, with the price plate riding the frame's corner, enough frame width for
   the badge and the plate to stay apart. Below this the columns collapse, which
   is the right order — the row loses a column before a card loses its layout. */
.grid--products { grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); }

.split { display: grid; gap: clamp(2rem, 5vw, 4rem); grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); align-items: start; }

/* The form variant: fields take the width, the summary holds the rail. It
   collapses to one column before the columns would squeeze the fields. */
.split--form { grid-template-columns: minmax(0, 1.35fr) minmax(300px, .85fr); }
@media (max-width: 900px) { .split--form { grid-template-columns: 1fr; } }

/* USP list */
.usp-list { list-style: none; margin: 0; padding: 0; display: grid; gap: .85rem; }
.usp-list li { display: flex; gap: .85rem; align-items: flex-start; font-size: .95rem; color: var(--navy); }
.usp-list .icon { color: var(--gold-deep); margin-top: .18rem; width: 1.05rem; height: 1.05rem; }

.usp-list--cols { grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }

/* Grids that hold companion cards (terms, standards, specifications) — a
   tighter stack than the card grids, because each cell is one line of copy. */
.grid--tight { gap: 1rem; }

/* Spec strip */
.strip { display: grid; gap: 1px; background: var(--line-soft); border: 1px solid var(--line-soft); border-radius: var(--radius-lg); overflow: hidden; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.strip-cell {
    background: var(--cream); padding: 1.7rem 1.5rem;
    display: flex; flex-direction: column; gap: .35rem;
    transition: background var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
/* Each cell lights up on its own, so the strip reads cell by cell rather than
   as one slab. */
.strip-cell:hover { background: var(--panel-2); box-shadow: inset 0 2px 0 var(--gold); }
.strip-cell .icon { color: var(--gold-deep); width: 1.3rem; height: 1.3rem; margin-bottom: .5rem; transition: transform var(--dur) var(--ease-spring); }
.strip-cell:hover .icon { transform: translateY(-2px); }
.strip-cell strong { font-size: .95rem; color: var(--navy); font-weight: 600; }
.strip-cell span { font-size: .82rem; color: var(--muted-2); }
/* A short strip — three or four label/value cells sitting inside a column of
   copy rather than spanning the page. */
.strip--sm { grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); }

/* A strip of record cells: the label above the fact, in the mono every label on
   the site is set in, and no icons — this is a register, not a row of features.
   Used on the certified page for the company's registered details, so it reads
   as the same kind of object as the documents above it. */
.strip--record .strip-cell span {
    font-family: var(--mono);
    font-size: var(--fs-label); font-weight: 500;
    letter-spacing: var(--track-label); text-transform: uppercase;
    color: var(--muted-2);
}

/* Comparison */
.compare { width: 100%; border-collapse: collapse; font-size: .93rem; }
.compare th, .compare td { padding: 1.05rem 1.25rem; text-align: left; border-bottom: 1px solid var(--line-soft); }
.compare thead th {
    font-family: var(--mono); font-size: .62rem; letter-spacing: .18em;
    text-transform: uppercase; color: var(--muted-2); font-weight: 500;
    border-bottom-color: var(--line-gold);
}
.compare thead th:nth-child(2) { color: var(--gold-ink); }
.compare tbody th { font-weight: 500; color: var(--navy); font-family: var(--sans); }
.compare tbody td:nth-child(2) { color: var(--navy); font-weight: 500; }
.compare tbody td:nth-child(3) { color: var(--muted-2); }
.compare tbody tr:hover { background: var(--gold-a08); }

/* 9 ── Tables ------------------------------------------------------------- */
.table-wrap { overflow-x: auto; border: 1px solid var(--line-soft); border-radius: var(--radius-lg); }
.table { width: 100%; border-collapse: collapse; font-size: .93rem; min-width: 620px; }
.table th, .table td { padding: 1rem 1.25rem; text-align: left; border-bottom: 1px solid var(--line-soft); }
.table thead th {
    background: var(--cream); font-family: var(--mono);
    font-size: .62rem; letter-spacing: .18em; text-transform: uppercase;
    color: var(--muted-2); font-weight: 500;
}
.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr[data-popular="true"] { background: var(--gold-a08); }
.table tbody tr[data-popular="true"] td:first-child { box-shadow: inset 2px 0 0 var(--gold); }
.table td.size-cell { font-family: var(--display); font-size: 1.1rem; color: var(--gold-deep); }
/* Two-column specification table — row labels read as spec names, values as
   figures. Used on the certified page for the measured build.
   A spec table is short by nature, so it drops the 620px floor the wide tables
   need and fits its column instead of scrolling inside it — and its outer
   columns sit on the frame's own text rail rather than adding a second inset to
   it, with the figures sharing one right rail the length of the table. */
.table--spec { min-width: 0; }
.table--spec tbody th { font-family: var(--sans); font-weight: 500; font-size: .93rem; color: var(--navy); }
.table--spec th:first-child, .table--spec td:first-child { padding-left: 0; }
.table--spec th:last-child, .table--spec td:last-child { padding-right: 0; }
.table--spec thead th:last-child, .table--spec td.size-cell { text-align: right; }
.pill {
    display: inline-block; font-size: .58rem; letter-spacing: .16em; text-transform: uppercase;
    color: var(--gold-ink); border: 1px solid var(--line-gold); border-radius: var(--radius-pill); padding: .18rem .6rem; margin-left: .55rem;
}

/* Steps */
.steps { counter-reset: step; list-style: none; margin: 0; padding: 0; display: grid; gap: 1.4rem; }
.steps li { position: relative; padding-left: 3.6rem; }
.steps li::before {
    counter-increment: step; content: counter(step, decimal-leading-zero);
    position: absolute; left: 0; top: .1rem;
    font-family: var(--display); font-size: .95rem; color: var(--gold-deep);
    border: 1px solid var(--line-gold); border-radius: var(--radius-sm);
    width: 2.4rem; height: 2.4rem; display: grid; place-items: center;
}
.steps strong { display: block; color: var(--navy); font-size: .98rem; margin-bottom: .2rem; }
.steps span { font-size: .9rem; color: var(--muted); }

/* 10 ── Forms ------------------------------------------------------------- */
.form-grid { display: grid; gap: 1.15rem; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.field { display: flex; flex-direction: column; gap: .5rem; }
.field--full { grid-column: 1 / -1; }
.field label {
    font-family: var(--mono);
    font-size: .64rem; font-weight: 500; letter-spacing: .2em;
    text-transform: uppercase; color: var(--muted);
}
.field .hint { font-size: .78rem; color: var(--muted-2); }
.field .error { font-size: .8rem; color: var(--danger); }
/* Error slots are always rendered so JavaScript can fill them; hide the empty ones */
.field .error:empty { display: none; }

input[type="text"], input[type="email"], input[type="tel"], input[type="number"],
input[type="password"], select, textarea {
    width: 100%; padding: .85rem 1rem;
    background: var(--white); color: var(--navy);
    border: 1px solid var(--line); border-radius: var(--radius);
    font-family: var(--sans); font-size: .95rem;
    transition: border-color .25s var(--ease), background .25s var(--ease);
}
input:hover, select:hover, textarea:hover { border-color: var(--line-gold); }
/* Focus is drawn navy on a gold halo: the border is the state, the halo is the
   emphasis, and neither depends on colour alone to be seen. */
input:focus, select:focus, textarea:focus {
    outline: none; border-color: var(--navy); background: var(--cream);
    box-shadow: 0 0 0 3px var(--gold-a16);
}
input[aria-invalid="true"], textarea[aria-invalid="true"], select[aria-invalid="true"] { border-color: var(--danger); }
textarea { resize: vertical; min-height: 130px; }
/* A field that only ever holds two lines — the address block on the order form. */
.textarea--short { min-height: 92px; }
select { appearance: none; background-image: linear-gradient(45deg, transparent 50%, var(--muted) 50%), linear-gradient(135deg, var(--muted) 50%, transparent 50%); background-position: calc(100% - 20px) 50%, calc(100% - 15px) 50%; background-size: 5px 5px, 5px 5px; background-repeat: no-repeat; padding-right: 2.6rem; }
::placeholder { color: var(--muted-2); }

.radio-row { display: flex; flex-wrap: wrap; gap: .8rem; }
.radio {
    display: flex; align-items: flex-start; gap: .7rem; cursor: pointer;
    border: 1px solid var(--line); border-radius: var(--radius); padding: .9rem 1.05rem;
    flex: 1 1 190px; transition: border-color .25s var(--ease), background .25s var(--ease);
}
.radio:hover { border-color: var(--line-gold); }
.radio:has(input:checked) { border-color: var(--gold); background: var(--gold-a08); }
.radio input { margin: .28rem 0 0; accent-color: var(--gold); }
.radio strong { display: block; font-size: .88rem; color: var(--navy); font-weight: 600; }
.radio span { font-size: .78rem; color: var(--muted-2); }

/* Alerts */
.alert {
    display: flex; gap: .8rem; align-items: flex-start;
    padding: 1rem 1.15rem; border-radius: var(--radius); font-size: .9rem;
    border: 1px solid var(--line); background: var(--panel); margin-bottom: 1.5rem;
}
.alert--success { border-color: rgba(127, 191, 138, .4); color: var(--success); background: rgba(127, 191, 138, .06); }
.alert--error   { border-color: rgba(228, 115, 107, .4); color: var(--danger);  background: rgba(228, 115, 107, .06); }
.alert--info    { border-color: var(--line-gold); color: var(--gold-ink); background: var(--gold-a08); }
.alert .icon { width: 1.1rem; height: 1.1rem; margin-top: .2rem; }
.alert ul { margin: .35rem 0 0; padding-left: 1.1rem; }
/* The message column is a flex child: without min-width:0 a long code path or
   unwrapped token would set the floor width and push the page sideways. */
.alert > * { min-width: 0; }
.alert code { overflow-wrap: anywhere; }

/* Order summary */
.summary { border: 1px solid var(--line-gold); border-radius: var(--radius-lg); background: var(--cream); padding: 1.75rem; }
/* On the order page the summary follows the fields down the rail. It only
   sticks while the two-column form layout is in force. */
@media (min-width: 901px) { .summary--sticky { position: sticky; top: 100px; } }
.summary h3 { font-size: 1.05rem; letter-spacing: .06em; }
.summary-row { display: flex; justify-content: space-between; gap: 1rem; padding-block: .6rem; font-size: .9rem; color: var(--muted); border-bottom: 1px solid var(--line-soft); }
.summary-row span:last-child { color: var(--navy); }
.summary-row--total { border-bottom: 0; border-top: 1px solid var(--line-gold); margin-top: .5rem; padding-top: 1rem; font-size: 1rem; }
.summary-row--total span:last-child { font-family: var(--display); font-size: 1.5rem; color: var(--gold-deep); }
.summary-note { font-size: .78rem; color: var(--muted-2); margin-top: 1rem; }
.free-ship-bar { height: 3px; background: var(--line-soft); border-radius: var(--radius-pill); overflow: hidden; margin: .75rem 0 .4rem; }
.free-ship-bar i { display: block; height: 100%; background: var(--gold); transition: width .4s var(--ease); }

/* Contact list */
.contact-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.3rem; }
.contact-list li { display: flex; gap: 1rem; }
.contact-list .icon { color: var(--gold-deep); margin-top: .22rem; }
.contact-list strong {
    display: block; margin-bottom: .3rem;
    font-family: var(--mono); font-size: var(--fs-label); font-weight: 500;
    letter-spacing: var(--track-label); text-transform: uppercase; color: var(--muted-2);
}
.contact-list a, .contact-list address { color: var(--navy); font-style: normal; font-size: .95rem; }

/* The masthead. Centred, so the eyebrow, the title and the standfirst stack on
   one axis down the middle of the page — and the tape rule closes that axis,
   exactly as it closes every section heading below it. */
.page-head {
    padding-block: clamp(3.5rem, 8vw, 6rem) clamp(2.5rem, 5vw, 3.5rem);
    border-bottom: 1px solid var(--line-soft);
    position: relative; overflow: hidden;
    text-align: center;
}
/* Symmetric bloom — the glow is anchored to the centre line, so it reads as
   light behind the title rather than a wash in one corner. */
.page-head::before {
    content: ""; position: absolute; inset: 0;
    background: radial-gradient(58% 130% at 50% 0%, var(--gold-a16), transparent 68%);
    pointer-events: none;
}
.page-head h1 { margin-bottom: 0; }
/* The tape closes the masthead — one woven edge under the title, the same one
   that sits under every section heading. */
.page-head h1::after {
    content: ""; display: block;
    width: 76px; height: 5px; margin: var(--space-5) auto 0;
    background: var(--rule-tape);
}
.page-head .lead { margin: var(--space-5) auto 0; }
.page-head .btn-row { justify-content: center; }
.crumbs { font-size: .72rem; letter-spacing: .16em; text-transform: uppercase; color: var(--muted-2); margin-bottom: 1.35rem; display: flex; gap: .6rem; flex-wrap: wrap; justify-content: center; }
.crumbs a { color: var(--muted-2); }
.crumbs a:hover { color: var(--gold-ink); }

/* ── Page banner — a section photograph as the ground for the masthead ───────
   The banner masthead is the flat one turned editorial: the centred stack is
   dropped for a single left axis, and the copy is trimmed to three parts —
   path, title, one-line standfirst. The tape still closes the title, anchored
   to that same left edge. */
.page-head--banner {
    display: flex;
    align-items: center;
    min-height: clamp(300px, 36vh, 420px);
    padding-block: clamp(3rem, 6.5vw, 4.5rem);
    border-bottom: 0;
    background-color: var(--white);
    background-position: center;
    background-size: cover;
    text-align: left;
}
/* Directional wash, the same treatment the hero slides use: the copy side is
   held at ink while the photograph survives on the right. */
.page-head--banner::before {
    background:
        linear-gradient(100deg, rgba(11, 36, 71, .93) 0%, rgba(11, 36, 71, .82) 35%, rgba(11, 36, 71, .4) 65%, rgba(11, 36, 71, .15) 100%),
        linear-gradient(0deg, rgba(11, 36, 71, .45) 0%, rgba(11, 36, 71, 0) 50%);
    z-index: 1;
}
.page-head--banner > .container { position: relative; z-index: 2; width: 100%; }
.page-head--banner .crumbs { justify-content: flex-start; margin-bottom: var(--space-4); }
/* The display scale is set for a full page masthead. On a banner it is pulled
   back a step so the title reads as a headline over the photograph rather than
   filling the band. */
.page-head--banner h1 {
    font-size: clamp(1.75rem, 3.3vw, 2.6rem);
    line-height: 1.14;
    max-width: 30ch;
    margin-bottom: 0;
    color: #fff;
    text-shadow: 0 2px 20px rgba(0, 0, 0, .25);
}
.page-head--banner h1::after { margin: var(--space-5) 0 0; }
/* One line on desktop, wrapping naturally from tablet down. */
.page-head--banner .lead { margin: var(--space-4) 0 0; max-width: 74ch; color: rgba(255, 255, 255, .75); text-shadow: 0 1px 8px rgba(0, 0, 0, .2); }
.page-head--banner .crumbs { color: rgba(255, 255, 255, .55); }
.page-head--banner .crumbs a { color: rgba(255, 255, 255, .55); transition: color var(--dur) var(--ease); }
.page-head--banner .crumbs a:hover { color: #fff; }

/* One image per section, painted on inline by background_style() from the
   SITE_IMAGES slot the page names — banner-about, banner-certified,
   banner-contact, banner-order, banner-product, banner-service. So a replacement
   banner is filed in the admin panel and no image path is written into this
   file. The classes stay as the hooks for the overlay and any per-section
   treatment; a slot with nothing filed leaves the masthead on its ink ground. */

/* 11 ── Footer ------------------------------------------------------------ */
.site-footer {
    position: relative;
    background: var(--navy-deep);
    border-top: 1px solid rgba(223, 166, 43, .15);
    padding-block: clamp(3rem, 6vw, 4.5rem) 0;
    color: rgba(255, 255, 255, .7);
}
/* The tape's second and last home: the footer seam, where the storefront
   signs off. Two placements, both landmark edges — that is the whole motif. */
.site-footer::before {
    content: ""; position: absolute; top: 0; left: 0; right: 0; height: 3px;
    background: var(--tape);
}
.footer-grid { display: grid; gap: 2.5rem; grid-template-columns: 1.5fr 1fr 1fr 1.3fr; }
@media (max-width: 900px) { .footer-grid { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); } }

/* Footer Title */
.footer-title {
    font-family: var(--mono);
    font-size: var(--fs-label); font-weight: 500; letter-spacing: var(--track-label);
    text-transform: uppercase; color: var(--gold);
    margin-bottom: var(--space-5); padding-bottom: var(--space-3);
    border-bottom: 1px solid rgba(223, 166, 43, .15);
}

/* Footer Links */
.footer-links { list-style: none; margin: 0; padding: 0; display: grid; gap: 0; }
.footer-links li { border-bottom: 1px solid var(--line-soft); }
.footer-links li:last-child { border-bottom: 0; }
.footer-links a {
    display: flex; align-items: center; gap: .5rem;
    color: rgba(255, 255, 255, .55); font-size: .88rem; padding: .6rem 0;
    transition: color .25s var(--ease), padding-left .25s var(--ease);
}
.footer-links a::before {
    content: ""; width: 4px; height: 4px; border-radius: 50%;
    background: var(--gold); opacity: 0; flex-shrink: 0;
    transition: opacity .25s var(--ease);
}
.footer-links a:hover { color: var(--gold-soft); padding-left: .35rem; }
.footer-links a:hover::before { opacity: 1; }

/* Footer About */
.footer-about p { font-size: .88rem; line-height: 1.7; color: rgba(255, 255, 255, .6); }
.footer-brand-link { display: inline-flex; align-items: center; gap: .65rem; text-decoration: none; margin-bottom: 1rem; }
.footer-brand-logo { display: flex; align-items: center; }
.footer-logo { height: 44px; width: auto; }
.footer-brand-text { line-height: 1.1; }
.footer-brand-text .brand-name { color: #fff; }
.footer-brand-text .brand-sub { color: var(--gold-soft); }

/* Trust Badge */
.footer-trust {
    display: inline-flex; align-items: center; gap: .5rem;
    background: rgba(223, 166, 43, .1); border: 1px solid rgba(223, 166, 43, .2);
    color: var(--gold-soft); font-size: .68rem; font-weight: 600; letter-spacing: .06em;
    padding: .45rem .85rem; border-radius: var(--radius);
}
.footer-trust .icon { width: .85rem; height: .85rem; }

/* Social Icons */
.footer-socials { display: flex; gap: .6rem; }
.f-social {
    width: 38px; height: 38px; border-radius: 50%;
    border: 1px solid rgba(255, 255, 255, .12); background: rgba(255, 255, 255, .05);
    color: rgba(255, 255, 255, .5); display: grid; place-items: center;
    transition: all .3s var(--ease);
}
.f-social .icon { width: 16px; height: 16px; }
.f-social:hover {
    background: var(--gold); border-color: var(--gold); color: var(--navy);
    transform: translateY(-3px); box-shadow: 0 6px 18px rgba(223, 166, 43, .18);
}

/* Footer Contact Items */
.footer-contact-list { display: grid; gap: 1.1rem; }
.f-contact-item { display: flex; align-items: flex-start; gap: .85rem; }
.f-contact-icon {
    width: 38px; height: 38px; min-width: 38px; margin-top: 2px;
    border-radius: var(--radius); background: var(--gold-a08);
    border: 1px solid var(--gold-a16); color: var(--gold-soft);
    display: grid; place-items: center; flex-shrink: 0;
}
.f-contact-icon .icon { width: 16px; height: 16px; }
.f-contact-label {
    font-family: var(--mono);
    font-size: .6rem; font-weight: 500; letter-spacing: .18em;
    text-transform: uppercase; color: rgba(255, 255, 255, .4); margin-bottom: .25rem;
}
/* A contact row is an icon beside its text, and the text is free to shrink: the
   row's own minimum would otherwise be set by its longest unbreakable token, and
   the email address is one — 230px of it, which with the 38px icon and the gap
   asks for 281px inside the footer's 252px column and pushed the whole page 9px
   sideways on a narrow screen. `anywhere` rather than `break-word` because it is
   the one that lowers the minimum: the address breaks only where it has to, and
   the margins at every width go back to being the layout's business. */
.f-contact-item > div:not(.f-contact-icon) { min-width: 0; }
.f-contact-value { font-size: .88rem; color: rgba(255, 255, 255, .8); line-height: 1.5; overflow-wrap: anywhere; }
.f-contact-value a { color: rgba(255, 255, 255, .8); }
.f-contact-value a:hover { color: var(--gold-soft); }

/* Footer Bottom */
.footer-bottom {
    margin-top: 2.5rem; padding: 1.5rem 0;
    border-top: 1px solid rgba(255, 255, 255, .08);
    background: rgba(0, 0, 0, .15);
}
.footer-bottom-inner {
    display: flex; flex-wrap: wrap; gap: .5rem; justify-content: center;
    font-size: .76rem; color: rgba(255, 255, 255, .4);
}
.footer-bottom-sep { color: rgba(255, 255, 255, .2); }
.site-footer address { font-style: normal; }
.footer-bottom a { color: rgba(255, 255, 255, .4); }
.footer-bottom a:hover { color: var(--gold-soft); }

/* Floating WhatsApp — a round button in WhatsApp's own green. The label is
   dropped rather than hidden: the circle already carries the meaning, and the
   accessible name lives in the link's aria-label. */
.float-cta {
    position: fixed; right: 1.25rem; bottom: 1.25rem; z-index: 80;
    display: inline-flex; align-items: center; justify-content: center;
    width: 3.5rem; height: 3.5rem;
    background: var(--wa-green); color: var(--white);
    border-radius: var(--radius-pill);
    box-shadow: 0 6px 22px rgba(37, 211, 102, .38);
    transition: all var(--dur) var(--ease);
}
.float-cta .icon { width: 1.7rem; height: 1.7rem; }
.float-cta:hover { background: var(--wa-green-deep); transform: translateY(-2px); box-shadow: 0 10px 30px rgba(37, 211, 102, .50); }

/* Dev banner */
.dev-banner {
    background: var(--gold-a16); border-bottom: 1px solid var(--line-gold);
    color: var(--navy); font-size: .78rem; padding: .6rem 1rem; text-align: center;
}
.dev-banner code { background: rgba(11, 36, 71, .07); padding: .1rem .4rem; border-radius: var(--radius-sm); color: var(--gold-ink); }

/* 15 ── Signature & utilities --------------------------------------------- */
.mt-0 { margin-top: 0; } .mb-0 { margin-bottom: 0; }
.mt-2 { margin-top: 1.4rem; } .mt-3 { margin-top: 2.4rem; } .mt-4 { margin-top: 3.5rem; }
.muted { color: var(--muted); }
.small { font-size: .84rem; }
.nowrap { white-space: nowrap; }
.hairline { height: 1px; background: var(--line-soft); border: 0; margin: 0; }
.hairline--spaced { margin: 1.25rem 0; }
/* Flash messages open the page, above the masthead. */
.flash-region { padding-top: 1.75rem; }

/* Available to screen readers only — for headings that structure a section
   without needing a visible title. */
.sr-only {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; border: 0;
    overflow: hidden; clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* The tape as a standalone element, for surfaces that need the seam but have
   no heading above it. */
.rule-tape { width: 64px; height: 4px; background: var(--tape); }
.rule-tape--full { width: 100%; }
.rule-tape--center { margin-inline: auto; }

/* Micro-label — use for any short uppercase label outside .eyebrow */
.label {
    font-family: var(--mono);
    font-size: var(--fs-label); font-weight: 500;
    letter-spacing: var(--track-label); text-transform: uppercase;
    color: var(--muted-2);
}
.label--gold { color: var(--gold-ink); }

/* Scroll reveal. Opt in with data-reveal; main.js adds .is-revealed as the
   element enters the viewport. The html.js flag is written by an inline
   script in the head, so nothing is hidden when JavaScript is unavailable. */
.js [data-reveal] {
    opacity: 0; transform: translateY(16px);
    transition: opacity var(--dur-slow) var(--ease-out), transform var(--dur-slow) var(--ease-out);
}
.js [data-reveal].is-revealed { opacity: 1; transform: none; }

/* Cards revealed together are staggered, so a grid arrives as a wave instead of
   as one slab. Six steps is as far as a row of cards ever runs. */
.js .grid > [data-reveal]:nth-child(2) { transition-delay: .09s; }
.js .grid > [data-reveal]:nth-child(3) { transition-delay: .18s; }
.js .grid > [data-reveal]:nth-child(4) { transition-delay: .27s; }
.js .grid > [data-reveal]:nth-child(5) { transition-delay: .36s; }
.js .grid > [data-reveal]:nth-child(6) { transition-delay: .45s; }

/* ── Motion ────────────────────────────────────────────────────────────────
   Two layers and nothing else.

   1. Feedback — hover and focus. Short, immediate, always a reaction to the
      pointer: the sweep across a button face, the ribbon drawing along a
      card's top edge, the light passing over a product plate, the icon
      stepping forward.
   2. Ambience — the three loops that never stop: the USP ticker, the shimmer
      travelling along the tape under each heading, and the breathing bloom
      behind a masthead. All decorative; none of them carries information.

   Scroll entrances use animation-timeline: view(). A browser without support
   ignores the whole block and renders every element at rest, so nothing is
   ever hidden behind an animation. Everything below animates a transform or
   an opacity only, and the reduced-motion block above switches the lot off. */
@keyframes card-sheen {
    from { opacity: 0; transform: translateX(0) skewX(-18deg); }
    35%  { opacity: 1; }
    to   { opacity: 0; transform: translateX(330%) skewX(-18deg); }
}
@keyframes tape-shimmer { to { background-position: 96px 0, 0 0; } }
@keyframes glow-breathe {
    0%, 100% { opacity: .72; transform: scale(1); }
    50%      { opacity: 1;   transform: scale(1.07); }
}
@keyframes cta-ring {
    0%   { transform: scale(1);   opacity: .5; }
    70%  { transform: scale(1.6); opacity: 0; }
    100% { transform: scale(1.6); opacity: 0; }
}
@keyframes rise-in {
    from { opacity: 0; transform: translateY(22px); }
    to   { opacity: 1; transform: none; }
}

/* The tape is the one ornament on this site, so it is also the one thing
   allowed to shimmer. It now appears once per page, on the masthead. */
.page-head h1::after {
    background-position: 0 0, 0 0;
    animation: tape-shimmer 7s linear infinite;
}

/* The masthead bloom breathes rather than pulses — a 9s cycle is long enough to
   read as light behind the title instead of a flashing element. */
.page-head::before { animation: glow-breathe 9s ease-in-out infinite; transform-origin: 50% 0; }

/* Floating WhatsApp button: a single ring leaves the button every 2.8s. It is
   the only thing on the site that moves on its own outside the ticker, and it
   marks the fastest route to a human. */
.float-cta::after {
    content: ""; position: absolute; inset: 0; border-radius: inherit;
    box-shadow: 0 0 0 2px var(--wa-a50);
    animation: cta-ring 2.8s var(--ease-out) infinite;
    pointer-events: none;
}

/* Entrances driven by scroll position. Kept on elements that do not already
   carry data-reveal, so the two systems never fight over the same transform. */
@supports (animation-timeline: view()) {
    .js .section-head:not([data-reveal]),
    .js .card:not([data-reveal]),
    .js .strip-cell:not([data-reveal]),
    .js .table-wrap:not([data-reveal]),
    .js .split > *:not([data-reveal]),
    .js .cert-card:not([data-reveal]),
    .js .steps li:not([data-reveal]),
    .js .faq-item:not([data-reveal]),
    .js .cert-grid > *:not([data-reveal]),
    .js .split > .grid > *:not([data-reveal]) {
        animation-name: rise-in;
        animation-duration: auto;            /* scroll-driven: no wall-clock length */
        /* backwards, never both: once the range has passed the element returns
           to its own declarations, so the card's hover lift still owns the
           transform. An animation would otherwise out-rank it forever. */
        animation-fill-mode: backwards;
        animation-timing-function: linear;
        animation-timeline: view();
        animation-range: entry 10% cover 32%;
    }
}

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    /* Overriding every transition is the one place !important earns its keep —
       it is the only way to honour the OS setting from a single rule. */
    *, *::before, *::after {
        transition-duration: .01ms !important;
        animation-duration: .01ms !important;
        animation-iteration-count: 1 !important;
    }
    .js [data-reveal] { opacity: 1; transform: none; }
}

/* E-commerce */
.qty { display: inline-flex; align-items: center; border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; }
.qty button { background: none; border: 0; color: var(--navy); padding: .7rem .95rem; cursor: pointer; font-size: 1rem; line-height: 1; }
.qty button:hover { color: var(--gold-deep); background: var(--gold-a08); }
.qty input { width: 3rem; text-align: center; border: 0; border-inline: 1px solid var(--line); border-radius: 0; padding: .7rem 0; }
.qty input:focus { background: transparent; }

.option-row { display: flex; flex-wrap: wrap; gap: .6rem; }
.option {
    border: 1px solid var(--line); border-radius: var(--radius); padding: .6rem 1rem;
    font-size: .82rem; cursor: pointer; color: var(--muted); background: none; font-family: var(--sans);
    transition: border-color .25s var(--ease), color .25s var(--ease);
}
.option:hover { border-color: var(--line-gold); color: var(--navy); }
.option[aria-pressed="true"] { border-color: var(--gold-deep); color: var(--gold-ink); background: var(--gold-a08); }
/* A chip the piece carries but cannot ship today. It stays in the row — hiding
   it shortens the run and leaves the customer with no answer — and it is closed:
   no hover, no pressed state, the note in words. */
.option--out { cursor: not-allowed; color: var(--muted-2); border-style: dashed; }
.option--out:hover { border-color: var(--line); color: var(--muted-2); }
.option-note {
    display: block; font-family: var(--mono); font-size: .55rem;
    letter-spacing: .1em; text-transform: uppercase;
}

/* 13 ── Certificates ------------------------------------------------------ */
/* Statutory documents are merchandised as products: the same card, the same
   plate, the same hover. The palette stays the storefront's — the document is
   the subject on the plate, and it is paper enough on its own. */

/* auto-fit with a ceiling. The columns still heal their own row count — four
   documents lay out as four, one as one — but a track stops growing, because a
   document is read at card size. Two documents stretched across the whole
   container gave each one a frame three times the width its page needed, and
   the page inside it sat in the middle of a band of empty navy. */
.cert-grid {
    display: grid; gap: 1.6rem;
    /* `min(272px, 100%)`, not a bare 272px: a track minimum is a floor, so on a
       screen narrower than the card the floor would push the grid past the
       container and scroll the page sideways. */
    grid-template-columns: repeat(auto-fit, minmax(min(272px, 100%), 356px));
    justify-content: center;
}

/* ── A certificate card is a product card whose subject is a document ───
   `.product-card` supplies the shell, the ribbon and the lift;
   `.product-card-visual` supplies the 4:3 rectangle, the studio ground, the
   watermark, the badge and the sheen. A document wants to add nothing to any of
   it, so the modifier below only takes things away. The substitution is the
   subject: where a product stands a photograph, a document stands page 1 of
   itself — cropped to the frame at rest, whole under the pointer. */

/* The woven twill and the inset hairline are both furniture for a photograph
   sitting inside the rectangle. A page bleeds to the frame's edges, so they
   step aside for it exactly as they do for a photograph. */
.product-card-visual--doc .visual::before,
.product-card-visual--doc .visual::after { display: none; }

/* The position in the register, set as data: № 01 beside № 02. It sits on a
   document now rather than on navy, so it carries its own ivory ground —
   whatever the page behind it happens to be, the numeral stays legible. */
.cert-card__index {
    position: absolute; top: 1rem; right: 1.05rem; z-index: 3;
    font-family: var(--mono);
    font-size: .58rem; font-weight: 500; letter-spacing: .18em;
    color: var(--navy);
    background: rgba(254, 253, 241, .92);
    border: 1px solid var(--line-gold-soft); border-radius: var(--radius-sm);
    padding: .28rem .5rem;
}

/* The whole frame is a way into the document, exactly as a product card's frame
   is a way into the piece. It is kept out of the tab order and out of the
   reading order, so a keyboard user gets one stop per card — the action in the
   body — rather than two. */
.cert-open {
    position: absolute; inset: 0; z-index: 4;
    border: 0; padding: 0; background: transparent; cursor: pointer;
}
/* Body: four things and no more — what it is, who issued it, one line of
   context, then the two things that can be done with it. The kind used to open
   a meta row of its own and the issuer a bordered foot of its own; both were a
   whole block to say one line. The kind is the badge on the frame now, and the
   issuer is a byline under the title, where an issuer belongs. */
.cert-card .product-card-body { gap: .55rem; }
.cert-card__issuer {
    margin: -.1rem 0 0;
    font-size: .78rem; line-height: 1.45; color: var(--muted);
}
.cert-card__summary {
    display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
    margin: 0; font-size: .84rem; line-height: 1.5; color: var(--muted-2);
}
/* The document's own line, in the mono the rest of the site's data is set in:
   the reference, the date and the file, each behind the eyebrow's hairline. */
/* The data line and the actions are one floor under the card: `margin-top: auto`
   pins them together to the bottom, so a summary that runs a line longer than
   its neighbour's does not float that card's reference, date and actions above
   the row's. Same reason the product card pins its price. */
.cert-card__meta {
    display: flex; align-items: center; gap: .55rem; flex-wrap: wrap;
    margin: auto 0 0; padding-top: .1rem;
    font-family: var(--mono); font-size: .6rem; letter-spacing: .12em;
    text-transform: uppercase;
}
.cert-card__meta > * + *::before {
    content: ""; display: inline-block; width: 12px; height: 1px;
    margin-right: .55rem; vertical-align: middle; background: var(--line-gold);
}
.cert-card__ref { font-weight: 500; color: var(--gold-ink); }
.cert-card__date { color: var(--navy); }
.cert-card__file { color: var(--muted-2); }

/* ── The sheet: page 1, twice ──────────────────────────────────────────
   One figure holds both views of the document. The bitmap is painted once by
   assets/js/pdf-docs.js and cut into the two canvases from that single render,
   so the reveal is a cross-fade between two views of the same pixels: no second
   fetch, no second render, and no scaling step that could distort the page.

   Both canvases take their size from CSS, because both are proportional to the
   frame: the crop canvas is the frame itself, and the whole-page canvas is the
   frame less the inset above — an inset pdf-docs.js reads back out of this
   declaration so it paints at exactly the size CSS will show. Keep it a plain
   percentage; the script parses it as one. */
.cert-sheet {
    --cert-inset: 9%;
    position: absolute; inset: 0; z-index: 1; margin: 0;
}

/* What stands in for the page: a ruled sheet carrying the brand. It fills the
   frame, because the frame is where the document would be — shown before a
   render, and kept whenever one cannot be made. */
.cert-sheet__plate {
    position: absolute; inset: 0; display: flex; flex-direction: column;
    align-items: center; justify-content: center; gap: .5rem;
    background:
        repeating-linear-gradient(180deg, transparent 0 9px, rgba(11, 36, 71, .06) 9px 10px),
        var(--paper);
    transition: opacity .42s var(--ease);
}
.cert-sheet[data-pdf-state="ready"] .cert-sheet__plate { opacity: 0; }
.cert-sheet__mark {
    font-family: var(--display); font-size: 2rem; font-weight: 700;
    letter-spacing: .06em; color: rgba(223, 166, 43, .42);
}
.cert-sheet__lines { display: grid; gap: .22rem; width: 46%; }
.cert-sheet__lines i { height: 1px; background: rgba(11, 36, 71, .18); }
.cert-sheet__lines i:nth-child(2) { width: 86%; }
.cert-sheet__lines i:nth-child(3) { width: 70%; }
.cert-sheet__label {
    font-family: var(--mono); font-size: .5rem; letter-spacing: .28em;
    text-transform: uppercase; color: rgba(11, 36, 71, .42);
}

/* The thumbnail: page 1 at the frame's own width, so the document bleeds to the
   frame's edges, with its foot dissolving into the ground. A page cut off
   mid-line reads as a mistake; a page fading out reads as one continuing past
   the frame — which is exactly what it is doing. This is the card's image
   wherever it is shown, pointer or finger. */
.cert-sheet__crop {
    position: absolute; inset: 0; overflow: hidden;
    opacity: 0;
    -webkit-mask-image: linear-gradient(180deg, #000 0 72%, rgba(0, 0, 0, 0) 100%);
            mask-image: linear-gradient(180deg, #000 0 72%, rgba(0, 0, 0, 0) 100%);
    transition: opacity .42s var(--ease), transform .6s var(--ease-out);
}
.cert-sheet__crop canvas { display: block; width: 100%; height: 100%; }

/* The document in full: the whole of page 1, standing on the ground with its
   own shadow under it. Sized by height and taking its width from the page's own
   proportion, so it reads as a sheet of paper at every frame width. */
.cert-sheet__full {
    position: absolute; inset: 0; display: grid; place-items: center;
    /* A definite row track, for the same reason the product frame sets one:
       without it the canvas's percentage height has no row to resolve against,
       so it falls back to the page's intrinsic height, the row grows to fit
       that, and the sheet ends up taller than the frame it was meant to stand
       inside — measured at 266.5px in a 264.5px frame before this line. */
    grid-template-rows: 100%;
    opacity: 0; transform: scale(1.03);
    transition: opacity .42s var(--ease), transform .55s var(--ease-out);
}
.cert-sheet__full canvas {
    display: block; width: auto; height: calc(100% - 2 * var(--cert-inset));
    /* A sheet on a near-white ground needs an edge of its own: the frame's
       ground is cream and the page is white, so without one the paper and the
       ground read as the same surface. The ring is a zero-blur shadow rather
       than a border, so the page keeps the exact height the script painted it
       at instead of being inset by two pixels of border-box. */
    box-shadow: 0 0 0 1px rgba(11, 36, 71, .13), 0 16px 26px -16px rgba(6, 26, 54, .45);
}

/* The crop arrives when the paint does. */
.cert-sheet[data-pdf-state="ready"] .cert-sheet__crop { opacity: 1; }

/* The reveal, and the one place the whole page is shown: a pointer that can rest
   on a card is a pointer that can open the document it carries. A finger gets
   the crop — which is the larger and more legible of the two views — and the
   action in the body, which opens every page of the file anyway.

   The hover rule is written after the ready rule on purpose: the two weigh the
   same, so source order is what decides which one wins while both apply. */
@media (hover: hover) {
    .cert-card:hover .cert-sheet__crop,
    .cert-card:focus-within .cert-sheet__crop { opacity: 0; transform: scale(.985); }
    .cert-card:hover .cert-sheet__full,
    .cert-card:focus-within .cert-sheet__full { opacity: 1; transform: none; }
}

/* The two things that can be done with a document, at the card's floor exactly
   where a product card keeps its one action — pinned to the bottom, so a row of
   cards lines up whatever their summaries do above. The actions live in the body
   rather than over the frame: the frame is busy showing the page, and an overlay
   drawn on top of a document is an overlay drawn over the one thing the card
   exists to show. A gold hairline rather than a fill, for the same reason the
   product card's action wears one — this ground is cream, and the fill on hover
   is navy, so the read is never lost. */
.cert-card__buttons { display: flex; gap: .5rem; padding-top: .4rem; }

/* A card can be linked to — the standards panel cites the test report, the
   register cites the GST certificate — so a card jumped to holds its place
   clear of the sticky masthead instead of under it. */
.cert-card { scroll-margin-top: 8rem; }

/* The closing route out of the registry band: one line of copy and two ways to
   take it. It is the band's last word, so it carries no trailing margin. */
.cert-ask { margin: var(--space-8) 0 0; }
.cert-card__buttons .btn { flex: 1; }
/* Icon-only, so it carries a name, and 44px keeps it thumb-sized beside the
   button. This ground is the card's cream, so the action takes the navy ink and
   the gold hairline the card is set in rather than the ivory it would take on
   navy. */
.cert-icon-action {
    flex: none; width: 46px; min-height: 44px; display: grid; place-items: center;
    color: var(--navy); background: transparent;
    border: 1px solid var(--line-gold); border-radius: var(--radius);
    transition: color var(--dur) var(--ease), border-color var(--dur) var(--ease),
                background var(--dur) var(--ease), transform var(--dur) var(--ease);
}
.cert-icon-action .icon { width: 17px; height: 17px; }
.cert-icon-action:hover {
    color: var(--on-navy); background: var(--navy); border-color: var(--navy);
    transform: translateY(-2px);
}

.cert-empty {
    border: 1px dashed var(--line-gold); border-radius: var(--radius-lg);
    padding: 3rem 2rem; text-align: center; color: var(--muted);
}

/* ── Document viewer modal ─────────────────────────────────────────────── */
.viewer {
    position: fixed; inset: 0; z-index: 200; display: none;
    align-items: center; justify-content: center; padding: clamp(.8rem, 3vw, 2rem);
}
.viewer[data-open="true"] { display: flex; }
.viewer__scrim {
    position: absolute; inset: 0; background: rgba(6, 26, 54, .93);
    backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
    opacity: 0; transition: opacity .3s var(--ease);
}
.viewer[data-open="true"] .viewer__scrim { opacity: 1; }

.viewer__panel {
    position: relative; z-index: 1; display: flex; flex-direction: column;
    width: min(1080px, 100%); height: min(92vh, 100%);
    background: var(--white); border: 1px solid var(--line-gold); border-radius: var(--radius-lg);
    overflow: hidden; box-shadow: 0 40px 90px -34px rgba(6, 26, 54, .55);
    animation: viewerIn .34s var(--ease) both;
}
@keyframes viewerIn { from { opacity: 0; transform: translateY(14px) scale(.985); } }
@media (prefers-reduced-motion: reduce) { .viewer__panel { animation: none; } }

.viewer__bar {
    display: flex; align-items: center; gap: 1rem; justify-content: space-between;
    padding: .85rem 1rem .85rem 1.35rem; border-bottom: 1px solid var(--line-soft); background: var(--panel);
}
.viewer__title { display: flex; flex-direction: column; min-width: 0; }
.viewer__title strong { font-size: .95rem; color: var(--navy); font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.viewer__title span { font-family: var(--mono); font-size: .64rem; letter-spacing: .12em; color: var(--muted-2); text-transform: uppercase; }
.viewer__actions { display: flex; align-items: center; gap: .5rem; flex: none; }

/* Page count and zoom — the controls PDF.js drives. They stay hidden until a
   document is actually in the panel, so the iframe fallback never shows a
   button that does nothing. */
.viewer__tools {
    display: flex; align-items: center; gap: .35rem; flex: none;
    padding-right: .5rem; border-right: 1px solid var(--line-soft);
}
/* The author rule above would otherwise beat the UA sheet's [hidden], leaving
   the controls on screen for the iframe fallback, where they do nothing. */
.viewer__tools[hidden] { display: none; }
.viewer__count {
    padding-right: .35rem; font-family: var(--mono); font-size: .6rem;
    letter-spacing: .16em; text-transform: uppercase; color: var(--muted-2);
}
.viewer__step {
    display: grid; place-items: center; width: 32px; height: 32px; cursor: pointer;
    background: none; border: 1px solid var(--line); border-radius: var(--radius-sm);
    color: var(--navy); font-size: .95rem; line-height: 1;
    transition: border-color var(--dur) var(--ease), color var(--dur) var(--ease), background var(--dur) var(--ease);
}
.viewer__step:hover { border-color: var(--line-gold); color: var(--gold-deep); background: var(--gold-a08); }
.viewer__zoom {
    min-width: 46px; text-align: center;
    font-family: var(--mono); font-size: .62rem; letter-spacing: .1em; color: var(--navy);
}

/* The page stack: every page of the document, in one scroll. */
.viewer__stage {
    flex: 1; display: grid; align-content: start; justify-items: center; gap: 1rem;
    overflow: auto; overscroll-behavior: contain;
    padding: clamp(.75rem, 2vw, 1.5rem); background: var(--navy-deep);
}
.viewer__pages { display: flex; flex-direction: column; align-items: center; gap: 1rem; }
.viewer__page {
    display: block; max-width: 100%; background: var(--white);
    box-shadow: 0 26px 55px -28px rgba(6, 26, 54, .8);
}
.viewer__status {
    margin: 0; font-family: var(--mono); font-size: .64rem;
    letter-spacing: .2em; text-transform: uppercase; color: rgba(254, 253, 241, .62);
}
.viewer__pages:not(:empty) ~ .viewer__status { display: none; }

/* One panel, two renderers — the stack PDF.js fills, or the browser's own
   viewer in the iframe. Only ever one of them is on screen. */
.viewer__stage, .viewer__frame { display: none; }
.viewer:not([data-cert-viewer-mode="frame"]) .viewer__stage { display: grid; }
.viewer[data-cert-viewer-mode="frame"] .viewer__frame { display: block; }

@media (max-width: 720px) {
    .viewer__bar { padding: .7rem .7rem .7rem 1rem; gap: .6rem; }
    .viewer__count { display: none; }
}
@media (max-width: 520px) {
    .viewer__actions [data-cert-viewer-newtab] { display: none; }
    .viewer__zoom { min-width: 38px; }
}

.viewer__close {
    display: grid; place-items: center; width: 36px; height: 36px; cursor: pointer;
    background: none; border: 1px solid var(--line); border-radius: var(--radius-sm); color: var(--navy);
}
.viewer__close:hover { border-color: var(--line-gold); color: var(--gold-deep); }
.viewer__frame { flex: 1; width: 100%; min-width: 0; border: 0; background: var(--navy-deep); }
.viewer__fallback { padding: 3rem 2rem; text-align: center; color: var(--muted); }
body[data-viewer-open="true"] { overflow: hidden; }

/* Product Detail Page — gallery + buy box.
   The gallery is two stacked rows: the stage, then the strip that drives it.
   Both are square, so the column is a fixed shape at every width and the
   buy box no longer stretches the frame into a portrait panel. */
.split--pdp { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: stretch; }

/* align-self: start keeps the gallery to its own height instead of the row's,
   which is what leaves it free to travel under a taller buy box. */
.pdp-gallery {
    display: flex; flex-direction: column; gap: 1rem;
    align-self: start; min-width: 0;
}

/* The frame is the source file's own ratio — one 500x500 export fills the
   stage edge to edge, uncropped, at every width. */
.pdp-stage {
    position: relative; flex: none; width: 100%; aspect-ratio: 1 / 1;
    border-radius: var(--radius-lg); overflow: hidden;
    border: 1px solid var(--line-soft);
}


/* Only stuck while the whole gallery fits the viewport — on a short one the
   strip would be pushed below the fold and never come back. */
@media (min-width: 901px) and (min-height: 880px) {
    .pdp-gallery { position: sticky; top: 100px; }
}

/* The cream mount the cards use, tinted with the piece's own accent, so the
   image sits on the ground the rest of the site composes against. */
.pdp-view {
    position: absolute; inset: 0; display: grid; place-items: center; overflow: hidden;
    background:
        radial-gradient(72% 64% at 50% 42%, var(--card-glow, rgba(223, 166, 43, .14)), transparent 70%),
        linear-gradient(160deg, var(--cream) 0%, var(--panel-2) 100%);
}
/* One file, several frames: the crop is driven by the custom properties the
   tile writes, so a view change is a transform, not a re-fetch — and a swap
   between stage and strip is those same three properties moving. A supplied
   frame simply resets them to scale 1 at 50% 50%. */
.pdp-image {
    width: 100%; height: 100%; object-fit: cover;
    transform: scale(var(--view-scale, 1));
    transform-origin: var(--view-x, 50%) var(--view-y, 50%);
    transition: transform var(--dur-slow) var(--ease);
}
.pdp-view .visual-monogram { font-size: clamp(3rem, 12vw, 7rem); color: var(--card-accent, var(--gold-deep)); }

/* The strip: four equal squares that share out the width the stage above them
   occupies, so the two rows read as one block. It holds every frame except the
   one on the stage, so the tiles carry no selected state — the frame you are
   looking at is simply absent from the row. */
.pdp-thumbs {
    display: grid; grid-template-columns: repeat(auto-fit, minmax(72px, 1fr));
    justify-content: start; gap: .75rem; margin: 0; padding: 0;
}
.pdp-thumb {
    position: relative; width: 100%; min-width: 0; padding: 0; cursor: pointer; overflow: hidden;
    aspect-ratio: 1 / 1; border: 1px solid var(--line); border-radius: var(--radius);
    background: linear-gradient(160deg, var(--cream), var(--panel-2));
    transition: border-color var(--dur) var(--ease), box-shadow var(--dur) var(--ease);
}
.pdp-thumb img {
    width: 100%; height: 100%; object-fit: cover;
    transform: scale(var(--view-scale, 1));
    transform-origin: var(--view-x, 50%) var(--view-y, 50%);
    /* A shade back from full strength, so the stage keeps the emphasis; hover
       is the promise that the frame is about to travel up there. */
    opacity: .88; transition: opacity var(--dur) var(--ease);
}
.pdp-thumb:hover img, .pdp-thumb:focus-visible img { opacity: 1; }
.pdp-thumb:hover { border-color: var(--line-gold); }
.pdp-thumb:focus-visible { outline: 2px solid var(--gold-deep); outline-offset: 2px; }
.product-gallery-badge {
    position: absolute; top: 1rem; left: 1rem; z-index: 3;
    background: var(--gold); color: var(--navy);
    font-size: .6rem; font-weight: 700; letter-spacing: .18em; text-transform: uppercase;
    padding: .4rem .75rem; border-radius: var(--radius-sm);
}
.product-gallery-save {
    position: absolute; top: 1rem; right: 1rem; z-index: 3;
    background: rgba(127, 191, 138, .9); color: var(--navy);
    font-size: .58rem; font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
    padding: .35rem .65rem; border-radius: var(--radius-sm);
}

/* ── The buy box ────────────────────────────────────────────────────────
   One column, read top to bottom as a specification card: who the piece is,
   what it costs, what it is, then the sheet a buyer checks before choosing a
   size. Each block is closed by a hairline, so the column scans in one pass
   instead of arriving as an even stack of blocks with equal weight. */
.pdp-info { display: flex; flex-direction: column; min-width: 0; }

/* The buy box opens on the collection label, and its cap line is set against
   the top of the gallery's stage rather than against the column's own top edge:
   the two columns start on the same rule that way, instead of the label sitting
   a dozen pixels down a white column while the frame begins at the masthead
   line. Fractions of a line are what this nudge corrects, so it is written in
   rem and never grows. */
.pdp-head { padding-bottom: 1.35rem; border-bottom: 1px solid var(--line); }
/* .eyebrow is an inline-flex box, and a vertical margin on one of those is
   measured against the line box rather than against the column — so here it is
   given block behaviour for the nudge to land. */
.pdp-head .eyebrow { display: flex; margin-top: -.25rem; }

.product-detail-title {
    font-family: var(--display); font-size: clamp(1.75rem, 3.4vw, 2.45rem);
    font-weight: 700; line-height: 1.08; margin-bottom: .5rem;
}
.product-detail-tagline { font-size: .95rem; color: var(--muted); margin-bottom: 0; max-width: 38ch; }

/* The piece in one paragraph. It is the only prose in the column, so it is set
   as the lead — a shade larger than body copy, with room to breathe — and it
   runs the full measure of the column, so the paragraph, the rules and the spec
   sheet all share one edge. */
.product-detail-desc {
    padding: 1.5rem 0 0;
    font-size: 1rem; line-height: 1.85; color: var(--muted);
}

/* ── The facts ─────────────────────────────────────────────────
   What the piece is and what it comes in — one block, five rows, and all of it
   read off the catalogue: the pack it arrives in, the fabric it is cut from,
   the waistband that opens it, the colours it is dyed in, then the sizes, each
   with the waist it covers. A row whose value is empty is not printed at all.

   The row is the unit and each fact keeps its own. The name stands in a column
   of its own at the left and the value answers beside it: a name over its own
   value is a field, a name beside it is a specification, and the block reads as
   a sheet the buyer scans down instead of a form they fill in. One hairline
   closes each row, at the soft weight, because five rows at the full weight
   would rule the column into a table.

   This is the third pass at the block and the first that keeps it whole. The
   first boxed it in a panel with a ground and a border of its own, which read
   as a second surface wedged inside the buy box; the second split it in two,
   the specification over the options, printing the names over their values in
   four groups — which read as a form, every fact arriving at the same weight.
   One list here, weighted inside it: the pack leads the block, the gauge keeps
   the full width, and nothing else is ranked. No size is marked as the popular
   one and no colour is pushed, because the box states what is available and the
   choice is made on order.php — the gauge gives every size the same cell, the
   same type and the same ground, which is the shape of the chart below it. */
.pdp-spec {
    display: flex; flex-direction: column;
    /* One rule opens the block, at the head's own weight — the same edge
       .pdp-head closes on. The rows close themselves below it at the soft
       weight, so the block reads as one sheet with a top and no bottom. */
    margin: 1.4rem 0 1.7rem; padding-top: 1.4rem;
    border-top: 1px solid var(--line);
}

/* One fact per row. The name column is a fixed six and eight tenths rem and
   never wraps — the longest name in the block is the waistband's — so every
   value starts on the same edge instead of stepping in and out with the length
   of the name over it. */
.pdp-spec-row {
    display: grid; grid-template-columns: 6.8rem minmax(0, 1fr);
    align-items: baseline; gap: .3rem 1.1rem;
    padding: .95rem 0; border-bottom: 1px solid var(--line-soft);
}
/* The last row closes the block itself: no rule under it, and no padding either,
   so the actions below sit on the rule above them rather than on space. */
.pdp-spec-row:last-child { padding-bottom: 0; border-bottom: 0; }

/* The name — mono micro, the label role the size table's column heads and every
   eyebrow on the site already use, and the only gold in the block. */
.pdp-spec-label {
    display: flex; align-items: baseline; gap: .5rem; white-space: nowrap;
    font-family: var(--mono); font-size: .6rem; font-weight: 500;
    letter-spacing: .16em; text-transform: uppercase; color: var(--gold-ink);
}
.pdp-spec-unit { color: var(--muted-2); letter-spacing: .08em; }

/* The value: set in the display face the piece's own name is set in, not in the
   sans a field would take. A trunk's fabric and its waistband are the two things
   the piece is sold on, and in body type at 1rem they arrived looking like form
   fields; in the display face they read as facts about a garment. The size is
   clamped so the two long values — the fabric, the waistband — hold one line on
   a desktop column and still fit a phone. */
.pdp-spec-value {
    margin: 0;
    font-family: var(--display); font-weight: 600; line-height: 1.28;
    font-size: clamp(1.08rem, 1.6vw, 1.3rem);
    color: var(--navy);
}

/* Colours are tags, not chips. The choice is made on order.php, so nothing here
   is buttoned: a cream ground and a soft hairline in place of the white panel
   and full hairline a control would take, no hover state of its own, and the
   cursor left alone — a row of values, not a row of buttons. */
.pdp-spec-value--tags {
    display: flex; flex-wrap: wrap; align-items: center; gap: .4rem;
    /* One line of the value type, so a row of tags measures the same height as
       a row whose value is a line of text and the block's rules stay even. */
    min-height: 1.52rem;
}
.pdp-tag {
    display: inline-flex; align-items: center; gap: .45rem;
    font-family: var(--sans); font-size: .82rem; color: var(--navy);
    background: var(--cream); border: 1px solid var(--line-soft);
    border-radius: var(--radius-sm); padding: .3rem .6rem .3rem .42rem;
}
.pdp-tag .option-swatch { margin-right: 0; }

/* The sizes row takes the whole width: the name sits on a line of its own and
   the gauge runs under it, because five cells need the measure the value column
   does not have — and a size is not a value read beside a name but the one fact
   here a buyer measures themselves against. */
.pdp-spec-row--gauge {
    grid-template-columns: minmax(0, 1fr); gap: .8rem; align-items: start;
}
.pdp-spec-gauge { margin: 0; }

/* ── The size gauge ───────────────────────────────────────────────────────
   The one loud thing in the buy box, and the closest the page gets to the
   range's own language: the sizes laid out the way the chart grades them, one
   cell each on shared hairlines, with the waist that size covers printed under
   the letter. It answers "which sizes, and will one fit me" in a single look,
   and it admits no ranking — every size gets the same cell, type and ground.
   The page writes the column count from the sizes the piece actually carries.
   It is also the only bordered element left in the column: a gauge is a row of
   cells, so the hairline between them is the device rather than decoration. */
.size-gauge {
    list-style: none; margin: 0; padding: 0;
    display: grid; grid-template-columns: repeat(var(--gauge-sizes, 5), minmax(0, 1fr));
    gap: 1px; background: var(--line);
    border: 1px solid var(--line); border-radius: var(--radius-sm); overflow: hidden;
}
.size-gauge li {
    display: flex; flex-direction: column; align-items: center; gap: .25rem;
    /* The cream ground the site's inset surfaces use, so the strip reads as one
       object against the white column rather than as five floating letters. */
    background: var(--cream); padding: .85rem .25rem .8rem;
}
/* The letter takes the display face the size table gives its size column, so a
   size looks the same in the box as it does in the chart below. */
.size-gauge .gauge-size {
    font-family: var(--display); font-size: 1.32rem; font-weight: 600;
    line-height: 1; color: var(--navy);
}
.size-gauge .gauge-measure {
    font-family: var(--mono); font-size: .62rem; color: var(--muted-2);
    /* A range never breaks across two lines: the cell is the unit. */
    white-space: nowrap;
}
/* A size the run carries but the shelf does not. It keeps its cell — the gauge
   is what the piece carries, closed or not — and drops its voice: the letter
   gives up the navy, the range is struck through, and the note says it in
   words, because a state carried by colour alone is not a state. */
.size-gauge li.is-sold-out .gauge-size { color: var(--muted-2); }
.size-gauge li.is-sold-out .gauge-measure { text-decoration: line-through; }
.size-gauge .gauge-note {
    font-family: var(--mono); font-size: .55rem; letter-spacing: .12em;
    text-transform: uppercase; color: var(--muted-2);
    /* The note is the one label in the block with no room to fold: it holds one
       line at every width, and the phone sizes it down rather than letting it
       break, because a wrapped label reads as two states. */
    white-space: nowrap;
}

/* The swatch, where a colour has to carry itself: a disc large enough to read
   as the colour rather than as a bullet beside a name. */
.option-swatch {
    display: inline-block; width: .95rem; height: .95rem; border-radius: 50%;
    border: 1px solid var(--line); margin-right: .45rem; vertical-align: middle;
}

/* Actions */
.product-detail-actions { display: flex; flex-wrap: wrap; gap: .75rem; }
.product-detail-actions .btn { flex: 1 1 45%; justify-content: center; }

/* Trust — a quiet line of guarantees under a hairline, not a row of chips.
   Nothing here is for sale, so nothing here competes with the actions. It is
   pinned to the foot of the column by design. */
.product-detail-trust {
    display: flex; flex-wrap: wrap; gap: .55rem 1.15rem;
    margin-top: auto; padding-top: 1.25rem; border-top: 1px solid var(--line-soft);
}
.product-detail-trust span {
    display: inline-flex; align-items: center; gap: .4rem;
    font-size: .68rem; letter-spacing: .06em; color: var(--muted-2);
}
.product-detail-trust .icon { width: .85rem; height: .85rem; color: var(--gold-deep); }

/* Product detail responsive */
@media (max-width: 900px) {
    /* One column: the buy box drops below the gallery and simply follows it. */
    .split--pdp { grid-template-columns: 1fr; }

    /* Full width, the buy box reads better spread out than as a narrow column —
       the actions sit on one line and the spec labels get a wider column. */
    .product-detail-actions .btn { flex: 1 1 auto; }
}
@media (max-width: 575px) {
    .product-detail-actions .btn { flex: 1 1 100%; width: 100%; }
    .pdp-head { padding-bottom: 1.15rem; }
    /* The gauge holds one row on the smallest phone — five cells of about 55px
       still carry a letter and its waist — so the type steps down rather than
       the row folding. The note over a closed size steps down with it and keeps
       one line: at 320px the cell has about fifty pixels in it and "SOLD OUT"
       measures forty-two at this step, so it fits rather than clips. */
    .size-gauge li { padding: .6rem .15rem; }
    .size-gauge .gauge-size { font-size: 1rem; }
    .size-gauge .gauge-measure { font-size: .55rem; }
    .size-gauge .gauge-note { font-size: .5rem; letter-spacing: .06em; }
    /* The name column narrows with the column rather than folding above its
       value: at five and a half rem the longest name — the waistband — still
       holds one line, so a phone reads the block the way a desktop does, down
       the same edge. The rows close up to keep five of them in one glance. */
    .pdp-spec-row {
        grid-template-columns: 5.5rem minmax(0, 1fr);
        gap: .25rem .8rem; padding: .8rem 0;
    }
    .pdp-spec-label { font-size: .55rem; letter-spacing: .12em; gap: .35rem; }
    .pdp-spec-row--gauge { grid-template-columns: minmax(0, 1fr); gap: .6rem; }
    /* Four frames still hold one row on the smallest phone — the tiles just
       get smaller (they land around 63px at 320px wide). */
    .pdp-thumbs { grid-template-columns: repeat(auto-fit, minmax(60px, 1fr)); gap: .5rem; }
}

/* 16 ── Contact ------------------------------------------------------------
   The contact page is a router, not a brochure: the way in, then the questions.
   Every label here reuses the mono micro-label role.
   ------------------------------------------------------------------------- */

/* Two panels, one object each: the routes and the form. They are a pair rather
   than a column so a phone number can be taken without first reading five
   labels, and both panels run the same head → body → foot rhythm, so the pair
   aligns at every divider instead of merely sitting side by side. */
/* Stretch, not start: the two panels are one spread, so they square off at the
   bottom and — with the foot's `margin-top: auto` — both feet then sit on the
   same line at the foot of the grid. The taller of the two costs the shorter
   one only the handful of pixels it was out by. */
.split--contact {
    grid-template-columns: minmax(0, .92fr) minmax(0, 1.08fr);
    align-items: stretch;
}
@media (max-width: 980px) { .split--contact { grid-template-columns: 1fr; } }

/* The certified page's pair: two registers of the same build, in equal columns
   because neither is a commentary on the other. The claim rows and the spec
   table inside them share one right rail, so the two panels' figures line up
   across the spread. */
.split--cert {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    align-items: stretch;
}
@media (max-width: 980px) { .split--cert { grid-template-columns: 1fr; } }

/* The surface. One definition, used by every panel on the site — this is what
   makes a pair read as a spread rather than two unrelated cards. It is a flex
   column so a panel's own body can grow and its .panel-foot can be pinned to
   the panel's floor. */
.panel {
    display: flex; flex-direction: column;
    background: var(--panel);
    border: 1px solid var(--line-gold);
    border-radius: var(--radius-lg);
    padding: clamp(1.5rem, 2.6vw, 2.4rem);
}

/* The head both panels open with: label, title, one supporting line, then the
   hairline that separates the promise from the thing that keeps it. */
.panel-head { padding-bottom: 1.4rem; margin-bottom: 1.9rem; border-bottom: 1px solid var(--line-soft); }
.panel-head h3 { margin-bottom: 0; }
/* The supporting line is allowed exactly two lines in both panels, so the
   hairline under it lands on the same row in each and the two bodies start
   together — the alignment is built in rather than left to whichever panel
   happens to hold the longer sentence. Both leads are written to two lines at
   the panel widths this grid uses; three lines would push one head down. */
.panel-lead {
    margin: var(--space-4) 0 0;
    font-size: .92rem;
    line-height: 1.65;
    min-height: calc(2 * 1.65 * .92rem);
}

/* The foot both panels close with: exactly one action, and at most one quiet
   aside beside it. `margin-top: auto` keeps the two feet level whenever the
   panels end up the same height. */
.panel-foot {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: 1.25rem 1.75rem;
    margin-top: auto; padding-top: 1.6rem;
    border-top: 1px solid var(--line-soft);
}
.panel-foot .btn { flex: 1 1 auto; }
.panel-foot p { flex: 1 1 13rem; margin: 0; }

/* A claim, as a row: the property on the left rail, the figure it was tested to
   on the right, and — under both, because a test needs its method more than it
   needs a column — the note that belongs to the test rather than to the number.
   One hairline per claim, the same language the table beside it speaks. */
/* `flex: 1` and the distribution are the safety net: the rows are given the
   pitch of the specification table's rows so the two bodies end together, and
   if a change ever leaves slack, it is spread between the four claims instead
   of pooling in one hole above the foot. */
.claim-list {
    list-style: none; margin: 0; padding: 0;
    flex: 1 1 auto; display: grid; align-content: space-between;
}
.claim {
    display: grid; grid-template-columns: minmax(0, 1fr) auto;
    align-items: baseline; gap: .3rem 1rem;
    padding: 1.22rem 0; border-top: 1px solid var(--line-soft);
}
/* No closing rule on the last row: the panel's own foot is a hairline, and two
   of them 14px apart read as a doubled line. The foot closes the list. */
.claim:last-child { border-bottom: 0; }
.claim-term { font-size: .95rem; color: var(--navy); }
.claim-value {
    font-family: var(--display); font-size: 1.1rem; line-height: 1.2;
    color: var(--gold-deep); white-space: nowrap;
}
.claim-note { grid-column: 1 / -1; font-size: .82rem; line-height: 1.5; color: var(--muted-2); }

/* The routes read as lines rather than a grid of cells: the panel is narrow and
   its own border already does the framing, so the rows need no rules of their
   own — the rhythm and the mono labels carry the grouping. */
.contact-routes { gap: 1.15rem; margin: 0 0 1.6rem; }
.contact-routes .icon { margin-top: .3rem; }
.contact-routes .route-foot {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: .3rem 1rem;
    margin: .55rem 0 0;
    font-size: .8rem; color: var(--muted-2);
}
.contact-routes .route-map {
    display: inline-flex; align-items: center; gap: .4rem;
    font-weight: 500; color: var(--gold-ink);
}
.contact-routes .route-map .icon { width: .85rem; height: .85rem; margin: 0; transition: transform var(--dur) var(--ease); }
.contact-routes .route-map:hover .icon { transform: translateX(3px); }



/* The enquiry form's controls are the site's boxed fields, read from the one
   place every other form reads them. The border carries the shape of the field,
   which is what a white panel needs — a hairline underline left the fields
   floating in it — and the hover, focus and invalid states arrive with it, so
   the form has no field styling of its own to keep in step.
   Labels stay visible above every control (never placeholders-as-labels), each
   control clears the 46px touch target on the shared padding, and errors sit
   under their own field. */
.contact-form .alert { margin-bottom: 1.6rem; }

/* The form is the panel's growing child, and it is a column of its own — which
   is what lets its .panel-foot reach the bottom of the panel with the same
   `margin-top: auto` the routes panel uses. Inside a plain block the auto
   margin resolves to zero and the two feet end up a few pixels apart, so this
   has to stay a flex column if the feet are to stay level. */
.contact-form .enquiry-form { display: flex; flex-direction: column; flex: 1 1 auto; }

.enquiry-form .form-fields { display: grid; gap: 1.35rem; margin-bottom: 1.6rem; }
/* Two fields to a row from 640px up, message across both. Five single-field
   rows is a form that reads as longer than it is, and the rows saved here are
   rows nobody has to scroll past. Below 640px the pair stacks, which is the
   width where a half-width field stops being comfortably tappable. */
@media (min-width: 640px) {
    .enquiry-form .form-fields { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); column-gap: 1.5rem; }
}

/* Two things the shared field rules do not carry: the message box is taller,
   because it is the one field a buyer writes a paragraph in — on both enquiry
   surfaces, the contact page's panel and the homepage's plate — and the option
   list is given the panel's own ground so it does not open as a grey OS list. */
.contact-form textarea,
.enquiry-plate textarea { min-height: 150px; }
.contact-form select option { background: var(--panel); color: var(--navy); }

/* The enquiry controls are the shared boxed fields at the compact control
   height both enquiry surfaces were built around — the contact page's panel and
   the homepage's plate. The border carries the shape and the states are the
   shared ones, so what is set here is only the padding: at the shared
   .85rem/1rem the controls come out 54px tall and push both surfaces a size
   larger than their own rhythm. */
.contact-form input[type="text"],
.contact-form input[type="email"],
.contact-form input[type="tel"],
.contact-form select,
.contact-form textarea,
.enquiry-plate input[type="text"],
.enquiry-plate input[type="email"],
.enquiry-plate input[type="tel"],
.enquiry-plate select,
.enquiry-plate textarea { padding: .68rem .9rem; }

.contact-form .field label { transition: color .2s var(--ease); }
.contact-form .field:focus-within label { color: var(--gold-ink); }
.contact-form .field .hint { font-size: .76rem; }

/* The homepage's enquiry — the same contract in a different setting.
   The contact page sets its enquiry as two cream panels: the direct lines, and
   the form beside them. The homepage closes on one card instead — the routes on
   the brand's own navy, the fields on a white plate lifted onto it — so
   following the handoff to the contact page is not a repeat of it. Only the
   ground is new here: the labels, the fields, the error slots and the one gold
   action are the site's own, and the plates exist so the field system's light
   ground colours never have to be re-stated for the navy behind the card. */
#enquiry { scroll-margin-top: 8rem; }

.enquiry-band {
    display: grid; grid-template-columns: minmax(0, 1fr);
    gap: clamp(1.5rem, 3.5vw, 3rem);
    padding: clamp(1.4rem, 3.5vw, 2.75rem);
    background: var(--navy);
    border: 1px solid var(--line-gold);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
}
/* The routes take the narrower column: the card is read left to right, and the
   form is the thing being asked of the reader. */
@media (min-width: 981px) {
    .enquiry-band { grid-template-columns: minmax(0, .8fr) minmax(0, 1.2fr); align-items: start; }
}

/* One label style for both columns; only its ink changes with the ground. */
.enquiry-label {
    display: block;
    font-family: var(--mono); font-size: var(--fs-label); font-weight: 500;
    letter-spacing: var(--track-label); text-transform: uppercase;
}
.enquiry-routes-col .enquiry-label { color: rgba(254, 253, 241, .55); }
.enquiry-plate .enquiry-label { color: var(--muted-2); margin-bottom: 1.35rem; }

/* Routes as ruled lines on the ink — the same language the footer lists in. */
.enquiry-routes { list-style: none; margin: 1.15rem 0 0; padding: 0; }
.enquiry-routes li {
    display: grid; grid-template-columns: 1.1rem minmax(0, 1fr);
    gap: .85rem; padding: 1rem 0;
    border-top: 1px solid rgba(254, 253, 241, .16);
}
.enquiry-routes li:last-child { border-bottom: 1px solid rgba(254, 253, 241, .16); }
.enquiry-routes .icon { width: 1.05rem; height: 1.05rem; margin-top: .2rem; color: var(--gold-soft); }
.enquiry-routes div { display: grid; gap: .2rem; }
.enquiry-routes span {
    font-family: var(--mono); font-size: .6rem; font-weight: 500;
    letter-spacing: .18em; text-transform: uppercase; color: rgba(254, 253, 241, .5);
}
.enquiry-routes a, .enquiry-routes strong {
    font-size: .95rem; font-weight: 500; color: var(--on-navy);
    overflow-wrap: anywhere;
    transition: color var(--dur) var(--ease);
}
.enquiry-routes a:hover { color: var(--gold-soft); }

/* The pin closes the routes column — the routes say how to reach a person, the
   map says where they are — and it is the same framed embed the contact page
   draws, at the height a column this wide can hold. */
.enquiry-map {
    position: relative; overflow: hidden;
    margin-top: clamp(1.25rem, 2.4vw, 1.75rem);
    height: clamp(190px, 22vw, 280px);
    border: 1px solid var(--line-gold-soft);
    border-radius: var(--radius);
    background: var(--panel-2);
}
.enquiry-map iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.enquiry-map-cap {
    display: flex; flex-wrap: wrap; align-items: baseline;
    gap: .35rem 1.25rem; margin: .85rem 0 0;
    font-size: .78rem; color: rgba(254, 253, 241, .6);
}
.enquiry-map-cap a {
    display: inline-flex; align-items: center; gap: .4rem;
    font-weight: 500; color: var(--gold-soft);
}
.enquiry-map-cap .icon { width: .85rem; height: .85rem; transition: transform var(--dur) var(--ease); }
.enquiry-map-cap a:hover .icon { transform: translateX(3px); }

/* The plate the fields sit on: white, so the field system's label, hint and
   error colours need no second set for the navy behind the card. */
.enquiry-plate {
    background: var(--panel);
    border-radius: var(--radius);
    padding: clamp(1.25rem, 3vw, 2.25rem);
    box-shadow: var(--shadow-md);
}
.enquiry-plate .alert { margin-bottom: 1.35rem; }
.enquiry-actions { display: flex; flex-wrap: wrap; align-items: center; gap: .85rem 1.25rem; }
.enquiry-note { margin: 0; font-size: .8rem; color: var(--muted-2); }
.enquiry-note--ink { margin: .7rem 0 0; color: rgba(254, 253, 241, .6); }
/* A full-width action on a phone: the plate is narrower than the button's box
   plus its note, and a two-line footer under a half-width button reads broken. */
@media (max-width: 560px) {
    .enquiry-actions .btn { width: 100%; }
}

/* FAQ — native details/summary, so it opens with no JavaScript at all */
.faq-list { max-width: 860px; margin-inline: auto; border-top: 1px solid var(--line-soft); }
.faq-item { border-bottom: 1px solid var(--line-soft); }
.faq-item summary {
    display: flex; align-items: flex-start; justify-content: space-between; gap: var(--space-5);
    padding: 1.1rem 0; cursor: pointer; list-style: none;
    font-family: var(--display); font-size: 1.06rem; line-height: 1.35; color: var(--navy);
    transition: color var(--dur) var(--ease);
}
.faq-item summary::-webkit-details-marker { display: none; }
.faq-item summary:hover,
.faq-item[open] summary { color: var(--gold-ink); }
.faq-marker { position: relative; flex: none; width: 14px; height: 14px; margin-top: .42rem; }
.faq-marker::before,
.faq-marker::after {
    content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 1px; background: currentColor;
}
.faq-marker::after { transform: rotate(90deg); transition: transform var(--dur) var(--ease); }
.faq-item[open] .faq-marker::after { transform: rotate(0deg); }
.faq-answer { padding-bottom: 1.25rem; max-width: 70ch; }
.faq-answer p { font-size: .93rem; margin: 0; }

/* Map — the office marked directly, in a hairline frame. The embed is
   lazy-loaded and given a fixed height, so it cannot shift the page while it
   loads. Google's embed is light by design and reads as a framed document
   against the ink, the same way the certified page sets its paper cards. */
.map-embed {
    position: relative; overflow: hidden;
    height: clamp(320px, 42vw, 520px);
    border: 1px solid var(--line-gold-soft); border-radius: var(--radius-lg);
    background: var(--panel-2);
}
.map-embed iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.map-caption {
    display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between;
    gap: .75rem 1.5rem; margin: 1.1rem 0 0; padding-top: 1rem;
    border-top: 1px solid var(--line-soft); font-size: .85rem; color: var(--muted);
}
.map-caption a {
    display: inline-flex; align-items: center; gap: .45rem;
    font-family: var(--mono); font-size: .62rem; font-weight: 500;
    letter-spacing: .16em; text-transform: uppercase; color: var(--gold-ink);
}
.map-caption a .icon { width: .85rem; height: .85rem; transition: transform var(--dur) var(--ease); }
.map-caption a:hover { color: var(--gold-deep); }
.map-caption a:hover .icon { transform: translateX(3px); }

/* 17 ── About (Our Craft) -------------------------------------------------
   Two devices carry this page: the opening brief, and the sheet of ten points.
   Both read in navy ink on the house ground and introduce no colour or spacing
   step the system does not already have. The banner above them is the standard
   one, shared with every other page. */

/* The brief runs the full width of the plate as two columns of different jobs:
   the frame on the left, the argument on the right. Below 980px the two stack
   in source order — frame first, then copy. */
.brief { display: grid; gap: clamp(1.75rem, 4.5vw, 4rem); grid-template-columns: 1fr; }
/* The frame is the hairline mount the product plates use, so a photograph reads
   as filed artwork rather than as decoration. 4:5 suits a garment shot on a
   desk; a phone gets the wider 3:2 crop, where a tall frame would push the copy
   past the fold. */
.brief-media {
    margin: 0; aspect-ratio: 4 / 5; overflow: hidden;
    background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius-lg);
}
.brief-media img { width: 100%; height: 100%; object-fit: cover; }
.brief-body { max-width: 68ch; }
.brief-body h2 { margin-bottom: var(--space-5); }
@media (min-width: 981px) {
    .brief { grid-template-columns: minmax(0, .9fr) minmax(0, 1.1fr); align-items: start; }
}
/* Nothing filed in assets/images/about/ yet: the copy takes the whole plate
   instead of sitting beside a hole. */
.brief--no-media { grid-template-columns: 1fr; }
@media (max-width: 980px) {
    .brief-media { aspect-ratio: 3 / 2; }
}

/* The story variant. The frame column is cut to the board's own size rather
   than split by proportion, so the board sits in the middle of its column with
   an even margin either side instead of hanging off the left edge. The band
   then reads as a caption to the board rather than as two equal halves, and
   both columns open on the same line — the board's top edge and the eyebrow sit
   level, which is what the eye checks for first in a two-column band. */
@media (min-width: 981px) {
    .brief--story { grid-template-columns: minmax(0, 360px) minmax(0, 1fr); align-items: start; }
}
/* The board is a portrait poster at exactly 13:19, and the frame holds that
   ratio at every width — including the phone, where the shared 3:2 crop would
   cut it to a letterbox strip. Nothing of the board is ever cropped.

   The frame is centred in its column rather than hung off its left edge: it is
   narrower than the column it sits in, and centring it spends that spare width
   evenly, so the board reads as a plate placed on the page instead of one that
   drifted left and left a gap behind it. */
.brief--story .brief-media {
    aspect-ratio: 13 / 19; max-width: 320px; justify-self: center;
}
/* With no board filed, the band is the copy alone — including under the story
   variant's own column rule above. */
.brief--story.brief--no-media { grid-template-columns: 1fr; }

/* The four proofs that close the story column — a mark and a phrase each, one
   line apiece, so the band can be skimmed as well as read. A gold hairline
   opens the set, as on the record rail further down, and the two columns fold
   to one when the copy column is too narrow to hold them. */
.story-notes {
    list-style: none; margin: var(--space-5) 0 0; padding: var(--space-5) 0 0;
    display: grid; gap: .85rem clamp(1.25rem, 3vw, 2rem);
    grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
    border-top: 1px solid var(--line-gold-soft);
}
.story-notes li { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: .7rem; align-items: center; }
.story-notes-icon { color: var(--gold-deep); display: flex; }
.story-notes-icon .icon { width: 1.05rem; height: 1.05rem; }
.story-notes strong { color: var(--navy); font-size: .93rem; font-weight: 600; line-height: 1.3; }

/* The history plate — the wide frame that names the markets the standard was
   proved in, with its caption read as a filed record. It sits at 16:9 and never
   crops, so a plate composed edge to edge keeps its edges at every width; the
   hairline mount is the one the brief frame and the product plates use. */
.history-plate { margin: clamp(2.5rem, 5vw, 3.5rem) 0 0; }
.history-plate img {
    display: block; width: 100%; height: auto; aspect-ratio: 16 / 9; object-fit: cover;
    background: var(--panel-2); border: 1px solid var(--line); border-radius: var(--radius-lg);
}
.history-plate figcaption {
    margin-top: var(--space-4);
    font-family: var(--mono); font-size: var(--fs-label); font-weight: 500;
    letter-spacing: var(--track-label); text-transform: uppercase; color: var(--muted-2);
}

/* The history, laid as a rail: one dated entry per column, a short tick of gold
   sitting on the rule above each. The sheet below already spends the ruled row,
   and this page carries both — so the record is set across rather than down, and
   the years, the only numbers this band owns, are the only thing in mono. */
.milestones {
    list-style: none; margin: clamp(2.25rem, 4.5vw, 3.25rem) 0 0; padding: 0;
    display: grid; gap: 1.6rem clamp(1.25rem, 3vw, 2.25rem);
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    border-top: 1px solid var(--line-gold-soft);
}
.milestones li { position: relative; padding-top: 1.6rem; }
.milestones li::before {
    content: ""; position: absolute; inset: -1px auto auto 0;
    width: 32px; height: 2px; background: var(--gold);
}
.milestones-year {
    display: block; margin-bottom: .55rem;
    font-family: var(--mono); font-size: var(--fs-label); font-weight: 500;
    letter-spacing: var(--track-label); color: var(--gold-ink);
}
.milestones strong { display: block; margin-bottom: .35rem; color: var(--navy); font-size: .97rem; line-height: 1.35; }
.milestones p { margin: 0; font-size: .88rem; color: var(--muted-2); }

/* The trade CTA — the page's one ask, and the last thing before the footer.

   It takes the brand's own navy. Every other band on the page is read on
   paper; this one is the only band that asks for something, so it is the only
   one allowed the dark ground, and the gold action is the strongest mark on the
   page. The house tape runs along its top edge — the same seam the footer signs
   off with, at card scale.

   Read left to right: offer, terms, action. On a desk the copy and the action
   are separate columns with a gold hairline ruled between them, so the button
   is always found at the card's right edge; below 981px they stack, and the
   action comes to rest at the left margin where a stacked block belongs. */
.trade-cta {
    position: relative; overflow: hidden;
    display: grid; gap: clamp(1.25rem, 2.5vw, 1.75rem);
    padding: clamp(1.4rem, 3.2vw, 2.25rem);
    background:
        radial-gradient(115% 140% at 100% 0%, rgba(223, 166, 43, .20), transparent 58%),
        var(--navy);
    border: 1px solid var(--line-gold);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-lg);
}
/* On a wide screen the card is allowed 28px past the text column it sits under,
   so the band reads wider than the measure above it. It never reaches the edge
   of the window: the page gutter sits outside that column, and 28px is well
   inside it. Gated at 1280px because that is where the container first has room
   to spare — below it, a break-out would only eat the page's own margin. */
@media (min-width: 1280px) {
    .trade-cta { margin-inline: -28px; }
}
/* The tape along the top edge, as the footer signs off with. */
.trade-cta::before {
    content: ""; position: absolute; inset: 0 0 auto 0; height: 3px; background: var(--tape);
}
/* And the mounted-plate hairline inset off it — the edge the product and brief
   frames use, so the dark card belongs to the same system as the light ones
   rather than being a new object on the page. */
.trade-cta::after {
    content: ""; position: absolute; inset: 9px; pointer-events: none;
    border: 1px solid rgba(223, 166, 43, .16);
    border-radius: calc(var(--radius-lg) - 5px);
}
@media (min-width: 981px) {
    .trade-cta { grid-template-columns: minmax(0, 1fr) max-content; align-items: center; }
}

/* The offer. No measure cap of its own: the grid column already sets one — it
   is the card's width minus the action column — and capping it again would wrap
   the heading onto a second line at every desktop width, which is the one place
   this band cannot afford a ragged edge.

   The heading is set a step below the page's own h2. This is a card, not a
   band, and the band's display size broke a four-word line across two at every
   width under 1440px; at card scale it holds one line, which is what lets the
   heading, the paragraph and the button read as a single row of thought. */
.trade-cta .eyebrow { color: var(--gold-soft); }
.trade-cta h2 {
    font-size: clamp(1.4rem, 2.2vw, 1.95rem); line-height: 1.16;
    color: var(--on-navy); margin-bottom: var(--space-3);
}
.trade-cta-body p { margin: 0; color: rgba(254, 253, 241, .74); }

/* The action. One control is the point of the band, so the gold button is the
   only element in this column and the only filled shape on the page. The column
   is sized to the button rather than to a fixed width — max-content, set on the
   grid above — so the label holds one line at every desktop width instead of
   wrapping inside a column that was guessed at. Stacked, the column stretches
   and the button takes the card's own width, which is the tap target a thumb
   wants. */
.trade-cta-action { display: grid; justify-items: stretch; }
@media (min-width: 981px) {
    .trade-cta-action {
        border-left: 1px solid rgba(223, 166, 43, .24);
        padding-left: clamp(1.5rem, 3vw, 2.25rem);
    }
}

/* One line of voice, ruled in the house tape — the same ribbon that draws under
   a section heading, cut to the width of a sentence. */
.quote-line { position: relative; margin: var(--space-8) 0; padding-top: var(--space-5); }
.quote-line::before {
    content: ""; position: absolute; inset: 0 auto auto 0;
    width: 68px; height: 2px; background: var(--rule-tape);
}
.quote-line p {
    margin: 0; font-family: var(--display); color: var(--navy);
    font-size: clamp(1.22rem, 2.3vw, 1.55rem); line-height: 1.38; letter-spacing: -.012em;
}
.quote-line cite {
    display: block; margin-top: .75rem; font-style: normal;
    font-family: var(--mono); font-size: .6rem; font-weight: 500;
    letter-spacing: .2em; text-transform: uppercase; color: var(--muted-2);
}

/* The ten points, numbered — the sheet read as the document it is rather than as
   ten loose rows on the band.

   The plate is mounted: a document, not a card. Warm paper at its head easing
   into the white of the band, a hairline edge, the house tape along its top,
   and a letterhead line — label at the left, count at the right, a dotted
   leader between them. The list rules upward from each row rather than downward
   from the last, so the first row's rule is the divider under the letterhead
   and no rule is left hanging below the final row, whatever the list's length. */
.spec-plate {
    position: relative; overflow: hidden;
    background: linear-gradient(168deg, var(--cream) 0%, var(--panel-2) 42%, var(--panel) 84%);
    border: 1px solid var(--line);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
}
.spec-plate::before {
    content: ""; position: absolute; inset: 0 0 auto 0; height: 3px; background: var(--tape);
}

/* The plate's own letterhead. A grid rather than a flex row so the leader gets a
   track of its own: it takes exactly the air between the two ends, and neither
   end can be pushed off its side when the count runs long. */
.spec-plate-head {
    display: grid; grid-template-columns: auto minmax(1.5rem, 1fr) auto;
    align-items: baseline; gap: .9rem;
    padding: clamp(1.15rem, 2.6vw, 1.5rem) clamp(1.15rem, 3vw, 2rem) clamp(.9rem, 2vw, 1.15rem);
}
/* The index mark: a dotted rule with no text in it. Led off the baseline so it
   sits inside the cap height of the two labels rather than on their floor. */
.spec-plate-leader {
    border-bottom: 1px dotted var(--line-gold); transform: translateY(-4px);
}
.spec-plate-label,
.spec-plate-count {
    font-family: var(--mono); font-size: var(--fs-label); font-weight: 500;
    letter-spacing: var(--track-label); text-transform: uppercase;
}
.spec-plate-label { color: var(--gold-ink); }
.spec-plate-count { color: var(--muted-2); }

/* Two columns on a desk with the gutter ruled; one column on a phone, where a
   second column would only make the rows harder to hold.

   The count is stated rather than left to auto-fit: ten points in three columns
   is four rows, four and two — a ragged sheet, and the odd/even rules below are
   written for pairs. Two it is, at every width the plate holds.

   The bottom padding is the plate's floor: the rows rule upward, so nothing is
   left hanging below the last point and the sheet simply ends in air. */
.spec-sheet {
    list-style: none; margin: 0;
    padding: 0 clamp(1.15rem, 3vw, 2rem) clamp(1.15rem, 3vw, 2rem);
    display: grid; grid-template-columns: 1fr;
}
@media (min-width: 981px) {
    .spec-sheet { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
.spec-sheet li {
    display: grid; grid-template-columns: 2.1rem minmax(0, 1fr); align-items: center;
    gap: .4rem 1rem; padding: clamp(.85rem, 1.7vw, 1.05rem) 0;
    border-top: 1px solid var(--line-soft);
    font-size: .95rem; line-height: 1.45; color: var(--navy);
    /* Keeps the wrap from leaving a lone word on the second line, which on a
       two-column sheet is the difference between ruled rows and ragged ones. */
    text-wrap: pretty;
    transition: border-top-color var(--dur) var(--ease);
}
/* The number as a chip — the gold plate the section icons sit on, at row scale,
   in tabular figures so 01 and 10 hold the same width and the column of numbers
   stays a column. */
.spec-sheet-num {
    width: 2.1rem; height: 2.1rem; display: grid; place-items: center;
    font-family: var(--mono); font-size: .62rem; font-weight: 500;
    letter-spacing: .02em; font-variant-numeric: tabular-nums;
    color: var(--gold-ink);
    background: var(--gold-a08);
    border: 1px solid var(--line-gold-soft); border-radius: var(--radius-sm);
    transition: background var(--dur) var(--ease), border-color var(--dur) var(--ease),
                color var(--dur) var(--ease);
}
/* The one interaction the sheet needs — it marks the row the reader is on
   without turning ten rows into ten buttons. Gated on hover, because a touch
   screen has no hover to leave and a stuck navy chip reads as a selection. */
@media (hover: hover) {
    .spec-sheet li:hover { border-top-color: var(--line-gold); }
    .spec-sheet li:hover > .spec-sheet-num {
        background: var(--navy); border-color: var(--navy); color: var(--gold-soft);
    }
}
@media (min-width: 981px) {
    .spec-sheet li:nth-child(odd)  { padding-right: 2.25rem; border-right: 1px solid var(--line-soft); }
    .spec-sheet li:nth-child(even) { padding-left: 2.25rem; }
}
/* A phone gives the sheet the whole column, so the rows carry it alone: the chip
   and the text step down a size to keep five ruled rows in the same height the
   desk spends on four, and the letterhead stacks — two mono strings that size
   cannot share 300px without one of them being clipped. */
@media (max-width: 600px) {
    .spec-plate-head { grid-template-columns: 1fr; gap: .4rem; }
    .spec-plate-leader { display: none; }
    .spec-sheet li { grid-template-columns: 1.9rem minmax(0, 1fr); gap: .35rem .9rem; font-size: .9rem; }
    .spec-sheet-num { width: 1.9rem; height: 1.9rem; font-size: .58rem; }
}


/* 12 ── Responsive -------------------------------------------------------- */
@media (max-width: 720px) {
    /* The gutter is the token's job at every width now — the fluid --gutter
       floors at 20px, which is the margin a phone wants. The old 1rem/.875rem
       overrides here and in the 480px block fought it and left a phone with
       14px of margin against a 1240px plate. */
    /* The band rhythm is the token's job at every width now. A phone override
       was here, and because .section and .section--joined carry the same
       specificity it also cancelled --joined's reduced leading edge — two bands
       on the same ground met at the full gap again. The token floors at 40px,
       which is what that override was reaching for. */
    .compare th, .compare td { padding: .8rem .85rem; }
    .btn { width: 100%; }
    .btn-row { flex-direction: column; }
    .btn-row .btn { width: 100%; }
    .strip-cell { padding: 1.3rem 1.15rem; }
}
@media (max-width: 480px) {
    /* Nothing to override: --gutter already floors at 20px for a phone. */
}

@media print {
    .primary-header, .header-top-strip, .site-footer, .float-cta { display: none; }
    body { background: #fff; color: #000; }
}
