/*
| khodkasb — the public layer.
|
| The landing page, the public pages, the legal pages and the two error pages.
| Loaded AFTER tokens.css and app.css and it adds to them; it does not restate
| them.
|
| Why it is a separate file and not more of app.css: app.css is the panel and
| the shopfront, and a marketing page matches almost none of it. Why it loads
| app.css anyway: .btn, .field, .card, .badge and .empty are already built,
| already token-driven and already carrying real traffic, and a second copy of
| a button is a copy that drifts. Reuse costs bytes once; drift costs a
| mismatched product forever.
|
| THERE IS NOT ONE COLOUR AND NOT ONE TYPEFACE IN THIS FILE, same rule as
| app.css. Every value below is a var() from tokens.css, or an alpha on a
| channel triplet from tokens.css. AC-BR-1 counts this file too.
*/

/* ==========================================================================
   1. LAYOUT PRIMITIVES
   ========================================================================== */

/*
| The one horizontal container. --page-x is a clamp, so the gutter grows with
| the screen and a phone never has text touching the glass.
*/
.wrap {
    width: 100%;
    max-width: var(--page-max);
    margin-inline: auto;
    padding-inline: var(--page-x);
}

.wrap--narrow {
    max-width: var(--page-narrow);
}

/*
| A landing section. Named .band rather than .section because app.css already
| owns .section__head / .section__title / .section__lead in the panel, and two
| meanings for one prefix is how a stylesheet starts lying.
|
| The vertical rhythm is deliberately generous - a clamp from 56px on a phone
| to 112px on a desktop - because the instruction for this page is that it
| breathes.
*/
.band {
    padding-block: var(--section-y);
}

.band--tight {
    padding-block: calc(var(--section-y) * .62);
}

/* The only large soft-coloured surface in the product. Sky wash, never the
   warm accent: the accent is a small colour by rule. */
.band--wash {
    background: var(--surface-wash);
}

.band--paper {
    background: var(--bg);
}

.band__head {
    max-width: 46ch;
    margin: 0 auto var(--space-10);
    text-align: center;
}

.band__title {
    margin: 0 0 var(--space-3);
    font-size: var(--text-3xl);
    line-height: var(--lh-snug);
    /* Persian headings break badly at arbitrary points. balance keeps the
       last line from being one orphaned word, and it degrades to normal
       wrapping where it is not supported. */
    text-wrap: balance;
}

.band__lead {
    margin: 0;
    font-size: var(--text-lg);
    line-height: var(--lh-relaxed);
    color: var(--text-muted);
    text-wrap: pretty;
}

/* The one button a band is allowed to end with, centred under whatever the
   band was about. */
.band__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-3);
    margin-top: var(--space-10);
}

/* A screenshot and its caption as one block. <figure> carries a UA margin
   that would knock the two columns out of alignment. */
.shotfig {
    margin: 0;
}

/* A responsive column set. .grid in app.css is the shopfront tile grid with
   its own sizing, so this one is .cols. */
.cols {
    display: grid;
    gap: var(--space-6);
}

@media (min-width: 700px) {
    .cols--2 { grid-template-columns: repeat(2, 1fr); }
    .cols--3 { grid-template-columns: repeat(3, 1fr); }
}

/*
| The skip link.
|
| .visually-hidden in app.css hides a thing permanently, which is right for a
| label and wrong for this: a skip link has to appear the moment it is focused
| or it helps nobody. So it is moved off screen rather than clipped, and it
| slides into view on focus.
|
| It is the first thing in the tab order on every public page. The landing page
| is long and its header is sticky; without this a keyboard user walks the
| whole navigation again on every single page.
*/
.skip {
    position: fixed;
    top: var(--space-3);
    inset-inline-start: var(--space-3);
    z-index: 100;
    padding: var(--space-3) var(--space-5);
    color: var(--accent-on);
    background: var(--accent);
    border-radius: var(--radius-pill);
    font-weight: 700;
    box-shadow: var(--shadow-2);
    transform: translateY(-250%);
    transition: transform var(--fast) var(--ease);
}

.skip:focus {
    transform: translateY(0);
    text-decoration: none;
}

/* ==========================================================================
   2. BUTTONS — the third level
   ========================================================================== */

/*
| Three levels, and they are a hierarchy rather than a palette:
|
|   .btn.btn--cta   the warm accent. ONE per screen. "فروشگاهت رو بساز".
|   .btn            the sky fill. The normal affirmative action. (app.css)
|   .btn--quiet     outlined. The alternative that must not compete. (app.css)
|
| The ink on the warm fill is --cta-on, which is the dark ink, measured at
| 6.37:1. White on this orange is 2.80:1 and fails AA, so white is not an
| option here and never becomes one.
*/
.btn--cta {
    color: var(--cta-on);
    background: var(--cta);
}

.btn--cta:hover:not(:disabled) {
    opacity: 1;
    background: var(--cta-hover);
}

