/* DA MARKET CO -- generated by modules/website.py. Do not edit by hand:
   every value below comes from modules/design.py's light theme. */
:root {
    --s-bg: #FFFFFF;
    --s-raised: #FFFFFF;
    --s-hover: #F5F5F7;
    --s-sunken: #FBFBFD;
    --line: rgba(0, 0, 0, 0.07);
    --line-strong: rgba(0, 0, 0, 0.13);
    --t-primary: #1D1D1F;
    --t-secondary: rgba(29, 29, 31, 0.68);
    --t-tertiary: rgba(29, 29, 31, 0.46);
    --accent: #5B57E8;
    --accent-soft: rgba(91, 87, 232, 0.10);
    --ok: #34C77B;
    --warn: #E0A33E;
    --err: #E5484D;
    --sh-1: 0 1px 2px rgba(0, 0, 0, 0.05);
    --sh-2: 0 4px 16px rgba(0, 0, 0, 0.07);
    --sh-3: 0 12px 40px rgba(0, 0, 0, 0.10);
    --r-sm: 8px;
    --r-md: 12px;
    --r-lg: 16px;
    --ease: cubic-bezier(0.32, 0.72, 0, 1);
    --fast: 0.16s;
    --base: 0.24s;
    --measure: 1220px;
    --gutter: clamp(1.25rem, 4vw, 3rem);
    --font: -apple-system, BlinkMacSystemFont, "Segoe UI", system-ui, Roboto, Helvetica, Arial, sans-serif;

    /* ---------- Motion system ----------
       One shared vocabulary for every animation in this file, so timing and
       hierarchy stay legible instead of each effect inventing its own
       numbers. `--ease` (from design.py) is the signature curve, reused for
       every hover/press micro-interaction; `--ease-out` is for things
       entering the page (a confident decelerate, not a wobble); `--ease-flow`
       is for continuous/looping motion (ticker, ambient drift) where a
       constant rate reads as calmer than an eased one. Durations step in a
       fixed scale -- micro < reveal < entrance < cinematic -- so an element's
       speed signals its weight: small UI reacts instantly, a hero beat takes
       its time. `--stagger` is the one cascade unit every list/grid reveal
       multiplies by its own index (`--i`), so every choreographed sequence in
       the site shares the same rhythm. */
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --ease-flow: cubic-bezier(0.45, 0, 0.55, 1);
    --dur-micro: 0.18s;
    --dur-reveal: 0.5s;
    --dur-entrance: 0.85s;
    --dur-cinematic: 1.3s;
    --stagger: 60ms;

    /* Runtime values SCRIPT overwrites on documentElement (cursor position as
       a 0-1 fraction of the viewport, and scroll progress for the one browser
       branch with no native scroll-timeline) -- declared here with inert
       defaults so a page that never runs that JS still resolves cleanly. */
    --px: 0.5;
    --py: 0.5;
    --scroll-progress: 0;
}

*, *::before, *::after { box-sizing: border-box; }

html { scroll-behavior: smooth; }
@media (prefers-reduced-motion: reduce) { html { scroll-behavior: auto; } }

/* ---------- Page transitions ----------
   Native cross-document View Transitions: the browser cross-fades outgoing
   and incoming pages, and -- because a card's photo and its product page's
   lead photo share a view-transition-name (set inline, keyed by product id)
   -- that one photo morphs in place instead of cross-fading with everything
   else. Pure CSS, no JS: unsupported browsers just navigate normally, so
   there is no fallback path to maintain. */
@view-transition { navigation: auto; }
::view-transition-old(root), ::view-transition-new(root) { animation-duration: 0.32s; }

body {
    margin: 0;
    background: var(--s-bg);
    color: var(--t-primary);
    font-family: var(--font);
    font-size: 16px;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* height:auto is load-bearing, not tidiness: the width/height attributes on
   each <img> are the real exported pixel dimensions (so the browser reserves
   the right space and the page does not shift as photos load), and without
   height:auto the height attribute is obeyed literally instead. */
img { max-width: 100%; height: auto; display: block; }
a { color: inherit; text-decoration: none; }

/* The `hidden` attribute is only a UA rule (`[hidden] { display: none }`),
   so any author `display:` beats it. `.card` sets `display: flex`, which made
   filtering silently no-op: the count said "1 item" while all five stayed on
   screen. Found by clicking the category chips, not by reading the code. */
[hidden] { display: none !important; }

/* Keyboard users must be able to see where they are. */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    border-radius: 3px;
}

.skip {
    position: absolute;
    left: -9999px;
    top: 0;
    background: var(--accent);
    color: #fff;
    padding: 0.7rem 1.1rem;
    z-index: 100;
}
.skip:focus { left: 0; }

/* ---------- Custom cursor ----------
   A small inverted dot, not a full cursor replacement -- the real system
   cursor stays for accuracy, this rides a few pixels behind it as a hover
   cue. mix-blend-mode:difference means one dot reads correctly over any
   part of the page, light or dark art, with no per-surface colour to
   maintain. Hover-only (a touch device has no cursor to enhance) and
   removed outright under reduced motion. */
.cursor {
    display: none;
    position: fixed;
    left: 0;
    top: 0;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    background: #fff;
    mix-blend-mode: difference;
    transform: translate(-50%, -50%);
    pointer-events: none;
    transition: width 0.25s var(--ease), height 0.25s var(--ease);
    z-index: 400;
}
@media (hover: hover) { .js .cursor { display: block; } }
.cursor.is-active { width: 44px; height: 44px; }

/* ---------- First-load curtain ----------
   A brief wordmark beat on a true first visit only -- not on every internal
   click, which would fight the View Transition navigation below rather than
   complement it. `sessionStorage` (set by SCRIPT, checked synchronously by
   the inline head script in _document() before first paint) remembers a
   visit for the tab's session, so the curtain never repeats itself and never
   flashes on a repeat visit. Hidden outright without JS or under reduced
   motion -- see the two overrides near the bottom of this stylesheet. */
