/* =============================================================================
   SOLANA DOG SPA
   Palette is unchanged from the previous build — the same black/orange/gold
   hexes, carried over by value. What changed is type, shape, layout and motion.
   ========================================================================== */

:root {
    /* --- Surfaces -----------------------------------------------------------
       The design brief wanted these near-blacks tinted toward the accent hue.
       Deliberately not doing that: warming #0f0f0f shifts the brand's base
       colour, which is the one thing we were told not to touch. So the four
       steps are the blacks that already existed, plus one interpolated step
       (--bg-2) so sections have something to alternate against. --surface-2 is
       pulled down from the old #2a2a2a because at #2a the "faint" text weight
       drops under 4.5:1 and we lose AA on metadata. */
    --bg: #0f0f0f;
    --bg-2: #141414;
    --surface: #1a1a1a;
    --surface-2: #222222;

    /* --- Accent family ------------------------------------------------------
       No semantic green anywhere. With no live-status pill on the site there is
       nothing for a green to signal, and an unused colour is just palette drift. */
    --accent: #ff8c42;
    --accent-2: #d4af37;
    --accent-deep: #e67e22;
    --accent-dim: #ffb380;

    /* Borders are the accent at very low alpha — hairlines that read as
       precision, never as solid grey dividers. --border-hi is hover only. */
    --border: rgba(255, 140, 66, 0.12);
    --border-hi: rgba(255, 140, 66, 0.55);
    --border-2: rgba(212, 175, 55, 0.14);

    /* The one gradient. Allowed exactly four uses site-wide: headline emphasis,
       big numbers, the primary button, and the nav progress bar. Adding a fifth
       is how a system like this starts looking like everything else. */
    /* The middle stop is the exact linear interpolation of the two brand hexes,
       so this renders identically to the orange->gold gradient the old build
       used. It is a three-stop gradient for control, not a new colour. */
    --grad: linear-gradient(135deg, #ff8c42 0%, #e99e3c 50%, #d4af37 100%);

    /* --- Text ---------------------------------------------------------------
       Three weights, each checked against --surface-2 (#222), the lightest
       surface any of them can land on. #666 from the old build is gone: it was
       3.3:1 on black and only ever survived on the white sections we removed. */
    --text: #ffffff;
    --muted: #e0e0e0;
    --faint: #999999;

    /* --- Shape --------------------------------------------------------------
       Asymmetric chamfers: one pair of corners sharp, the opposite pair carved.
       This is the strongest signature in the system — there is no plain
       border-radius anywhere in this file except on gradient washes, where the
       radius describes an invisible shape rather than a visible edge. */
    --r-card: 3px 18px 3px 18px;
    --r-btn: 2px 13px 2px 13px;
    --r-sm: 2px 10px 2px 10px;

    /* --- Type ---------------------------------------------------------------
       Fallbacks are real stacks, not just `sans-serif`: if Google Fonts is
       blocked the page must still set cleanly rather than reflow into Times. */
    --display: 'Bricolage Grotesque', 'Arial Black', 'Helvetica Neue', sans-serif;
    --sans: 'Space Grotesk', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
    --mono: 'Space Mono', ui-monospace, SFMono-Regular, Menlo, monospace;

    /* Only these sizes are used for anything that is not display type. The gap
       between 20px and the hero clamp is intentional and very large. */
    --t11: 11px;
    --t12: 12px;
    --t13: 13px;
    --t14: 14px;
    --t16: 16px;
    --t18: 18px;
    --t20: 20px;

    /* --- Space --------------------------------------------------------------
       4px base. No arbitrary values below; if a number is not on this scale it
       is a mistake, not a decision. */
    --s4: 4px;
    --s8: 8px;
    --s12: 12px;
    --s16: 16px;
    --s24: 24px;
    --s32: 32px;
    --s48: 48px;
    --s64: 64px;
    --s96: 96px;
    --s128: 128px;

    --section-y: clamp(96px, 11vw, 152px);
    --maxw: 1160px;

    /* A long shadow with heavily negative spread. This is a lift — the card
       appears to rise off the page — not a glow around its edges. */
    --lift: 0 22px 60px -20px rgba(255, 140, 66, 0.28);
    --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* =============================================================================
   RESET & BASE
   ========================================================================== */

*,
*::before,
*::after {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html {
    scroll-behavior: smooth;
    /* The sticky nav is 62px; without this, in-page anchors land underneath it. */
    scroll-padding-top: 78px;
    -webkit-text-size-adjust: 100%;
}

body {
    font-family: var(--sans);
    font-size: var(--t16);
    line-height: 1.65;
    color: var(--muted);
    background: var(--bg);
    /* Chamfered cards sit flush to the viewport edge at small widths; without
       this their shadows push a horizontal scrollbar. */
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
}

a {
    color: inherit;
    text-decoration: none;
}

img,
svg,
iframe {
    max-width: 100%;
    display: block;
}

button {
    font: inherit;
    color: inherit;
    background: none;
    border: none;
    cursor: pointer;
}

ul,
ol {
    list-style: none;
}

/* --- Display type -----------------------------------------------------------
   Bricolage Grotesque carries an optical-size axis. opsz is set per size rather
   than left to default: at hero scale the default optical size renders the
   counters too open and the whole line goes soft. */
h1,
h2,
h3,
.display {
    font-family: var(--display);
    font-weight: 800;
    text-transform: uppercase;
    letter-spacing: -0.028em;
    line-height: 0.94;
    color: var(--text);
    font-variation-settings: 'opsz' 40;
}

/* --- Mono ------------------------------------------------------------------
   Mono is load-bearing here, not a code style. Every eyebrow, label, date,
   caption, chip, nav link and footer line uses it. */
.mono,
.eyebrow,
.chip,
.meta {
    font-family: var(--mono);
    text-transform: uppercase;
    font-size: var(--t12);
    letter-spacing: 0.14em;
    line-height: 1.5;
}

.eyebrow {
    display: inline-flex;
    align-items: center;
    gap: var(--s8);
    color: var(--accent);
    font-size: var(--t12);
    letter-spacing: 0.24em;
}

/* The small rule that precedes an eyebrow. Marks the start of a section the way
   a tick mark does on an instrument. */
.eyebrow::before {
    content: '';
    width: 26px;
    height: 1px;
    background: var(--accent);
    opacity: 0.7;
}

.meta {
    color: var(--faint);
    letter-spacing: 0.16em;
    font-size: var(--t11);
}

/* Prose is capped in ch, never px — the measure has to stay right regardless of
   how the font falls back. */
.prose {
    max-width: 58ch;
    color: var(--muted);
}

.prose--tight {
    max-width: 46ch;
}

/* =============================================================================
   LAYOUT PRIMITIVES
   ========================================================================== */

.container {
    max-width: var(--maxw);
    margin: 0 auto;
    padding: 0 var(--s24);
}

/* Every section carries one hairline top border and alternates its ground, so
   the page has a slow tonal rhythm instead of hard banded rules. */
.section {
    padding: var(--section-y) 0;
    border-top: 1px solid var(--border);
    background: var(--bg);
    position: relative;
}

.section--alt {
    background: var(--bg-2);
}

/* A single row of chips does not need a full --section-y on both sides; at that
   height the strip reads as an empty band rather than a divider. */
.section--slim {
    padding: var(--s64) 0;
}

.section__head {
    margin-bottom: var(--s64);
}

.section__head h2 {
    font-size: clamp(30px, 4.4vw, 54px);
    font-variation-settings: 'opsz' 32;
    margin-top: var(--s16);
}

.section__head .prose {
    margin-top: var(--s24);
}

/* =============================================================================
   ACCESSIBILITY
   ========================================================================== */

.skip-link {
    position: absolute;
    top: var(--s8);
    left: var(--s8);
    z-index: 200;
    padding: var(--s12) var(--s24);
    background: var(--accent);
    color: var(--bg);
    font-family: var(--mono);
    font-size: var(--t12);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    border-radius: var(--r-sm);
    transform: translateY(-160%);
    transition: transform 0.18s var(--ease);
}

.skip-link:focus-visible {
    transform: translateY(0);
}

/* One global focus ring. Applied on :focus-visible only so pointer users never
   see it, but it is never removed for anyone navigating by keyboard. */
/* No border-radius here. This selector matches every focusable element, so
   setting one would strip the chamfer off any button or link the moment it took
   focus. The outline follows the element's own corners. */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
}

/* Clip-rect rather than display:none — content hidden this way is still read by
   assistive tech and still found by in-page search. */
.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* =============================================================================
   TEXTURE
   ========================================================================== */

/* Grain. position:fixed is mandatory — on a scrolling container this repaints
   every frame and drops mobile framerate through the floor. */
body::after {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 60;
    pointer-events: none;
    opacity: 0.03;
    mix-blend-mode: overlay;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='180' height='180'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='180' height='180' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* Very low-alpha radial washes. Two per page, never more — a third stops
   reading as light and starts reading as a coloured background. */
.wash {
    position: absolute;
    pointer-events: none;
    border-radius: 50%;
    z-index: 0;
    filter: blur(8px);
}

.wash--a {
    width: min(720px, 90vw);
    height: 520px;
    top: -180px;
    right: -160px;
    background: radial-gradient(circle, rgba(255, 140, 66, 0.07) 0%, transparent 68%);
}

.wash--b {
    width: min(560px, 80vw);
    height: 420px;
    bottom: -140px;
    left: -140px;
    background: radial-gradient(circle, rgba(212, 175, 55, 0.055) 0%, transparent 70%);
}

.section > .container {
    position: relative;
    z-index: 1;
}

/* =============================================================================
   NAV
   ========================================================================== */

.nav {
    position: sticky;
    top: 0;
    z-index: 50;
    height: 62px;
    background: rgba(15, 15, 15, 0.72);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    border-bottom: 1px solid var(--border);
}

.nav__inner {
    max-width: var(--maxw);
    margin: 0 auto;
    padding: 0 var(--s24);
    height: 100%;
    display: flex;
    align-items: center;
    gap: var(--s32);
}

.nav__mark {
    font-family: var(--mono);
    font-size: var(--t13);
    text-transform: uppercase;
    letter-spacing: 0.28em;
    color: var(--text);
    display: flex;
    align-items: center;
    gap: var(--s8);
    white-space: nowrap;
}

.nav__mark i {
    color: var(--accent);
    font-size: var(--t14);
}

.nav__links {
    display: flex;
    align-items: center;
    gap: var(--s32);
    margin-left: auto;
}

.nav__links a {
    font-family: var(--mono);
    font-size: var(--t12);
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--faint);
    padding: var(--s8) 0;
    position: relative;
    transition: color 0.2s var(--ease);
}

.nav__links a::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: 0;
    width: 100%;
    height: 1px;
    background: var(--accent);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.24s var(--ease);
}

/* The underline fires on focus as well as hover — a hover-only affordance is
   invisible to anyone driving the page from the keyboard. */
.nav__links a:hover,
.nav__links a:focus-visible,
.nav__links a[aria-current='page'] {
    color: var(--text);
}

.nav__links a:hover::after,
.nav__links a:focus-visible::after,
.nav__links a[aria-current='page']::after {
    transform: scaleX(1);
}

.nav__right {
    display: flex;
    align-items: center;
    gap: var(--s16);
}

.nav__phone {
    font-family: var(--mono);
    font-size: var(--t12);
    letter-spacing: 0.1em;
    color: var(--accent-2);
    white-space: nowrap;
    transition: color 0.2s var(--ease);
}

.nav__phone:hover,
.nav__phone:focus-visible {
    color: var(--accent);
}

/* ⌘K trigger. Doubles as the mobile menu button — see the 920px breakpoint. */
.kbd-btn {
    display: inline-flex;
    align-items: center;
    gap: var(--s8);
    font-family: var(--mono);
    font-size: var(--t11);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--faint);
    padding: var(--s8) var(--s12);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    background: rgba(255, 255, 255, 0.02);
    transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}

