/* ==========================================================================
   SnowServers "Aurora" design system
   ==========================================================================

   A full rewrite of the previous flat dark-glass sheet. The identity now rests
   on three things:

     1. A near-black base with an animated aurora behind everything, so large
        empty areas are never actually flat.
     2. One tri-colour spectrum (violet -> ice -> mint) used for every accent:
        headings, hairline borders, button fills, focus rings, chart strokes.
        Because it is a single gradient rather than three separate colours,
        elements far apart on a page still read as part of one system.
     3. Hairline gradient borders drawn with a mask, rather than solid 1px
        rules. This is what makes panels feel lit rather than outlined.

   Motion contract with assets/js/site.js. Do not rename these:
     [data-reveal] + [data-visible="true"]   scroll entrance, --reveal-delay
     [data-reveal="rise|left|right"]         entrance direction
     [data-spotlight]                        JS sets --mx / --my
     [data-tilt], [data-magnetic]            JS writes inline `transform`
                                             => never set transform on those
                                             elements here; animate a child or
                                             pseudo-element instead.

   Every animation is disabled under prefers-reduced-motion at the bottom.
   ========================================================================== */

/* ---------------------------------------------------------------- tokens -- */
:root {
    /* Surfaces. Near-black, cool-shifted, with a blue cast that deepens as
       surfaces recede rather than the usual lighten-as-you-elevate. */
    --bg: #04060d;
    --bg-elev: #070b16;
    --bg-elev-2: #0b1122;
    --bg-well: #02040a;

    /* The spectrum. Every accent on the site is a sample of this. */
    --violet: #7c6cff;
    --ice: #5bb8ff;
    --cyan: #3fe8c8;
    --frost: #b9e2ff;
    --ice-deep: #2f8fd8;

    --spectrum: linear-gradient(115deg, var(--violet), var(--ice) 48%, var(--cyan));
    --spectrum-soft: linear-gradient(
        115deg,
        rgba(124, 108, 255, 0.16),
        rgba(91, 184, 255, 0.16) 48%,
        rgba(63, 232, 200, 0.16)
    );

    /* Glass. Two weights; anything needing a third wants a different layout. */
    --glass: rgba(130, 175, 255, 0.045);
    --glass-strong: rgba(130, 175, 255, 0.085);

    /* Hairlines. --line is the solid fallback; the gradient borders run the
       spectrum through a mask. See the shared .card::before rule. */
    --line: rgba(140, 185, 255, 0.13);
    --line-bright: rgba(140, 185, 255, 0.28);

    /* Text */
    --text: #eef4ff;
    --muted: #9aaecb;
    --dim: #66788f;

    /* Status */
    --ok: #3fe8c8;
    --warn: #ffc46b;
    --down: #ff6b81;

    /* Shape */
    --r-sm: 10px;
    --r: 16px;
    --r-lg: 24px;
    --r-xl: 32px;
    --r-pill: 999px;

    /* Depth */
    --shadow-sm: 0 4px 16px -6px rgba(0, 0, 0, 0.6);
    --shadow: 0 24px 60px -18px rgba(0, 0, 0, 0.85);
    --shadow-lift: 0 36px 80px -24px rgba(0, 0, 0, 0.9);
    --glow-ice: 0 0 48px -10px rgba(91, 184, 255, 0.45);
    --glow-mint: 0 0 48px -10px rgba(63, 232, 200, 0.4);

    /* Rhythm */
    --container: 1200px;
    --gutter: clamp(1.1rem, 4vw, 2.2rem);
    --section-y: clamp(4.5rem, 10vw, 8rem);

    /* Type */
    --font-display: "Archivo", "Segoe UI", system-ui, sans-serif;
    --font-body: "Public Sans", "Segoe UI", system-ui, sans-serif;
    --font-mono: "JetBrains Mono", ui-monospace, "SF Mono", Menlo, monospace;

    /* Easing. --ease-out for entrances; --ease-spring for anything the user
       directly caused (hover, click) so it feels physical. */
    --ease-out: cubic-bezier(0.22, 1, 0.36, 1);
    --ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);
}

/* ------------------------------------------------------------ foundation -- */
*,
*::before,
*::after {
    box-sizing: border-box;
}

html {
    scroll-behavior: smooth;
    /* The sticky nav is 68px tall; without this, anchor jumps land underneath
       it. */
    scroll-padding-top: 92px;
    -webkit-text-size-adjust: 100%;
}

body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: var(--font-body);
    font-size: 1rem;
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
    overflow-x: hidden;
}

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

a {
    color: var(--ice);
    text-decoration: none;
    transition: color 0.18s var(--ease-out);
}

a:hover {
    color: var(--frost);
}

::selection {
    background: rgba(91, 184, 255, 0.28);
    color: #fff;
}

:focus-visible {
    outline: 2px solid var(--ice);
    outline-offset: 3px;
    border-radius: 4px;
}

* {
    scrollbar-color: rgba(140, 185, 255, 0.25) transparent;
    scrollbar-width: thin;
}

::-webkit-scrollbar {
    width: 10px;
    height: 10px;
}

::-webkit-scrollbar-track {
    background: var(--bg-well);
}

::-webkit-scrollbar-thumb {
    background: rgba(140, 185, 255, 0.2);
    border-radius: var(--r-pill);
    border: 2px solid var(--bg-well);
}

::-webkit-scrollbar-thumb:hover {
    background: rgba(140, 185, 255, 0.35);
}

/* ------------------------------------------------------------- utilities -- */
.container {
    width: 100%;
    max-width: var(--container);
    margin-inline: auto;
    padding-inline: var(--gutter);
}

.visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

.center {
    text-align: center;
}

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

.skip-link {
    position: absolute;
    top: -60px;
    left: 1rem;
    z-index: 200;
    padding: 0.7rem 1.2rem;
    border-radius: var(--r-sm);
    background: var(--bg-elev-2);
    color: var(--text);
    font-weight: 600;
    box-shadow: var(--shadow);
    transition: top 0.22s var(--ease-out);
}

.skip-link:focus {
    top: 1rem;
}

/* ------------------------------------------------------------ background -- */
/* Three drifting aurora blobs, fixed to the viewport so they do not move with
   scroll, so the page reads as sitting in front of a light source rather
   than on top of a pattern. blur() at this scale is expensive, which is why
   there are only three and why the blur radius itself is never animated. */
.aurora {
    position: fixed;
    inset: 0;
    z-index: -3;
    overflow: hidden;
    pointer-events: none;
}

.aurora i {
    position: absolute;
    display: block;
    border-radius: 50%;
    filter: blur(90px);
    opacity: 0.5;
    will-change: transform;
}

.aurora i:nth-child(1) {
    top: -22vh;
    left: -12vw;
    width: 62vw;
    height: 62vw;
    background: radial-gradient(circle, rgba(124, 108, 255, 0.5), transparent 68%);
    animation: drift-a 34s var(--ease-out) infinite alternate;
}

.aurora i:nth-child(2) {
    top: 8vh;
    right: -18vw;
    width: 58vw;
    height: 58vw;
    background: radial-gradient(circle, rgba(91, 184, 255, 0.45), transparent 68%);
    animation: drift-b 42s var(--ease-out) infinite alternate;
}

.aurora i:nth-child(3) {
    bottom: -28vh;
    left: 22vw;
    width: 66vw;
    height: 66vw;
    background: radial-gradient(circle, rgba(63, 232, 200, 0.3), transparent 70%);
    animation: drift-c 50s var(--ease-out) infinite alternate;
}

@keyframes drift-a {
    to {
        transform: translate3d(14vw, 10vh, 0) scale(1.18);
    }
}

@keyframes drift-b {
    to {
        transform: translate3d(-12vw, 16vh, 0) scale(0.86);
    }
}

@keyframes drift-c {
    to {
        transform: translate3d(10vw, -12vh, 0) scale(1.12);
    }
}

