/* ==========================================================================
   Atroot — Landing page styles
   ========================================================================== */


/* --------------------------------------------------------------------------
   Self-hosted Manrope variable font (privacy-first, no CDN dependency)
   -------------------------------------------------------------------------- */

@font-face {
    font-family: 'Manrope';
    font-weight: 200 800;
    font-style: normal;
    font-display: swap;
    /* Order of preference:
         1. system-installed copy (zero bytes over the wire),
         2. our bundled woff2 (≈54 KB, Brotli-compressed),
         3. legacy ttf fallback (≈167 KB) for the unlikely browser
            that doesn't speak woff2. */
    src: local('Manrope'),
         url('fonts/Manrope.woff2') format('woff2'),
         url('fonts/Manrope.ttf')   format('truetype');
}


/* --------------------------------------------------------------------------
   Design tokens
   -------------------------------------------------------------------------- */

:root {
    /* Surfaces & text */
    --color-bg: #ffffff;
    --color-text: #0a0a0a;
    --color-text-secondary: #525252;
    --color-text-muted: #737373;
    --color-text-faint: rgba(10, 10, 10, 0.4);

    /* Brand accent */
    --color-accent: #0284c7;
    --color-accent-bg: rgba(2, 132, 199, 0.04);
    --color-accent-bg-strong: rgba(2, 132, 199, 0.08);

    /* Borders & glass */
    --color-border: rgba(10, 10, 10, 0.08);
    --color-border-strong: rgba(10, 10, 10, 0.18);
    --color-glass: rgba(255, 255, 255, 0.55);
    /* lighter — lets background bleed through */
    --color-glass-strong: rgba(255, 255, 255, 0.88);

    /* Status — amber-700 (semantic "in development" / WIP color,
       distinct from brand accent; AA-passes on white at 14px/500) */
    --color-status: #b45309;
    --color-status-border: rgba(180, 83, 9, 0.20);
    --color-status-border-strong: rgba(180, 83, 9, 0.50);
    --color-status-bg-strong: rgba(180, 83, 9, 0.10);

    /* Radii */
    --radius-sm: 0.5rem;
    --radius-md: 0.75rem;
    --radius-lg: 1rem;

    /* Easing */
    --ease-out: cubic-bezier(0.16, 1, 0.3, 1);
    --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);

    /* Common timings */
    --pulse-dur: 2.4s;     /* status badge ring pulse + status dot ping */
    --switch-dur: 0.18s;   /* dropdown open/close (lang & download) */

    /* Layout rhythm */
    --hpad-page: 1.5rem;
    --hpad-page-sm: 1rem;
    --control-h: 40px;     /* shared height for pill controls (lang
                              trigger, nav item, brand, feature, status,
                              social link). Mobile media query overrides. */
    --focus-radius: 4px;   /* outline rounding under :focus-visible */

    /* Surface elevation — single shared shadow for any "card / panel"
       (preview card, language menu, download menu). Three layers:
       inset hairline (replaces a hard border), inset top highlight
       (refracted edge), three outer drops at increasing distance for
       depth without grime. Tuned to read clean on white. */
    --shadow-card:
        inset 0 0 0 1px rgba(10, 10, 10, 0.06),
        inset 0 1px 0 rgba(255, 255, 255, 0.7),
        0 1px 2px rgba(10, 10, 10, 0.04),
        0 8px 24px -8px rgba(10, 10, 10, 0.08),
        0 28px 56px -24px rgba(10, 10, 10, 0.16);

    /* Preview mockup image — locale-specific. Default English; an
       html[lang="ru"] override below swaps it.
       Served as WebP (≈75 KB) on every browser that supports it; the
       PNG originals (≈1 MB each) stay in the repo as a fallback for
       any future need but are not referenced by CSS. */
    --preview-img: url('assets/preview-en.webp');
}

html[lang="ru"] {
    --preview-img: url('assets/preview-ru.webp');
}


/* --------------------------------------------------------------------------
   Reset & base
   -------------------------------------------------------------------------- */

*,
*::before,
*::after {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
    /* Suppress the translucent grey overlay iOS Safari draws on every
       tappable element — we already provide our own :hover / :active
       feedback. */
    -webkit-tap-highlight-color: transparent;
}

html {
    scroll-behavior: smooth;
}

html,
body {
    height: 100%;
}

html,
body {
    /* `clip` is hard-stopped: unlike `hidden` it forbids ANY scrolling on
       this axis, including swipe gestures on iOS Safari. Tooltips,
       ambient auras and the spotlight halo all paint slightly past their
       anchors and on a touch device that overflow used to manifest as a
       fluttering horizontal scroll bar; `clip` removes it entirely. */
    overflow-x: clip;
}

::selection {
    background-color: rgba(2, 132, 199, 0.18);
    color: var(--color-text);
}

body {
    font-family: 'Manrope', -apple-system, BlinkMacSystemFont, system-ui, sans-serif;
    background: var(--color-bg);
    color: var(--color-text);
    line-height: 1.5;

    /* Type quality */
    text-rendering: optimizeLegibility;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    font-feature-settings: 'kern' 1, 'liga' 1, 'calt' 1, 'ss01' 1;

    display: flex;
    flex-direction: column;
    align-items: center;
    /* horizontally centre main + footer */
    justify-content: safe center;
    /* vertically centre main+footer as a group; scrolls when overflowing */
    min-height: 100vh;
    padding: 5.5rem var(--hpad-page) 0;
    /* footer carries its own bottom padding */

    position: relative;
}

/* Ambient depth — soft accent auras behind content for "designed canvas" feel */
body::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: 0;
    pointer-events: none;
    background:
        radial-gradient(ellipse 1400px 700px at 50% -150px, rgba(2, 132, 199, 0.07), transparent 60%),
        radial-gradient(ellipse 700px 500px at 92% 96%, rgba(2, 132, 199, 0.045), transparent 65%),
        radial-gradient(ellipse 600px 400px at 8% 90%, rgba(2, 132, 199, 0.025), transparent 65%);
}


/* --------------------------------------------------------------------------
   Site header — fixed bar, full-bleed visually; inner row is centred in
   a 1200px container (brand sits at the container's left edge, not at
   the literal page corner on wide displays).

   Two visual states, animated by JS via `.is-scrolled`:
       idle       — fully transparent, no blur, no shadow.
       scrolled   — iOS-style liquid glass + subtle vertical shrink.
   Transition runs on background-color, background-image, box-shadow,
   backdrop-filter and the inner padding/min-height. Idle state declares
   "zero" values for every transitioning property so the toggle animates
   in both directions instead of jump-cutting on first scroll.
   -------------------------------------------------------------------------- */