.kbd-btn:hover,
.kbd-btn:focus-visible {
    color: var(--text);
    border-color: var(--border-hi);
}

.kbd-btn__label--menu {
    display: none;
}

/* Nothing can open the palette without JS, so the trigger would be a control
   that does nothing. The nav links are in the markup regardless. */
body:not(.js) .kbd-btn {
    display: none;
}

/* Scroll progress. Driven by one scrubbed ScrollTrigger, never a scroll
   listener — a listener here fires hundreds of times a second and fights the
   compositor for the same property. */
.progress {
    position: absolute;
    left: 0;
    bottom: -2px;
    height: 2px;
    width: 100%;
    background: var(--grad);
    transform: scaleX(0);
    transform-origin: left;
}

/* Without JS there is no ScrollTrigger to drive it, so it would sit at zero
   width forever. Hide it rather than leave a dead element in the layout. */
body:not(.js) .progress {
    display: none;
}

/* =============================================================================
   BUTTONS — two weights only
   ========================================================================== */

.btn {
    display: inline-flex;
    align-items: center;
    gap: var(--s12);
    font-family: var(--mono);
    font-size: var(--t12);
    text-transform: uppercase;
    letter-spacing: 0.16em;
    padding: var(--s16) var(--s32);
    border-radius: var(--r-btn);
    border: 1px solid transparent;
    transition: transform 0.18s var(--ease), box-shadow 0.24s var(--ease),
        border-color 0.2s var(--ease), background-color 0.2s var(--ease);
    will-change: transform;
}

