@layer reset, tokens, base, layout, components, utilities;

/* ---------- fonts ---------- */
/* Self-hosted, latin subset (gwfh.mranftl.com). Covers German umlauts (U+00C0–00FF). */

@font-face {
    font-family: "Inter";
    src: url("../fonts/Inter-Regular-poxsSdI.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}
@font-face {
    font-family: "Inter";
    src: url("../fonts/Inter-Medium-Uwgfwtt.woff2") format("woff2");
    font-weight: 500;
    font-style: normal;
    font-display: swap;
}
@font-face {
    font-family: "Inter";
    src: url("../fonts/Inter-SemiBold-keCwlca.woff2") format("woff2");
    font-weight: 600;
    font-style: normal;
    font-display: swap;
}

@font-face {
    font-family: "Space Grotesk";
    src: url("../fonts/SpaceGrotesk-Medium-vzQeC3q.woff2") format("woff2");
    font-weight: 500;
    font-style: normal;
    font-display: swap;
}
@font-face {
    font-family: "Space Grotesk";
    src: url("../fonts/SpaceGrotesk-Bold-SNsywT4.woff2") format("woff2");
    font-weight: 700;
    font-style: normal;
    font-display: swap;
}
@font-face {
    /* Site chrome display face only (headings, brand, step numbers) — the
       printed poster's own title keeps Space Grotesk via --poster-font-display,
       independent of the site's visual redesign. Single weight (400); Bebas
       Neue ships no bold, and body { font-synthesis: none } deliberately
       skips faux-bold rather than fake it. */
    font-family: "Bebas Neue";
    src: url("../fonts/BebasNeue-Regular-kCS-Go6.woff2") format("woff2");
    font-weight: 400;
    font-style: normal;
    font-display: swap;
}

@layer tokens {
    :root {
        color-scheme: light;

        /* ground — paper (site chrome only; the printed poster itself stays
           dark regardless of site theme, see --poster-* below) */
        --bg: #f6f4ef;
        --surface: #ffffff;
        --surface-2: #eeebe2;
        --surface-3: #e3ddcd;
        --line: #dedace;
        --line-strong: #c9c3b3;

        /* ink */
        --fg: #181a1f;
        --fg-dim: #565a62;   /* 6.3:1 on --bg, passes AA */
        --muted: #6b6f78;    /* 4.6:1 on --bg, passes AA (darker than the comp's #8B8F98, which was ~3:1) */
        --placeholder: #b3b0a4; /* input ::placeholder only, not general muted text — lighter
           still than the comp's own #8B8F98 (~1.8:1 on --surface-2, well below AA on purpose):
           a hint this faint reads unambiguously as empty rather than pre-filled; --muted's
           AA-passing darkness reads as real typed content in an input specifically. */

        /* brand — the accent is the printed route colour */
        --accent: #e23f2e;        /* route + decorative fills/borders/focus */
        --accent-strong: #c22f21; /* CTA background: white label clears AA (5.6:1) */
        --accent-text: #c22f21;   /* the red as text on paper: links, eyebrow, badges (5.1:1, AA) */
        --accent-fg: #ffffff;
        --accent-ink: #9c2619;    /* pressed / hover-darken */
        --accent-wash: rgba(226, 63, 46, 0.12);

        /* secondary accent (feature icons, quiet highlights) */
        --secondary: #57634c;
        --secondary-tint: #e7ebe0;

        /* passepartout (the light mount around a dark poster) — unrelated to
           the site theme above, this is the physical print's own colour option */
        --paper: #f4f1ea;
        --poster-ground: #1c1f26;
        --poster-ink: #f4f1ea;
        --poster-muted: #9aa3b2;
        --poster-divider: rgba(255, 255, 255, 0.14);

        /* status */
        --ok: #1f7a4c;   /* 4.8:1 on --bg, passes AA */
        --warn: #96660a; /* 4.6:1 on --bg, passes AA */

        /* track polyline (read by assets/controllers/map_controller.js) —
           drawn on the dark poster mockup, independent of site theme */
        --map-track: var(--accent);
        --map-casing: #0f1115;

        /* type */
        --font-display: "Bebas Neue", "Inter", ui-sans-serif, system-ui, sans-serif;
        --poster-font-display: "Space Grotesk", "Inter", ui-sans-serif, system-ui, sans-serif;
        --font-body: "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;

        /* shape + rhythm */
        --radius: 10px;
        --radius-sm: 6px;
        --radius-lg: 16px;
        --maxw: 1040px;
        --gutter: clamp(1rem, 4vw, 2rem);
        --section-gap: clamp(3.5rem, 9vw, 6rem);
        --header-h: 4.5rem; /* actual .site-header height (~71px); used to offset in-page anchor scrolling below the sticky header */

        /* elevation — ink-tinted, not pure black, to stay warm on paper */
        --shadow-card: 0 1px 2px rgba(24, 26, 31, 0.06), 0 10px 28px rgba(24, 26, 31, 0.08);
        --shadow-poster: 0 20px 48px rgba(24, 26, 31, 0.18);

        --focus: 0 0 0 3px var(--accent-wash), 0 0 0 1px var(--accent);
    }
}

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

    * {
        margin: 0;
    }

    html {
        -webkit-text-size-adjust: 100%;
        scroll-padding-top: calc(var(--header-h) + 1rem); /* keeps anchor targets clear of the sticky header */
    }

    body {
        min-height: 100dvh;
        display: flex;
        flex-direction: column;
        background: var(--bg);
        color: var(--fg);
        font: 400 1rem/1.65 var(--font-body);
        font-synthesis: none;
        -webkit-font-smoothing: antialiased;
        text-rendering: optimizeLegibility;
        overflow-x: clip; /* full-bleed sections (e.g. .hero--contour) break out to 100vw */
    }

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

    h1,
    h2,
    h3 {
        font-family: var(--font-display);
        font-weight: 400; /* Bebas Neue ships one weight; its own heft does the work */
        line-height: 1.1;
        letter-spacing: -0.02em;
        color: var(--fg);
        text-wrap: balance;
    }

    h1 {
        font-size: clamp(2rem, 1.3rem + 3.2vw, 3.25rem);
    }

    h2 {
        font-size: clamp(1.5rem, 1.2rem + 1.4vw, 2rem);
        letter-spacing: -0.015em;
    }

    h3 {
        font-size: 1.15rem;
        font-weight: 500;
        letter-spacing: -0.01em;
    }

    p {
        text-wrap: pretty;
    }

    /*
     * Minimal flow spacing for plain content (product, checkout, account …).
     * Zero specificity via :where() so components and utilities win easily.
     */
    :where(main) p {
        margin-bottom: 1rem;
    }
    :where(main) h1 {
        margin-bottom: 0.5rem;
    }
    :where(main) :is(p, ul, ol, figure, table, .card, .notice) + :is(h2, h3) {
        margin-top: 2rem;
    }
    :where(main) :is(h2, h3) + * {
        margin-top: 0.5rem;
    }
    :where(main) > :last-child,
    :where(main .section) > :last-child {
        margin-bottom: 0;
    }

    a {
        color: var(--accent-text);
        text-decoration-thickness: 1px;
        text-underline-offset: 2px;
    }

    a:hover {
        color: var(--accent-text);
        text-decoration-thickness: 2px;
    }

    strong,
    b {
        font-weight: 600;
        color: var(--fg);
    }

    :where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
        outline: none;
        box-shadow: var(--focus);
        border-radius: var(--radius-sm);
    }

    ::selection {
        background: var(--accent);
        color: var(--accent-fg);
    }

    .tnum {
        font-variant-numeric: tabular-nums;
    }

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

    @media (prefers-reduced-motion: reduce) {
        *,
        *::before,
        *::after {
            animation-duration: 0.01ms !important;
            animation-iteration-count: 1 !important;
            transition-duration: 0.01ms !important;
        }
    }
}

