/*
    The public site's own layer, on top of the shared tokens.

    tokens.css supplies the palette, the surface treatments and the spacing scale, so the site and
    the launcher stay one product. What it does NOT supply is a display type scale: its --font-title
    is 22px because it was written for a desktop panel where the largest thing on screen is a page
    header. A landing page needs a 68px headline, so that scale is defined here rather than pushed
    back into tokens.css where it would be dead weight for three hosts that will never use it.

    Everything else defers. If a colour appears here as a literal, it is a bug.

    One rule worth stating because it decides a dozen small choices: ACCENT MARKS THE PRICE YOU ARE
    ABOUT TO PAY, not every price on screen. A term you are selecting is accent, the same as in the
    launcher's checkout, because it is the same control seen twice. A "from" price in a listing is
    ordinary text - you are browsing, not committing.
*/

/*
    Space Grotesk, self-hosted rather than linked from a CDN so the page owns every request it makes.
    Latin subset only - the site has no other script - and one variable file spans 400 to 700, so a
    weight is an axis position rather than another download.

    Copyright 2020 The Space Grotesk Project Authors, SIL Open Font License 1.1.
    https://github.com/floriankarsten/space-grotesk
*/
@font-face {
    font-family: "Space Grotesk";
    src: url("../fonts/space-grotesk-latin.woff2") format("woff2");
    font-weight: 400 700;
    font-style: normal;
    font-display: swap;
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
    --measure: 1200px;
    --nav-height: 76px;

    /*
        Display face, for the wordmark and every heading. Body text stays on the system stack: it is
        what the launcher renders in, and matching it there is worth more than matching the headings.
        The @font-face block below defines Space Grotesk; the rest of the stack is what shows while
        it loads and if it fails.
    */
    --font-display: "Space Grotesk", "Segoe UI Variable Display", "Segoe UI", system-ui, sans-serif;

    --display-1: clamp(2.75rem, 6.5vw, 4.25rem);
    --display-2: clamp(1.5rem, 2.6vw, 1.875rem);
    --lead: clamp(1rem, 1.4vw, 1.125rem);

    --label: 0.6875rem;
    --label-tracking: 0.09em;
}

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

html { scroll-behavior: smooth; }

/*
    Column, so the footer is pushed to the bottom of short pages rather than stopping wherever the
    content ran out. .aurora is fixed and #aurora-sksl is hidden, so neither takes part in the flex
    layout; the three flow children are the nav, main and the footer.
*/
body {
    margin: 0;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    background: var(--page-background);
    color: var(--text-primary);
    font-family: "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, sans-serif;
    line-height: 1.6;
    -webkit-font-smoothing: antialiased;
}

main { flex: 1 0 auto; display: flex; flex-direction: column; }

h1, h2, h3 { margin: 0; font-family: var(--font-display); line-height: 1.1; letter-spacing: -0.025em; font-weight: 700; }
p { margin: 0; }
a { color: inherit; text-decoration: none; }
img, canvas { display: block; max-width: 100%; }

/*
    FocusOnNavigate hands focus to the page heading so a screen reader lands on it, which the browser
    then rings as though it had been clicked. The tabindex is -1, so nothing here is reachable by Tab
    and no keyboard user loses an indicator.
*/
[tabindex="-1"]:focus { outline: none; }

/* ---------------------------------------------------------------------------------------------
   Background. Fixed so the shader does not scroll with the content, and the gradient underneath is
   the fallback the renderer reveals by hiding the canvas when WebGL is missing or the context is
   lost - the same arrangement the panel uses, for the same reason: a failed background must never
   leave the page on a bare colour.
   --------------------------------------------------------------------------------------------- */