.intro-veil { display: none; }
.js:not(.no-intro) .intro-veil {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 300;
    pointer-events: none;
}
/* Two halves of one curtain, told apart by CSS adjacency (+) rather than a
   second class -- they part in opposite directions on dismissal, so the
   page opens rather than simply fading in. */
.intro-panel {
    position: absolute;
    inset: 0 50% 0 0;
    background: var(--s-bg);
    transition: transform var(--dur-cinematic) var(--ease-flow);
}
.intro-panel + .intro-panel { inset: 0 0 0 50%; }
.js.intro-done .intro-panel { transform: translateX(-100%); }
.js.intro-done .intro-panel + .intro-panel { transform: translateX(100%); }
.intro-mark {
    position: absolute;
    top: 50%;
    left: 50%;
    transform: translate(-50%, -50%) scale(1.1);
    font-size: clamp(1.3rem, 4.5vw, 2.2rem);
    font-weight: 650;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    opacity: 0;
    animation: intro-mark-in 0.7s var(--ease-out) 0.15s forwards;
    transition: opacity 0.4s ease, transform var(--dur-cinematic) var(--ease-flow);
}
.js.intro-done .intro-mark { opacity: 0; transform: translate(-50%, -50%) scale(0.9); }
@keyframes intro-mark-in { to { opacity: 1; transform: translate(-50%, -50%) scale(1); } }

.wrap {
    max-width: var(--measure);
    margin: 0 auto;
    padding-inline: var(--gutter);
}

/* ---------- Header ---------- */
.site-head {
    position: sticky;
    top: 0;
    z-index: 50;
    background: color-mix(in srgb, var(--s-bg) 88%, transparent);
    backdrop-filter: saturate(140%) blur(8px);
    border-bottom: 1px solid var(--line);
    transition: box-shadow 0.2s var(--ease), transform 0.4s var(--ease),
                backdrop-filter 0.3s var(--ease), min-height 0.25s var(--ease);
    view-transition-name: site-header;
}
/* Set by SCRIPT once the page has scrolled past the top -- a quiet cue that
   the header is now floating over content, not a second visual language. It
   also compresses a little and thickens its own blur, so "floating over
   content" reads as a real material change, not just a shadow. */
.site-head.scrolled { box-shadow: var(--sh-1); backdrop-filter: saturate(160%) blur(14px); }
.site-head.scrolled .wrap { min-height: 56px; }
/* Set by SCRIPT while scrolling down past one header-height -- the header
   steps out of the way of reading, then returns the instant you scroll back
   up. Skipped entirely under reduced motion (see the media block below), so
   it never fights a user who has asked for a still interface. */
.site-head.hide-nav { transform: translateY(-100%); }
.site-head .wrap {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    min-height: 68px;
}
.wordmark {
    font-size: 1.02rem;
    font-weight: 650;
    letter-spacing: 0.16em;
    text-transform: uppercase;
}
.site-nav { position: relative; display: flex; gap: 1.6rem; font-size: 0.9rem; }
.site-nav a {
    position: relative;
    color: var(--t-secondary);
    padding-block: 0.35rem;
    transition: color 0.16s ease;
}
/* A static underline marks the current page without JS; the sliding bar
   below (.nav-indicator, JS-positioned) owns the hover state instead of a
   second underline per link, so the two never draw at once. */
.site-nav a::after {
    content: "";
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
    height: 1px;
    background: var(--accent);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform 0.3s var(--ease);
}
.site-nav a:hover, .site-nav a[aria-current="page"] { color: var(--t-primary); }
.site-nav a[aria-current="page"]::after { transform: scaleX(1); }
/* One shared bar that glides between links on hover (JS sets left/width) --
   a liquid-menu feel a per-link underline can't produce alone. */
.nav-indicator {
    position: absolute;
    left: 0;
    bottom: 0;
    height: 1px;
    width: 0;
    background: var(--accent);
    opacity: 0;
    transition: left 0.3s var(--ease), width 0.3s var(--ease), opacity 0.2s ease;
    pointer-events: none;
}
.js .nav-indicator { opacity: 1; }
@media (max-width: 620px) { .site-nav { gap: 1rem; font-size: 0.84rem; } }

/* Anchor targets land under the sticky header without this -- #about and
   #contact are real in-page jumps, not just decoration on the nav links. */
section[id] { scroll-margin-top: 88px; }

/* ---------- Type ---------- */
.eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.7rem;
    font-weight: 650;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--t-tertiary);
}
/* The one accent mark repeated on every eyebrow label sitewide -- hero,
   section heads, the shop count, the back-link -- so the accent reads as a
   system rather than a single decorative effect used once. */
.eyebrow::before { content: ""; width: 14px; height: 2px; background: var(--accent); flex: none; }

h1, h2, h3 { margin: 0; line-height: 1.08; }
/* A distinctive display face for the two headline sizes only -- body copy,
   nav, buttons and prices all stay on --font. Pairing an editorial serif
   with the system sans is what separates "catalog" from "SaaS landing page"
   without loading a webfont. */
h1, h2 {
    font-family: Georgia, "Iowan Old Style", "Palatino Linotype", Palatino, serif;
    font-weight: 600;
    letter-spacing: -0.008em;
}
h1 { font-size: clamp(2.8rem, 8.5vw, 7.2rem); line-height: 0.98; letter-spacing: -0.018em; text-wrap: balance; }
h2 { font-size: clamp(1.6rem, 3.4vw, 2.4rem); line-height: 1.1; }
h3 { font-size: 1.05rem; font-weight: 620; letter-spacing: -0.012em; line-height: 1.3; }
p { margin: 0; }
.lede { font-size: clamp(1rem, 1.6vw, 1.15rem); color: var(--t-secondary); max-width: 46ch; }

/* ---------- Hero ---------- */
.hero { padding-block: clamp(4rem, 12vw, 8.5rem) clamp(3rem, 7vw, 5rem); position: relative; overflow: hidden; isolation: isolate; }
.hero .eyebrow { margin-bottom: 1.6rem; }
.hero h1 { max-width: 11ch; }
.hero .lede { margin-top: 1.6rem; }
.hero-actions { display: flex; flex-wrap: wrap; gap: 0.8rem; margin-top: 2.4rem; }
.hero-stats {
    display: flex;
    flex-wrap: wrap;
    gap: clamp(2rem, 6vw, 4.5rem);
    margin-top: clamp(3rem, 7vw, 5rem);
    padding-top: 2rem;
    border-top: 1px solid var(--line);
}
.hero-stats .n { font-size: 1.9rem; font-weight: 620; letter-spacing: -0.03em; }
.hero-stats .k { font-size: 0.78rem; color: var(--t-tertiary); margin-top: 0.2rem; }