@layer layout {
    .wrap {
        width: 100%;
        max-width: var(--maxw);
        margin-inline: auto;
        padding-inline: var(--gutter);
    }

    /* header */
    .site-header {
        position: sticky;
        top: 0;
        z-index: 20;
        background: color-mix(in srgb, var(--surface) 90%, transparent);
        backdrop-filter: blur(8px);
        border-bottom: 1px solid var(--line);
    }
    .site-header .wrap {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 1rem;
        padding-block: 0.85rem;
    }
    .brand {
        display: inline-flex;
        align-items: baseline;
        gap: 0;
        font-family: var(--font-display);
        font-weight: 400;
        font-size: 1.4rem;
        letter-spacing: 0.01em;
        color: var(--fg);
        text-decoration: none;
    }
    .brand b {
        color: var(--accent-text);
        font-weight: 400;
    }
    .brand__label {
        position: relative;
    }
    .brand__suffix {
        font-family: var(--font-body);
        font-size: 0.6em;
        font-weight: 600;
        text-transform: none;
    }
    .brand__label::after {
        content: "";
        position: absolute;
        left: -2px;
        right: 0;
        bottom: calc(0.3125em + 2px);
        height: 1px;
        background: var(--accent);
    }
    .site-footer__brand .brand__label::after {
        left: -2px;
    }
    .site-header__actions {
        display: flex;
        align-items: center;
        gap: 0.75rem;
    }
    .site-header__account {
        display: none;
        align-items: center;
        gap: 0.9rem;
        font-size: 0.925rem;
    }
    .site-header__account a {
        color: var(--fg-dim);
        text-decoration: none;
    }
    .site-header__account a:hover {
        color: var(--fg);
    }
    .site-header__account .site-nav__user {
        max-width: 16ch;
        overflow: hidden;
        color: var(--muted);
        text-overflow: ellipsis;
        white-space: nowrap;
    }
    .site-header__toggle {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 2.25rem;
        height: 2.25rem;
        margin-right: -0.4rem;
        padding: 0;
        border: none;
        background: none;
        color: var(--fg);
        cursor: pointer;
    }
    .site-nav {
        position: absolute;
        inset-inline: 0;
        top: 100%;
        display: none;
        flex-direction: column;
        gap: 1rem;
        padding: 1.25rem var(--gutter);
        background: var(--bg);
        border-top: 1px solid var(--line);
        border-bottom: 1px solid var(--line);
        font-size: 0.975rem;
        font-weight: 500;
    }
    .site-nav.is-open {
        display: flex;
    }
    .site-nav a {
        padding-block: 0.35rem;
        color: var(--fg-dim);
        text-decoration: none;
    }
    .site-nav a:hover {
        color: var(--fg);
    }
    .site-nav__user {
        color: var(--muted);
    }
    @media (min-width: 54rem) {
        .site-header .wrap {
            position: relative;
        }
        .site-header__toggle {
            display: none;
        }
        .site-header__account {
            display: flex;
        }
        .site-nav {
            position: static;
            display: flex;
            flex-direction: row;
            align-items: center;
            gap: clamp(1.25rem, 2.5vw, 2rem);
            padding: 0;
            background: none;
            border: none;
            font-size: 0.925rem;
            font-weight: 400;
        }
        /* Anmelden/Meine Bestellungen/Abmelden already sit in
           .site-header__actions on desktop — don't show them twice. */
        .site-nav__account {
            display: none;
        }
    }

    /* main + rhythm */
    main.wrap {
        display: block;
        flex: 1 0 auto;
        padding-block: clamp(2rem, 5vw, 3.5rem) var(--section-gap);
    }
    .section + .section {
        margin-top: var(--section-gap);
    }
    .section--tight + .section,
    .section + .section--tight {
        margin-top: 2.75rem;
    }
    .stack > * + * {
        margin-top: 1rem;
    }
    .lead {
        max-width: 54ch;
        color: var(--fg-dim);
        font-size: clamp(1.05rem, 1rem + 0.5vw, 1.25rem);
    }
    .product-dimensions {
        margin-top: 0.5rem;
        color: var(--fg-dim);
        font-size: 0.95rem;
    }
    .backlink {
        display: inline-block;
        margin-bottom: 1.5rem;
        color: var(--muted);
        font-size: 0.925rem;
        text-decoration: none;
    }
    .backlink:hover {
        color: var(--fg-dim);
    }

    .gpx-upload {
        margin-top: 1rem;
    }
    .link-button {
        background: none;
        border: none;
        padding: 0;
        color: var(--muted);
        font-size: 0.925rem;
        text-decoration: underline;
        cursor: pointer;
    }
    .link-button:hover {
        color: var(--fg-dim);
    }
    .gpx-upload__panel {
        margin-top: 0.75rem;
        max-width: 28rem;
    }

    /* footer — deliberately inverted (ink bg) for a hard close under the
       paper page; reuses --fg/--bg rather than new tokens since it's the
       same ink colour, just swapped which side is background. */
    .site-footer {
        background: var(--fg);
        color: color-mix(in srgb, var(--bg) 65%, transparent);
        font-size: 0.9rem;
    }
    .site-footer__grid {
        display: grid;
        gap: 2.5rem;
        padding-block: 3.5rem;
    }
    @media (min-width: 40rem) {
        .site-footer__grid {
            grid-template-columns: repeat(2, 1fr);
        }
    }
    @media (min-width: 60rem) {
        .site-footer__grid {
            grid-template-columns: 1.4fr 1fr 1fr 1fr;
        }
    }
    .site-footer__brand {
        display: inline-flex;
        align-items: baseline;
        gap: 0;
        margin-bottom: 1rem;
        font-family: var(--font-display);
        font-size: 1.25rem;
        color: var(--bg);
        text-decoration: none;
    }
    .site-footer__brand b {
        color: var(--accent-text);
        font-weight: 400;
    }
    .site-footer p {
        max-width: 28ch;
        line-height: 1.6;
    }
    .site-footer__heading {
        margin-bottom: 1rem;
        color: var(--bg);
        font-size: 0.875rem;
        font-weight: 600;
    }
    .site-footer__links {
        display: grid;
        gap: 0.65rem;
        padding: 0;
        margin: 0;
        list-style: none;
    }
    .site-footer a {
        color: inherit;
        text-decoration: none;
    }
    .site-footer a:hover {
        color: var(--bg);
    }
    .site-footer__social {
        display: flex;
        gap: 0.75rem;
    }
    .site-footer__social a {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 2.25rem;
        height: 2.25rem;
        border: 1px solid color-mix(in srgb, var(--bg) 15%, transparent);
        border-radius: 999px;
        transition: border-color 0.14s ease, color 0.14s ease;
    }
    .site-footer__social a:hover {
        border-color: var(--accent);
        color: var(--accent);
    }
    .site-footer__bottom {
        display: flex;
        flex-direction: column;
        gap: 0.35rem;
        padding-block: 1.5rem;
        border-top: 1px solid color-mix(in srgb, var(--bg) 12%, transparent);
        font-size: 0.8rem;
        color: color-mix(in srgb, var(--bg) 45%, transparent);
    }
    @media (min-width: 40rem) {
        .site-footer__bottom {
            flex-direction: row;
            justify-content: space-between;
        }
    }
    .site-footer__legal,
    .site-footer__attribution {
        display: flex;
        flex-direction: column;
        gap: 0.15rem;
    }
    @media (min-width: 40rem) {
        .site-footer__attribution {
            align-items: flex-end;
        }
    }
}