/* Film grain. Breaks up the banding large blurred gradients produce on 8-bit
   displays. Without it the aurora shows visible steps. */
.grain {
    position: fixed;
    inset: -50%;
    z-index: -2;
    pointer-events: none;
    opacity: 0.13;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.55'/%3E%3C/svg%3E");
    animation: grain-shift 6s steps(6) infinite;
}

@keyframes grain-shift {
    0% {
        transform: translate(0, 0);
    }
    20% {
        transform: translate(-3%, 2%);
    }
    40% {
        transform: translate(2%, -3%);
    }
    60% {
        transform: translate(-2%, -2%);
    }
    80% {
        transform: translate(3%, 1%);
    }
    100% {
        transform: translate(0, 0);
    }
}

#snow {
    position: fixed;
    inset: 0;
    z-index: -1;
    pointer-events: none;
}

/* ------------------------------------------------------------ typography -- */
h1,
h2,
h3,
h4 {
    font-family: var(--font-display);
    font-weight: 700;
    line-height: 1.08;
    letter-spacing: -0.035em;
    margin: 0 0 0.6rem;
    text-wrap: balance;
}

h1 {
    font-size: clamp(2.5rem, 6.2vw, 4.4rem);
    font-weight: 800;
    letter-spacing: -0.045em;
}

h2 {
    font-size: clamp(1.9rem, 4.2vw, 3rem);
}

h3 {
    font-size: clamp(1.1rem, 2vw, 1.3rem);
    letter-spacing: -0.02em;
}

h4 {
    font-size: 0.78rem;
    font-family: var(--font-mono);
    font-weight: 500;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--dim);
}

p {
    margin: 0 0 1rem;
}

/* Gradient headline fragment. The shimmer sweeps a brighter band across the
   text; background-size is oversized to give it somewhere to sweep to. */
.grad {
    background: linear-gradient(
        100deg,
        var(--violet) 0%,
        var(--ice) 30%,
        #ffffff 45%,
        var(--cyan) 62%,
        var(--ice) 100%
    );
    background-size: 220% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: shimmer 7s linear infinite;
}

@keyframes shimmer {
    to {
        background-position: -220% 0;
    }
}

/* Load-bearing claim. The pages carry a lot of plain-spoken prose on purpose,
   which is good for tone but leaves the one sentence that is actually the
   argument reading at the same weight as the sentence explaining it. This
   lifts it: full-strength text on a body-text background, over a spectrum
   hairline that echoes the gradient borders elsewhere.

   Deliberately quieter than .grad, which is a headline treatment and would
   fight the surrounding paragraph if used inline. Use .claim on at most one
   fragment per section, and only on claims that are true today — see the
   "Advertised but does not exist" list in HANDOFF.md. */
.claim {
    color: var(--text);
    font-weight: 600;
    background-image: var(--spectrum);
    background-repeat: no-repeat;
    background-position: 0 100%;
    background-size: 100% 2px;
    padding-bottom: 0.08em;
    /* Rounded ends on the underline, which a border-bottom cannot do. */
    border-radius: 1px;
}

.table-note {
    max-width: 78ch;
    margin: 1rem auto 0;
    font-size: 0.92rem;
}

.lede {
    font-size: clamp(1.02rem, 1.5vw, 1.18rem);
    color: var(--muted);
    line-height: 1.7;
    max-width: 62ch;
}

/* Mono uppercase kicker. It sits above every section heading and is the main
   thing tying section headers together across pages. */
.eyebrow {
    display: inline-block;
    margin-bottom: 0.9rem;
    font-family: var(--font-mono);
    font-size: 0.7rem;
    font-weight: 500;
    letter-spacing: 0.2em;
    text-transform: uppercase;
    color: var(--ice);
}

code {
    font-family: var(--font-mono);
    font-size: 0.88em;
    padding: 0.12em 0.42em;
    border-radius: 5px;
    background: var(--glass-strong);
    color: var(--frost);
}

/* ---------------------------------------------------------------- layout -- */
.section {
    position: relative;
    padding-block: var(--section-y);
}

.section--tight {
    padding-block: clamp(2.6rem, 6vw, 4rem);
}

.page-head {
    padding-top: clamp(3rem, 8vw, 5rem);
}

.section-head {
    display: grid;
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    gap: clamp(1.5rem, 4vw, 3.5rem);
    align-items: end;
}

.section-head h1,
.section-head h2 {
    margin-bottom: 0;
}

@media (max-width: 820px) {
    .section-head {
        grid-template-columns: 1fr;
        align-items: start;
    }
}

.grid {
    display: grid;
    gap: 1.15rem;
}

.grid--2 {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 300px), 1fr));
}

.grid--3 {
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 290px), 1fr));
}

.btn-row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.8rem;
    margin: 1.8rem 0 1.2rem;
}

/* Wave divider between sections. Restyled from a solid fill to a fading
   spectrum hairline so it separates without drawing a hard rule. */
.drift {
    position: relative;
    line-height: 0;
    color: var(--bg-elev);
    opacity: 0.9;
}

.drift svg {
    width: 100%;
    height: clamp(28px, 4vw, 46px);
}

.drift::after {
    content: "";
    position: absolute;
    inset-inline: 0;
    bottom: 0;
    height: 1px;
    background: var(--spectrum);
    opacity: 0.3;
}

/* --------------------------------------------------------------- reveals -- */
/* Entrance animation, driven by [data-visible] which site.js sets from an
   IntersectionObserver; --reveal-delay staggers siblings. */
[data-reveal] {
    opacity: 0;
    transform: translateY(22px);
    transition:
        opacity 0.7s var(--ease-out),
        transform 0.7s var(--ease-out);
    transition-delay: var(--reveal-delay, 0ms);
}

[data-reveal="rise"] {
    transform: translateY(38px) scale(0.97);
}

[data-reveal="left"] {
    transform: translateX(-28px);
}

[data-reveal="right"] {
    transform: translateX(28px);
}

[data-reveal][data-visible="true"] {
    opacity: 1;
    transform: none;
}

/* --------------------------------------------------------------- buttons -- */
.btn {
    position: relative;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    padding: 0.72rem 1.35rem;
    border: 0;
    border-radius: var(--r-pill);
    background: var(--glass-strong);
    color: var(--text);
    font-family: var(--font-body);
    font-size: 0.94rem;
    font-weight: 600;
    text-align: center;
    cursor: pointer;
    overflow: hidden;
    isolation: isolate;
    box-shadow: inset 0 0 0 1px var(--line);
    transition:
        box-shadow 0.25s var(--ease-out),
        background 0.25s var(--ease-out),
        color 0.25s var(--ease-out);
}

/* Sheen sweep on hover. It sits above the fill but below the label, which is
   why .btn is `isolation: isolate` and direct children get z-index 2. */
.btn::after {
    content: "";
    position: absolute;
    inset: 0;
    z-index: 1;
    background: linear-gradient(105deg, transparent 38%, rgba(255, 255, 255, 0.28) 50%, transparent 62%);
    transform: translateX(-120%);
    transition: transform 0.6s var(--ease-out);
    pointer-events: none;
}

.btn:hover::after {
    transform: translateX(120%);
}

.btn > * {
    position: relative;
    z-index: 2;
}

.btn:hover {
    background: rgba(130, 175, 255, 0.14);
    box-shadow: inset 0 0 0 1px var(--line-bright);
    color: #fff;
}

/* Magnetic buttons have their transform written inline by site.js, so the
   pressed state uses filter rather than scale, because a transform here would be
   overwritten on the next pointermove. */
.btn:active {
    filter: brightness(0.94);
}

.btn--primary {
    background: var(--spectrum);
    color: #04060d;
    font-weight: 700;
    box-shadow:
        inset 0 0 0 1px rgba(255, 255, 255, 0.18),
        var(--glow-ice);
}

