/* ============================================================
   pages.css — Standalone page overrides (landing, login)
   ============================================================ */

/* ── Centered full-screen pages ── */
.page-centered {
    min-height: 100vh;
    background: #f3f4f6;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
}

/* ── Auth / landing card ── */
.auth-card {
    background: var(--color-surface);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-md);
    padding: var(--space-8);
    width: 100%;
    max-width: 384px;
}

.auth-card__title {
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--color-text);
    text-align: center;
    margin-bottom: var(--space-2);
}

.auth-card__title a {
    color: inherit;
    text-decoration: none;
    transition: color var(--transition);
}

.auth-card__title a:hover {
    color: var(--color-text-muted);
}

.auth-card__tagline {
    font-size: 0.875rem;
    color: var(--color-text-muted);
    text-align: center;
    margin-bottom: var(--space-8);
}

/* Label above the studio list shown on the home page to a signed-in user who has no
   dashboard on THIS host (apex, or a studio they don't belong to). */
.auth-card__hint {
    font-size: 0.8125rem;
    color: var(--color-text-muted);
    text-align: center;
    margin-bottom: var(--space-2);
}

/* Links inside the auth card must LOOK like links. base.css resets every <a> to
   `color: inherit; text-decoration: none`, which rendered the signup consent links
   ("Terms & Conditions", "Privacy Policy", "Liability Waiver") as plain text — a
   member cannot read a document they cannot tell is clickable, and that is the whole
   point of the consent checkbox next to it (UAT-AUTH-001a). Scoped to the card, so
   the brand lockup keeps its own `.auth-card__title a` treatment (higher specificity).

   `:not(.btn)` is load-bearing, not tidiness. `.auth-card a` (0,1,1) outranks
   `.btn--primary` (0,1,0), so it also repainted the anchors that are BUTTONS — giving
   the "Sign in" and "Dashboard" buttons `color: var(--color-primary)` on top of the
   `background: var(--color-primary)` they already had. Text and background became the
   same colour and the label vanished, on every tenant and on the apex, because both
   sides of that collision read the same variable. A button is not a link: it already
   signals affordance with its fill, so it must keep its own contrast-safe foreground
   (`--color-on-primary`, picked by ReadableTextColor). */
.auth-card a:not(.btn) {
    color: var(--color-primary);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.auth-card a:not(.btn):hover {
    color: var(--color-primary-hover);
}

/* Login form inside auth card: remove surface padding, tighten spacing */
.auth-card .form {
    padding: 0;
    gap: var(--space-4);
}

.auth-card .form__actions {
    border-top: none;
    padding-top: 0;
    margin-top: var(--space-2);
}

/* ── Settings page ── */
.settings-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
}

.settings-section__body {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    padding: var(--space-5) var(--space-6);
}

.settings-form .form__actions {
    padding: 0;
    border-top: none;
}

/* Logo preview */
.logo-preview-wrapper {
    width: 300px;
    height: 300px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    overflow: hidden;
    margin-bottom: var(--space-3);
}

.logo-preview__img {
    width: 300px;
    height: 300px;
    object-fit: contain;
    display: block;
}

.logo-preview__placeholder {
    width: 100%;
    height: 100%;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-2);
    background: #f9fafb;
    color: var(--color-text-subtle);
}

.logo-preview__icon {
    font-size: 2.5rem;
}

.logo-preview__label {
    font-size: 0.875rem;
}

/* Color picker + text input pair */
.color-input-wrapper {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.color-input__swatch {
    width: 40px;
    height: 38px;
    padding: 2px;
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    cursor: pointer;
    flex-shrink: 0;
    background: var(--color-surface);
}

/* Hint text below inputs */
.form__hint {
    font-size: 0.75rem;
    color: var(--color-text-subtle);
    margin-top: var(--space-1);
}

/* ============================================================
   .site-* — the APEX marketing surface (templates/site/, templates/landing/)
   ============================================================

   Tokens only: every colour, radius, shadow and space below reads a custom
   property already declared in base.css. No new hard-coded colour, no new font
   stack, no third-party anything.

   No dark theme, on purpose. Public pages never load dark.css — only
   templates/dashboard/layout.html.twig does — so adding one here would mean a
   sixth stylesheet on every public request, which is a separate decision.
   ============================================================ */

.site-body {
    background: var(--color-surface);
}

.site-container {
    width: 100%;
    max-width: 1120px;
    margin: 0 auto;
    padding: 0 var(--space-5);
}

/* ~42rem measure for prose. Long lines are the single biggest readability
   failure on a legal page, which is the page most likely to be read in full. */
.site-container--narrow {
    max-width: 42rem;
}

/* ── Sections ── */
/* 32px on a phone, 48px from 720px up — 2026-10-17, the homepage was still long on mobile. */
.site-section {
    padding: var(--space-8) 0;
}

@media (min-width: 720px) {
    .site-section {
        padding: var(--space-12) 0;
    }
}

/* Alternating band, so sections separate without a rule line. */
.site-section--panel {
    background: var(--color-bg);
    border-top: 1px solid var(--color-border);
    border-bottom: 1px solid var(--color-border);
}

.site-section--tight {
    padding: var(--space-10) 0;
}

/* The closing band (templates/site/_cta.html.twig), in the logo blue on every page. */
.site-section--cta {
    background: var(--color-accent-blue-fg);
    color: var(--color-nav-active-text);
}

.site-section__head {
    max-width: 44rem;
    margin: 0 0 var(--space-8);
}

.site-section__title {
    font-size: 1.875rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.2;
    margin-bottom: var(--space-3);
}

.site-section__lead {
    color: var(--color-text-muted);
    font-size: 1.0625rem;
}

.site-eyebrow {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--color-text-subtle);
    margin-bottom: var(--space-2);
}