/* Asymmetric two-column composition: copy first, one real specimen photo
   second -- not a centered text-only SaaS hero. Collapses to one column
   (photo above copy) once there is no room for both. */
.hero-grid {
    display: grid;
    grid-template-columns: 1fr 1.1fr;
    gap: clamp(2.5rem, 6vw, 5rem);
    align-items: center;
}
@media (max-width: 880px) {
    .hero-grid { grid-template-columns: 1fr; }
    .hero-visual { order: -1; max-width: 22rem; width: 100%; margin-inline: auto; }
}

/* ---------- Hero atmosphere: a drafting-table grid, not a gradient blob ----------
   This shop's whole premise is real photographs of real objects, so the
   backdrop reads as a light table rather than a generic ambient glow. Pure
   CSS, static (no per-frame repaint), masked so it fades out rather than
   ending on a hard edge. */
.hero::before {
    content: "";
    position: absolute;
    inset: -10% -10% auto -10%;
    height: 140%;
    background-image:
        linear-gradient(var(--line) 1px, transparent 1px),
        linear-gradient(90deg, var(--line) 1px, transparent 1px);
    background-size: 56px 56px;
    -webkit-mask-image: radial-gradient(ellipse 65% 55% at 28% 32%, black 0%, transparent 72%);
    mask-image: radial-gradient(ellipse 65% 55% at 28% 32%, black 0%, transparent 72%);
    z-index: -1;
    pointer-events: none;
}
/* A few pixels of native scroll-linked drift on the light-table backdrop --
   no JS, no scroll listener. Unsupported browsers just keep the static
   version above; unlike most of this file's motion, there is no JS fallback
   to gate reduced motion, so it is skipped entirely without that preference. */
@media (prefers-reduced-motion: no-preference) {
    @supports (animation-timeline: scroll()) {
        .hero::before {
            animation: hero-drift auto ease-out;
            animation-timeline: scroll(root);
            animation-range: 0 60vh;
        }
        @keyframes hero-drift {
            from { transform: translateY(0); }
            to { transform: translateY(4%); }
        }
        /* A second layer at roughly half the backdrop's rate and the
           opposite direction: real depth separation (near photo, far grid)
           rather than one flat plane moving. */
        .hero-visual {
            animation: hero-visual-drift auto ease-out;
            animation-timeline: scroll(root);
            animation-range: 0 60vh;
        }
        @keyframes hero-visual-drift {
            from { transform: translateY(0); }
            to { transform: translateY(-2%); }
        }
        /* Large type in motion: the headline settles a size tighter as the
           hero scrolls past, so the page's biggest element is not the one
           thing standing still. */
        .hero h1 {
            animation: hero-type-settle auto ease-out;
            animation-timeline: scroll(root);
            animation-range: 0 50vh;
        }
        @keyframes hero-type-settle {
            from { transform: none; }
            to { transform: translateY(-2%) scale(0.95); }
        }
    }
}

/* ---------- Ambient idle motion ----------
   A very low-opacity gradient that drifts on its own, slowly, forever --
   the one piece of motion on the page with no trigger at all, so the page
   still reads as alive when nothing is being scrolled, hovered or clicked.
   Fixed and behind everything; a pure CSS keyframe loop, no JS, no per-frame
   cost. */
.ambient {
    position: fixed;
    inset: -20%;
    z-index: -1;
    pointer-events: none;
    background: radial-gradient(45rem 45rem at center, var(--accent-soft), transparent 62%);
    background-repeat: no-repeat;
    background-size: 160% 160%;
    background-position: 10% 10%;
    opacity: 0.6;
}
@media (prefers-reduced-motion: no-preference) {
    .ambient { animation: ambient-drift 24s var(--ease-flow) infinite alternate; }
    @keyframes ambient-drift {
        from { background-position: 10% 10%; }
        to { background-position: 75% 65%; }
    }
}

/* ---------- Background field: the page's atmosphere, not a blank canvas ----------
   One fixed layer behind .ambient's glow: a light-table grid (the hero's own
   motif, now sitewide) plus three outlined shapes and a grain layer, each
   moving at its own rate as the page scrolls -- real depth separation rather
   than one flat backdrop. Native `scroll(root)` drives it where supported; a
   `--scroll-progress` custom property (set by SCRIPT's rAF loop, only when
   the browser lacks scroll-timelines at all) drives the same transforms
   where it is not. Both branches, and the property itself, live only under
   `no-preference` -- reduced motion leaves every layer in place, just still. */