/* The focus ring has to change with the fill or it disappears into it. */
.btn--cta:focus-visible {
    outline-color: var(--cta-ink);
}

/*
| .btn is width:100% by design - it lives in forms and card footers. On a
| landing page two buttons sit side by side, so this is the opt-out.
*/
.btn--auto {
    width: auto;
}

/*
| Press feedback, on every button on the public pages.
|
| app.css already gives .btn a scale on :active. This adds the shadow half of
| it: a button lifts slightly at rest, presses flat, and comes back. The
| transition list names properties rather than "all", so nothing else on the
| element gets animated by accident.
*/
.btn--cta,
.band .btn,
.sitehead .btn {
    box-shadow: var(--shadow-1);
    transition: transform var(--fast) var(--ease),
                box-shadow var(--fast) var(--ease),
                background-color var(--fast) var(--ease),
                opacity var(--fast) var(--ease);
}

.band .btn:hover:not(:disabled),
.sitehead .btn:hover:not(:disabled) {
    box-shadow: var(--shadow-2);
    transform: translateY(-1px);
}

.band .btn:active:not(:disabled),
.sitehead .btn:active:not(:disabled) {
    box-shadow: var(--shadow-1);
    transform: translateY(0) scale(.985);
}

/* ==========================================================================
   3. CARDS
   ========================================================================== */

/*
| The public card lifts on hover. app.css's .card is a static panel surface
| and stays that way; this is the modifier the marketing pages ask for.
*/
.card--lift {
    box-shadow: var(--shadow-1);
    transition: transform var(--slow) var(--ease),
                box-shadow var(--slow) var(--ease),
                border-color var(--slow) var(--ease);
}

.card--lift:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow-3);
    border-color: var(--border-strong);
}

.card--pad {
    padding: var(--space-8);
}

.card__title {
    margin: 0 0 var(--space-2);
    font-size: var(--text-xl);
    line-height: var(--lh-snug);
}

.card__text {
    margin: 0;
    color: var(--text-muted);
    line-height: var(--lh-relaxed);
}

/*
| The numbered marker on a step card. This is one of the three jobs the warm
| accent is allowed to do, and it is small by construction: a 44px disc.
*/
.card__num {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    margin-bottom: var(--space-4);
    font-family: var(--font-heading);
    font-size: var(--text-lg);
    font-weight: 700;
    color: var(--cta-on);
    background: var(--cta);
    border-radius: var(--radius-pill);
}

/* The same disc in the quiet variant, for lists where nothing is the CTA. */
.card__num--calm {
    color: var(--accent-on);
    background: var(--accent);
}

/* ==========================================================================
   4. SITE HEADER
   ========================================================================== */

.sitehead {
    position: sticky;
    top: 0;
    z-index: 40;
    background: rgb(var(--paper-rgb) / .82);
    backdrop-filter: blur(12px);
    -webkit-backdrop-filter: blur(12px);
    border-bottom: 1px solid transparent;
    transition: border-color var(--slow) var(--ease),
                box-shadow var(--slow) var(--ease);
}

/* Set by site.js once the page has been scrolled at all. Without it the
   header floats with no edge over content that has slid under it. */
.sitehead.is-stuck {
    border-bottom-color: var(--border);
    box-shadow: var(--shadow-1);
}

.sitehead__bar {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    min-height: 64px;
}

.sitehead__brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    flex: 0 0 auto;
    color: var(--text);
    font-family: var(--font-heading);
    font-size: var(--text-lg);
    font-weight: 700;
}

.sitehead__brand:hover {
    text-decoration: none;
}

/*
| The mark, never stretched. There is exactly one logo file in this product and
| it is square; height plus width:auto is what keeps a future non-square file
| from being squashed into this box instead of overflowing it visibly.
*/
.sitehead__mark {
    display: block;
    height: 32px;
    width: auto;
}

.sitehead__nav {
    display: none;
    align-items: center;
    gap: var(--space-6);
    margin-inline-start: var(--space-6);
}

.sitehead__link {
    color: var(--text-muted);
    font-size: var(--text-sm);
    font-weight: 700;
    padding-block: var(--space-2);
    transition: color var(--fast) var(--ease);
}

.sitehead__link:hover {
    color: var(--text);
    text-decoration: none;
}

.sitehead__actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    margin-inline-start: auto;
}

/* The desktop-only half of the actions. On a phone these live in the sheet. */
.sitehead__wide {
    display: none;
}

/*
| The disclosure button. It is a real <button aria-expanded>, so the state is
| announced rather than implied by an icon rotating.
*/
.sitehead__toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    padding: 0;
    color: var(--text);
    background: transparent;
    border: 1.5px solid var(--border);
    border-radius: var(--radius-sm);
    cursor: pointer;
    transition: background-color var(--fast) var(--ease),
                border-color var(--fast) var(--ease);
}

.sitehead__toggle:hover {
    border-color: var(--border-strong);
    background: var(--surface);
}