.btn--primary:hover {
    background: var(--spectrum);
    color: #04060d;
    box-shadow:
        inset 0 0 0 1px rgba(255, 255, 255, 0.32),
        0 0 60px -8px rgba(91, 184, 255, 0.7);
}

.btn--ghost {
    background: transparent;
    box-shadow: inset 0 0 0 1px transparent;
    color: var(--muted);
}

.btn--ghost:hover {
    background: var(--glass);
    color: var(--text);
}

.btn--lg {
    padding: 0.95rem 1.9rem;
    font-size: 1rem;
}

.btn--block {
    display: flex;
    width: 100%;
}

/* ------------------------------------------------------------------ nav -- */
.nav {
    position: sticky;
    top: 0;
    z-index: 100;
    background: rgba(4, 6, 13, 0.72);
    backdrop-filter: blur(18px) saturate(150%);
    -webkit-backdrop-filter: blur(18px) saturate(150%);
}

.nav::after {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 1px;
    background: var(--spectrum);
    opacity: 0.35;
    pointer-events: none;
}

/* site.js flips data-stuck once the page has scrolled past 8px. The bar starts
   almost transparent over the hero and solidifies as content passes under it. */
.nav[data-stuck="true"] {
    background: rgba(4, 6, 13, 0.88);
    box-shadow: 0 12px 40px -20px rgba(0, 0, 0, 0.9);
}

.nav[data-stuck="true"]::after {
    opacity: 0.6;
}

.nav__inner {
    display: flex;
    align-items: center;
    gap: 1.2rem;
    height: 68px;
}

.nav__progress {
    position: absolute;
    inset: auto 0 0 0;
    height: 2px;
    z-index: 2;
    pointer-events: none;
}

.nav__progress i {
    display: block;
    height: 100%;
    background: var(--spectrum);
    transform: scaleX(0);
    transform-origin: 0 50%;
    box-shadow: 0 0 12px rgba(91, 184, 255, 0.8);
}

.brand {
    display: inline-flex;
    align-items: center;
    gap: 0.6rem;
    color: var(--text);
    font-family: var(--font-display);
    font-size: 1.12rem;
    font-weight: 800;
    letter-spacing: -0.03em;
    flex-shrink: 0;
}

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

/* The dendrite mark turns slowly and forever. It is the only piece of
   perpetual motion on the page, so it stays very slow. Much under 60s and it
   starts reading as a loading spinner. */
.brand__mark {
    width: 30px;
    height: 30px;
    color: var(--ice);
    filter: drop-shadow(0 0 10px rgba(91, 184, 255, 0.55));
    animation: spin-slow 60s linear infinite;
}

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

.brand__name b {
    font-weight: 800;
}

.brand__name span {
    font-weight: 600;
    color: var(--muted);
}

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

.nav__links {
    display: flex;
    align-items: center;
    gap: 0.25rem;
    margin-left: auto;
}

.nav__links > a {
    position: relative;
    padding: 0.5rem 0.85rem;
    border-radius: var(--r-pill);
    color: var(--muted);
    font-size: 0.92rem;
    font-weight: 500;
    white-space: nowrap;
}

/* Underline grows from the centre on hover, and stays put for the current
   page. */
.nav__links > a::after {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 0.28rem;
    width: 0;
    height: 2px;
    border-radius: 2px;
    background: var(--spectrum);
    transform: translateX(-50%);
    transition: width 0.28s var(--ease-out);
}

.nav__links > a:hover {
    color: var(--text);
}

.nav__links > a:hover::after,
.nav__links > a[aria-current="page"]::after {
    width: calc(100% - 1.7rem);
}

.nav__links > a[aria-current="page"] {
    color: var(--text);
}

.nav__cta {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    margin-left: 0.7rem;
}

.nav__toggle {
    display: none;
    margin-left: auto;
    padding: 0.5rem;
    border: 0;
    border-radius: var(--r-sm);
    background: var(--glass);
    color: var(--text);
    cursor: pointer;
    box-shadow: inset 0 0 0 1px var(--line);
}

.nav__toggle svg {
    width: 22px;
    height: 22px;
}

.nav__toggle .icon-close,
.nav__toggle[aria-expanded="true"] .icon-open {
    display: none;
}

.nav__toggle[aria-expanded="true"] .icon-close {
    display: block;
}

@media (max-width: 900px) {
    .nav__toggle {
        display: block;
    }

    .nav__links {
        position: absolute;
        inset: 68px 0 auto 0;
        flex-direction: column;
        align-items: stretch;
        gap: 0.15rem;
        margin: 0;
        padding: 1rem var(--gutter) 1.5rem;
        background: rgba(4, 6, 13, 0.97);
        backdrop-filter: blur(18px);
        -webkit-backdrop-filter: blur(18px);
        border-bottom: 1px solid var(--line);
        /* clip-path rather than display:none, so the panel can animate open
           and the links keep their place in the focus order. */
        clip-path: inset(0 0 100% 0);
        opacity: 0;
        transition:
            clip-path 0.36s var(--ease-out),
            opacity 0.24s var(--ease-out);
    }

    .nav__links[data-open="true"] {
        clip-path: inset(0 0 0 0);
        opacity: 1;
    }

    .nav__links > a {
        padding: 0.75rem 0.6rem;
        font-size: 1rem;
    }

    .nav__links > a::after {
        display: none;
    }

    .nav__cta {
        flex-direction: column;
        align-items: stretch;
        gap: 0.55rem;
        margin: 0.7rem 0 0;
    }
}

/* ----------------------------------------------------------------- cards -- */
.card {
    position: relative;
    padding: 1.6rem;
    border-radius: var(--r-lg);
    background: linear-gradient(160deg, var(--glass-strong), var(--glass));
    box-shadow: var(--shadow-sm);
    overflow: hidden;
    transition:
        box-shadow 0.35s var(--ease-out),
        background 0.35s var(--ease-out);
}

/* Hairline gradient border. Drawn as a masked pseudo-element rather than a
   real border so the spectrum can run through it and the radius still
   matches. */