.bg-field { position: fixed; inset: 0; z-index: -2; overflow: hidden; pointer-events: none; }
.bg-grid {
    position: absolute;
    inset: -10%;
    background-image:
        linear-gradient(var(--line) 1px, transparent 1px),
        linear-gradient(90deg, var(--line) 1px, transparent 1px);
    background-size: 72px 72px;
    opacity: 0.5;
    -webkit-mask-image: radial-gradient(ellipse 90% 70% at 50% 30%, black 0%, transparent 78%);
    mask-image: radial-gradient(ellipse 90% 70% at 50% 30%, black 0%, transparent 78%);
}
.bg-noise {
    position: absolute;
    inset: -10%;
    background-image: url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A//www.w3.org/2000/svg%22%20width%3D%22140%22%20height%3D%22140%22%3E%3Cfilter%20id%3D%22n%22%3E%3CfeTurbulence%20type%3D%22fractalNoise%22%20baseFrequency%3D%220.85%22%20numOctaves%3D%222%22%20stitchTiles%3D%22stitch%22/%3E%3CfeColorMatrix%20type%3D%22matrix%22%20values%3D%220%200%200%200%200%20%200%200%200%200%200%20%200%200%200%200%200%20%200%200%200%200.4%200%22/%3E%3C/filter%3E%3Crect%20width%3D%22100%25%22%20height%3D%22100%25%22%20filter%3D%22url%28%23n%29%22/%3E%3C/svg%3E");
    background-size: 140px 140px;
    opacity: 0.5;
    mix-blend-mode: overlay;
}
.bg-shape { position: absolute; border: 1px solid var(--line-strong); }
.bg-shape-a { width: 38vmax; height: 38vmax; border-radius: 50%; top: -12vmax; right: -14vmax; }
.bg-shape-b { width: 26vmax; height: 26vmax; top: 55vh; left: -10vmax; transform: rotate(12deg); }
.bg-shape-c { width: 16vmax; height: 16vmax; border-radius: 50%; top: 130vh; right: 6vw; border-color: var(--accent-soft); }
@media (prefers-reduced-motion: no-preference) {
    @supports (animation-timeline: scroll()) {
        .bg-grid { animation: bg-grid-drift auto linear; animation-timeline: scroll(root); animation-range: 0 100%; }
        @keyframes bg-grid-drift { from { transform: translateY(0) rotate(0deg); } to { transform: translateY(-10%) rotate(0.8deg); } }
        .bg-shape-a { animation: bg-shape-a-drift auto linear; animation-timeline: scroll(root); animation-range: 0 100%; }
        @keyframes bg-shape-a-drift { from { transform: translate(0, 0); } to { transform: translate(-8%, -22%); } }
        .bg-shape-b { animation: bg-shape-b-drift auto linear; animation-timeline: scroll(root); animation-range: 0 100%; }
        @keyframes bg-shape-b-drift { from { transform: rotate(12deg) translate(0, 0); } to { transform: rotate(38deg) translate(10%, -32%); } }
        .bg-shape-c { animation: bg-shape-c-drift auto linear; animation-timeline: scroll(root); animation-range: 0 100%; }
        @keyframes bg-shape-c-drift { from { transform: translate(0, 0); } to { transform: translate(-6%, -16%); } }
    }
    @supports not (animation-timeline: scroll()) {
        .bg-grid { transform: translateY(calc(var(--scroll-progress, 0) * -10%)) rotate(calc(var(--scroll-progress, 0) * 0.8deg)); }
        .bg-shape-a { transform: translate(calc(var(--scroll-progress, 0) * -8%), calc(var(--scroll-progress, 0) * -22%)); }
        .bg-shape-b { transform: rotate(calc(12deg + var(--scroll-progress, 0) * 26deg)) translate(calc(var(--scroll-progress, 0) * 10%), calc(var(--scroll-progress, 0) * -32%)); }
        .bg-shape-c { transform: translate(calc(var(--scroll-progress, 0) * -6%), calc(var(--scroll-progress, 0) * -16%)); }
    }
}
/* A few pixels of cursor-parallax on the two nearest shapes -- `translate` is
   its own CSS property, independent of the `transform` the animation above
   drives, so the two compose instead of one silently overriding the other.
   SCRIPT sets --px/--py from the same rAF loop the custom cursor already
   uses; hover-only, so a touch device never gets a stuck offset. */
@media (hover: hover) and (prefers-reduced-motion: no-preference) {
    .js .bg-shape-a { translate: calc((var(--px, 0.5) - 0.5) * -26px) calc((var(--py, 0.5) - 0.5) * -26px); }
    .js .bg-shape-c { translate: calc((var(--px, 0.5) - 0.5) * 18px) calc((var(--py, 0.5) - 0.5) * 18px); }
}

/* ---------- Ticker: the three principles as a moving band ----------
   Content, not decoration -- it restates the same three claims the About
   section makes in full, so it is aria-hidden rather than a second,
   redundant announcement for screen readers. Duplicated once so the loop is
   seamless: at -50% the track has scrolled exactly one full copy. */
.ticker {
    border-block: 1px solid var(--line);
    overflow: hidden;
    background: var(--s-sunken);
}
.ticker-track {
    display: flex;
    width: max-content;
    gap: 3.5rem;
    padding-block: 1rem;
    animation: ticker-scroll 28s linear infinite;
}
.ticker:hover .ticker-track { animation-play-state: paused; }
.ticker-track span {
    font-size: 0.78rem;
    font-weight: 650;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--t-tertiary);
    white-space: nowrap;
}
.ticker-track span::after { content: "\2014"; margin-left: 3.5rem; color: var(--line-strong); }
@keyframes ticker-scroll {
    from { transform: translateX(0); }
    to { transform: translateX(-50%); }
}

/* ---------- Signature motif: archival specimen brackets ----------
   One reusable mark, not four competing ones: corner brackets, the way a
   museum label or an inspection tag frames the thing it is describing.
   Used only where a real photograph -- or its honest absence -- is on
   screen: the hero specimen, a product's lead photo, and no-photo states. */
.brackets { position: relative; }
.brackets::before, .brackets::after {
    content: "";
    position: absolute;
    width: 22px;
    height: 22px;
    border: 2px solid var(--accent);
    pointer-events: none;
}
.brackets::before { top: -1px; left: -1px; border-right: none; border-bottom: none; }
.brackets::after { bottom: -1px; right: -1px; border-left: none; border-top: none; }

/* A floating photograph, not a boxed thumbnail: shadow instead of a hairline
   border so the image reads as the hero's real subject, with its caption
   overlaid on the photo itself (the way a gallery wall label sits on the
   frame, not below it) rather than a second row competing for attention. */
.specimen-frame {
    position: relative;
    aspect-ratio: 4 / 5;
    border-radius: var(--r-lg);
    overflow: hidden;
    background: var(--s-sunken);
    box-shadow: var(--sh-3);
    --mx: 50%;
    --my: 50%;
}
.specimen-frame img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 1.1s var(--ease);
}
.specimen-frame:hover img { transform: scale(1.045); }
/* The frame itself, not the img, carries the scroll-linked settle -- the img
   already owns the hover scale above, and the frame has no transform of its
   own at rest, so the two compose instead of one silently winning. */