.sitehead__toggle svg {
    width: 22px;
    height: 22px;
    stroke: currentColor;
    fill: none;
    stroke-width: 2;
    stroke-linecap: round;
}

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

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

/* The collapsed menu. hidden until the button says otherwise; [hidden] with
   display:none is what keeps its links out of the tab order when closed. */
.sitemenu[hidden] {
    display: none;
}

.sitemenu {
    padding: var(--space-2) 0 var(--space-6);
    border-top: 1px solid var(--border);
    animation: sitemenu-in var(--slow) var(--ease) both;
}

@keyframes sitemenu-in {
    from { opacity: 0; transform: translateY(-6px); }
    to   { opacity: 1; transform: translateY(0); }
}

.sitemenu__link {
    display: block;
    padding: var(--space-3) 0;
    color: var(--text);
    font-size: var(--text-base);
    font-weight: 700;
}

.sitemenu__actions {
    display: grid;
    gap: var(--space-3);
    margin-top: var(--space-4);
}

@media (min-width: 860px) {
    .sitehead__nav,
    .sitehead__wide {
        display: flex;
    }

    .sitehead__toggle {
        display: none;
    }

    .sitemenu {
        display: none;
    }
}

/* ==========================================================================
   5. HERO
   ========================================================================== */

.hero {
    position: relative;
    isolation: isolate;
    overflow: hidden;
    padding-block: calc(var(--section-y) * 1.15) var(--section-y);
    text-align: center;
}

/*
| The moving background.
|
| Three soft radial gradients on a light canvas, drifting. Deliberately cheap:
| no blur() filter, no canvas, no particles, no grid, no mesh. A radial
| gradient is already soft, so the expensive part is skipped entirely, and the
| only animated property is transform - which the compositor can run on its
| own thread without touching layout or paint.
|
| aria-hidden and pointer-events:none: it is decoration and must never be in
| the way of a tap or a screen reader.
*/
.aura {
    position: absolute;
    inset: -10%;
    z-index: -1;
    pointer-events: none;
    overflow: hidden;
}

.aura__blob {
    position: absolute;
    border-radius: var(--radius-pill);
    opacity: .55;
    will-change: transform;
}

.aura__blob--a {
    top: -8%;
    inset-inline-start: -6%;
    width: 60vw;
    height: 60vw;
    max-width: 620px;
    max-height: 620px;
    background: radial-gradient(circle at 50% 50%,
                rgb(var(--teal-rgb) / .55) 0%,
                rgb(var(--teal-rgb) / .18) 45%,
                rgb(var(--teal-rgb) / 0) 70%);
    animation: drift-a 26s var(--ease-in-out) infinite alternate;
}

.aura__blob--b {
    top: 4%;
    inset-inline-end: -12%;
    width: 66vw;
    height: 66vw;
    max-width: 700px;
    max-height: 700px;
    background: radial-gradient(circle at 50% 50%,
                rgb(var(--sky-rgb) / .55) 0%,
                rgb(var(--sky-rgb) / .18) 45%,
                rgb(var(--sky-rgb) / 0) 70%);
    animation: drift-b 32s var(--ease-in-out) infinite alternate;
}

/*
| The third blob is the one that gets dropped first. It is display:none below
| 700px, so a weak phone renders two composited layers instead of three - the
| "cheaper version" the instruction asks for, decided at build time rather
| than by measuring at runtime.
*/
.aura__blob--c {
    display: none;
    bottom: -20%;
    inset-inline-start: 28%;
    width: 48vw;
    height: 48vw;
    max-width: 520px;
    max-height: 520px;
    background: radial-gradient(circle at 50% 50%,
                rgb(var(--sky-rgb) / .35) 0%,
                rgb(var(--teal-rgb) / .14) 50%,
                rgb(var(--sky-rgb) / 0) 72%);
    animation: drift-c 38s var(--ease-in-out) infinite alternate;
}

@media (min-width: 700px) {
    .aura__blob--c {
        display: block;
    }
}

@keyframes drift-a {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to   { transform: translate3d(6%, 8%, 0) scale(1.12); }
}

@keyframes drift-b {
    from { transform: translate3d(0, 0, 0) scale(1.06); }
    to   { transform: translate3d(-7%, 6%, 0) scale(1); }
}

@keyframes drift-c {
    from { transform: translate3d(0, 0, 0) scale(1); }
    to   { transform: translate3d(4%, -7%, 0) scale(1.1); }
}

.hero__title {
    max-width: 20ch;
    margin: 0 auto var(--space-5);
    font-size: var(--text-4xl);
    line-height: var(--lh-tight);
    text-wrap: balance;
}

.hero__lead {
    max-width: 52ch;
    margin: 0 auto var(--space-8);
    font-size: var(--text-lg);
    line-height: var(--lh-relaxed);
    color: var(--text-muted);
    text-wrap: pretty;
}

.hero__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-3);
}

/* On the narrowest screens the two buttons stack full width rather than
   shrinking into two thin pills side by side. */
