/*
 * Mooniez Store - storefront preview.
 *
 * Built to the owner's own aesthetic, not a shop template: near-black
 * monochrome, still film grain, pixel display type, y2k chrome, and cute kept
 * to small doses. Japanese/Korean editorial restraint carried by typography
 * and spacing - the shop is English throughout, no foreign glyphs.
 *
 * Color discipline: grayscale by default. Lilac (#A78BFA) is the only real
 * hue and it marks interactive things - the active page, a price, a filter.
 * That restraint is what stops it tipping into "cute".
 *
 * Structure follows how asset shops actually work: every product is its own
 * page, the catalog is searchable and filterable, and there is a docs section
 * so buyers can read the manual before they buy.
 *
 * Deliberately single-theme - commits to dark and paints every color
 * explicitly, so it holds on any host background.
 */

:root {
    /* Both ends pulled a step toward each other on 2026-08-22: near-white
       text on near-black bleeds at the edges (halation), which is the most
       common reason a dark site is hard to read - dyslexic readers most of
       all. Still a neon night, just a legible one. */
    /* Channel tokens. Every veil, hairline and shadow in the stylesheet is
       drawn from these two, so a theme flips them once rather than in ninety
       separate declarations. */
    --w: 255, 255, 255;
    --star-opacity: 0.85;
    --k: 0, 0, 0;
    --fill-solid: #191426;

    /* Opaque sheets - the nav, popovers, the sticky bar, the highlighted
       plan. They cannot be built from the veil tokens because they have to
       hide the page behind them, so each is its own token and daylight
       repaints them below. */
    --sheet-nav:   linear-gradient(180deg, rgba(16, 16, 22, 0.62), rgba(8, 8, 11, 0.48));
    --sheet-nav-2: linear-gradient(180deg, rgba(14, 14, 20, 0.74), rgba(8, 8, 11, 0.62));
    --sheet-pop:   linear-gradient(180deg, rgba(22, 18, 34, 0.97), rgba(12, 10, 20, 0.96));
    --sheet-bar:   linear-gradient(180deg, rgba(16, 16, 22, 0.66), rgba(8, 8, 11, 0.58));
    --sheet-tip:   rgba(16, 12, 26, 0.98);
    --sheet-best:  linear-gradient(160deg, #241C36 0%, #171227 55%, #120F1E 100%);

    --void:      #100D1A;
    --void-2:    #171423;
    --panel:     rgba(var(--w), 0.042);
    --panel-2:   rgba(var(--w), 0.075);
    --line:      rgba(var(--w), 0.13);
    --line-2:    rgba(var(--w), 0.26);

    --text:      #E9E7F2;
    --muted:     #BCBCC9;
    /* One step brighter than --muted, for text long enough to be read rather
       than glanced at. */
    --text-2:    #D9D9E4;
    --dim:       #8A8A98;

    --lilac:     #A78BFA;
    --lilac-dim: rgba(167, 139, 250, 0.22);

    /* The MoonHaven gradient, the same one the server wears, used here as
       neon: it lights things up, it never fills a surface. Grunge survives
       color as long as color behaves like a sign and not like paint. */
    --violet:    #8B5CF6;
    --pink:      #EC4899;
    --aqua:      #7DD3FC;
    --neon:      linear-gradient(100deg, #8B5CF6 0%, #EC4899 52%, #A78BFA 100%);
    --neon-soft: linear-gradient(100deg, rgba(139, 92, 246, 0.30), rgba(236, 72, 153, 0.22), rgba(167, 139, 250, 0.30));
    --glow:      0 0 1.6rem rgba(236, 72, 153, 0.30), 0 0 3rem rgba(139, 92, 246, 0.22);

    --ok:        #6EE7B7;

    /* Tag colors, deliberately outside the brand gradient - see .tag--fw. */
    --tag-warm:  #FAD989;
    --tag-mint:  #7EE8C5;

    --chrome: linear-gradient(180deg, #FFFFFF 0%, #DADAE4 40%, #75757F 52%, #C6C6D2 64%, #FFFFFF 100%);

    /* The fill inside a gradient rim. Near-opaque on purpose: the rim is two
       backgrounds on one element - fill to the padding box, gradient to the
       border box - and only an opaque fill hides the gradient everywhere
       except the edge. */
    --pill-base: linear-gradient(rgba(26, 21, 42, 0.95), rgba(16, 13, 27, 0.95));

    /* Glass. Kept as tokens so every surface frosts by the same amount and
       the sheet thickness can be tuned in one place. */
    --glass:      rgba(var(--w), 0.055);
    --glass-soft: rgba(var(--w), 0.018);
    --glass-veil: rgba(var(--k), 0.45);
    --blur:       blur(22px) saturate(150%);
    --blur-lite:  blur(14px) saturate(130%);

    --r-lg: 1.15rem;
    --r-md: 0.8rem;
    --r-sm: 0.45rem;

    --ease: cubic-bezier(0.4, 0, 0.2, 1);
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --speed: 0.22s;

    --wrap: 94rem;
    --gutter: clamp(1.25rem, 4vw, 3rem);
    --section-y: clamp(3.5rem, 9vh, 6rem);
    --head-clear: clamp(10rem, 20vh, 13rem);

    --px: 'Pixelify Sans', 'Outfit', system-ui, sans-serif;
}

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

/* The page commits to dark, so the browser has to know it - without this the
   native dropdown list of a <select> opens as white system chrome with our
   pale text on it, which is unreadable. */
html { color-scheme: dark; scroll-behavior: smooth; scroll-padding-top: 11rem; -webkit-text-size-adjust: 100%; }
html[data-mh="light"] { color-scheme: light; --star-opacity: 0; }

/* Daylight. Same design in different weather: the channel tokens invert, the
   surfaces become paper instead of glass over black, and the brand gradient
   darkens a step so it reads ON light rather than glowing over dark.
   The attribute is ours, not `data-theme` - the artifact host stamps that one
   from the viewer's own setting and it must not yank the page around. */
html[data-mh="light"] {
    --w: 24, 18, 40;
    --k: 40, 30, 70;
    --fill-solid: #FFFFFF;

    --void:      #F4F2FA;
    --void-2:    #EAE6F5;
    --panel:     rgba(var(--w), 0.035);
    --panel-2:   rgba(var(--w), 0.065);
    --line:      rgba(var(--w), 0.16);
    --line-2:    rgba(var(--w), 0.30);

    --text:      #1B1630;
    --muted:     #4B4462;
    --text-2:    #322B4A;
    --dim:       #6E667F;

    --lilac:     #6D28D9;
    --lilac-dim: rgba(109, 40, 217, 0.16);

    --violet:    #7C3AED;
    --pink:      #DB2777;
    --aqua:      #0EA5E9;
    --neon:      linear-gradient(100deg, #7C3AED 0%, #DB2777 52%, #8B5CF6 100%);
    --neon-soft: linear-gradient(100deg, rgba(124, 58, 237, 0.16), rgba(219, 39, 119, 0.12), rgba(139, 92, 246, 0.16));
    --glow:      0 0 1.4rem rgba(219, 39, 119, 0.18), 0 0 2.6rem rgba(124, 58, 237, 0.14);

    --ok:        #0F9D6B;
    --tag-warm:  #8A6100;
    --tag-mint:  #0B7A5E;

    --glass:      rgba(255, 255, 255, 0.72);
    --glass-soft: rgba(255, 255, 255, 0.42);
    --glass-veil: rgba(255, 255, 255, 0.55);

    --chrome: linear-gradient(180deg, #6B6478 0%, #2A2438 40%, #8C84A0 52%, #3A3350 64%, #6B6478 100%);
    --pill-base: linear-gradient(#FFFFFF, #F3EEFD);

    --sheet-nav:   linear-gradient(180deg, rgba(255, 255, 255, 0.82), rgba(248, 246, 253, 0.66));
    --sheet-nav-2: linear-gradient(180deg, rgba(255, 255, 255, 0.92), rgba(246, 244, 252, 0.84));
    --sheet-pop:   linear-gradient(180deg, rgba(255, 255, 255, 0.98), rgba(248, 246, 253, 0.97));
    --sheet-bar:   linear-gradient(180deg, rgba(255, 255, 255, 0.9), rgba(246, 244, 252, 0.82));
    --sheet-tip:   rgba(27, 22, 48, 0.96);
    --sheet-best:  linear-gradient(160deg, #FFFFFF 0%, #F6F1FF 55%, #EFE9FC 100%);
}
/* A shadow that lifted a card off black has to sit under it on paper. */
html[data-mh="light"] .item:hover,
html[data-mh="light"] .tier:hover {
    box-shadow: 0 0.9rem 2rem rgba(var(--k), 0.13), 0 0 1.4rem rgba(219, 39, 119, 0.16);
}
/* White on the brand gradient stays white - it sits on color, not on paper. */
html[data-mh="light"] .btn--primary,
html[data-mh="light"] .tier__flag { color: #fff; }

/* A product thumbnail is a white crest on a dark veil. On paper that is a
   white crest on white, so daylight gives the panel a lavender tint to sit
   the moon against. */
html[data-mh="light"] .item__shot,
html[data-mh="light"] .folio__shot,
html[data-mh="light"] .lightbox__shot,
html[data-mh="light"] .media {
    background:
        radial-gradient(85% 100% at 50% 0%, rgba(124, 58, 237, 0.14), transparent 70%),
        linear-gradient(180deg, #EFE9FB, #E4DCF6);
}
html[data-mh="light"] .item__crest,
html[data-mh="light"] .media__mark { opacity: 0.62; }
html[data-mh="light"] .item:hover .item__crest { opacity: 0.85; }

/* The two moons wear metal, and metal has to be dark to read on paper: the
   same ramps, shifted down so the letters keep their shape on white. */
html[data-mh="light"] .tier__name--blue {
    background: linear-gradient(105deg, #0A2E56 0%, #1668B4 38%, #2E9BE0 72%, #0A2E56 100%);
}
html[data-mh="light"] .tier__name--blue .ic { color: #1668B4; filter: none; }
html[data-mh="light"] .tier__name--full {
    background: linear-gradient(105deg, #5A3E06 0%, #A87B10 38%, #D8A521 72%, #5A3E06 100%);
}
html[data-mh="light"] .tier__name--full .ic { color: #A87B10; filter: none; }

/* On the highlighted plan the card itself is white, so its disabled button
   needs the page's own ink rather than the pale tone it wears on black. */
html[data-mh="light"] .tier--best .btn--ghost,
html[data-mh="light"] .tier .btn--ghost { color: var(--muted); border-color: var(--line-2); }

body {
    min-height: 100%;
    font-family: 'Outfit', system-ui, -apple-system, sans-serif;
    font-size: clamp(1.02rem, 0.98rem + 0.2vw, 1.1rem);
    line-height: 1.72;
    color: var(--text);
    background: var(--void);
    overflow-x: hidden;
    -webkit-font-smoothing: antialiased;
}

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

::selection { background: rgba(236, 72, 153, 0.4); color: #fff; }

/* One scrollbar treatment for the whole site, page and panels alike. */
* { scrollbar-width: thin; scrollbar-color: rgba(236, 72, 153, 0.55) rgba(var(--w), 0.05); }
::-webkit-scrollbar { width: 0.6rem; height: 0.45rem; }
::-webkit-scrollbar-track { border-radius: 999px; background: rgba(var(--w), 0.05); }
::-webkit-scrollbar-thumb {
    border-radius: 999px;
    background: linear-gradient(180deg, #8B5CF6, #EC4899, #A78BFA);
}
::-webkit-scrollbar-thumb:horizontal { background: var(--neon); }
::-webkit-scrollbar-thumb:hover { background: linear-gradient(180deg, #A78BFA, #F472B6, #C4B5FD); }
::-webkit-scrollbar-corner { background: transparent; }

:focus-visible { outline: 2px solid var(--lilac); outline-offset: 3px; }

/* ==============================================================
   Backdrop
   ============================================================== */

.bg {
    position: fixed;
    inset: 0;
    z-index: -2;
    background:
        radial-gradient(95% 60% at 50% -10%, #2E2444 0%, transparent 66%),
        radial-gradient(65% 50% at 85% 16%, rgba(167, 139, 250, 0.22) 0%, transparent 70%),
        radial-gradient(60% 45% at 10% 6%, rgba(236, 72, 153, 0.18) 0%, transparent 72%),
        radial-gradient(60% 45% at 82% 84%, rgba(125, 211, 252, 0.10) 0%, transparent 72%),
        radial-gradient(75% 55% at 6% 76%, rgba(139, 92, 246, 0.16) 0%, transparent 74%),
        var(--void);
}
.bg::before, .bg::after {
    content: '';
    position: absolute;
    border-radius: 50%;
    filter: blur(90px);
    will-change: transform;
}
.bg::before {
    top: -15%; left: -10%;
    width: 45rem; aspect-ratio: 1;
    background: radial-gradient(closest-side, rgba(var(--w), 0.09), transparent 70%);
    animation: haze 38s var(--ease) infinite alternate;
}
.bg::after {
    bottom: -20%; right: -8%;
    width: 40rem; aspect-ratio: 1;
    background: radial-gradient(closest-side, rgba(236, 72, 153, 0.16), transparent 70%);
    animation: haze 46s var(--ease) infinite alternate-reverse;
}
@keyframes haze {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to   { transform: translate3d(4rem, -3rem, 0) scale(1.18); }
}

/* The sky: motes and moons, drifting. This replaces the film grain - the
   color and the movement do the texturing now.

   Each mote is one element with its own position, size, drift distance and
   duration set inline, so the field never looks like a repeating pattern.
   Twelve of them is enough at this opacity; more just costs frames. */
.sky { position: fixed; inset: 0; z-index: -1; overflow: hidden; pointer-events: none; }

/* The star field, the same one the server site draws. Behind the motes, and
   turned off entirely in light mode - white dots on a pale page are dirt. */
.sky__stars {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: var(--star-opacity, 0.85);
    transition: opacity 0.6s var(--ease);
}

.mote {
    position: absolute;
    border-radius: 50%;
    background: var(--mote, rgba(var(--w), 0.75));
    opacity: 0;
    animation: moteRise var(--dur, 26s) linear var(--delay, 0s) infinite;
    will-change: transform, opacity;
}
@keyframes moteRise {
    0%   { transform: translate3d(0, 12vh, 0) scale(0.6); opacity: 0; }
    12%  { opacity: 0.75; }
    82%  { opacity: 0.55; }
    100% { transform: translate3d(var(--drift, 3vw), -108vh, 0) scale(1); opacity: 0; }
}

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

.vignette {
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
    background: radial-gradient(130% 95% at 50% 45%, transparent 55%, rgba(var(--k), 0.45) 100%);
}

.vstrip {
    position: fixed;
    top: 50%;
    left: clamp(0.5rem, 1.6vw, 1.35rem);
    z-index: 5;
    transform: translateY(-50%);
    writing-mode: vertical-rl;
    text-orientation: mixed;
    font-size: 0.68rem;
    font-weight: 500;
    letter-spacing: 0.46em;
    text-transform: uppercase;
    color: rgba(var(--w), 0.2);
    pointer-events: none;
    user-select: none;
}
.vstrip::before, .vstrip::after {
    content: '';
    display: block;
    width: 1px;
    height: 3.5rem;
    margin: 0 auto;
    background: linear-gradient(180deg, transparent, var(--line-2), transparent);
}
.vstrip::before { margin-bottom: 1.25rem; }
.vstrip::after  { margin-top: 1.25rem; }
@media (max-width: 82rem) { .vstrip { display: none; } }

/* ==============================================================
   Layout
   ============================================================== */

.wrap { width: 100%; max-width: var(--wrap); margin-inline: auto; padding-inline: var(--gutter); }
section { position: relative; }
.section { padding-block: var(--section-y); }
.section--tight { padding-top: 0; }
.section--top { padding-top: var(--head-clear); }

.card {
    position: relative;
    background: linear-gradient(160deg, var(--glass), var(--glass-soft));
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    backdrop-filter: var(--blur);
    -webkit-backdrop-filter: var(--blur);
}
.card::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    box-shadow: inset 0 1px 0 rgba(var(--w), 0.11);
}

/* ==============================================================
   Type
   ============================================================== */

h1, h2, h3, h4 { font-weight: 700; line-height: 1.28; letter-spacing: -0.018em; text-wrap: balance; }

/* The pixel face is display-only: the store name, page titles and section
   bands. It never carries body copy, navigation or prices. */
.h1, .band h2, .mast__title, .foot__name { font-family: var(--px); font-weight: 700; letter-spacing: 0.005em; line-height: 1.12; }
.h1 { font-size: clamp(2.1rem, 1.4rem + 3vw, 3.6rem); font-weight: 700; line-height: 1.08; }
.h2 { font-size: clamp(1.55rem, 1.2rem + 1.6vw, 2.35rem); }
.h3 { font-size: clamp(1.02rem, 0.95rem + 0.4vw, 1.22rem); }

.chrome {
    background: var(--chrome);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
    filter: drop-shadow(0 2px 0 rgba(var(--k), 0.6));
}

.lede { font-size: clamp(1.06rem, 1rem + 0.35vw, 1.2rem); line-height: 1.75; color: var(--text-2, var(--muted)); font-weight: 400; max-width: min(44rem, 68ch); text-wrap: pretty; }

.eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 0.45rem;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--dim);
}
.eyebrow::before { content: '✧'; color: var(--pink); letter-spacing: 0; text-shadow: 0 0 0.6rem rgba(236, 72, 153, 0.6); }

.sub { font-weight: 400; font-size: 0.68rem; letter-spacing: 0.2em; text-transform: lowercase; color: var(--dim); }
.muted { color: var(--muted); }

/* Section heading with rules either side. */
.band {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: clamp(0.75rem, 2vw, 1.5rem);
    margin-bottom: clamp(1.5rem, 3.5vh, 2.25rem);
    text-align: center;
}
.band::before, .band::after {
    content: '';
    flex: 0 1 6rem;
    height: 1px;
    background: linear-gradient(90deg, transparent, rgba(236, 72, 153, 0.65));
}
.band::after { background: linear-gradient(90deg, rgba(139, 92, 246, 0.65), transparent); }
.band h2 .ic { display: inline-block; vertical-align: -0.12em; margin-right: 0.5rem; color: var(--pink); filter: drop-shadow(0 0 0.5rem rgba(236, 72, 153, 0.55)); }
.band h2 { font-size: clamp(1.35rem, 1.1rem + 1.2vw, 2rem); letter-spacing: 0.06em; text-transform: uppercase; white-space: nowrap; }
/* The band heading is pixel; its descriptor must opt back out or it inherits
   the display face at a size the pixel grid cannot carry. */
.band__sub { display: block; margin-top: 0.4rem; font-family: 'Outfit', system-ui, sans-serif; font-size: 0.68rem; font-weight: 500; letter-spacing: 0.3em; text-transform: uppercase; color: var(--dim); }

.seal {
    display: inline-grid;
    place-items: center;
    width: 2.15rem;
    height: 2.15rem;
    flex: none;
    border: 1.5px solid var(--lilac);
    border-radius: 0.35rem;
    font-family: var(--px);
    font-size: 1.05rem;
    font-weight: 700;
    line-height: 1;
    color: var(--lilac);
    background: rgba(167, 139, 250, 0.08);
    transform: rotate(-4deg);
}

/* Icons size themselves off the text beside them. */
/* The symbols live inside a <g> that carries fill/stroke, but a <use>
   reference does not inherit from where the symbol was written - only from
   where it is used. So the paint has to be stated here, on the icon itself,
   or every symbol falls back to a solid black fill. */
.ic {
    width: 1.15em;
    height: 1.15em;
    flex: none;
    display: block;
    fill: none;
    stroke: currentColor;
    stroke-width: 1.85;
    stroke-linecap: round;
    stroke-linejoin: round;
}
.ic--lg { width: 1.65em; height: 1.65em; }

/* Line icons at this weight read as hairlines on a near-black ground, so
   they get a light behind them rather than a heavier stroke - the stroke is
   what keeps them delicate. */
.ic--neon { color: #fff; filter: drop-shadow(0 0 0.45rem rgba(236, 72, 153, 0.55)) drop-shadow(0 0 1rem rgba(139, 92, 246, 0.35)); }

/* ==============================================================
   Buttons & tags
   ============================================================== */

.btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    /* Pills, the same geometry as the website's - it was the last thing
       keeping the two sets of buttons from being the same object. The border
       stays at 1.5px because the ghost variant paints its gradient rim
       there; the primary makes it transparent. */
    padding: 0.9rem 2rem;
    border: 1.5px solid var(--line-2);
    border-radius: 999px;
    overflow: hidden;
    background: transparent;
    font-family: 'Outfit', system-ui, sans-serif;
    font-weight: 600;
    font-size: 1rem;
    letter-spacing: 0.005em;
    cursor: pointer;
    white-space: nowrap;
    transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease),
                background-color var(--speed) var(--ease), transform var(--speed) var(--ease),
                box-shadow var(--speed) var(--ease);
}
.btn::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.3s var(--ease);
    background: radial-gradient(5rem circle at var(--mx, 50%) var(--my, 50%), rgba(var(--w), 0.22), transparent 62%);
}
.btn:hover::before { opacity: 1; }

.btn--primary {
    color: #fff;
    border-color: transparent;
    /* No rim on the filled button - a lighter tint of its own fill just reads
       as a white outline. The border stays 1.5px and transparent so this
       button is exactly as tall as the outlined ones beside it, and
       background-origin: border-box makes the gradient span the border ring
       too. Without that the ring shows the gradient REPEATING, and the tail
       of it is pink - which looks like a pink border and is not one. */
    border-color: transparent;
    background: linear-gradient(120deg, #8B5CF6, #EC4899);
    background-origin: border-box;
    background-size: 200% 100%;
    box-shadow: 0 0.6rem 1.6rem rgba(139, 92, 246, 0.35);
}
/* The gradient slides rather than the colour changing, so the hover reads as
   the light moving across it. */
.btn--primary:hover {
    background-position: 100% 0;
    transform: translateY(-2px);
    box-shadow: 0 0.9rem 2.2rem rgba(236, 72, 153, 0.45);
}
.btn--primary:active { transform: translateY(0); }

.btn--ghost {
    color: var(--text);
    /* Transparent border plus two backgrounds: only the rim takes the
       gradient. Same recipe as the website's ghost button. */
    border-color: transparent;
    background: var(--pill-base) padding-box, var(--neon) border-box;
}
/* The fill lifts a touch on hover. The gradient layer has to be restated or
   the background shorthand drops it and the rim disappears. */
/* The website's ghost hover, value for value: the fill lifts a touch, the
   button rises, and there is no glow. The gradient layer has to be restated
   or the background shorthand drops it and the rim disappears mid-hover. */
.btn--ghost:hover {
    background:
        linear-gradient(rgba(var(--w), 0.06), rgba(var(--w), 0.06)) padding-box,
        var(--pill-base) padding-box,
        var(--neon) border-box;
    transform: translateY(-2px);
}

/* Buttons sitting INSIDE a card do not lift. The card is already rising under
   the cursor, and a button rising as well reads as two things moving at
   different speeds - the View button on a product tile was the worst of it.
   They still light up; they just stay put. */
.item .btn:hover,
.strip__card .btn:hover,
.folio__item .btn:hover,
.upsell .btn:hover { transform: none; }

/* Buttons in a form take the small size automatically (owner, 2026-08-26).
   The live chat's Send is the size that reads right next to input fields, and
   a full-size button under a text area looks like it belongs to the page
   rather than to the form. Sharing the declaration with .btn--sm rather than
   restating the values means the two can never drift apart, and a form added
   later is right without anyone remembering the modifier. */
.btn--sm,
.form .btn { padding: 0.6rem 1.35rem; font-size: 0.92rem; }
.btn--wide { width: 100%; }
.btn[aria-disabled="true"] { opacity: 0.4; pointer-events: none; }

.btn-row { display: flex; flex-wrap: wrap; gap: 0.7rem; }

.tag {
    display: inline-flex;
    align-items: center;
    padding: 0.2rem 0.65rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: rgba(var(--w), 0.03);
    font-size: 0.78rem;
    font-weight: 500;
    letter-spacing: 0.03em;
    color: var(--muted);
}
/* Tags sit off the site's own gradient on purpose: purple and pink carry
   MoonHaven's own voice everywhere else, so a tag borrowing them reads as
   decoration. A soft candle-yellow and a mint read as information instead. */
.tag--fw {
    color: var(--tag-warm);
    border-color: rgba(250, 217, 137, 0.3);
    background: rgba(250, 217, 137, 0.055);
}
/* The promises about the work itself, rather than what it runs on. */
.tag--style {
    color: var(--tag-mint);
    border-color: rgba(126, 232, 197, 0.28);
    background: rgba(126, 232, 197, 0.055);
}

/* ==============================================================
   Masthead + nav
   ============================================================== */

.nav {
    position: fixed;
    inset: 0 0 auto 0;
    z-index: 200;
    /* A sheet you can see the page move behind, not a lid on top of it. */
    background: var(--sheet-nav);
    border-bottom: 1px solid var(--line);
    backdrop-filter: blur(26px) saturate(160%);
    -webkit-backdrop-filter: blur(26px) saturate(160%);
    box-shadow: inset 0 -1px 0 rgba(var(--w), 0.05);
    transition: box-shadow 0.3s var(--ease), background-color 0.3s var(--ease);
}
/* Weather behind the masthead: two soft gradient clouds crossing each other
   slowly, and a dust of motes over the top. It has to stay under the text -
   the header is a place to read the menu, not a light show. */
.nav__weather {
    position: absolute;
    inset: 0;
    z-index: 0;
    overflow: hidden;
    pointer-events: none;
}
.nav > *:not(.nav__weather) { position: relative; z-index: 1; }

.nav__weather::before {
    content: '';
    position: absolute;
    inset: -60% -10%;
    pointer-events: none;
    background:
        radial-gradient(28% 60% at 20% 45%, rgba(139, 92, 246, 0.30), transparent 70%),
        radial-gradient(24% 55% at 62% 55%, rgba(236, 72, 153, 0.24), transparent 70%),
        radial-gradient(20% 45% at 88% 40%, rgba(125, 211, 252, 0.14), transparent 70%);
    filter: blur(28px);
    animation: navDrift 34s var(--ease) infinite alternate;
    will-change: transform;
}
@keyframes navDrift {
    from { transform: translate3d(-4%, 0, 0) scale(1); }
    to   { transform: translate3d(4%, 1.5%, 0) scale(1.12); }
}

/* The motes are one repeating dot pattern sliding sideways - cheaper than
   any particle system and it reads the same at this size. */
.nav__weather::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    opacity: 0.5;
    background-image:
        radial-gradient(1.5px 1.5px at 12% 30%, rgba(var(--w), 0.65), transparent 60%),
        radial-gradient(1.5px 1.5px at 34% 70%, rgba(236, 72, 153, 0.6), transparent 60%),
        radial-gradient(1.5px 1.5px at 58% 25%, rgba(var(--w), 0.5), transparent 60%),
        radial-gradient(1.5px 1.5px at 76% 62%, rgba(167, 139, 250, 0.7), transparent 60%),
        radial-gradient(1.5px 1.5px at 92% 38%, rgba(var(--w), 0.45), transparent 60%);
    background-size: 60% 100%;
    animation: navMotes 26s linear infinite;
}
@keyframes navMotes { to { background-position: 60% 0; } }

@media (prefers-reduced-motion: reduce) {
    .nav__weather::before, .nav__weather::after { animation: none; }
}

/* Once content is passing underneath it thickens slightly - enough to keep
   the title legible, not enough to become opaque. */
.nav.is-stuck {
    background: var(--sheet-nav-2);
    box-shadow: 0 0.75rem 2rem rgba(var(--k), 0.5), inset 0 -1px 0 rgba(var(--w), 0.06);
}

.mast {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: clamp(0.9rem, 2.2vh, 1.5rem) var(--gutter) clamp(0.7rem, 1.6vh, 1.1rem);
    transition: padding 0.35s var(--ease);
}
.nav.is-stuck .mast { padding-block: 0.6rem 0.45rem; }

.mast__brand { display: flex; align-items: center; gap: clamp(0.7rem, 1.6vw, 1.1rem); }
.mast__mark {
    width: clamp(2.4rem, 4vw, 3.6rem);
    height: auto;
    aspect-ratio: 1;
    flex: none;
    filter: drop-shadow(0 0 0.9rem rgba(236, 72, 153, 0.45)) drop-shadow(0 0 1.8rem rgba(139, 92, 246, 0.3));
    transition: transform 0.45s var(--ease-out), filter 0.45s var(--ease), width 0.35s var(--ease);
}
.nav.is-stuck .mast__mark { width: clamp(2rem, 3vw, 2.6rem); }
.mast__brand:hover .mast__mark { transform: rotate(-7deg) scale(1.07); filter: drop-shadow(0 0 1.2rem rgba(236, 72, 153, 0.8)) drop-shadow(0 0 2.4rem rgba(139, 92, 246, 0.5)); }

.mast__text { display: flex; flex-direction: column; align-items: flex-start; line-height: 1.05; }
.mast__title {
    font-family: var(--px);
    font-size: clamp(1.75rem, 1rem + 3.2vw, 3.4rem);
    font-weight: 700;
    white-space: nowrap;
    transition: font-size 0.35s var(--ease);
}
.nav.is-stuck .mast__title { font-size: clamp(1.35rem, 0.9rem + 1.9vw, 2.1rem); }

.mast__sub {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    margin-top: 0.3rem;
    font-size: clamp(0.58rem, 0.5rem + 0.2vw, 0.72rem);
    font-weight: 500;
    letter-spacing: 0.34em;
    text-transform: uppercase;
    color: var(--dim);
    transition: opacity 0.3s var(--ease), max-height 0.35s var(--ease);
}
.nav.is-stuck .mast__sub { opacity: 0; max-height: 0; margin-top: 0; }

.nav__bar {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 1rem;
    padding: 0 var(--gutter) 0.55rem;
}

.nav__links { display: flex; align-items: center; justify-content: center; gap: clamp(0.4rem, 1.4vw, 1.4rem); }

.nav__link {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.5rem 0.35rem;
    border: 0;
    background: none;
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: clamp(1rem, 0.95rem + 0.28vw, 1.15rem);
    font-weight: 500;
    color: var(--muted);
    cursor: pointer;
    transition: color var(--speed) var(--ease);
}
.nav__link:hover, .nav__link[aria-expanded="true"] { color: #fff; }

.nav__link .ic { color: var(--dim); transition: color var(--speed) var(--ease), filter var(--speed) var(--ease); }
.nav__link:hover .ic, .nav__link.is-active .ic, .nav__link[aria-expanded="true"] .ic {
    color: var(--pink);
    filter: drop-shadow(0 0 0.45rem rgba(236, 72, 153, 0.6));
}
.nav__link::after {
    content: '';
    position: absolute;
    left: 0.35rem;
    right: 0.35rem;
    bottom: 0.15rem;
    height: 2px;
    background: var(--neon);
    box-shadow: 0 0 0.7rem rgba(236, 72, 153, 0.5);
    transform: scaleX(0);
    transform-origin: left;
    transition: transform var(--speed) var(--ease-out);
}
.nav__link:hover::after, .nav__link[aria-expanded="true"]::after { transform: scaleX(1); }

/* The active page keeps its underline, so you always know where you are. */
.nav__link.is-active { color: #fff; }
.nav__link.is-active::after { transform: scaleX(1); }

/* Part of the link, not a control of its own: it inherits the hover color
   and sits inside the underline. */
/* The theme switch. Reads as one of the nav's own controls rather than a
   setting bolted on: same hairline, same hover, and it swaps which of two
   icons shows instead of changing shape. */
.themeswitch {
    position: relative;
    display: inline-grid;
    place-items: center;
    width: 2.35rem;
    height: 2.35rem;
    margin-left: 0.35rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: var(--panel);
    color: var(--muted);
    cursor: pointer;
    transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease),
                background-color var(--speed) var(--ease);
}
.themeswitch:hover { color: var(--text); border-color: var(--line-2); background: var(--panel-2); }
.themeswitch .ic {
    grid-area: 1 / 1;
    transition: opacity 0.25s var(--ease), transform 0.35s var(--ease-out);
}
/* Two switches, one per breakpoint, so it is last in the bar on a wide
   screen and next to the close button on a narrow one. Only ever one on
   screen, and both are wired to the same handler. */
.themeswitch--mast { display: none; }

.themeswitch__sun { opacity: 0; transform: rotate(-90deg) scale(0.6); }
html[data-mh="light"] .themeswitch__moon { opacity: 0; transform: rotate(90deg) scale(0.6); }
html[data-mh="light"] .themeswitch__sun { opacity: 1; transform: none; }
.sr {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.nav__caret {
    display: inline-block;
    font-size: 0.6em;
    color: var(--dim);
    transition: color var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.nav__link:hover .nav__caret { color: inherit; }
.nav__link[aria-expanded="true"] .nav__caret { color: var(--pink); transform: rotate(180deg); }

.drop { position: relative; display: flex; align-items: center; }
.drop__menu {
    position: absolute;
    top: calc(100% + 0.55rem);
    left: 50%;
    width: min(23rem, 88vw);
    padding: 0.5rem;
    background: var(--sheet-pop);
    border: 1px solid var(--line-2);
    border-radius: var(--r-md);
    box-shadow: 0 1.5rem 3rem rgba(var(--k), 0.75), inset 0 1px 0 rgba(var(--w), 0.08);
    backdrop-filter: blur(26px) saturate(160%);
    -webkit-backdrop-filter: blur(26px) saturate(160%);
    opacity: 0;
    visibility: hidden;
    transform: translate(-50%, -0.5rem);
    /* Closing is quicker than opening on purpose: a menu getting out of the
       way should not linger. Opening keeps the softer entrance below. */
    transition: opacity 0.11s var(--ease), transform 0.11s var(--ease), visibility 0.11s;
}
.drop__menu.is-open {
    opacity: 1;
    visibility: visible;
    transform: translate(-50%, 0);
    transition: opacity 0.2s var(--ease), transform 0.2s var(--ease-out), visibility 0.2s;
}
/* The panel sits below the link with a gap. The gap is inside no element, so
   traveling across it used to count as leaving the menu. This bridges it -
   invisible, part of the panel, so the pointer never leaves. */
.drop__menu::before {
    content: '';
    position: absolute;
    left: 0;
    right: 0;
    top: -0.75rem;
    height: 0.75rem;
}

.drop__item {
    display: flex;
    align-items: center;
    gap: 0.85rem;
    width: 100%;
    padding: 0.6rem 0.7rem;
    border: 0;
    border-radius: var(--r-sm);
    background: none;
    text-align: left;
    cursor: pointer;
    transition: background-color var(--speed) var(--ease);
}
.drop__item:hover { background: var(--panel-2); }
.drop__item.is-on { background: var(--neon-soft); }
.drop__icon {
    display: grid;
    place-items: center;
    width: 2.5rem;
    height: 2.5rem;
    flex: none;
    border: 1px solid var(--line-2);
    border-radius: var(--r-sm);
    background: rgba(var(--w), 0.05);
    font-size: 1.2rem;
    color: var(--text);
}
.drop__item:hover .drop__icon, .drop__item.is-on .drop__icon {
    border-color: rgba(236, 72, 153, 0.45);
    background: var(--neon-soft);
    color: #fff;
}
.drop__label { display: flex; flex-direction: column; line-height: 1.25; }
.drop__name { font-size: 0.98rem; font-weight: 600; }
.drop__sub { font-size: 0.66rem; letter-spacing: 0.14em; text-transform: lowercase; color: var(--dim); }
.drop__count { margin-left: auto; font-size: 0.78rem; color: var(--dim); font-variant-numeric: tabular-nums; }

.nav__toggle {
    position: absolute;
    top: 50%;
    right: var(--gutter);
    transform: translateY(-50%);
    display: none;
    flex-direction: column;
    justify-content: center;
    gap: 0.3rem;
    width: 2.7rem;
    height: 2.7rem;
    padding: 0.6rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: rgba(var(--w), 0.04);
    cursor: pointer;
}
.nav__toggle span { display: block; height: 2px; background: var(--text); transition: transform 0.3s var(--ease), opacity 0.2s var(--ease); }
.nav__toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(0.4rem) rotate(45deg); }
.nav__toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav__toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-0.4rem) rotate(-45deg); }

/* ==============================================================
   Hero
   ============================================================== */

.hero { padding-block: calc(var(--head-clear) + clamp(3.5rem, 10vh, 7rem)) clamp(2rem, 5vh, 3.5rem); }

.hero__grid {
    display: grid;
    gap: clamp(1.75rem, 4vw, 3rem);
    grid-template-columns: minmax(0, 1fr);
    align-items: center;
    text-align: center;
}
@media (min-width: 64rem) {
    .hero__grid { grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.6fr) minmax(0, 0.8fr); }
}

.claims { display: grid; gap: 0; }

/* Hairline-separated rows rather than free-floating lines - they read as a
   list attached to the hero instead of text drifting in space. */
.claim {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.6rem 0;
    border-bottom: 1px solid var(--line);
    font-size: clamp(0.78rem, 0.74rem + 0.18vw, 0.88rem);
    font-weight: 600;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--muted);
}
.claim:last-child { border-bottom: 0; }
.claim .ic { color: var(--pink); opacity: 0.95; }

/* Each column faces the middle, so the two flanks frame the pitch. */
@media (min-width: 64rem) {
    .hero__grid > .claims:first-child { text-align: right; }
    .hero__grid > .claims:first-child .claim { flex-direction: row-reverse; }
}
@media (max-width: 64rem) {
    .claims { max-width: 26rem; margin-inline: auto; }
    .claim { justify-content: center; }
}

.hero__welcome {
    display: block;
    margin-bottom: 0.4rem;
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: clamp(1.05rem, 0.9rem + 0.7vw, 1.45rem);
    font-weight: 500;
    color: var(--muted);
}
/* Sparkles behind the wordmark. Positions are set inline so the field is
   deliberate rather than random, and each one twinkles on its own delay so
   they never pulse in unison. They sit behind the title and ignore the
   pointer - decoration, not furniture. */
.hero__wordmark { position: relative; display: inline-block; margin-bottom: 1.1rem; }

/* A moon drifting beside the name. It sits outside the text flow so it can
   never push the wordmark around, and it steps aside on small screens where
   there is no margin to float in. */
.hero__moon {
    position: absolute;
    right: calc(100% + clamp(0.75rem, 2vw, 2rem));
    top: 50%;
    width: clamp(2.6rem, 5vw, 4.5rem);
    aspect-ratio: 1;
    filter: drop-shadow(0 0 1rem rgba(167, 139, 250, 0.55)) drop-shadow(0 0 2.4rem rgba(236, 72, 153, 0.35));
    animation: heroMoonFloat 7s ease-in-out infinite;
    will-change: transform;
}
@keyframes heroMoonFloat {
    0%, 100% { transform: translateY(-50%) rotate(-6deg); }
    50%      { transform: translateY(-64%) rotate(4deg); }
}
/* Same drift, but this one is positioned from the top rather than centerd. */
.maker__moon { animation-name: makerMoonFloat; }
@keyframes makerMoonFloat {
    0%, 100% { transform: translateY(0) rotate(-6deg); }
    50%      { transform: translateY(-0.6rem) rotate(4deg); }
}
@media (max-width: 52rem) { .hero__moon { display: none; } }
@media (prefers-reduced-motion: reduce) { .hero__moon { animation: none; } }
.sparks { position: absolute; inset: -18% -8%; pointer-events: none; z-index: 0; }
.sparks i {
    position: absolute;
    left: var(--x);
    top: var(--y);
    font-size: var(--s);
    font-style: normal;
    line-height: 1;
    color: var(--c);
    opacity: 0;
    transform: translate(-50%, -50%) scale(0.4) rotate(0deg);
    text-shadow: 0 0 0.6rem currentColor;
    animation: twinkle 4.2s var(--ease) var(--d) infinite;
}
@keyframes twinkle {
    0%, 100% { opacity: 0;    transform: translate(-50%, -50%) scale(0.35) rotate(0deg); }
    35%      { opacity: 0.95; transform: translate(-50%, -50%) scale(1)    rotate(35deg); }
    60%      { opacity: 0.55; transform: translate(-50%, -50%) scale(0.8)  rotate(60deg); }
}
@media (prefers-reduced-motion: reduce) { .sparks i { animation: none; opacity: 0.5; transform: translate(-50%, -50%) scale(0.8); } }

.hero__title { position: relative; z-index: 1; margin-bottom: 0; }
.hero .lede { margin-inline: auto; }
/* The four facts a buyer checks first, set as a spec strip rather than a row
   of pills: label above value, hairlines between, ruled off top and bottom.
   Pills read as decoration; a spec strip reads as information, which is what
   these are. Marked up as a <dl> because that is literally what it is - four
   terms and their definitions. */
.hero__spec {
    /* Two by two, not four across. This sits in the hero's centre column,
       which is about 480px wide between the two claim lists - four abreast
       wraps there every time and the stray divider lands in mid-air. */
    display: grid;
    grid-template-columns: repeat(2, minmax(0, 1fr));
    max-width: 30rem;
    margin: clamp(1.6rem, 3.5vh, 2.25rem) auto 0;
    padding-block: clamp(0.85rem, 2vh, 1.1rem);
    border-block: 1px solid var(--line);
}
.hero__spec > div { padding: 0.4rem 0.9rem; text-align: center; }
/* Ruled like a table: between the columns, and between the rows. */
.hero__spec > div:nth-child(even) { border-left: 1px solid var(--line); }
.hero__spec > div:nth-child(n+3) { margin-top: 0.75rem; padding-top: 0.9rem; border-top: 1px solid var(--line); }

.hero__spec dt {
    font-size: 0.64rem;
    font-weight: 600;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--dim);
}
.hero__spec dd {
    margin-top: 0.35rem;
    font-size: 0.95rem;
    font-weight: 500;
    color: var(--text);
}
.hero__spec b {
    font-weight: 700;
    background: var(--neon);
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}

/* One per row where two will not fit, and the column rule goes with it. */
@media (max-width: 26rem) {
    .hero__spec { grid-template-columns: minmax(0, 1fr); }
    .hero__spec > div:nth-child(even) { border-left: 0; }
    .hero__spec > div + div { margin-top: 0.75rem; padding-top: 0.9rem; border-top: 1px solid var(--line); }
}

.ticker {
    position: relative;
    margin-top: clamp(2.5rem, 6vh, 3.5rem);
    padding-block: 0.7rem;
    border-block: 1px solid var(--line);
    overflow: hidden;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
            mask-image: linear-gradient(90deg, transparent, #000 12%, #000 88%, transparent);
}
.ticker__track { display: flex; width: max-content; animation: marquee 34s linear infinite; }
.ticker:hover .ticker__track { animation-play-state: paused; }
.ticker__group { display: flex; gap: 2.5rem; padding-right: 2.5rem; }
.ticker__item {
    display: inline-flex;
    align-items: center;
    gap: 0.65rem;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.24em;
    text-transform: uppercase;
    color: var(--muted);
    white-space: nowrap;
}
.ticker__item::after { content: '✧'; color: var(--pink); letter-spacing: 0; }
@keyframes marquee { to { transform: translateX(-50%); } }

/* ==============================================================
   Media panel
   ============================================================== */

.media {
    position: relative;
    aspect-ratio: 16 / 9;
    display: grid;
    place-items: center;
    border-radius: var(--r-lg);
    overflow: hidden;
    background:
        radial-gradient(90% 100% at 50% 0%, rgba(167, 139, 250, 0.16), transparent 70%),
        linear-gradient(160deg, var(--glass), var(--glass-soft));
    border: 1px solid var(--line);
    backdrop-filter: var(--blur-lite);
    -webkit-backdrop-filter: var(--blur-lite);
}
.media__inner { display: grid; justify-items: center; gap: 0.85rem; padding: 1.5rem; text-align: center; }
.media__mark { width: clamp(3rem, 6vw, 4.5rem); aspect-ratio: 1; opacity: 0.7; }
.media__label { font-family: 'Outfit', system-ui, sans-serif; font-size: 0.95rem; letter-spacing: 0.08em; color: var(--muted); }
.media__note { font-size: 0.88rem; color: var(--dim); }
.media::before, .media::after {
    content: '';
    position: absolute;
    width: 1.4rem;
    height: 1.4rem;
    border: 1px solid var(--line-2);
    pointer-events: none;
}
.media::before { top: 0.75rem; left: 0.75rem; border-right: 0; border-bottom: 0; }
.media::after  { bottom: 0.75rem; right: 0.75rem; border-left: 0; border-top: 0; }

/* ==============================================================
   Values band
   ============================================================== */

.values {
    display: grid;
    gap: clamp(0.9rem, 1.8vw, 1.35rem);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
}
.value {
    position: relative;
    padding: clamp(1.5rem, 2.8vw, 2rem);
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: linear-gradient(160deg, var(--glass), var(--glass-soft));
    backdrop-filter: var(--blur);
    -webkit-backdrop-filter: var(--blur);
    text-align: center;
    transition: transform 0.35s var(--ease-out), border-color 0.35s var(--ease);
}
.value::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    box-shadow: inset 0 1px 0 rgba(var(--w), 0.11);
}
.value__icon {
    display: grid;
    place-items: center;
    width: clamp(4.5rem, 8vw, 5.75rem);
    aspect-ratio: 1;
    margin: 0 auto 1rem;
    border: 1px solid rgba(236, 72, 153, 0.4);
    border-radius: 50%;
    background: var(--neon-soft);
    box-shadow: var(--glow);
    font-size: 1.45rem;
    color: #fff;
}
.value__icon .ic { filter: drop-shadow(0 0 0.5rem rgba(236, 72, 153, 0.55)); }
.value h3 { margin-bottom: 0.4rem; }
.value p { font-size: 0.98rem; font-weight: 400; color: var(--muted); }

/* ==============================================================
   Catalog
   ============================================================== */

.shopbar { display: grid; gap: 0.85rem; margin-bottom: clamp(1.5rem, 3vh, 2rem); }
@media (min-width: 52rem) { .shopbar { grid-template-columns: minmax(0, 1fr) auto; align-items: center; } }

.search {
    display: flex;
    align-items: center;
    gap: 0.65rem;
    padding: 0.6rem 0.95rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: rgba(var(--w), 0.03);
    transition: border-color var(--speed) var(--ease);
}
.search:focus-within {
    border-color: transparent;
    background:
        linear-gradient(var(--fill-solid), var(--fill-solid)) padding-box,
        var(--neon) border-box;
}
.search .ic { color: var(--muted); }
.search input {
    width: 100%;
    border: 0;
    background: none;
    outline: none;
    font-family: 'Outfit', sans-serif;
    font-size: 0.97rem;
    color: var(--text);
}
.search input::placeholder { color: var(--dim); }

.filters { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.filter {
    padding: 0.45rem 1rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: rgba(var(--w), 0.03);
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 0.95rem;
    font-weight: 500;
    color: var(--muted);
    cursor: pointer;
    transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease), background-color var(--speed) var(--ease);
}
.filter:hover { color: #fff; border-color: var(--line-2); }
.filter.is-on { color: #fff; background: var(--neon-soft); border-color: rgba(236, 72, 153, 0.55); font-weight: 600; }

.catalog { display: grid; gap: clamp(0.9rem, 1.8vw, 1.35rem); grid-template-columns: repeat(auto-fill, minmax(min(100%, 19rem), 1fr)); }

/* The home rows are short lists, not a catalog: they center instead of
   filling the page. */
#featured-grid, #free-grid {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 19rem), 1fr));
    max-width: 76rem;
    margin-inline: auto;
}

/* The whole card is the link - the owner asked for every item to be
   clickable, not just its button. */
.item {
    display: flex;
    flex-direction: column;
    border-radius: var(--r-lg);
    overflow: hidden;
    cursor: pointer;
    /* Lighter than .card's blur, which this element would otherwise inherit.
       Keep it: the tile no longer lifts, so nothing animates over the blur. */
    backdrop-filter: var(--blur-lite);
    -webkit-backdrop-filter: var(--blur-lite);
}
.item__shot {
    position: relative;
    aspect-ratio: 16 / 10;
    backface-visibility: hidden;
    display: grid;
    place-items: center;
    background:
        radial-gradient(85% 100% at 50% 0%, rgba(var(--w), 0.09), transparent 70%),
        var(--glass-veil);
    border-bottom: 1px solid var(--line);
    overflow: hidden;
}
.item__crest { width: 30%; aspect-ratio: 1; opacity: 0.5; transition: opacity 0.45s var(--ease), transform 0.45s var(--ease-out); }
.item:hover .item__crest { opacity: 0.9; transform: scale(1.06); }

.badge {
    position: absolute;
    top: 0.7rem;
    right: 0.7rem;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0.2rem 0.6rem;
    border: 1px solid var(--line-2);
    border-radius: 999px;
    background: rgba(8, 8, 10, 0.85);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 0.75rem;
    font-weight: 500;
    z-index: 2;
}
.badge::before { content: ''; width: 0.38rem; height: 0.38rem; border-radius: 50%; background: currentColor; }
.badge.is-free { color: #7DD3FC; border-color: rgba(125, 211, 252, 0.4); }

.item__body { display: flex; flex-direction: column; flex: 1; padding: clamp(1.1rem, 2.2vw, 1.45rem); }
.item__cat { display: flex; align-items: baseline; gap: 0.5rem; font-size: 0.68rem; font-weight: 600; letter-spacing: 0.2em; text-transform: uppercase; color: var(--dim); }
.item__cat .sub { font-size: 0.64rem; letter-spacing: 0.13em; }
.item__name { margin-top: 0.4rem; }
.item__desc { margin-top: 0.45rem; font-size: 0.97rem; font-weight: 400; color: var(--muted); }
.item__tags {
    display: flex;
    flex-wrap: wrap;
    align-content: flex-end;
    gap: 0.3rem;
    /* Two rows of pills plus the gap between them. Reserved whether they are
       used or not, so every card's divider sits on the same line. */
    min-height: calc(2 * 1.55rem + 0.3rem);
    margin-top: auto;
    padding-top: 0.9rem;
    margin-bottom: 1.2rem;
    overflow: hidden;
}

/* Pinned to the bottom of the card: two margin-top declarations were fighting
   here and the literal one won, so the rule above the price sat wherever the
   description happened to end. One `auto` and the dividers line up across a
   row however long the copy is. */
.item__foot {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.7rem;
    padding-top: 1.2rem;
    border-top: 1px solid var(--line);
}
.price {
    display: flex;
    align-items: baseline;
    gap: 0.5rem;
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 1.35rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    background: var(--neon);
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}
/* Children inherit the transparent fill, so anything that is not the price
   itself has to opt back out or it disappears. */
.price__was, .price small { -webkit-text-fill-color: currentColor; color: var(--dim); }
.price__was { font-size: 0.95rem; font-weight: 400; text-decoration: line-through; }
.price small { font-family: 'Outfit', sans-serif; font-size: 0.84rem; font-weight: 400; }

.catalog__empty { padding: 3rem 1.5rem; text-align: center; color: var(--muted); font-weight: 400; grid-column: 1 / -1; }

/* ==============================================================
   Product page
   ============================================================== */

.crumbs { display: flex; align-items: center; gap: 0.5rem; margin-bottom: 1.25rem; font-size: 0.89rem; color: var(--dim); }
.crumbs a:hover {
    background: var(--neon);
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}

.product {
    display: grid;
    gap: clamp(1.5rem, 4vw, 3rem);
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
}
@media (min-width: 64rem) { .product { grid-template-columns: minmax(0, 1.45fr) minmax(19rem, 1fr); } }

.product__title { margin-bottom: 0.75rem; font-family: 'Outfit', system-ui, sans-serif; letter-spacing: -0.02em; }
.product__tags { display: flex; flex-wrap: wrap; gap: 0.4rem; margin: 1.1rem 0; }
.product__price { font-size: clamp(1.9rem, 1.5rem + 1.4vw, 2.6rem); margin: 1.25rem 0; }
.product__buy { display: flex; flex-wrap: wrap; gap: 0.7rem; }
.product__assure { display: grid; gap: 0.45rem; margin-top: 1.25rem; }
.product__assure li { list-style: none; position: relative; padding-left: 1.4rem; font-size: 0.96rem; color: var(--muted); font-weight: 400; }
.product__assure li::before {
    content: '';
    position: absolute;
    left: 0.15rem;
    top: 0.55em;
    width: 0.5rem;
    height: 0.26rem;
    border-left: 1.5px solid var(--ok);
    border-bottom: 1.5px solid var(--ok);
    transform: rotate(-45deg);
}

/* Subscription nudge on the product page. */
.upsell {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.85rem;
    margin-top: 1.5rem;
    padding: 0.95rem 1.15rem;
    border: 1px solid rgba(236, 72, 153, 0.35);
    border-radius: var(--r-md);
    background: var(--neon-soft);
}
.upsell__text { flex: 1 1 14rem; }
.upsell__text b {
    font-family: 'Outfit', system-ui, sans-serif;
    background: var(--neon);
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}
.upsell__note { display: block; font-size: 0.88rem; color: var(--dim); }

/* Feature blocks, numbered because they are an ordered walk through the
   product - not decoration. */
.feature {
    display: grid;
    gap: clamp(1.25rem, 3vw, 2.5rem);
    grid-template-columns: minmax(0, 1fr);
    align-items: center;
    padding-block: clamp(1.75rem, 4vh, 2.75rem);
    border-top: 1px solid var(--line);
}
@media (min-width: 60rem) {
    .feature { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
    .feature:nth-child(even) .feature__text { order: 2; }
}
.feature__no { font-family: var(--px); font-size: 1.1rem; font-weight: 700; color: var(--pink); }
.feature__text h3 { margin: 0.35rem 0 0.6rem; font-size: clamp(1.25rem, 1.05rem + 0.9vw, 1.75rem); }
.feature__text p { font-size: 0.95rem; font-weight: 400; color: var(--muted); }

.pillars { display: grid; gap: clamp(0.9rem, 1.8vw, 1.35rem); grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); }
.pillar { padding: clamp(1.2rem, 2.4vw, 1.6rem); border-radius: var(--r-md);
    transition: transform 0.35s var(--ease-out), border-color 0.35s var(--ease); }
.pillar:hover { transform: translateY(-4px); border-color: rgba(236, 72, 153, 0.4); }
.pillar__icon {
    display: grid;
    place-items: center;
    width: 3rem;
    height: 3rem;
    margin-bottom: 0.85rem;
    border: 1px solid rgba(167, 139, 250, 0.35);
    border-radius: var(--r-sm);
    background: var(--neon-soft);
    font-size: 1.3rem;
    color: #fff;
}
.pillar__icon .ic { filter: drop-shadow(0 0 0.45rem rgba(139, 92, 246, 0.6)); }
.pillar p { margin-top: 0.4rem; font-size: 0.97rem; font-weight: 400; color: var(--muted); }

/* Sticky buy bar, the way product pages keep the action reachable. */
.buybar {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 150;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1rem;
    padding: 0.7rem var(--gutter);
    background: var(--sheet-bar);
    border-top: 1px solid var(--line);
    box-shadow: inset 0 1px 0 rgba(var(--w), 0.06);
    backdrop-filter: blur(26px) saturate(160%);
    -webkit-backdrop-filter: blur(26px) saturate(160%);
    transform: translateY(110%);
    transition: transform 0.35s var(--ease-out);
}
.buybar.is-up { transform: none; }
.buybar__name { font-family: 'Outfit', system-ui, sans-serif; font-size: 1rem; }
.buybar__price { font-family: 'Outfit', system-ui, sans-serif; font-size: 1.1rem; font-weight: 700; color: var(--lilac); }
.buybar__left { display: flex; flex-direction: column; min-width: 0; }
@media (max-width: 34rem) { .buybar__name { display: none; } }

/* ==============================================================
   Product gallery, tabs and buy card
   The reference shops all do the same three things on a product
   page: show the work big with a thumbnail rail under it, split the
   reading into tabs, and keep the price and the button in reach
   while you scroll. Worth taking. The look stays ours.
   ============================================================== */

.gallery { display: grid; gap: 0.6rem; }

.gallery__thumbs {
    display: flex;
    gap: 0.5rem;
    overflow-x: auto;
    /* The lift on hover happens inside this scroller, so the row needs room
       above the thumbnails or the raised border is clipped off. */
    padding: 0.4rem 0 0.4rem;
    scrollbar-width: thin;
}
.thumb {
    flex: none;
    display: grid;
    place-items: center;
    gap: 0.3rem;
    width: 5.6rem;
    padding: 0.55rem 0.3rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--glass-soft);
    color: var(--dim);
    font-size: 0.6rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    cursor: pointer;
    transition: border-color var(--speed) var(--ease), color var(--speed) var(--ease),
                background var(--speed) var(--ease), transform var(--speed) var(--ease);
}
.thumb:hover { color: var(--text); border-color: var(--line-2); transform: translateY(-2px); }
.thumb.is-on { color: #fff; border-color: var(--pink); background: var(--neon-soft); }
.thumb .ic { width: 1.35rem; height: 1.35rem; opacity: 0.85; }

.tabs {
    display: flex;
    gap: 0.15rem;
    margin-top: clamp(1.6rem, 3.5vh, 2.4rem);
    border-bottom: 1px solid var(--line);
    overflow-x: auto;
    scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab {
    flex: none;
    margin-bottom: -1px;
    padding: 0.75rem 1.1rem;
    border: 0;
    border-bottom: 2px solid transparent;
    background: none;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--dim);
    cursor: pointer;
    transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.tab:hover { color: var(--text); }
.tab.is-on { color: #fff; border-bottom-color: transparent; background-image: var(--neon); background-size: 100% 2px; background-position: bottom; background-repeat: no-repeat; }

.tabpanel { padding-top: clamp(1.2rem, 2.5vh, 1.75rem); }
.tabpanel[hidden] { display: none; }

/* Numbered feature bullets - the same rhythm as the docs pages, but
   compact enough to live inside a tab. */
.bullets { display: grid; gap: 1.15rem; margin-top: 1.35rem; }
.bullet { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.85rem; align-items: start; }
.bullet__no {
    font-family: var(--px);
    font-size: 1rem;
    font-weight: 700;
    line-height: 1.7;
    background: var(--neon);
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}
.bullet h4 { font-size: 1.02rem; font-weight: 600; }
.bullet p { margin-top: 0.2rem; font-size: 0.98rem; font-weight: 400; color: var(--muted); }

.buycard { padding: clamp(1.25rem, 2.5vw, 1.7rem); border-radius: var(--r-md); }
/* Sticking the card alone left the trust box and the subscription nudge to
   scroll up underneath it. The column is what should stick. */
@media (min-width: 64rem) {
    .product__aside { position: sticky; top: calc(var(--head-clear) - 4.75rem); }
}
.buycard__name {
    margin: 0.5rem 0 0.15rem;
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: clamp(1.5rem, 1.2rem + 1.2vw, 2.1rem);
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.15;
}
.buycard__desc { font-size: 0.97rem; font-weight: 400; color: var(--muted); }
.buycard__price { margin: 1rem 0 1.15rem; }
.buycard__price .price { font-size: clamp(1.8rem, 1.5rem + 1.2vw, 2.4rem); }
.buycard__tags { display: flex; flex-wrap: wrap; gap: 0.35rem; margin-top: 1.1rem; }
.buycard .btn + .btn, .buycard__actions + .btn { margin-top: 0.6rem; }
.buycard__actions { display: grid; grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr); gap: 0.6rem; }
.buycard__actions .btn { width: 100%; padding-inline: 0.9rem; }
.buycard__actions .btn + .btn { margin-top: 0; }
.buycard__meta {
    display: grid;
    gap: 0.4rem;
    margin-top: 1.15rem;
    padding-top: 1.1rem;
    border-top: 1px solid var(--line);
    font-size: 0.82rem;
    color: var(--muted);
}
.buycard__meta div { display: flex; justify-content: space-between; gap: 1rem; }
.buycard__meta .ic { display: inline-block; vertical-align: -0.14em; margin-right: 0.45rem; color: var(--dim); }
.buycard__meta dt, .buycard__meta span:first-child { color: var(--dim); }

/* Who takes the money, said plainly. Trust is the one thing a small
   shop has to spell out. */
.trust {
    margin-top: 0.9rem;
    padding: 1rem 1.1rem;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: var(--glass-soft);
}
.trust__head {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--text);
}
.trust__head .ic { color: #fff; filter: drop-shadow(0 0 0.45rem rgba(139, 92, 246, 0.6)); }
.trust__note { margin-top: 0.45rem; font-size: 0.87rem; font-weight: 400; color: var(--dim); }
.trust__grid {
    display: grid;
    gap: 0.4rem 1rem;
    grid-template-columns: repeat(auto-fit, minmax(9.5rem, 1fr));
    margin-top: 0.85rem;
}
.trust__item { display: flex; gap: 0.45rem; font-size: 0.89rem; color: var(--muted); }
.trust__item::before { content: '\2713'; color: var(--ok); }

/* Recommended row. A strip that scrolls rather than three big cards,
   so it reads as a footnote to the product and not a second shop. */
/* A drifting row rather than a scrolling one, like the category ticker on the
   home page: no scrollbar to look at, and it carries on by itself. The track
   holds the cards twice so the loop has nothing to jump over, and hovering
   pauses it so a card can actually be read and clicked. */
.strip {
    overflow: hidden;
    padding: 0.5rem 0 0.6rem;
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
            mask-image: linear-gradient(90deg, transparent, #000 4%, #000 96%, transparent);
}
.strip__track {
    display: flex;
    width: max-content;
    gap: clamp(0.7rem, 1.5vw, 1rem);
    animation: stripDrift 46s linear infinite;
    will-change: transform;
}
.strip:hover .strip__track, .strip:focus-within .strip__track { animation-play-state: paused; }
.strip__set { display: flex; gap: clamp(0.7rem, 1.5vw, 1rem); padding-right: clamp(0.7rem, 1.5vw, 1rem); }
@keyframes stripDrift { to { transform: translateX(-50%); } }
@media (prefers-reduced-motion: reduce) {
    .strip { overflow-x: auto; overflow-y: hidden; }
    .strip__track { animation: none; }
}

/* Same treatment wherever else a row scrolls sideways. */
.gallery__thumbs { scrollbar-color: rgba(167, 139, 250, 0.5) rgba(var(--w), 0.05); }
.gallery__thumbs::-webkit-scrollbar { height: 0.35rem; }
.gallery__thumbs::-webkit-scrollbar-track { border-radius: 999px; background: rgba(var(--w), 0.05); }
.gallery__thumbs::-webkit-scrollbar-thumb { border-radius: 999px; background: var(--neon); }
.strip__card {
    flex: none;
    width: clamp(11rem, 24vw, 13.5rem);
    scroll-snap-align: start;
    border-radius: var(--r-md);
    overflow: hidden;
}

/* Gradient rim on hover (owner, 2026-08-26). Drawn as a masked ring rather
   than the two-background trick the pills use: that one needs an opaque fill
   painted to the padding box to hide the gradient everywhere except the edge,
   and this card is deliberately translucent glass - an opaque fill would make
   it visibly denser the moment you point at it. A ring masked out of its own
   middle sits on top of whatever is underneath and changes nothing else. Same
   idiom as .tier--best::before, minus the spin.

   inset: 0 puts it just inside the border, so the border itself goes
   transparent on hover and the rim becomes the edge - otherwise you see both
   and the card looks doubly outlined. */
.strip__card::after {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 3;
    padding: 1.5px;
    border-radius: inherit;
    pointer-events: none;
    background: var(--neon);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
            mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask-composite: exclude;
    opacity: 0;
    transition: opacity 0.4s var(--ease);
}
.strip__card:hover::after { opacity: 1; }
.strip__shot {
    position: relative;
    display: grid;
    place-items: center;
    aspect-ratio: 16 / 10;
    border-bottom: 1px solid var(--line);
    background: linear-gradient(150deg, rgba(var(--w),0.05), rgba(var(--w),0.012));
}
.strip__shot svg { width: 32%; aspect-ratio: 1; opacity: 0.55; }
.strip__body { display: block; padding: 0.75rem 0.85rem 0.9rem; }
.strip__cat, .strip__name, .strip__price { display: block; }
.strip__cat { font-size: 0.62rem; font-weight: 600; letter-spacing: 0.18em; text-transform: uppercase; color: var(--dim); }
.strip__name { margin-top: 0.25rem; font-size: 1.01rem; font-weight: 600; }
.strip__price {
    margin-top: 0.35rem;
    font-size: 1.02rem;
    font-weight: 700;
    font-variant-numeric: tabular-nums;
    background: var(--neon);
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}

/* The bottom buy bar is the phone answer to the sticky card, so it
   stands down once the card itself is on screen. */
@media (min-width: 64rem) { .buybar { display: none; } }

/* ==============================================================
   Shop: filter rail
   ============================================================== */

.shop { display: grid; gap: clamp(1.4rem, 3vw, 2.4rem); align-items: start; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 64rem) { .shop { grid-template-columns: 15.5rem minmax(0, 1fr); } }

.rail { padding: 1.15rem; border-radius: var(--r-md); }
@media (min-width: 64rem) { .rail { position: sticky; top: calc(var(--head-clear) - 4.75rem); } }

.rail__label {
    padding: 0 0.6rem 0.5rem;
    font-size: 0.66rem;
    font-weight: 600;
    letter-spacing: 0.22em;
    text-transform: uppercase;
    color: var(--dim);
}
.rail__label + .rail__label, .rail__group + .rail__label { padding-top: 1.1rem; }
.rail__group { display: grid; gap: 0.15rem; }
.rail__cat {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    padding: 0.5rem 0.6rem;
    border: 0;
    border-radius: var(--r-sm);
    background: none;
    color: var(--muted);
    font-size: 0.99rem;
    text-align: left;
    cursor: pointer;
    transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.rail__cat:hover { background: var(--panel); color: #fff; }
.rail__cat.is-on { background: var(--neon-soft); color: #fff; box-shadow: inset 0 0 0 1px rgba(236, 72, 153, 0.3); }
.rail__cat .ic { color: var(--dim); }
.rail__cat.is-on .ic { color: var(--pink); }
.rail__count { margin-left: auto; font-size: 0.75rem; color: var(--dim); font-variant-numeric: tabular-nums; }
.rail__clear {
    width: 100%;
    margin-top: 1.1rem;
    padding: 0.55rem 0.6rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: none;
    color: var(--muted);
    font-size: 0.78rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    cursor: pointer;
    transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.rail__clear:hover { color: #fff; border-color: var(--pink); box-shadow: 0 0 1rem rgba(236, 72, 153, 0.2); }
.rail .search { margin-bottom: 1.1rem; }

/* Narrower cards beside the rail, so the grid still holds three across
   on a desktop instead of dropping to two. */
.shop .catalog { grid-template-columns: repeat(auto-fill, minmax(min(100%, 16.5rem), 1fr)); }

/* Type filters as pills: a row that wraps, above the results it changes. */
.typebar {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-bottom: clamp(0.9rem, 2vh, 1.2rem);
}
.pill {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.35rem 0.8rem;
    /* Gradient rim: transparent border, fill to the padding box, gradient to
       the border box. Same recipe as the buttons and the website's chips. */
    border: 1.5px solid transparent;
    border-radius: 999px;
    background: var(--pill-base) padding-box, var(--neon) border-box;
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 0.85rem;
    color: var(--muted);
    cursor: pointer;
    transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease),
                background var(--speed) var(--ease);
}
.pill:hover { color: #fff; }
.pill.is-on {
    color: #fff;
    border-color: transparent;
    background: var(--neon-soft) padding-box, var(--neon) border-box;
}
.pill__n { font-size: 0.75rem; color: var(--dim); font-variant-numeric: tabular-nums; }
.pill.is-on .pill__n { color: rgba(var(--w), 0.75); }

.results {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
    margin-bottom: clamp(1rem, 2vh, 1.4rem);
}
.results__count { font-size: 0.9rem; color: var(--muted); }
.results__count b { color: var(--text); font-variant-numeric: tabular-nums; }
.sort { display: flex; align-items: center; gap: 0.5rem; font-size: 0.72rem; letter-spacing: 0.18em; text-transform: uppercase; color: var(--dim); }
.sort select {
    padding: 0.45rem 0.75rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: var(--glass-soft);
    color: var(--text);
    font: inherit;
    font-size: 0.85rem;
    letter-spacing: 0;
    text-transform: none;
    cursor: pointer;
}
.sort select:hover { border-color: var(--line-2); }


/* A state, not a decoration: used where something on the site is
   deliberately switched off, so the page never reads as broken. */
.notice {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.9rem;
    margin-bottom: clamp(1.4rem, 3vh, 2rem);
    padding: 1rem 1.25rem;
    border: 1px solid rgba(167, 139, 250, 0.35);
    border-radius: var(--r-md);
    background: rgba(167, 139, 250, 0.08);
}
.notice .ic { color: #fff; filter: drop-shadow(0 0 0.5rem rgba(236, 72, 153, 0.6)); }
.notice__text { flex: 1 1 18rem; }
.notice__text b {
    font-family: 'Outfit', system-ui, sans-serif;
    background: var(--neon);
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}
.notice__text span { display: block; margin-top: 0.25rem; font-size: 0.96rem; font-weight: 400; color: var(--muted); }


/* ==============================================================
   Select, dressed
   The real <select> stays in the markup and keeps working; it is
   made invisible but still focusable, and this panel is drawn in
   its place so the menu matches the site instead of the OS.
   ============================================================== */

.sel { position: relative; display: inline-flex; }
.sel select {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    opacity: 0;
    pointer-events: none;
}
.sel__button {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    padding: 0.5rem 0.85rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: rgba(var(--w), 0.03);
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 0.9rem;
    color: var(--text);
    cursor: pointer;
    text-align: left;
    transition: border-color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.sel__button:hover { border-color: var(--line-2); }
.sel__caret { margin-left: auto; font-size: 0.6rem; color: var(--dim); transition: transform var(--speed) var(--ease); }
.sel.is-open .sel__button { border-color: transparent; background:
    linear-gradient(var(--fill-solid), var(--fill-solid)) padding-box,
    var(--neon) border-box; }
.sel.is-open .sel__caret { transform: rotate(180deg); color: var(--pink); }

.sel__menu {
    position: absolute;
    top: calc(100% + 0.4rem);
    right: 0;
    z-index: 60;
    min-width: 100%;
    width: max-content;
    max-width: min(22rem, 80vw);
    padding: 0.35rem;
    border: 1px solid var(--line-2);
    border-radius: var(--r-md);
    background: var(--sheet-pop);
    box-shadow: 0 1.2rem 2.5rem rgba(var(--k), 0.6), inset 0 1px 0 rgba(var(--w), 0.07);
    backdrop-filter: blur(22px) saturate(160%);
    -webkit-backdrop-filter: blur(22px) saturate(160%);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-0.35rem);
    transition: opacity 0.2s var(--ease), transform 0.2s var(--ease-out), visibility 0.2s;
}
.sel.is-open .sel__menu { opacity: 1; visibility: visible; transform: none; }

.sel__option {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    width: 100%;
    padding: 0.5rem 0.7rem;
    border: 0;
    border-radius: var(--r-sm);
    background: none;
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 0.92rem;
    color: var(--muted);
    text-align: left;
    cursor: pointer;
    transition: background var(--speed) var(--ease), color var(--speed) var(--ease);
}
.sel__option:hover { background: var(--panel); color: #fff; }
.sel__option.is-on { background: var(--neon-soft); color: #fff; }
.sel__option.is-on::after { content: '\2713'; margin-left: auto; color: var(--ok); font-size: 0.85rem; }

/* In the results bar the control should fill the space the old select had. */
.sort .sel { min-width: 11rem; }
.field .sel { width: 100%; }
.field .sel__button { padding: 0.65rem 0.85rem; font-size: 0.97rem; }

/* ==============================================================
   Contact form and live chat
   Two ways in, said in one place: a form for people who want to
   write and leave, and a chat for people who want an answer now.
   Both end up in the same Discord tickets, which is the honest
   thing to say on the page rather than pretend they are separate
   support desks.
   ============================================================== */

.contact { display: grid; gap: clamp(1.4rem, 3vw, 2.4rem); align-items: start; grid-template-columns: minmax(0, 1fr); }
@media (min-width: 64rem) { .contact { grid-template-columns: minmax(0, 1.35fr) minmax(18rem, 1fr); } }

.form { display: grid; gap: 1rem; padding: clamp(1.3rem, 2.6vw, 1.9rem); border-radius: var(--r-md); }
.form__row { display: grid; gap: 1rem; grid-template-columns: minmax(0, 1fr); align-items: start; }
@media (min-width: 40rem) { .form__row { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.field { display: grid; gap: 0.4rem; align-content: start; }
.field > span {
    font-size: 0.68rem;
    font-weight: 600;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--dim);
}
.field input, .field select, .field textarea {
    width: 100%;
    padding: 0.65rem 0.85rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: rgba(var(--w), 0.03);
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 0.97rem;
    color: var(--text);
    outline: none;
    transition: border-color var(--speed) var(--ease), background-color var(--speed) var(--ease);
}
.field textarea { min-height: 9rem; resize: vertical; line-height: 1.65; }
.field input:focus, .field select:focus, .field textarea:focus {
    border-color: transparent;
    background:
        linear-gradient(var(--fill-solid), var(--fill-solid)) padding-box,
        var(--neon) border-box;
}
.field input::placeholder, .field textarea::placeholder { color: var(--dim); }
/* Belt and braces: some platforms paint the list from the option itself. */
.field select option, .sort select option { background-color: var(--fill-solid); color: var(--text); }

.form__note { font-size: 0.88rem; font-weight: 400; color: var(--dim); }
.field__hint { font-size: 0.8rem; font-style: normal; color: var(--dim); }

/* The sent state replaces the form rather than sitting under it, so
   nobody wonders whether it went through. */
.sent { display: grid; gap: 0.6rem; place-items: center; padding: clamp(2rem, 5vw, 3rem); text-align: center; }
.sent__mark { display: grid; place-items: center; width: 3rem; height: 3rem; border: 1.5px solid var(--lilac); border-radius: 50%; color: var(--lilac); font-size: 1.3rem; }
.sent p { font-size: 1.02rem; color: var(--muted); font-weight: 400; max-width: 26rem; }

.ways { display: grid; gap: 0.9rem; }

/* About page: a portrait card on the left, the story as small titled cards
   on the right. One thought per card so it can be skimmed. */
.maker {
    display: grid;
    gap: clamp(0.9rem, 1.8vw, 1.35rem);
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
}
@media (min-width: 62rem) {
    .maker { grid-template-columns: minmax(0, 19rem) minmax(0, 1fr); align-items: stretch; }
    .maker__side { display: flex; flex-direction: column; }
    .maker__side .btn-row { margin-top: 1rem; }
}

.maker__side { padding: clamp(1.3rem, 2.6vw, 1.75rem); border-radius: var(--r-lg); text-align: center; }
.maker__portrait {
    position: relative;
    aspect-ratio: 885 / 1080;
    margin-bottom: 1.1rem;
    border-radius: var(--r-md);
    overflow: hidden;
    border: 1px solid rgba(236, 72, 153, 0.35);
    box-shadow: var(--glow);
    background: var(--neon-soft);
}
.maker__photo {
    width: 100%;
    height: 100%;
    object-fit: cover;
    /* A whisper of the brand over a black-and-white photograph, and a lift at
       the bottom so the badge and the name below it stay legible. */
    filter: saturate(0) contrast(1.04);
}
.maker__portrait::after {
    content: '';
    position: absolute;
    inset: 0;
    pointer-events: none;
    background:
        linear-gradient(180deg, transparent 55%, rgba(var(--k), 0.55) 100%),
        radial-gradient(80% 60% at 80% 0%, rgba(236, 72, 153, 0.18), transparent 70%),
        radial-gradient(70% 60% at 10% 100%, rgba(139, 92, 246, 0.20), transparent 70%);
}
.maker__moon {
    position: absolute;
    left: 1%;
    top: 11%;
    z-index: 2;
    width: 26%;
    aspect-ratio: 1;
    filter: drop-shadow(0 0 0.9rem rgba(167, 139, 250, 0.6)) drop-shadow(0 0 2rem rgba(236, 72, 153, 0.4));
    animation: heroMoonFloat 7s ease-in-out infinite;
    will-change: transform;
}
@media (prefers-reduced-motion: reduce) { .maker__moon { animation: none; } }

.maker__badge {
    position: absolute;
    right: 0.7rem;
    bottom: 0.7rem;
    width: 2.4rem;
    aspect-ratio: 1;
    opacity: 0.9;
    filter: drop-shadow(0 0 0.6rem rgba(236, 72, 153, 0.6));
}
.maker__name { font-family: var(--px); font-size: 1.6rem; font-weight: 700; line-height: 1.1; }
.maker__role {
    margin-top: 0.25rem;
    font-size: 0.7rem;
    font-weight: 600;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    background: var(--neon);
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}
.maker__tag { margin-top: 0.75rem; font-size: 0.95rem; color: var(--muted); }
.maker__side .socials { justify-content: center; margin-top: auto; padding-top: clamp(1.4rem, 4vh, 2.6rem); }

.notes { display: grid; gap: clamp(0.9rem, 1.8vw, 1.35rem); grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr)); }
.note { padding: clamp(1.2rem, 2.4vw, 1.6rem); border-radius: var(--r-lg); }
.note__head { display: flex; align-items: center; gap: 0.6rem; margin-bottom: 0.6rem; }
.note__head .ic { color: #fff; filter: drop-shadow(0 0 0.5rem rgba(236, 72, 153, 0.6)); }
.note p { font-size: 0.96rem; color: var(--muted); }
.note p b { color: var(--text); }

/* Team cards: portrait, name, role in the accent - the reference layout,
   at the size a three-person team actually needs. */
.team { display: grid; gap: clamp(0.9rem, 1.8vw, 1.35rem); grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr)); }
.member { padding: clamp(1.4rem, 2.6vw, 1.9rem); border-radius: var(--r-lg); text-align: center; transition: transform 0.35s var(--ease-out), border-color 0.35s var(--ease); }
.member__avatar {
    display: grid;
    place-items: center;
    width: clamp(4.5rem, 8vw, 5.75rem);
    aspect-ratio: 1;
    margin: 0 auto 1rem;
    border-radius: 50%;
    border: 1px solid rgba(236, 72, 153, 0.4);
    background: var(--neon-soft);
    box-shadow: var(--glow);
    color: #fff;
}
.member__avatar svg { width: 45%; aspect-ratio: 1; fill: none; stroke: currentColor; stroke-width: 1.85; stroke-linecap: round; stroke-linejoin: round; }
.member__name { font-family: 'Outfit', system-ui, sans-serif; font-size: 1.15rem; font-weight: 700; }
.member__role {
    margin-top: 0.2rem;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    background: var(--neon);
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}
.member__bio { margin-top: 0.85rem; font-size: 0.94rem; color: var(--muted); }

/* What the work is actually done with. Two rows of chips rather than prose,
   because this is a list and reads faster as one. */
.stack {
    grid-column: 1 / -1;
    display: grid;
    gap: clamp(1rem, 2vw, 1.4rem);
    padding: clamp(1.2rem, 2.4vw, 1.7rem);
    border-radius: var(--r-lg);
}
.stack__row { display: grid; gap: 0.7rem; grid-template-columns: minmax(0, 1fr); align-items: start; }
@media (min-width: 48rem) { .stack__row { grid-template-columns: 10rem minmax(0, 1fr); gap: 1.2rem; } }
.stack__label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--dim);
    padding-top: 0.3rem;
}
.stack__label .ic { color: var(--pink); }
.chips { display: flex; flex-wrap: wrap; gap: 0.4rem; }
.chip {
    padding: 0.3rem 0.8rem;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: rgba(var(--w), 0.03);
    font-size: 0.88rem;
    color: var(--muted);
    transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.chip:hover { color: #fff; border-color: rgba(236, 72, 153, 0.45); background: var(--neon-soft); }

.elsewhere { display: grid; gap: 0.9rem; grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); }
.elsewhere .way { transition: transform 0.35s var(--ease-out), border-color 0.35s var(--ease); }
.way { padding: clamp(1.1rem, 2.2vw, 1.4rem); border-radius: var(--r-md); }
.way__head { display: flex; align-items: center; gap: 0.6rem; }
.way__head .ic { color: #fff; filter: drop-shadow(0 0 0.5rem rgba(236, 72, 153, 0.6)); }
.way__head h3 { font-size: 1.02rem; }
.way p { margin-top: 0.55rem; font-size: 0.98rem; font-weight: 400; color: var(--muted); }
.way .btn { margin-top: 0.9rem; }
.way__meta { display: flex; justify-content: space-between; gap: 1rem; margin-top: 0.9rem; padding-top: 0.85rem; border-top: 1px solid var(--line); font-size: 0.88rem; color: var(--dim); }
.way__meta b { color: var(--text); font-weight: 600; }

/* Tooltips. One attribute, one rule, no library: anything with data-tip
   gets a label above it on hover and on keyboard focus. */
[data-tip] { position: relative; }
[data-tip]::after {
    content: attr(data-tip);
    position: absolute;
    bottom: calc(100% + 0.5rem);
    left: 50%;
    transform: translate(-50%, 0.3rem);
    padding: 0.35rem 0.65rem;
    border: 1px solid var(--line-2);
    border-radius: var(--r-sm);
    background: var(--sheet-tip);
    box-shadow: 0 0.6rem 1.5rem rgba(var(--k), 0.55);
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 0.78rem;
    font-weight: 500;
    letter-spacing: 0.02em;
    color: var(--text);
    white-space: nowrap;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition: opacity 0.2s var(--ease), transform 0.2s var(--ease-out), visibility 0.2s;
    z-index: 50;
}
[data-tip]:hover::after, [data-tip]:focus-visible::after {
    opacity: 1;
    visibility: visible;
    transform: translate(-50%, 0);
}
/* The back-to-top button sits at the bottom edge, so its label goes beside
   it rather than above, where it would run off the viewport. */
.totop[data-tip]::after { bottom: 50%; left: calc(100% + 0.6rem); transform: translate(-0.3rem, 50%); }
.totop[data-tip]:hover::after { transform: translate(0, 50%); }

/* Social row: circular icon buttons, lit on hover like everything else. */
.socials { display: flex; flex-wrap: wrap; gap: 0.5rem; }
/* A team card is centred, so its row of icons has to be too - the maker card
   it borrows this from is left-aligned. */
.member .socials { justify-content: center; margin: 0.85rem 0 0.2rem; }
.social {
    display: grid;
    place-items: center;
    width: 2.6rem;
    height: 2.6rem;
    border: 1.5px solid transparent;
    border-radius: 50%;
    background: var(--pill-base) padding-box, var(--neon) border-box;
    color: var(--muted);
    transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease),
                background var(--speed) var(--ease), transform var(--speed) var(--ease);
}
/* The Discord entry is a handle, not a link - it keeps the look but not the
   cursor, so nobody clicks expecting to go somewhere. */
.social--tag { cursor: default; }

.social:hover {
    color: #fff;
    border-color: transparent;
    background: var(--neon-soft) padding-box, var(--neon) border-box;
    transform: translateY(-2px);
}
.social .ic { width: 1.15rem; height: 1.15rem; }

/* Back to top. Left side, mirroring the chat bubble on the right, and it
   only exists once you are far enough down for it to be useful. */
.totop {
    position: fixed;
    left: clamp(1rem, 3vw, 1.75rem);
    bottom: 4.75rem;
    z-index: 160;
    display: grid;
    place-items: center;
    width: 2.9rem;
    height: 2.9rem;
    /* Gradient rim, same as the buttons and the notice. */
    border: 1.5px solid transparent;
    border-radius: 50%;
    background: var(--sheet-pop) padding-box, var(--neon) border-box;
    box-shadow: 0 0.75rem 2rem rgba(var(--k), 0.5), inset 0 1px 0 rgba(var(--w), 0.08);
    backdrop-filter: var(--blur-lite);
    -webkit-backdrop-filter: var(--blur-lite);
    color: var(--muted);
    font-size: 1.1rem;
    cursor: pointer;
    opacity: 0;
    visibility: hidden;
    transform: translateY(0.6rem);
    transition: opacity 0.3s var(--ease), transform 0.3s var(--ease-out),
                visibility 0.3s, color var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.totop.is-up { opacity: 1; visibility: visible; transform: none; }
/* The rim is a background layer, so border-color would paint a flat line over
   it. Brighten instead - and the lift has to be on .is-up, because the base
   transform is what keeps the button tucked away while it is hidden. */
.totop:hover { color: #fff; }
.totop.is-up:hover { transform: translateY(-2px); box-shadow: 0 0.75rem 2rem rgba(var(--k), 0.5), 0 0 1.4rem rgba(236, 72, 153, 0.4); }
@media (min-width: 64rem) { .totop { bottom: clamp(1rem, 3vh, 1.75rem); } }

/* ---- Privacy notice ----
   Bottom left, stacked above the back-to-top button. The right-hand corner
   belongs to the chat launcher, and on a product page the buy bar as well. */
.notice-card {
    position: fixed;
    left: clamp(1rem, 3vw, 1.75rem);
    bottom: calc(clamp(1rem, 3vh, 1.75rem) + 3.6rem);
    z-index: 250;
    display: grid;
    gap: 0.7rem;
    justify-items: start;
    width: min(23rem, calc(100vw - 2rem));
    padding: 1.15rem 1.3rem;
    /* Gradient rim, same as the buttons: transparent border, fill to the
       padding box, gradient to the border box. */
    border: 1.5px solid transparent;
    border-radius: var(--r-md);
    background: var(--sheet-pop) padding-box, var(--neon) border-box;
    box-shadow: 0 1.5rem 3rem rgba(var(--k), 0.55), var(--glow);
    opacity: 0;
    transform: translateY(0.75rem);
    transition: opacity 0.35s var(--ease), transform 0.35s var(--ease-out);
}
.notice-card.is-in { opacity: 1; transform: none; }

/* .notice-card sets its own display, which beats the [hidden] attribute - so
   the attribute has to be spelled out or a dismissed notice stays put. */
.notice-card[hidden] { display: none; }

.notice-card__title { display: flex; align-items: center; gap: 0.5rem; font-size: 0.95rem; font-weight: 600; }
.notice-card__title .ic { color: var(--pink); }
.notice-card__text { font-size: 0.86rem; line-height: 1.6; color: var(--muted); }

/* The link reads like every other link in running text: brand gradient, no
   underline at rest, one sweeping in from the left on hover. */
/* Gradient lettering with an underline that sweeps in from the left.

   Both are the same gradient, laid on the element twice: the first copy is
   clipped to the glyphs - that is the lettering - and the second is a flat bar
   parked along the bottom edge with zero width, grown to full width on hover.
   background-clip takes one value per layer, which is what makes the pair
   possible: `text` for the type, `padding-box` for the bar.

   It has to be a background rather than the two obvious alternatives. An
   absolutely positioned ::after collapses when a link wraps onto a second
   line, which is what hid this underline in the privacy notice.
   text-decoration wraps correctly but cannot be hidden at rest, because with
   background-clip:text the decoration is painted from the text's own
   background and ignores text-decoration-color. box-decoration-break: clone
   gives each line fragment its own bar, so a wrapped link is underlined all
   the way through. */
.notice-card__text a {
    background-image: var(--neon), var(--neon);
    -webkit-background-clip: text, padding-box;
            background-clip: text, padding-box;
    background-repeat: no-repeat;
    background-position: 0 0, 0 100%;
    background-size: 100% 100%, 0% 2px;
    color: transparent;
    -webkit-text-fill-color: transparent;
    -webkit-box-decoration-break: clone;
            box-decoration-break: clone;
    text-decoration: none;
    transition: background-size 0.35s var(--ease-out);
}
.notice-card__text a:hover {
    background-size: 100% 100%, 100% 2px;
}

/* Only in the notice: the link sits on its own line (owner, 2026-08-26). This
   is a narrow column, so inline it always broke mid-phrase - "What we" on one
   line and "do with data" on the next. Everywhere else links stay inline; this
   is the one place the column is too narrow for that to read.
   fit-content keeps the sweeping underline the width of the words rather than
   the width of the box. The full stop moved inside the link with it, so it
   travels on the same line instead of being left behind as an orphan. */
.notice-card__text a {
    display: block;
    width: fit-content;
    margin-top: 0.45rem;
}



/* On a phone the back-to-top button and the chat launcher already own both
   bottom corners, so the notice takes the full width above them. */
@media (max-width: 40rem) {
    .notice-card {
        left: 1rem;
        right: 1rem;
        width: auto;
        bottom: calc(clamp(1rem, 3vh, 1.75rem) + 4rem);
    }
}

/* Chat launcher. Sits above the phone buy bar so the two never stack
   on top of each other. */
.chat { position: fixed; right: clamp(1rem, 3vw, 1.75rem); bottom: 4.75rem; z-index: 160; display: grid; justify-items: end; gap: 0.75rem; }
@media (min-width: 64rem) { .chat { bottom: clamp(1rem, 3vh, 1.75rem); } }

.chat__launcher {
    display: inline-flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.7rem 1.15rem;
    border: 1.5px solid transparent;
    border-radius: 999px;
    background: var(--sheet-pop) padding-box, var(--neon) border-box;
    box-shadow: 0 1rem 2.5rem rgba(var(--k), 0.55), inset 0 1px 0 rgba(var(--w), 0.08);
    backdrop-filter: var(--blur-lite);
    -webkit-backdrop-filter: var(--blur-lite);
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 0.9rem;
    font-weight: 600;
    color: var(--text);
    cursor: pointer;
    transition: transform var(--speed) var(--ease-out), border-color var(--speed) var(--ease);
}
.chat__launcher:hover { transform: translateY(-2px); }
.chat__launcher .ic { color: #fff; filter: drop-shadow(0 0 0.5rem rgba(236, 72, 153, 0.7)); }
/* A steady dot says "there is a dot". A breathing one says somebody is
   there - which is the whole claim the button is making. */
.chat__dot {
    width: 0.45rem;
    height: 0.45rem;
    border-radius: 50%;
    background: var(--ok);
    box-shadow: 0 0 0.5rem var(--ok);
    animation: chatPulse 2.4s var(--ease) infinite;
}
@keyframes chatPulse {
    0%, 100% { transform: scale(1);    box-shadow: 0 0 0.5rem var(--ok); }
    50%      { transform: scale(1.35); box-shadow: 0 0 0.9rem var(--ok), 0 0 1.6rem rgba(110, 231, 183, 0.5); }
}
@media (prefers-reduced-motion: reduce) { .chat__dot { animation: none; } }

.chat__panel {
    width: min(21rem, calc(100vw - 2rem));
    border-radius: var(--r-md);
    overflow: hidden;
    opacity: 0;
    visibility: hidden;
    transform: translateY(0.75rem) scale(0.98);
    transform-origin: bottom right;
    transition: opacity 0.22s var(--ease), transform 0.22s var(--ease-out), visibility 0.22s;
}
.chat__panel.is-open { opacity: 1; visibility: visible; transform: none; }

.chat__head { display: flex; align-items: center; gap: 0.6rem; padding: 0.9rem 1rem; border-bottom: 1px solid var(--line); }
.chat__head h3 { font-size: 0.98rem; }
.chat__head .chat__dot { margin-left: auto; }
.chat__close { border: 0; background: none; color: var(--dim); font-size: 1.1rem; line-height: 1; cursor: pointer; padding: 0 0.15rem; }
.chat__close:hover { color: #fff; }

.chat__body { display: grid; gap: 0.6rem; padding: 1rem; }
.chat__msg { padding: 0.75rem 0.9rem; border: 1px solid var(--line); border-radius: var(--r-sm); background: rgba(var(--w), 0.03); font-size: 0.95rem; font-weight: 400; color: var(--muted); }
.chat__msg b { color: var(--text); font-weight: 600; }
.chat__form { display: grid; gap: 0.6rem; padding: 0 1rem 1rem; }
.chat__form textarea {
    min-height: 5rem;
    padding: 0.65rem 0.8rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: rgba(var(--w), 0.03);
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 0.92rem;
    color: var(--text);
    outline: none;
    resize: vertical;
}
.chat__form textarea:focus {
    border-color: transparent;
    background:
        linear-gradient(var(--fill-solid), var(--fill-solid)) padding-box,
        var(--neon) border-box;
}
.chat__alt { font-size: 0.76rem; color: var(--dim); text-align: center; }


/* ==============================================================
   Docs
   ============================================================== */

.docs {
    display: grid;
    gap: clamp(1.5rem, 3vw, 2.5rem);
    grid-template-columns: minmax(0, 1fr);
    align-items: start;
}
@media (min-width: 60rem) { .docs { grid-template-columns: 16rem minmax(0, 1fr); } }

.docs__side { padding: 1.1rem; border-radius: var(--r-md); }
@media (min-width: 60rem) { .docs__side { position: sticky; top: calc(var(--head-clear) - 3.5rem); } }

.docs__group { margin-bottom: 1.1rem; }
.docs__group:last-child { margin-bottom: 0; }
.docs__label { font-size: 0.68rem; font-weight: 600; letter-spacing: 0.22em; text-transform: uppercase; color: var(--dim); padding: 0 0.6rem 0.5rem; }
.docs__link {
    display: block;
    width: 100%;
    padding: 0.5rem 0.6rem;
    border: 0;
    border-radius: var(--r-sm);
    background: none;
    text-align: left;
    font-size: 0.94rem;
    color: var(--muted);
    cursor: pointer;
    transition: background-color var(--speed) var(--ease), color var(--speed) var(--ease);
}
.docs__link:hover { background: var(--panel-2); color: #fff; }
.docs__link.is-on { background: var(--neon-soft); color: #fff; }

.doc h2 { margin-top: 2rem; font-size: clamp(1.3rem, 1.1rem + 0.9vw, 1.75rem); }
.doc h2:first-child { margin-top: 0; }
.doc p { margin-top: 0.9rem; font-size: 1.03rem; line-height: 1.78; color: var(--muted); font-weight: 400; }
.doc ul, .doc ol { margin: 0.9rem 0 0 1.2rem; font-size: 1.03rem; color: var(--muted); font-weight: 400; }
.doc li { margin-top: 0.4rem; }
.doc code {
    padding: 0.1rem 0.4rem;
    border: 1px solid var(--line);
    border-radius: 0.3rem;
    background: rgba(var(--w), 0.04);
    font-family: ui-monospace, Consolas, monospace;
    font-size: 0.88em;
    color: var(--lilac);
}
.doc pre {
    margin-top: 0.85rem;
    padding: 1rem 1.1rem;
    border: 1px solid var(--line);
    border-radius: var(--r-sm);
    background: rgba(var(--k), 0.35);
    overflow-x: auto;
    font-family: ui-monospace, Consolas, monospace;
    font-size: 0.85rem;
    line-height: 1.7;
    color: var(--muted);
}
.doc pre code { border: 0; background: none; padding: 0; color: inherit; }

.callout {
    display: flex;
    gap: 0.85rem;
    margin-top: 1.25rem;
    padding: 0.95rem 1.1rem;
    border-left: 2px solid var(--pink);
    border-radius: 0 var(--r-sm) var(--r-sm) 0;
    background: rgba(167, 139, 250, 0.07);
}
.callout p { margin: 0; font-size: 0.92rem; }

/* Docs overview: a card per manual, so the section opens with what is
   available rather than dropping you straight into page one. */
.docstats { display: flex; flex-wrap: wrap; gap: clamp(1.5rem, 4vw, 3rem); margin: 1.5rem 0 0; padding-top: 1.25rem; border-top: 1px solid var(--line); }
.docstat__n { font-family: 'Outfit', system-ui, sans-serif; font-size: clamp(1.5rem, 1.2rem + 1vw, 2rem); font-weight: 700; color: var(--lilac); font-variant-numeric: tabular-nums; }
.docstat__l { font-size: 0.68rem; font-weight: 600; letter-spacing: 0.22em; text-transform: uppercase; color: var(--dim); }

.doccards { display: grid; gap: clamp(0.9rem, 1.8vw, 1.35rem); grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr)); margin-top: 1.75rem; }

.doccard {
    display: flex;
    flex-direction: column;
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    overflow: hidden;
    background: linear-gradient(160deg, var(--glass), var(--glass-soft));
    backdrop-filter: var(--blur-lite);
    -webkit-backdrop-filter: var(--blur-lite);
    text-align: left;
    cursor: pointer;
    transition: transform 0.35s var(--ease-out), border-color 0.35s var(--ease);
}

.doccard__shot {
    position: relative;
    aspect-ratio: 16 / 9;
    display: grid;
    place-items: center;
    background:
        radial-gradient(85% 100% at 50% 0%, rgba(167, 139, 250, 0.14), transparent 70%),
        var(--glass-veil);
    border-bottom: 1px solid var(--line);
}
.doccard__shot svg { width: 26%; aspect-ratio: 1; opacity: 0.45; transition: opacity 0.35s var(--ease); }
.doccard:hover .doccard__shot svg { opacity: 0.85; }
.doccard__pages {
    position: absolute;
    top: 0.6rem;
    right: 0.6rem;
    padding: 0.15rem 0.55rem;
    border: 1px solid var(--line-2);
    border-radius: var(--r-sm);
    background: rgba(8, 8, 10, 0.85);
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 0.7rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--muted);
}

.doccard__body { display: flex; flex-direction: column; flex: 1; padding: 1.1rem; }
.doccard__title { display: flex; align-items: center; gap: 0.6rem; font-size: 1.02rem; font-weight: 600; }
.doccard__blurb { margin-top: 0.5rem; font-size: 0.88rem; font-weight: 400; color: var(--muted); }
.doccard__foot { display: flex; flex-wrap: wrap; align-items: center; gap: 0.35rem; margin-top: auto; padding-top: 1rem; }
.doccard__read { margin-left: auto; font-family: 'Outfit', system-ui, sans-serif; font-size: 0.85rem; color: var(--lilac); white-space: nowrap; }

.docsearch { margin-bottom: 1rem; }
.docs__empty { padding: 0.6rem; font-size: 0.88rem; color: var(--dim); }

/* ==============================================================
   Subscription
   ============================================================== */

.subs {
    display: grid;
    gap: clamp(1.5rem, 4vw, 3rem);
    grid-template-columns: minmax(0, 1fr);
    align-items: center;
    padding: clamp(1.5rem, 4vw, 2.75rem);
    border-radius: var(--r-lg);
}
@media (min-width: 62rem) { .subs { grid-template-columns: minmax(0, 1fr) minmax(0, 1.15fr); } }

.subs__list { list-style: none; display: grid; gap: 0.55rem; margin-top: 1.25rem; }
.subs__list li { position: relative; padding-left: 1.5rem; font-size: 0.93rem; font-weight: 400; color: var(--muted); }
.subs__list li::before {
    content: '';
    position: absolute;
    left: 0.15rem;
    top: 0.55em;
    width: 0.55rem;
    height: 0.28rem;
    border-left: 1.5px solid var(--lilac);
    border-bottom: 1.5px solid var(--lilac);
    transform: rotate(-45deg);
}


/* All five plans in one band: the two packages on the left, the collections
   on the right. Five cards need more room than the site's reading width, so
   this section - and only this section - is allowed to be wider. */
/* Only this page goes wider than the site measure: six plan cards side by
   side is the one layout that needs it. */
.wrap--plans { max-width: 112rem; }

.plans { display: grid; gap: clamp(2rem, 5vw, 4.5rem); grid-template-columns: minmax(0, 1fr); }
@media (min-width: 78rem) {
    .plans { grid-template-columns: minmax(0, 1.08fr) minmax(0, 2.05fr); align-items: start; }
    /* A hairline in the gap, so the split reads as deliberate. */
    .plans__col + .plans__col { position: relative; }
    .plans__col + .plans__col::before {
        content: '';
        position: absolute;
        top: 0;
        bottom: 2rem;
        left: clamp(-2.25rem, -2.5vw, -1rem);
        width: 1px;
        background: linear-gradient(180deg, transparent, var(--line), transparent);
    }
}
.plans__col { display: grid; gap: clamp(0.85rem, 1.6vw, 1.15rem); align-content: start; }
.plans__head {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    flex-wrap: wrap;
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--text);
}
.plans__head .ic { color: var(--pink); filter: drop-shadow(0 0 0.5rem rgba(236, 72, 153, 0.55)); }
.plans__head span {
    font-size: 0.68rem;
    font-weight: 500;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--dim);
}
.plans .tiers { margin: 0; }
.plans__foot { margin-top: 0.6rem; font-size: 0.88rem; color: var(--muted); text-align: center; }
.plans__foot b { color: var(--text); }
@media (min-width: 78rem) {
    .plans .tiers--full { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: none; }
    .plans .tiers--lanes { grid-template-columns: repeat(4, minmax(0, 1fr)); }
    /* Tighter type inside five columns, or the cards read as a wall. */
    .plans .tier { padding: clamp(1.2rem, 1.6vw, 1.5rem); }
    .plans .tier__body { font-size: 0.9rem; }
    /* Every plan line holds one row and shrinks a little rather than wrap,
       so the five cards can be read across instead of down. */
    /* No nowrap. A promise long enough to be worth making is long enough to
       need a second line, and clipping it mid-word is worse than wrapping it -
       the hanging indent keeps tick and text aligned. */
    .plans .tier__list li { padding: 0 0.35rem 0 1.25rem; font-size: clamp(0.78rem, 0.6vw, 0.88rem); line-height: 1.45; }
    .plans .tier__price { font-size: clamp(1.6rem, 1.2rem + 1vw, 2rem); }
    .plans .tiers--full .tier--best { padding: clamp(1.35rem, 1.8vw, 1.7rem) clamp(1.2rem, 1.5vw, 1.45rem); }
    .plans .tiers--full .tier--best .tier__name { font-size: 1.2rem; }
    .plans .tiers--full .tier--best .tier__price { font-size: clamp(1.8rem, 1.3rem + 1.2vw, 2.3rem); }
    .plans .tiers--full .tier--best .tier__list { gap: 0.55rem; margin: 1rem 0 1.3rem; }
    .plans .tiers--full .tier--best .tier__list li { font-size: clamp(0.8rem, 0.62vw, 0.9rem); }
}

/* Billing period switch. Segmented rather than a dropdown: four options is
   few enough to show all at once, and seeing the savings is the point. */
.periods {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.4rem;
    margin: 0 auto clamp(1.5rem, 3.5vh, 2.25rem);
    padding: 0.4rem;
    width: fit-content;
    border: 1px solid var(--line);
    border-radius: 999px;
    background: rgba(var(--w), 0.03);
    position: relative;
}

/* The moving highlight. One element that travels, instead of a background
   appearing on one button and vanishing from another - which is a cut, not a
   movement, and reads as the row flickering.

   Position and size come from main.js as three custom properties, so the CSS
   never needs to know how many buttons there are or how wide their labels
   are. It travels in both axes because the row wraps to two lines on a narrow
   screen, and a thumb that only knew about x would sit on the wrong line. */
.periods__thumb {
    position: absolute;
    top: 0;
    left: 0;
    width: var(--thumb-w, 0);
    height: var(--thumb-h, 0);
    border-radius: 999px;
    background: var(--neon-soft);
    box-shadow: inset 0 0 0 1px rgba(236, 72, 153, 0.4);
    transform: translate3d(var(--thumb-x, 0), var(--thumb-y, 0), 0);
    transition: transform 0.42s var(--ease-out), width 0.42s var(--ease-out);
    pointer-events: none;
}
/* Before the first measurement, and while the window is being resized, the
   thumb is placed rather than animated - otherwise it flies in from the
   corner on arrival and chases the layout on every resize frame. */
.periods__thumb.is-instant { transition: none; }
.periods__thumb:not(.is-ready) { opacity: 0; }

@media (prefers-reduced-motion: reduce) {
    .periods__thumb { transition: none; }
}

.period {
    position: relative;
    z-index: 1;
    display: flex;
    align-items: center;
    gap: 0.45rem;
    padding: 0.5rem 1.05rem;
    border: 0;
    border-radius: 999px;
    background: none;
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--muted);
    cursor: pointer;
    transition: color var(--speed) var(--ease), background var(--speed) var(--ease);
}
.period:hover { color: #fff; }
.period.is-on { color: #fff; }
.period__save {
    padding: 0.05rem 0.45rem;
    border-radius: 999px;
    background: rgba(110, 231, 183, 0.14);
    font-size: 0.72rem;
    font-weight: 700;
    color: var(--ok);
}

/* The shimmer: a conic gradient turning behind the card, with the card's own
   surface laid back over the middle so only a moving rim is visible. The
   spinning layer is square and oversized, or its corners would sweep through
   the card as it turns. */
/* The rim, rebuilt as a ring that sits ON TOP of the card.
   The old version was a rotating square behind the surface, which meant
   anything else at the edge - the corner ribbon - painted over it and the
   light appeared to break. Here the conic gradient fills the card and a mask
   keeps only a 2px band of it, so the highlight travels the border above
   everything, and the angle animates rather than the element. */
@property --rim {
    syntax: '<angle>';
    inherits: false;
    initial-value: 0deg;
}

.tier--best { overflow: hidden; isolation: isolate; background: var(--sheet-best); }
.tier--best::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 4;
    padding: 2px;
    border-radius: inherit;
    pointer-events: none;
    background: conic-gradient(from var(--rim),
        rgba(167, 139, 250, 0) 0 58%,
        rgba(167, 139, 250, 0.55) 74%,
        rgba(var(--w), 0.95) 85%,
        rgba(236, 72, 153, 0.55) 95%,
        rgba(236, 72, 153, 0) 100%);
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
            mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
            mask-composite: exclude;
    animation: rimSpin 5.5s linear infinite;
}
/* Browsers without @property leave --rim un-animated; the ring still draws,
   it simply does not travel. */
@keyframes rimSpin { to { --rim: 360deg; } }
@media (prefers-reduced-motion: reduce) { .tier--best::before { animation: none; } }

.tiers { display: grid; gap: clamp(0.85rem, 1.8vw, 1.35rem); grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); align-items: start; }
/* The collections are versions of one choice, so they stand the same height
   and their lists read as the same checklist ticked differently. */
.tiers--lanes { align-items: stretch; }
.tiers--lanes .tier { height: 100%; }
/* Two plans read better as a pair than as two thirds of a row. */
@media (min-width: 62rem) {
    .tiers--full { grid-template-columns: repeat(2, minmax(0, 1fr)); max-width: 48rem; margin-inline: auto; align-items: center; }
    .tiers--full .tier--best {
        padding: clamp(1.9rem, 3vw, 2.4rem) clamp(1.8rem, 3vw, 2.3rem);
        align-self: stretch;
    }
    .tiers--full .tier--best .tier__kind { max-width: 60%; }
    .tiers--full .tier--best .tier__name { font-size: 1.4rem; }
    .tiers--full .tier--best .tier__price { font-size: clamp(2rem, 1.6rem + 1.6vw, 2.8rem); }
    .tiers--full .tier--best .tier__list { gap: 0.6rem; margin: 1.15rem 0 1.4rem; }
    .tiers--full .tier--best .tier__list li { font-size: 1rem; }
    /* Shorter, and centerd against the taller card beside it. */
    .tiers--full .tier:not(.tier--best) { padding-block: clamp(1.3rem, 2.2vw, 1.7rem); }
}

.tiers__lead {
    max-width: 52rem;
    margin: 0 auto clamp(1.25rem, 3vh, 1.75rem);
    font-size: 1rem;
    color: var(--text-2, var(--muted));
    line-height: 1.75;
    /* Left, not centerd. A centerd paragraph starts every line in a different
       place, so the eye has to hunt for it on the way back - the single
       typographic habit that hurts a dyslexic reader most. */
    text-align: left;
    text-wrap: pretty;
}
.tiers__lead br { content: ''; display: block; height: 0.5rem; }
.tiers__lead b { color: var(--text); }
.tier {
    position: relative;
    display: flex;
    flex-direction: column;
    padding: clamp(1.4rem, 2.6vw, 1.9rem);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: linear-gradient(160deg, var(--glass), var(--glass-soft));
    backdrop-filter: var(--blur-lite);
    -webkit-backdrop-filter: var(--blur-lite);
    box-shadow: 0 0 0 rgba(var(--k), 0), 0 0 0 rgba(236, 72, 153, 0);
    transition: transform 0.4s var(--ease-out),
                border-color 0.4s var(--ease),
                box-shadow 0.4s var(--ease);
}
/* The middle tier is the one most people should take, so it is lit rather
   than merely labeled. */
.tier--best { border-color: var(--line); box-shadow: 0 0 1.6rem rgba(236, 72, 153, 0.16); }
.tier--best:hover {
    border-color: rgba(236, 72, 153, 0.45);
    box-shadow: 0 1.4rem 2.8rem rgba(var(--k), 0.55), 0 0 2.4rem rgba(236, 72, 153, 0.3);
}
.tier__list { list-style: none; display: grid; gap: 0.5rem; margin: 1.1rem 0 1.35rem; }
.tier__list li { position: relative; padding-left: 1.5rem; font-size: 0.94rem; color: var(--muted); }
.tier__list li::before {
    content: '';
    position: absolute;
    left: 0.15rem;
    top: 0.55em;
    width: 0.55rem;
    height: 0.28rem;
    border-left: 1.5px solid var(--ok);
    border-bottom: 1.5px solid var(--ok);
    transform: rotate(-45deg);
}
.tier__above {
    margin-top: 0.3rem;
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: var(--muted);
}
.tier__above span { color: var(--dim); font-weight: 400; }

.tier__list li.is-off { color: var(--dim); }
.tier__list li.is-off::before {
    content: '✕';
    left: 0.1rem;
    top: 0;
    width: auto;
    height: auto;
    border: 0;
    transform: none;
    font-size: 0.85rem;
    font-weight: 700;
    line-height: 1.7;
    color: #F87171;
}
.tier .btn { margin-top: auto; }
.tiers__notes {
    display: grid;
    gap: 0.6rem clamp(1.5rem, 4vw, 3rem);
    width: fit-content;
    max-width: 74rem;
    margin: clamp(1rem, 2vh, 1.4rem) auto clamp(1.2rem, 2.5vh, 1.75rem);
}
@media (min-width: 62rem) { .tiers__notes { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
.tiers__note {
    display: grid;
    grid-template-columns: 1.3rem minmax(0, 1fr);
    gap: 0.6rem;
    align-items: start;
    font-size: 0.9rem;
    color: var(--muted);
    text-align: left;
}
.tiers__note .ic { margin-top: 0.25rem; color: var(--ok); }

/* Tier comparison. A tick or a cross in every cell, because a blank cell
   makes people wonder whether it was forgotten. */
/* A reading measure for whatever on this page is TEXT rather than a row of
   plan cards: the notice, both tables, the questions. The plan band is 112rem
   because five cards need that room - a sentence stretched that far is hard
   to follow back to the left edge. The cards themselves keep the full width. */
.matrix-card { max-width: 68rem; margin-inline: auto; }

/* One line of reassurance above the prices, and the whole explanation once,
   below them. Three boxes saying the same thing in different words was the
   problem, not the layout. */
.guarantee {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 0.55rem;
    margin: 0 auto clamp(1.4rem, 3vh, 2rem);
    color: var(--muted);
    text-align: center;
}
.guarantee .ic { color: var(--ok); flex: none; }
.explainer {
    max-width: 68rem;
    margin: 0 auto clamp(1.6rem, 3.5vh, 2.4rem);
    color: var(--text-2, var(--muted));
    line-height: 1.75;
    text-wrap: pretty;
}
.explainer b { color: var(--text); }
.explainer br { content: ''; display: block; height: 0.6rem; }

/* The comparison and its explanations, as one row: the table holds the left
   column, the answers fill three columns beside it. This row is deliberately
   wider than the reading measure above it - four columns of ticks and three
   of boxes need the space. */
.compare {
    display: grid;
    gap: clamp(0.7rem, 1.6vw, 1rem);
    grid-template-columns: minmax(0, 1fr);
    align-items: stretch;
    max-width: 92rem;
    margin-inline: auto;
}
@media (min-width: 90rem) {
    .compare { grid-template-columns: minmax(0, 1.3fr) minmax(0, 2fr); }
}
.compare__table { margin-bottom: 0 !important; }
.compare__side { display: grid; align-content: stretch; }

/* The promises and the pitch, side by side above the plans. Same total width
   as the notice above them - the page reads as one column until the plan
   cards deliberately break out of it. */
.prenote {
    display: grid;
    gap: clamp(0.7rem, 1.6vw, 1rem);
    grid-template-columns: minmax(0, 1fr);
    margin-bottom: clamp(1.4rem, 3vh, 2rem);
}
@media (min-width: 52rem) {
    .prenote { grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: stretch; }
}
.prenote__box {
    display: grid;
    align-content: center;
    gap: 0.85rem;
    padding: clamp(1rem, 2vw, 1.4rem);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: linear-gradient(160deg, var(--glass), var(--glass-soft));
}
.prenote .tiers__lead, .prenote .tiers__notes { margin: 0; max-width: none; }

/* The explanations, as boxes rather than table rows. A row that spans the
   full width makes the eye travel from a two-word label to a four-line
   answer; a box keeps the question and its answer in one place. */
.explain__lead {
    max-width: 68ch;
    margin: 0 0 clamp(0.9rem, 2vh, 1.3rem);
    color: var(--muted);
    text-wrap: pretty;
}
.explain__lead b { color: var(--text); }
.explain__grid {
    display: grid;
    gap: clamp(0.55rem, 1.1vw, 0.8rem);
    grid-template-columns: minmax(0, 1fr);
    /* Equal rows that divide up whatever height the table beside them has, so
       the two columns finish level instead of one running long. */
    grid-auto-rows: 1fr;
    height: 100%;
}
@media (min-width: 48rem) {
    .explain__grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}
@media (min-width: 90rem) {
    .explain__grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.explain__item {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: flex-start;
    height: 100%;
    gap: 0.3rem;
    padding: clamp(0.7rem, 1.1vw, 0.9rem);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: linear-gradient(160deg, var(--glass), var(--glass-soft));
    transition: border-color 0.35s var(--ease);
}
.explain__title {
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 0.98rem;
    font-weight: 700;
    color: var(--lilac);
}
.explain__item p { color: var(--muted); text-wrap: pretty; }
.matrix caption span {
    display: block;
    margin-top: 0.55rem;
    font-weight: 400;
    color: var(--muted);
}
.matrix { width: 100%; border-collapse: collapse; font-size: 0.98rem; }
.matrix th, .matrix td { padding: 0.8rem 0.9rem; border-bottom: 1px solid var(--line); text-align: center; vertical-align: middle; }
.matrix tbody tr:last-child th, .matrix tbody tr:last-child td { border-bottom: 0; }
.matrix thead th { border-bottom: 1px solid var(--line-2); font-size: 0.72rem; font-weight: 600; letter-spacing: 0.2em; text-transform: uppercase; color: var(--dim); }
.matrix thead th.is-best {
    background: var(--neon);
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}
.matrix tbody th { width: 46%; text-align: left; font-weight: 500; color: var(--text); }
.matrix td { color: var(--muted); white-space: nowrap; }
.matrix col.is-best-col { background: rgba(236, 72, 153, 0.06); }
.yes, .no { display: inline-grid; place-items: center; width: 1.5rem; height: 1.5rem; border-radius: 50%; font-size: 0.85rem; font-weight: 700; }
.yes { color: var(--ok); border: 1px solid rgba(110, 231, 183, 0.4); background: rgba(110, 231, 183, 0.1); }
.no { color: var(--dim); border: 1px solid var(--line); }
.tier__flag {
    position: absolute;
    top: 1.5rem;
    right: -3.6rem;
    z-index: 2;
    width: 12rem;
    padding: 0.3rem 0;
    transform: rotate(45deg);
    background: var(--neon);
    box-shadow: 0 0.4rem 1.2rem rgba(var(--k), 0.45);
    color: #fff;
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 0.68rem;
    font-weight: 700;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    text-align: center;
    text-shadow: 0 1px 2px rgba(var(--k), 0.35);
    pointer-events: none;
}
.tier--best .tier__name, .tier--best .tier__price { position: relative; }
.tier--best { padding-top: clamp(2.2rem, 3.4vw, 2.6rem); }
.tier__kind { font-size: 0.66rem; font-weight: 600; letter-spacing: 0.24em; text-transform: uppercase; color: var(--dim); }
.tier__name .ic { display: inline-block; vertical-align: -0.14em; margin-right: 0.45rem; color: var(--pink); }
.tier__name { margin-top: 0.35rem; font-family: 'Outfit', system-ui, sans-serif; font-size: 1.2rem; font-weight: 700; color: var(--lilac); }

/* The two whole-shop plans wear their own metal, so they read as a different
   kind of thing from the four collections. The dark end of each gradient is a
   deep tint rather than true black - on a near-black card, black letters are
   just missing letters. The icon takes the bright end of the same ramp. */
.tier__name--blue {
    background: linear-gradient(105deg, #123A63 0%, #2E7FC9 38%, #8ED2FF 72%, #123A63 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}
.tier__name--blue .ic { color: #8ED2FF; -webkit-text-fill-color: currentColor; filter: drop-shadow(0 0 0.5rem rgba(142, 210, 255, 0.55)); }

.tier__name--full {
    background: linear-gradient(105deg, #4A3410 0%, #C89A2A 38%, #FFE08A 72%, #4A3410 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
}
.tier__name--full .ic { color: #FFE08A; -webkit-text-fill-color: currentColor; filter: drop-shadow(0 0 0.5rem rgba(255, 224, 138, 0.55)); }
.tier__price { margin-top: 0.9rem; font-family: 'Outfit', system-ui, sans-serif; font-size: clamp(1.8rem, 1.4rem + 1.4vw, 2.4rem); font-weight: 700; font-variant-numeric: tabular-nums; }
.tier__per { font-size: 0.75rem; color: var(--dim); }
.tier__body { margin-top: 0.9rem; font-size: 0.9rem; font-weight: 400; color: var(--muted); }

/* ==============================================================
   Services, terms, FAQ
   ============================================================== */

.services { display: grid; gap: clamp(0.9rem, 1.8vw, 1.35rem); grid-template-columns: repeat(auto-fit, minmax(min(100%, 16rem), 1fr)); }
.service { padding: clamp(1.35rem, 2.6vw, 1.85rem); border-radius: var(--r-lg); transition: transform 0.35s var(--ease-out), border-color 0.35s var(--ease); }
/* Icon and title on one line. The icon was a block above the heading, which
   cost a whole row of height and made four cards read as four separate
   stacks rather than one row of headings. */
.service__title {
    display: flex;
    align-items: center;
    gap: 0.8rem;
    margin: 0 0 0.6rem;
}
.service__icon {
    display: grid;
    place-items: center;
    width: 2.7rem;
    height: 2.7rem;
    flex: none;
    border: 1px solid rgba(167, 139, 250, 0.35);
    border-radius: var(--r-sm);
    background: var(--neon-soft);
    font-size: 1.25rem;
    color: #fff;
}
.service__icon .ic { filter: drop-shadow(0 0 0.5rem rgba(139, 92, 246, 0.6)); }

/* The name is set like a band heading - pixel face, uppercase, chrome - so a
   card title and a section title are recognisably the same kind of thing.
   Smaller tracking than the band's: at this size 0.06em pushes two words
   onto two lines. */
.service__name {
    font-family: var(--px);
    font-size: clamp(0.92rem, 0.86rem + 0.32vw, 1.1rem);
    font-weight: 700;
    letter-spacing: 0.03em;
    line-height: 1.2;
    text-transform: uppercase;
    background: var(--chrome);
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
    filter: drop-shadow(0 2px 0 rgba(var(--k), 0.6));
}
.service p { font-size: 0.98rem; font-weight: 400; color: var(--muted); }
.service__meta { margin-top: 1rem; font-family: 'Outfit', system-ui, sans-serif; font-size: 0.92rem; color: var(--lilac); }

.spec { width: 100%; border-collapse: collapse; font-size: 1.02rem; }
.spec caption { text-align: left; padding: 0 0 1rem; color: var(--muted); font-weight: 400; }
.spec th, .spec td { padding: 0.9rem 1.05rem; text-align: left; border-bottom: 1px solid var(--line); vertical-align: top; }
.spec tr:last-child th, .spec tr:last-child td { border-bottom: 0; }
.spec th { width: 30%; font-size: 0.97rem; font-weight: 600; color: #fff; }
.spec td { color: var(--muted); font-weight: 400; }
.spec-scroll { overflow-x: auto; padding: clamp(0.9rem, 2vw, 1.4rem); }

.faq { display: grid; gap: 0.55rem; }
.faq details { border-radius: var(--r-md); overflow: hidden; }
.faq summary {
    background-image: linear-gradient(90deg,
        #F3F3F8 0%, #F3F3F8 42%,
        #8B5CF6 62%, #EC4899 80%, #A78BFA 100%);
    background-size: 240% 100%;
    background-position: 0% 0;
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 1rem clamp(1.1rem, 2.2vw, 1.45rem);
    font-weight: 600;
    font-size: 1.02rem;
    cursor: pointer;
    list-style: none;
    /* One transition declaration, and it has to be this one. The rule used to
       set `transition: background-position` up top and then `transition:
       color` again down here - the second replaced the first outright, so the
       sweep had no duration and the colour snapped on. Colour is not animated
       anyway: the text fill is transparent and the gradient does the work. */
    transition: background-position 0.4s var(--ease);
}
.faq summary::-webkit-details-marker { display: none; }
.faq summary:hover { background-position: 100% 0; }
.faq__no { font-size: 0.85rem; color: var(--dim); -webkit-text-fill-color: var(--dim); font-variant-numeric: tabular-nums; }
/* A chevron rather than a plus: it points down at something closed and up at
   something open, which says "there is more below" instead of "add". Drawn
   from two borders on an empty box, so it is unaffected by the gradient text
   fill the summary carries. */
.faq summary::after {
    content: '';
    margin-left: auto;
    flex: none;
    width: 0.62rem;
    height: 0.62rem;
    border-right: 2px solid var(--lilac);
    border-bottom: 2px solid var(--lilac);
    /* The translate keeps the shape optically centred as it spins - a
       rotated square pivots around its box, not around the tick it draws. */
    transform: rotate(45deg) translate(-0.15rem, -0.15rem);
    transition: transform 0.34s var(--ease-out);
}
.faq details[open] summary::after { transform: rotate(-135deg) translate(-0.15rem, -0.15rem); }
.faq__body { padding: 0 clamp(1.1rem, 2.2vw, 1.45rem) 1.4rem; font-size: 1.04rem; line-height: 1.78; font-weight: 400; color: var(--muted); }
.faq__body a, .doc p a, .note p a, .way p a, .spec td a, .chat__alt a, .legal p a, .explain__item p a, .matrix caption a, .matrix td a {
    background-image: var(--neon), var(--neon);
    -webkit-background-clip: text, padding-box;
            background-clip: text, padding-box;
    background-repeat: no-repeat;
    background-position: 0 0, 0 100%;
    background-size: 100% 100%, 0% 2px;
    color: transparent;
    -webkit-text-fill-color: transparent;
    -webkit-box-decoration-break: clone;
            box-decoration-break: clone;
    text-decoration: none;
    transition: background-size 0.35s var(--ease-out);
}
.faq__body a:hover, .doc p a:hover, .note p a:hover, .way p a:hover, .spec td a:hover, .chat__alt a:hover, .legal p a:hover, .explain__item p a:hover, .matrix caption a:hover, .matrix td a:hover {
    background-size: 100% 100%, 100% 2px;
}


/* ==============================================================
   Portfolio
   ============================================================== */

.folio { display: grid; gap: clamp(0.9rem, 1.8vw, 1.35rem); grid-template-columns: repeat(auto-fill, minmax(min(100%, 17rem), 1fr)); }

.folio__item {
    position: relative;
    display: block;
    width: 100%;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: var(--r-lg);
    background: none;
    overflow: hidden;
    text-align: left;
    cursor: pointer;
    transition: transform 0.35s var(--ease-out), border-color 0.35s var(--ease),
                box-shadow 0.35s var(--ease-out);
}

.folio__shot {
    position: relative;
    aspect-ratio: 16 / 10;
    display: grid;
    place-items: center;
    overflow: hidden;
    background:
        radial-gradient(85% 100% at 50% 0%, rgba(var(--w), 0.09), transparent 70%),
        var(--glass-veil);
    border-bottom: 1px solid var(--line);
}
.folio__shot img { width: 100%; height: 100%; object-fit: cover; }
.folio__crest { width: 30%; aspect-ratio: 1; opacity: 0.5; transition: opacity 0.45s var(--ease), transform 0.45s var(--ease-out); }
.folio__item:hover .folio__crest { opacity: 0.9; transform: scale(1.06); }

/* The kind of work, on the picture rather than under it - it is the first
   thing being scanned for, and it should not cost a line of layout. */
.folio__tag {
    position: absolute;
    top: 0.7rem;
    left: 0.7rem;
    padding: 0.2rem 0.6rem;
    border: 1px solid var(--line-2);
    border-radius: 999px;
    background: var(--sheet-tip);
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    color: var(--text);
}

/* All three are spans inside a <button>, so they are inline by default and
   ran together into one long line that wrapped out of the card. They have to
   be told to be blocks. */
.folio__body { display: block; padding: 0.85rem clamp(0.9rem, 2vw, 1.15rem) 1rem; }
.folio__name { display: block; font-size: 1rem; font-weight: 600; line-height: 1.3; }
.folio__sub { display: block; margin-top: 0.2rem; font-size: 0.86rem; line-height: 1.4; color: var(--dim); }
.folio__shot { display: grid; }

/* Filters. Same pills the shop uses, so they behave the way they look. */
.folio__filters { display: flex; flex-wrap: wrap; gap: 0.45rem; margin-bottom: clamp(1rem, 2.5vh, 1.5rem); }

/* ---- Lightbox ---- */

.lightbox {
    position: fixed;
    inset: 0;
    z-index: 400;
    display: grid;
    place-items: center;
    padding: clamp(1rem, 4vw, 3rem);
    background: rgba(var(--k), 0.86);
    backdrop-filter: blur(14px);
    -webkit-backdrop-filter: blur(14px);
    opacity: 0;
    visibility: hidden;
    /* Belt and braces with visibility: a closed overlay must never sit over
       the page swallowing clicks. */
    pointer-events: none;
    transition: opacity 0.3s var(--ease), visibility 0.3s;
}
.lightbox.is-open { opacity: 1; visibility: visible; pointer-events: auto; }

.lightbox__panel {
    position: relative;
    width: min(58rem, 100%);
    max-height: 100%;
    overflow-y: auto;
    border: 1px solid var(--line-2);
    border-radius: var(--r-lg);
    background: var(--sheet-pop);
    box-shadow: 0 2rem 5rem rgba(var(--k), 0.65);
    transform: scale(0.97);
    transition: transform 0.35s var(--ease-out);
}
.lightbox.is-open .lightbox__panel { transform: none; }

.lightbox__shot {
    position: relative;
    aspect-ratio: 16 / 9;
    display: grid;
    place-items: center;
    overflow: hidden;
    border-bottom: 1px solid var(--line);
    background:
        radial-gradient(85% 100% at 50% 0%, rgba(var(--w), 0.09), transparent 70%),
        var(--glass-veil);
}
.lightbox__shot img { width: 100%; height: 100%; object-fit: contain; }
.lightbox__crest { width: 22%; aspect-ratio: 1; opacity: 0.45; }

/* The writing sits under the picture, which is the whole point of opening it
   rather than just enlarging a thumbnail. */
.lightbox__body { padding: clamp(1.1rem, 2.5vw, 1.6rem); }
.lightbox__tag { display: inline-block; margin-bottom: 0.5rem; }
.lightbox__name { font-size: clamp(1.15rem, 1rem + 0.6vw, 1.5rem); font-weight: 700; }
.lightbox__text { margin-top: 0.6rem; font-size: 1rem; line-height: 1.75; color: var(--muted); }

.lightbox__close,
.lightbox__step {
    position: absolute;
    z-index: 2;
    display: grid;
    place-items: center;
    width: 2.6rem;
    height: 2.6rem;
    border: 1px solid var(--line-2);
    border-radius: 50%;
    background: var(--sheet-tip);
    color: var(--muted);
    font-size: 1.05rem;
    cursor: pointer;
    transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.lightbox__close:hover,
.lightbox__step:hover { color: #fff; border-color: var(--pink); }
.lightbox__close { top: 0.7rem; right: 0.7rem; }
.lightbox__step--prev { top: calc(28% - 1.3rem); left: 0.7rem; }
.lightbox__step--next { top: calc(28% - 1.3rem); right: 0.7rem; }

.lightbox__count {
    position: absolute;
    top: 0.7rem;
    left: 0.7rem;
    z-index: 2;
    padding: 0.25rem 0.65rem;
    border: 1px solid var(--line-2);
    border-radius: 999px;
    background: var(--sheet-tip);
    font-size: 0.75rem;
    font-variant-numeric: tabular-nums;
    color: var(--muted);
}

/* ==============================================================
   Crosslink & footer
   ============================================================== */

.crosslink {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 1.4rem;
    padding: clamp(1.4rem, 3.5vw, 2.1rem);
    border-radius: var(--r-lg);
    overflow: hidden;
}
.crosslink__lead { display: flex; align-items: center; gap: 1.05rem; flex: 1 1 24rem; }
.crosslink img { width: 3.4rem; flex: none; filter: drop-shadow(0 0 0.9rem rgba(167, 139, 250, 0.4)); }
.crosslink__text p { margin-top: 0.35rem; font-size: 0.92rem; font-weight: 400; color: var(--muted); }

.foot {
    position: relative;
    margin-top: var(--section-y);
    border-top: 1px solid var(--line);
    overflow: hidden;
    background: linear-gradient(180deg, rgba(var(--w), 0.035), rgba(var(--w), 0.008));
    backdrop-filter: var(--blur-lite);
    -webkit-backdrop-filter: var(--blur-lite);
}
/* The same weather as the masthead, so the page closes on the note it
   opened with. Slower down here - nothing should compete with the links. */
.foot::before {
    content: '';
    position: absolute;
    inset: -70% -15% auto -15%;
    height: 220%;
    z-index: 0;
    pointer-events: none;
    background:
        radial-gradient(26% 55% at 18% 50%, rgba(139, 92, 246, 0.26), transparent 70%),
        radial-gradient(22% 50% at 55% 45%, rgba(236, 72, 153, 0.20), transparent 70%),
        radial-gradient(18% 42% at 85% 55%, rgba(125, 211, 252, 0.12), transparent 70%);
    filter: blur(34px);
    animation: navDrift 48s var(--ease) infinite alternate;
    will-change: transform;
}
.foot > * { position: relative; z-index: 1; }
@media (prefers-reduced-motion: reduce) { .foot::before { animation: none; } }

.foot__brand { display: flex; align-items: center; gap: 0.85rem; margin-bottom: 0.9rem; }
.foot__mark { width: 3rem; aspect-ratio: 1; flex: none; opacity: 0.95; }
.foot__col a .ic { color: var(--dim); transition: color var(--speed) var(--ease), filter var(--speed) var(--ease); }
.foot__col a:hover .ic { color: var(--pink); filter: drop-shadow(0 0 0.4rem rgba(236, 72, 153, 0.6)); }

/* Contact row: the two things people actually want from a footer. */
.foot__reach { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.5rem; margin-top: 1rem; max-width: 24rem; }
.foot__reach .btn { width: 100%; padding: 0.55rem 0.9rem; font-size: 0.88rem; }
@media (max-width: 26rem) { .foot__reach { grid-template-columns: minmax(0, 1fr); } }
.foot__inner {
    display: grid;
    gap: clamp(1.5rem, 4vw, 3rem);
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 13rem), 1fr));
    padding-block: clamp(2rem, 4.5vh, 3rem);
}
.foot__name { font-family: var(--px); font-weight: 700; font-size: 1.2rem; }
.foot__tag {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    font-size: 0.68rem;
    letter-spacing: 0.18em;
    text-transform: uppercase;
    color: var(--dim);
}

/* The loadscreen's sign-off, brought over: a beating heart and a name with
   the brand gradient drifting through it under a soft white sweep. */
.heartbeat {
    width: 1.3em;
    height: 1.3em;
    fill: currentColor;
    stroke: none;
    color: var(--pink);
    animation: heartbeat 2.2s ease-in-out infinite;
}
@keyframes heartbeat {
    0%, 100% { transform: scale(1); }
    12%      { transform: scale(1.25); }
    24%      { transform: scale(1); }
    36%      { transform: scale(1.18); }
    48%      { transform: scale(1); }
}

.credit {
    font-weight: 700;
    background:
        linear-gradient(110deg, rgba(var(--w), 0) 40%, rgba(var(--w), 0.35) 50%, rgba(var(--w), 0) 60%),
        linear-gradient(to right, #8B5CF6, #EC4899, #A78BFA, #8B5CF6);
    background-size: 220% auto, 200% auto;
    -webkit-background-clip: text;
            background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;
    animation: creditSweep 9s linear infinite;
}
@keyframes creditSweep { to { background-position: -220% 0, -200% 0; } }

@media (prefers-reduced-motion: reduce) {
    .heartbeat, .credit { animation: none; }
}
.foot__blurb { margin-top: 0.85rem; font-size: 0.88rem; font-weight: 400; color: var(--muted); }
.foot__col h4 { font-size: 0.7rem; font-weight: 600; letter-spacing: 0.22em; text-transform: uppercase; color: var(--dim); margin-bottom: 0.75rem; }
/* Icon beside the label, not stacked above it - this rule comes after the
   footer block higher up, so it is the one that has to carry the layout. */
.foot__col a {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.26rem 0;
    font-size: 0.96rem;
    color: var(--muted);
    transition: color var(--speed) var(--ease);
}
/* The gradient arrives by sliding, not by swapping. A background-clip:text
   fill cannot be transitioned - swapping to one is instant, which is why
   these links snapped while the website's faded. Painting the link with a
   gradient that is flat for its first 42% and animating the POSITION gives
   the same colours and a real transition. currentColor keeps the flat part
   correct in both themes without a second rule. */
.foot__col a {
    background-image: linear-gradient(90deg,
        currentColor 0%, currentColor 42%,
        #8B5CF6 62%, #EC4899 80%, #A78BFA 100%);
    background-size: 240% 100%;
    background-position: 0 0;
    -webkit-background-clip: text;
            background-clip: text;
    -webkit-text-fill-color: transparent;
    transition: background-position 0.4s var(--ease);
}
.foot__col a:hover { background-position: 100% 0; }

/* The icon is a real SVG, so it just changes colour. */
.foot__col a .ic { -webkit-text-fill-color: initial; }
/* The legal row. Required to be reachable from every page, so it sits above
   the copyright line rather than inside it. */
.foot__law {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.4rem 1.4rem;
    padding: 1.1rem var(--gutter) 0;
    font-size: 0.86rem;
}
.foot__law a { color: var(--muted); transition: color var(--speed) var(--ease); }
.foot__law a:hover { color: var(--text); }

/* A legal page is a stack of short sections - readable width, headings that
   can be scanned, and the blanks impossible to miss. */
.legal {
    padding: clamp(1.1rem, 2.2vw, 1.6rem) clamp(1.1rem, 2.4vw, 1.8rem);
    margin-bottom: clamp(0.7rem, 1.6vw, 1rem);
    border: 1px solid var(--line);
    border-radius: var(--r-md);
    background: linear-gradient(160deg, var(--glass), var(--glass-soft));
}
.legal h3 {
    font-family: 'Outfit', system-ui, sans-serif;
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--lilac);
    margin-bottom: 0.55rem;
}
.legal p { color: var(--muted); margin-bottom: 0.6rem; text-wrap: pretty; }
.legal p:last-child { margin-bottom: 0; }
/* A note to the owner rather than to the reader: visibly a task, so it cannot
   be mistaken for part of the policy or quietly left in. */
.legal__todo {
    padding: 0.5rem 0.75rem;
    border-left: 2px solid rgba(236, 72, 153, 0.5);
    background: rgba(236, 72, 153, 0.06);
    font-size: 0.94em;
    color: var(--text-2, var(--muted));
}
.legal code {
    padding: 0.1rem 0.35rem;
    border-radius: var(--r-sm);
    background: var(--panel-2);
    font-family: ui-monospace, Consolas, monospace;
    font-size: 0.92em;
}
.fillme {
    display: inline-block;
    padding: 0.05rem 0.45rem;
    border: 1px dashed rgba(236, 72, 153, 0.6);
    border-radius: var(--r-sm);
    background: rgba(236, 72, 153, 0.1);
    color: var(--pink);
    font-size: 0.86em;
    font-weight: 600;
    letter-spacing: 0.06em;
}

.foot__legal { padding: 1.1rem var(--gutter) 2rem; border-top: 1px solid var(--line); font-size: 0.76rem; text-align: center; color: var(--dim); }

/* ==============================================================
   Pages & reveal
   ============================================================== */

.page { display: none; }
.page.is-current { display: block; animation: pageIn 0.4s var(--ease-out) both; }
@keyframes pageIn { from { opacity: 0; transform: translateY(0.6rem); } }

/* The arrival slides with `translate`, not with `transform`.

   They look identical, and that is the point: a card's hover state also wants
   `transform`, and `transition` is a single property, so whichever rule wins
   decides what animates for BOTH. The reveal used to win, which left every
   card with opacity and transform transitions only - so a hover changed the
   border and the shadow instantly while the lift alone was smooth. It had been
   patched card by card (.item, .tier); every other card on the site still
   snapped.

   Splitting them ends the argument. The reveal owns `translate` and `opacity`,
   hover owns `transform`, and one transition list can carry both at their own
   durations - slow to arrive, quick to answer the pointer. Nothing needs
   restoring after `.is-in`, and a card added later is smooth without being
   named here. */
.reveal {
    opacity: 0;
    translate: 0 1.3rem;
    transition: opacity 0.7s var(--ease-out), translate 0.7s var(--ease-out),
                transform 0.35s var(--ease-out), border-color 0.35s var(--ease),
                box-shadow 0.35s var(--ease-out), color 0.3s var(--ease),
                background-color 0.3s var(--ease);
}
.reveal.is-in { opacity: 1; translate: none; }

.reveal-child {
    opacity: 0;
    translate: 0 1.1rem;
    transition: opacity 0.6s var(--ease-out), translate 0.6s var(--ease-out),
                transform 0.35s var(--ease-out), border-color 0.35s var(--ease),
                box-shadow 0.35s var(--ease-out), color 0.3s var(--ease),
                background-color 0.3s var(--ease);
}
.reveal-child.is-in { opacity: 1; translate: none; }

/* A grid whose children arrive one after another. The delay is set from
   script so it follows the real number of cards. */
.reveal--stagger > * {
    opacity: 0;
    translate: 0 1.1rem;
    transition: opacity 0.6s var(--ease-out), translate 0.6s var(--ease-out),
                transform 0.35s var(--ease-out), border-color 0.35s var(--ease),
                box-shadow 0.35s var(--ease-out), color 0.3s var(--ease),
                background-color 0.3s var(--ease);
}
.reveal--stagger.is-in > * { opacity: 1; translate: none; }
@media (prefers-reduced-motion: reduce) {
    .reveal--stagger > * { opacity: 1; translate: none; transition: none; }
}

.item.reveal-child.is-in {
    transition: transform 0.4s var(--ease-out),
                border-color 0.4s var(--ease),
                box-shadow 0.4s var(--ease);
}

/* The plan cards sit inside staggered grids, whose blanket rule above
   overrides the tier's own easing and pins its transform. Restate both here,
   after it, so a subscription card hovers exactly like an item card. */
@media (prefers-reduced-motion: reduce) {
    .tier:hover { transform: none; }
}

/* ==============================================================
   Responsive
   ============================================================== */

@media (max-width: 60rem) {
    .nav__toggle { display: flex; }
    .mast { justify-content: flex-start; }

    .themeswitch--bar { display: none; }
    .themeswitch--mast {
        display: inline-grid;
        position: absolute;
        top: 50%;
        right: calc(var(--gutter) + 3.3rem);
        transform: translateY(-50%);
        margin: 0;
    }
    .nav__bar { padding: 0; }
    .nav__links {
        /* Absolute against .nav, which is the fixed one. It was fixed with
           top:100%, which only lands correctly because the backdrop-filter on
           .nav happens to make it a containing block for fixed children -
           true today, and not something worth depending on. */
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        max-height: calc(100vh - 5rem);
        overflow-y: auto;
        flex-direction: column;
        align-items: stretch;
        gap: 0.15rem;
        padding: 1rem var(--gutter) 1.75rem;
        /* Nearly opaque. At 0.8 the aurora and the hero copy read straight
           through the links and drowned the icons. */
        background: linear-gradient(180deg, rgba(14, 14, 20, 0.965), rgba(9, 9, 13, 0.945));
        border-bottom: 1px solid var(--line);
        backdrop-filter: blur(28px) saturate(160%);
        -webkit-backdrop-filter: blur(28px) saturate(160%);
        opacity: 0;
        visibility: hidden;
        /* Belt and braces with visibility: a closed menu must never sit over
           the page swallowing taps. */
        pointer-events: none;
        transform: translateY(-0.6rem);
        transition: opacity 0.3s var(--ease), transform 0.3s var(--ease-out), visibility 0.3s;
    }
    .nav__links.is-open { opacity: 1; visibility: visible; pointer-events: auto; transform: none; }
    .nav__link { padding: 0.85rem 0.25rem; font-size: 1.2rem; border-bottom: 1px solid var(--line); }
    .nav__link::after { display: none; }

    /* Shop lives inside a .drop wrapper, and .nav__link is inline-flex, so it
       shrank to the width of the word while every sibling filled the menu -
       same text size, a third of the tap target and a stub of an underline.
       It fills its wrapper here like every other row. */
    .drop { display: block; }
    .drop > .nav__link { width: 100%; }

    /* And no caret: the dropdown deliberately does not open on touch any
       more, so an arrow promising one would be a lie. Here Shop is a link. */
    .nav__caret { display: none; }
    .drop__menu {
        position: static;
        width: auto;
        transform: none;
        opacity: 1;
        visibility: visible;
        display: none;
        margin: 0.4rem 0 0.6rem;
        background: rgba(var(--w), 0.03);
        box-shadow: none;
    }
    .drop__menu.is-open { display: block; }
}

@media (max-width: 30rem) {
    .mast__sub { display: none; }
    .btn-row .btn { flex: 1 1 100%; }
    .spec th, .spec td { display: block; width: auto; }
    .spec th { border-bottom: 0; padding-bottom: 0.25rem; }
}

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    *, *::before, *::after { animation-duration: 0.01ms !important; animation-iteration-count: 1 !important; transition-duration: 0.01ms !important; }
    .reveal, .reveal-child { opacity: 1; transform: none; }
    .page.is-current { animation: none; }
    .ticker__track { animation: none; }
}

/* ==============================================================
   Raised tiles
   Last in the file on purpose - see the note below.
   ============================================================== */

/* One lift for every raised tile (owner, 2026-08-26). The shop items already
   had the good version and everything else had its own near-miss - four pixels
   instead of five, its own duration, a different border tint - so the page
   felt slightly different depending on what your mouse was over. Shared now,
   which also means a tile added later matches without being told to. The team
   cards are deliberately not in the list (owner, 2026-08-26) - they are
   portraits, not things you click.

   The resting shadow is written out as an explicit zero rather than left as
   `none`, because a shadow cannot interpolate from nothing and would otherwise
   snap in. The distance is small: five pixels reads as a lift, more reads as a
   jump.

   On performance, because this is where hover jank comes from. `will-change`
   and `translateZ(0)` are NOT used here. Both promote an element to its own
   compositing layer, and on a grid of tiles that means dozens of permanent
   layers competing for GPU memory - the documented way to make things slower,
   not faster. The transform promotes itself for the length of the transition,
   which is when it is actually needed. The blurred shadow is the other
   expense, and it is affordable precisely because only one tile is ever
   hovered: one element repaints, not the grid.

   Two reasons this block sits at the very end. Each tile's own rule declares a
   `transition` further up, and `transition` is a single property - whichever
   rule comes last decides what animates, so this has to be last or the old
   durations quietly win. For the same reason the list carries the reveal's
   `opacity` and `translate`: the scroll-reveal rules would otherwise be
   overridden here and these tiles would pop in instead of arriving. */
.tier, .value, .service, .elsewhere .way, .strip__card,
.item, .folio__item, .explain__item, .doccard {
    border-color: var(--line);
    box-shadow: 0 0 0 rgba(var(--k), 0), 0 0 0 rgba(236, 72, 153, 0);
    transition: opacity 0.6s var(--ease-out), translate 0.6s var(--ease-out),
                transform 0.4s var(--ease-out),
                border-color 0.4s var(--ease),
                box-shadow 0.4s var(--ease);
}
/* Everything answers with the same colour and glow. */
.tier:hover, .value:hover, .service:hover, .elsewhere .way:hover, .strip__card:hover,
.item:hover, .explain__item:hover, .doccard:hover {
    border-color: rgba(236, 72, 153, 0.45);
    box-shadow: 0 0.85rem 1.7rem rgba(var(--k), 0.42), 0 0 1.1rem rgba(236, 72, 153, 0.14);
}
/* Portfolio tiles answer with the border alone (owner, 2026-08-26). Both parts
   of that shadow are blurred - a 2.5rem drop and a 1.8rem pink halo - and on a
   grid of pictures the halo is the thing that reads as the tile going soft at
   the edges. The border still says it is live. */
.folio__item:hover {
    border-color: rgba(236, 72, 153, 0.45);
}
/* Only these lift. Shop items and portfolio tiles stay put (owner,
   2026-08-26) - they sit in dense grids of images, which is where a moving
   tile is both the most distracting and the most expensive thing on screen.
   Nor do the Side by side boxes on the subscription page: they are there to be
   read against each other, and a column that jumps as you compare it to the
   one beside it is exactly the wrong thing, nor the documentation cards. The
   tiers above them still lift. */
.tier:hover, .value:hover, .service:hover, .elsewhere .way:hover, .strip__card:hover {
    transform: translate3d(0, -5px, 0);
}
@media (prefers-reduced-motion: reduce) {
    .tier:hover, .value:hover, .service:hover, .elsewhere .way:hover, .strip__card:hover { transform: none; }
}

/* After the shared block above, which tints every tile's border on hover -
   this one wants its gradient rim to be the edge instead, so the border under
   it gets out of the way. */
.strip__card:hover { border-color: transparent; }