/* ── Header ── */
.site-header {
    position: sticky;
    top: 0;
    z-index: 20;
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
}

.site-header__inner {
    display: flex;
    align-items: center;
    gap: var(--space-6);
    height: var(--header-height);
}

.site-header__brand {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-weight: 700;
    letter-spacing: -0.02em;
    flex-shrink: 0;
}

.site-header__nav {
    display: none;
    gap: var(--space-5);
    font-size: 0.875rem;
    color: var(--color-text-muted);
}

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

.site-header__actions {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    margin-left: auto;
}

.site-header__signin {
    font-size: 0.875rem;
    color: var(--color-text-muted);
}

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

/* CSS-only responsive, still no JavaScript. Below 900px the inline nav is hidden and
   the same links live in a native <details> menu (.site-menu) — the browser opens and
   closes it, so none of the Turbo re-execution traps in note 16 apply. 900px, not the
   720px it used to be: five links plus the two buttons do not fit at 720. */
.site-menu {
    margin-left: calc(-1 * var(--space-3));
}

.site-menu__toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-md);
    color: var(--color-text);
    cursor: pointer;
    list-style: none;
}

/* The disclosure triangle, in both engines that draw one. */
.site-menu__toggle::-webkit-details-marker {
    display: none;
}

.site-menu__toggle::marker {
    content: '';
}

.site-menu__toggle:hover,
.site-menu[open] .site-menu__toggle {
    background: var(--color-bg);
}

/* Anchored to .site-header, which is position: sticky and therefore the containing block. */
.site-menu__panel {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    display: flex;
    flex-direction: column;
    padding: var(--space-2) var(--space-4) var(--space-4);
    background: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    box-shadow: var(--shadow-md);
}

.site-menu__panel a {
    padding: var(--space-3) 0;
    font-weight: 500;
    color: var(--color-text);
    border-bottom: 1px solid var(--color-border);
}

.site-menu__panel a:last-child {
    border-bottom: 0;
}

@media (min-width: 900px) {
    .site-header__nav {
        display: flex;
    }

    .site-menu {
        display: none;
    }
}

/* The "see all" link under a homepage section that continues on its own page. */
.site-section__more {
    margin-top: var(--space-6);
    text-align: center;
    font-weight: 500;
}

/* The opening block of a marketing sub-page (/features, /pricing, /why-gopractice, /faq):
   the h1 and lead, shorter than the homepage hero because the content follows at once. */
.site-section--page-head {
    padding: var(--space-10) 0 var(--space-8);
}

/* ── Hero ── */
.site-section--hero {
    padding: var(--space-10) 0 var(--space-8);
}

@media (min-width: 720px) {
    .site-section--hero {
        padding: var(--space-12) 0;
    }
}

.site-hero {
    display: grid;
    gap: var(--space-10);
    grid-template-columns: 1fr;
    align-items: center;
}

.site-hero__copy {
    max-width: 40rem;
}

/* The diagram is supporting material, so it takes the smaller share and drops out of the
   flow entirely below 900px rather than shrinking to something illegible. The copy and the
   CTA are the page; the picture never competes with them for space on a phone. */
@media (min-width: 900px) {
    .site-hero {
        grid-template-columns: 1.15fr 0.85fr;
    }
}

.site-hero__figure {
    display: none;
}

@media (min-width: 900px) {
    .site-hero__figure {
        display: block;
    }
}

.site-hero__figure svg {
    width: 100%;
    height: auto;
}

/* Abstract shapes only — see the template comment. Tokens, so it recolours with the rest
   of the page and can never drift into looking like a real screenshot. */
.site-fig__card {
    fill: var(--color-surface);
    stroke: var(--color-border);
}

.site-fig__bar {
    fill: var(--color-nav-bg);
}

.site-fig__dot {
    fill: var(--color-accent-green-fg);
}

.site-fig__row {
    fill: var(--color-bg);
    stroke: var(--color-border);
}

.site-fig__row--active {
    fill: var(--color-accent-green-bg);
    stroke: var(--color-accent-green-fg);
}