.aurora {
    position: fixed;
    inset: 0;
    z-index: -1;
    background:
        radial-gradient(58% 78% at 6% 42%, #1E6FA84D 0%, transparent 62%),
        radial-gradient(54% 74% at 94% 38%, #7B3FA84D 0%, transparent 62%),
        var(--page-background);
}

.aurora canvas { width: 100%; height: 100%; }

/*
    Backdrop scrim. Not decoration - the panel measured this and the number is theirs: a live Aurora
    frame peaks at rgb(149,208,255), and against that --text-secondary behind a translucent surface
    lands at 3.9:1, under the 4.5 floor. Raising the surface fills until text passes is what turns
    glass into flat dark cards; dimming the backdrop fixes the same number and leaves the shader
    visible and moving, which is also what frosted glass over a light source actually looks like.

    Shaped rather than uniform, which is the correction after two flat attempts both looked wrong.
    The panel damps hard and evenly because it is a dense table with secondary text sitting directly
    on the shader in hundreds of places. This page is the opposite: nearly all of its text lives in
    raised surfaces inside a 1200px measure, and the shader is the only thing on screen carrying any
    colour at all. Dimming it evenly turns the launcher's vivid cyan and magenta into uniform murk -
    which is exactly what happened.

    So the dim follows the content instead. The centre band, where the measure sits, is damped hard;
    the outer edges are left almost untouched, and that is where AuroraTheme puts its curtains
    anyway - its own remarks call the dark centre "where the content sits". The page is reinforcing
    the shader's design rather than fighting it. The vertical pass darkens the foot, where the
    footer's small muted text would otherwise sit on bare shader.
*/
.aurora::after {
    content: "";
    position: absolute;
    inset: 0;
    pointer-events: none;

    /*
        Alpha is baked into the colour stops rather than set with `opacity`. An opacity property
        applies to the whole layer including the flat fill underneath the gradient, so raising it to
        strengthen the vignette silently strengthens the uniform dim as well - which is how the
        first attempt at this ended up darker than the value it was meant to lighten.
    */
    background:
        linear-gradient(180deg, #0A0E1400 62%, #0A0E14B8 100%),
        linear-gradient(90deg, #0A0E1400 0%, #0A0E14BF 20%, #0A0E14BF 80%, #0A0E1400 100%),
        #0A0E1433;
}

/*
    The same scrim, inverted, where the surface style is light.

    Its job is the contrast of the text that sits DIRECTLY on the shader - the page headings, the
    footer - and under Flat's light palette that text is dark, so darkening the backdrop pushes it
    the wrong way. Shaped identically: hard through the centre band where the measure sits, barely
    there at the edges where the shader's curtains are.

    Carried further than the dark pass because the floor is higher: dark text needs the backdrop
    close to the panel white beside it, where light text only needed the shader's peaks damped.
*/
@media (prefers-color-scheme: light) {
    .theme-flat .aurora::after {
        background:
            linear-gradient(180deg, #F2F2F700 58%, #F2F2F7F2 100%),
            linear-gradient(90deg, #F2F2F700 0%, #F2F2F7F2 18%, #F2F2F7F2 82%, #F2F2F700 100%),
            #F2F2F766;
    }
}

/* ---------------------------------------------------------------------------------------------
   Layout
   --------------------------------------------------------------------------------------------- */

.wrap {
    width: 100%;
    max-width: var(--measure);
    margin-inline: auto;
    padding-inline: var(--space-5);
}

.section { padding-block: clamp(1.75rem, 3vw, 2.75rem); }

/* The first thing under the ribbon needs more room above it than two sections need between them. */
main > .section:first-child:not(.section-center) { padding-top: clamp(2.5rem, 4vw, 3.5rem); }

/*
    A page that is one card and nothing else. It takes the whole space between the ribbon and the
    footer and centres in it, rather than hanging from the top with a screen of empty background
    underneath. Column, so the wrap keeps the block-flow width it has everywhere else.

    Both offsets below are margins that centring would otherwise count as content. The card is the
    only thing on the page, so a trailing margin under it is space with nothing after it to separate,
    and the footer's own gap is already covered by main filling the height - left in, the two of them
    put the card off centre by half their sum.
*/
.section-center { flex: 1; display: flex; flex-direction: column; justify-content: center; }
.section-center > .wrap { margin-bottom: 0; }
main:has(> .section-center) + .footer { margin-top: 0; }

.section-head { text-align: center; margin-bottom: clamp(1.25rem, 2.2vw, 1.75rem); }
.section-head h2 { font-size: var(--display-2); }

.section-head p {
    margin-top: var(--space-2);
    color: var(--text-secondary);
    font-size: 0.9375rem;
}

.label {
    font-size: var(--label);
    font-weight: 700;
    letter-spacing: var(--label-tracking);
    text-transform: uppercase;
    color: var(--text-secondary);
}

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

/* ---------------------------------------------------------------------------------------------
   Navigation
   --------------------------------------------------------------------------------------------- */

.nav {
    position: sticky;
    top: 0;
    z-index: 50;
    height: var(--nav-height);
    display: flex;
    align-items: center;

    /* Only Glass raises this; the opaque surfaces have nothing to see through and use their panel. */
    background: var(--nav-bg, var(--panel-bg));
    border-bottom: 1px solid var(--divider);
}

.nav .wrap { display: flex; align-items: center; gap: var(--space-5); }

/*
    The wordmark, and the one place the display face is NOT used. Space Grotesk's wide geometric caps
    already carry their own air, so the tracking a locked-up wordmark needs reads as a gap between
    letters rather than as one mark; the UI face takes 0.16em cleanly at this size.
*/
.brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-size: 1.0625rem;
    font-weight: 700;
    letter-spacing: 0.16em;
}

.brand img { display: block; flex: none; width: 28px; height: 28px; object-fit: contain; }

/*
    Header wordmark hover: a shockwave passes through the letters. Each glyph lifts, brightens with
    an accent glow, dips slightly under, then settles. Each letter's animation-delay is scaled from
    its --i index (set in markup), so the wave travels left to right. Footer brand stays quiet.

    inline-block on the letter is required so translate/scale apply; the outer span keeps its
    default inline flow so the 0.16em tracking on .brand still sits between glyphs.
*/
.nav .brand .wm-letter {
    display: inline-block;
    transform-origin: 50% 100%;
    will-change: transform, color;
}
.nav .brand:hover .wm-letter,
.nav .brand:focus-visible .wm-letter {
    animation: brand-wave 700ms cubic-bezier(0.22, 0.68, 0.36, 1.6) both;
    animation-delay: calc(var(--i) * 55ms);
}
@keyframes brand-wave {
    0%   { transform: translateY(0)       scale(1);    color: currentColor;  text-shadow: none; }
    35%  { transform: translateY(-0.4em)  scale(1.18); color: var(--accent); text-shadow: 0 0 14px color-mix(in srgb, var(--accent) 70%, transparent); }
    65%  { transform: translateY(0.06em)  scale(0.96); color: currentColor;  text-shadow: none; }
    100% { transform: translateY(0)       scale(1);    color: currentColor;  text-shadow: none; }
}

.nav-links { display: flex; align-items: center; gap: var(--space-1); margin-inline: auto; }

/*
    Sized to the control scale the buttons beside them use, so the whole ribbon sits on one height
    and every item in it reads as something you can press.
*/
.nav-links a {
    display: flex;
    align-items: center;
    height: var(--control-height-lg);
    padding-inline: var(--space-4);
    border: 1px solid transparent;
    border-radius: var(--pill-radius);
    color: var(--text-secondary);
    font-size: 0.9375rem;
    font-weight: 500;
    white-space: nowrap;
    transition: color .15s, background-color .15s, border-color .15s;
}

.nav-links a:hover { background: var(--row-hover); color: var(--text-primary); }

.nav-links a[aria-current="page"] {
    background: var(--row-hover);
    border-color: var(--tile-stroke);
    color: var(--text-primary);
}

.nav-actions { display: flex; align-items: center; gap: var(--space-3); }

/* Signed in, this button carries a username, and an account named after an email address is long. */
.nav-actions .signin { max-width: 12rem; overflow: hidden; }

/* ---------------------------------------------------------------------------------------------
   Buttons. Three weights only - filled for the one action a screen wants, outlined for its
   alternative, quiet for everything else. A fourth would need a reason.
   --------------------------------------------------------------------------------------------- */

.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    height: var(--control-height-lg);
    padding-inline: var(--space-5);
    border: 1px solid transparent;
    border-radius: var(--field-radius);
    font: inherit;
    font-size: 0.9375rem;
    font-weight: 600;
    cursor: pointer;
    white-space: nowrap;
    transition: background-color .15s, border-color .15s, color .15s, transform .1s;
}

.btn:active { transform: translateY(1px); }

.btn-primary { background: var(--accent); color: var(--primary-button-text); }
.btn-primary:hover { background: var(--accent-muted); }

.btn-outline { border-color: var(--accent); color: var(--accent); }
.btn-outline:hover { background: color-mix(in srgb, var(--accent) 10%, transparent); }

.btn-quiet { border-color: var(--panel-stroke); color: var(--text-primary); background: var(--ghost-bg); }
.btn-quiet:hover { background: var(--row-hover); }

.btn-lg { height: 52px; padding-inline: var(--space-6); font-size: 1rem; }

/* Full width, for the one action a form has. display:flex overrides .btn's inline-flex. */
.btn-block { display: flex; width: 100%; margin-top: var(--space-5); }

/* A quiet alternative to the primary action - a link, not a second button competing with it. */
.link-action {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    color: var(--accent);
    font-size: 0.9375rem;
    font-weight: 600;
}

.link-action svg { transition: transform .15s; }
.link-action:hover svg { transform: translateX(3px); }

/* ---------------------------------------------------------------------------------------------
   Surfaces
   --------------------------------------------------------------------------------------------- */

/*
    Glass's surface fills, raised - and raised HERE, over the token set, rather than written into
    the rules that use them. Every surface below reads its fill from the token, so Slate and Flat
    get theirs and this site follows a surface choice instead of painting one dark colour whatever
    the visitor picked.

    No backdrop-filter anywhere on this site, which is what these values compensate for - both they
    and the reason are copied from the panel rather than re-derived, because it already paid for
    the answer.

    The background here is ALWAYS animated. Blurring a surface whose backdrop is a full-viewport
    canvas re-snapshots and re-blurs it on every frame, which is the expensive part; the desktop
    hosts have no blur at all and tokens.css says why - "layered alpha rather than a platform blur,
    which is exactly why it ports".

    So opacity is the only thing carrying the glass read, and the values are graded - tile, then
    card, then panel, then nav - so every surface is one material at a different depth rather than
    four unrelated greys. The binding constraint is --text-secondary, not body text: it paints every
    label and rate line and sits far closer to the surface than white does.
*/
.theme-glass {
    --nav-bg: #141B24E0;
    --panel-bg: #141B24A6;
    --card-bg: #10161FE8;
    --tile-bg: #141B248C;
    --tile-hover-bg: #16303FA6;
}

.panel {
    background: var(--panel-bg);
    border: 1px solid var(--panel-stroke);
    border-radius: var(--panel-radius);
    box-shadow: var(--panel-shadow);
}

/* ---------------------------------------------------------------------------------------------
   Hero
   --------------------------------------------------------------------------------------------- */

.hero { padding-block: clamp(2rem, 4.5vw, 3.5rem) clamp(1.25rem, 2.5vw, 2rem); }

.hero-grid {
    display: grid;
    grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.3fr);
    gap: clamp(1.5rem, 3.5vw, 3rem);
    align-items: center;
}

.hero h1 { font-size: var(--display-1); letter-spacing: -0.04em; }

.hero-lead {
    margin-top: var(--space-5);
    max-width: 27rem;
    color: var(--text-secondary);
    font-size: var(--lead);
}

.hero-actions { margin-top: clamp(1.75rem, 3vw, 2.5rem); display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-5); }

.badge {
    display: inline-flex;
    align-items: center;
    height: var(--control-height);
    padding-inline: var(--space-4);
    margin-bottom: var(--space-5);
    background: var(--tile-bg);
    border: 1px solid var(--tile-stroke);
    border-radius: var(--pill-radius);
    color: var(--accent);
    font-size: var(--label);
    font-weight: 700;
    letter-spacing: var(--label-tracking);
    text-transform: uppercase;
}

/*
    The hero image is the product's own window, not an illustration of it - the only honest answer to
    "what am I actually buying", and it stays current for free because tools/ViewPreview and
    tools/Capture-AppWindow.ps1 in ReflexFramework regenerate it from the shipping app.
*/
/*
    The back window sits in flow and sets the height; only the front one is positioned. A fixed
    min-height here instead meant the front window overflowed its container the moment the images got
    bigger, which is what made the whole shot read as texture rather than as a demonstration.
*/
.hero-shot { position: relative; padding-bottom: 24%; }

.shot {
    border-radius: var(--card-radius);
    border: 1px solid var(--panel-stroke);
    overflow: hidden;

    /* Dark in every surface style: it is the ground a screenshot sits on, not a surface of ours. */
    background: #0B1119;
    box-shadow: 0 28px 70px #000000B3;
}

.shot-back { position: relative; width: 72%; }
.shot-front { position: absolute; bottom: 0; right: 0; width: 78%; z-index: 1; }

.shot img { width: 100%; height: auto; }

/*
    A shot with its explanation beside it. The window keeps the width because it is the thing being
    explained; the prose only has to say which screen you are looking at.
*/
.shot-row {
    display: grid;
    grid-template-columns: minmax(0, 1.5fr) minmax(0, 1fr);
    gap: clamp(1.5rem, 3.5vw, 2.5rem);
    align-items: center;
}

.shot-row h3 { font-size: 1.125rem; }
.shot-row p { margin-top: var(--space-3); color: var(--text-secondary); font-size: 0.9375rem; }

/* ---------------------------------------------------------------------------------------------
   Numbered steps. Hairline-separated rows rather than cards: a step is one line of instruction, and
   a card around each would make eight of them look like eight products. An <ol>, so the order a
   reader cannot see in the layout is still in the markup - the drawn number is decorative. The track
   count comes from auto-fit, so three steps sit on one row and eight reflow instead of filling.

   The number is the only accent on the page outside a price, which is deliberate - it is what makes
   the list read as an ORDER rather than as bullets.
   --------------------------------------------------------------------------------------------- */

.steps {
    list-style: none;
    margin: 0;
    padding: 0;
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(21rem, 1fr));
    column-gap: clamp(1.5rem, 4vw, 2.5rem);
}

.step {
    display: flex;
    gap: var(--space-4);
    padding-block: var(--space-4);
    border-top: 1px solid var(--divider);
}

/* Not .mono: that carries user-select:all, which turns a click near a step number into a selection
   of it, and --mono-color, which is the colour for values a reader copies. */
.step-n {
    flex: none;
    width: 1.75rem;
    font-family: ui-monospace, "Cascadia Mono", Consolas, monospace;
    font-size: 0.8125rem;
    /* The step text's own line box (0.9375rem x 1.6), so a smaller number still sits on its first
       line rather than riding above it. */
    line-height: 1.5rem;
    color: var(--accent);
}

.step p { font-size: 0.9375rem; line-height: 1.6; }

/* ---------------------------------------------------------------------------------------------
   Products
   --------------------------------------------------------------------------------------------- */

.product-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
    gap: var(--space-4);
}