@media (max-width: 480px) {
    .hero__actions {
        display: grid;
    }

    .hero__actions .btn {
        width: 100%;
    }
}

/* ==========================================================================
   6. THE SCROLL MORPH — one photo, three places
   ========================================================================== */

/*
| A tall section with a sticky stage in it. As the section passes the viewport
| site.js writes data-state="1|2|3" on the stage and the three frames cross
| fade. The photo itself never changes and never moves: it is the SAME <img>
| in all three states, which is the whole idea being demonstrated.
|
| The scroll is not hijacked. The page scrolls at its normal speed, the stage
| is merely sticky while its parent goes past, and someone flicking down gets
| past the whole thing in one gesture.
|
| With JavaScript off, or with reduced motion on, no data-state is ever
| written and the CSS below falls through to showing state 3 - the finished
| order - as a plain static card.
*/
.morph {
    position: relative;
}

.morph__track {
    /* Three screens of travel, capped so a tall desktop does not turn this
       into a minute of scrolling. */
    height: 260vh;
    max-height: 2200px;
}

.morph__stage {
    position: sticky;
    top: 84px;
    display: flex;
    align-items: center;
    justify-content: center;
    height: min(86vh, 720px);
}

/*
| THE GEOMETRY, BECAUSE IT IS THE WHOLE TRICK.
|
| .morph__frames is the card box and it is the only thing in flow. Its padding
| reserves a strip above the photo and a taller strip below it; the photo sits
| in that gap, in normal flow, and therefore defines the card's height.
|
| The three frames are absolutely positioned over the same box and are flex
| columns pinned to those two strips - a head that lands in the top reserve and
| a foot that lands in the bottom one. So the furniture is drawn AROUND the
| photo, never on top of it, and the photo itself is one element that is never
| swapped, never duplicated and never re-decoded.
*/
.morph__frames {
    --morph-head: 44px;
    --morph-foot: 132px;

    position: relative;
    width: 100%;
    /*
    | The photo is square and is this box minus its padding, so this width is
    | also what sets the card's height. Capping it against the viewport height
    | as well as an absolute maximum is what keeps a short window - a phone in
    | landscape, a laptop with three toolbars - from pushing the caption off
    | the bottom of the sticky stage.
    */
    max-width: min(380px, 40vh);
    margin-inline: auto;
    padding: calc(var(--space-4) + var(--morph-head))
             var(--space-4)
             calc(var(--space-4) + var(--morph-foot));
}

.morph__photo {
    position: relative;
    z-index: 1;
    display: block;
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    background: var(--surface-sunken);
    border-radius: var(--radius-md);
}

.morph__frame {
    position: absolute;
    inset: 0;
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    padding: var(--space-4);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-2);
    opacity: 0;
    visibility: hidden;
    transform: translateY(10px) scale(.985);
    transition: opacity var(--slower) var(--ease),
                transform var(--slower) var(--ease),
                visibility 0s linear var(--slower);
}

.morph__frame.is-on {
    opacity: 1;
    visibility: visible;
    transform: translateY(0) scale(1);
    transition-delay: 0s, 0s, 0s;
}

/* The two strips. Their heights are the reserve, so nothing a frame puts in
   them can push the photo or resize the card between states. */
.morph__head {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    height: var(--morph-head);
}

.morph__foot {
    display: flex;
    flex-direction: column;
    justify-content: flex-start;
    height: var(--morph-foot);
    padding-top: var(--space-3);
}

/*
| The resting state, and it has to be correct in all three combinations.
|
|   no script            -> no .js class, no data-state  -> frame 3, static
|   script + reduced     -> .js, and site.js skips the morph entirely, so
|                           still no data-state          -> frame 3, static
|   script + normal      -> the no-preference block below hands the pre-script
|                           moment to frame 1, so the sequence does not start
|                           by fading backwards from the end.
*/
.morph__frames:not([data-state]) .morph__frame--3 {
    opacity: 1;
    visibility: visible;
    transform: none;
}

@media (prefers-reduced-motion: no-preference) {
    .js .morph__frames:not([data-state]) .morph__frame--3 {
        opacity: 0;
        visibility: hidden;
        transform: translateY(10px) scale(.985);
    }

    .js .morph__frames:not([data-state]) .morph__frame--1 {
        opacity: 1;
        visibility: visible;
        transform: none;
    }
}

.morph__caption {
    max-width: 34ch;
    margin: var(--space-6) auto 0;
    text-align: center;
    color: var(--text-muted);
    font-size: var(--text-sm);
    line-height: var(--lh-relaxed);
    /* The height is reserved for the longest of the three captions so the
       stage does not resize as the state changes. */
    min-height: 3.4em;
}

/* --- frame 1: an Instagram-shaped post ---------------------------------- */

.morph__avatar {
    width: 28px;
    height: 28px;
    border-radius: var(--radius-pill);
    background: var(--surface-sunken);
    border: 1px solid var(--border);
}