.site-fig__ghost {
    fill: var(--color-text-subtle);
}

.site-fig__ghost--faint {
    opacity: 0.5;
}

.site-fig__pill {
    fill: var(--color-border);
}

.site-fig__check-bg {
    fill: var(--color-accent-green-fg);
}

.site-fig__check {
    fill: none;
    stroke: var(--color-surface);
    stroke-width: 2.5;
    stroke-linecap: round;
    stroke-linejoin: round;
}

.site-hero__title {
    font-size: 2.25rem;
    font-weight: 700;
    letter-spacing: -0.03em;
    line-height: 1.1;
    margin-bottom: var(--space-4);
}

@media (min-width: 720px) {
    .site-hero__title {
        font-size: 3rem;
    }
}

.site-hero__lead {
    font-size: 1.125rem;
    color: var(--color-text-muted);
    margin-bottom: var(--space-6);
}

.site-hero__actions {
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-3);
    margin-bottom: var(--space-8);
}

.site-hero__points {
    display: grid;
    gap: var(--space-2);
    font-size: 0.9375rem;
    color: var(--color-text-muted);
}

.site-hero__points li {
    padding-left: var(--space-6);
    position: relative;
}

/* Inline content marker rather than an icon font or an SVG sprite — a check glyph
   needs no network request and no third-party origin. */
.site-hero__points li::before {
    content: "\2713";
    position: absolute;
    left: 0;
    color: var(--color-accent-green-fg);
    font-weight: 700;
}

/* ── The 0% commission claim ── */
.site-claim {
    background: var(--color-accent-green-bg);
    border-top: 1px solid var(--color-border);
    border-bottom: 1px solid var(--color-border);
}

.site-claim__inner {
    display: flex;
    flex-direction: column;
    gap: var(--space-4);
    align-items: flex-start;
}

@media (min-width: 720px) {
    .site-claim__inner {
        flex-direction: row;
        gap: var(--space-8);
        align-items: baseline;
    }
}

/* The figure is the whole point of the band, so it is sized like a headline rather than
   like body copy. `aria-hidden` in the markup, with the value repeated inside the <h2>
   for screen readers — otherwise the heading announces as a sentence fragment. */
.site-claim__figure {
    font-size: 3.5rem;
    font-weight: 700;
    line-height: 1;
    letter-spacing: -0.04em;
    color: var(--color-accent-green-fg);
    flex-shrink: 0;
}

.site-claim__text {
    max-width: 44rem;
}

.site-claim__title {
    font-size: 1.375rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    margin-bottom: var(--space-2);
}

.site-claim__body {
    color: var(--color-text-muted);
    margin-bottom: var(--space-3);
}

/* The Mollie disclosure. Smaller than the claim it qualifies, but deliberately NOT
   fine print: it is normal readable body text, because it is what makes the headline
   checkable rather than a thing a prospect discovers on their first invoice. */
.site-claim__note {
    font-size: 0.875rem;
    color: var(--color-text-muted);
    border-left: 3px solid var(--color-accent-green-fg);
    padding-left: var(--space-4);
}

/* Screen-reader-only text. Not `display:none`, which removes it from the accessibility
   tree as well as from the page. */
.site-visually-hidden {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
}

/* ── Grids + cards ── */
.site-grid {
    display: grid;
    gap: var(--space-5);
    grid-template-columns: 1fr;
}

@media (min-width: 640px) {
    .site-grid--3,
    .site-grid--4 {
        grid-template-columns: repeat(2, minmax(0, 1fr));
    }
}

@media (min-width: 1024px) {
    .site-grid--3 {
        grid-template-columns: repeat(3, minmax(0, 1fr));
    }

    .site-grid--4 {
        grid-template-columns: repeat(4, minmax(0, 1fr));
    }
}

.site-card {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: var(--space-6);
}

/* Icon chip on a card. Sized in em so it tracks the card's own type scale, and it holds
   its colour rather than the muted body colour — the icon is the thing giving the section
   visual rhythm, so it must not read as disabled. */
.site-card__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    margin-bottom: var(--space-4);
    border-radius: var(--radius-md);
    background: var(--color-accent-indigo-bg);
    color: var(--color-accent-indigo-fg);
}

.site-icon {
    width: 22px;
    height: 22px;
}

.site-card__title {
    font-size: 1.0625rem;
    font-weight: 600;
    margin-bottom: var(--space-2);
}

.site-card__body {
    font-size: 0.9375rem;
    color: var(--color-text-muted);
}

/* ── Pricing ── */
.site-price {
    position: relative;
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    padding: var(--space-4);
}

/* Two price cards per row even on a phone: each is short (name, price, limits), so 2×2
   halves the height of the price ladder. 2026-10-17. */
@media (max-width: 639px) {
    .site-grid--4 {
        grid-template-columns: repeat(2, minmax(0, 1fr));
        gap: var(--space-3);
    }

    .site-price__amount {
        font-size: 1.375rem;
    }

    .site-price__limits {
        font-size: 0.8125rem;
    }
}