/*
    Artwork fills the card and the chrome stays out of its way - the palette is deliberately
    desaturated so the product art is the only saturated thing on any screen.

    --tint is per product and stands in for the banner at api/apps/{id}/image?type=Banner. It is a
    shape to fill, not a style to migrate.
*/
.product {
    position: relative;
    display: flex;
    align-items: center;
    gap: var(--space-4);
    min-height: 9rem;
    padding: var(--space-4);
    border: 1px solid var(--card-stroke);
    border-radius: var(--card-radius);
    overflow: hidden;
    /*
        The tint fades to `transparent` rather than to the ground at zero alpha - which the featured
        panel below cannot do, and says why. A small card's tint core fills it, so the ramp through
        transparent black is never wide enough to read as a smudge.
    */
    background:
        radial-gradient(85% 140% at 16% 50%, var(--tint) 0%, transparent 72%),
        linear-gradient(120deg, color-mix(in srgb, var(--text-primary) 6%, transparent), transparent 55%),
        var(--card-bg);
    transition: border-color .15s, transform .15s, box-shadow .15s;
}

.product:hover {
    border-color: var(--tile-stroke);
    transform: translateY(-2px);
    box-shadow: 0 16px 36px #00000066;
}

/*
    The icon-in-a-rounded-square convention the desktop hosts already use, rather than a bare letter
    floating in the card - a loose glyph reads as a missing asset, and a tile reads as a deliberate
    monogram. Swaps for the real banner without the layout moving.
*/
/* Real banner artwork, when the product has any. Replaces the monogram, not the layout around it. */
.product-art {
    flex: none;
    width: 6rem;
    height: 4.5rem;
    object-fit: cover;
    border-radius: var(--tile-radius);
    border: 1px solid var(--card-stroke);
}