.card::before {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 1px;
    background: var(--spectrum);
    opacity: 0.22;
    -webkit-mask:
        linear-gradient(#000 0 0) content-box,
        linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask:
        linear-gradient(#000 0 0) content-box,
        linear-gradient(#000 0 0);
    mask-composite: exclude;
    pointer-events: none;
    transition: opacity 0.3s var(--ease-out);
    z-index: 1;
}

.card:hover {
    box-shadow: var(--shadow);
}

.card:hover::before {
    opacity: 0.5;
}

.card > * {
    position: relative;
    z-index: 2;
}

.card h3 {
    margin-bottom: 0.5rem;
}

.card p:last-child {
    margin-bottom: 0;
}

/* Cursor spotlight. --mx/--my come from site.js; the 50% fallback keeps the
   highlight centred before the first pointer event and on touch, where the
   listener never fires. */
[data-spotlight]::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: radial-gradient(
        320px circle at var(--mx, 50%) var(--my, 50%),
        rgba(130, 190, 255, 0.13),
        transparent 62%
    );
    opacity: 0;
    transition: opacity 0.35s var(--ease-out);
    pointer-events: none;
    z-index: 1;
}

[data-spotlight]:hover::after {
    opacity: 1;
}

/* Frosted variant: brighter, for the one card in a group that matters most. */
.card--crystal {
    background: linear-gradient(160deg, rgba(130, 175, 255, 0.13), rgba(63, 232, 200, 0.05));
}

.card--crystal::before {
    opacity: 0.42;
}

.icon-tile {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin-bottom: 1rem;
    border-radius: 13px;
    background: var(--spectrum-soft);
    color: var(--ice);
    box-shadow: inset 0 0 0 1px var(--line);
    transition:
        color 0.3s var(--ease-out),
        transform 0.35s var(--ease-spring);
}

.icon-tile svg {
    width: 21px;
    height: 21px;
}

.card:hover .icon-tile {
    color: var(--cyan);
    transform: translateY(-2px) rotate(-4deg);
}

.feature h3 {
    font-size: 1.08rem;
}

.feature p {
    color: var(--muted);
    font-size: 0.94rem;
    margin-bottom: 0;
}

/* ----------------------------------------------------------------- hero -- */
.hero {
    position: relative;
    padding-block: clamp(3.5rem, 9vw, 7rem) clamp(3rem, 7vw, 5rem);
}

/* Perspective grid floor. Decorative only, and masked so it fades out well
   before it reaches the text. */
.hero::before {
    content: "";
    position: absolute;
    inset: auto 0 0 0;
    height: 46%;
    background-image:
        linear-gradient(rgba(91, 184, 255, 0.09) 1px, transparent 1px),
        linear-gradient(90deg, rgba(91, 184, 255, 0.09) 1px, transparent 1px);
    background-size: 62px 62px;
    transform: perspective(340px) rotateX(58deg);
    transform-origin: 50% 100%;
    -webkit-mask-image: linear-gradient(transparent, #000 55%);
    mask-image: linear-gradient(transparent, #000 55%);
    pointer-events: none;
    z-index: -1;
}

.hero__grid {
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    gap: clamp(2rem, 5vw, 4rem);
    align-items: center;
}

@media (max-width: 980px) {
    .hero__grid {
        grid-template-columns: 1fr;
    }
}

.hero__sub {
    font-size: clamp(1.02rem, 1.5vw, 1.15rem);
    color: var(--muted);
    line-height: 1.72;
    max-width: 56ch;
    margin-top: 1.3rem;
}

.hero__note {
    font-size: 0.86rem;
    color: var(--dim);
    margin: 0;
}

.hero__note strong {
    color: var(--muted);
    font-weight: 600;
}

/* --------------------------------------------------------------- console -- */
/* The hero's illustrative server console. data-tilt writes an inline transform
   on .console, so nothing here may set transform on it. */
.console {
    position: relative;
    border-radius: var(--r-lg);
    background: linear-gradient(165deg, rgba(11, 17, 34, 0.96), rgba(2, 4, 10, 0.96));
    box-shadow:
        var(--shadow-lift),
        inset 0 0 0 1px var(--line);
    overflow: hidden;
    transition: transform 0.35s var(--ease-out);
    transform-style: preserve-3d;
}

.console::before {
    content: "";
    position: absolute;
    inset-inline: 0;
    top: 0;
    height: 1px;
    background: var(--spectrum);
    opacity: 0.7;
    z-index: 2;
}

.console__bar {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.7rem 1rem;
    border-bottom: 1px solid var(--line);
    background: rgba(130, 175, 255, 0.04);
}

.console__dots {
    display: inline-flex;
    gap: 0.34rem;
}

.console__dots i {
    width: 9px;
    height: 9px;
    border-radius: 50%;
    background: rgba(140, 185, 255, 0.22);
}

.console__title {
    font-family: var(--font-mono);
    font-size: 0.76rem;
    color: var(--dim);
}

.console__pill {
    margin-left: auto;
    padding: 0.16rem 0.6rem;
    border-radius: var(--r-pill);
    background: rgba(63, 232, 200, 0.14);
    color: var(--cyan);
    font-family: var(--font-mono);
    font-size: 0.68rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    box-shadow: inset 0 0 0 1px rgba(63, 232, 200, 0.3);
}

.console__body {
    margin: 0;
    padding: 1rem;
    /* Fixed height so the typing animation never reflows the page as lines
       arrive. A growing box would shift everything below it on every tick. */
    height: 210px;
    overflow: hidden;
    font-family: var(--font-mono);
    font-size: 0.76rem;
    line-height: 1.85;
    color: var(--muted);
    white-space: pre-wrap;
    word-break: break-word;
}

.console__body b {
    color: var(--cyan);
    font-weight: 500;
}

.console__body i {
    color: var(--ice);
    font-style: normal;
}

.console__metrics {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1px;
    margin: 0;
    background: var(--line);
    border-top: 1px solid var(--line);
}

.console__metrics > div {
    padding: 0.75rem 1rem;
    background: rgba(4, 6, 13, 0.9);
}

.console__metrics dt {
    font-family: var(--font-mono);
    font-size: 0.64rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--dim);
}

.console__metrics dd {
    margin: 0.15rem 0 0;
    font-family: var(--font-mono);
    font-size: 0.95rem;
    color: var(--text);
}

/* ---------------------------------------------------- tick budget widget -- */
/* Sits inside the hero console. Two bars, same workload, different machine.

   site.js sets data-tick-state on each ROW rather than on the widget, so the
   two bars colour independently: our processor can be comfortable while the
   slower one is already over the line, which is the whole point of drawing
   them together. */
.tickw {
    padding: 0.95rem 1rem 1.1rem;
    border-top: 1px solid var(--line);
}

.tickw__title {
    margin: 0 0 0.75rem;
    font-family: var(--font-mono);
    font-size: 0.68rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--dim);
}

.tickw__row {
    --tickw-accent: var(--cyan);
    display: grid;
    grid-template-columns: 8.5rem 1fr 3.6rem;
    align-items: center;
    gap: 0.7rem;
    margin-bottom: 0.5rem;
}

.tickw__row[data-tick-state='tight'] {
    --tickw-accent: var(--warn);
}

.tickw__row[data-tick-state='over'] {
    --tickw-accent: var(--down);
}

.tickw__rowlabel {
    font-size: 0.76rem;
    color: var(--muted);
    line-height: 1.3;
}

.tickw__track {
    position: relative;
    display: block;
    height: 10px;
    border-radius: var(--r-pill);
    background: rgba(130, 175, 255, 0.08);
    overflow: hidden;
}

.tickw__fill {
    display: block;
    height: 100%;
    width: 0;
    border-radius: var(--r-pill);
    background: var(--tickw-accent);
    box-shadow: 0 0 16px -4px var(--tickw-accent);
    transition:
        width 0.25s var(--ease-out),
        background 0.25s var(--ease-out);
}

.tickw__rowms {
    font-family: var(--font-mono);
    font-size: 0.78rem;
    text-align: right;
    color: var(--tickw-accent);
    font-variant-numeric: tabular-nums;
    transition: color 0.25s var(--ease-out);
}

/* The scale sits under the tracks, so its left gutter has to match the row's
   label column or the "50 ms budget" mark drifts away from the bar ends. */
.tickw__scale {
    display: flex;
    justify-content: space-between;
    margin: 0.15rem 0 0;
    padding-left: 9.2rem;
    padding-right: 4.3rem;
    font-family: var(--font-mono);
    font-size: 0.6rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--dim);
}

.tickw__controls {
    display: grid;
    gap: 0.6rem;
    margin-top: 1rem;
}

.tickw__ctl {
    display: block;
    cursor: pointer;
}

.tickw__label {
    display: flex;
    justify-content: space-between;
    font-family: var(--font-mono);
    font-size: 0.68rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--muted);
}