.morph__handle {
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--text);
}

.morph__social {
    display: flex;
    gap: var(--space-3);
}

.morph__social svg {
    width: 20px;
    height: 20px;
    stroke: var(--ink-400);
    fill: none;
    stroke-width: 1.6;
}

/* --- frame 2: a shopfront tile ------------------------------------------ */

.morph__name {
    margin: 0 0 var(--space-1);
    font-size: var(--text-base);
    font-weight: 700;
}

.morph__price {
    margin: 0 0 var(--space-3);
    font-family: var(--font-heading);
    font-size: var(--text-lg);
    /* Money is neutral and strong, never a brand colour. Same law as
       .tile__price in app.css. */
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

.morph__fakebtn {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 44px;
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--accent-on);
    background: var(--accent);
    border-radius: var(--radius-pill);
}

/* --- frame 3: an order in the panel ------------------------------------- */

.morph__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-top: var(--space-2);
}

/* The quiet label a frame puts in its head strip when the head is not a
   profile row - the shopfront's own name, the panel's own section. */
.morph__crumb {
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--text-muted);
}

.morph__ref {
    font-size: var(--text-sm);
    color: var(--text-muted);
    font-variant-numeric: tabular-nums;
}

.morph__paid {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    padding: var(--space-1) var(--space-3);
    font-size: var(--text-xs);
    font-weight: 700;
    color: var(--ok-ink);
    background: rgb(var(--ok-rgb) / .12);
    border-radius: var(--radius-pill);
}

@media (max-width: 700px) {
    /*
    | The cheaper mobile version, and it keeps all three states.
    |
    | The travel is shorter and the sticky stage is smaller, so the whole
    | sequence costs one screen of scrolling instead of two and a half. The
    | cross fade is the same; what is dropped is the distance, not the idea.
    */
    .morph__track {
        height: 190vh;
    }

    .morph__stage {
        top: 72px;
        height: min(80vh, 560px);
    }

    .morph__frames {
        --morph-foot: 118px;
    }
}

/* ==========================================================================
   7. FEATURE / SHOWCASE / HONESTY
   ========================================================================== */

.feature {
    display: flex;
    gap: var(--space-4);
    align-items: flex-start;
}

.feature__icon {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 44px;
    height: 44px;
    border-radius: var(--radius-sm);
    background: rgb(var(--sky-rgb) / .14);
}

.feature__icon svg {
    width: 22px;
    height: 22px;
    stroke: var(--accent-ink);
    fill: none;
    stroke-width: 1.7;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.feature__body h3 {
    margin: 0 0 var(--space-2);
    font-size: var(--text-lg);
    line-height: var(--lh-snug);
}

.feature__body p {
    margin: 0;
    color: var(--text-muted);
    line-height: var(--lh-relaxed);
}

/*
| A screenshot of the real product. The frame is a browser-ish chrome so a
| flat PNG does not read as part of the page.
*/
.shot {
    overflow: hidden;
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-2);
}

.shot__chrome {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-4);
    background: var(--surface);
    border-bottom: 1px solid var(--border);
}

.shot__dot {
    width: 9px;
    height: 9px;
    border-radius: var(--radius-pill);
    background: var(--border-strong);
}

.shot__img {
    display: block;
    width: 100%;
    height: auto;
    background: var(--surface-sunken);
}

.shot__cap {
    margin: var(--space-3) 0 0;
    font-size: var(--text-sm);
    color: var(--text-muted);
    text-align: center;
}

/*
| The honesty block. Deliberately the plainest thing on the page: no icon
| grid, no colour, no ornament. It is where the product says what stage it is
| at, and decoration on that sentence would undercut it.
*/
.honest {
    padding: var(--space-8);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-lg);
}

.honest h3 {
    margin: 0 0 var(--space-3);
    font-size: var(--text-lg);
}

.honest p {
    margin: 0 0 var(--space-4);
    color: var(--text-muted);
    line-height: var(--lh-relaxed);
}

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

.honest__list {
    margin: 0;
    padding: 0;
    list-style: none;
}

.honest__list li {
    position: relative;
    padding-inline-start: var(--space-6);
    padding-block: var(--space-2);
    color: var(--text-muted);
    line-height: var(--lh-relaxed);
}

/* The tick is drawn with a border rather than a glyph so it cannot be read
   aloud as a character and cannot be mistaken for an emoji. */
.honest__list li::before {
    content: '';
    position: absolute;
    inset-inline-start: 2px;
    top: 1.05em;
    width: 10px;
    height: 6px;
    border-inline-start: 2px solid var(--ok-ink);
    border-bottom: 2px solid var(--ok-ink);
    transform: rotate(-45deg);
}

/* ==========================================================================
   8. CTA BAND
   ========================================================================== */

.ctaband {
    text-align: center;
    background:
        radial-gradient(60% 120% at 50% 0%,
            rgb(var(--sky-rgb) / .16) 0%,
            rgb(var(--sky-rgb) / 0) 70%),
        var(--surface-wash);
}