.site-header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    /* Highest stacking layer on the page. Hero rows use z:1-4 and the
       universal tooltip ::after uses z:1000 inside its anchor's local
       stacking context — the header still paints above main content
       because its z-index is set on the document's root stacking. */
    z-index: 100;
    pointer-events: none;

    /* Glass surface fades in once the user scrolls past the hero top. */
    background-color: transparent;
    background-image: linear-gradient(to bottom, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0) 100%);
    box-shadow: 0 0 0 transparent;
    backdrop-filter: blur(0) saturate(100%);
    -webkit-backdrop-filter: blur(0) saturate(100%);

    transition:
        background-color 0.32s var(--ease-smooth),
        background-image 0.32s var(--ease-smooth),
        box-shadow 0.32s var(--ease-smooth),
        backdrop-filter 0.32s var(--ease-smooth),
        -webkit-backdrop-filter 0.32s var(--ease-smooth);
}

/* iOS-style "liquid" glass:
   – blur(24px) saturate(180%) so background colours stay vivid through
     the bar instead of washing out;
   – low base opacity (0.55) lets the page actually show through;
   – top-to-transparent gradient overlay fakes specular refraction;
   – bright inset top highlight reads as a glass edge;
   – inset bottom hairline + soft drop shadow ground the bar without
     a hard border line. */
.site-header.is-scrolled {
    background-color: rgba(252, 252, 254, 0.55);
    background-image: linear-gradient(to bottom,
            rgba(255, 255, 255, 0.30) 0%,
            rgba(255, 255, 255, 0) 100%);
    backdrop-filter: blur(24px) saturate(180%);
    -webkit-backdrop-filter: blur(24px) saturate(180%);
    box-shadow:
        inset 0 1px 0 rgba(255, 255, 255, 0.75),
        inset 0 -1px 0 rgba(10, 10, 10, 0.06),
        0 1px 0 rgba(255, 255, 255, 0.4),
        0 14px 36px -16px rgba(10, 10, 10, 0.12);
}

.site-header__inner {
    width: 100%;
    max-width: 1200px;
    margin: 0 auto;
    padding: 1.25rem var(--hpad-page);
    min-height: 64px;

    display: flex;
    justify-content: space-between;
    align-items: center;

    /* Padding tightens on scroll for a subtle shrink effect. Inner controls
       (lang trigger, nav items) stay at their natural 40px height. */
    transition: padding 0.25s var(--ease-smooth), min-height 0.25s var(--ease-smooth);
}

.site-header.is-scrolled .site-header__inner {
    padding-top: 0.625rem;
    padding-bottom: 0.625rem;
    min-height: 52px;
}

.site-header__inner>* {
    pointer-events: auto;
}


/* Brand mark (top-left) — same height as lang trigger for perfect alignment */
.brand {
    display: inline-flex;
    align-items: center;
    /* logo and name read as a single mark */
    gap: 0.0625rem;
    height: var(--control-h);

    text-decoration: none;
    color: var(--color-text);
}

.brand:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 4px;
    border-radius: var(--focus-radius);
}

.brand__logo {
    width: 32px;
    height: 32px;
    display: block;
    user-select: none;
    -webkit-user-drag: none;
}

.brand__name {
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: -0.018em;
    color: var(--color-text);
    line-height: 1;
    transition: color 0.25s ease;
}

.brand:hover .brand__name {
    color: var(--color-accent);
}


/* --------------------------------------------------------------------------
   Site nav — secondary header navigation, currently inactive
   (placeholder items reveal a "coming soon" tooltip on hover)
   -------------------------------------------------------------------------- */

.site-nav {
    display: flex;
    align-items: center;
    gap: 0.125rem;
    height: var(--control-h);
}

.site-nav__item {
    display: inline-flex;
    align-items: center;
    height: 100%;
    padding: 0 0.875rem;

    background: transparent;
    border: none;
    border-radius: var(--radius-md);

    font-family: inherit;
    font-size: 0.9375rem;
    font-weight: 500;
    letter-spacing: -0.005em;
    color: var(--color-text-muted);

    cursor: not-allowed;
    pointer-events: auto;
    /* keep :hover firing while disabled */
    transition: color 0.2s ease, background-color 0.2s ease;
}

.site-nav__item:hover {
    color: var(--color-text-secondary);
    background-color: rgba(10, 10, 10, 0.04);
}

.site-nav__item:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

/* Forward-looking value badge inside a nav item (e.g. "No sign-up" beside
   the upcoming Sync feature). Soft accent fill so it reads as a marketing
   tag rather than a control; same --radius-md as every other pill control
   on the page so it visually belongs to the same family. aria-hidden in
   HTML keeps the parent button's accessible name clean for assistive tech. */
.site-nav__badge {
    display: inline-flex;
    align-items: center;
    height: 18px;
    padding: 0 0.4375rem;
    margin-left: 0.4375rem;

    border-radius: var(--radius-md);
    background-color: var(--color-accent-bg-strong);
    color: var(--color-accent);

    font-size: 0.6875rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    line-height: 1;
    white-space: nowrap;
}


/* --------------------------------------------------------------------------
   Language switcher
   -------------------------------------------------------------------------- */

.lang-switcher {
    position: relative;
}

.lang-trigger {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0 0.75rem 0 0.6875rem;
    height: var(--control-h);

    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background-color: var(--color-glass);
    backdrop-filter: blur(14px) saturate(180%);
    -webkit-backdrop-filter: blur(14px) saturate(180%);

    font-family: inherit;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--color-text);

    cursor: pointer;
    transition:
        border-color 0.2s ease,
        background-color 0.2s ease,
        box-shadow 0.2s ease;
}

.lang-trigger:hover {
    border-color: var(--color-border-strong);
    background-color: var(--color-glass-strong);
}

.lang-trigger:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 3px;
}

.lang-trigger[aria-expanded="true"] {
    border-color: rgba(2, 132, 199, 0.30);
    background-color: var(--color-glass-strong);
    box-shadow: 0 0 0 3px var(--color-accent-bg-strong);
}

.lang-trigger__icon {
    width: 15px;
    height: 15px;
    color: var(--color-text-secondary);
    flex-shrink: 0;
    transition: color 0.2s ease;
}

.lang-trigger:hover .lang-trigger__icon {
    color: var(--color-text);
}

.lang-trigger[aria-expanded="true"] .lang-trigger__icon {
    color: var(--color-accent);
}

.lang-trigger__chevron {
    width: 10px;
    height: 7px;
    color: var(--color-text-muted);
    transition: transform 0.22s ease, color 0.2s ease;
}

.lang-trigger[aria-expanded="true"] .lang-trigger__chevron {
    transform: rotate(180deg);
    color: var(--color-accent);
}


.lang-menu {
    position: absolute;
    top: calc(100% + 0.5rem);
    right: 0;
    min-width: 100%;

    display: flex;
    flex-direction: column;
    gap: 0.0625rem;
    padding: 0.25rem;

    /* Border replaced by inset hairline inside --shadow-card so the menu
       has the same edge as the preview card and the download menu.
       Solid white background — language picker is small (2 rows) and
       must read crisply over hero content; no glass tint here. */
    border-radius: var(--radius-md);
    background-color: #ffffff;
    box-shadow: var(--shadow-card);

    opacity: 0;
    transform: translateY(-6px) scale(0.96);
    transform-origin: top right;
    pointer-events: none;
    transition:
        opacity   var(--switch-dur) ease,
        transform var(--switch-dur) ease;
}