@layer components {
    /* ---------- button ---------- */
    .btn {
        --btn-bg: var(--accent-strong);
        --btn-fg: var(--accent-fg);
        display: inline-flex;
        align-items: center;
        justify-content: center;
        gap: 0.5rem;
        padding: 0.8rem 1.4rem;
        border: 1px solid transparent;
        border-radius: var(--radius-sm);
        background: var(--btn-bg);
        color: var(--btn-fg);
        font: 600 0.975rem/1 var(--font-body);
        text-decoration: none;
        cursor: pointer;
        transition: background-color 0.14s ease, border-color 0.14s ease, transform 0.14s ease;
    }
    .btn:hover {
        background: var(--accent-ink);
        color: var(--btn-fg);
    }
    .btn:active {
        transform: translateY(1px);
    }
    .btn--ghost {
        --btn-bg: transparent;
        --btn-fg: var(--fg);
        border-color: var(--line-strong);
    }
    .btn--ghost:hover {
        --btn-bg: var(--surface-2);
        background: var(--surface-2);
        color: var(--fg);
        border-color: var(--line-strong);
    }
    .btn--block {
        display: flex;
        width: 100%;
    }
    .btn[disabled],
    .btn.is-disabled {
        background: var(--surface-2);
        border-color: var(--line);
        color: var(--muted);
        cursor: not-allowed;
        pointer-events: none;
    }
    .btn.is-loading::after {
        content: '';
        width: 1em;
        height: 1em;
        border-radius: 50%;
        border: 2px solid var(--btn-fg);
        border-right-color: transparent;
        animation: btn-spin 0.6s linear infinite;
    }
    @keyframes btn-spin {
        to {
            transform: rotate(360deg);
        }
    }

    /* ---------- notice ---------- */
    .notice {
        border: 1px solid var(--line);
        border-left: 3px solid var(--accent);
        border-radius: var(--radius-sm);
        background: var(--surface);
        padding: 0.9rem 1.1rem;
        color: var(--fg-dim);
    }
    .notice + .notice,
    .stat-grid + .notice {
        margin-top: 0.75rem;
    }

    /* ---------- badge ---------- */
    .badge {
        display: inline-flex;
        align-items: center;
        gap: 0.4rem;
        padding: 0.25rem 0.6rem;
        border: 1px solid var(--line-strong);
        border-radius: 999px;
        font-size: 0.8rem;
        font-weight: 500;
        color: var(--fg-dim);
    }
    .badge--ok {
        border-color: color-mix(in srgb, var(--ok) 45%, transparent);
        color: var(--ok);
    }
    .badge--accent {
        border-color: color-mix(in srgb, var(--accent) 55%, transparent);
        color: var(--accent-text);
    }

    /* ---------- step progress (order flow: upload → preview → address) ---------- */
    .step-progress {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        margin-bottom: 1.25rem;
        padding: 0;
        list-style: none;
    }
    .step-progress__item {
        display: flex;
        align-items: center;
        gap: 0.5rem;
        color: var(--muted);
        font-size: 0.875rem;
    }
    .step-progress__item + .step-progress__item::before {
        content: '';
        width: 1.25rem;
        height: 1px;
        background: var(--line-strong);
    }
    .step-progress__dot {
        display: inline-flex;
        flex: none;
        align-items: center;
        justify-content: center;
        width: 1.5rem;
        height: 1.5rem;
        border: 1px solid var(--line-strong);
        border-radius: 999px;
        font-family: var(--font-display);
        font-size: 0.9rem;
        font-weight: 400;
        font-variant-numeric: tabular-nums;
    }
    .step-progress__item--current {
        color: var(--fg);
    }
    .step-progress__item--current .step-progress__dot,
    .step-progress__item--done .step-progress__dot {
        border-color: transparent;
        background: var(--accent-strong);
        color: var(--accent-fg);
    }
    .step-progress__label {
        display: none;
    }
    @media (min-width: 30rem) {
        .step-progress__label {
            display: inline;
        }
    }

    /* ---------- section header ---------- */
    .section-header {
        max-width: 46ch;
        margin-bottom: clamp(1.5rem, 4vw, 2.5rem);
    }
    .stack > .section-header {
        margin-bottom: 0.4rem;
    }
    .section-header p {
        margin-top: 0.75rem;
        color: var(--fg-dim);
    }

    /* ---------- prose ---------- */
    .prose > * + * {
        margin-top: 1rem;
    }
    .prose {
        color: var(--fg-dim);
    }

    /* ---------- hero ---------- */
    .hero {
        display: grid;
        gap: clamp(2rem, 6vw, 4rem);
        align-items: center;
    }
    .hero--contour {
        /* Full-bleed like .trust-bar's strip in the comp: the pattern runs
           edge-to-edge behind the (still centred, max-width) hero content. */
        position: relative;
    }
    .hero--contour::before {
        content: '';
        position: absolute;
        /* Bleed past the section's own box so the pattern sits flush against
           the header above (main.wrap's top padding-block) and the trust bar
           below (the .section--tight adjacency margin) instead of leaving a
           plain paper gap on either side — mirrors those two rules' values. */
        top: calc(-1 * clamp(2rem, 5vw, 3.5rem));
        left: 50%;
        width: 100vw;
        height: calc(100% + clamp(2rem, 5vw, 3.5rem) + 2.75rem);
        transform: translateX(-50%);
        z-index: -1;
        pointer-events: none;
        /* Faint topo-map contour rings, echoing the printed route relief.
           Colours mirror --secondary (#57634c) and --fg (#181a1f); kept as
           literal rgba() since background-image gradients can't read the
           custom properties' component channels directly. */
        background-image:
            repeating-radial-gradient(circle at 22% 18%, transparent 0, transparent 34px, rgba(87, 99, 76, 0.10) 35px, rgba(87, 99, 76, 0.10) 36px, transparent 37px),
            repeating-radial-gradient(circle at 82% 74%, transparent 0, transparent 46px, rgba(24, 26, 31, 0.05) 47px, rgba(24, 26, 31, 0.05) 48px, transparent 49px);
    }
    @media (min-width: 52rem) {
        .hero {
            grid-template-columns: 1.05fr 0.95fr;
        }
    }
    .hero__media img {
        display: block;
        width: 100%;
        height: auto;
        border-radius: var(--radius-sm);
        box-shadow: var(--shadow-poster);
    }
    .hero__actions {
        display: flex;
        flex-wrap: wrap;
        gap: 0.75rem;
        margin-top: 1.75rem;
    }
    .hero__note {
        margin-top: 1rem;
        font-size: 0.9rem;
        color: var(--muted);
    }

    /* ---------- price note / delivery + payment info (#112) ---------- */
    .price-note {
        display: block;
        color: var(--muted);
        font-size: 0.75rem;
        font-weight: 400;
    }
    .delivery-payment-info {
        margin-top: 0.75rem;
        color: var(--muted);
        font-size: 0.85rem;
    }
    /* § 312j Abs. 2 BGB: order summary directly above the order button */
    .order-summary {
        padding: 0.75rem 0;
        border-top: 1px solid var(--line);
    }
    .order-summary p {
        margin: 0;
    }
    .order-summary__price .price-note {
        display: inline;
        margin-left: 0.35rem;
    }
    .order-summary__details {
        margin-top: 0.25rem;
        color: var(--muted);
        font-size: 0.85rem;
    }

    /* ---------- trust bar ---------- */
    /* Full-bleed strip like the hero contour: the section itself carries no
       box, its ::before paints edge-to-edge behind the centred __inner. */
    .trust-bar {
        position: relative;
    }
    .trust-bar::before {
        content: '';
        position: absolute;
        top: 0;
        left: 50%;
        width: 100vw;
        height: 100%;
        transform: translateX(-50%);
        z-index: -1;
        background: var(--surface-2);
        border-block: 1px solid var(--line);
    }
    .trust-bar__inner {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        justify-content: space-between;
        gap: 1rem 2rem;
        padding: 1.5rem clamp(1.25rem, 4vw, 2rem);
    }
    .trust-bar__inner--center {
        justify-content: center;
    }
    .trust-bar__platforms {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
        gap: 0.4rem 0.9rem;
        color: var(--muted);
        font-size: 0.9rem;
    }
    .trust-bar__platforms strong {
        color: var(--fg-dim);
        font-weight: 500;
    }
    .trust-bar__sep {
        color: var(--line-strong);
    }

    /* ---------- feature grid ---------- */
    .features {
        display: grid;
        gap: 1px;
        border: 1px solid var(--line);
        border-radius: var(--radius-lg);
        background: var(--line);
        overflow: hidden;
    }
    @media (min-width: 34rem) {
        .features {
            grid-template-columns: repeat(2, 1fr);
        }
    }
    @media (min-width: 56rem) {
        .features {
            grid-template-columns: repeat(3, 1fr);
        }
    }
    .feature {
        background: var(--bg);
        padding: 2rem;
    }
    .feature svg {
        margin-bottom: 1.25rem;
        color: var(--accent);
    }
    .feature h3 {
        margin-bottom: 0.5rem;
        font-size: 1.05rem;
    }
    .feature p {
        color: var(--fg-dim);
        font-size: 0.925rem;
    }

    /* ---------- cta banner (own block in base.html.twig, sits outside
       main.wrap so the dark background can run full-bleed) ---------- */
    .cta-banner {
        background: var(--fg);
    }
    .cta-banner .wrap {
        padding-block: clamp(3rem, 8vw, 5rem);
        text-align: center;
    }
    .cta-banner h2 {
        color: var(--bg);
    }
    .cta-banner p {
        max-width: 42ch;
        margin: 0.75rem auto 0;
        color: color-mix(in srgb, var(--bg) 70%, transparent);
        font-size: clamp(1.05rem, 1rem + 0.5vw, 1.2rem);
    }
    .cta-banner .btn {
        margin-top: 1.75rem;
    }

    /* ---------- steps ---------- */
    .steps {
        display: grid;
        gap: 1.25rem;
        counter-reset: step;
        padding: 0;
        list-style: none;
    }
    @media (min-width: 44rem) {
        .steps {
            grid-template-columns: repeat(3, 1fr);
        }
    }

    /* ---------- examples ---------- */
    .examples {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
        gap: 1.25rem;
    }
    .examples__item img {
        display: block;
        width: 100%;
        height: auto;
        border-radius: var(--radius-sm);
        box-shadow: var(--shadow-poster);
    }
    .examples--preview {
        margin-top: 2rem;
    }
    .examples__caption {
        grid-column: 1 / -1;
        margin-bottom: 0.25rem;
        color: var(--muted);
        font-size: 0.9rem;
    }
    .examples__item--zoomable {
        position: relative;
        display: block;
        width: 100%;
        padding: 0;
        border: none;
        background: none;
        font: inherit;
        text-align: left;
        cursor: zoom-in;
    }
    .examples__item--zoomable::after {
        content: '';
        position: absolute;
        inset: 0;
        border-radius: var(--radius-sm);
        background: rgba(24, 26, 31, 0.08);
        opacity: 0;
        transition: opacity 0.15s ease;
    }
    .examples__item--zoomable:hover::after,
    .examples__item--zoomable:focus-visible::after {
        opacity: 1;
    }

    /* ---------- gallery lightbox (home "fertiges Poster" real photos) ---------- */
    .gallery__dialog {
        /* dialog:modal's UA default is position:fixed;inset:0;margin:auto,
           which centers it in the viewport — position:relative would replace
           that and leave it wherever it sits in the (scrolled) document flow.
           Keep fixed/inset so centering still works, just add relative's
           other effect (containing block for the absolutely positioned
           close/nav/counter children below). */
        position: fixed;
        inset: 0;
        margin: auto;
        padding: 0;
        border: none;
        border-radius: var(--radius-lg);
        background: transparent;
        box-shadow: none;
        width: min(92vw, 900px);
        height: fit-content;
        max-height: 90vh;
        overflow: visible;
    }
    .gallery__dialog::backdrop {
        background: rgba(0, 0, 0, 0.75);
    }
    .gallery__image {
        display: block;
        width: 100%;
        max-height: 85vh;
        object-fit: contain;
        border-radius: var(--radius-sm);
        box-shadow: var(--shadow-poster);
        background: #111;
    }
    .gallery__close,
    .gallery__nav {
        position: absolute;
        z-index: 1;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 2.25rem;
        height: 2.25rem;
        padding: 0;
        border: 1px solid rgba(0, 0, 0, 0.15);
        border-radius: 999px;
        background: rgba(255, 255, 255, 0.85);
        color: #111;
        font-size: 1.25rem;
        line-height: 1;
        cursor: pointer;
    }
    .gallery__close:hover,
    .gallery__nav:hover {
        background: #fff;
    }
    .gallery__nav[hidden],
    .gallery__counter[hidden] {
        display: none;
    }
    .gallery__close {
        top: -1rem;
        right: -1rem;
    }
    .gallery__nav--prev {
        top: 50%;
        left: -1rem;
        transform: translateY(-50%);
    }
    .gallery__nav--next {
        top: 50%;
        right: -1rem;
        transform: translateY(-50%);
    }
    .gallery__counter {
        position: absolute;
        left: 50%;
        bottom: -1.75rem;
        transform: translateX(-50%);
        color: #fff;
        font-size: 0.85rem;
    }
    /* Negative offsets above place the controls just outside the image, which
       only fits with the vw-based margin a wide dialog leaves; at narrow
       widths that margin collapses, so pull them back on-image instead. */
    @media (max-width: 30rem) {
        .gallery__close,
        .gallery__nav {
            background: rgba(0, 0, 0, 0.5);
            color: #fff;
            border-color: rgba(255, 255, 255, 0.3);
        }
        .gallery__close {
            top: 0.5rem;
            right: 0.5rem;
        }
        .gallery__nav--prev {
            left: 0.5rem;
        }
        .gallery__nav--next {
            right: 0.5rem;
        }
        .gallery__counter {
            bottom: 0.5rem;
        }
    }
    .step {
        border: 1px solid var(--line);
        border-radius: var(--radius);
        background: var(--surface);
        padding: 1.5rem;
    }
    .step__n {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 2rem;
        height: 2rem;
        margin-bottom: 0.9rem;
        border-radius: 999px;
        background: var(--accent-strong);
        color: var(--accent-fg);
        font-family: var(--font-display);
        font-weight: 400;
        font-variant-numeric: tabular-nums;
    }
    .step h3 {
        margin-bottom: 0.35rem;
        color: var(--fg);
    }
    .step p {
        color: var(--fg-dim);
        font-size: 0.95rem;
    }

    /* ---------- poster: passepartout around a dark print ---------- */
    .poster {
        --mat: clamp(14px, 4.5%, 30px);
        --frame-mat: #000;
        position: relative;
        display: block;
        max-width: 520px;
        padding: var(--mat);
        border: 1px solid rgba(0, 0, 0, 0.25);
        border-radius: var(--radius-sm);
        background: var(--frame-mat);
        box-shadow: var(--shadow-poster);
    }
    /* #78: light poster color scheme (the printed content's own background/
       ink, independent of the physical frame below) — overrides the
       --poster-* tokens that .poster__frame and its stats/title descendants
       already read, so the on-screen mockup, the customer snapshot, and the
       print PDF (same captured element, see poster_snapshot_controller.js)
       all pick it up with no extra wiring. --poster-dialog is a descendant of
       .poster (see PosterCard.html.twig), so it inherits these too even
       though <dialog> promotes to the top layer on showModal() — top-layer
       promotion doesn't change the DOM parent chain CSS inheritance follows. */
    .poster--light {
        --poster-ground: var(--paper);
        --poster-ink: #1c1f26;
        --poster-muted: #6f6a60;
        --poster-divider: rgba(28, 31, 38, 0.14);
    }
    /* #78: the physical frame colour (ProductVariant::frameColor) shown by
       the on-screen mat/passepartout — a separate axis from --poster-light
       above. --frame-mat also drives .poster__dialog's background, the same
       inheritance-through-descendant reasoning as --poster-light. */
    .poster--frame-light {
        --frame-mat: #f5f4f0;
    }
    .poster__zoom {
        position: absolute;
        top: 0.6rem;
        right: 0.6rem;
        z-index: 1;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 2rem;
        height: 2rem;
        padding: 0;
        border: 1px solid rgba(0, 0, 0, 0.15);
        border-radius: 999px;
        background: rgba(255, 255, 255, 0.85);
        color: #111;
        cursor: pointer;
    }
    .poster__zoom svg {
        width: 1rem;
        height: 1rem;
    }
    .poster__zoom:hover {
        background: #fff;
    }
    .poster__dialog {
        --mat: clamp(14px, 4.5%, 30px);
        margin: auto;
        padding: var(--mat);
        border: 1px solid rgba(0, 0, 0, 0.25);
        border-radius: var(--radius-sm);
        background: var(--frame-mat, #000);
        box-shadow: var(--shadow-poster);
        width: min(95vw, 832px);
        max-height: 90vh;
        overflow: auto;
    }
    .poster__dialog::backdrop {
        background: rgba(0, 0, 0, 0.65);
    }
    /* lightbox_controller.js sets the clone's width/transform inline: fixed at
       its original on-screen size, then CSS-scaled up to fill the dialog. A
       true optical zoom, not a re-layout — .poster__title (and the stats)
       size themselves off this element's own inline-size via `cqw`, so
       re-layouting at the dialog's much wider size would clamp the font
       differently than the small card the print export is captured from,
       making this preview look disproportionate to the actual product. */
    .poster__dialog-close {
        position: absolute;
        top: 0.6rem;
        right: 0.6rem;
        z-index: 1;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        width: 2rem;
        height: 2rem;
        padding: 0;
        border: 1px solid rgba(0, 0, 0, 0.15);
        border-radius: 999px;
        background: rgba(255, 255, 255, 0.85);
        color: #111;
        font-size: 1.25rem;
        line-height: 1;
        cursor: pointer;
    }
    .poster__dialog-close:hover {
        background: #fff;
    }
    .poster__frame {
        position: relative;
        display: flex;
        flex-direction: column;
        aspect-ratio: var(--poster-ratio, 1);
        padding: clamp(10px, 5%, 20px);
        /* #78: a visible edge between the print and the mat regardless of
           colour — without it, a white frame (--frame-mat) around a light
           poster (--poster-ground) has no boundary at all. */
        border: 1px solid rgba(128, 134, 145, 0.4);
        border-radius: 2px;
        background: var(--poster-ground);
        color: var(--poster-ink);
        container-type: inline-size;
        overflow: hidden;
        /* Dragging the START/ZIEL labels (map_controller.js's pointerdown/
           move handling) can carry the browser's own native text-selection
           gesture across the stats text below the map — visible as a red
           highlight box (::selection uses --accent) flashing mid-drag. */
        user-select: none;
        -webkit-user-select: none;
    }
    /* #67: while the order preview waits for street data (map_controller.js's
       hideUntilStreetsValue), title/stats/map stay in the DOM — sized and laid
       out as normal — but invisible, so nothing jumps once the spinner is
       swapped out for the finished poster. visibility, not display: none,
       is what keeps the map controller's own size measurements correct
       while hidden. */
    .poster__frame--loading > *:not(.poster__spinner) {
        visibility: hidden;
    }
    .poster__spinner {
        position: absolute;
        inset: 0;
        display: flex;
        flex-direction: column;
        align-items: center;
        justify-content: center;
        gap: 0.75rem;
    }
    .poster__spinner::after {
        content: '';
        width: 2.5rem;
        height: 2.5rem;
        border-radius: 50%;
        border: 3px solid var(--poster-ink);
        border-right-color: transparent;
        animation: btn-spin 0.6s linear infinite;
    }
    .poster__spinner-text {
        color: var(--poster-ink);
        opacity: 0.75;
        font-size: 0.8rem;
    }
    .poster__title {
        /* The printed poster's own typography — deliberately independent of
           --font-display (the site chrome's Bebas Neue) so a marketing
           redesign never silently changes what's on the actual product. */
        margin: 0; /* reset the <p>'s default browser margin — spacing to .poster__map is .poster__map's own margin-top */
        text-align: center;
        font-family: var(--poster-font-display);
        font-weight: 700;
        font-size: clamp(0.6rem, 5.5cqw, 1.35rem);
        letter-spacing: 0.04em;
        text-transform: uppercase;
        line-height: 1.02;
        text-wrap: balance;
    }
    .poster__subtitle {
        font-size: 0.67em; /* a third smaller than the headline */
    }
    .poster__subtitle--empty {
        visibility: hidden;
    }
    .poster__map {
        flex: 1;
        min-height: 0;
        margin-top: clamp(2px, 1%, 4px);
        margin-bottom: clamp(8px, 4%, 16px);
        overflow: hidden;
    }
    .poster__map svg {
        display: block;
        width: 100%;
        height: 100%;
    }
    /* honest disclaimer when the street background couldn't be fetched — sits
       outside the poster mockup itself, so it never reads as part of the product */
    .poster__map-notice {
        max-width: 460px;
        margin-top: 0.6rem;
        color: var(--muted);
        font-size: 0.8rem;
    }
    /* 1fr auto 1fr — mirrors .form--compact .field-grid's own three-column
       layout further down. Two equal-width outer tracks keep the zeit column
       truly centred on the poster regardless of how long "Ort" or "Datum"'s
       values are (space-between only centres it when both outer columns
       happen to be the same width). align-items: start lines up ZEIT's own
       label with ORT:/DATUM:'s — like the printed poster — instead of
       vertically centring the whole (taller, because of the big time value)
       zeit block against them. */
    .poster__stats {
        position: relative;
        display: grid;
        grid-template-columns: 1fr auto 1fr;
        align-items: start;
        gap: clamp(6px, 4cqw, 16px);
        padding-top: clamp(6px, 3cqw, 14px);
        font-variant-numeric: tabular-nums;
    }
    .poster__stats::before {
        content: '';
        position: absolute;
        top: 0;
        left: 0;
        right: 0;
        height: 1px;
        background: linear-gradient(to right, transparent, var(--poster-divider) 20%, var(--poster-divider) 80%, transparent);
    }
    /* Pin each column to its track explicitly: grid auto-placement skips a
       display:none zeit block (no Fahrzeit given) and packs the remaining
       items into the first free tracks, which would drag .poster__stats-col--right
       into the middle track instead of leaving it on the right. */
    .poster__stats > .poster__stats-col {
        grid-column: 1;
    }
    .poster__stats > .poster__stat--zeit {
        grid-column: 2;
    }
    .poster__stats > .poster__stats-col--right {
        grid-column: 3;
    }
    /* Grid per column (not a flex row) so the two labels ("Ort:"/"Distanz:",
       "Datum:"/"Höhenmeter:") line up flush against each other — like the
       printed poster's right-aligned label column + left-aligned value
       column. .poster__stat--inline becomes display:contents so its label/
       value spans join this grid directly instead of nesting a row inside it. */
    .poster__stats-col {
        display: grid;
        grid-template-columns: auto auto;
        column-gap: 0.35em;
        row-gap: 2px;
        align-items: baseline;
        min-width: 0;
        justify-self: start;
    }
    .poster__stats-col--right {
        justify-self: end;
    }
    .poster__stat {
        display: flex;
        flex-direction: column;
        gap: 0.15em;
        min-width: 0;
    }
    /* .poster__stat--inline sets display:contents and .poster__stat--zeit sets
       display:flex again — both author rules, so [hidden]'s UA-stylesheet
       display:none loses to them without this explicit override. */
    .poster__stat[hidden] {
        display: none;
    }
    .poster__stat--zeit {
        align-items: center;
        text-align: center;
        justify-content: center;
        justify-self: center;
        flex-shrink: 0;
        gap: 0;
    }
    .poster__stat--zeit .poster__stat-value {
        font-size: clamp(0.42rem, 3.8cqw, 0.88rem);
    }
    .poster__stat--inline {
        display: contents;
    }
    .poster__stat-label {
        font-size: clamp(0.37rem, 2cqw, 0.5rem);
        letter-spacing: 0.06em;
        text-transform: uppercase;
        color: var(--poster-muted);
        justify-self: end;
    }
    .poster__stat-value {
        font-size: clamp(0.46rem, 2.7cqw, 0.67rem);
        font-weight: 700;
        color: var(--poster-ink);
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        justify-self: start;
    }
    /* Ort/Distanz/Datum/Höhenmeter: small, unlike Zeit's big centred value — and
       responsive to the card's own width (container query units, not a fixed
       px), so the home page's much narrower gallery cards don't end up with
       text that looks oversized relative to the shrunk-down card around it. */
    .poster__stat--inline .poster__stat-label,
    .poster__stat--inline .poster__stat-value {
        font-size: clamp(6px, 2cqw, 8px);
    }
    .poster--plain .poster__frame {
        align-items: center;
        justify-content: center;
        aspect-ratio: var(--poster-ratio, 1);
    }

    /* ---------- preview layout (poster + order details) ---------- */
    .preview-layout {
        display: grid;
        gap: clamp(1.5rem, 5vw, 3rem);
        align-items: start;
    }
    @media (min-width: 48rem) {
        .preview-layout {
            grid-template-columns: minmax(0, 28rem) 1fr;
        }
    }

    /* ---------- poster strip (home examples) ---------- */
    .poster-strip {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
        gap: clamp(1.25rem, 4vw, 2rem);
    }
    .poster-strip .poster {
        max-width: none;
    }
    .poster-figure {
        margin: 0;
    }
    .poster-figure figcaption {
        margin-top: 0.75rem;
        color: var(--muted);
        font-size: 0.875rem;
    }

    /* ---------- stat list ---------- */
    .stat-list {
        display: flex;
        flex-wrap: wrap;
        gap: 0.4rem 0.85rem;
        padding: 0;
        margin: 0;
        list-style: none;
        color: var(--fg-dim);
        font-variant-numeric: tabular-nums;
    }
    .stat-list li {
        display: inline-flex;
        gap: 0.4rem;
    }
    .stat-list li + li::before {
        content: "·";
        margin-right: 0.85rem;
        color: var(--muted);
    }
    .stat-grid {
        display: grid;
        grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
        gap: 0.75rem;
        padding: 0;
        margin: 0;
        list-style: none;
    }
    .stat-grid li {
        border: 1px solid var(--line);
        border-radius: var(--radius-sm);
        background: var(--surface);
        padding: 0.75rem 1rem;
    }
    .stat-grid span {
        display: block;
        color: var(--muted);
        font-size: 0.78rem;
    }
    .stat-grid strong {
        font-size: 1.1rem;
        font-variant-numeric: tabular-nums;
    }

    /* ---------- cards / product grid ---------- */
    .card {
        border: 1px solid var(--line);
        border-radius: var(--radius);
        background: var(--surface);
        padding: 1.5rem;
    }
    .product-grid {
        display: grid;
        grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
        gap: 1.25rem;
    }
    .product-card {
        display: block;
        border: 1px solid var(--line);
        border-radius: var(--radius);
        background: var(--surface);
        padding: 1.5rem;
        color: inherit;
        text-decoration: none;
        box-shadow: var(--shadow-card);
        transition: border-color 0.14s ease, transform 0.14s ease, box-shadow 0.14s ease;
    }
    .product-card:hover {
        border-color: var(--line-strong);
        transform: translateY(-2px);
        box-shadow: var(--shadow-poster);
    }
    .product-card__image {
        display: block;
        width: 100%;
        height: auto;
        margin-bottom: 1rem;
        border-radius: var(--radius-sm);
    }
    .product-card h2 {
        margin-bottom: 0.35rem;
        font-size: 1.2rem;
    }
    .product-card .from {
        color: var(--muted);
        font-size: 0.9rem;
    }

    /* ---------- variant picker (#89: one card per frame colour, with its own
       photo and its own CTA — colour is chosen right here, not deferred to
       /gestalten as the earlier colour-chip version did) ---------- */
    .variant-grid {
        display: grid;
        gap: 1.25rem;
    }
    @media (min-width: 30rem) {
        .variant-grid {
            grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
        }
    }
    .variant-card {
        display: grid;
        gap: 0.85rem;
        border: 1px solid var(--line);
        border-radius: var(--radius);
        background: var(--surface);
        padding: 1rem;
        box-shadow: var(--shadow-card);
    }
    .variant-card--sold-out {
        opacity: 0.6;
    }
    .variant-card__image {
        overflow: hidden;
        border: 1px solid var(--line-strong);
        border-radius: var(--radius-sm);
        background: var(--poster-ground);
    }
    .variant-card__image img {
        display: block;
        width: 100%;
        height: 100%;
        object-fit: cover;
    }
    .variant-card__body {
        display: flex;
        align-items: baseline;
        justify-content: space-between;
        gap: 0.75rem;
    }
    .variant-card__text {
        display: grid;
        gap: 0.15rem;
    }
    .variant-card__label {
        font-weight: 500;
    }
    .variant-card__dimensions {
        color: var(--muted);
        font-size: 0.85rem;
    }
    .variant-card__price {
        text-align: right;
        color: var(--muted);
        font-size: 0.925rem;
        font-variant-numeric: tabular-nums;
    }
    .variant-card--sold-out .variant-card__price {
        font-style: italic;
    }

    .variant-list {
        display: grid;
        gap: 0.6rem;
        padding: 0;
        margin: 0;
        list-style: none;
    }
    .variant-list li {
        display: flex;
        justify-content: space-between;
        gap: 1rem;
        border: 1px solid var(--line);
        border-radius: var(--radius-sm);
        background: var(--surface);
        padding: 0.85rem 1rem;
    }
    .variant-list .price {
        font-variant-numeric: tabular-nums;
        white-space: nowrap;
    }

    /* ---------- forms ---------- */
    .form {
        display: grid;
        gap: 1.25rem;
        max-width: 32rem;
    }
    .field label,
    .form-label {
        display: block;
        margin-bottom: 0.4rem;
        font-weight: 500;
        font-size: 0.925rem;
    }
    .field input[type="text"],
    .field input[type="email"],
    .field input[type="file"],
    .field input[type="number"],
    .field input[type="date"],
    .field input[type="time"],
    .field select,
    .field textarea,
    input[type="text"],
    input[type="email"],
    input[type="number"],
    input[type="date"],
    input[type="time"],
    select,
    textarea {
        width: 100%;
        max-width: 100%;
        padding: 0.7rem 0.85rem;
        border: 1px solid var(--line-strong);
        border-radius: var(--radius-sm);
        background: var(--surface-2);
        color: var(--fg);
        font: inherit;
    }
    /* Browser default placeholder styling is too close to a real value (full
       weight, near-full contrast in some browsers) — reads as "already
       filled in" rather than a suggestion. Reported again even after
       switching to var(--muted): muted is deliberately AA-dark for ordinary
       secondary text, which is exactly why example values like "2:35:36"
       still read as something the customer already typed. var(--placeholder)
       is lighter on purpose. Explicit opacity: 1 (Firefox otherwise dims its
       own already-muted color further) so the colour alone carries the
       de-emphasis, consistently across browsers. */
    .field input::placeholder,
    .field textarea::placeholder,
    input::placeholder,
    textarea::placeholder {
        color: var(--placeholder);
        opacity: 1;
    }
    .field input[type="file"] {
        padding: 1.1rem;
        border-style: dashed;
        background: var(--surface);
        cursor: pointer;
    }
    .field input[type="file"]:hover {
        border-color: var(--accent);
    }
    .field input::file-selector-button {
        margin-right: 0.85rem;
        padding: 0.5rem 0.9rem;
        border: 1px solid var(--line-strong);
        border-radius: var(--radius-sm);
        background: var(--surface-3);
        color: var(--fg);
        font: inherit;
        cursor: pointer;
    }
    .field .form-help,
    .form-help,
    .help-text,
    small.help {
        display: block;
        margin-top: 0.35rem;
        color: var(--muted);
        font-size: 0.85rem;
    }
    .help-text--tiny {
        font-size: 0.72rem;
    }
    .field .form-error-message,
    ul.form-error-message,
    .form-error-message {
        margin-top: 0.35rem;
        padding: 0;
        list-style: none;
        color: var(--accent-text);
        font-size: 0.875rem;
    }
    .field--invalid input,
    .field--invalid select {
        border-color: var(--accent);
    }
    /* ---------- checkbox ---------- */
    /* Themed from the palette instead of shipping the bare browser default —
       a rounded box that fills with the accent colour and draws its own
       checkmark (clip-path, not an icon font/SVG asset) when checked. */
    input[type="checkbox"] {
        appearance: none;
        -webkit-appearance: none;
        width: 1.75rem;
        height: 1.75rem;
        flex: none;
        display: inline-grid;
        place-content: center;
        border: 1px solid var(--line-strong);
        border-radius: 7px;
        background: var(--surface-2);
        cursor: pointer;
        transition: background-color 0.14s ease, border-color 0.14s ease;
    }
    input[type="checkbox"]::before {
        content: '';
        width: 1.05rem;
        height: 1.05rem;
        clip-path: polygon(14% 44%, 0% 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
        background: var(--accent-fg);
        transform: scale(0);
        transition: transform 0.12s ease;
    }
    input[type="checkbox"]:checked {
        background: var(--accent-strong);
        border-color: var(--accent-strong);
    }
    input[type="checkbox"]:checked::before {
        transform: scale(1);
    }
    input[type="checkbox"]:focus-visible {
        outline: none;
        box-shadow: var(--focus);
    }
    input[type="checkbox"]:disabled {
        opacity: 0.5;
        cursor: not-allowed;
    }

    /* ---------- toggle group (poster preview: Start-/Zielpunkt anzeigen,
       Farbschema hell/dunkel, Rahmenfarbe schwarz/weiß) ----------
       Renders an expanded, two-choice ChoiceType/EntityType (radio inputs) as a
       segmented switch: the radio stays functional/accessible but visually
       hidden, its label becomes the visible pill (`input:checked + label`). */
    /* Background, not border: a border sits on top of/around the selected
       label's own fill and can visibly nick its edge (reported: a rim at the
       bottom where the accent colour didn't quite reach) — a background behind
       the (unselected) labels reads as the same "track" but never overlaps the
       selected fill at all. */
    .toggle-group {
        display: flex;
        position: relative;
        background: var(--surface-2);
        border-radius: var(--radius-sm);
        overflow: hidden;
    }
    .toggle-group input[type="radio"] {
        position: absolute;
        width: 1px;
        height: 1px;
        margin: 0;
        opacity: 0;
    }
    /* flex: 1 1 0 (not just 1 1 auto) so both options split the group evenly
       regardless of label text length ("Hell" vs "Dunkel", "weiß" vs "schwarz") —
       and stretch (the flex default) fills each half's full height, so the
       selected background reaches every edge of its segment. */
    .toggle-group label {
        flex: 1 1 0;
        margin: 0;
        padding: 0.5rem 1.1rem;
        color: var(--muted);
        font-size: 0.875rem;
        font-weight: 500;
        text-align: center;
        cursor: pointer;
        transition: background-color 0.14s ease, color 0.14s ease;
    }
    .toggle-group input[type="radio"]:checked + label {
        background: var(--accent-strong);
        color: var(--accent-fg);
    }
    .toggle-group input[type="radio"]:focus-visible + label {
        outline: none;
        box-shadow: var(--focus);
    }
    .toggle-group input[type="radio"]:disabled + label {
        opacity: 0.5;
        cursor: not-allowed;
    }

    /* Label left, control right, all three rows (Start-/Zielpunkt, Farbschema,
       Rahmenfarbe) starting their control at the same x position — a fixed
       label column width, not justify-content: space-between (which would size
       each row's gap off its own label length instead of lining the controls
       up with each other). */
    .field--row {
        display: flex;
        align-items: center;
        gap: 1rem;
    }
    /* Direct child, not a plain descendant selector: a descendant `.field--row
       label` would also match the toggle-group's own "Hell"/"Dunkel" labels
       nested inside it (same 0,1,1 specificity, later in the file, so it was
       winning) and pin each segment to this fixed 9rem instead of letting
       `.toggle-group label`'s flex: 1 1 0 split them evenly — leaving the
       track colour exposed past the last segment and making the coloured
       segment a different total width on every row (different choice counts). */
    /* 10.5rem, not 9rem: the longest row label ("Start-/Zielpunkt anzeigen")
       wrapped onto two lines at 9rem in the compact form's 0.8rem/500 type —
       10.5rem is the shortest width it fits at one line, plus a little slack
       for font-rendering variance across browsers. */
    .field--row > label,
    .field--row > .form-label {
        flex: 0 0 10.5rem;
        margin: 0;
    }
    .field--row .toggle-group {
        flex: 1 1 auto;
    }

    /* ---------- compact form (order preview, next to the poster) ---------- */
    .form--compact {
        gap: 0.7rem;
        max-width: none;
    }
    .form--compact .field label,
    .form--compact .form-label {
        margin-bottom: 0.25rem;
        font-size: 0.8rem;
    }
    /* .form--compact .field label above outranks .toggle-group label (two
       classes vs one) and was winning on margin-bottom/font-size regardless
       of source order — the stray 4px margin-bottom then ate into the
       stretched pill's height, showing as a sliver of the track colour along
       the bottom of the selected (coloured) side. Match its specificity and
       come later in the file to reclaim both properties for the toggle. */
    .form--compact .field--row label,
    .form--compact .field--row .form-label {
        margin: 0;
    }
    .form--compact .toggle-group label {
        margin-bottom: 0;
        font-size: 0.875rem;
    }
    .form--compact .field input[type="text"],
    .form--compact .field input[type="number"],
    .form--compact .field input[type="date"],
    .form--compact .field input[type="time"],
    .form--compact .field select,
    .form--compact .field textarea {
        padding: 0.45rem 0.6rem;
        font-size: 0.875rem;
    }
    /* Extra breathing room above "Farbschema" — it follows right after the
       dense Ort/Distanz/Zeit/Datum/Höhenmeter grid, which otherwise reads as
       one crowded block with the field right after it. Targets whichever
       field-row comes right after the grid rather than a specific field, so it
       keeps working if the fields get reordered again. */
    .form--compact .field-grid + .field--row {
        margin-top: 0.5rem;
    }
    /* Ort/Distanz left, Fahrzeit centred (spans both rows), Datum/Höhenmeter
       right — mirrors the printed poster's own three-column stat layout
       (.poster__stats: left stack / zeit / right stack). Named areas, not
       DOM-order auto-placement, since "zeit" needs to span two rows in the
       middle column while ort/distanz and datum/hoehenmeter stay put. */
    .form--compact .field-grid {
        display: grid;
        grid-template-columns: 1fr auto 1fr;
        grid-template-areas:
            'ort zeit datum'
            'distanz zeit hoehenmeter';
        align-items: center;
        gap: 0.7rem;
    }
    .form--compact .field-grid__ort {
        grid-area: ort;
    }
    .form--compact .field-grid__datum {
        grid-area: datum;
        justify-self: end;
    }
    .form--compact .field-grid__distanz {
        grid-area: distanz;
    }
    .form--compact .field-grid__hoehenmeter {
        grid-area: hoehenmeter;
        justify-self: end;
    }
    /* Small stat fields, not full column width — matches the Fahrzeit field's
       own compact size and the small footprint of the values on the printed
       poster itself. Datum/Höhenmeter share a width (same row, right column). */
    .form--compact .field-grid__ort input,
    .form--compact .field-grid__distanz input,
    .form--compact .field-grid__datum input,
    .form--compact .field-grid__hoehenmeter input {
        width: 11.5rem;
    }
    .form--compact .field-grid__zeit {
        grid-area: zeit;
        text-align: center;
    }
    .form--compact .field-grid__zeit input[type="text"] {
        width: 6rem;
        text-align: center;
    }

    /* ---------- auth card ---------- */
    .auth {
        max-width: 26rem;
        margin-inline: auto;
        text-align: center;
    }
    .auth .form {
        margin-inline: auto;
        text-align: start;
    }

    /* ---------- orders list ---------- */
    .orders {
        display: grid;
        gap: 0.75rem;
        padding: 0;
        margin: 0;
        list-style: none;
    }
    .order-row {
        display: flex;
        flex-wrap: wrap;
        gap: 0.5rem 1.25rem;
        align-items: center;
        justify-content: space-between;
        border: 1px solid var(--line);
        border-radius: var(--radius);
        background: var(--surface);
        padding: 1rem 1.25rem;
    }
    .order-row__main {
        display: grid;
        gap: 0.2rem;
        flex: 1 1 12rem;
    }
    .order-row .badge {
        flex: none;
    }
    .order-row > a {
        flex: none;
        text-decoration: none;
        white-space: nowrap;
    }
    .order-row__title {
        font-weight: 600;
        color: var(--fg);
    }
    .order-row__meta {
        color: var(--muted);
        font-size: 0.875rem;
        font-variant-numeric: tabular-nums;
    }
}