@media (min-width: 640px) {
    .site-price {
        padding: var(--space-6);
    }
}

.site-price--featured {
    border-color: var(--color-primary);
    box-shadow: var(--shadow-md);
}

.site-price__badge {
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-accent-indigo-fg);
}

.site-price__name {
    font-size: 1rem;
    font-weight: 600;
}

.site-price__amount {
    font-size: 1.75rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.2;
}

/* "excl. BTW" is normal readable text beside the price, deliberately not fine
   print: every customer is a business, and a price a business buyer has to
   re-derive is a price that gets re-negotiated at the first invoice. */
.site-price__vat {
    display: block;
    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: 0;
    color: var(--color-text-muted);
}

.site-price__limits {
    font-size: 0.875rem;
    font-weight: 500;
}

/* One feature list under the price cards, shared by all four tiers (site.pricing.includes). */
.site-pricing__includes {
    margin-top: var(--space-5);
    color: var(--color-text-muted);
}

/* The commission callout under the price cards. Green like the claim band above, so a
   reader who scrolled straight to pricing recognises it as the same promise rather than
   a new one — and so the two can never drift into saying different things. */
.site-pricing__commission {
    margin-top: var(--space-6);
    padding: var(--space-5);
    background: var(--color-accent-green-bg);
    border: 1px solid var(--color-accent-green-fg);
    border-radius: var(--radius-md);
}

.site-pricing__commission-title {
    font-weight: 700;
    color: var(--color-accent-green-fg);
    margin-bottom: var(--space-2);
}

.site-pricing__commission-body {
    font-size: 0.9375rem;
    color: var(--color-text-muted);
}

.site-pricing__rule {
    margin-top: var(--space-6);
    padding: var(--space-4) var(--space-5);
    background: var(--color-accent-blue-bg);
    color: var(--color-accent-blue-fg);
    border-radius: var(--radius-md);
    font-weight: 600;
}

/* Optional paid services. Neutral rather than green: the green blocks on this page all
   say "this costs you nothing", and this one says the opposite, so it must not borrow
   their visual promise. */
.site-pricing__services {
    margin-top: var(--space-5);
    padding: var(--space-5);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
}

.site-pricing__services-title {
    font-size: 1rem;
    font-weight: 600;
    margin-bottom: var(--space-2);
}

.site-pricing__services-body {
    font-size: 0.9375rem;
    color: var(--color-text-muted);
    margin-bottom: var(--space-3);
}

.site-pricing__note {
    margin-top: var(--space-3);
    font-size: 0.875rem;
    color: var(--color-text-muted);
}

/* The entry point into /pricing/calculator, sitting under the tier rule.

   A real button rather than a text link, and centred rather than left-aligned: the rule above
   it ends on a question the reader is now asking about their own studio, and a body-weight link
   loses that moment to the next paragraph. */
.site-pricing__calculator {
    margin-top: var(--space-4);
}

/* The entry point into /pricing/calculator.

   A CARD rather than a .btn, and that is the design: every other button on this page is a pitch
   ("Talk to us"), and sharing their shape made the one interactive element in the section read as
   a third CTA. A tool should not look like a sales button. This is the only element of this shape
   on the page. It also cannot reuse .btn, which sets `white-space: nowrap` — right for "Save",
   wrong for a sentence, which would overflow the card on a 320px screen. */
.calc-cta {
    display: flex;
    align-items: center;
    gap: var(--space-4);
    max-width: 32rem;
    margin-inline: auto;
    padding: var(--space-4) var(--space-5);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    color: var(--color-text);
    text-decoration: none;
    transition: border-color var(--transition), box-shadow var(--transition), transform var(--transition);
}

.calc-cta:hover,
.calc-cta:focus-visible {
    border-color: var(--color-primary);
    box-shadow: var(--shadow-md);
    transform: translateY(-1px);
}

.calc-cta__icon {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-md);
    background: var(--color-accent-indigo-bg);
    font-size: 1.25rem;
}

.calc-cta__label {
    flex: 1;
    min-width: 0;
    font-weight: 600;
    text-align: left;
}

.calc-cta__chevron {
    flex-shrink: 0;
    font-size: 1.5rem;
    line-height: 1;
    color: var(--color-text-subtle);
    transition: transform var(--transition);
}

.calc-cta:hover .calc-cta__chevron,
.calc-cta:focus-visible .calc-cta__chevron {
    transform: translateX(3px);
}

@media (prefers-reduced-motion: reduce) {
    .calc-cta,
    .calc-cta__chevron {
        transition: none;
    }

    .calc-cta:hover,
    .calc-cta:focus-visible,
    .calc-cta:hover .calc-cta__chevron,
    .calc-cta:focus-visible .calc-cta__chevron {
        transform: none;
    }
}