.btn:hover {
    transform: translateY(-2px);
}

.btn:active {
    transform: translateY(1px);
}

/* Small solid dot before the label. Reads as a status indicator on an
   instrument panel rather than as decoration. */
.btn::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex: none;
    opacity: 0.85;
}

/* Near-black ink on the gradient. The previous build used white on #ff8c42,
   which measures ~2.1:1 and fails AA outright; #0f0f0f against the gradient's
   lightest stop is ~9.5:1. */
.btn--primary {
    background: var(--grad);
    color: var(--bg);
    font-weight: 700;
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45),
        0 10px 30px -12px rgba(255, 140, 66, 0.55);
}

.btn--primary:hover {
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.45),
        0 16px 40px -14px rgba(255, 140, 66, 0.7);
}

.btn--ghost {
    border-color: var(--border-hi);
    color: var(--text);
    background: rgba(255, 255, 255, 0.03);
}

.btn--ghost:hover,
.btn--ghost:focus-visible {
    background: rgba(255, 140, 66, 0.08);
}

.btn--sm {
    padding: var(--s12) var(--s24);
    font-size: var(--t11);
}

/* The third weight, where one is needed. A mono text link with an offset
   underline — deliberately not a third button. */
.tlink {
    font-family: var(--mono);
    font-size: var(--t12);
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 6px;
    text-decoration-thickness: 1px;
    text-decoration-color: var(--border-hi);
    transition: text-decoration-color 0.2s var(--ease);
}

.tlink:hover,
.tlink:focus-visible {
    text-decoration-color: var(--accent);
}

/* =============================================================================
   CARDS
   ========================================================================== */

.card {
    position: relative;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: var(--r-card);
    /* Asymmetric padding — more at the bottom than the top. Balances the
       corner-pinned index number, which optically weights the top edge. */
    padding: var(--s32) var(--s32) var(--s48);
    overflow: hidden;
    transition: border-color 0.26s var(--ease), box-shadow 0.3s var(--ease),
        background-color 0.26s var(--ease);
    isolation: isolate;
}

.card--tint {
    background: var(--surface-2);
}

.card:hover,
.card:focus-within {
    border-color: var(--border-hi);
    box-shadow: var(--lift);
}

/* Cursor-following highlight. --mx/--my are written by JS on pointermove; they
   default to the card centre so a card that is focused rather than hovered
   still lights up sensibly. */
.card::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.3s var(--ease);
    background: radial-gradient(
        340px circle at var(--mx, 50%) var(--my, 50%),
        rgba(255, 140, 66, 0.1),
        transparent 62%
    );
}

.card:hover::after,
.card:focus-within::after {
    opacity: 1;
}

.card__index {
    position: absolute;
    top: var(--s24);
    right: var(--s24);
    font-family: var(--mono);
    font-size: var(--t11);
    letter-spacing: 0.2em;
    color: var(--faint);
    opacity: 0.55;
}

.card__icon {
    font-size: 22px;
    color: var(--accent);
    margin-bottom: var(--s24);
}

.card h3 {
    font-size: var(--t20);
    font-variation-settings: 'opsz' 18;
    letter-spacing: -0.015em;
    margin-bottom: var(--s12);
}

.card p {
    font-size: var(--t14);
    color: var(--muted);
    max-width: 46ch;
}

/* =============================================================================
   CHIPS & HAIRLINE-GROUPED LISTS
   For dense sets of small items. A comma-run reads as prose and a box grid
   reads as six more cards; chips read as data.
   ========================================================================== */

.group {
    border-top: 1px solid var(--border);
    padding-top: var(--s24);
    margin-top: var(--s48);
}

.group__label {
    font-family: var(--mono);
    font-size: var(--t11);
    text-transform: uppercase;
    letter-spacing: 0.28em;
    color: var(--faint);
    margin-bottom: var(--s24);
}

.chips {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s12);
}

.chip {
    display: inline-flex;
    align-items: center;
    gap: var(--s12);
    padding: var(--s12) var(--s16);
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    background: var(--surface);
    color: var(--muted);
    font-size: var(--t12);
    letter-spacing: 0.1em;
    transition: border-color 0.22s var(--ease), color 0.22s var(--ease);
}

.chip:hover {
    border-color: var(--border-hi);
    color: var(--text);
}