/*
    A wash over whatever surface the card is, which is what --row-hover is in every theme - a light
    veil on a dark card, a dark one on a light card. The letter is held off full strength so the
    monogram reads as a placeholder rather than as a heading.
*/
.product-mark {
    flex: none;
    width: 3.5rem;
    height: 3.5rem;
    display: grid;
    place-items: center;
    background: var(--row-hover);
    border: 1px solid var(--card-stroke);
    border-radius: var(--tile-radius);
    font-family: var(--font-display);
    font-size: 1.375rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    color: color-mix(in srgb, var(--text-primary) 78%, transparent);
}

.product-body { display: flex; flex-direction: column; gap: var(--space-05); min-width: 0; }
.product-body h3 { font-size: 1.0625rem; }
.product-from { color: var(--text-secondary); font-size: 0.875rem; }
.product-body .link-action { margin-top: var(--space-2); font-size: 0.875rem; }

/* ---------------------------------------------------------------------------------------------
   Terms. Deliberate parity with the launcher's checkout - the buyer meets this control twice and it
   has to be the same control both times, down to the accent on the price and the check on the right.
   --------------------------------------------------------------------------------------------- */

/*
    11rem, not 15: the tiles also render inside the checkout's narrower left column, where a 15rem
    minimum wrapped three terms onto two rows and broke the row-of-tiles the launcher shows.
*/
.terms { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: var(--space-3); }

a.term { display: block; color: inherit; }