/* ── The PUBLIC pricing calculator (/pricing/calculator) ──
   Tier + price only. It reuses the pricing section's own .site-price card so the answer looks
   like the table it came from; these rules are everything it adds. The fee-comparison rules that
   used to live here moved to the admin block below when the comparison did. */
.calc-tier {
    margin-top: var(--space-4);
}

/* The 0% commission callout, repeated on the calculator's result. Same green as the pricing
   section's, so a reader arriving from that page recognises it as the same promise rather than a
   new one. */
.calc-commission {
    margin-top: var(--space-6);
    padding: var(--space-5);
    background: var(--color-accent-green-bg);
    border: 1px solid var(--color-accent-green-fg);
    border-radius: var(--radius-md);
}

.calc-commission__title {
    font-weight: 700;
    color: var(--color-accent-green-fg);
    margin-bottom: var(--space-2);
}

.calc-commission__body {
    font-size: 0.9375rem;
    color: var(--color-text-muted);
}

/* The "Custom tier — talk to us" panel. Neutral rather than green: the green blocks on this site
   all say "this costs you nothing", and this one says something else. */
.calc-scope {
    margin-top: var(--space-6);
    padding: var(--space-5);
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
}

.calc-scope__title {
    font-size: 1.0625rem;
    font-weight: 600;
}

.calc-scope__body {
    font-size: 0.9375rem;
    color: var(--color-text-muted);
    margin-top: var(--space-2);
}

.calc-disclosure__vat {
    margin-top: var(--space-4);
    font-size: 0.8125rem;
    color: var(--color-text-subtle);
}

/* ── The ADMIN fee comparison (/dashboard/admin/pricing-calculator) ──
   Structure comes from the dashboard's own components — .page-header, .surface, .table, .flash —
   so the page reads like every other admin screen. Only the blocks below have no dashboard
   equivalent, and each is built from the same tokens as the components around it.

   ⚠️ EDIT THIS REGION BY ITS MARKERS, NOT BY A SPAN TO THE NEXT COMMENT. On 2026-09-09 an edit
   here replaced everything up to the next comment it found and silently deleted 297 lines — the
   contact form, the footer, the FAQ, the CTA, the legal prose, the notices, the WhatsApp button,
   .btn--lg and .btn--outline, all of which begin immediately below. No test caught it: the suite
   asserts MARKUP, and every page still returned 200 with its classes intact and no rules behind
   them. MarketingStylesheetCoverageTest now does. */
/* `.calc-bill` and `.calc-disclosure` deliberately have NO rules: they are structural wrappers
   around a .surface embed, and the tests filter on them. Do not delete them from the template
   because "the class does nothing" — it is doing the only job it has. */
.calc-admin-warning {
    margin-bottom: var(--space-5);
}

.calc-admin-result > * + * {
    margin-top: var(--space-5);
}

/* The resolved tier, inside a .surface body. */
.calc-tier__name {
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--color-text-muted);
}

.calc-tier__amount {
    font-size: 1.75rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    line-height: 1.2;
    margin-top: var(--space-1);
}

.calc-tier__vat {
    display: block;
    font-size: 0.8125rem;
    font-weight: 500;
    letter-spacing: 0;
    color: var(--color-text-muted);
}

/* ⚠️ THE HERO. Deliberately heavier than a .metric-card: it is the one number to lead a call
   with, and it must outrank the net difference in .calc-bill__net below, which dips at every tier
   step. Restyling this down — or .calc-bill__net up — is a sales regression, not a design change;
   AdminPricingCalculatorFramingTest fails the build on the class moving. */
.calc-hero {
    display: flex;
    align-items: flex-start;
    gap: var(--space-4);
    background: var(--color-accent-green-bg);
    border: 1px solid var(--color-accent-green-fg);
    border-radius: var(--radius-lg);
    box-shadow: var(--shadow-sm);
    padding: var(--space-5) var(--space-6);
}

.calc-hero__icon {
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    width: 40px;
    height: 40px;
    border-radius: var(--radius-md);
    background: var(--color-surface);
    font-size: 1.25rem;
}

.calc-hero__body {
    min-width: 0;
    flex: 1;
}

.calc-hero__eyebrow {
    font-size: 0.6875rem;
    font-weight: 700;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-accent-green-fg);
}

.calc-hero__figure {
    font-size: 2.25rem;
    font-weight: 800;
    letter-spacing: -0.03em;
    line-height: 1.15;
    color: var(--color-accent-green-fg);
    margin-top: var(--space-1);
}

.calc-hero__title {
    font-size: 1rem;
    font-weight: 700;
}

.calc-hero__year {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--color-text-muted);
    margin-top: var(--space-1);
}

.calc-hero__assumption {
    font-size: 0.8125rem;
    color: var(--color-text-subtle);
    margin-top: var(--space-3);
}

/* Footnote weight, and that is the point — see the block comment above. The bill itself is the
   dashboard's own .table, so it needs no rules here beyond the total row. */
.calc-bill__net-wrap {
    border-top: 1px solid var(--color-border);
}