.lang-menu[data-open="true"] {
    opacity: 1;
    transform: translateY(0) scale(1);
    pointer-events: auto;
}

.lang-option {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 1.5rem;
    padding: 0.5rem 0.625rem 0.5rem 0.75rem;
    min-width: 120px;

    border: none;
    border-radius: var(--radius-sm);
    background: transparent;

    font-family: inherit;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--color-text);
    text-align: left;
    white-space: nowrap;

    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

.lang-option:hover {
    background-color: rgba(10, 10, 10, 0.04);
}

.lang-option:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: -2px;
}

.lang-option.active {
    color: var(--color-accent);
}

.lang-option__check {
    width: 14px;
    height: 14px;
    color: var(--color-accent);
    opacity: 0;
    transition: opacity var(--switch-dur) ease;
    flex-shrink: 0;
}

.lang-option.active .lang-option__check {
    opacity: 1;
}


/* --------------------------------------------------------------------------
   Main hero — oversized headline + product preview card + follow CTA
   -------------------------------------------------------------------------- */

main {
    position: relative;
    /* No z-index here — main intentionally stays without its own root
       stacking context so its descendants share the page's stacking
       order with the fixed header (z:100), the ambient body::before
       (z:0), and the page footer. The hero rows below carry their own
       z-indexes in the "Tooltip stacking hierarchy" block. */
    text-align: center;
    width: 100%;
    max-width: 760px;

    display: flex;
    flex-direction: column;
    align-items: center;
}


/* Hero headline — categorical product description, fluid type */
.hero-title {
    font-size: clamp(1.75rem, 4.2vw, 2.75rem);
    font-weight: 700;
    line-height: 1.1;
    letter-spacing: -0.028em;
    color: var(--color-text);

    max-width: 22ch;
    margin-bottom: 2.5rem;

    text-wrap: balance;
    /* even line lengths in modern browsers */
}

.hero-title .accent {
    color: var(--color-accent);
    cursor: help;
    text-decoration: underline dotted;
    text-decoration-thickness: 1px;
    text-decoration-color: rgba(2, 132, 199, 0.4);
    text-underline-offset: 0.12em;
    transition: text-decoration-color 0.25s ease;
}

.hero-title .accent:hover,
.hero-title .accent:focus-visible {
    text-decoration-color: var(--color-accent);
    outline: none;
}



/* When a non-empty subtitle follows the title, tighten the gap between them */
main:has(.hero-subtitle:not(:empty)) .hero-title {
    margin-bottom: 1.25rem;
}


/* Hero subtitle — supporting tagline beneath the title */
.hero-subtitle {
    font-size: clamp(0.9375rem, 1.5vw, 1.0625rem);
    font-weight: 400;
    line-height: 1.55;
    color: var(--color-text-secondary);

    max-width: 56ch;
    margin-bottom: 1.5rem;

    text-wrap: pretty;
}

.hero-subtitle:empty {
    display: none;
}

/* Inline glossary term — dotted underline signals "hover for explanation" */
.hero-subtitle__term {
    cursor: help;
    text-decoration: underline dotted;
    text-decoration-thickness: 1px;
    text-decoration-color: var(--color-text-muted);
    text-underline-offset: 4px;
    transition: color 0.25s ease, text-decoration-color 0.25s ease;
}

.hero-subtitle__term:hover,
.hero-subtitle__term:focus-visible {
    color: var(--color-text);
    /* Dotted underline darkens to match text — no accent tint here, the
       term is glossary semantics, not a brand call-out. */
    text-decoration-color: var(--color-text);
    outline: none;
}


/* --------------------------------------------------------------------------
   Feature pills — small fact strip beneath the subtitle
   (Free · Local · Private · Verifiable)
   -------------------------------------------------------------------------- */

.features {
    list-style: none;
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.5rem;
    margin-bottom: 2.5rem;
}

.feature {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    height: var(--control-h);
    padding: 0 0.9375rem 0 0.875rem;

    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background-color: var(--color-glass);
    backdrop-filter: blur(14px) saturate(180%);
    -webkit-backdrop-filter: blur(14px) saturate(180%);

    font-size: 0.875rem;
    font-weight: 500;
    letter-spacing: 0.01em;
    color: var(--color-text-secondary);
    white-space: nowrap;

    cursor: help;
    transition:
        background-color 0.18s ease,
        border-color 0.18s ease,
        color 0.18s ease;
}

.feature:hover,
.feature:focus-visible {
    background-color: var(--color-glass-strong);
    border-color: var(--color-border-strong);
    color: var(--color-text);
    outline: none;
}

.feature__icon {
    width: 15px;
    height: 15px;
    color: var(--color-text-secondary);
    flex-shrink: 0;
}


/* --------------------------------------------------------------------------
   Preview card — large window with subtly blurred upcoming UI inside
   -------------------------------------------------------------------------- */

.preview-card {
    /* OUTER container — carries the shared --shadow-card elevation and
       the rounded-corner shape. NO overflow:hidden / clip-path here:
       clip-path would obliterate the three outer drop-shadow layers
       inside --shadow-card, which is what makes the preview card look
       elevated identically to lang-menu and download-menu. */
    position: relative;
    width: 100%;
    max-width: 720px;
    aspect-ratio: 16 / 10;

    border-radius: var(--radius-lg);
    background-color: var(--color-bg);
    box-shadow: var(--shadow-card);

    margin-bottom: 2.5rem;
}

/* INNER clipping wrapper — overflow:hidden + clip-path live here so the
   spotlight stack (blurred mockup, blob mask, dot grid) is bounded by
   the rounded corners on every browser, including iOS Safari (which
   ignores border-radius + overflow:hidden when descendants carry
   filter / mask / transform). The outer card's box-shadow is unaffected
   because clip-path is now scoped to this wrapper, not the card itself. */
.preview-card__clip {
    position: absolute;
    inset: 0;
    border-radius: inherit;
    overflow: hidden;
    clip-path: inset(0 round var(--radius-lg));
}

/* Lightly-blurred mockup — silhouettes show through, fine type doesn't. */
.preview-card__bg {
    position: absolute;
    inset: 0;
    z-index: 0;
    background-image: var(--preview-img);
    background-size: contain;
    background-position: center;
    background-repeat: no-repeat;
    filter: blur(2px) saturate(1.2);
    opacity: 0.95;
    pointer-events: none;

    /* Soft fade to the card edges so the blurred mockup never touches the
       hairline directly — kills the "dirty rim" look around corners. */
    -webkit-mask-image: radial-gradient(ellipse 110% 130% at center,
            rgba(0, 0, 0, 1) 60%, rgba(0, 0, 0, 0) 100%);
    mask-image: radial-gradient(ellipse 110% 130% at center,
            rgba(0, 0, 0, 1) 60%, rgba(0, 0, 0, 0) 100%);
}