.ctaband__title {
    max-width: 24ch;
    margin: 0 auto var(--space-4);
    font-size: var(--text-3xl);
    line-height: var(--lh-snug);
    text-wrap: balance;
}

.ctaband__lead {
    max-width: 46ch;
    margin: 0 auto var(--space-8);
    color: var(--text-muted);
    line-height: var(--lh-relaxed);
}

/* ==========================================================================
   9. FOOTER
   ========================================================================== */

.sitefoot {
    padding-block: var(--space-16) var(--space-10);
    background: var(--bg);
    border-top: 1px solid var(--border);
}

.sitefoot__top {
    display: grid;
    gap: var(--space-10);
    margin-bottom: var(--space-12);
}

@media (min-width: 760px) {
    .sitefoot__top {
        grid-template-columns: 1.4fr 1fr 1fr;
    }
}

.sitefoot__about p {
    max-width: 34ch;
    margin: var(--space-3) 0 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
    line-height: var(--lh-relaxed);
}

.sitefoot__title {
    margin: 0 0 var(--space-4);
    font-family: var(--font-heading);
    font-size: var(--text-sm);
    font-weight: 700;
    color: var(--text);
}

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

.sitefoot__list a {
    color: var(--text-muted);
    font-size: var(--text-sm);
}

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

.sitefoot__bottom {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding-top: var(--space-6);
    border-top: 1px solid var(--border);
    color: var(--text-faint);
    font-size: var(--text-xs);
}

.sitefoot__social {
    display: flex;
    gap: var(--space-2);
}

.sitefoot__social a {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border: 1px solid var(--border);
    border-radius: var(--radius-pill);
    color: var(--text-muted);
    transition: border-color var(--fast) var(--ease),
                color var(--fast) var(--ease);
}

.sitefoot__social a:hover {
    border-color: var(--accent);
    color: var(--accent-ink);
}

.sitefoot__social svg {
    width: 18px;
    height: 18px;
    stroke: currentColor;
    fill: none;
    stroke-width: 1.7;
}

/* ==========================================================================
   10. ACCORDION
   ========================================================================== */

/*
| Built on <details>/<summary>, so it opens with the keyboard, is announced
| correctly and works with JavaScript off. The only thing scripted about it is
| nothing at all.
*/
.acc {
    display: grid;
    gap: var(--space-3);
}

.acc__item {
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    overflow: hidden;
    transition: border-color var(--fast) var(--ease),
                box-shadow var(--fast) var(--ease);
}

.acc__item[open] {
    border-color: var(--border-strong);
    box-shadow: var(--shadow-1);
}

.acc__q {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    padding: var(--space-5) var(--space-5);
    min-height: 44px;
    font-weight: 700;
    line-height: var(--lh-normal);
    cursor: pointer;
    list-style: none;
}

.acc__q::-webkit-details-marker {
    display: none;
}

.acc__q:hover {
    background: var(--surface);
}

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

/*
| The chevron. Drawn as a rotated border rather than a glyph, and it is
| mirrored by the writing direction automatically because the box it sits in
| is laid out with logical properties.
*/
.acc__mark {
    flex: 0 0 auto;
    width: 10px;
    height: 10px;
    border-inline-end: 2px solid var(--ink-400);
    border-bottom: 2px solid var(--ink-400);
    transform: rotate(45deg) translate(-2px, -2px);
    transition: transform var(--fast) var(--ease);
}

.acc__item[open] .acc__mark {
    transform: rotate(-135deg) translate(-2px, -2px);
}

.acc__a {
    padding: 0 var(--space-5) var(--space-5);
    color: var(--text-muted);
    line-height: var(--lh-relaxed);
}

.acc__a p {
    margin: 0 0 var(--space-3);
}

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

/* ==========================================================================
   11. PRICING TABLE
   ========================================================================== */

.plans {
    display: grid;
    gap: var(--space-6);
    align-items: start;
}

@media (min-width: 860px) {
    .plans {
        grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    }
}

.plan {
    display: flex;
    flex-direction: column;
    padding: var(--space-8);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-1);
}

.plan--featured {
    border-color: var(--accent);
    box-shadow: var(--shadow-3);
}

.plan__name {
    margin: 0 0 var(--space-2);
    font-size: var(--text-xl);
}

.plan__for {
    margin: 0 0 var(--space-6);
    color: var(--text-muted);
    font-size: var(--text-sm);
    line-height: var(--lh-relaxed);
    min-height: 3.2em;
}

.plan__price {
    font-family: var(--font-heading);
    font-size: var(--text-3xl);
    line-height: var(--lh-tight);
    color: var(--text);
    font-variant-numeric: tabular-nums;
}