.calc-bill__net {
    font-size: 0.875rem;
    color: var(--color-text-muted);
}

.calc-bill__total td {
    font-weight: 700;
}

.calc-next {
    padding: var(--space-4) var(--space-5);
    background: var(--color-accent-blue-bg);
    color: var(--color-accent-blue-fg);
    border-radius: var(--radius-lg);
}

.calc-next__title {
    font-weight: 700;
}

.calc-next__body {
    font-size: 0.9375rem;
    margin-top: var(--space-1);
}

.calc-disclosure__list {
    margin: 0 0 0 var(--space-5);
    padding: 0;
    font-size: 0.875rem;
    color: var(--color-text-muted);
}

.calc-disclosure__list li + li {
    margin-top: var(--space-2);
}

/* Two number inputs on a surface. The dashboard's only form chrome is the wizard's (wiz-*), and
   that belongs to the multi-step CREATE forms — its custom properties are declared on .wiz-root,
   so the classes do not survive outside it. These rules use the same tokens as their neighbours. */
.calc-form {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-end;
    gap: var(--space-4);
}

.calc-form__row {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    min-width: 10rem;
    flex: 1 1 10rem;
}

.calc-form__label {
    font-size: 0.875rem;
    font-weight: 500;
}

.calc-form__input {
    width: 100%;
    padding: var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--color-text);
}

.calc-form__input:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 1px;
}

/* ── FAQ ── */
.site-faq {
    display: grid;
    gap: var(--space-3);
}

.site-faq__item {
    background: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: var(--space-4) var(--space-5);
}

.site-faq__q {
    font-weight: 600;
    cursor: pointer;
    list-style: none;
}

.site-faq__q::-webkit-details-marker {
    display: none;
}

.site-faq__q::after {
    content: "+";
    float: right;
    color: var(--color-text-subtle);
    font-weight: 400;
}

.site-faq__item[open] > .site-faq__q::after {
    content: "\2212";
}

.site-faq__a {
    margin-top: var(--space-3);
    font-size: 0.9375rem;
    color: var(--color-text-muted);
}

/* ── Closing CTA ── */
.site-cta {
    text-align: center;
}

.site-cta__title {
    font-size: 1.75rem;
    font-weight: 700;
    letter-spacing: -0.02em;
    margin-bottom: var(--space-3);
}

/* Full white, not the grey nav text: white on #2563eb is 5.2:1, the grey drops below 4.5:1. */
.site-cta__lead {
    color: var(--color-nav-active-text);
    margin-bottom: var(--space-6);
}

/* White button with blue text — the near-black .btn--primary sits heavily on blue. */
.site-cta__button {
    background: var(--color-nav-active-text);
    color: var(--color-accent-blue-fg);
}

.site-cta__button:hover {
    background: var(--color-accent-blue-bg);
}

/* ── Prose (legal pages) ── */
.site-prose {
    font-size: 1rem;
    line-height: 1.7;
    color: var(--color-text);
}

.site-prose h2 {
    font-size: 1.125rem;
    font-weight: 600;
    margin: var(--space-8) 0 var(--space-3);
}

.site-prose p {
    margin-bottom: var(--space-4);
    color: var(--color-text-muted);
}

.site-prose ul {
    list-style: disc;
    padding-left: var(--space-6);
    margin-bottom: var(--space-4);
    color: var(--color-text-muted);
}

.site-prose li {
    margin-bottom: var(--space-2);
}

/* The legal pages are admin-written Markdown since 2026-10-15, so every element CommonMark
   (GitHub-flavoured) can emit needs a rule here — h3, ordered lists, tables, quotes. */
.site-prose h3 {
    font-size: 1rem;
    font-weight: 600;
    margin: var(--space-6) 0 var(--space-2);
}

.site-prose ol {
    list-style: decimal;
    padding-left: var(--space-6);
    margin-bottom: var(--space-4);
    color: var(--color-text-muted);
}

.site-prose strong {
    font-weight: 600;
    color: var(--color-text);
}

.site-prose blockquote {
    border-left: 3px solid var(--color-border);
    padding-left: var(--space-4);
    margin: 0 0 var(--space-4);
}

.site-prose hr {
    border: 0;
    border-top: 1px solid var(--color-border);
    margin: var(--space-8) 0;
}

/* A wide table scrolls inside itself; the page body never scrolls sideways on a phone. */
.site-prose table {
    display: block;
    overflow-x: auto;
    border-collapse: collapse;
    margin-bottom: var(--space-4);
    font-size: 0.9375rem;
    color: var(--color-text-muted);
}

.site-prose th,
.site-prose td {
    border: 1px solid var(--color-border);
    padding: var(--space-2) var(--space-3);
    text-align: left;
    vertical-align: top;
}

.site-prose th {
    font-weight: 600;
    color: var(--color-text);
    background: var(--color-surface);
}

/* /about: the story is read, not consulted, so it gets a larger size and full-strength
   text instead of the muted legal-page body. */