/* "Generation field" mask shared by the sharp mockup layer, the dot grid
   and the halo. Built from TWO overlapping radial gradients (blob A +
   blob B) composited additively. As the blobs drift independently the
   visible area morphs between a single round shape, a peanut, and two
   touching circles — the ChatGPT image-gen wobble.

   All four blob coordinates are CSS @property variables so a single
   shared keyframes set drives every layer in lockstep. */
/* inherits:true is critical — the animation runs on .preview-card, but
   the variables are READ by .preview-card__bg-clear, ::before and ::after.
   Without inheritance the children get the initial values forever and
   the blobs never move. */
@property --ax {
    syntax: '<percentage>';
    inherits: true;
    initial-value: 22%;
}

@property --ay {
    syntax: '<percentage>';
    inherits: true;
    initial-value: 32%;
}

@property --bx {
    syntax: '<percentage>';
    inherits: true;
    initial-value: 78%;
}

@property --by {
    syntax: '<percentage>';
    inherits: true;
    initial-value: 68%;
}

@property --as {
    syntax: '<percentage>';
    inherits: true;
    initial-value: 26%;
}

@property --bs {
    syntax: '<percentage>';
    inherits: true;
    initial-value: 26%;
}

/* Blob animation lives on the bg-clear element rather than .preview-card
   so it doesn't get overwritten by the staggered entrance fade-in below.
   @property variables inherit from .preview-card__bg-clear into its
   sibling pseudos via the parent — but pseudos are children of .preview-card
   not of .preview-card__bg-clear, so we re-run the same animation on
   .preview-card itself AS PART of its compound entrance + blob animation
   declared near the entrance block. */

.preview-card__bg-clear {
    position: absolute;
    inset: 0;
    z-index: 1;
    background-image: var(--preview-img);
    background-size: contain;
    background-position: center;
    background-repeat: no-repeat;
    filter: blur(1px) saturate(1.2);
    opacity: 0.97;
    pointer-events: none;

    -webkit-mask-image:
        radial-gradient(closest-side circle at center,
            rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.9) 55%, rgba(0, 0, 0, 0) 100%),
        radial-gradient(closest-side circle at center,
            rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.9) 55%, rgba(0, 0, 0, 0) 100%);
    mask-image:
        radial-gradient(closest-side circle at center,
            rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.9) 55%, rgba(0, 0, 0, 0) 100%),
        radial-gradient(closest-side circle at center,
            rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.9) 55%, rgba(0, 0, 0, 0) 100%);
    -webkit-mask-repeat: no-repeat, no-repeat;
    mask-repeat: no-repeat, no-repeat;
    -webkit-mask-composite: source-over;
    mask-composite: add;
    -webkit-mask-size: var(--as) calc(var(--as) * 1.4), var(--bs) calc(var(--bs) * 1.4);
    mask-size: var(--as) calc(var(--as) * 1.4), var(--bs) calc(var(--bs) * 1.4);
    /* --ax / --ay are the TRUE blob centre (in % of card). mask-position
       wants the top-left corner of the gradient image, so subtract half
       the mask-size on each axis. Lets the centre go to 0% / 100% — or
       beyond — and have the blob actually reach (and overflow) edges. */
    -webkit-mask-position:
        calc(var(--ax) - var(--as) / 2)  calc(var(--ay) - var(--as) * 0.7),
        calc(var(--bx) - var(--bs) / 2)  calc(var(--by) - var(--bs) * 0.7);
    mask-position:
        calc(var(--ax) - var(--as) / 2)  calc(var(--ay) - var(--as) * 0.7),
        calc(var(--bx) - var(--bs) / 2)  calc(var(--by) - var(--bs) * 0.7);
}

/* Faint static dot grid inside the blob area. No size animation — the
   only thing that moves is the blob silhouette below.
   Pseudo lives on .preview-card__clip (the inner clipping wrapper) so
   it sits inside the rounded mask along with __bg / __bg-clear. */
.preview-card__clip::before {
    content: '';
    position: absolute;
    inset: 0;
    z-index: 2;
    pointer-events: none;

    background-image: radial-gradient(circle,
            rgba(120, 120, 120, 0.40) 0.6px,
            transparent 1.1px);
    background-size: 14px 14px;

    -webkit-mask-image:
        radial-gradient(closest-side circle at center,
            rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.9) 55%, rgba(0, 0, 0, 0) 100%),
        radial-gradient(closest-side circle at center,
            rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.9) 55%, rgba(0, 0, 0, 0) 100%);
    mask-image:
        radial-gradient(closest-side circle at center,
            rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.9) 55%, rgba(0, 0, 0, 0) 100%),
        radial-gradient(closest-side circle at center,
            rgba(0, 0, 0, 1) 0%, rgba(0, 0, 0, 0.9) 55%, rgba(0, 0, 0, 0) 100%);
    -webkit-mask-repeat: no-repeat, no-repeat;
    mask-repeat: no-repeat, no-repeat;
    -webkit-mask-composite: source-over;
    mask-composite: add;
    -webkit-mask-size: var(--as) calc(var(--as) * 1.4), var(--bs) calc(var(--bs) * 1.4);
    mask-size: var(--as) calc(var(--as) * 1.4), var(--bs) calc(var(--bs) * 1.4);
    /* --ax / --ay are the TRUE blob centre (in % of card). mask-position
       wants the top-left corner of the gradient image, so subtract half
       the mask-size on each axis. Lets the centre go to 0% / 100% — or
       beyond — and have the blob actually reach (and overflow) edges. */
    -webkit-mask-position:
        calc(var(--ax) - var(--as) / 2)  calc(var(--ay) - var(--as) * 0.7),
        calc(var(--bx) - var(--bs) / 2)  calc(var(--by) - var(--bs) * 0.7);
    mask-position:
        calc(var(--ax) - var(--as) / 2)  calc(var(--ay) - var(--as) * 0.7),
        calc(var(--bx) - var(--bs) / 2)  calc(var(--by) - var(--bs) * 0.7);
}



/* Single source of truth for blob motion.
   Most of the cycle the two blobs sit on top of each other with a tiny
   offset and slightly different sizes — their union reads as ONE
   organic blob that morphs (oval, lopsided, breathing). Only at two
   short moments (around 40-50% and 88-95%) do they pull apart enough to
   read as a brief split + reunion, like ChatGPT's image-gen field
   occasionally pinching into two. */
/* Blob orbit — drifts across the WHOLE preview, including up to and
   slightly past every edge. Coordinates run from -8% to 108% so the
   blob occasionally spills off-card; the card's overflow:hidden clips
   the spill, but inside the card the user sees the spotlight reaching
   ALL the way to the corners (no permanent blurry rim).
   Sizes are ~5% bigger than before (35-43% vs the old 30-38%). */
