/* ─────────────────────────────────────────────────────────────────────────────
   Vapor Storage Manager — shared site layer

   Loaded AFTER each page's inline <style>, so anything here wins on a tie.
   Holds only what must be identical everywhere: the background wash and the
   small-detail motion. Page-specific layout stays in the page.
   ───────────────────────────────────────────────────────────────────────────── */

/* ── Light theme: the page is not pure white ──
   Every page declares its own light palette in :root, and this file loads after
   them, so one override here reaches all twelve instead of editing each.
   .dark-mode sits on <body>, so dark still wins inside body — this only replaces
   the light values inherited from <html>.

   Pure #ffffff under a full-viewport gradient reads as glare, and the wash has
   nothing to tint. The page drops to a soft grey-white; --secondary-bg has to move
   with it or panels drawn on the page would land on the same colour and vanish.
   The gap between them is kept at roughly what it was, so nothing that relied on
   "panel is slightly darker than page" changes behaviour. This matches the desktop
   app's light page value, which is the same decision made twice. */
:root {
    --bg-color:      #f6f8fa;
    --secondary-bg:  #ebeef2;
    --glass-bg:      rgba(246, 248, 250, 0.8);
}

/* ── Gradient palette ──
   Two stops read as flat, so the wash uses six. The theme's own background is
   still one of them (--grad-base), which keeps the "light bleeding into dark"
   idea, but the rest are colour so the field has somewhere to travel.
   Alpha stays low: these sit behind body copy and must never cost contrast.
   Red is deliberately absent — it means "error" everywhere else in the UI. */
/* Light theme needs MORE colour than dark, not less. A translucent tint over
   near-black darkens toward its own hue and reads strongly; the same tint over
   white only lifts it a few percent and washes out. These alphas are roughly
   1.5x the dark values, and the hues are deeper (less white mixed in) for the
   same reason. Text is black here, so contrast has plenty of headroom. */
:root {
    --grad-blue:    rgba(21, 92, 210, 0.50);
    --grad-violet:  rgba(118, 63, 216, 0.46);
    --grad-cyan:    rgba(14, 150, 168, 0.40);
    --grad-magenta: rgba(178, 51, 168, 0.38);
    --grad-green:   rgba(26, 148, 94, 0.34);
}
.dark-mode {
    --grad-blue:    rgba(56, 139, 253, 0.42);
    --grad-violet:  rgba(163, 113, 247, 0.38);
    --grad-cyan:    rgba(45, 200, 215, 0.30);
    --grad-magenta: rgba(219, 97, 208, 0.28);
    --grad-green:   rgba(63, 185, 80, 0.24);
}

/* ── Site-wide wash ──
   Two independent layers drifting at different speeds and along different
   paths: one layer alone reads as a single sheet sliding about, two reading
   against each other look like the colours are actually mixing.

   position: fixed so it covers the viewport on every page without growing the
   document — an absolutely positioned wash pushed the page to 1462px in a
   1280px window and produced a sideways scroll.

   Deliberately NO filter: blur(). Radial gradients are soft on their own; a
   blurred full-viewport layer is expensive and was the source of the haze.

   linear + a keyframe set whose 0% and 100% are identical, so the loop closes
   seamlessly and the motion never appears to stop and reverse. (ease-in-out
   with `alternate` visibly paused at each end — that is the "should move
   constantly" complaint.)

   animation-delay comes from a variable set by site.js before first paint, so
   the phase carries across page navigations instead of snapping back to 0. */
body::before,
body::after {
    content: '';
    position: fixed;
    inset: -18%;
    z-index: -1;
    pointer-events: none;
    will-change: transform;
}

body::before {
    background:
        radial-gradient(38% 34% at 14% 18%, var(--grad-blue)    0%, transparent 62%),
        radial-gradient(36% 32% at 74% 22%, var(--grad-violet)  0%, transparent 60%),
        radial-gradient(34% 30% at 58% 82%, var(--grad-magenta) 0%, transparent 62%);
    /* blur is safe here: this is a standalone fixed layer with no text of its own,
       so nothing readable gets resampled. It is what turns three hard blobs into an
       actual mesh. */
    filter: blur(60px);
    animation: washA 26s linear infinite;
    animation-delay: var(--wash-delay-a, 0s);
}

body::after {
    background:
        radial-gradient(36% 32% at 86% 74%, var(--grad-cyan)   0%, transparent 62%),
        radial-gradient(34% 30% at 20% 78%, var(--grad-green)  0%, transparent 60%),
        radial-gradient(32% 30% at 44% 8%,  var(--grad-blue)   0%, transparent 62%),
        /* Keeps the middle of the viewport from staying flat — with blobs only at
           the edges the centre, which is exactly where the eye sits, read as plain
           white. Wide and soft so it tints rather than sits behind the copy. */
        radial-gradient(54% 46% at 50% 48%, var(--grad-violet) 0%, transparent 72%);
    filter: blur(60px);
    animation: washB 37s linear infinite;
    animation-delay: var(--wash-delay-b, 0s);
}