.site-about__story .site-prose p {
    font-size: 1.125rem;
    color: var(--color-text);
}

/* The story is one readable column, with the photo centred under the text, so the text
   is read first on every screen size. */
.site-about__story {
    max-width: 44rem;
}

.site-about__photo-wrap {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-4);
    margin: var(--space-8) 0 0;
}

.site-about__caption {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: var(--space-1);
    text-align: center;
}

.site-about__name {
    font-size: 1.125rem;
    color: var(--color-text);
}

.site-about__role {
    color: var(--color-text-subtle);
}

/* Placeholder until the founder's photo exists — see the template comment. */
.site-about__photo {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-3);
    aspect-ratio: 4 / 5;
    width: 100%;
    max-width: 16rem;
    border: 2px dashed var(--color-border);
    border-radius: var(--radius-lg);
    background: var(--color-surface);
    color: var(--color-text-subtle);
    font-size: 0.875rem;
}

.site-prose__meta {
    font-size: 0.875rem;
    color: var(--color-text-subtle);
    margin-bottom: var(--space-6);
}

/* base.css resets every <a> to `color: inherit; text-decoration: none`, so a link
   inside body copy is invisible as a link unless it says so. Same fix, same
   reason, as .auth-card a:not(.btn) above. */
.site-link,
.site-prose a {
    color: var(--color-primary);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.site-link:hover,
.site-prose a:hover {
    color: var(--color-primary-hover);
}

/* ── Notices ── */
.site-notice {
    border: 1px solid transparent;
    border-radius: var(--radius-md);
    padding: var(--space-4) var(--space-5);
    margin-bottom: var(--space-6);
    font-size: 0.9375rem;
}

.site-notice__title {
    font-size: 1rem;
    font-weight: 600;
    margin-bottom: var(--space-1);
}

.site-notice--success {
    background: var(--flash-success-bg);
    color: var(--flash-success-text);
    border-color: var(--flash-success-border);
}

.site-notice--error {
    background: var(--flash-error-bg);
    color: var(--flash-error-text);
    border-color: var(--flash-error-border);
}

.site-notice--warning {
    background: var(--flash-warning-bg);
    color: var(--flash-warning-text);
    border-color: var(--flash-warning-border);
}

/* ── Contact form ── */
.site-form {
    display: flex;
    flex-direction: column;
    gap: var(--space-5);
    align-items: flex-start;
}

.site-form__row {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    width: 100%;
}

.site-form__label {
    font-size: 0.875rem;
    font-weight: 500;
}

.site-form__optional {
    font-weight: 400;
    color: var(--color-text-subtle);
}

.site-form__input {
    width: 100%;
    padding: var(--space-3);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    background: var(--color-surface);
    color: var(--color-text);
}

.site-form__input:focus-visible {
    outline: 2px solid var(--color-primary);
    outline-offset: 1px;
}

.site-form__textarea {
    resize: vertical;
    min-height: 9rem;
}

.site-form__hint,
.site-form__privacy {
    font-size: 0.8125rem;
    color: var(--color-text-subtle);
}

/* The honeypot. Moved off-screen rather than `display:none`: a bot that reads the
   computed style skips a hidden field, and one that does not, fills this. It is
   also aria-hidden and tabindex="-1" in the template, so no keyboard or screen
   reader user can ever reach it by accident. */
.site-form__trap {
    position: absolute;
    left: -9999px;
    width: 1px;
    height: 1px;
    overflow: hidden;
}

/* ── Footer ── */
.site-footer {
    background: var(--color-bg);
    border-top: 1px solid var(--color-border);
    padding: var(--space-12) 0 var(--space-8);
}

/* Phone (since 2026-10-17): the brand on its own row, then two columns — Product on the
   left, Company and Follow stacked on the right, starting level with Product. From 720px
   the four sit in one row as before. */
.site-footer__inner {
    display: grid;
    gap: var(--space-6) var(--space-5);
    grid-template-columns: 1fr 1fr;
}

.site-footer__brand {
    grid-column: 1 / -1;
}

.site-footer__col--product {
    grid-row: span 2;
}

@media (min-width: 720px) {
    .site-footer__inner {
        gap: var(--space-8);
        grid-template-columns: 2fr 1fr 1fr 1fr;
    }

    .site-footer__brand {
        grid-column: auto;
    }

    .site-footer__col--product {
        grid-row: auto;
    }
}

.site-footer__logo {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    font-weight: 700;
    letter-spacing: -0.02em;
    margin-bottom: var(--space-2);
}

.site-footer__tagline {
    font-size: 0.875rem;
    color: var(--color-text-muted);
    max-width: 28rem;
}

.site-footer__col {
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    font-size: 0.875rem;
    color: var(--color-text-muted);
}

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

.site-footer__heading {
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: var(--color-text-subtle);
}

.site-footer__legal {
    margin-top: var(--space-8);
    padding-top: var(--space-5);
    border-top: 1px solid var(--color-border);
    font-size: 0.875rem;
    color: var(--color-text-muted);
}

.site-footer__fineprint {
    font-size: 0.75rem;
    color: var(--color-text-subtle);
    margin-top: var(--space-1);
}

/* ── Shared button sizes used by the marketing surface ── */
.btn--lg {
    padding: var(--space-3) var(--space-6);
    font-size: 1rem;
}

/* An outline button, for the secondary hero action. `.btn--ghost` is borderless
   and reads as a link at hero size, which loses the "this is a second choice, not
   a footnote" distinction. */
.btn--outline {
    background: transparent;
    color: var(--color-text);
    box-shadow: inset 0 0 0 1px var(--color-border);
}

.btn--outline:hover {
    background: var(--color-bg);
}

/* ── Floating WhatsApp button ── */
/* Fixed to the viewport, bottom-right, on every screen size. It only exists at all when
   the admin switch is on and a number is saved — see templates/site/_whatsapp.html.twig.
   It is a <button> inside a form (the number is resolved server-side, never in the HTML),
   so the form takes no space and the button drops the browser's button chrome. */
.site-whatsapp-form {
    margin: 0;
}

.site-whatsapp {
    border: 0;
    cursor: pointer;
    font: inherit;
    position: fixed;
    z-index: 40;
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-3);
    border-radius: 999px;
    /* WhatsApp brand green. One of the very few hard-coded colours on this surface, and
       deliberate: recolouring a brand mark to match our palette makes it harder to
       recognise, which defeats the only reason to use the mark at all. */
    background: #25d366;
    color: #ffffff;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.22);
    transition: transform var(--transition), box-shadow var(--transition);

    /* env() keeps it clear of the iPhone home indicator and any rounded-display inset.
       The max() means it degrades to a plain 20px on every browser that does not support
       env(), rather than collapsing to zero. */
    right: max(var(--space-5), env(safe-area-inset-right));
    bottom: max(var(--space-5), env(safe-area-inset-bottom));
}