@keyframes preview-blobs {

    /* united, top-left corner — partly off the edge */
    0% {
        --ax: 8%;
        --ay: 12%;
        --as: 38%;
        --bx: 12%;
        --by: 16%;
        --bs: 34%;
    }

    14% {
        --ax: 70%;
        --ay: 18%;
        --as: 40%;
        --bx: 74%;
        --by: 22%;
        --bs: 36%;
    }

    /* spills off the right edge */
    24% {
        --ax: 102%;
        --ay: 50%;
        --as: 38%;
        --bx: 96%;
        --by: 54%;
        --bs: 34%;
    }

    /* brief split — A bottom-right (off edge), B left */
    36% {
        --ax: 90%;
        --ay: 88%;
        --as: 34%;
        --bx: 22%;
        --by: 78%;
        --bs: 34%;
    }

    44% {
        --ax: 78%;
        --ay: 92%;
        --as: 34%;
        --bx: 8%;
        --by: 70%;
        --bs: 34%;
    }

    /* back together, drifting through bottom */
    56% {
        --ax: 50%;
        --ay: 96%;
        --as: 38%;
        --bx: 54%;
        --by: 92%;
        --bs: 36%;
    }

    /* spills off the bottom-left */
    66% {
        --ax: 12%;
        --ay: 92%;
        --as: 38%;
        --bx: -2%;
        --by: 96%;
        --bs: 34%;
    }

    /* up the left side */
    78% {
        --ax: -4%;
        --ay: 50%;
        --as: 38%;
        --bx: 8%;
        --by: 46%;
        --bs: 36%;
    }

    /* second brief split — A high right, B low centre */
    88% {
        --ax: 92%;
        --ay: 14%;
        --as: 34%;
        --bx: 50%;
        --by: 86%;
        --bs: 34%;
    }

    /* reunite, finishing back at the top-left starting pose */
    100% {
        --ax: 8%;
        --ay: 12%;
        --as: 38%;
        --bx: 12%;
        --by: 16%;
        --bs: 34%;
    }
}

/* Status badge — anchored to the top-right corner of the preview card,
   reading as a system-level annotation over the blurred mock UI */
.preview-card .status {
    position: absolute;
    top: 1rem;
    right: 1rem;
    margin: 0;
    z-index: 4;
}


/* --------------------------------------------------------------------------
   Status badge — minimal annotation pill ("● Status · In development")
   with subtle pulsing dot
   -------------------------------------------------------------------------- */

.status {
    position: relative;

    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0 1rem;
    height: var(--control-h);

    border: 1px solid var(--color-status-border);
    border-radius: var(--radius-md);
    /* matches lang-trigger / feature / links */

    font-size: 0.875rem;
    font-weight: 500;
    letter-spacing: 0.01em;
    color: var(--color-status);

    background-color: rgba(255, 255, 255, 0.92);
    backdrop-filter: blur(20px) saturate(180%);
    -webkit-backdrop-filter: blur(20px) saturate(180%);

    /* Annotation, not interactive — explicit default cursor so it doesn't
       pick up `pointer` from any neighbouring stacking context. */
    cursor: default;

    /* Same duration as the dot ping below — they breathe in sync. */
    animation: status-pulse var(--pulse-dur) ease-in-out infinite;
}

/* Gentle border + ring pulse, synced with the dot's ping —
   echoes the lang-trigger active-state ring, but breathing */
@keyframes status-pulse {

    0%,
    100% {
        border-color: var(--color-status-border);
        box-shadow:
            0 0 0 3px var(--color-status-bg-strong),
            0 1px 2px rgba(10, 10, 10, 0.04);
    }

    50% {
        border-color: var(--color-status-border-strong);
        box-shadow:
            0 0 0 4px var(--color-status-bg-strong),
            0 1px 2px rgba(10, 10, 10, 0.04);
    }
}

.status__dot {
    position: relative;
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background-color: var(--color-status);
    flex-shrink: 0;
}

.status__dot::before {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background-color: var(--color-status);
    animation: ping var(--pulse-dur) ease-in-out infinite;
}

@keyframes ping {
    0% {
        transform: scale(1);
        opacity: 0.6;
    }

    75%,
    100% {
        transform: scale(2.5);
        opacity: 0;
    }
}


/* --------------------------------------------------------------------------
   Download CTA — split button (primary + caret) with platform dropdown.
   Primary advertises the auto-detected OS; caret opens a menu listing
   all supported platforms plus a fallback link to GitHub releases.
   -------------------------------------------------------------------------- */

.download {
    position: relative;
    display: inline-flex;
    align-items: stretch;
    margin-bottom: 2.5rem;
    /* Lifted above .preview-card (z:1) so the dropdown opens IN FRONT of
       the preview card instead of being clipped behind it. Stays below
       the sticky header (z:100). */
    z-index: 5;
}

.download__primary {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0 1.125rem 0 1rem;
    height: 44px;

    border: none;
    border-radius: var(--radius-md) 0 0 var(--radius-md);
    background-color: var(--color-accent);
    color: #ffffff;

    font-family: inherit;
    font-size: 0.9375rem;
    font-weight: 600;
    letter-spacing: -0.005em;
    white-space: nowrap;

    cursor: pointer;
    pointer-events: auto;
    transition: background-color 0.2s ease, color 0.2s ease;
}

.download__primary:hover {
    background-color: #0369a1;
    /* accent darkened ~10% */
}

.download__primary:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

.download__primary[aria-disabled="true"] {
    background-color: rgba(10, 10, 10, 0.06);
    color: var(--color-text-muted);
    cursor: not-allowed;
}

.download__primary[aria-disabled="true"]:hover {
    background-color: rgba(10, 10, 10, 0.08);
}

.download__icon {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}

.download__caret {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 44px;

    border: none;
    border-left: 1px solid rgba(255, 255, 255, 0.18);
    border-radius: 0 var(--radius-md) var(--radius-md) 0;
    background-color: var(--color-accent);
    color: #ffffff;

    cursor: pointer;
    transition: background-color 0.2s ease, color 0.2s ease;
}

.download__caret:hover {
    background-color: #0369a1;
}

.download__caret:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 2px;
}

/* When primary is in disabled state, caret follows the muted look */
.download__primary[aria-disabled="true"]+.download__caret {
    background-color: rgba(10, 10, 10, 0.06);
    color: var(--color-text-muted);
    border-left-color: rgba(10, 10, 10, 0.06);
}

.download__primary[aria-disabled="true"]+.download__caret:hover {
    background-color: rgba(10, 10, 10, 0.08);
    color: var(--color-text-secondary);
}

.download__caret svg {
    width: 11px;
    height: 7px;
    transition: transform 0.22s ease;
}

.download__caret[aria-expanded="true"] svg {
    transform: rotate(180deg);
}