.chip i {
    color: var(--accent);
    font-size: var(--t13);
}

/* The price rides on the chip itself rather than in a separate column — keeps
   a twelve-item list scannable without turning it into a table. */
.chip__val {
    color: var(--accent-2);
    letter-spacing: 0.06em;
    padding-left: var(--s12);
    border-left: 1px solid var(--border);
}

/* =============================================================================
   HERO
   ========================================================================== */

.hero {
    position: relative;
    min-height: min(94vh, 880px);
    display: flex;
    align-items: center;
    padding: var(--s128) 0 var(--s96);
    overflow: hidden;
}

/* The logo art lives on its own layer so the scrim can be graded across it
   without also washing out the type sitting on top. */
.hero__media {
    position: absolute;
    inset: 0;
    z-index: 0;
    /* Biased upward rather than centred. logo.png carries the "SOLANA DOG SPA"
       wordmark below the artwork, and a centred cover crop drags it into the
       bottom of the hero as a ghosted serif blob behind the meta row. At 18%
       the dog and mountains fill the frame and the type is pushed out of it.
       (A pre-cropped asset was tried instead; at 2.5:1 it forced cover to scale
       by height on desktop and blew the dog up to fill the viewport.) */
    background: url('logo.png') no-repeat center 18%;
    background-size: cover;
    background-attachment: fixed;
}

/* The artwork is a logo on a white field. Left-weighted so the type sits on
   near-black while the dog stays legible on the right; the vertical pass fades
   the whole thing into --bg so the hero doesn't end on a hard edge. */
.hero__media::after {
    content: '';
    position: absolute;
    inset: 0;
    background: linear-gradient(
            90deg,
            rgba(15, 15, 15, 0.95) 0%,
            rgba(15, 15, 15, 0.88) 34%,
            rgba(15, 15, 15, 0.46) 62%,
            rgba(15, 15, 15, 0.52) 100%
        ),
        /* The two passes multiply. Right of centre they compound to ~0.59, which
           is the point where the dog still reads as a dog; much past 0.7 and the
           artwork turns into an indistinct dark smudge. */
            linear-gradient(
                180deg,
                rgba(15, 15, 15, 0.62) 0%,
                rgba(15, 15, 15, 0.25) 40%,
                rgba(15, 15, 15, 0.98) 100%
            );
}

/* .hero is a flex row, which makes .container a flex item — and a flex item with
   `margin: 0 auto` shrink-wraps its content and centres the result instead of
   filling the row. Without this the hero block floats to the middle of the page
   while every other section starts at the container's left edge. */
.hero > .container {
    width: 100%;
}

/* Sized in px, not ch. A ch-based measure is correct for prose but strangles
   the headline: 58ch of Space Grotesk is ~580px, and "PAMPERED" set at the
   108px clamp ceiling needs ~600px, so the D was being sliced off by the hero's
   overflow:hidden. The paragraph keeps its own ch cap below. */
.hero__inner {
    position: relative;
    z-index: 2;
    max-width: 760px;
}

.hero h1 {
    font-size: clamp(46px, 7.8vw, 108px);
    line-height: 0.92;
    letter-spacing: -0.035em;
    font-variation-settings: 'opsz' 96;
    margin: var(--s24) 0;
}

/* Headline emphasis — one of the four sanctioned uses of the gradient.
   Targets an explicit class, not `h1 span`: the reveal wraps every line in
   generated spans at runtime, and a descendant selector would paint the whole
   headline gradient the moment JS ran. */
.hero h1 .em {
    background: var(--grad);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
}

.hero p {
    font-size: var(--t18);
    color: var(--muted);
    max-width: 52ch;
}

.hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s16);
    margin-top: var(--s48);
}

/* A row of mono facts under the CTAs. Reads as an instrument readout and does
   the work the old "urgency tag" was doing, without the shouting. */
.hero__meta {
    display: flex;
    flex-wrap: wrap;
    gap: var(--s32);
    margin-top: var(--s48);
    padding-top: var(--s24);
    border-top: 1px solid var(--border);
}

.hero__meta div {
    font-family: var(--mono);
    font-size: var(--t11);
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--faint);
}

.hero__meta strong {
    display: block;
    color: var(--text);
    font-weight: 400;
    letter-spacing: 0.1em;
    margin-top: var(--s4);
}

/* The one ornament on the site: a slowly rotating textPath ring. Hidden below
   1020px, where there is no spare width for anything purely decorative. */
.badge-ring {
    position: absolute;
    right: var(--s64);
    bottom: var(--s96);
    width: 132px;
    height: 132px;
    z-index: 2;
    opacity: 0.8;
}

.badge-ring svg {
    width: 100%;
    height: 100%;
    animation: ring-spin 26s linear infinite;
}

.badge-ring text {
    font-family: var(--mono);
    font-size: 9.5px;
    letter-spacing: 0.32em;
    text-transform: uppercase;
    fill: var(--accent-2);
}

.badge-ring i {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    color: var(--accent);
    font-size: 20px;
}

@keyframes ring-spin {
    to {
        transform: rotate(360deg);
    }
}

/* Compact hero for the interior pages — no artwork, just the wash and type. */
.pagehead {
    position: relative;
    padding: var(--s128) 0 var(--s96);
    overflow: hidden;
    border-bottom: 1px solid var(--border);
}

.pagehead h1 {
    font-size: clamp(38px, 6.4vw, 82px);
    font-variation-settings: 'opsz' 72;
    margin: var(--s24) 0 var(--s24);
}

.pagehead .prose {
    color: var(--muted);
}

/* =============================================================================
   ASYMMETRIC SPAN GRID  (index — services)
   3 columns, cells spanning 2+1 then 1+2. Exactly as many cells as items, so
   there is never a filler tile propping up the shape.
   ========================================================================== */

.spangrid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: var(--s24);
}

.spangrid > :nth-child(1) {
    grid-column: span 2;
}

.spangrid > :nth-child(4) {
    grid-column: span 2;
}