.site-whatsapp:hover {
    transform: translateY(-2px);
    box-shadow: 0 10px 26px rgba(0, 0, 0, 0.26);
}

.site-whatsapp:focus-visible {
    outline: 3px solid var(--color-text);
    outline-offset: 3px;
}

/* Icon-only on a phone: a labelled pill eats real estate on a 360px screen and can cover
   a CTA. The accessible name lives in aria-label on the anchor either way, so hiding the
   text costs a screen reader nothing. */
.site-whatsapp__label {
    display: none;
    font-weight: 600;
    padding-right: var(--space-2);
}

@media (min-width: 640px) {
    .site-whatsapp__label {
        display: inline;
    }
}

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

    .site-whatsapp:hover {
        transform: none;
    }
}

/* ── WhatsApp alternative on /contact ── */
/* Same admin switch as the floating button; a form posting to WhatsAppRedirectController. */
.site-contact-whatsapp {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin: 0 0 var(--space-6);
    padding: var(--space-4);
    border: 1px solid var(--color-border);
    border-radius: var(--radius-lg);
}

.site-contact-whatsapp__text {
    margin: 0;
}

.site-contact-whatsapp__button {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
    cursor: pointer;
}

/* Room under the footer so the button never sits on top of the legal line — the one piece
   of text on the page a reader may specifically be looking for. */
.site-footer {
    padding-bottom: calc(var(--space-12) + var(--space-8));
}

/* ── Swipe row (homepage "What it fixes") ──
   Below 640px the grid becomes one horizontal row that snaps card by card, with the next
   card peeking in so it reads as "there is more". card_carousel_controller advances it on a
   timer until the visitor touches it; from 640px up it is the ordinary .site-grid and the
   controller finds nothing to scroll. Bleeds to the screen edge by cancelling the container
   gutter (.site-container, --space-5) and restoring it as padding. */
@media (max-width: 639px) {
    .site-swipe {
        display: flex;
        gap: var(--space-3);
        overflow-x: auto;
        scroll-snap-type: x mandatory;
        scroll-padding: 0 var(--space-5);
        overscroll-behavior-x: contain;
        margin: 0 calc(-1 * var(--space-5));
        padding: 0 var(--space-5) var(--space-2);
        scrollbar-width: none;
    }

    .site-swipe::-webkit-scrollbar {
        display: none;
    }

    .site-swipe > * {
        flex: 0 0 85%;
        scroll-snap-align: start;
    }
}

/* A problem → answer card: the icon, the "Today:" line, then the answer. A column so the
   cards in a swipe row stretch to one height. */
.site-solve {
    display: flex;
    flex-direction: column;
}

/* The "Today: <problem>" line above each answer. */
.site-solve__before {
    font-size: 0.875rem;
    color: var(--color-text-muted);
    margin-bottom: var(--space-2);
}

.site-solve__label {
    font-weight: 600;
    color: var(--color-text-subtle);
}