.term {
    position: relative;
    padding: var(--space-5);
    background: var(--tile-bg);
    border: 1px solid var(--card-stroke);
    border-radius: var(--tile-radius);
    transition: border-color .15s, background-color .15s, box-shadow .2s, transform .1s;
}

/* Glass names its own; elsewhere the accent wash is mixed into whatever the tile already is. */
.term:hover {
    border-color: var(--accent);
    background: var(--tile-hover-bg, color-mix(in srgb, var(--accent) 12%, var(--tile-bg)));
    box-shadow: 0 0 0 1px var(--accent), 0 0 28px -4px var(--accent);
}

.term:active { transform: translateY(1px); }

/* The term being bought. Same treatment as the hover, held - it is the launcher's selected tile. */
.term.is-selected {
    border-color: var(--accent);
    background: var(--tile-hover-bg, color-mix(in srgb, var(--accent) 12%, var(--tile-bg)));
    box-shadow: 0 0 0 1px var(--accent);
}

.term-check { position: absolute; top: var(--space-3); right: var(--space-3); }

.term-name { font-size: 0.9375rem; color: var(--text-secondary); }
.term-price { font-family: var(--font-display); font-size: 2rem; font-weight: 700; letter-spacing: -0.035em; color: var(--accent); line-height: 1.2; }
.term-rate { font-size: 0.8125rem; color: var(--text-secondary); }

/* ---------------------------------------------------------------------------------------------
   Assurances. Not a row of adjectives - every one states a specific, checkable fact about what
   happens to the buyer, in the words the checkout already uses. "Premium quality" is a claim a
   competitor copies in a minute; "nothing renews" is a promise the product keeps.
   --------------------------------------------------------------------------------------------- */

.assurances {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
    padding: clamp(1.25rem, 2.5vw, 1.75rem);
}

.assurance {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    padding-inline: clamp(0.5rem, 2vw, 1.5rem);
}

.assurance + .assurance { border-left: 1px solid var(--divider); }

.assurance .glyph {
    flex: none;
    width: 2.75rem;
    height: 2.75rem;
    display: grid;
    place-items: center;
    border: 1px solid var(--tile-stroke);
    border-radius: 50%;
    color: var(--accent);
}

.assurance p { font-size: 0.9375rem; line-height: 1.45; }

/* ---------------------------------------------------------------------------------------------
   Store
   --------------------------------------------------------------------------------------------- */

.featured { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); overflow: hidden; }