/* The wide cells earn their width with more type and more air, not by
   stretching the same content across a longer line. */
.spangrid > [class*='span-2'],
.spangrid > :nth-child(1),
.spangrid > :nth-child(4) {
    padding: var(--s48) var(--s48) var(--s64);
}

.spangrid > :nth-child(1) .card__icon {
    font-size: 34px;
}

.spangrid > :nth-child(1) h3 {
    font-size: clamp(26px, 3vw, 36px);
    font-variation-settings: 'opsz' 28;
}

.price {
    display: inline-block;
    margin-top: var(--s24);
    font-family: var(--display);
    font-weight: 800;
    font-size: clamp(28px, 3.4vw, 42px);
    font-variation-settings: 'opsz' 30;
    letter-spacing: -0.03em;
    /* Big numbers — the second sanctioned use of the gradient. */
    background: var(--grad);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
}

.price small {
    display: block;
    font-family: var(--mono);
    font-size: var(--t11);
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--faint);
    -webkit-text-fill-color: var(--faint);
    margin-bottom: var(--s8);
}

/* The wide closing cell of the span grid: not a card, a call to the full menu. */
.spangrid__cta {
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: var(--s24);
    border: 1px solid var(--border);
    border-radius: var(--r-card);
    padding: var(--s48);
    background: linear-gradient(135deg, rgba(255, 140, 66, 0.05), transparent 60%);
}

.spangrid__cta h3 {
    font-size: clamp(24px, 2.6vw, 32px);
    font-variation-settings: 'opsz' 26;
}

/* =============================================================================
   BENTO  (index — visit us)
   4 columns, mixed spans, single-height rows. One cell gets the lighter tint so
   the block never reads as four identical dark rectangles.
   ========================================================================== */

.bento {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--s24);
    /* The tilt-up entrance rotates on rotateX; without perspective on the
       parent the rotation flattens into a vertical squash. */
    perspective: 1200px;
}

.bento__cell {
    grid-column: span 2;
}

/* A full-width cell holding one short paragraph leaves most of the row empty,
   because prose is capped by measure and cannot stretch to fill it. Splitting
   the cell puts the heading and the body side by side so the width is actually
   used, rather than widening the measure past readable. */
.bento__cell--wide {
    grid-column: span 4;
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1.5fr);
    gap: 0 var(--s48);
    align-items: start;
    /* Without this the two auto rows stretch to fill the card and the heading
       drifts to the bottom, half a card away from the paragraph it belongs to.
       Packing the rows to the top keeps the icon, heading and body together. */
    align-content: start;
}

.bento__cell--wide .card__icon {
    grid-column: 1;
    grid-row: 1;
}

.bento__cell--wide h3 {
    grid-column: 1;
    grid-row: 2;
}

.bento__cell--wide > p {
    grid-column: 2;
    grid-row: 1 / span 2;
    max-width: 58ch;
    margin-top: 0;
}

.bento__cell h3 {
    font-size: var(--t18);
    font-variation-settings: 'opsz' 16;
}

.bento__cell p,
.bento__cell address {
    font-style: normal;
    font-size: var(--t14);
    color: var(--muted);
    margin-top: var(--s12);
}

.bento__cell a {
    color: var(--accent);
    text-underline-offset: 4px;
}

.bento__cell a:hover,
.bento__cell a:focus-visible {
    text-decoration: underline;
}

/* =============================================================================
   OVERLAPPING SPLIT  (index — reviews)
   Used exactly once on the site. It is an accent; a second one turns it into a
   pattern and it stops reading as deliberate.
   ========================================================================== */

.overlap {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    align-items: start;
}

.overlap__a {
    grid-column: 1 / 8;
    /* Reserved gutter. The second card laps this corner, and without the
       reservation the rating text runs underneath it. */
    padding-right: var(--s96);
}

.overlap__b {
    grid-column: 7 / 13;
    margin-top: var(--s96);
    z-index: 2;
    background: var(--surface-2);
}

.rating {
    display: flex;
    align-items: baseline;
    gap: var(--s16);
}

.rating__num {
    font-family: var(--display);
    font-weight: 800;
    font-size: clamp(56px, 8vw, 96px);
    line-height: 0.86;
    font-variation-settings: 'opsz' 72;
    letter-spacing: -0.04em;
    /* Big numbers — third sanctioned gradient use. */
    background: var(--grad);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
}

.rating__stars {
    color: var(--accent-2);
    font-size: var(--t16);
    letter-spacing: 2px;
}

.rating__meta {
    margin-top: var(--s24);
    padding-top: var(--s24);
    border-top: 1px solid var(--border);
}

/* =============================================================================
   RAIL TIMELINE  (hours)
   Two columns split by a hairline. Mono days right-aligned on the left, times
   on the right, dots straddling the rail.
   ========================================================================== */

/* Capped well short of the container. The times are short strings, so at full
   1160px width the rail trails off into a long empty gutter and stops reading
   as a deliberate block. */
.rail {
    position: relative;
    max-width: 760px;
}

/* The line is absolutely positioned at a fixed offset rather than drawn as a
   grid border, because it has to be scaled independently by the scrub. */