.download__menu {
    /* Default direction: above. The CTA sits near the page fold, so opening
       upward is the right default; JS flips to data-position="below" when
       there isn't enough room above. Putting "above" in CSS (instead of
       only in JS) keeps the menu out of the document's scroll height while
       it's closed — otherwise the absolutely-positioned menu, anchored
       below the CTA, would push body.scrollHeight ~360px past the footer. */
    position: absolute;
    bottom: calc(100% + 0.5rem);
    top: auto;
    /* Centred on the CTA via translateX(-50%); transform also drives the
       open/close motion, so all transforms compose into a single value. */
    left: 50%;
    right: auto;
    min-width: 18rem;

    display: flex;
    flex-direction: column;
    gap: 0.0625rem;
    padding: 0.25rem;

    /* Border replaced by inset hairline inside --shadow-card so the menu
       has the same edge as the preview card and the language menu. */
    border-radius: var(--radius-md);
    background-color: var(--color-glass-strong);
    backdrop-filter: blur(20px) saturate(180%);
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    box-shadow: var(--shadow-card);

    opacity: 0;
    transform: translate(-50%, 6px) scale(0.96);
    transform-origin: bottom center;
    pointer-events: none;
    transition: opacity var(--switch-dur) ease, transform var(--switch-dur) ease;
    z-index: 20;
}

/* Override: open below the trigger when the JS positioner detects there
   isn't enough room above (e.g. very short viewport). */
.download__menu[data-position="below"] {
    top: calc(100% + 0.5rem);
    bottom: auto;
    transform: translate(-50%, -6px) scale(0.96);
    transform-origin: top center;
}

.download__menu[data-open="true"] {
    opacity: 1;
    transform: translate(-50%, 0) scale(1);
    pointer-events: auto;
}

.download__menu-item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.5625rem 0.75rem;

    border: none;
    border-radius: var(--radius-sm);
    background: transparent;

    font-family: inherit;
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--color-text);
    text-align: left;
    text-decoration: none;
    white-space: nowrap;

    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}

/* Two-line item body — platform name on top, format/meta as muted subtitle */
.download__menu-item__body {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 0.0625rem;
    min-width: 0;
    text-align: left;
}

.download__menu-item__name {
    font-size: 0.875rem;
    font-weight: 500;
    color: inherit;
    line-height: 1.25;
}

.download__menu-item__meta {
    font-size: 0.6875rem;
    font-weight: 400;
    color: var(--color-text-muted);
    line-height: 1.3;
    letter-spacing: 0;
}

.download__menu-item[aria-disabled="true"] .download__menu-item__meta {
    color: rgba(115, 115, 115, 0.6);
}

.download__menu-item.is-active .download__menu-item__meta {
    color: rgba(2, 132, 199, 0.7);
}

.download__menu-item:hover {
    background-color: rgba(10, 10, 10, 0.04);
}

.download__menu-item:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: -2px;
}

.download__menu-item[aria-disabled="true"] {
    color: var(--color-text-muted);
    cursor: not-allowed;
}

.download__menu-item[aria-disabled="true"]:hover {
    color: var(--color-text-secondary);
}

/* Brand/platform icon on the left of each menu item — Apple, Windows panes,
   Tux, Android droid, GitHub octocat. Renders muted, hover follows item. */
.download__menu-item__os {
    width: 18px;
    height: 18px;
    color: var(--color-text-secondary);
    flex-shrink: 0;
    transition: color 0.15s ease;
}

.download__menu-item:hover .download__menu-item__os {
    color: var(--color-text);
}

.download__menu-item[aria-disabled="true"] .download__menu-item__os {
    color: var(--color-text-muted);
}

/* Checkmark next to currently-selected OS — mirrors lang-option pattern */
.download__menu-item__check {
    width: 14px;
    height: 14px;
    color: var(--color-accent);
    opacity: 0;
    transition: opacity var(--switch-dur) ease;
    flex-shrink: 0;
}

.download__menu-item.is-active {
    color: var(--color-accent);
}

.download__menu-item.is-active .download__menu-item__check {
    opacity: 1;
}

/* Active item — name, OS icon and meta all stay accent regardless of
   hover (mirrors the lang-menu's active-state behaviour). */
.download__menu-item.is-active .download__menu-item__os {
    color: var(--color-accent);
}

.download__menu-item.is-active:hover {
    color: var(--color-accent);
}

.download__menu-item.is-active:hover .download__menu-item__os {
    color: var(--color-accent);
}

.download__menu-item.is-active:hover .download__menu-item__meta {
    color: rgba(2, 132, 199, 0.7);
}

.download__menu-sep {
    border: none;
    height: 1px;
    background-color: var(--color-border);
    margin: 0.25rem 0;
}

.download__menu-item__arrow {
    width: 12px;
    height: 12px;
    color: var(--color-text-muted);
    flex-shrink: 0;
}


/* --------------------------------------------------------------------------
   Follow prompt — small text label above social links
   -------------------------------------------------------------------------- */

.follow-prompt {
    font-size: 0.875rem;
    font-weight: 500;
    letter-spacing: 0.01em;
    color: var(--color-text-muted);
    margin-bottom: 1rem;
}


/* --------------------------------------------------------------------------
   Social links — clean modern hover (no shadow / no lift)
   -------------------------------------------------------------------------- */

.links {
    display: flex;
    gap: 0.625rem;
    justify-content: center;
    flex-wrap: wrap;
}

.links a {
    position: relative;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;

    padding: 0 1.125rem;
    height: var(--control-h);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);

    font-size: 0.875rem;
    font-weight: 500;
    color: var(--color-text);
    text-decoration: none;

    background-color: var(--color-glass);
    backdrop-filter: blur(14px) saturate(180%);
    -webkit-backdrop-filter: blur(14px) saturate(180%);

    transition:
        border-color 0.3s ease,
        color 0.3s ease,
        background-color 0.3s ease;
}

.links a:hover {
    border-color: rgba(2, 132, 199, 0.35);
    color: var(--color-accent);
    background-color: var(--color-accent-bg);
}

.links a:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 3px;
}

.links a svg {
    width: 18px;
    height: 18px;
    flex-shrink: 0;
}


/* --------------------------------------------------------------------------
   Footer — copyright + secondary contact, fixed bottom-center
   -------------------------------------------------------------------------- */

.site-footer {
    /* No z-index — would trap tooltips on Privacy/Terms inside footer's
       stacking context. Document order keeps footer above body::before. */
    width: 100%;
    text-align: center;
    padding: 1.5rem var(--hpad-page);
    margin-top: 1rem;
    /* combines with padding-top for 2.5rem total — matches preview→follow rhythm */

    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: 0.02em;
    color: var(--color-text-faint);
}

.site-footer__separator {
    display: inline-block;
    margin: 0 0.625rem;
    opacity: 0.5;
}