/*
    Fades to #10161F00, not to `transparent`. A gradient stop of `transparent` is rgba(0,0,0,0), so
    the browser interpolates the whole ramp through transparent BLACK - over a panel-sized radial
    that is most of the visible area, and it renders as a near-black smudge rather than a glow. The
    small product cards got away with it because their core fills the card; this one did not.
*/
/*
    Dark in every surface style, like .shot: this is the stage the product's artwork is mounted on,
    and a poster frame that changed colour with the chrome around it would be the odd one out. The
    monogram below is lit for this ground rather than for the panel.
*/
.featured-art {
    min-height: 20rem;
    display: grid;
    place-items: center;
    background:
        radial-gradient(62% 78% at 44% 74%, var(--tint) 0%, #10161F00 72%),
        radial-gradient(50% 62% at 80% 16%, #FFFFFF29 0%, #10161F00 70%),
        linear-gradient(200deg, #FFFFFF1A, #10161F00 55%),
        #10161F;
}

/*
    The no-artwork state, and it is a REQUIRED state rather than a placeholder: ProductInfo says
    BannerAssetId is "zero when the product has no artwork" and that the store "falls back rather
    than showing a broken image". So this panel has to look composed with nothing in it - a tinted
    void only reads as art-shaped hole. The monogram is the same rounded-square convention the cards
    use, scaled up; a real banner replaces the whole block without the layout moving.

    Lit for the stage above rather than for the surface style, for the same reason the stage is: it
    sits on the artwork ground in every theme, and a light chip there would vanish.
*/
.featured-mark {
    width: 6.5rem;
    height: 6.5rem;
    display: grid;
    place-items: center;
    background: #FFFFFF14;
    border: 1px solid #FFFFFF29;
    border-radius: var(--panel-radius);
    font-family: var(--font-display);
    font-size: 2.75rem;
    font-weight: 700;
    letter-spacing: -0.03em;
    color: #FFFFFFD6;
}

.featured-body {
    padding: clamp(1.5rem, 3vw, 2.5rem);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: flex-start;
    gap: var(--space-3);
}

.featured-body h2 { font-size: var(--display-2); }
.featured-body .btn { margin-top: var(--space-2); }

/* The left-aligned page head. Same distance to the content below as .section-head's centred one. */
.store-head {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: clamp(1.25rem, 2.2vw, 1.75rem);
}

.store-head p { margin-top: var(--space-2); font-size: 0.9375rem; }

.store-head h2 { font-size: var(--display-2); }
.store-count { color: var(--text-secondary); font-size: 0.875rem; }

/* ---------------------------------------------------------------------------------------------
   Product detail
   --------------------------------------------------------------------------------------------- */

.features { list-style: none; margin: var(--space-2) 0 0; padding: 0; display: grid; gap: var(--space-2); }
.features li { display: flex; align-items: flex-start; gap: var(--space-3); font-size: 0.9375rem; }
.features svg { flex: none; margin-top: 0.15rem; color: var(--accent); }

/* Collapsed by default: the terms are the seller's full legal text, not page copy. */
.terms-disclosure { margin-top: var(--space-6); }
.terms-disclosure summary { cursor: pointer; color: var(--accent); font-size: 0.9375rem; font-weight: 600; }

.terms-disclosure pre {
    margin-top: var(--space-4);
    padding: var(--space-4);
    max-height: 22rem;
    overflow: auto;
    background: var(--field-bg);
    border: 1px solid var(--field-stroke);
    border-radius: var(--field-radius);
    color: var(--text-secondary);
    font-size: 0.8125rem;
    line-height: 1.6;
    white-space: pre-wrap;
}

/* ---------------------------------------------------------------------------------------------
   Checkout. Product left, order right, everything visible at once - the launcher's arrangement,
   because a buyer should never scroll to find what they are about to be charged.
   --------------------------------------------------------------------------------------------- */

/*
    The two columns are the same height because the grid makes them so, not because their contents
    happen to add up. Content here is conditional - a coupon error, a provider choice, a sign-in
    block that becomes a purchase form - so any balance struck by counting pixels is lost the next
    time one of those appears. Stretching both to the row and letting each absorb its own slack is
    the version that stays true.
*/
.checkout-grid { display: grid; grid-template-columns: minmax(0, 1.35fr) minmax(0, 1fr); gap: var(--space-5); }

/*
    The buyer's side: what they are getting, then which term, then how they pay. When this column is
    the shorter one the slack goes to the gaps between its panels rather than into any one of them,
    which keeps every panel the size its content asked for and still lands the last edge on the line
    the order card ends on.
*/
.checkout-flow { display: flex; flex-direction: column; gap: var(--space-5); justify-content: space-between; }


/*
    The commit - the acceptance and the control that acts on it - sits at the foot of the card, so
    when this column is the shorter one the slack collects above it instead of trailing beneath it.
*/
.checkout-commit { display: flex; flex-direction: column; gap: var(--space-3); margin-top: auto; }

.checkout-product { display: flex; align-items: flex-start; gap: var(--space-4); }
.checkout-product-body { display: flex; flex-direction: column; gap: var(--space-2); min-width: 0; }
.checkout-product-body h3 { font-size: 1.25rem; }
.checkout-note { font-size: 0.8125rem; }

/*
    The icon rides ON the monogram rather than beside it, and arrives as --icon on the tile itself.

    A background rather than an image element, because the request may legitimately 404 - nothing on
    the contract says whether an app has an icon. A background that fails paints nothing and the
    letter underneath stands; a failed <img> leaves the browser's broken-image glyph in the tile,
    empty alt or not. With --icon unset the background-image declaration has nothing to resolve and
    drops out entirely, which is the same outcome by the same path.
*/
.checkout-mark { position: relative; width: 4.5rem; height: 4.5rem; font-size: 1.75rem; overflow: hidden; }
.checkout-mark-sm { width: 2.5rem; height: 2.5rem; font-size: 1rem; }

.checkout-mark::after {
    content: "";
    position: absolute;
    inset: 0;
    background-image: var(--icon);
    background-size: cover;
    background-position: center;
}

.order-item {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding-bottom: var(--space-4);
    border-bottom: 1px solid var(--divider);
    font-size: 0.9375rem;
}

.order-item-body { display: flex; flex-direction: column; min-width: 0; margin-right: auto; }
.order-item-body .muted { font-size: 0.875rem; }

.order-lines { display: grid; gap: var(--space-2); }
.order-lines > div { display: flex; justify-content: space-between; gap: var(--space-4); font-size: 0.9375rem; }

/* Both order options, stacked; the Apply that commits them sits on the row of the last one. */
.options-form { display: grid; gap: var(--space-3); }
.options-form .field { margin-bottom: 0; }
.options-row { display: flex; align-items: flex-end; gap: var(--space-2); }
.options-row .field { flex: 1; min-width: 0; }
.options-row .btn { flex: none; height: var(--control-height-lg); }

.order-total {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: var(--space-4);
    margin-block: var(--space-4);
    padding-top: var(--space-4);
    border-top: 1px solid var(--divider);
}

/*
    Accepted methods, in the buyer's column rather than under the total. It is the width that decides
    this: a mark small enough to fit beside a price is a mark nobody can read, and the question the
    strip answers - can I actually pay for this - belongs with the purchase either way.

    Light tiles, which is the one place on this site that is not glass: these are other people's
    marks, drawn for a light ground, and a Visa wordmark knocked out in white is not a Visa wordmark.
    The hairline is dark rather than white so a tile still has an edge on a light surface style.
*/
.pay-panel h3 { font-size: 1.125rem; }
.pay-panel > p { font-size: 0.9375rem; }
/*
    One track per mark, from a count the component works out - see PaymentMarks. The tracks share the
    width, so the row is flush on both edges whatever the seller accepts, and the cap keeps a
    one-method seller from being handed a tile the width of the panel.

    A single row, deliberately: this panel sits beside the order card and the two have to end near
    enough together to read as a pair, and a second row of marks is 50px the left column cannot
    spare. It is the width of the column that sets the mark size here, not a chosen height.
*/
.pay-marks {
    display: grid;
    grid-template-columns: repeat(var(--pay-cols, 6), minmax(0, 4.5rem));
    gap: var(--space-2);
    margin-top: var(--space-2);
}

.pay-mark {
    display: grid;
    place-items: center;
    height: 2.25rem;
    padding: 0.1875rem 0.25rem;
    background: #F5F6F8;
    border: 1px solid #0000001F;
    border-radius: 6px;
    box-shadow: 0 1px 2px #0000004D;
}

/* Every mark shares one viewBox, so they scale together and sit on one optical line. */
.pay-mark svg { width: 100%; height: 100%; }

/* The acknowledgement. Never pre-ticked - it is evidence, and a default would make it worthless. */
.consent { display: flex; align-items: flex-start; gap: var(--space-3); margin-block: var(--space-4); font-size: 0.8125rem; line-height: 1.5; }
.consent input { flex: none; margin-top: 0.2rem; width: 1.1rem; height: 1.1rem; accent-color: var(--accent); }

/* A step on the card, not an aside inside it: its own rule, its own label, its own full-width row. */
.signin-gate { padding-top: var(--space-4); border-top: 1px solid var(--divider); }
.signin-gate p { font-size: 0.875rem; }
.signin-gate > .label + p { margin-top: var(--space-2); }
.signin-gate .auth-methods { flex-direction: column; }

/*
    One select control for the whole site; the two that use it differ only in size.

    appearance:none takes it out of native rendering, so it is a box this stylesheet controls rather
    than a platform widget dropped into the page. That also removes the UA dropmarker, so the chevron
    is drawn here.
*/
.field select,
.theme-choice select {
    appearance: none;
    -webkit-appearance: none;
    background-color: var(--field-bg);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23AEAEB2' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-size: 10px 6px;
    border: 1px solid var(--field-stroke);
    border-radius: var(--field-radius);
    color: var(--text-primary);
    font: inherit;
}

/*
    appearance:none does not reach the OS-drawn popup, so the option surface needs a colour of its
    own or Glass renders white text on white.

    It must be an OPAQUE one. --panel-bg is deliberately translucent, and the option list is painted
    by the platform outside the page's compositing tree - it has no backdrop to blend with, so a
    translucent colour collapses to whatever flat approximation the platform picks. That grey wash is
    not a colour choice; it is translucency used where translucency does not exist.
*/
.field select option,
.theme-choice select option { background: var(--surface-opaque); color: var(--text-primary); }

.field select {
    width: 100%;
    height: var(--control-height-lg);
    padding-inline: var(--space-3) 34px;
    background-position: right var(--space-3) center;
}

/* ---------------------------------------------------------------------------------------------
   Account and sign-in
   --------------------------------------------------------------------------------------------- */

.auth-wrap { max-width: 27rem; }

.auth-methods { display: flex; flex-wrap: wrap; gap: var(--space-2); margin-top: var(--space-4); }

/* The provider mark rides in the button's own gap and never scales with its label. */
.auth-methods .btn svg { flex: none; width: 18px; height: 18px; }

/* Stacked methods space on the flex gap; .btn-block's margin belongs to a form's submit. */
.auth-methods .btn-block { margin-top: 0; }
.auth-divider + .auth-methods { margin-top: 0; }

/* A rule with the alternative written into it, rather than a bare line and a floating label. */
.auth-divider {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-block: var(--space-5);
    color: var(--text-secondary);
    font-size: 0.8125rem;
}

.auth-divider::before,
.auth-divider::after { content: ""; flex: 1; height: 1px; background: var(--divider); }

.method-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-2); }
.method-list li { display: flex; gap: var(--space-3); font-size: 0.9375rem; text-transform: capitalize; }
.method-list .muted { text-transform: none; }

.auth-panel { padding: clamp(1.5rem, 3vw, 2.25rem); }
.auth-note { margin-top: var(--space-5); text-align: center; font-size: 0.875rem; }

.field { display: block; margin-bottom: var(--space-4); }
.field > span { display: block; margin-bottom: var(--space-2); font-size: 0.875rem; color: var(--text-secondary); }

.field input {
    width: 100%;
    height: var(--control-height-lg);
    padding-inline: var(--space-3);
    background: var(--field-bg);
    border: 1px solid var(--field-stroke);
    border-radius: var(--field-radius);
    color: var(--text-primary);
    font: inherit;
    font-size: 0.9375rem;
}

.field input:focus-visible { outline: none; border-color: var(--accent); }

/* The line that sits between the last field and the form's action. */
.field-row { display: flex; align-items: center; justify-content: space-between; gap: var(--space-4); font-size: 0.875rem; }
.field-row a { color: var(--accent); }
.field-row a:hover { color: var(--accent-muted); }

/* A box and its label as one target. .consent is the taller sibling, for text that wraps. */
.checkbox { display: inline-flex; align-items: center; gap: var(--space-2); color: var(--text-secondary); cursor: pointer; }
.checkbox input { flex: none; width: 1rem; height: 1rem; margin: 0; accent-color: var(--accent); }
.checkbox:hover { color: var(--text-primary); }

/*
    One error line, above the form. A refusal names what to do next and never which half was wrong -
    telling the person in front of the screen that the username exists is a support incident.
*/
.form-error {
    margin-bottom: var(--space-4);
    padding: var(--space-3);
    background: color-mix(in srgb, var(--danger) 12%, transparent);
    border: 1px solid color-mix(in srgb, var(--danger) 35%, transparent);
    border-radius: var(--field-radius);
    color: var(--danger);
    font-size: 0.875rem;
}

/*
    In the auth card only. Everything else in it is centred, so a message that is not reads as
    misaligned - while the same box on the checkout spans the full measure beside left-aligned order
    lines, where centring one short sentence is what would look wrong.
*/
.auth-panel .form-error,
.auth-panel #auth-status { text-align: center; }

.validation-message { display: block; margin-top: var(--space-2); color: var(--danger); font-size: 0.8125rem; }

.account-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(20rem, 1fr)); gap: var(--space-4); }
.account-panel { padding: clamp(1.25rem, 2.5vw, 1.75rem); display: flex; flex-direction: column; gap: var(--space-3); }
.account-panel p { font-size: 0.9375rem; }