.rail__line {
    position: absolute;
    /* 168px day column + half the 32px gap. If this drifts from the dot offset
       below, the dots sit beside the rail instead of straddling it. */
    left: 184px;
    top: 6px;
    bottom: 0;
    width: 1px;
    background: linear-gradient(180deg, var(--accent), var(--accent-2));
    transform-origin: top;
    /* Taper the bottom so the rail fades out instead of stopping dead against
       the last row. */
    -webkit-mask-image: linear-gradient(180deg, #000 76%, transparent 100%);
    mask-image: linear-gradient(180deg, #000 76%, transparent 100%);
}

.rail__row {
    display: grid;
    grid-template-columns: 168px var(--s32) 1fr;
    align-items: baseline;
    padding: var(--s16) 0;
    position: relative;
}

.rail__day {
    text-align: right;
    font-family: var(--mono);
    font-size: var(--t12);
    text-transform: uppercase;
    letter-spacing: 0.16em;
    color: var(--text);
}

.rail__time {
    grid-column: 3;
    font-family: var(--mono);
    font-size: var(--t16);
    letter-spacing: 0.1em;
    color: var(--accent-2);
}

/* Dot straddles the rail: the line sits at 184px, minus half the 7px dot. */
.rail__row::before {
    content: '';
    position: absolute;
    left: 180.5px;
    top: 26px;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--accent);
    box-shadow: 0 0 0 4px var(--bg-2);
}

.rail__row.is-closed .rail__day,
.rail__row.is-closed .rail__time {
    color: var(--faint);
}

.rail__row.is-closed::before {
    background: var(--faint);
    opacity: 0.6;
}

/* =============================================================================
   MAP
   ========================================================================== */

.map {
    margin-top: var(--s64);
    border: 1px solid var(--border);
    border-radius: var(--r-card);
    overflow: hidden;
}

.map iframe {
    width: 100%;
    height: 420px;
    border: 0;
    display: block;
    /* Google's tiles ship bright and blue and fight everything around them.
       Inverting and re-rotating the hue pulls them into the dark palette
       without hosting a custom-styled map. */
    filter: invert(0.92) hue-rotate(180deg) saturate(0.55) contrast(0.92);
}

/* =============================================================================
   COMMAND PALETTE
   ========================================================================== */

.palette {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: none;
    padding: clamp(64px, 14vh, 160px) var(--s24) var(--s24);
    background: rgba(8, 8, 8, 0.72);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}

.palette.is-open {
    display: block;
}

.palette__panel {
    max-width: 560px;
    margin: 0 auto;
    border-radius: var(--r-card);
    overflow: hidden;
    /* Gradient border via the padding-box/border-box double background. The one
       place in the system where a gradient border is earned. */
    border: 1px solid transparent;
    background: linear-gradient(var(--surface), var(--surface)) padding-box,
        var(--grad) border-box;
    box-shadow: 0 40px 90px -30px rgba(0, 0, 0, 0.9);
}

.palette__input {
    width: 100%;
    padding: var(--s24);
    font-family: var(--mono);
    font-size: var(--t14);
    letter-spacing: 0.1em;
    color: var(--text);
    background: transparent;
    border: none;
    border-bottom: 1px solid var(--border);
    outline: none;
}

/* Placeholder is metadata and still has to clear 4.5:1 — the browser default
   grey does not. */
.palette__input::placeholder {
    color: var(--faint);
    text-transform: uppercase;
}

.palette__list {
    max-height: 320px;
    overflow-y: auto;
    padding: var(--s8);
}

.palette__item {
    display: flex;
    align-items: center;
    gap: var(--s16);
    width: 100%;
    text-align: left;
    padding: var(--s12) var(--s16);
    border-radius: var(--r-sm);
    font-family: var(--mono);
    font-size: var(--t12);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--muted);
}

.palette__item i {
    color: var(--accent);
    width: 16px;
    text-align: center;
}

.palette__item small {
    margin-left: auto;
    color: var(--faint);
    font-size: var(--t11);
    letter-spacing: 0.1em;
}

/* Selection is driven by the arrow keys, so it is a class rather than :hover —
   the highlight has to be able to sit somewhere the pointer isn't. */
.palette__item.is-active,
.palette__item:hover {
    background: rgba(255, 140, 66, 0.1);
    color: var(--text);
}

.palette__empty {
    padding: var(--s24);
    font-family: var(--mono);
    font-size: var(--t12);
    color: var(--faint);
    text-transform: uppercase;
    letter-spacing: 0.14em;
}

/* =============================================================================
   PAGE-SPECIFIC
   ========================================================================== */

/* --- Services: size bento ------------------------------------------------- */
.sizes {
    display: grid;
    grid-template-columns: repeat(4, 1fr);
    gap: var(--s24);
    perspective: 1200px;
}

.sizes .card {
    padding: var(--s32) var(--s24) var(--s48);
    /* The four size descriptions wrap to different line counts, which leaves the
       prices sitting at four different heights. In a price table that reads as
       sloppy, so the price is pushed to the bottom of every card instead. */
    display: flex;
    flex-direction: column;
}

.sizes .price {
    margin-top: auto;
    padding-top: var(--s24);
}

.sizes h3 {
    font-size: var(--t18);
    font-variation-settings: 'opsz' 16;
}

.sizes .price {
    font-size: clamp(24px, 2.4vw, 32px);
}

.note {
    margin-top: var(--s48);
    border: 1px solid var(--border-2);
    border-radius: var(--r-card);
    padding: var(--s48);
    background: linear-gradient(135deg, rgba(212, 175, 55, 0.05), transparent 55%);
}

.note h3 {
    font-size: var(--t20);
    font-variation-settings: 'opsz' 18;
    margin-bottom: var(--s16);
}

/* --- Booking: the number is the interface --------------------------------- */
.dial {
    display: block;
    font-family: var(--display);
    font-weight: 800;
    font-size: clamp(40px, 7.6vw, 96px);
    line-height: 0.94;
    letter-spacing: -0.035em;
    font-variation-settings: 'opsz' 88;
    /* Big numbers — fourth and final sanctioned gradient use on this page. */
    background: var(--grad);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    margin: var(--s24) 0 var(--s32);
    /* The number is the primary action; it needs to be a comfortable tap target
       on its own, not just a styled string. */
    padding: var(--s8) 0;
}

.facts {
    display: grid;
    gap: 0;
    margin-top: var(--s48);
}

.facts__row {
    display: grid;
    grid-template-columns: 180px 1fr;
    gap: var(--s24);
    padding: var(--s24) 0;
    border-top: 1px solid var(--border);
}

.facts__row dt {
    font-family: var(--mono);
    font-size: var(--t11);
    text-transform: uppercase;
    letter-spacing: 0.24em;
    color: var(--faint);
}