.site-footer__contact {
    color: var(--color-text-faint);
    text-decoration: none;
    transition: color 0.2s ease;
}

.site-footer__contact:hover {
    color: var(--color-accent);
}

.site-footer__contact:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 3px;
    border-radius: var(--focus-radius);
}

/* Inactive footer links (Privacy / Terms) — styled to read as text-links
   while disabled; tooltip on hover explains the state */
.site-footer__link {
    background: none;
    border: none;
    padding: 0;

    font-family: inherit;
    font-size: inherit;
    font-weight: inherit;
    line-height: inherit;
    letter-spacing: inherit;
    color: var(--color-text-faint);
    vertical-align: baseline;

    cursor: not-allowed;
    pointer-events: auto;
    transition: color 0.2s ease;
}

.site-footer__link:hover {
    color: var(--color-text-secondary);
}

.site-footer__link:focus-visible {
    outline: 2px solid var(--color-accent);
    outline-offset: 3px;
    border-radius: var(--focus-radius);
}


/* --------------------------------------------------------------------------
   View Transitions API — smooth language swap
   -------------------------------------------------------------------------- */

::view-transition-old(root),
::view-transition-new(root) {
    animation-duration: 0.28s;
    animation-timing-function: var(--ease-smooth);
}


/* --------------------------------------------------------------------------
   Page entrance — staggered fade-in
   -------------------------------------------------------------------------- */