/* A hardware id is a support handle: selectable, monospace, never a label. */
.mono { font-family: ui-monospace, "Cascadia Mono", Consolas, monospace; font-size: 0.875rem; color: var(--mono-color); user-select: all; }

.order-list { list-style: none; margin: 0; padding: 0; display: grid; gap: var(--space-3); }

.order-list li {
    display: grid;
    grid-template-columns: 1fr auto auto;
    gap: var(--space-4);
    align-items: baseline;
    padding-bottom: var(--space-3);
    border-bottom: 1px solid var(--divider);
    font-size: 0.9375rem;
}

.order-list li:last-child { border-bottom: 0; padding-bottom: 0; }

/* ---------------------------------------------------------------------------------------------
   Footer
   --------------------------------------------------------------------------------------------- */

.footer { border-top: 1px solid var(--divider); margin-top: clamp(2.5rem, 5vw, 4rem); }

.footer .wrap {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: var(--space-4) var(--space-5);
    padding-block: var(--space-6);
}

/* Pushed to the far edge, so the row reads brand-left, links-right rather than trailing off centre. */
.footer-links { display: flex; flex-wrap: wrap; gap: var(--space-5); margin-left: auto; }
.footer a { color: var(--text-secondary); font-size: 0.875rem; }
.footer a:hover { color: var(--text-primary); }
.footer small { color: var(--text-secondary); font-size: 0.875rem; }
.footer .brand { font-size: 0.875rem; color: var(--text-secondary); }
.footer .brand img { width: 24px; height: 24px; }