.facts__row dd {
    font-size: var(--t14);
    color: var(--muted);
}

.facts__row dd a {
    color: var(--accent);
}

/* --- Privacy: numbered prose ---------------------------------------------- */
.policy {
    display: grid;
    grid-template-columns: 88px 1fr;
    gap: var(--s32);
    padding: var(--s48) 0;
    border-top: 1px solid var(--border);
}

.policy__num {
    font-family: var(--mono);
    font-size: var(--t12);
    letter-spacing: 0.24em;
    color: var(--accent);
    padding-top: var(--s8);
}

.policy h2 {
    font-size: clamp(22px, 2.4vw, 30px);
    font-variation-settings: 'opsz' 24;
    margin-bottom: var(--s16);
}

.policy p,
.policy li {
    font-size: var(--t14);
    color: var(--muted);
    max-width: 58ch;
}

.policy ul {
    margin-top: var(--s16);
    display: grid;
    gap: var(--s8);
}

.policy li {
    padding-left: var(--s24);
    position: relative;
}

.policy li::before {
    content: '';
    position: absolute;
    left: 0;
    top: 10px;
    width: 8px;
    height: 1px;
    background: var(--accent);
}

.policy a {
    color: var(--accent);
    text-decoration: underline;
    text-underline-offset: 4px;
}

/* =============================================================================
   AWARD
   The badge is a third party's mark (Craig Press) and is reproduced in its own
   colours — dark navy and gold on white. It cannot be recoloured to suit the
   page, and its navy shield would vanish against --bg, so it sits on a light
   plaque. That plaque is the only light surface on the site and it is earned:
   it reads as a physical certificate mounted on a dark wall rather than as a
   stray white box.
   ========================================================================== */

.award__grid {
    display: grid;
    grid-template-columns: minmax(0, 340px) minmax(0, 1fr);
    gap: var(--s64);
    align-items: center;
}

.award__plaque {
    /* var(--text) IS #ffffff — the plaque introduces no value that is not
       already a token in the system. */
    background: var(--text);
    border: 1px solid var(--border-2);
    border-radius: var(--r-card);
    padding: var(--s32);
    /* Gold-tinted lift rather than the standard orange one: the shadow reads as
       the plaque standing off the wall, and gold is the award's own colour. */
    box-shadow: 0 22px 60px -20px rgba(212, 175, 55, 0.34);
}

.award__plaque img {
    width: 100%;
    height: auto;
}

.award__body h2 {
    font-size: clamp(28px, 4vw, 50px);
    font-variation-settings: 'opsz' 32;
    margin: var(--s16) 0 var(--s24);
}

.award__meta {
    margin-top: var(--s32);
    padding-top: var(--s24);
    border-top: 1px solid var(--border);
}

/* =============================================================================
   CLOSING CTA — the calmest section on the page
   ========================================================================== */

.closing {
    text-align: center;
}

.closing h2 {
    font-size: clamp(32px, 5.2vw, 66px);
    font-variation-settings: 'opsz' 56;
    margin: var(--s24) 0;
}

.closing .prose {
    margin: 0 auto var(--s48);
}

.closing .eyebrow {
    justify-content: center;
}

/* =============================================================================
   FOOTER
   ========================================================================== */

.footer {
    border-top: 1px solid var(--border);
    background: var(--bg);
    padding: var(--s96) 0 var(--s32);
}

.footer__grid {
    display: grid;
    grid-template-columns: 1.6fr 1fr 1fr 1fr;
    gap: var(--s48);
}

.footer h3 {
    font-size: var(--t18);
    font-variation-settings: 'opsz' 16;
    margin-bottom: var(--s16);
}

.footer h4 {
    font-family: var(--mono);
    font-size: var(--t11);
    font-weight: 400;
    text-transform: uppercase;
    letter-spacing: 0.28em;
    color: var(--faint);
    margin-bottom: var(--s24);
}

.footer p {
    font-size: var(--t14);
    color: var(--muted);
    max-width: 42ch;
}

.footer__links {
    display: grid;
    gap: var(--s12);
}

.footer__links a,
.footer address {
    font-family: var(--mono);
    font-style: normal;
    font-size: var(--t12);
    letter-spacing: 0.1em;
    color: var(--muted);
    transition: color 0.2s var(--ease);
}

.footer__links a:hover,
.footer__links a:focus-visible {
    color: var(--accent);
}

.footer address {
    display: grid;
    gap: var(--s12);
    line-height: 1.7;
}

.footer address a {
    color: var(--accent-2);
}

.footer address i {
    color: var(--accent);
    width: 14px;
    margin-right: var(--s8);
}

.social {
    display: flex;
    gap: var(--s12);
    margin-top: var(--s24);
}

.social a {
    width: 38px;
    height: 38px;
    display: grid;
    place-items: center;
    border: 1px solid var(--border);
    border-radius: var(--r-sm);
    color: var(--muted);
    transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}

.social a:hover,
.social a:focus-visible {
    color: var(--accent);
    border-color: var(--border-hi);
}

.footer__bottom {
    margin-top: var(--s64);
    padding-top: var(--s24);
    border-top: 1px solid var(--border);
    display: flex;
    flex-wrap: wrap;
    gap: var(--s16);
    justify-content: space-between;
}

.footer__bottom p {
    font-family: var(--mono);
    font-size: var(--t11);
    text-transform: uppercase;
    letter-spacing: 0.14em;
    color: var(--faint);
    max-width: none;
}

/* =============================================================================
   MOTION SUPPORT
   ========================================================================== */

/* Line masks are created by JS at runtime, not declared here — a hidden initial
   state in CSS would leave every heading invisible if the GSAP CDN is blocked. */
.line-mask {
    display: block;
    overflow: hidden;
}

.line-mask > span {
    display: block;
}

/* =============================================================================
   RESPONSIVE — 1020 / 920 / 700
   ========================================================================== */