.tickw__label b {
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

/* Range inputs need the vendor pseudo-elements written out separately: a
   browser drops the whole selector list if it does not recognise one of them,
   so these cannot be combined. */
.tickw__ctl input[type='range'] {
    -webkit-appearance: none;
    appearance: none;
    width: 100%;
    height: 22px;
    margin-top: 0.2rem;
    background: transparent;
    cursor: grab;
}

.tickw__ctl input[type='range']:active {
    cursor: grabbing;
}

.tickw__ctl input[type='range']::-webkit-slider-runnable-track {
    height: 4px;
    border-radius: var(--r-pill);
    background: rgba(130, 175, 255, 0.16);
}

.tickw__ctl input[type='range']::-moz-range-track {
    height: 4px;
    border-radius: var(--r-pill);
    background: rgba(130, 175, 255, 0.16);
}

.tickw__ctl input[type='range']::-webkit-slider-thumb {
    -webkit-appearance: none;
    appearance: none;
    width: 16px;
    height: 16px;
    margin-top: -6px;
    border: 0;
    border-radius: 50%;
    background: var(--ice);
    box-shadow: 0 0 14px -2px var(--ice);
    transition: transform 0.18s var(--ease-spring);
}

.tickw__ctl input[type='range']::-moz-range-thumb {
    width: 16px;
    height: 16px;
    border: 0;
    border-radius: 50%;
    background: var(--ice);
    box-shadow: 0 0 14px -2px var(--ice);
}

.tickw__ctl input[type='range']:hover::-webkit-slider-thumb {
    transform: scale(1.15);
}

.tickw__ctl input[type='range']:focus-visible {
    outline: 0;
}

.tickw__ctl input[type='range']:focus-visible::-webkit-slider-thumb {
    box-shadow: 0 0 0 4px rgba(91, 184, 255, 0.3);
}

.tickw__ctl input[type='range']:focus-visible::-moz-range-thumb {
    box-shadow: 0 0 0 4px rgba(91, 184, 255, 0.3);
}

/* The recommendation. This is the widget's actual call to action, so it gets a
   panel of its own rather than sitting as another line of text. */
.tickw__rec {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: 0.7rem;
    margin-top: 1rem;
    padding: 0.75rem 0.9rem;
    border-radius: var(--r);
    background: var(--spectrum-soft);
    box-shadow: inset 0 0 0 1px var(--line);
}

.tickw__reclabel {
    font-family: var(--font-mono);
    font-size: 0.62rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--dim);
}

.tickw__rec strong {
    margin-left: 0.35rem;
    font-family: var(--font-display);
    font-size: 1.05rem;
    letter-spacing: -0.02em;
    color: var(--text);
}

.tickw__recwhy {
    display: block;
    margin-top: 0.1rem;
    font-size: 0.74rem;
    color: var(--muted);
}

.tickw__recbtn {
    padding: 0.5rem 1rem;
    font-size: 0.84rem;
    white-space: nowrap;
}

.tickw__note {
    margin: 0.7rem 0 0;
    font-size: 0.68rem;
    line-height: 1.5;
    color: var(--dim);
}

@media (max-width: 480px) {
    /* The label column stops fitting; stack the bar under its name and drop
       the scale, which has nothing left to line up with. */
    .tickw__row {
        grid-template-columns: 1fr 3.6rem;
    }

    .tickw__rowlabel {
        grid-column: 1 / -1;
        margin-bottom: -0.2rem;
    }

    .tickw__scale {
        display: none;
    }
}
/* ----------------------------------------------------------------- stats -- */
/* 1px grid gap over a --line background is what draws the dividers, so the
   cells need no borders of their own. */
.stats {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 190px), 1fr));
    gap: 1px;
    border-radius: var(--r-lg);
    background: var(--line);
    overflow: hidden;
    box-shadow: inset 0 0 0 1px var(--line);
}

.stat {
    padding: 1.5rem 1.2rem;
    background: rgba(7, 11, 22, 0.86);
    text-align: center;
    transition: background 0.3s var(--ease-out);
}

.stat:hover {
    background: rgba(11, 17, 34, 0.95);
}

.stat__num {
    font-family: var(--font-display);
    font-size: clamp(1.7rem, 3.4vw, 2.4rem);
    font-weight: 800;
    letter-spacing: -0.04em;
    background: var(--spectrum);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    /* Tabular figures stop the count-up animation jittering the width. */
    font-variant-numeric: tabular-nums;
}

.stat__label {
    margin-top: 0.35rem;
    font-size: 0.82rem;
    color: var(--muted);
}

/* ----------------------------------------------------------------- plans -- */
.plans {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 260px), 1fr));
    gap: 1.15rem;
    align-items: start;
}


/* ---- product picker (homepage "what do you want to host?") ---- */
.pick {
    display: flex;
    flex-direction: column;
    padding: 1.7rem 1.5rem;
    position: relative;
}

.pick__badge {
    position: absolute;
    top: 1rem;
    right: 1rem;
    font-size: .72rem;
    font-weight: 600;
    letter-spacing: .02em;
    padding: .22rem .6rem;
    border-radius: 999px;
    border: 1px solid currentColor;
}

.pick__badge--ok {
    color: #6ee7b7;
}

.pick__badge--warn {
    color: #fcd34d;
}