@keyframes fade-in {
    from {
        opacity: 0;
        transform: translateY(8px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.site-header,
.hero-title,
.hero-subtitle,
.features,
.follow-prompt,
.links,
.site-footer {
    animation: fade-in 0.7s var(--ease-out) both;
}

/* preview-card runs the blob morph in parallel with its entrance fade.
   Compound shorthand declares both animations on the single `animation`
   property so neither is overwritten by the other. */
.preview-card {
    animation:
        fade-in 0.7s var(--ease-out) 0.42s both,
        preview-blobs 20s ease-in-out infinite;
}

.site-header {
    animation-delay: 0.05s;
}

.hero-title {
    animation-delay: 0.18s;
}

.hero-subtitle {
    animation-delay: 0.26s;
}

.features {
    animation-delay: 0.34s;
}

.follow-prompt {
    animation-delay: 0.56s;
}

.links {
    animation-delay: 0.66s;
}

.site-footer {
    animation-delay: 0.78s;
}


/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */

@media (max-width: 640px) {
    body {
        /* Top padding fits the two-row mobile header (brand+lang row +
           sub-row with secondary nav) with breathing room. */
        padding: 7rem var(--hpad-page-sm) 0;

        /* Anchor content to the top on mobile. The desktop default
           (`justify-content: safe center`) vertically centres
           main+footer within `min-height: 100vh` — useful for short
           pages on big screens, harmful on mobile where it can push
           the hero below the fold when content+padding sit close to
           100vh. Forcing flex-start removes the ambiguity. */
        justify-content: flex-start;
    }

    .site-header__inner {
        padding: 0.75rem var(--hpad-page-sm);
        min-height: auto;

        /* Two-row layout on mobile: brand + lang in row 1, secondary
           nav wraps onto row 2 via flex-basis:100% on .site-nav. */
        flex-wrap: wrap;
        row-gap: 0.375rem;
    }

    /* Keep header height stable on scroll on mobile — shrinking padding
       on a two-row layout would jolt the body offset. */
    .site-header.is-scrolled .site-header__inner {
        padding-top: 0.75rem;
        padding-bottom: 0.75rem;
        min-height: auto;
    }

    /* All pills shrink uniformly to 36px on mobile */
    .brand,
    .lang-trigger,
    .status,
    .feature,
    .links a {
        /* WCAG 2.5.5 / Apple HIG ask for ≥40 px touch targets;
           keeping the desktop --control-h instead of dropping to 36 px. */
        height: var(--control-h);
    }

    .brand__logo {
        width: 28px;
        height: 28px;
    }

    .brand__name {
        font-size: 1.125rem;
    }

    .lang-trigger {
        padding: 0 0.625rem 0 0.5625rem;
        font-size: 0.8125rem;
    }

    .lang-trigger__icon {
        width: 13px;
        height: 13px;
    }

    .status {
        padding: 0 0.875rem;
        font-size: 0.8125rem;
    }

    .preview-card .status {
        top: 0.875rem;
        right: 0.875rem;
    }

    .links a {
        padding: 0 1rem;
        font-size: 0.8125rem;
    }

    .hero-title {
        margin-bottom: 1.75rem;
    }

    main:has(.hero-subtitle:not(:empty)) .hero-title {
        margin-bottom: 1rem;
    }

    .hero-subtitle {
        font-size: 0.9375rem;
        line-height: 1.5;
        margin-bottom: 1.25rem;
    }

    .features {
        gap: 0.375rem;
        margin-bottom: 1.75rem;
    }

    .feature {
        padding: 0 0.875rem;
        font-size: 0.8125rem;
    }

    .preview-card {
        margin-bottom: 1.75rem;
        border-radius: var(--radius-md);
    }
    /* Clip wrapper inherits border-radius via `border-radius: inherit`,
       but clip-path uses an explicit length so we override it on mobile. */
    .preview-card__clip {
        clip-path: inset(0 round var(--radius-md));
    }

    /* Secondary nav drops onto a sub-row beneath the brand and lang
       switcher. order:3 + flex-basis:100% pushes it onto a new flex
       line; centred to balance the visual weight of the row above. */
    .site-nav {
        order: 3;
        flex-basis: 100%;
        height: 32px;
        gap: 0.125rem;
        justify-content: center;
    }

    .site-nav__item {
        font-size: 0.8125rem;
        padding: 0 0.625rem;
    }

    .site-nav__badge {
        height: 16px;
        font-size: 0.625rem;
        padding: 0 0.375rem;
        margin-left: 0.375rem;
    }

    .download {
        margin-bottom: 1.75rem;
    }

    .download__primary {
        height: 40px;
        padding: 0 0.875rem 0 0.8125rem;
        font-size: 0.875rem;
        gap: 0.4375rem;
    }

    .download__icon {
        width: 16px;
        height: 16px;
    }

    .download__caret {
        /* 40 × 40 keeps the caret at the WCAG touch-target minimum. */
        width: 40px;
        height: 40px;
    }

    .download__menu {
        min-width: 11rem;
    }

    .site-footer {
        padding: 1rem var(--hpad-page-sm);
        margin-top: 0.75rem;
        font-size: 0.75rem;
        line-height: 1.7;
        /* breathing room when content wraps to 2 lines */
    }

    .site-footer__separator {
        margin: 0 0.4375rem;
        /* tighter on narrow viewports */
    }
}


/* --------------------------------------------------------------------------
   Universal tooltip — populated by JS via data-tooltip-text attribute
   (sourced from translations under data-tooltip-key)
   -------------------------------------------------------------------------- */

[data-tooltip-text] {
    position: relative;
}

[data-tooltip-text]::after {
    content: attr(data-tooltip-text);
    position: absolute;
    /* Default direction: above. Same offset (0.625rem ≈ 10px) regardless of
       direction so all tooltips sit at identical distance from their anchor. */
    bottom: calc(100% + 0.625rem);
    top: auto;
    left: 50%;

    width: max-content;
    max-width: min(280px, calc(100vw - 2rem));
    padding: 0.5rem 0.6875rem;
    background-color: rgba(15, 15, 15, 0.97);
    color: rgba(255, 255, 255, 0.95);
    font-family: inherit;
    font-size: 0.75rem;
    font-weight: 500;
    line-height: 1.4;
    letter-spacing: 0;
    text-align: center;
    /* Override inherited / propagated styles from anchor element
       (e.g. dotted underline from .hero-title .accent and .hero-subtitle__term
       that would otherwise visually run through the tooltip text) */
    white-space: normal;
    word-break: normal;
    overflow-wrap: break-word;
    text-decoration: none;
    border-radius: var(--radius-sm);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    box-shadow: 0 4px 12px -2px rgba(0, 0, 0, 0.18);

    opacity: 0;
    transform: translateX(calc(-50% + var(--tooltip-shift-x, 0px))) translateY(4px);
    pointer-events: none;
    /* Top of the stacking order — sits above the preview-card spotlight
       (z:4 inside that card), the site header (z:100 on the page) and
       any future overlays. The header still hides scroll-passing content
       behind its glass because tooltips only open over their anchors. */
    z-index: 1000;
    transition: opacity var(--switch-dur) ease, transform var(--switch-dur) ease;
}

/* Three ways the tooltip becomes visible:
     1. focus-visible — always (works on every device, incl. keyboard nav)
     2. tap          — JS sets [data-tooltip-show] on the anchor
     3. hover        — only on pointer devices (see media query below);
                       iOS Safari otherwise leaves the tooltip stuck on
                       after a tap until the next tap elsewhere.
   No transition-delay anywhere — the tooltip should appear immediately
   when the user actually targets the anchor. The opacity/transform
   transition itself (--switch-dur ≈ 0.18s) is enough to keep the
   reveal smooth without feeling slow. */
[data-tooltip-text]:focus-visible::after,
[data-tooltip-text][data-tooltip-show]::after {
    opacity: 1;
    transform: translateX(calc(-50% + var(--tooltip-shift-x, 0px))) translateY(0);
}

/* Initial-direction variant: tooltip below (used on top-anchored elements
   like the nav items and the hero "Experience"/"опыт" word). */
[data-tooltip-position="below"]::after {
    bottom: auto;
    top: calc(100% + 0.625rem);
    transform: translateX(calc(-50% + var(--tooltip-shift-x, 0px))) translateY(-4px);
}

[data-tooltip-position="below"]:focus-visible::after,
[data-tooltip-position="below"][data-tooltip-show]::after {
    transform: translateX(calc(-50% + var(--tooltip-shift-x, 0px))) translateY(0);
}

/* Runtime auto-flip overrides — JS sets data-tooltip-flip when the static
   direction would overflow the viewport. These override data-tooltip-position. */
[data-tooltip-flip="above"]::after {
    bottom: calc(100% + 0.625rem);
    top: auto;
    transform: translateX(calc(-50% + var(--tooltip-shift-x, 0px))) translateY(4px);
}

[data-tooltip-flip="above"]:focus-visible::after,
[data-tooltip-flip="above"][data-tooltip-show]::after {
    transform: translateX(calc(-50% + var(--tooltip-shift-x, 0px))) translateY(0);
}

[data-tooltip-flip="below"]::after {
    bottom: auto;
    top: calc(100% + 0.625rem);
    transform: translateX(calc(-50% + var(--tooltip-shift-x, 0px))) translateY(-4px);
}

[data-tooltip-flip="below"]:focus-visible::after,
[data-tooltip-flip="below"][data-tooltip-show]::after {
    transform: translateX(calc(-50% + var(--tooltip-shift-x, 0px))) translateY(0);
}

/* Mouse / trackpad / pen — devices with a real hover state. */
@media (hover: hover) {
    [data-tooltip-text]:hover::after {
        opacity: 1;
        transform: translateX(calc(-50% + var(--tooltip-shift-x, 0px))) translateY(0);
    }
    [data-tooltip-position="below"]:hover::after,
    [data-tooltip-flip="above"]:hover::after,
    [data-tooltip-flip="below"]:hover::after {
        transform: translateX(calc(-50% + var(--tooltip-shift-x, 0px))) translateY(0);
    }
}

/* Tooltip stacking hierarchy.
   Each anchor row gets its own stacking context with a z-index that
   reflects "earlier in the DOM, but tooltips must paint OVER the things
   that come after". Higher = paints over.
   All values stay BELOW the sticky header (z:100) so that scrolling
   content is always covered by the bar.
       hero-title    (Experience)  →  z 4
       hero-subtitle (model)       →  z 3
       features      (4 pills)     →  z 2  (over preview card)
       preview-card                →  z 1
   The tooltip pseudo-element itself uses a tall z-index inside its
   anchor's local stacking context, so it never gets clipped by the
   anchor's siblings — but the row z-index keeps it below the header. */
.hero-title {
    position: relative;
    z-index: 4;
}

.hero-subtitle {
    position: relative;
    z-index: 3;
}

.features {
    position: relative;
    z-index: 2;
}

/* .preview-card already declares `position: relative` in its main rule
   (see top of "Preview card" section); only the z-index is added here. */
.preview-card {
    z-index: 1;
}

/* Each pill gets its own stacking context (its tooltip needs to escape
   the .features row without being clipped by neighbouring pills) AND
   a descending z-index left → right. So when a tooltip from an
   earlier pill (e.g. "Free") opens downward and overlaps a later
   pill's tooltip area, it always paints on top. */
.feature {
    position: relative;
}
.feature:nth-of-type(1) { z-index: 4; }   /* Free / Бесплатно */
.feature:nth-of-type(2) { z-index: 3; }   /* Local / Локально */
.feature:nth-of-type(3) { z-index: 2; }   /* Private / Конфиденциально */
.feature:nth-of-type(4) { z-index: 1; }   /* Verifiable / Проверяемо */


/* --------------------------------------------------------------------------
   Reduced motion — respect user preference
   -------------------------------------------------------------------------- */

@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

    .status,
    .preview-card__bg-clear,
    .preview-card__clip::before,
    .status__dot::before,
    .site-header,
    .hero-title,
    .hero-subtitle,
    .features,
    .preview-card,
    .download,
    .follow-prompt,
    .links,
    .site-footer {
        animation: none;
    }

    *,
    *::before,
    *::after {
        transition-duration: 0.01ms !important;
        animation-duration: 0.01ms !important;
        animation-iteration-count: 1 !important;
    }
}