@media (max-width: 1020px) {
    .badge-ring {
        display: none;
    }

    .spangrid {
        grid-template-columns: repeat(2, 1fr);
    }

    .spangrid > :nth-child(1),
    .spangrid > :nth-child(4) {
        grid-column: span 2;
    }

    .sizes {
        grid-template-columns: repeat(2, 1fr);
    }

    .footer__grid {
        grid-template-columns: 1fr 1fr;
    }
}

@media (max-width: 920px) {
    /* The palette takes over as the mobile menu — but only when JS is running
       to open it. Without JS the links stay in the bar and the button goes. */
    body.js .nav__links {
        display: none;
    }

    body.js .kbd-btn__label--kbd {
        display: none;
    }

    body.js .kbd-btn__label--menu {
        display: inline;
    }

    body:not(.js) .nav {
        height: auto;
        padding: var(--s12) 0;
    }

    body:not(.js) .nav__inner {
        flex-wrap: wrap;
        gap: var(--s16);
    }

    body:not(.js) .nav__links {
        gap: var(--s24);
        margin-left: 0;
    }

    .nav__phone {
        display: none;
    }

    /* The overlap unstacks completely — a lapped corner at this width just
       looks like a layout bug. */
    .overlap__a,
    .overlap__b {
        grid-column: 1 / -1;
    }

    .overlap__a {
        padding-right: var(--s32);
    }

    .overlap__b {
        margin-top: var(--s24);
    }

    .hero__media {
        /* fixed attachment repaints the whole layer on every scroll frame on
           iOS and stutters badly. */
        background-attachment: scroll;
    }

    .bento__cell {
        grid-column: span 4;
    }

    /* Below this the plaque column starves the prose beside it. Stacking also
       caps the badge so it does not become the largest thing on the page. */
    .award__grid {
        grid-template-columns: 1fr;
        gap: var(--s32);
    }

    .award__plaque {
        max-width: 320px;
    }

    /* Side-by-side needs real width to be worth it; below this the two columns
       squeeze the measure down to a few words per line. */
    .bento__cell--wide {
        grid-template-columns: 1fr;
        gap: var(--s16);
    }

    .bento__cell--wide .card__icon,
    .bento__cell--wide h3,
    .bento__cell--wide > p {
        grid-column: 1;
        grid-row: auto;
    }

    .footer__grid {
        gap: var(--s32);
    }
}

@media (max-width: 700px) {
    .container,
    .nav__inner {
        padding: 0 var(--s16);
    }

    /* Spans are reset explicitly rather than left to inherit. A span-2 surviving
       into a 1-column grid creates a phantom second column and the whole block
       falls out of alignment. */
    .spangrid,
    .sizes,
    .bento,
    .overlap {
        grid-template-columns: 1fr;
        display: grid;
    }

    .spangrid > *,
    .sizes > *,
    .bento__cell,
    .bento__cell--wide,
    .overlap__a,
    .overlap__b {
        grid-column: auto;
    }

    /* These repeat the 1020 selectors on purpose. `.spangrid > *` above cannot
       clear their span: `.spangrid > :nth-child(1)` scores (0,2,0) against its
       (0,1,0), so the span-2 from the 1020 block survives into this 1-column
       grid, opens a phantom second column, and the cards after the wide one get
       placed into it off-screen. Matching the specificity is what actually
       resets them. */
    .spangrid > :nth-child(1),
    .spangrid > :nth-child(4),
    .spangrid__cta {
        grid-column: auto;
        padding: var(--s32) var(--s24) var(--s48);
    }

    .overlap__a {
        padding-right: 0;
    }

    .hero {
        min-height: auto;
        padding: var(--s96) 0 var(--s64);
    }

    /* Four meta items will not sit on one line here, and as a flex row they wrap
       3 + 1, orphaning the last one. A 2x2 grid keeps the block square. */
    .hero__meta {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: var(--s24);
    }

    /* On a narrow, tall hero `cover` scales to fit the *height*, so the whole
       image — wordmark included — comes back into frame regardless of position.
       Oversizing to 145% of the hero height re-crops it out, and 62% horizontal
       keeps the dog's face in view rather than the mountains. */
    .hero__media {
        background-size: auto 145%;
        background-position: 62% 8%;
    }

    /* Left-weighting the scrim only works when there is width to weight. At
       phone size the crop is tight, so go back to an even wash. */
    .hero__media::after {
        background: linear-gradient(
            180deg,
            rgba(15, 15, 15, 0.82) 0%,
            rgba(15, 15, 15, 0.74) 45%,
            rgba(15, 15, 15, 0.98) 100%
        );
    }

    .rail__row {
        grid-template-columns: 1fr;
        gap: var(--s4);
        padding-left: var(--s24);
    }

    .rail__day {
        text-align: left;
    }

    .rail__time {
        grid-column: 1;
    }

    .rail__line {
        left: 3px;
    }

    .rail__row::before {
        left: 0;
        top: 22px;
    }

    .facts__row,
    .policy {
        grid-template-columns: 1fr;
        gap: var(--s8);
    }

    .footer__grid {
        grid-template-columns: 1fr;
    }

    .btn {
        width: 100%;
        justify-content: center;
    }

    .hero__actions {
        flex-direction: column;
    }
}

/* =============================================================================
   REDUCED MOTION
   Not a blanket kill: durations go to zero, but elements that would be stranded
   mid-state are removed outright and masked lines are unmasked so nothing ends
   up clipped at rest.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
    *,
    *::before,
    *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }

    html {
        scroll-behavior: auto;
    }

    /* Nothing drives these, so they would sit at 0% and 0 scale forever. */
    .progress,
    .badge-ring {
        display: none;
    }

    /* JS skips creating masks under reduced motion, but if a mask already
       exists from a resize, an unset overflow keeps the text visible. */
    .line-mask {
        overflow: visible;
    }

    /* Show the rail in its finished state rather than mid-draw. */
    .rail__line {
        transform: none !important;
    }

    .hero__media {
        background-attachment: scroll;
    }
}