.pick__badge--muted {
    color: var(--muted, #8b93a7);
    opacity: .8;
}

.pick__name {
    margin: .9rem 0 .2rem;
}

.pick__tagline {
    color: var(--muted, #8b93a7);
    font-size: .92rem;
    margin: 0 0 .7rem;
}

.pick__blurb {
    margin: 0 0 1.3rem;
}

.pick .btn {
    margin-top: auto;
}

/* Matches the height a button would occupy so the three cards stay aligned. */
.pick__none {
    margin-top: auto;
    text-align: center;
    color: var(--muted, #8b93a7);
    font-size: .88rem;
    padding: .72rem 0;
}

.plan {
    display: flex;
    flex-direction: column;
    /* data-tilt writes an inline transform here too, so none in CSS. */
    padding: 1.7rem 1.5rem;
}

.plan .btn {
    margin-top: auto;
}

/* The featured plan gets a slowly rotating conic border. Rotating a
   pseudo-element and clipping it with overflow:hidden is far cheaper than
   animating a gradient's angle, which cannot be composited. */
.plan--featured {
    background: linear-gradient(160deg, rgba(124, 108, 255, 0.14), rgba(63, 232, 200, 0.06));
}

.plan--featured::before {
    opacity: 0;
}

.plan--featured::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    padding: 1px;
    background: conic-gradient(
        from 0deg,
        transparent 0deg,
        var(--violet) 60deg,
        var(--ice) 130deg,
        var(--cyan) 200deg,
        transparent 260deg,
        transparent 360deg
    );
    -webkit-mask:
        linear-gradient(#000 0 0) content-box,
        linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask:
        linear-gradient(#000 0 0) content-box,
        linear-gradient(#000 0 0);
    mask-composite: exclude;
    animation: border-run 6s linear infinite;
    pointer-events: none;
    z-index: 1;
}

@keyframes border-run {
    to {
        rotate: 360deg;
    }
}

.plan__tag {
    position: absolute;
    top: 1rem;
    right: 1rem;
    z-index: 3;
    padding: 0.22rem 0.7rem;
    border-radius: var(--r-pill);
    background: var(--spectrum);
    color: #04060d;
    font-family: var(--font-mono);
    font-size: 0.62rem;
    font-weight: 500;
    letter-spacing: 0.1em;
    text-transform: uppercase;
}

.plan__name {
    font-size: 1.35rem;
    margin-bottom: 0.35rem;
}

.plan__blurb {
    font-size: 0.9rem;
    color: var(--muted);
    /* Reserved height keeps the price lines level across cards whose blurbs
       wrap to different numbers of lines. */
    min-height: 3.2em;
}

.plan__price {
    display: flex;
    align-items: baseline;
    gap: 0.3rem;
    margin: 0.6rem 0 0.2rem;
}

.plan__amount {
    font-family: var(--font-display);
    font-size: 2.7rem;
    font-weight: 800;
    letter-spacing: -0.045em;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.plan__per {
    font-size: 0.9rem;
    color: var(--dim);
}

.plan__annual {
    font-size: 0.78rem;
    color: var(--dim);
    margin-bottom: 1.2rem;
}

.plan__specs {
    list-style: none;
    margin: 0 0 1.5rem;
    padding: 0;
    display: grid;
    gap: 0.62rem;
}

.plan__specs li,
.checklist li {
    display: flex;
    align-items: flex-start;
    gap: 0.62rem;
    font-size: 0.9rem;
    color: var(--muted);
}

.plan__specs svg,
.checklist svg {
    flex: 0 0 auto;
    width: 15px;
    height: 15px;
    margin-top: 0.32em;
    color: var(--cyan);
}

.plan__specs strong,
.checklist strong {
    color: var(--text);
    font-weight: 600;
}

.plan__foot {
    margin: 0.7rem 0 0;
    font-size: 0.74rem;
    color: var(--dim);
    text-align: center;
}

.checklist {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0.62rem;
}

.billing-toggle {
    display: inline-flex;
    gap: 0.25rem;
    padding: 0.3rem;
    border-radius: var(--r-pill);
    background: var(--glass);
    box-shadow: inset 0 0 0 1px var(--line);
}

.billing-toggle button {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 1.1rem;
    border: 0;
    border-radius: var(--r-pill);
    background: transparent;
    color: var(--muted);
    font-family: inherit;
    font-size: 0.9rem;
    font-weight: 600;
    cursor: pointer;
    transition:
        background 0.28s var(--ease-out),
        color 0.28s var(--ease-out);
}

.billing-toggle button[aria-pressed="true"] {
    background: var(--spectrum);
    color: #04060d;
    box-shadow: var(--glow-ice);
}

.save-badge {
    padding: 0.1rem 0.45rem;
    border-radius: var(--r-pill);
    background: rgba(63, 232, 200, 0.18);
    color: var(--cyan);
    font-family: var(--font-mono);
    font-size: 0.62rem;
    letter-spacing: 0.05em;
}

.billing-toggle button[aria-pressed="true"] .save-badge {
    background: rgba(4, 6, 13, 0.25);
    color: #04060d;
}

/* ----------------------------------------------------------------- table -- */
.table-wrap {
    /* Wide tables scroll inside this box; the page body must never scroll
       sideways. */
    overflow-x: auto;
    border-radius: var(--r-lg);
    box-shadow: inset 0 0 0 1px var(--line);
    background: rgba(7, 11, 22, 0.7);
}

table {
    width: 100%;
    min-width: 640px;
    border-collapse: collapse;
    font-size: 0.9rem;
}

th,
td {
    padding: 0.85rem 1rem;
    text-align: left;
    border-bottom: 1px solid var(--line);
}

thead th {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    font-weight: 500;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--ice);
    background: rgba(130, 175, 255, 0.05);
    white-space: nowrap;
}

tbody th {
    color: var(--text);
    font-weight: 600;
    white-space: nowrap;
}

tbody td {
    color: var(--muted);
}

tbody tr {
    transition: background 0.22s var(--ease-out);
}

tbody tr:hover {
    background: rgba(130, 175, 255, 0.045);
}

tbody tr:last-child th,
tbody tr:last-child td {
    border-bottom: 0;
}

/* ----------------------------------------------------------------- steps -- */
.steps {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 270px), 1fr));
    gap: 1.15rem;
    counter-reset: step;
}

.step {
    display: flex;
    gap: 1rem;
    align-items: flex-start;
}

/* The number comes from a CSS counter, so reordering or inserting a step in
   build.py renumbers everything automatically. */
.step__num {
    counter-increment: step;
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 38px;
    height: 38px;
    border-radius: 12px;
    background: var(--spectrum-soft);
    box-shadow: inset 0 0 0 1px var(--line);
    font-family: var(--font-mono);
    font-size: 0.85rem;
    color: var(--ice);
    transition: transform 0.35s var(--ease-spring);
}

.step__num::before {
    content: counter(step, decimal-leading-zero);
}

.step:hover .step__num {
    transform: scale(1.08) rotate(-5deg);
}

.step h3 {
    font-size: 1.02rem;
}

.step p {
    font-size: 0.92rem;
    color: var(--muted);
    margin-bottom: 0;
}

.step__meta {
    display: inline-block;
    margin-top: 0.6rem;
    padding: 0.14rem 0.55rem;
    border-radius: var(--r-pill);
    background: rgba(63, 232, 200, 0.12);
    color: var(--cyan);
    font-family: var(--font-mono);
    font-size: 0.66rem;
    letter-spacing: 0.06em;
}

/* ------------------------------------------------------------ spec sheet -- */
.specsheet {
    border-radius: var(--r-lg);
    overflow: hidden;
    box-shadow: inset 0 0 0 1px var(--line);
    background: rgba(7, 11, 22, 0.7);
}

.specsheet__row {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem 1.5rem;
    padding: 0.95rem 1.2rem;
    border-bottom: 1px solid var(--line);
    transition: background 0.22s var(--ease-out);
}

.specsheet__row:last-child {
    border-bottom: 0;
}

.specsheet__row:hover {
    background: rgba(130, 175, 255, 0.045);
}

.specsheet__row span {
    flex: 0 0 190px;
    font-family: var(--font-mono);
    font-size: 0.72rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--dim);
    padding-top: 0.2em;
}

.specsheet__row strong {
    flex: 1 1 300px;
    font-weight: 500;
    color: var(--text);
}

/* -------------------------------------------------------- tick explainer -- */
.tickbox {
    padding: clamp(1.4rem, 3vw, 2.2rem);
    border-radius: var(--r-xl);
    background: linear-gradient(160deg, rgba(130, 175, 255, 0.07), rgba(4, 6, 13, 0.5));
    box-shadow: inset 0 0 0 1px var(--line);
}

.tickbox__head p {
    max-width: 68ch;
}

.tickbar {
    margin-top: 1.6rem;
    padding: 1.2rem;
    border-radius: var(--r);
    background: rgba(2, 4, 10, 0.6);
    box-shadow: inset 0 0 0 1px var(--line);
}

.tickbar__row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.7rem 1rem;
    margin-bottom: 0.9rem;
}

.tickbar__label {
    flex: 0 0 150px;
    font-family: var(--font-mono);
    font-size: 0.74rem;
    color: var(--muted);
}

.tickbar__track {
    position: relative;
    flex: 1 1 240px;
    height: 30px;
    border-radius: var(--r-sm);
    background: rgba(130, 175, 255, 0.06);
    overflow: hidden;
}

/* --w is set inline per row. Scaling from 0 means the bar draws itself rather
   than appearing pre-filled. */
.tickbar__fill {
    display: block;
    height: 100%;
    width: var(--w, 50%);
    border-radius: var(--r-sm);
    transform-origin: 0 50%;
    animation: bar-grow 1.1s var(--ease-out) both;
}

@keyframes bar-grow {
    from {
        transform: scaleX(0);
    }
    to {
        transform: scaleX(1);
    }
}

.tickbar__fill--good {
    background: linear-gradient(90deg, rgba(63, 232, 200, 0.35), var(--cyan));
    box-shadow: var(--glow-mint);
}

.tickbar__fill--bad {
    background: linear-gradient(90deg, rgba(255, 107, 129, 0.3), var(--down));
    box-shadow: 0 0 40px -10px rgba(255, 107, 129, 0.6);
}

.tickbar__cap {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    padding-inline: 0.7rem;
    font-family: var(--font-mono);
    font-size: 0.68rem;
    color: rgba(4, 6, 13, 0.85);
    font-weight: 500;
}

.tickbar__budget {
    display: flex;
    justify-content: space-between;
    font-family: var(--font-mono);
    font-size: 0.66rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--dim);
    padding-top: 0.4rem;
    border-top: 1px dashed var(--line);
}

/* ------------------------------------------------------------------ cta -- */
.cta {
    position: relative;
    padding: clamp(2.2rem, 5vw, 3.6rem);
    border-radius: var(--r-xl);
    background:
        radial-gradient(120% 140% at 50% 0%, rgba(124, 108, 255, 0.22), transparent 60%),
        linear-gradient(160deg, rgba(130, 175, 255, 0.1), rgba(4, 6, 13, 0.65));
    box-shadow:
        inset 0 0 0 1px var(--line-bright),
        var(--shadow);
    text-align: center;
    overflow: hidden;
}

/* Slow sheen, so the largest flat panel on any page is never quite static. */
.cta::after {
    content: "";
    position: absolute;
    inset: -50%;
    background: linear-gradient(115deg, transparent 42%, rgba(255, 255, 255, 0.05) 50%, transparent 58%);
    animation: cta-sheen 9s linear infinite;
    pointer-events: none;
}