/* Closed loops: 100% lands exactly on 0%, so `linear infinite` never hitches.
   Amplitude is large on purpose — at 2-4% the blobs barely left their starting
   position and the whole thing read as a still image. Rotation is included
   because translation alone slides the sheet; rotating the field is what makes
   the colours actually pass through each other. */
@keyframes washA {
    0%   { transform: translate3d(0, 0, 0)        rotate(0deg); }
    25%  { transform: translate3d(12%, 7%, 0)     rotate(4deg); }
    50%  { transform: translate3d(4%, 14%, 0)     rotate(0deg); }
    75%  { transform: translate3d(-9%, 6%, 0)     rotate(-4deg); }
    100% { transform: translate3d(0, 0, 0)        rotate(0deg); }
}

@keyframes washB {
    0%   { transform: translate3d(0, 0, 0)        rotate(0deg) scale(1); }
    25%  { transform: translate3d(-11%, 6%, 0)    rotate(-5deg) scale(1.08); }
    50%  { transform: translate3d(-4%, -10%, 0)   rotate(0deg) scale(1.14); }
    75%  { transform: translate3d(10%, -5%, 0)    rotate(5deg) scale(1.06); }
    100% { transform: translate3d(0, 0, 0)        rotate(0deg) scale(1); }
}

/* ─────────────────────────────────────────────────────────────────────────────
   Text sharpness

   Everything below exists to keep glyphs crisp. The rule is simple: never
   scale, rotate or half-pixel-shift a box that contains text. Transforms
   promote an element to its own composited layer, where the text is rasterised
   once and then resampled — which is exactly the blur being complained about.
   ───────────────────────────────────────────────────────────────────────────── */

/* Once revealed, drop the transform entirely. translateY(0) still leaves the
   element on a composited layer; `none` hands it back to normal text rendering. */
.reveal.visible { transform: none; }

/* The price is type — animate its colour, not its size. */
.price-card:hover .price,
.plan:hover .price { transform: none; }

/* Feature icons contain their number ("01", "02"…), so they get a border and
   background response instead of the previous rotate + scale. */
.feature-card:hover .feature-icon {
    transform: none;
    background-color: var(--secondary-bg);
    border-color: var(--text-color);
}

/* ─────────────────────────────────────────────────────────────────────────────
   Small-detail motion — every page

   Only transform/opacity on NON-text elements, plus colour and border on the
   rest. All compositor-friendly: none of it triggers layout during scroll.
   ───────────────────────────────────────────────────────────────────────────── */