@media (prefers-reduced-motion: no-preference) {
    @supports (animation-timeline: view()) {
        .specimen-frame { animation: frame-settle linear both; animation-timeline: view(); animation-range: cover; }
        @keyframes frame-settle { from { transform: scale(1.08); } to { transform: scale(1); } }
    }
}
/* The same cursor-spotlight language as the product cards (.card .shot::after),
   reused rather than reinvented, so the hero's one interactive surface speaks
   the same visual dialect as the grid below it. */
.specimen-frame::after {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(280px circle at var(--mx) var(--my), var(--accent-soft), transparent 65%);
    opacity: 0;
    transition: opacity 0.25s var(--ease);
}
.specimen-frame:hover::after { opacity: 1; }
.specimen-frame::after {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 40%;
    background: linear-gradient(to top, rgba(0, 0, 0, 0.55), transparent);
    pointer-events: none;
}
.specimen-cap {
    position: absolute;
    left: 1.3rem;
    right: 1.3rem;
    bottom: 1.15rem;
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
}
.specimen-lot {
    font-size: 0.72rem;
    font-weight: 650;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: #fff;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}
.specimen-title { font-size: 0.84rem; color: rgba(255, 255, 255, 0.85); text-align: right; }

/* ---------- No-photo placeholder ----------
   A diagonal archive-stripe fill plus the signature brackets, not a flat
   grey box -- still unambiguous that no photograph exists. */
.photo-placeholder {
    display: grid;
    place-items: center;
    width: 100%;
    height: 100%;
    padding: 1rem;
    text-align: center;
    line-height: 1.5;
    color: var(--t-tertiary);
    font-size: 0.72rem;
    font-weight: 650;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    background: repeating-linear-gradient(
        45deg,
        var(--s-sunken) 0, var(--s-sunken) 9px,
        var(--s-hover) 9px, var(--s-hover) 10px
    );
}

/* ---------- Word-stagger reveal ----------
   Server-rendered spans carry --i (their word index); the parent still uses
   the ordinary [data-reveal]/.in hook the IntersectionObserver below already
   sets, so no second script hook is needed. Each word both rises and unmasks
   (clip-path, not just opacity), which is what makes it read as type being
   *unveiled* rather than merely fading up. */
.split-reveal span {
    display: inline-block;
    --i: 0;
    clip-path: inset(0 0 -20% 0);
}
.js [data-reveal].split-reveal { opacity: 1; transform: none; }
.js .split-reveal span {
    opacity: 0;
    transform: translateY(100%);
    clip-path: inset(0 0 -20% 0);
    transition: opacity var(--dur-entrance) var(--ease-out),
                transform var(--dur-entrance) var(--ease-out);
    transition-delay: calc(var(--i, 0) * var(--stagger));
}
.js [data-reveal].split-reveal.in span { opacity: 1; transform: none; }

/* ---------- Reveal hierarchy ----------
   Three named tiers, one shared observer. Weight communicates importance:
   `rise` is for section-scale moments (hero copy, section heads) and moves
   further, slower; the bare attribute is the quiet default for body content;
   `mask` uncovers imagery with a clip-path wipe instead of a fade, so a
   photograph feels *revealed* rather than loaded in. */
.js [data-reveal="rise"] { transform: translateY(30px); }
.js [data-reveal="rise"].in {
    transition: opacity var(--dur-entrance) var(--ease-out),
                transform var(--dur-entrance) var(--ease-out);
}
.js [data-reveal="mask"] .shot,
.js [data-reveal="mask"] .lead,
.js [data-reveal="mask"] .specimen-frame {
    clip-path: inset(0 0 100% 0);
    transition: clip-path var(--dur-cinematic) var(--ease-out);
    transition-delay: calc(var(--i, 0) * var(--stagger));
}
.js [data-reveal="mask"].in .shot,
.js [data-reveal="mask"].in .lead,
.js [data-reveal="mask"].in .specimen-frame { clip-path: inset(0 0 0% 0); }
.js [data-reveal="mask"] { transform: translateY(18px); }

/* A fourth tier for section heads inside a chapter band: a diagonal wipe
   instead of a fade, so a tone-change section announces itself differently
   than the plain "rise" used everywhere else. */
.js [data-reveal="wipe"] { clip-path: inset(0 0 0 100%); }
.js [data-reveal="wipe"].in {
    clip-path: inset(0 0 0 0%);
    transition: clip-path var(--dur-cinematic) var(--ease-out);
}

/* A fifth tier: a cinematic settle (shrink + rise together) for the one
   section head that isn't inside a chapter band -- so the page's three
   section entrances (wipe / scale / rise) each choreograph differently
   instead of repeating one gesture three times. */
.js [data-reveal="scale"] { transform: scale(0.94) translateY(14px); }
.js [data-reveal="scale"].in {
    transform: none;
    transition: opacity var(--dur-cinematic) var(--ease-out), transform var(--dur-cinematic) var(--ease-out);
}

/* A thin rule that draws itself in as it enters view -- reuses the eyebrow
   dash's accent colour at sentence scale, so it reads as the same motif
   grown up rather than a new decoration. Static (full-width) without JS or
   under reduced motion; the draw-in is the enhancement, not the content. */
.draw-line { height: 1px; background: var(--line-strong); transform-origin: left; margin-bottom: 1.6rem; }
@media (prefers-reduced-motion: no-preference) {
    @supports (animation-timeline: view()) {
        .draw-line {
            transform: scaleX(0);
            animation: draw-line-in linear both;
            animation-timeline: view();
            animation-range: entry 0% cover 35%;
        }
        @keyframes draw-line-in { to { transform: scaleX(1); } }
    }
}

.js [data-reveal].stagger-1.in { transition-delay: 0.1s; }
.js [data-reveal].stagger-2.in { transition-delay: 0.2s; }
.js [data-reveal].stagger-3.in { transition-delay: 0.3s; }

/* The same accent dash used on every eyebrow (see .eyebrow::before below)
   draws in from nothing rather than appearing at full width -- a small,
   repeated structural beat that ties every section head to the same
   choreography without adding a second animation vocabulary. */
.js [data-reveal] .eyebrow::before { width: 0; transition: width var(--dur-reveal) var(--ease-out) 0.15s; }
.js [data-reveal].in .eyebrow::before { width: 14px; }

/* ---------- Buttons ---------- */
.btn {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.72rem 1.35rem;
    border-radius: var(--r-sm);
    border: 1px solid var(--line-strong);
    font-size: 0.9rem;
    font-weight: 550;
    background: var(--s-bg);
    --mx: 0px;
    --my: 0px;
    transform: translate(var(--mx), var(--my));
    transition: background-color 0.16s ease, border-color 0.16s ease, transform 0.16s var(--ease);
}
.btn:hover { background: var(--s-hover); }
.btn:active { transform: translate(var(--mx), var(--my)) scale(0.985); }
.btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; position: relative; overflow: hidden; }
.btn.primary:hover { filter: brightness(1.08); }
/* A single soft sweep on hover, not a looping shine -- it plays once because
   it is driven by :hover's transform, not a repeating keyframe. */