@keyframes cta-sheen {
    from {
        transform: translateX(-40%);
    }
    to {
        transform: translateX(40%);
    }
}

.cta > * {
    position: relative;
    z-index: 1;
}

.cta h2 {
    margin-bottom: 0.7rem;
}

.cta p {
    color: var(--muted);
    max-width: 56ch;
    margin-inline: auto;
}

.cta .btn-row {
    justify-content: center;
    margin-bottom: 0;
}

/* --------------------------------------------------------------- notices -- */
.notice {
    display: flex;
    gap: 0.85rem;
    padding: 1rem 1.15rem;
    border-radius: var(--r);
    background: rgba(255, 196, 107, 0.07);
    box-shadow: inset 0 0 0 1px rgba(255, 196, 107, 0.24);
    font-size: 0.9rem;
    color: var(--muted);
}

.notice svg {
    flex: 0 0 auto;
    width: 19px;
    height: 19px;
    margin-top: 0.15em;
    color: var(--warn);
}

.notice p:last-child {
    margin-bottom: 0;
}

.notice strong {
    color: var(--text);
}

/* ------------------------------------------------------------------ faq -- */
.faq {
    display: grid;
    gap: 0.7rem;
}

details {
    border-radius: var(--r);
    background: var(--glass);
    box-shadow: inset 0 0 0 1px var(--line);
    overflow: hidden;
    transition: box-shadow 0.28s var(--ease-out);
}

details[open] {
    box-shadow: inset 0 0 0 1px var(--line-bright);
    background: var(--glass-strong);
}

summary {
    position: relative;
    padding: 1rem 3rem 1rem 1.15rem;
    font-family: var(--font-display);
    font-size: 1rem;
    font-weight: 600;
    letter-spacing: -0.015em;
    cursor: pointer;
    list-style: none;
    transition: color 0.2s var(--ease-out);
}

summary::-webkit-details-marker {
    display: none;
}

summary:hover {
    color: var(--frost);
}

/* Chevron drawn from two borders, so it needs no extra markup or SVG. */
summary::after {
    content: "";
    position: absolute;
    top: 1.35rem;
    right: 1.25rem;
    width: 8px;
    height: 8px;
    border-right: 2px solid var(--ice);
    border-bottom: 2px solid var(--ice);
    transform: rotate(45deg);
    transition: transform 0.3s var(--ease-out);
}

details[open] summary::after {
    transform: rotate(-135deg);
}

/* Permalink to a single answer. Sits left of the chevron, hidden until the
   question is hovered or the link itself is focused -- it is a tool for the
   person sending a link, not something every reader needs to look at. It stays
   reachable by keyboard: :focus-visible brings it back regardless of hover. */
.faq__link {
    position: absolute;
    top: 1.15rem;
    right: 2.6rem;
    color: var(--ice);
    text-decoration: none;
    font-weight: 700;
    opacity: 0;
    transition: opacity 0.2s var(--ease-out);
}

summary:hover .faq__link,
.faq__link:focus-visible {
    opacity: 0.7;
}

.faq__link:hover {
    opacity: 1;
}

/* Landing on a linked answer: mark it so the eye finds it after the jump. */
details:target {
    border-color: var(--frost);
}

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

.faq__body {
    padding: 0 1.15rem 1.15rem;
    color: var(--muted);
    font-size: 0.94rem;
    animation: fade-down 0.35s var(--ease-out);
}

.faq__body p:last-child {
    margin-bottom: 0;
}

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

/* ---------------------------------------------------------------- status -- */
.status-banner {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    padding: 1.1rem 1.3rem;
    border-radius: var(--r);
    background: var(--glass);
    box-shadow: inset 0 0 0 1px var(--line);
}

.status-banner__dot {
    flex: 0 0 auto;
    width: 11px;
    height: 11px;
    border-radius: 50%;
    background: var(--ok);
}

/* The idle dot reads as "nothing is being measured" rather than "all clear". */
.status-banner__dot--idle {
    background: var(--dim);
}

.svc {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.6rem 1rem;
    padding: 1.1rem 1.3rem;
    border-bottom: 1px solid var(--line);
    transition: background 0.22s var(--ease-out);
}

.svc:last-child {
    border-bottom: 0;
}

.svc:hover {
    background: rgba(130, 175, 255, 0.04);
}

.svc__name {
    font-weight: 600;
}

.svc__desc {
    font-size: 0.84rem;
    color: var(--dim);
}

.pill {
    padding: 0.22rem 0.7rem;
    border-radius: var(--r-pill);
    font-family: var(--font-mono);
    font-size: 0.68rem;
    letter-spacing: 0.05em;
    background: rgba(63, 232, 200, 0.14);
    color: var(--cyan);
    box-shadow: inset 0 0 0 1px rgba(63, 232, 200, 0.28);
    white-space: nowrap;
}

.pill--idle {
    background: var(--glass);
    color: var(--dim);
    box-shadow: inset 0 0 0 1px var(--line);
}

/* 90-day uptime strip. A repeating gradient draws the ticks, so there is no
   markup for 90 individual bars. */
.uptime {
    flex: 1 1 100%;
    height: 26px;
    border-radius: 5px;
    background: repeating-linear-gradient(
        90deg,
        rgba(130, 175, 255, 0.09) 0,
        rgba(130, 175, 255, 0.09) 4px,
        transparent 4px,
        transparent 7px
    );
}

.uptime-legend {
    flex: 1 1 100%;
    display: flex;
    justify-content: space-between;
    font-family: var(--font-mono);
    font-size: 0.64rem;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--dim);
}

.empty-state {
    text-align: center;
    padding-block: 2.6rem;
}

.empty-state .icon-tile {
    margin-inline: auto;
}

.empty-state p {
    max-width: 52ch;
    margin-inline: auto;
    margin-bottom: 0;
}

/* ----------------------------------------------------------------- forms -- */
.form {
    display: grid;
    gap: 1rem;
}

.form__row {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(min(100%, 220px), 1fr));
    gap: 1rem;
}

.field {
    display: grid;
    gap: 0.35rem;
}

/* Two fields that belong together on one line, stacking on narrow screens.
   Used for "server type + version", which read as one choice. */
.field-row {
    display: grid;
    gap: 1rem;
    grid-template-columns: 1fr;
}

@media (min-width: 620px) {
    .field-row {
        grid-template-columns: 1fr 1fr;
    }
}

.field__hint {
    font-size: 0.78rem;
    color: var(--dim);
}

.field label {
    font-family: var(--font-mono);
    font-size: 0.7rem;
    letter-spacing: 0.13em;
    text-transform: uppercase;
    color: var(--dim);
}

.field input,
.field select,
.field textarea {
    width: 100%;
    padding: 0.75rem 0.9rem;
    border: 0;
    border-radius: var(--r-sm);
    background: rgba(2, 4, 10, 0.6);
    color: var(--text);
    font-family: inherit;
    font-size: 0.95rem;
    box-shadow: inset 0 0 0 1px var(--line);
    transition:
        box-shadow 0.22s var(--ease-out),
        background 0.22s var(--ease-out);
}

.field textarea {
    min-height: 130px;
    resize: vertical;
}

.field input:focus,
.field select:focus,
.field textarea:focus {
    outline: 0;
    background: rgba(2, 4, 10, 0.85);
    box-shadow:
        inset 0 0 0 1px var(--ice),
        0 0 0 3px rgba(91, 184, 255, 0.18);
}

.field input::placeholder,
.field textarea::placeholder {
    color: var(--dim);
}

.field__error {
    font-size: 0.78rem;
    color: var(--down);
    min-height: 1em;
}