/*
| The empty slot.
|
| The numbers for this page do not exist yet, and the instruction is explicit:
| an empty slot stays visibly empty rather than being filled with a plausible
| sample. This is what visibly empty looks like - a dashed placeholder that
| nobody could mistake for a price.
*/
.plan__price--tbd {
    display: inline-flex;
    align-items: center;
    min-height: 1.6em;
    padding: var(--space-1) var(--space-5);
    font-family: var(--font-body);
    font-size: var(--text-base);
    font-weight: 700;
    color: var(--text-faint);
    border: 2px dashed var(--border-strong);
    border-radius: var(--radius-sm);
}

.plan__unit {
    margin: var(--space-2) 0 var(--space-6);
    color: var(--text-faint);
    font-size: var(--text-sm);
    min-height: 1.6em;
}

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

.plan__list li {
    position: relative;
    padding-inline-start: var(--space-6);
    color: var(--text-muted);
    font-size: var(--text-sm);
    line-height: var(--lh-relaxed);
}

.plan__list li::before {
    content: '';
    position: absolute;
    inset-inline-start: 2px;
    top: .62em;
    width: 9px;
    height: 5px;
    border-inline-start: 2px solid var(--ok-ink);
    border-bottom: 2px solid var(--ok-ink);
    transform: rotate(-45deg);
}

.plan__foot {
    margin-top: auto;
}

/* ==========================================================================
   12. DOCUMENT PAGES (terms, privacy, about)
   ========================================================================== */

.doc {
    display: grid;
    gap: var(--space-10);
}

@media (min-width: 960px) {
    .doc {
        grid-template-columns: 240px 1fr;
        align-items: start;
    }

    .doc__toc {
        position: sticky;
        top: 96px;
    }
}

.doc__toc {
    padding: var(--space-5);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
}

.doc__toc h2 {
    margin: 0 0 var(--space-4);
    font-size: var(--text-sm);
}

.doc__toc ol {
    margin: 0;
    padding: 0;
    list-style: none;
    display: grid;
    gap: var(--space-2);
    counter-reset: toc;
}

.doc__toc li {
    counter-increment: toc;
}

.doc__toc a {
    display: block;
    padding: var(--space-1) 0;
    color: var(--text-muted);
    font-size: var(--text-sm);
    line-height: var(--lh-normal);
}

.doc__toc a::before {
    content: counter(toc) '. ';
    color: var(--text-faint);
    font-variant-numeric: tabular-nums;
}

.doc__body {
    min-width: 0;
    max-width: 68ch;
}

.doc__body h2 {
    /* Anchored headings need room under the sticky header or the link jumps
       to a title hidden behind it. */
    scroll-margin-top: 96px;
    margin: var(--space-12) 0 var(--space-4);
    font-size: var(--text-2xl);
    line-height: var(--lh-snug);
}

.doc__body h2:first-child {
    margin-top: 0;
}

.doc__body h3 {
    margin: var(--space-8) 0 var(--space-3);
    font-size: var(--text-lg);
}

.doc__body p,
.doc__body li {
    line-height: var(--lh-relaxed);
    color: var(--text-muted);
}

.doc__body ul,
.doc__body ol {
    padding-inline-start: var(--space-6);
    display: grid;
    gap: var(--space-2);
}

/*
| The slot a legal text has not arrived for yet.
|
| Same principle as the empty price: it says out loud that it is empty. It is
| not lorem ipsum and it is not an invented clause, because a plausible-looking
| terms page is worse than an obviously unfinished one.
*/
.doc__pending {
    display: grid;
    gap: var(--space-3);
    padding: var(--space-6);
    color: var(--text-muted);
    background: var(--surface);
    border: 2px dashed var(--border-strong);
    border-radius: var(--radius-md);
    line-height: var(--lh-relaxed);
}

/* Takes bare text or paragraphs. With paragraphs the gap above does the
   spacing, so the UA margins have to go or the box pads unevenly. */
.doc__pending > p {
    margin: 0;
}

.doc__meta {
    margin: 0 0 var(--space-10);
    color: var(--text-faint);
    font-size: var(--text-sm);
}

/* ==========================================================================
   13. CONTACT
   ========================================================================== */

.contact {
    display: grid;
    gap: var(--space-4);
}

.contact__row {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    padding: var(--space-5);
    background: var(--bg);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
}

.contact__row--empty {
    background: var(--surface);
    border-style: dashed;
    border-color: var(--border-strong);
}

.contact__label {
    font-weight: 700;
    font-size: var(--text-sm);
}

.contact__value {
    margin-inline-start: auto;
    color: var(--text-muted);
    font-size: var(--text-sm);
}

.contact__value--ltr {
    direction: ltr;
    text-align: left;
    font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   14. ERROR PAGES
   ========================================================================== */

.oops {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: 70vh;
    text-align: center;
}

.oops__code {
    font-family: var(--font-heading);
    font-size: var(--text-5xl);
    line-height: 1;
    color: var(--border-strong);
    font-variant-numeric: tabular-nums;
}

.oops__title {
    margin: var(--space-5) 0 var(--space-3);
    font-size: var(--text-2xl);
}

.oops__text {
    max-width: 40ch;
    margin: 0 0 var(--space-8);
    color: var(--text-muted);
    line-height: var(--lh-relaxed);
}

.oops__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: var(--space-3);
}