.btn.primary::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(120deg, transparent 30%, rgba(255, 255, 255, 0.35) 50%, transparent 70%);
    transform: translateX(-120%);
    transition: transform 0.6s var(--ease);
    pointer-events: none;
}
.btn.primary:hover::after { transform: translateX(120%); }

/* ---------- Sections ---------- */
section { padding-block: clamp(3rem, 8vw, 6rem); }
.sec-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1.5rem;
    padding-bottom: 1.4rem;
    margin-bottom: 2.4rem;
    border-bottom: 1px solid var(--line);
}

/* A chapter is a full-bleed tone change on the <section> itself, with an
   ordinary .wrap inside it -- breaks the page into visually distinct bands
   using only the two surface tokens the palette already has. */
.chapter {
    position: relative;
    overflow: hidden;
    isolation: isolate;
    background: var(--s-sunken);
    border-block: 1px solid var(--line);
}
/* The same light-table grid as the hero backdrop, on the tone-change bands
   only -- ties the page's two "chapters" to one motif instead of a flat
   colour swap being the whole idea of a chapter. Drifts on its own scroll
   position (view(), not scroll(root)) so it works regardless of how far
   down the page the section sits. */
.chapter::before {
    content: "";
    position: absolute;
    inset: -20% -10%;
    background-image:
        linear-gradient(var(--line) 1px, transparent 1px),
        linear-gradient(90deg, var(--line) 1px, transparent 1px);
    background-size: 64px 64px;
    z-index: -1;
    pointer-events: none;
}
@media (prefers-reduced-motion: no-preference) {
    @supports (animation-timeline: view()) {
        .chapter::before {
            animation: chapter-parallax linear both;
            animation-timeline: view();
            animation-range: cover;
        }
        @keyframes chapter-parallax {
            from { transform: translateY(-6%); }
            to { transform: translateY(6%); }
        }
    }
}

/* ---------- About: asymmetric editorial layout, not equal-width cards ----------
   The heading column stays put while the list scrolls past it on wide
   screens; on narrow ones it just stacks. Numbering is legitimate here --
   these three steps are a real, ordered process, not a decorative 01/02/03. */
.about-grid { display: grid; grid-template-columns: 0.8fr 1.2fr; gap: clamp(2rem, 6vw, 5rem); align-items: start; }
.about-head h2 { margin-top: 0.6rem; }
@media (min-width: 861px) { .about-head { position: sticky; top: 6rem; } }
.about-list { list-style: none; margin: 0; padding: 0; display: grid; }
.about-item {
    display: grid;
    grid-template-columns: 2.6rem 1fr;
    gap: 1.2rem;
    padding-block: 1.8rem;
    border-top: 1px solid var(--line);
    transition: padding-left 0.25s var(--ease);
}
.about-item:last-child { border-bottom: 1px solid var(--line); }
.about-item:hover { padding-left: 0.4rem; }
.about-n { font-size: 0.85rem; font-weight: 650; color: var(--t-tertiary); font-variant-numeric: tabular-nums; padding-top: 0.2rem; }
.about-item h3 { margin: 0; }
.about-item .lede { margin-top: 0.5rem; }
@media (max-width: 860px) { .about-grid { grid-template-columns: 1fr; } }

/* ---------- Product grid ---------- */
.grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(258px, 1fr));
    gap: clamp(1.5rem, 3vw, 2.4rem);
}
/* One featured item breaks the uniform 3-up rhythm on the landing page only
   -- the shop grid stays uniform because it is filtered/searched and a card
   changing size under a live filter would read as a layout bug. */
.grid-feature .card:first-child .shot { aspect-ratio: 16 / 9; }
@media (min-width: 640px) {
    .grid-feature .card:first-child { grid-column: span 2; }
}
.card {
    display: flex;
    flex-direction: column;
    --mx: 50%;
    --my: 50%;
    transition: transform 0.35s var(--ease);
}
.card:hover { transform: translateY(-4px); }
.card .shot {
    position: relative;
    aspect-ratio: 4 / 5;
    overflow: hidden;
    border-radius: var(--r-md);
    background: var(--s-sunken);
    --tilt-x: 0deg;
    --tilt-y: 0deg;
    /* perspective() on the element itself needs no separate parent property
       -- a common, well-supported way to tilt one card without affecting
       its siblings. JS only ever writes --tilt-x/--tilt-y; the fallback of
       0deg here is what keeps a no-JS or reduced-motion render dead flat. */
    transform: perspective(900px) rotateX(var(--tilt-y)) rotateY(var(--tilt-x));
    transition: transform 0.35s var(--ease), box-shadow 0.2s var(--ease);
}
/* No border at rest, by design -- the photograph is the card. A shadow only
   appears on hover, when the card is already lifting off the page. */
.card:hover .shot { box-shadow: var(--sh-3); }
/* A slow entrance zoom, scoped to a wrapper the tilt/hover transforms above
   never touch -- `.shot` already owns the cursor-tilt transform and `img`
   already owns the hover scale, so a scroll-linked scale on either would
   silently replace one effect with the other (animations win the cascade
   over transitions/hover on the same property, whichever declares last).
   `view()` tracks the card's own visibility, so it plays correctly no matter
   how far down the grid the card sits. */