/* Logo diamond — a single glyph with no reading text, so rotation is safe. */
.logo::before {
    display: inline-block;
    transition: transform 0.45s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.logo:hover::before { transform: rotate(135deg); }

/* Nav underline grows from the centre rather than snapping on. */
.main-nav a { position: relative; border-bottom-color: transparent !important; }
.main-nav a::after {
    content: '';
    position: absolute; left: 50%; right: 50%; bottom: 0;
    height: 2px; background: var(--text-color);
    transition: left 0.28s ease, right 0.28s ease;
}
.main-nav a:hover::after,
.main-nav a.active::after { left: 0; right: 0; }

/* Theme toggle: the icon turns as the palette swaps. */
.theme-toggle svg { transition: transform 0.5s cubic-bezier(0.34, 1.56, 0.64, 1); }
.theme-toggle:hover svg { transform: rotate(45deg); }
.theme-toggle:active { transform: scale(0.94); }

/* Language switcher */
.lang-switcher button { transition: opacity 0.25s ease, transform 0.25s ease; }
.lang-switcher button:active { transform: translateY(1px); }

/* ─────────────────────────────────────────────────────────────────────────────
   Cards

   Cards lift by whole pixels — fractional offsets are what smear edges.

   Two separate bugs are fixed here, and both are easy to reintroduce:

   1. This file loads AFTER each page's inline <style>, so the `transition`
      declared here OVERRODE the page's `transition: all`. background-color was
      missing from the list, which is why .feature-card's hover background
      snapped on with no fade whatsoever. Every property that actually changes
      on hover must stay listed below.

   2. Fading from an unset background is a trap. The initial value is
      `transparent` — which is rgba(0,0,0,0), i.e. transparent BLACK — so a fade
      toward white passes through grey and reads as a dirty flash. Cards
      therefore carry a resting tint of the SAME colour as their hover state at
      a lower alpha. Only the alpha moves, so the fade is clean, and the wash
      still shows through a card at rest instead of being punched out by it.
   ───────────────────────────────────────────────────────────────────────────── */

:root {
    --card-surface:       rgba(255, 255, 255, 0.34);
    --card-surface-hover: rgba(255, 255, 255, 0.92);
}
.dark-mode {
    --card-surface:       rgba(10, 10, 10, 0.34);
    --card-surface-hover: rgba(10, 10, 10, 0.92);
}

/* Group A — cards you click or choose between: full response.
   Group B — standing panels: they get the line and the border only. They must
   not lift or repaint, because each one holds something being read or typed
   into: a form field, an order total, or the licence key itself. */
.feature-card, .step-card, .price-card, .plan, .dl-card, .card, .stat, .fix-item,
.faq-item, .contact-box, .highlight, .wishlist, .summary, .key-panel {
    position: relative;
    transition:
        transform        0.35s cubic-bezier(0.22, 1, 0.36, 1),
        box-shadow       0.35s cubic-bezier(0.22, 1, 0.36, 1),
        background-color 0.45s ease,
        border-color     0.35s ease;
}

/* Group A only: the resting tint and the hover repaint. */
.feature-card, .step-card, .price-card, .plan, .dl-card, .card, .stat, .fix-item {
    background-color: var(--card-surface);
}
.feature-card:hover, .step-card:hover, .price-card:hover, .plan:hover,
.dl-card:hover, .card:hover, .stat:hover, .fix-item:hover {
    background-color: var(--card-surface-hover);
}

/* The line.
   Drawn on ::after and animated with scaleX rather than width: scaleX runs on
   the compositor, while width relayouts the card on every frame of the hover.
   Safe to transform because a pseudo-element holds no text of its own — the
   no-transform rule further up applies to boxes that contain readable copy.
   Offset by 1px on three sides so it covers the card's own border and reads as
   that edge lighting up, rather than as a second line sitting just inside it. */
.feature-card::after, .step-card::after, .price-card::after, .plan::after,
.dl-card::after, .card::after, .stat::after, .fix-item::after, .faq-item::after,
.contact-box::after, .highlight::after, .wishlist::after, .summary::after,
.key-panel::after {
    content: '';
    position: absolute;
    left: -1px;
    bottom: -1px;
    width: calc(100% + 2px);
    height: 2px;
    background: var(--text-color);
    transform: scaleX(0);
    transform-origin: left center;
    transition: transform 0.55s cubic-bezier(0.22, 1, 0.36, 1);
    pointer-events: none;
}
.feature-card:hover::after, .step-card:hover::after, .price-card:hover::after,
.plan:hover::after, .dl-card:hover::after, .card:hover::after,
.stat:hover::after, .fix-item:hover::after, .faq-item:hover::after, .contact-box:hover::after,
.highlight:hover::after, .wishlist:hover::after, .summary:hover::after,
.key-panel:hover::after {
    transform: scaleX(1);
}

/* Do NOT put `reveal` on a card. `.reveal.visible { transform: none }` above has
   the same specificity as `.card:hover` and is declared later in this file, so it
   silently wins and the card stops lifting. Wrap the card in a revealed element
   instead. */

/* An unpublished download card is inert, so it must not look interactive. */
.card[aria-disabled="true"]::after, .dl-card[aria-disabled="true"]::after {
    display: none;
}

.faq-item:hover, .contact-box:hover, .highlight:hover,
.wishlist:hover, .summary:hover, .key-panel:hover {
    border-color: var(--text-color);
}

/* ── Links inside body copy ──
   No page ever declared a colour for these, so they fell through to the browser
   default of #0000EE. On a dark panel that is all but unreadable — the support
   address in the pricing note was the visible symptom, but every FAQ answer,
   terms paragraph and refund link had it too.

   Scoped two ways so nothing else moves: only inside <main>, and only links with
   no class of their own. Every nav item, button and card link carries a class and
   its own colour rule, and a class selector still outranks this.

   Underline instead of a colour, because the design runs on ink-and-paper black
   and white — adding a blue would put a third colour into a two-colour page. */
main a:not([class]) {
    color: var(--text-color);
    text-decoration: underline;
    text-decoration-thickness: 1px;
    text-underline-offset: 3px;
    transition: text-decoration-thickness 0.2s ease, opacity 0.2s ease;
}
main a:not([class]):hover {
    text-decoration-thickness: 2px;
}

/* Trust / badge ticks — pure symbols, safe to spin. */
.badge::before, .trust div::before {
    display: flex;
    transition: transform 0.4s cubic-bezier(0.34, 1.56, 0.64, 1), background-color 0.3s ease;
}
.badge:hover::before, .trust div:hover::before {
    transform: rotate(360deg);
    background-color: var(--secondary-bg);
}

/* Window chrome dots pick up their real colours on hover. */
.window-dot { transition: background-color 0.4s ease, border-color 0.4s ease; }
.window-frame:hover .window-dot:nth-child(1) { background-color: #ff5f57; border-color: #ff5f57; }
.window-frame:hover .window-dot:nth-child(2) { background-color: #febc2e; border-color: #febc2e; }
.window-frame:hover .window-dot:nth-child(3) { background-color: #28c840; border-color: #28c840; }

/* Links */
footer a, .footer-links a { transition: opacity 0.3s ease, border-bottom-color 0.3s ease; }

/* Buttons settle when pressed. */
.cta-btn:active, .buy-btn:active, .cta-ghost:active, .action-btn:active { transform: translateY(1px); }

/* ── Respect the OS setting ──
   Anyone who asked for less motion gets none of the above. */
@media (prefers-reduced-motion: reduce) {
    body::before, body::after { animation: none; }
    *, *::before, *::after {
        animation-duration: 0.001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: 0.001ms !important;
        scroll-behavior: auto !important;
    }
    .reveal { opacity: 1; transform: none; }
}

/* ── The header on a narrow screen ────────────────────────────────────────────
   Seven nav items, a logo, a theme toggle and a language switcher do not fit on
   a phone, and letting them wrap turns the header into three stacked rows that
   push the page content off the first screen.

   Progressive enhancement, deliberately: the button is created by site.js and it
   adds .has-burger to the header. Everything below is scoped to that class, so
   with scripting off nothing here applies and the nav keeps the wrapping layout
   it has always had — usable, just tall. A nav hidden by CSS that only a
   JavaScript-created button can reveal would be a nav that disappears entirely
   for anyone without it.

   These rules live here rather than in each page's own <style> because there are
   twelve pages plus the generated ones, and this file loads after all of them. */

.nav-toggle { display: none; }

/* Tighten the desktop row before hiding anything. Seven items at a 26px gap
   overflow around 1150px in English and sooner in Russian, where every label is
   longer; 16px buys back about 60px and postpones the collapse. */
@media (min-width: 1001px) {
    .main-nav { gap: 16px; }
}

@media (max-width: 1000px) {
    .nav-toggle {
        display: flex;
        flex-direction: column;
        justify-content: center;
        align-items: center;
        gap: 4px;
        width: 36px;
        height: 36px;
        padding: 0;
        background: none;
        border: 1px solid var(--border-color);
        color: var(--text-color);
        cursor: pointer;
        transition: background-color 0.3s ease, color 0.3s ease;
    }
    .nav-toggle:hover { background-color: var(--text-color); }
    .nav-toggle:hover span { background-color: var(--bg-color); }
    .nav-toggle:active { transform: translateY(1px); }

    .nav-toggle span {
        display: block;
        width: 18px;
        height: 2px;
        background-color: var(--text-color);
        transition: transform 0.3s ease, opacity 0.2s ease, background-color 0.3s ease;
    }

    /* Three bars fold into a cross. Safe to transform: they are bars, not text. */
    .nav-open .nav-toggle span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
    .nav-open .nav-toggle span:nth-child(2) { opacity: 0; }
    .nav-open .nav-toggle span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

    /* The panel is absolutely positioned rather than reflowing the header, so it
       does not depend on the per-page media queries at 860px — those set order
       and width on .main-nav and would fight this at every breakpoint between
       the two. The header is sticky, which makes it the containing block. */
    header.has-burger .main-nav {
        display: none;
        position: absolute;
        top: 100%;
        left: 0;
        right: 0;
        flex-direction: column;
        align-items: stretch;
        justify-content: flex-start;
        gap: 0;
        order: 0;
        width: auto;
        margin: 0;
        padding: 4px 20px 12px;
        border-top: 1px solid var(--border-color);
        border-bottom: 1px solid var(--border-color);

        /* Opaque, unlike the header itself. A translucent panel over page content
           leaves the labels sitting on whatever happens to scroll under them. */
        background-color: var(--bg-color);
    }

    header.has-burger.nav-open .main-nav { display: flex; }

    header.has-burger .main-nav a {
        padding: 13px 2px;
        font-size: 13px;
        letter-spacing: 1px;
        border-bottom: 1px solid var(--border-color);
        border-top: none;
        opacity: 0.85;
        transition: opacity 0.2s ease, padding-left 0.2s ease;
    }
    header.has-burger .main-nav a:last-child { border-bottom: none; }
    header.has-burger .main-nav a:hover,
    header.has-burger .main-nav a.active { opacity: 1; padding-left: 8px; }
    header.has-burger .main-nav a.active { font-weight: 900; }
}

/* Below the old breakpoint the header itself has to give up some padding, and
   the logo is the only thing wide enough to be worth shrinking. */
@media (max-width: 520px) {
    header { padding: 12px 16px; }
    .logo { font-size: 16px; letter-spacing: 0.5px; }
    .controls { gap: 10px; }
}