/* ==========================================================================
   15. STATE COMPONENTS — skeleton, alert
   ========================================================================== */

/*
| The loading state is a grey skeleton, never a spinner in the middle of a
| page: a skeleton keeps the layout at its final size, so nothing jumps when
| the content lands.
*/
.skel {
    background: var(--surface-sunken);
    border-radius: var(--radius-xs);
    background-image: linear-gradient(
        90deg,
        rgb(var(--paper-rgb) / 0) 0%,
        rgb(var(--paper-rgb) / .65) 50%,
        rgb(var(--paper-rgb) / 0) 100%);
    background-size: 200% 100%;
    background-repeat: no-repeat;
    animation: skel-sweep 1.2s linear infinite;
}

@keyframes skel-sweep {
    from { background-position: 200% 0; }
    to   { background-position: -200% 0; }
}

.skel--line { height: 12px; margin-bottom: var(--space-3); }
.skel--title { height: 20px; width: 55%; margin-bottom: var(--space-4); }
.skel--short { width: 62%; }
.skel--block { height: 180px; border-radius: var(--radius-md); }

.alert {
    display: flex;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    border: 1px solid var(--border);
    border-radius: var(--radius-md);
    line-height: var(--lh-relaxed);
    background: var(--bg);
}

.alert--bad {
    color: var(--text);
    border-color: rgb(var(--bad-rgb) / .35);
    background: rgb(var(--bad-rgb) / .07);
}

.alert--ok {
    border-color: rgb(var(--ok-rgb) / .35);
    background: rgb(var(--ok-rgb) / .07);
}

.alert--note {
    border-color: rgb(var(--warn-rgb) / .35);
    background: rgb(var(--warn-rgb) / .07);
}

/*
| .alert is a flex ROW so an icon can sit beside one line of text. When the
| message is more than one sentence it arrives as paragraphs instead, and three
| <p> in a flex row is not a paragraph - it is three columns. This says out
| loud which of the two an alert is.
|
| Deliberately a modifier and not `.alert:has(> p)`: :has() is fine on a recent
| browser and absent on the older Android this audience actually holds, and the
| failure mode there would be the broken three-column layout on a page about
| money. A class costs one word and cannot regress.
*/
.alert--stack {
    flex-direction: column;
    gap: var(--space-2);
}

/* The padding is the padding: UA margins on the first and last child would
   add to it unevenly. */
.alert--stack > p {
    margin: 0;
}

/* ==========================================================================
   16. SCROLL REVEAL
   ========================================================================== */

/*
| One-shot only. site.js adds .is-in the first time an element crosses into
| view and then stops observing it, so nothing re-animates on the way back up
| - a section that fades every time you scroll past it is a section you cannot
| re-read.
|
| The starting state is applied only when the script is present. Without
| JavaScript the html element never gets .js and every one of these is simply
| visible, which is why the page is readable with scripting off.
*/
.js .reveal {
    opacity: 0;
    transform: translateY(14px);
    transition: opacity var(--slower) var(--ease-out),
                transform var(--slower) var(--ease-out);
}

.js .reveal.is-in {
    opacity: 1;
    transform: none;
}

/* A short stagger for the children of a revealed group. Four steps is enough;
   past that the last card arrives late enough to feel broken. */
.js .reveal--d1 { transition-delay: 60ms; }
.js .reveal--d2 { transition-delay: 120ms; }
.js .reveal--d3 { transition-delay: 180ms; }

/* ==========================================================================
   17. REDUCED MOTION
   ========================================================================== */

/*
| The single switch. Everything this file animates is listed here and turned
| off - the drifting background, the reveal, the morph cross fade, the
| skeleton sweep and the menu.
|
| Turning motion off must never turn CONTENT off: every rule below leaves the
| element visible in its final state.
*/
@media (prefers-reduced-motion: reduce) {
    .aura__blob {
        animation: none;
    }

    .skel {
        animation: none;
        background-image: none;
    }

    .sitemenu {
        animation: none;
    }

    .js .reveal,
    .js .reveal.is-in {
        opacity: 1;
        transform: none;
        transition: none;
    }

    .morph__frame {
        transition: none;
    }

    .card--lift:hover {
        transform: none;
    }

    .band .btn:hover:not(:disabled),
    .sitehead .btn:hover:not(:disabled),
    .band .btn:active:not(:disabled),
    .sitehead .btn:active:not(:disabled) {
        transform: none;
    }
}

/* ==========================================================================
   18. PRINT
   ========================================================================== */

@media print {
    .sitehead,
    .sitefoot__social,
    .aura,
    .ctaband {
        display: none;
    }

    .doc {
        display: block;
    }

    .doc__toc {
        display: none;
    }
}