/*
    Two independent choices, so two controls: the background is a shader, the surface is a token set,
    and neither implies the other. Same split the admin panel and the desktop hosts present.

    Captions use the site's .label rather than the footer's link typography: at link size and colour
    they read as two more footer links. Controls take their fill and stroke from the field tokens,
    which is what makes the picker restyle itself the moment it is used.
*/
.theme-picker { display: flex; flex-wrap: wrap; align-items: center; gap: var(--space-2) var(--space-4); }

.theme-choice { display: inline-flex; align-items: center; gap: var(--space-2); }

.theme-choice select {
    height: var(--control-height-sm);
    padding-inline: var(--space-2) 26px;
    background-position: right var(--space-2) center;
    font-size: 0.875rem;
    line-height: normal;
}

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

@media (max-width: 980px) {
    .hero-grid, .featured { grid-template-columns: minmax(0, 1fr); }

    /*
        Tightened, never hidden. Display:none here left Pricing and Support reachable only from the
        footer, which is a scroll away and not where anyone looks for navigation - and there is no
        menu button to replace them with.
    */
    .nav-links { gap: 0; }
    .nav-links a { padding-inline: var(--space-2); font-size: 0.875rem; }
    .nav .wrap { gap: var(--space-3); }
    .featured-art { min-height: 13rem; }
    .shot-row { grid-template-columns: minmax(0, 1fr); }
    .hero-shot { min-height: 18rem; }
    .assurance + .assurance { border-left: 0; border-top: 1px solid var(--divider); padding-top: var(--space-4); margin-top: var(--space-4); }
}

/*
    The checkout stacks earlier than the rest of the page.

    Two columns are only worth it while the left one can hold the three plan tiles on one row and the
    payment marks on another. Below this both wrap, the column grows well past the order card beside
    it, and the marks are squeezed narrower than the wordmarks inside them - one column gives all
    three the full measure instead.

    The line is --measure, because that is exactly what it depends on: at 1200px the wrap stops
    growing and the two columns are the width they were designed against. A pixel narrower and every
    row inside them is a pixel narrower too, which is where the wrapping starts.
*/
@media (max-width: 1199px) {
    .checkout-grid { grid-template-columns: minmax(0, 1fr); }
}

/*
    The wordmark drops to the mark alone, and only in the ribbon - the footer has room to wrap. Below
    this the three nav links plus two buttons no longer fit beside the name, and the ribbon is the
    one row on the site where nothing may be hidden: there is no menu button to hide it behind.
*/
@media (max-width: 720px) {
    .nav .brand span { display: none; }
}

/*
    Below this the mark, three links and two buttons measure wider than the viewport, so the ribbon
    wraps rather than overflowing: the mark and the two controls on the first row, the links spread
    across the second. Nothing may be hidden here - there is no menu button to hide it behind - and
    shrinking type and padding far enough to hold one row lands the links under the 24px touch floor.

    The height becomes the content's, so it is padding that sets the ribbon's depth from here down.

    640 rather than the ~530 the row actually measures on this machine: the threshold is a width and
    the thing being avoided is a text measurement, so it is set with room for a platform whose UI
    face runs wider. Wrapping a little early costs a row of white space; wrapping late costs a
    ribbon that runs off the side of the screen.
*/
@media (max-width: 640px) {
    .nav { height: auto; padding-block: var(--space-2); }
    .nav .wrap { flex-wrap: wrap; row-gap: var(--space-2); }

    /* Last in the flex order, so it takes the second row on its own. */
    .nav-links { order: 1; width: 100%; margin-inline: 0; justify-content: space-between; }

    /* The margin the links gave up, so the controls still sit against the right edge. */
    .nav-actions { margin-inline-start: auto; }

    /*
        Three across, whatever the count. The column count the component picks balances the ROWS,
        which is the right answer on a column 600px wide and the wrong one on a phone: five tracks
        there are narrower than the mark inside them, and the strip shrinks to illegible rather than
        taking another row it has plenty of room for.
    */
    .pay-marks { grid-template-columns: repeat(3, minmax(0, 7rem)); }
}

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    .btn, .product, .term, .nav-links a, .link-action svg { transition: none; }
    .nav .brand:hover .wm-letter,
    .nav .brand:focus-visible .wm-letter { animation: none; }
}