/* Shake on an invalid submit. The error text alone is easy to miss. */
.field[data-invalid="true"] input,
.field[data-invalid="true"] textarea,
.field[data-invalid="true"] select {
    box-shadow: inset 0 0 0 1px var(--down);
    animation: shake 0.35s var(--ease-out);
}

@keyframes shake {
    0%,
    100% {
        transform: translateX(0);
    }
    25% {
        transform: translateX(-5px);
    }
    75% {
        transform: translateX(5px);
    }
}

.hint,
.form-note {
    font-size: 0.8rem;
    color: var(--dim);
}

/* Honeypot. Must be invisible without being display:none or hidden — some bots
   skip fields that are obviously non-rendering, and this catches more of them
   if it looks like a real input in the DOM. Pulled out of flow and off-screen,
   and aria-hidden + tabindex="-1" in the markup keep it away from keyboard and
   screen-reader users. */
.hp {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* Hidden until the submit succeeds; site.js sets data-show. Kept
   in the DOM rather than injected so the role="status" live region already
   exists when the message lands in it. */
.form-success {
    display: none;
    padding: 1.1rem 1.3rem;
    border-radius: var(--r);
    background: rgba(63, 232, 200, 0.09);
    box-shadow: inset 0 0 0 1px rgba(63, 232, 200, 0.3);
    color: var(--muted);
}

.form-success[data-show="true"] {
    display: block;
    animation: toast-in 0.45s var(--ease-spring);
}

.form-success strong {
    color: var(--text);
}

/* The success heading is focused programmatically; it is not interactive, so
   suppress the ring but keep the focus itself for screen readers. */
.form-success [data-focus] {
    outline: 0;
}

/* Delivery failure. Deliberately styled as an error and not a muted footnote:
   the visitor's message was NOT sent and the alternative routes below it are
   the only thing standing between them and a lost enquiry. role="alert" in the
   markup so it is announced immediately rather than politely queued. */
.form-failure {
    display: none;
    margin-top: 1.1rem;
    padding: 1.1rem 1.3rem;
    border-radius: var(--r);
    background: rgba(255, 107, 107, 0.09);
    box-shadow: inset 0 0 0 1px rgba(255, 107, 107, 0.32);
    color: var(--muted);
}

.form-failure[data-show="true"] {
    display: block;
    animation: toast-in 0.45s var(--ease-spring);
}

.form-failure strong {
    color: var(--text);
}

.form-failure a {
    color: var(--text);
    text-decoration: underline;
}

.form-failure [data-focus] {
    outline: 0;
}

/* ------------------------------------------------------------------ auth -- */
.auth {
    display: grid;
    grid-template-columns: minmax(0, 1.1fr) minmax(0, 0.9fr);
    gap: clamp(1.5rem, 4vw, 3rem);
    align-items: start;
}

@media (max-width: 900px) {
    .auth {
        grid-template-columns: 1fr;
    }
}

.auth__card {
    padding: clamp(1.6rem, 3vw, 2.4rem);
}

.auth__divider {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    margin: 1.6rem 0 1.2rem;
    font-family: var(--font-mono);
    font-size: 0.68rem;
    letter-spacing: 0.14em;
    text-transform: uppercase;
    color: var(--dim);
}

.auth__divider::before,
.auth__divider::after {
    content: "";
    flex: 1;
    height: 1px;
    background: linear-gradient(90deg, transparent, var(--line-bright), transparent);
}

/* ---------------------------------------------------------------- prose -- */
/* Legal and long-form pages: narrow measure, generous leading. */
.prose {
    max-width: 74ch;
    color: var(--muted);
}

.prose h2 {
    margin-top: 2.6rem;
    font-size: clamp(1.35rem, 2.4vw, 1.7rem);
    color: var(--text);
}

.prose h3 {
    margin-top: 1.8rem;
    color: var(--text);
}

.prose ul,
.prose ol {
    padding-left: 1.2rem;
    margin-bottom: 1rem;
}

.prose li {
    margin-bottom: 0.45rem;
}

.prose strong {
    color: var(--text);
}

/* -------------------------------------------------------------- 404 page -- */
.notfound {
    text-align: center;
    padding-block: clamp(4rem, 12vw, 8rem);
}

.notfound__code {
    font-family: var(--font-display);
    font-size: clamp(5rem, 20vw, 11rem);
    font-weight: 800;
    line-height: 1;
    letter-spacing: -0.06em;
    background: var(--spectrum);
    background-size: 220% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    animation: shimmer 7s linear infinite;
}

.notfound .btn-row {
    justify-content: center;
}

/* ---------------------------------------------------------------- footer -- */
.footer {
    position: relative;
    margin-top: var(--section-y);
    padding-block: clamp(2.6rem, 6vw, 4rem) 2rem;
    background: rgba(2, 4, 10, 0.6);
}

.footer::before {
    content: "";
    position: absolute;
    inset: 0 0 auto 0;
    height: 1px;
    background: var(--spectrum);
    opacity: 0.3;
}

.footer__grid {
    display: grid;
    grid-template-columns: minmax(0, 1.6fr) repeat(3, minmax(0, 1fr));
    gap: 2rem;
}

@media (max-width: 820px) {
    .footer__grid {
        grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr));
    }

    .footer__about {
        grid-column: 1 / -1;
    }
}

.footer__about p {
    margin-top: 0.9rem;
    max-width: 34ch;
    font-size: 0.9rem;
    color: var(--muted);
}

.footer h4,
.footer .footer__h {
    margin-bottom: 0.9rem;
}

.footer ul {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    gap: 0.5rem;
}

.footer ul a {
    color: var(--muted);
    font-size: 0.9rem;
}

.footer ul a:hover {
    color: var(--text);
}

.footer__bottom {
    display: flex;
    flex-wrap: wrap;
    gap: 1rem;
    align-items: center;
    justify-content: space-between;
    margin-top: 2.4rem;
    padding-top: 1.4rem;
    border-top: 1px solid var(--line);
    font-size: 0.82rem;
    color: var(--dim);
}

.footer__bottom p {
    margin: 0;
}

.social {
    display: flex;
    gap: 0.5rem;
}

.social a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 36px;
    height: 36px;
    border-radius: 50%;
    background: var(--glass);
    color: var(--muted);
    box-shadow: inset 0 0 0 1px var(--line);
    transition:
        color 0.24s var(--ease-out),
        transform 0.24s var(--ease-spring),
        box-shadow 0.24s var(--ease-out);
}

.social a:hover {
    color: var(--cyan);
    transform: translateY(-3px);
    box-shadow: inset 0 0 0 1px var(--line-bright);
}

.social svg {
    width: 17px;
    height: 17px;
}

/* ---------------------------------------------------------------- toasts -- */
.toast-region {
    position: fixed;
    right: 1rem;
    bottom: 1rem;
    z-index: 300;
    display: grid;
    gap: 0.6rem;
    justify-items: end;
    pointer-events: none;
}

.toast {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    max-width: min(92vw, 380px);
    padding: 0.85rem 1.1rem;
    border-radius: var(--r);
    background: rgba(11, 17, 34, 0.96);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    color: var(--text);
    font-size: 0.88rem;
    box-shadow:
        inset 0 0 0 1px var(--line-bright),
        var(--shadow);
    animation: toast-in 0.4s var(--ease-spring);
}

.toast[data-leaving="true"] {
    animation: toast-out 0.3s var(--ease-out) forwards;
}

@keyframes toast-in {
    from {
        opacity: 0;
        transform: translateY(14px) scale(0.96);
    }
}

@keyframes toast-out {
    to {
        opacity: 0;
        transform: translateY(10px) scale(0.97);
    }
}

/* -------------------------------------------------------- reduced motion -- */
/* Everything decorative stops. The reveal states are forced visible because a
   user who turns this on mid-session would otherwise be left looking at
   permanently invisible content, because site.js only short-circuits the observer at
   load time. */
@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }

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

    [data-reveal] {
        opacity: 1;
        transform: none;
    }

    .grain {
        display: none;
    }
}