.card .shot-scroll { width: 100%; height: 100%; }
@media (prefers-reduced-motion: no-preference) {
    @supports (animation-timeline: view()) {
        .card .shot-scroll {
            animation: shot-kenburns linear both;
            animation-timeline: view();
            animation-range: cover;
        }
        @keyframes shot-kenburns { from { transform: scale(1.16); } to { transform: scale(1); } }
    }
}
/* A soft spotlight that follows the cursor, positioned by JS-set --mx/--my.
   Confined to .shot (never the title/price text) so it can never soften
   reading contrast. */
.card .shot::after {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(220px circle at var(--mx) var(--my), var(--accent-soft), transparent 65%);
    opacity: 0;
    transition: opacity 0.25s var(--ease);
    pointer-events: none;
}
.card:hover .shot::after { opacity: 1; }
/* Color is earned on approach, not given away at rest -- a duotone hush
   over the catalogue that resolves to the real photograph on hover, so the
   grid reads as an archive until you engage with one piece. */
.card .shot img { filter: grayscale(0.9) contrast(1.05); transition: filter 0.6s var(--ease); }
.card:hover .shot img { filter: grayscale(0) contrast(1); }
/* A cursor-anchored cue rather than a corner badge -- it only exists where
   the pointer already is, so it never competes with the photograph. */
.view-cue {
    position: absolute;
    left: var(--mx);
    top: var(--my);
    transform: translate(-50%, -50%) scale(0.6);
    display: grid;
    place-items: center;
    width: 4.6rem;
    height: 4.6rem;
    border-radius: 50%;
    background: var(--s-bg);
    color: var(--t-primary);
    font-size: 0.72rem;
    font-weight: 650;
    letter-spacing: 0.05em;
    text-transform: uppercase;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.25s var(--ease), transform 0.25s var(--ease);
}
.card:hover .view-cue { opacity: 1; transform: translate(-50%, -50%) scale(1); }
.card .shot img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    transition: transform 0.5s cubic-bezier(0.32, 0.72, 0, 1);
}
.card:hover .shot img { transform: scale(1.035); }
/* A second photo, if one exists, crossfades in on hover -- a quiet way to
   let a card tell more of a product's story without a second click. */
.card .shot img.shot-alt { position: absolute; inset: 0; opacity: 0; }
.card:hover .shot img.shot-alt { opacity: 1; }
.card .meta { padding-top: 0.95rem; display: flex; flex-direction: column; gap: 0.3rem; }
.card .row { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; }
.card .price { font-weight: 620; font-variant-numeric: tabular-nums; white-space: nowrap; }
.card .sub { font-size: 0.82rem; color: var(--t-tertiary); }
.card:hover h3 { color: var(--accent); }
.card h3 { transition: color 0.16s ease; }

/* ---------- Status pills ---------- */
.pill {
    display: inline-flex;
    align-items: center;
    gap: 0.42rem;
    padding: 0.24rem 0.6rem;
    border-radius: 999px;
    border: 1px solid var(--line);
    font-size: 0.72rem;
    font-weight: 550;
    color: var(--t-secondary);
    background: var(--s-bg);
}
.pill .dot { width: 6px; height: 6px; border-radius: 50%; background: var(--line-strong); }
.pill.available .dot { background: var(--ok); }
.pill.sold .dot { background: var(--err); }
.card .shot .pill { position: absolute; left: 0.7rem; top: 0.7rem; }

/* ---------- Filters ---------- */
.filters {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: center;
    margin-bottom: 2rem;
}
.filters input[type="search"] {
    flex: 1 1 240px;
    min-width: 0;
    padding: 0.66rem 0.9rem;
    border-radius: var(--r-sm);
    border: 1px solid var(--line-strong);
    background: var(--s-bg);
    color: var(--t-primary);
    font: inherit;
    font-size: 0.9rem;
}
/* A horizontal rail, not a wrapping row -- category chips scroll and snap
   sideways, the one deliberately horizontal gesture on an otherwise vertical
   page. The trailing gradient hints there is more to scroll to without
   another arrow icon; the mask fades the same way at both ends. */
.chips {
    display: flex;
    gap: 0.45rem;
    overflow-x: auto;
    scroll-snap-type: x proximity;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    padding-block: 0.15rem;
    margin-inline: -0.15rem;
    padding-inline: 0.15rem;
    mask-image: linear-gradient(to right, transparent 0, black 1.25rem, black calc(100% - 1.25rem), transparent 100%);
}
.chips::-webkit-scrollbar { display: none; }
.chip {
    flex: none;
    scroll-snap-align: start;
    padding: 0.42rem 0.85rem;
    border-radius: 999px;
    border: 1px solid var(--line-strong);
    background: var(--s-bg);
    color: var(--t-secondary);
    font: inherit;
    font-size: 0.82rem;
    cursor: pointer;
    transition: background-color 0.16s ease, color 0.16s ease, border-color 0.16s ease, transform 0.16s var(--ease);
}
.chip:active { transform: scale(0.95); }
.chip:hover { background: var(--s-hover); color: var(--t-primary); }
.chip[aria-pressed="true"] {
    background: var(--accent);
    border-color: var(--accent);
    color: #fff;
}

/* ---------- Empty / status states ---------- */
.state {
    border: 1px dashed var(--line-strong);
    border-radius: var(--r-lg);
    padding: clamp(2.5rem, 7vw, 4.5rem) 1.5rem;
    text-align: center;
    color: var(--t-secondary);
}
.state h3 { margin-bottom: 0.5rem; color: var(--t-primary); }
.state p { max-width: 46ch; margin-inline: auto; font-size: 0.92rem; }

/* ---------- Product detail ---------- */
.detail { display: grid; grid-template-columns: 1.05fr 0.95fr; gap: clamp(2rem, 5vw, 4rem); align-items: start; }
@media (max-width: 860px) { .detail { grid-template-columns: 1fr; } }
.gallery { display: flex; flex-direction: column; gap: 0.9rem; }
/* The lead photo is contained, never cropped: this shop's whole promise is
   that you can see the item as it is, including the flaws. Cropping is fine
   on a catalogue thumbnail, where the detail page is one click away. */
.gallery .lead {
    position: relative;
    border-radius: var(--r-lg);
    overflow: hidden;
    background: var(--s-sunken);
    box-shadow: var(--sh-3);
    aspect-ratio: 1;
    display: grid;
    place-items: center;
    --mx: 50%;
    --my: 50%;
}
.gallery .lead img {
    width: 100%;
    height: 100%;
    object-fit: contain;
    transition: transform 0.6s var(--ease), opacity 0.18s ease;
}
.gallery .lead:hover img { transform: scale(1.03); }
.gallery .lead img.is-swapping { opacity: 0; }
/* Same frame/img split as .specimen-frame above -- the container carries the
   scroll settle, the img keeps its own hover scale and swap fade. */
@media (prefers-reduced-motion: no-preference) {
    @supports (animation-timeline: view()) {
        .gallery .lead { animation: frame-settle linear both; animation-timeline: view(); animation-range: cover; }
    }
}
/* Same spotlight language as the hero specimen and the cards -- one cursor
   dialect across the whole site rather than a special case per surface. */
.gallery .lead::after {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(280px circle at var(--mx) var(--my), var(--accent-soft), transparent 65%);
    opacity: 0;
    transition: opacity 0.25s var(--ease);
}
.gallery .lead:hover::after { opacity: 1; }
.gallery .thumbs { display: grid; grid-template-columns: repeat(auto-fill, minmax(88px, 1fr)); gap: 0.6rem; }
.gallery .thumbs img {
    border-radius: var(--r-sm);
    border: 1px solid var(--line);
    aspect-ratio: 1;
    object-fit: cover;
    cursor: pointer;
    transition: border-color 0.16s ease, transform 0.16s var(--ease);
}
.gallery .thumbs img:hover { border-color: var(--accent); transform: translateY(-2px); }
.gallery .thumbs img.is-active { border-color: var(--accent); }
.detail .price-big { font-size: 2rem; font-weight: 620; letter-spacing: -0.03em; font-variant-numeric: tabular-nums; }
/* The catalogue-lot number, printed oversized and quiet -- an auction
   house's own numbering made into a typographic flourish rather than a
   second badge next to the one that already shows availability. */
.detail-head { position: relative; }
.lot-mark {
    position: absolute;
    top: -0.4em;
    right: 0;
    font-size: clamp(3rem, 8vw, 5.5rem);
    font-weight: 650;
    letter-spacing: -0.02em;
    color: var(--s-hover);
    z-index: -1;
    line-height: 1;
    pointer-events: none;
    user-select: none;
}
@media (max-width: 640px) { .lot-mark { display: none; } }
.spec { width: 100%; border-collapse: collapse; margin-top: 1.6rem; font-size: 0.9rem; }
.spec th, .spec td { text-align: left; padding: 0.7rem 0; border-bottom: 1px solid var(--line); vertical-align: top; }
.spec th { color: var(--t-tertiary); font-weight: 500; width: 38%; }
.prose { margin-top: 1.6rem; color: var(--t-secondary); white-space: pre-line; }

/* ---------- Footer ---------- */
.site-foot {
    border-top: 1px solid var(--line);
    padding-block: 3rem 2.5rem;
    margin-top: clamp(3rem, 8vw, 6rem);
    color: var(--t-tertiary);
    font-size: 0.86rem;
}
.foot-grid { display: flex; flex-wrap: wrap; gap: 2.5rem; justify-content: space-between; }
.foot-grid h4 { margin: 0 0 0.7rem; font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--t-tertiary); font-weight: 650; }
.foot-grid a:hover { color: var(--t-primary); }
.foot-grid ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.4rem; }
.foot-bottom { margin-top: 2.5rem; padding-top: 1.4rem; border-top: 1px solid var(--line); display: flex; flex-wrap: wrap; gap: 1rem; justify-content: space-between; }

/* ---------- Scroll reveal ----------
   Opt-in and short. Elements are visible by default and only animate when
   the script marks the document -- so with JavaScript off, or a crawler
   reading the HTML, nothing is hidden. */
.js [data-reveal] { opacity: 0; transform: translateY(10px); }
.js [data-reveal].in {
    opacity: 1;
    transform: none;
    transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
}
@media (prefers-reduced-motion: reduce) {
    .js [data-reveal] { opacity: 1; transform: none; }
    .js [data-reveal].in { transition: none; }
    .card .shot img, .card:hover .shot img { transition: none; transform: none; filter: none; }
    .view-cue { display: none; }
    .ticker { animation: none !important; }
    .ticker-track { animation: none !important; }
    .js .split-reveal span { opacity: 1 !important; transform: none !important; transition: none !important; }
    .card, .card:hover { transition: none; transform: none; }
    .card .shot { transition: none; transform: none !important; }
    .btn.primary::after { transition: none; }
    .btn.primary { transform: none !important; }
    .site-head { transition: none; }
    .specimen-frame img, .specimen-frame:hover img { transition: none; transform: none; }
    .gallery .lead img, .gallery .lead:hover img { transition: none; transform: none; }
    .gallery .thumbs img:hover { transform: none; }
    .about-item { transition: none; }
    ::view-transition-group(*), ::view-transition-old(*), ::view-transition-new(*) { animation: none !important; }
    .js [data-reveal="mask"] .shot,
    .js [data-reveal="mask"] .lead,
    .js [data-reveal="mask"] .specimen-frame { clip-path: none; transition: none; }
    .js .split-reveal span { clip-path: none !important; }
    .js [data-reveal] .eyebrow::before { width: 14px; transition: none; }
    .site-head, .site-head.hide-nav { transform: none !important; transition: none; }
    .specimen-frame::after { transition: none; }
    .gallery .lead::after { transition: none; }
    .site-nav a::after { transition: none; }
    .chips { scroll-behavior: auto; }
    .cursor { display: none !important; }
    .js [data-reveal="wipe"] { clip-path: none !important; }
    .intro-veil { display: none !important; }
}
