/* ─────────────────────────────────────────────────────────── */
/*  preflight.xml - global styles                              */
/*  Modern glass + gradient aesthetic over Fluent substrate.   */
/* ─────────────────────────────────────────────────────────── */

:root {
    /* ─── Brand + accent (indigo → violet) ──────────────────── */
    --pf-accent: #6366f1;
    /* indigo-500 */
    --pf-accent-strong: #4f46e5;
    /* indigo-600 */
    --pf-accent-soft: #eef2ff;
    --pf-accent-ring: rgba(99, 102, 241, 0.22);
    --pf-accent-gradient: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
    --pf-accent-rgb: 99, 102, 241;

    /* ─── Surface (light) ──────────────────────────────────── */
    --pf-bg: #f6f7fb;
    --pf-bg-rgb: 246, 247, 251;
    --pf-surface: #ffffff;
    --pf-surface-2: #f1f5f9;
    --pf-border: rgba(15, 23, 42, 0.08);
    --pf-border-strong: rgba(15, 23, 42, 0.14);

    /* ─── Text (light) ─────────────────────────────────────── */
    --pf-text: #0f172a;
    --pf-text-muted: #475569;
    --pf-text-subtle: #94a3b8;

    /* ─── Cards (light) ────────────────────────────────────── */
    --pf-card-bg: linear-gradient(155deg,
            rgba(99, 102, 241, 0.04) 0%,
            rgba(255, 255, 255, 0.92) 55%,
            rgba(139, 92, 246, 0.03) 100%);
    --pf-card-border: rgba(99, 102, 241, 0.14);
    --pf-card-border-hover: rgba(139, 92, 246, 0.38);
    --pf-card-shadow: 0 4px 20px rgba(15, 23, 42, 0.05),
        0 1px 2px rgba(15, 23, 42, 0.04);
    --pf-card-shadow-hover: 0 12px 40px rgba(99, 102, 241, 0.18),
        0 2px 6px rgba(15, 23, 42, 0.06);
    --pf-card-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.9);

    /* ─── Glass chrome ─────────────────────────────────────── */
    --pf-chrome-bg: rgba(241, 245, 249, 0.9);
    /* slate-100 with slight transparency */
    --pf-chrome-border: rgba(15, 23, 42, 0.12);
    --pf-chrome-inset: rgba(15, 23, 42, 0.03);
    /* subtle visible tint for switcher pills */

    /* ─── Modals (palette / help) - solid surface, no gradient */
    --pf-modal-bg: rgba(255, 255, 255, 0.96);
    --pf-modal-border: rgba(15, 23, 42, 0.1);

    /* ─── Typography ───────────────────────────────────────── */
    --pf-font-sans: 'DM Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
    --pf-font-mono: 'JetBrains Mono', 'SFMono-Regular', Menlo, monospace;

    /* ─── Radius / motion ──────────────────────────────────── */
    --pf-radius-sm: 8px;
    --pf-radius-md: 12px;
    --pf-radius-lg: 18px;
    --pf-radius-pill: 999px;
    --pf-ease: cubic-bezier(0.4, 0, 0.2, 1);
    --pf-ease-out: cubic-bezier(0.16, 1, 0.3, 1);
}

/* ─── Dark (system preference) ─────────────────────────────── */
@media (prefers-color-scheme: dark) {
    :root:not(.theme-light) {
        --pf-accent: #8b5cf6;
        /* violet-500 */
        --pf-accent-strong: #7c3aed;
        /* violet-600 */
        --pf-accent-soft: rgba(139, 92, 246, 0.14);
        --pf-accent-ring: rgba(139, 92, 246, 0.4);
        --pf-accent-gradient: linear-gradient(135deg, #6d28d9 0%, #a855f7 100%);
        --pf-accent-rgb: 139, 92, 246;

        --pf-bg: #151627;
        --pf-bg-rgb: 21, 22, 39;
        --pf-surface: #1c1f36;
        --pf-surface-2: #242845;
        --pf-border: rgba(139, 92, 246, 0.18);
        --pf-border-strong: rgba(139, 92, 246, 0.32);

        --pf-text: #eef1f8;
        --pf-text-muted: #b4bac8;
        --pf-text-subtle: #7c849a;

        --pf-card-bg: linear-gradient(155deg,
                rgba(139, 92, 246, 0.08) 0%,
                rgba(28, 31, 54, 0.95) 55%,
                rgba(168, 85, 247, 0.05) 100%);
        --pf-card-border: rgba(139, 92, 246, 0.22);
        --pf-card-border-hover: rgba(168, 85, 247, 0.6);
        --pf-card-shadow: 0 8px 32px rgba(0, 0, 0, 0.3),
            0 2px 8px rgba(0, 0, 0, 0.2);
        --pf-card-shadow-hover: 0 20px 60px rgba(139, 92, 246, 0.38),
            0 4px 12px rgba(0, 0, 0, 0.4);
        --pf-card-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.06);

        --pf-chrome-bg: rgba(36, 40, 69, 0.92);
        --pf-chrome-border: rgba(139, 92, 246, 0.26);
        --pf-chrome-inset: rgba(139, 92, 246, 0.1);

        --pf-modal-bg: rgba(28, 31, 54, 0.97);
        --pf-modal-border: rgba(139, 92, 246, 0.24);

        --accent-fill-rest: #7c3aed;
        --accent-fill-hover: #6d28d9;
        --accent-fill-active: #5b21b6;
        --accent-foreground-rest: #c4b5fd;
    }
}

/* ─── Explicit user override (bootstrap script) ────────────── */
html.theme-dark {
    --pf-accent: #8b5cf6;
    --pf-accent-strong: #7c3aed;
    --pf-accent-soft: rgba(139, 92, 246, 0.14);
    --pf-accent-ring: rgba(139, 92, 246, 0.4);
    --pf-accent-gradient: linear-gradient(135deg, #6d28d9 0%, #a855f7 100%);
    --pf-accent-rgb: 139, 92, 246;

    --pf-bg: #151627;
    --pf-bg-rgb: 21, 22, 39;
    --pf-surface: #1c1f36;
    --pf-surface-2: #242845;
    --pf-border: rgba(139, 92, 246, 0.18);
    --pf-border-strong: rgba(139, 92, 246, 0.32);

    --pf-text: #eef1f8;
    --pf-text-muted: #b4bac8;
    --pf-text-subtle: #7c849a;

    --pf-card-bg: linear-gradient(155deg,
            rgba(139, 92, 246, 0.08) 0%,
            rgba(28, 31, 54, 0.95) 55%,
            rgba(168, 85, 247, 0.05) 100%);
    --pf-card-border: rgba(139, 92, 246, 0.22);
    --pf-card-border-hover: rgba(168, 85, 247, 0.6);
    --pf-card-shadow: 0 8px 32px rgba(0, 0, 0, 0.3),
        0 2px 8px rgba(0, 0, 0, 0.2);
    --pf-card-shadow-hover: 0 20px 60px rgba(139, 92, 246, 0.38),
        0 4px 12px rgba(0, 0, 0, 0.4);
    --pf-card-highlight: inset 0 1px 0 rgba(255, 255, 255, 0.06);

    --pf-chrome-bg: rgba(36, 40, 69, 0.92);
    --pf-chrome-border: rgba(139, 92, 246, 0.26);
    --pf-chrome-inset: rgba(139, 92, 246, 0.1);

    --pf-modal-bg: rgba(28, 31, 54, 0.97);
    --pf-modal-border: rgba(139, 92, 246, 0.24);

    --accent-fill-rest: #7c3aed;
    --accent-fill-hover: #6d28d9;
    --accent-fill-active: #5b21b6;
    --accent-foreground-rest: #c4b5fd;
}

html.theme-light {
    /* all defaults from :root apply */
}

/* ─── Document + texture ──────────────────────────────────── */
html,
body {
    height: 100%;
    margin: 0;
    font-family: var(--pf-font-sans);
    background: var(--pf-bg);
    color: var(--pf-text);
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

/* Ambient accent glow - a radial violet wash behind the viewport,
   anchored top-center, fading into the canvas. Gives the page depth
   without a full background image. */
body::before {
    content: '';
    position: fixed;
    inset: -20% -10% auto -10%;
    height: 70vh;
    background:
        radial-gradient(ellipse 60% 50% at 50% 0%,
            rgba(var(--pf-accent-rgb), 0.18) 0%,
            rgba(var(--pf-accent-rgb), 0.08) 30%,
            transparent 70%);
    pointer-events: none;
    z-index: 0;
    opacity: 0.9;
    transition: opacity 0.4s var(--pf-ease);
}

/* Very subtle noise texture for surface grit. SVG is data-embedded
   so it works offline and doesn't add a network request. */
body::after {
    content: '';
    position: fixed;
    inset: 0;
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='200' height='200'><filter id='n'><feTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='2' seed='4'/><feColorMatrix values='0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.5 0'/></filter><rect width='100%25' height='100%25' filter='url(%23n)'/></svg>");
    opacity: 0.025;
    pointer-events: none;
    z-index: 0;
    mix-blend-mode: overlay;
}

html.theme-dark body::after {
    opacity: 0.04;
}

#app {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
    position: relative;
    z-index: 1;
}

/* ─── FluentLayout - islands (header/footer detached from edges) ──
   Fluent applies a scoped style `.layout[b-xxxxx]` that pins display
   to flex. We need !important on display + grid-template-rows to win
   the specificity war and get a proper 3-row island grid. */
.layout {
    min-height: 100vh;
    flex: 1;
    padding: 0.75rem !important;
    gap: 0.75rem !important;
    display: grid !important;
    grid-template-rows: auto 1fr auto !important;
    grid-template-columns: 1fr !important;
}

/* Header island - glass card anchored at the top with gutters */
.layout>.header,
fluent-header.header {
    position: sticky;
    top: 0.75rem;
    margin: 0 !important;
    padding: 0 1.25rem !important;
    height: 3.5rem !important;
    max-width: min(87.5rem, 100%);
    width: 100%;
    justify-self: center;
    /* Header background is INTENTIONALLY more translucent than --pf-chrome-bg
       (which sits ~0.9 alpha for chip/menu surfaces). The sticky header passes
       page content underneath as the user scrolls; the backdrop-filter blur
       only reads as a glass effect when there's enough alpha for the blurred
       backdrop to show through. ~0.55-0.65 keeps the chrome readable while
       still letting motion behind it register. */
    background: linear-gradient(135deg,
            rgba(241, 245, 249, 0.62) 0%,
            rgba(241, 245, 249, 0.5) 100%) !important;
    backdrop-filter: blur(20px) saturate(180%);
    -webkit-backdrop-filter: blur(20px) saturate(180%);
    border: 1px solid var(--pf-chrome-border);
    border-radius: var(--pf-radius-lg);
    box-shadow: var(--pf-card-shadow);
    z-index: 50;
}

html.theme-dark .layout>.header,
html.theme-dark fluent-header.header {
    background: linear-gradient(135deg,
            rgba(36, 40, 69, 0.62) 0%,
            rgba(36, 40, 69, 0.5) 100%) !important;
}

@media (prefers-color-scheme: dark) {
    html:not(.theme-light) .layout>.header,
    html:not(.theme-light) fluent-header.header {
        background: linear-gradient(135deg,
                rgba(36, 40, 69, 0.62) 0%,
                rgba(36, 40, 69, 0.5) 100%) !important;
    }
}

/* Smooth blur tail BELOW the header - a slim, fixed strip sitting right
   under the sticky header's bottom edge. Content that scrolls under the
   header already gets blurred by the header's own backdrop-filter; this
   strip adds a short fade-out so the blur doesn't stop with a hard line
   where the header's glass panel ends. The mask makes the effect taper
   to zero within ~18px so nothing beyond that is touched. */
.layout::after {
    content: '';
    position: fixed;
    left: 0;
    right: 0;
    top: 4.25rem;
    /* 12px gap + 56px header height */
    height: 1.125rem;
    pointer-events: none;
    z-index: 40;
    /* under the header (50), above page content */
    backdrop-filter: blur(10px);
    -webkit-backdrop-filter: blur(10px);
    -webkit-mask-image: linear-gradient(to bottom,
            rgba(0, 0, 0, 0.9) 0%,
            rgba(0, 0, 0, 0.5) 45%,
            rgba(0, 0, 0, 0) 100%);
    mask-image: linear-gradient(to bottom,
            rgba(0, 0, 0, 0.9) 0%,
            rgba(0, 0, 0, 0.5) 45%,
            rgba(0, 0, 0, 0) 100%);
}

.layout>.body-content {
    padding: 1.5rem 0;
    /* breathing room between the header island and page content */
    max-width: min(87.5rem, 100%);
    width: 100%;
    justify-self: center;
}

/* ─── Page transition ─────────────────────────────────────────
   The page-enter animation is driven by Web Animations API in
   js/theme.js (preflightNav.replayPageAnim). We use WAAPI instead
   of CSS keyframes because CSS animations get neutralized by the
   browser's reduced-motion emulation in headless/automated test
   contexts; WAAPI runs in every environment and honors the real
   user preference via matchMedia inside the JS. */

/* ─── Theme crossfade ─────────────────────────────────────────
   Two layers stack on top of each other when the user toggles
   light ↔ dark:

   1. CSS transitions below, gated by `<html class="theme-
      transitioning">` (added by js/theme.js for ~500ms around the
      swap). Works in every browser and covers FluentDesignTheme's
      separate re-paint. This is the reliable baseline.

   2. View Transitions API (Chromium/Safari) - a full-viewport
      crossfade on top of #1. Graceful no-op in Firefox. */

html.theme-transitioning body,
html.theme-transitioning .layout>.header,
html.theme-transitioning .layout>.footer,
html.theme-transitioning fluent-header,
html.theme-transitioning fluent-footer,
html.theme-transitioning fluent-card,
html.theme-transitioning .pf-switcher,
html.theme-transitioning .pf-switcher__btn,
html.theme-transitioning .pf-icon-btn,
html.theme-transitioning .fluent-nav-link,
html.theme-transitioning .fluent-messagebar,
html.theme-transitioning .pf-palette,
html.theme-transitioning .pf-help,
html.theme-transitioning .pf-hero-title {
    transition:
        background-color 0.4s var(--pf-ease),
        background 0.4s var(--pf-ease),
        color 0.4s var(--pf-ease),
        border-color 0.4s var(--pf-ease),
        box-shadow 0.4s var(--pf-ease) !important;
}

/* Tunes the default VT root crossfade used by theme toggles in
    js/theme.js. Route changes use a post-render WAAPI animation instead,
    so Blazor keeps full ownership of navigation. */
::view-transition-old(root),
::view-transition-new(root) {
    animation-duration: 0.42s;
    animation-timing-function: var(--pf-ease);
}

/* Footer - deliberately quiet: no island chrome, just muted text
   centered at the bottom of the viewport. Draws no focus. */
.layout>.footer,
fluent-footer.footer {
    margin: 0 !important;
    padding: 0.625rem 1rem !important;
    max-width: min(87.5rem, 100%);
    width: 100%;
    justify-self: center;
    background: transparent !important;
    border: 0 !important;
    color: var(--pf-text-subtle) !important;
    justify-content: center;
    text-align: center;
    min-height: 0 !important;
    height: auto !important;
}

/* ─── Glass cards (aggressive override, static by default) ───
   FluentCard body is replaced with our gradient fill, accent-tinted
   border, inner highlight, and soft shadow. Hover effects are OFF
   by default - only `.pf-card--interactive` (Landing ModeCards,
   PresetPicker presets) lifts and glows on hover. */
fluent-card {
    background: var(--pf-card-bg) !important;
    backdrop-filter: blur(14px) saturate(140%);
    -webkit-backdrop-filter: blur(14px) saturate(140%);
    border: 1px solid var(--pf-card-border) !important;
    border-radius: var(--pf-radius-lg) !important;
    box-shadow:
        var(--pf-card-highlight),
        var(--pf-card-shadow) !important;
    position: relative;
    overflow: hidden;
}

/* Interactive-only: opts into hover lift, accent wash + glow. */
fluent-card.pf-card--interactive {
    transition:
        transform 0.35s var(--pf-ease-out),
        border-color 0.25s var(--pf-ease),
        box-shadow 0.35s var(--pf-ease-out) !important;
    cursor: pointer;
}

fluent-card.pf-card--interactive::before {
    content: '';
    position: absolute;
    inset: -1px;
    border-radius: inherit;
    background: radial-gradient(circle at 20% 0%,
            rgba(var(--pf-accent-rgb), 0.08) 0%,
            transparent 50%);
    opacity: 0;
    transition: opacity 0.35s var(--pf-ease);
    pointer-events: none;
}

fluent-card.pf-card--interactive:hover {
    transform: translateY(-5px) scale(1.005);
    border-color: var(--pf-card-border-hover) !important;
    box-shadow:
        var(--pf-card-highlight),
        var(--pf-card-shadow-hover) !important;
}

/* ─── Mode-card anchor wrapper ──────────────────────────────
   Landing/Wizard cards are wrapped in <a href> so the click-interceptor
   in view-transitions.js can wrap navigation in document.startViewTransition.
   The anchor itself must be invisible - it inherits the card's full
   click target, no underline, no extra spacing. */
.pf-mode-card-anchor {
    display: block;
    height: 100%;
    color: inherit;
    text-decoration: none;
    cursor: pointer;
    border-radius: var(--pf-radius-lg);
}

.pf-mode-card-anchor:focus {
    outline: none;
}

.pf-mode-card-anchor:focus-visible {
    outline: 2px solid var(--pf-accent);
    outline-offset: 2px;
}

fluent-card.pf-card--interactive:active {
    transform: translateY(-1px) scale(0.998);
    transition:
        transform 0.12s var(--pf-ease),
        border-color 0.12s var(--pf-ease),
        box-shadow 0.12s var(--pf-ease) !important;
}

fluent-card.pf-card--interactive:hover::before {
    opacity: 1;
}

/* Fluent message bars - thematic restyle so warnings/info/errors
   speak the pf-* palette instead of Fluent's orange/blue defaults.
   FluentMessageBar renders as <div class="fluent-messagebar intent-*">. */
.fluent-messagebar {
    background: var(--pf-card-bg) !important;
    border: 1px solid var(--pf-border) !important;
    border-radius: var(--pf-radius-md) !important;
    color: var(--pf-text) !important;
    backdrop-filter: blur(8px);
}

.fluent-messagebar.intent-warning {
    border-left: 3px solid var(--pf-warn) !important;
}

.fluent-messagebar.intent-info {
    border-left: 3px solid var(--pf-accent) !important;
}

.fluent-messagebar.intent-error {
    border-left: 3px solid var(--pf-danger) !important;
}

.fluent-messagebar.intent-success {
    border-left: 3px solid var(--pf-success) !important;
}

.fluent-messagebar-icon {
    color: var(--pf-warn);
}

.fluent-messagebar.intent-info .fluent-messagebar-icon {
    color: var(--pf-accent);
}

.fluent-messagebar.intent-error .fluent-messagebar-icon {
    color: var(--pf-danger);
}

.fluent-messagebar.intent-success .fluent-messagebar-icon {
    color: var(--pf-success);
}

/* Fluent nav menu (Advanced + Docs sidebars) - restyle as clear
   solid-surface pill list. Previously we tried transparent bg which
   let the card gradient leak through looking "stripey". Giving each
   link a solid subtle surface makes them read as clear buttons. */

/* Nuke every background/shadow inside the menu - we re-enable only
   what we want explicitly below, avoiding Blazor-scoped leaks. */
.fluent-nav-menu,
.fluent-nav-menu *,
.fluent-nav-menu *::before,
.fluent-nav-menu *::after {
    background-color: transparent !important;
    background-image: none !important;
    box-shadow: none !important;
}

.fluent-nav-menu,
.fluent-nav-menu.fluent-nav-menu {
    border: 0 !important;
    padding: 0 !important;
    margin: 0 !important;
    display: flex !important;
    flex-direction: column !important;
    gap: 2px !important;
}

.fluent-nav-item,
.fluent-nav-item.fluent-nav-item {
    border: 0 !important;
    padding: 0 !important;
    margin: 0 !important;
    min-height: 0 !important;
}

.fluent-nav-link,
.fluent-nav-link.fluent-nav-link,
a.fluent-nav-link {
    display: flex !important;
    align-items: center !important;
    gap: 0.4rem !important;
    padding: 0.25rem 0.5rem !important;
    /* really compact */
    margin: 0 !important;
    border-radius: var(--pf-radius-sm) !important;
    color: var(--pf-text-muted) !important;
    font-family: var(--pf-font-sans) !important;
    font-size: 0.75rem !important;
    /* 12px - sidebar scale */
    line-height: 1.3 !important;
    font-weight: 500 !important;
    text-decoration: none !important;
    border: 1px solid var(--pf-border) !important;
    background-color: var(--pf-surface-2) !important;
    outline: none !important;
    height: auto !important;
    min-height: 0 !important;
    position: relative;
    overflow: hidden;
    transition:
        background-color 0.15s var(--pf-ease),
        color 0.15s var(--pf-ease),
        border-color 0.15s var(--pf-ease),
        box-shadow 0.2s var(--pf-ease),
        transform 0.15s var(--pf-ease) !important;
}

.fluent-nav-link:hover,
a.fluent-nav-link:hover {
    background-color: var(--pf-chrome-bg) !important;
    color: var(--pf-text) !important;
    border-color: var(--pf-accent) !important;
    /* crisp accent edge on hover */
    box-shadow:
        0 0 0 1px var(--pf-accent-ring),
        0 4px 12px rgba(var(--pf-accent-rgb), 0.22) !important;
    transform: translateX(3px);
}

.fluent-nav-link.active,
.fluent-nav-link[aria-current],
.fluent-nav-link[aria-current="page"],
a.fluent-nav-link.active {
    background-color: var(--pf-accent-soft) !important;
    color: var(--pf-accent) !important;
    border-color: var(--pf-accent-ring) !important;
    font-weight: 600 !important;
}

.fluent-nav-icon {
    display: none !important;
    /* Fluent-reserved icon slot is empty */
}

.fluent-nav-text,
.fluent-nav-text.fluent-nav-text {
    flex: 1 !important;
    padding: 0 !important;
    margin: 0 !important;
    overflow: hidden !important;
    text-overflow: ellipsis !important;
    white-space: nowrap !important;
    color: inherit !important;
    font: inherit !important;
}

/* Primary CTA buttons - premium gradient with lift on hover */
fluent-button[appearance="accent"]::part(control) {
    background: var(--pf-accent-gradient);
    border: 0;
    border-radius: var(--pf-radius-md);
    font-weight: 500;
    box-shadow:
        0 1px 0 rgba(255, 255, 255, 0.15) inset,
        0 2px 8px rgba(var(--pf-accent-rgb), 0.3);
    transition:
        filter 0.2s var(--pf-ease),
        transform 0.2s var(--pf-ease-out),
        box-shadow 0.3s var(--pf-ease-out);
}

fluent-button[appearance="accent"]:hover::part(control) {
    filter: brightness(1.1) saturate(1.08);
    transform: translateY(-2px);
    box-shadow:
        0 1px 0 rgba(255, 255, 255, 0.2) inset,
        0 10px 28px rgba(var(--pf-accent-rgb), 0.5);
}

/* Press: pull below the resting line so the click feels tactile.
   transition kept short (120ms) so the tap reads as snappy, not laggy. */
fluent-button[appearance="accent"]:active::part(control) {
    transform: translateY(1px) scale(0.985);
    filter: brightness(0.92);
    transition:
        filter 0.12s var(--pf-ease),
        transform 0.12s var(--pf-ease),
        box-shadow 0.12s var(--pf-ease);
}

/* ─── Brand logo (gradient-filled airplane glyph) ─────────────
   The ✈ char uses variation selector U+FE0E (text presentation)
   so the browser renders it as an outlined shape - which makes
   `background-clip: text` work and the airplane takes the hero-
   title gradient (slate text → indigo/violet accent).
   No pill bg - cleaner, the shape IS the logo. */
.pf-brand {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 auto;
    min-width: 2.75rem;
    height: 2.75rem;
    padding: 0 0.375rem;
    overflow: visible;
    font-size: 1.75rem;
    line-height: 1;
    text-decoration: none;
}

.pf-brand:focus-visible {
    outline: 2px solid var(--pf-accent-ring);
    outline-offset: 4px;
    border-radius: var(--pf-radius-md);
}

/* Brand mark SVG - shared by header (.pf-brand), hero (.pf-hero-icon)
   and 404 (.pf-404__icon). Sizing/glow is parent-driven so each context
   can pick what fits; base is just a smooth-rendering block.

   No hover animation here: the earlier scale(1.08) on `.pf-brand` plus
   translate(2px,-1px) on the inner SVG pushed the icon's drop-shadow
   past the header's rounded clip on every hover - visually clipping
   the logo. A brand mark should feel anchored, not "react". The
   browser-default cursor on <a> and the focus-visible outline above
   carry all the affordance this needs. */
.pf-brand-icon {
    display: block;
}

/* Header pill: fixed compact size that fits the 2.75rem header chip. */
.pf-brand .pf-brand-icon {
    width: 2rem;
    height: 2rem;
    filter: drop-shadow(0 2px 10px var(--pf-accent-ring));
}

/* Hero (.pf-hero-title--with-icon) - icon scales with the surrounding
   text, sits to the left of "preflight.xml". The h1 itself becomes
   inline-flex so it can be centered inside the (text-align:center)
   wrapper without breaking onto two lines. */
.pf-hero-title--with-icon {
    display: inline-flex;
    align-items: center;
    gap: 0.4em;
}

.pf-hero-title .pf-hero-icon {
    width: 1em;
    height: 1em;
    flex: 0 0 auto;
    filter: drop-shadow(0 6px 18px rgba(var(--pf-accent-rgb), 0.32));
}

/* ─── Footer - quiet inline text, not a chip ────────────── */
.pf-footer-line {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    flex-wrap: wrap;
    justify-content: center;
    font-family: var(--pf-font-sans);
    font-size: 0.75rem;
    color: var(--pf-text-subtle);
    line-height: 1.4;
}

.pf-footer-sep {
    opacity: 0.5;
    user-select: none;
}

/* Author - plain inline link. Tiny GitHub mark before the name,
   no background or border. Accent colour reveals only on hover. */
.pf-author {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    color: inherit;
    text-decoration: none;
    font-weight: 500;
    transition: color 0.2s var(--pf-ease);
}

.pf-author svg {
    opacity: 0.7;
    transition: opacity 0.2s var(--pf-ease), color 0.2s var(--pf-ease);
}

.pf-author:hover {
    color: var(--pf-accent);
}

.pf-author:hover svg {
    opacity: 1;
    color: var(--pf-accent);
}

.pf-author:focus-visible,
.pf-footer-link:focus-visible {
    outline: 2px solid var(--pf-accent-ring);
    outline-offset: 2px;
    border-radius: 4px;
}

/* MIT / other minor footer links */
.pf-footer-link {
    color: inherit;
    text-decoration: none;
    font-weight: 500;
    transition: color 0.2s var(--pf-ease);
}

.pf-footer-link:hover {
    color: var(--pf-accent);
}

/* Footer items rendered as <button> (e.g. "What's new" → opens modal,
   not a navigable URL) need to look identical to <a> siblings. Strip the
   default button chrome and inherit the link family typography. */
.pf-footer-link--btn {
    appearance: none;
    background: transparent;
    border: 0;
    padding: 0;
    cursor: pointer;
    font: inherit;
}

/* ─── Modernized Fluent outline/hypertext buttons ───────────
   Glass pills that play nice with the new palette. */
fluent-button[appearance="outline"]::part(control),
fluent-anchor[appearance="outline"]::part(control) {
    background: var(--pf-chrome-bg) !important;
    border: 1px solid var(--pf-border-strong) !important;
    color: var(--pf-text) !important;
    border-radius: var(--pf-radius-md) !important;
    font-family: var(--pf-font-sans) !important;
    font-weight: 500 !important;
    backdrop-filter: blur(10px);
    transition:
        background 0.2s var(--pf-ease),
        border-color 0.2s var(--pf-ease),
        transform 0.2s var(--pf-ease),
        box-shadow 0.25s var(--pf-ease) !important;
}

fluent-button[appearance="outline"]:hover::part(control),
fluent-anchor[appearance="outline"]:hover::part(control) {
    border-color: var(--pf-accent) !important;
    color: var(--pf-accent) !important;
    transform: translateY(-2px);
    box-shadow: 0 6px 16px var(--pf-accent-ring) !important;
}

fluent-button[appearance="outline"]:active::part(control),
fluent-anchor[appearance="outline"]:active::part(control) {
    transform: translateY(1px) scale(0.985) !important;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.18) !important;
    transition:
        background 0.12s var(--pf-ease),
        border-color 0.12s var(--pf-ease),
        transform 0.12s var(--pf-ease),
        box-shadow 0.12s var(--pf-ease) !important;
}

/* Small SVG icon slotted into a Fluent button label. Aligns with text baseline. */
.pf-btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    margin-right: 0.375rem;
    vertical-align: -0.15em;
}

.pf-btn-icon svg {
    display: block;
    color: currentColor;
}

/* Hypertext anchors - minimal, accent on hover only */
fluent-anchor[appearance="hypertext"] {
    color: var(--pf-text-muted) !important;
    transition: color 0.2s var(--pf-ease) !important;
    font-weight: 500 !important;
}

fluent-anchor[appearance="hypertext"]:hover {
    color: var(--pf-accent) !important;
}

/* ─── Breadcrumbs in header ─────────────────────────────── */
.pf-breadcrumbs {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-family: var(--pf-font-sans);
    font-size: 0.875rem;
    font-weight: 500;
    color: var(--pf-text-muted);
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.pf-breadcrumbs__link {
    color: var(--pf-text-muted);
    text-decoration: none;
    transition: color 0.2s var(--pf-ease);
}

.pf-breadcrumbs__link:hover {
    color: var(--pf-accent);
}

.pf-breadcrumbs__current {
    color: var(--pf-text);
}

.pf-breadcrumbs__sep {
    color: var(--pf-text-subtle);
    user-select: none;
    font-size: 0.8125rem;
}

/* ─── Icon button (⌘K, etc.) ─────────────────────────────── */
.pf-icon-btn {
    appearance: none;
    background: var(--pf-chrome-bg);
    border: 1px solid var(--pf-chrome-border);
    color: var(--pf-text-muted);
    font-family: var(--pf-font-mono);
    font-size: 0.75rem;
    font-weight: 600;
    padding: 0.375rem 0.625rem;
    border-radius: var(--pf-radius-md);
    cursor: pointer;
    transition: all 0.2s var(--pf-ease);
    backdrop-filter: blur(10px);
    letter-spacing: 0.02em;
}

.pf-icon-btn:hover {
    color: var(--pf-accent);
    border-color: var(--pf-accent);
    transform: translateY(-1px);
}

.pf-icon-btn:focus-visible {
    outline: 2px solid var(--pf-accent-ring);
    outline-offset: 2px;
}

/* ─── Modal overlay (used by palette + help) ─────────────────
   Always laid out (display: flex) but hidden via opacity + visibility +
   pointer-events so the OPEN/CLOSE transitions actually run. With the old
   `display: none → flex` toggle the browser skipped the opacity transition
   on first paint of the modal; the result was an instant pop-in/pop-out
   that read as "wooden". Visibility is delayed on close so the fade has
   time to complete before the element becomes inert. */
.pf-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.5);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    display: flex;
    /* Center vertically by default - the previous flex-start + 15vh padding
       was a Spotlight-style anchor that made bigger info modals (changelog,
       palette in scroll mode) read as "stuck to the top". 4vh padding keeps
       a small breathing strip on tall viewports while still letting tall
       content scroll inside its own modal body. */
    align-items: center;
    justify-content: center;
    padding: 4vh 1rem;
    z-index: 1000;
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
    transition:
        opacity 0.28s var(--pf-ease),
        backdrop-filter 0.28s var(--pf-ease),
        visibility 0s linear 0.28s;
}

.pf-overlay.is-open {
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
    transition:
        opacity 0.28s var(--pf-ease),
        backdrop-filter 0.28s var(--pf-ease),
        visibility 0s linear 0s;
}

/* ─── Command palette ─────────────────────────────────────── */
.pf-palette {
    width: min(40rem, 92vw);
    max-height: 70vh;
    background: var(--pf-modal-bg);
    backdrop-filter: blur(24px) saturate(180%);
    -webkit-backdrop-filter: blur(24px) saturate(180%);
    border: 1px solid var(--pf-modal-border);
    border-radius: var(--pf-radius-lg);
    box-shadow:
        var(--pf-card-highlight),
        0 24px 80px rgba(0, 0, 0, 0.3),
        0 0 80px var(--pf-accent-ring);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    font-family: var(--pf-font-sans);
    /* Resting (closed) state - pulled up & slightly shrunk. The .pf-overlay
       wrapper handles the fade; here we drive the snap-in/out with transform
       so closing also gets a transition (the old animation: paletteIn only
       ran on entry). */
    opacity: 0;
    transform: translateY(-16px) scale(0.96);
    transition:
        opacity 0.32s var(--pf-ease-out),
        transform 0.4s var(--pf-ease-out);
}

.pf-overlay.is-open .pf-palette {
    opacity: 1;
    transform: translateY(0) scale(1);
}

.pf-palette__input-row {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 1rem 1.25rem;
    border-bottom: 1px solid var(--pf-border);
}

.pf-palette__prefix {
    color: var(--pf-accent);
    font-size: 1.125rem;
    font-family: var(--pf-font-mono);
    font-weight: 600;
}

.pf-palette__input {
    flex: 1;
    appearance: none;
    background: transparent;
    border: 0;
    outline: 0;
    font-family: var(--pf-font-sans);
    font-size: 1rem;
    color: var(--pf-text);
    min-width: 0;
}

.pf-palette__input::placeholder {
    color: var(--pf-text-subtle);
}

.pf-palette__hint {
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-sm);
    padding: 0.125rem 0.375rem;
    font-family: var(--pf-font-mono);
    font-size: 0.6875rem;
    color: var(--pf-text-subtle);
}

.pf-palette__list {
    list-style: none;
    margin: 0;
    padding: 0.375rem;
    overflow-y: auto;
    flex: 1;
}

.pf-palette__item {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.625rem 0.875rem;
    border-radius: var(--pf-radius-md);
    cursor: pointer;
    color: var(--pf-text);
    transition: background 0.12s var(--pf-ease);
}

.pf-palette__item.is-selected {
    background: var(--pf-accent-soft);
    color: var(--pf-accent-strong);
}

.pf-palette__item.is-selected .pf-palette__group,
.pf-palette__item.is-selected .pf-palette__hint-text {
    color: var(--pf-accent);
}

.pf-palette__icon {
    font-size: 1.25rem;
    line-height: 1;
    flex-shrink: 0;
}

.pf-palette__text {
    flex: 1;
    min-width: 0;
}

.pf-palette__label {
    font-size: 0.875rem;
    font-weight: 500;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.pf-palette__hint-text {
    font-size: 0.75rem;
    color: var(--pf-text-muted);
    font-family: var(--pf-font-mono);
    margin-top: 0.125rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.pf-palette__group {
    font-size: 0.6875rem;
    color: var(--pf-text-subtle);
    text-transform: uppercase;
    letter-spacing: 0.06em;
    font-weight: 600;
    flex-shrink: 0;
}

.pf-palette__empty {
    padding: 2rem 1rem;
    text-align: center;
    color: var(--pf-text-subtle);
    font-size: 0.875rem;
}

.pf-palette__footer {
    display: flex;
    justify-content: center;
    gap: 1.25rem;
    padding: 0.625rem 1rem;
    border-top: 1px solid var(--pf-border);
    font-size: 0.75rem;
    color: var(--pf-text-subtle);
}

.pf-palette__footer kbd {
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-sm);
    padding: 0.0625rem 0.25rem;
    margin-right: 0.25rem;
    font-family: var(--pf-font-mono);
    font-size: 0.6875rem;
}

/* ─── Shortcuts help overlay ─────────────────────────────── */
.pf-help {
    width: min(26.25rem, 92vw);
    background: var(--pf-modal-bg);
    backdrop-filter: blur(24px) saturate(180%);
    border: 1px solid var(--pf-modal-border);
    border-radius: var(--pf-radius-lg);
    box-shadow:
        var(--pf-card-highlight),
        0 24px 80px rgba(0, 0, 0, 0.3);
    font-family: var(--pf-font-sans);
    overflow: hidden;
    /* Same open/close pattern as .pf-palette - both transitions run via
       the parent .pf-overlay.is-open class, so closing animates too. */
    opacity: 0;
    transform: translateY(-16px) scale(0.96);
    transition:
        opacity 0.32s var(--pf-ease-out),
        transform 0.4s var(--pf-ease-out);
}

.pf-overlay.is-open .pf-help {
    opacity: 1;
    transform: translateY(0) scale(1);
}

.pf-help__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1rem 1.25rem;
    border-bottom: 1px solid var(--pf-border);
}

.pf-help__title {
    font-size: 1rem;
    font-weight: 600;
    color: var(--pf-text);
}

.pf-help__close {
    appearance: none;
    background: transparent;
    border: 0;
    color: var(--pf-text-subtle);
    font-size: 0.875rem;
    cursor: pointer;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: var(--pf-radius-sm);
    transition: all 0.15s var(--pf-ease);
}

.pf-help__close:hover {
    color: var(--pf-text);
    background: var(--pf-surface-2);
}

.pf-help__list {
    list-style: none;
    margin: 0;
    padding: 0.75rem;
}

.pf-help__item {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0.5rem 0.625rem;
    font-size: 0.875rem;
    color: var(--pf-text);
}

.pf-help__item:hover {
    background: var(--pf-surface-2);
    border-radius: var(--pf-radius-sm);
}

.pf-help__keys {
    display: inline-flex;
    gap: 0.25rem;
}

.pf-help__keys kbd,
.pf-palette__footer kbd {
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-sm);
    padding: 0.125rem 0.375rem;
    font-family: var(--pf-font-mono);
    font-size: 0.75rem;
    color: var(--pf-text);
    line-height: 1;
}

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

.pf-help__footer {
    padding: 0.625rem 1rem;
    border-top: 1px solid var(--pf-border);
    text-align: center;
    font-size: 0.75rem;
    color: var(--pf-text-subtle);
}

/* ─── Changelog modal ────────────────────────────────────────
   "What's new" overlay rendered from CHANGELOG.md (embedded resource).
   Mirrors .pf-help / .pf-palette - same chrome family - but wider so
   release notes have room to breathe and the body scrolls when
   the changelog grows past one viewport. */
.pf-changelog {
    width: min(48rem, 92vw);
    max-height: 80vh;
    background: var(--pf-modal-bg);
    backdrop-filter: blur(24px) saturate(180%);
    -webkit-backdrop-filter: blur(24px) saturate(180%);
    border: 1px solid var(--pf-modal-border);
    border-radius: var(--pf-radius-lg);
    box-shadow:
        var(--pf-card-highlight),
        0 24px 80px rgba(0, 0, 0, 0.3),
        0 0 80px var(--pf-accent-ring);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    font-family: var(--pf-font-sans);
    /* Same open/close pattern as .pf-help / .pf-palette - both transitions
       run via the parent .pf-overlay.is-open class, so closing animates too. */
    opacity: 0;
    transform: translateY(-16px) scale(0.96);
    transition:
        opacity 0.32s var(--pf-ease-out),
        transform 0.4s var(--pf-ease-out);
}

.pf-overlay.is-open .pf-changelog {
    opacity: 1;
    transform: translateY(0) scale(1);
}

.pf-changelog__header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 1rem 1.25rem;
    border-bottom: 1px solid var(--pf-border);
    flex: 0 0 auto;
}

.pf-changelog__title {
    font-size: 1.0625rem;
    font-weight: 600;
    color: var(--pf-text);
}

.pf-changelog__close {
    appearance: none;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--pf-radius-sm);
    width: 2rem;
    height: 2rem;
    cursor: pointer;
    color: var(--pf-text-muted);
    font-size: 1rem;
    line-height: 1;
    transition:
        background 0.18s var(--pf-ease),
        color 0.18s var(--pf-ease),
        border-color 0.18s var(--pf-ease);
}

.pf-changelog__close:hover {
    background: var(--pf-surface-2);
    color: var(--pf-text);
    border-color: var(--pf-border);
}

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

.pf-changelog__body {
    overflow-y: auto;
    flex: 1 1 auto;
    padding: 1.25rem 1.5rem;
}

/* Markdown-rendered content. Markdig emits a flat sequence of <h2>, <h3>,
   <ul>, <p>, <code>, <a> - so we style them generically inside this
   scope to keep the changelog consistent without polluting global rules. */
.pf-changelog__content {
    color: var(--pf-text);
    font-size: 0.9375rem;
    line-height: 1.55;
}

.pf-changelog__content> :first-child {
    margin-top: 0;
}

.pf-changelog__content> :last-child {
    margin-bottom: 0;
}

.pf-changelog__content h1,
.pf-changelog__content h2 {
    font-size: 1.125rem;
    font-weight: 700;
    color: var(--pf-text);
    margin: 1.5rem 0 0.5rem;
    padding-bottom: 0.375rem;
    border-bottom: 1px solid var(--pf-border);
}

.pf-changelog__content h3 {
    font-size: 0.9375rem;
    font-weight: 700;
    color: var(--pf-accent);
    margin: 1.25rem 0 0.5rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.pf-changelog__content h4 {
    font-size: 0.875rem;
    font-weight: 600;
    color: var(--pf-text-muted);
    margin: 1rem 0 0.375rem;
}

.pf-changelog__content p {
    margin: 0.5rem 0;
}

.pf-changelog__content ul,
.pf-changelog__content ol {
    padding-left: 1.25rem;
    margin: 0.5rem 0;
}

.pf-changelog__content li {
    margin: 0.25rem 0;
}

.pf-changelog__content a {
    color: var(--pf-accent);
    text-decoration: none;
    border-bottom: 1px dotted var(--pf-accent-ring);
    transition: color 0.15s var(--pf-ease);
}

.pf-changelog__content a:hover {
    color: var(--pf-accent-strong, var(--pf-accent));
    border-bottom-style: solid;
}

.pf-changelog__content code {
    font-family: var(--pf-font-mono);
    font-size: 0.85em;
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-sm);
    padding: 0.0625rem 0.3125rem;
}

.pf-changelog__content pre {
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-md);
    padding: 0.75rem 1rem;
    overflow-x: auto;
    font-size: 0.8125rem;
}

.pf-changelog__content pre code {
    background: transparent;
    border: 0;
    padding: 0;
}

.pf-changelog__content blockquote {
    margin: 0.75rem 0;
    padding: 0.5rem 1rem;
    border-left: 3px solid var(--pf-accent);
    background: var(--pf-surface-2);
    color: var(--pf-text-muted);
    border-radius: 0 var(--pf-radius-sm) var(--pf-radius-sm) 0;
}

.pf-changelog__content hr {
    border: 0;
    border-top: 1px solid var(--pf-border);
    margin: 1.5rem 0;
}

.pf-changelog__footer {
    padding: 0.75rem 1.25rem;
    border-top: 1px solid var(--pf-border);
    display: flex;
    justify-content: flex-end;
    flex: 0 0 auto;
}

.pf-changelog__source {
    color: var(--pf-text-muted);
    text-decoration: none;
    font-size: 0.8125rem;
    transition: color 0.15s var(--pf-ease);
}

.pf-changelog__source:hover {
    color: var(--pf-accent);
}

/* ─── Import XML modal ──────────────────────────────────────
   Two equally-prominent paths: drop zone (drag-and-drop or click → native
   picker) and paste pad. Same chrome family as .pf-changelog / .pf-help. */
.pf-import {
    width: min(36rem, 92vw);
    max-height: 86vh;
    background: var(--pf-modal-bg);
    backdrop-filter: blur(24px) saturate(180%);
    -webkit-backdrop-filter: blur(24px) saturate(180%);
    border: 1px solid var(--pf-modal-border);
    border-radius: var(--pf-radius-lg);
    box-shadow:
        var(--pf-card-highlight),
        0 24px 80px rgba(0, 0, 0, 0.3),
        0 0 80px var(--pf-accent-ring);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    font-family: var(--pf-font-sans);
    opacity: 0;
    transform: translateY(-16px) scale(0.96);
    transition:
        opacity 0.32s var(--pf-ease-out),
        transform 0.4s var(--pf-ease-out);
}

.pf-overlay.is-open .pf-import {
    opacity: 1;
    transform: translateY(0) scale(1);
}

.pf-import__header,
.pf-import__footer {
    display: flex;
    align-items: center;
    padding: 1rem 1.25rem;
    flex: 0 0 auto;
}

.pf-import__header {
    justify-content: space-between;
    border-bottom: 1px solid var(--pf-border);
}

.pf-import__title {
    font-size: 1.0625rem;
    font-weight: 600;
    color: var(--pf-text);
}

.pf-import__close {
    appearance: none;
    background: transparent;
    border: 1px solid transparent;
    border-radius: var(--pf-radius-sm);
    width: 2rem;
    height: 2rem;
    cursor: pointer;
    color: var(--pf-text-muted);
    font-size: 1rem;
    line-height: 1;
    transition:
        background 0.18s var(--pf-ease),
        color 0.18s var(--pf-ease),
        border-color 0.18s var(--pf-ease);
}

.pf-import__close:hover {
    background: var(--pf-surface-2);
    color: var(--pf-text);
    border-color: var(--pf-border);
}

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

.pf-import__body {
    overflow-y: auto;
    flex: 1 1 auto;
    padding: 1.25rem 1.5rem;
    display: flex;
    flex-direction: column;
    gap: 1rem;
}

/* Drop zone - dashed border, big icon, hover/dragover lift. Click anywhere
   inside opens the native picker as fallback for non-drag environments. */
.pf-import__dropzone {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 1.5rem 1.25rem;
    border: 2px dashed var(--pf-border-strong);
    border-radius: var(--pf-radius-md);
    background: var(--pf-surface-2);
    cursor: pointer;
    transition:
        border-color 0.2s var(--pf-ease),
        background 0.2s var(--pf-ease),
        transform 0.2s var(--pf-ease-out);
}

.pf-import__dropzone:hover {
    border-color: var(--pf-accent);
    background: rgba(var(--pf-accent-rgb), 0.06);
}

.pf-import__dropzone.is-dragover {
    border-color: var(--pf-accent);
    background: rgba(var(--pf-accent-rgb), 0.12);
    transform: scale(1.01);
}

.pf-import__dropzone-icon {
    font-size: 2.25rem;
    line-height: 1;
    margin-bottom: 0.5rem;
}

.pf-import__dropzone-title {
    font-size: 0.9375rem;
    font-weight: 600;
    color: var(--pf-text);
    margin-bottom: 0.25rem;
}

.pf-import__dropzone-hint {
    font-size: 0.8125rem;
    color: var(--pf-text-muted);
    margin-bottom: 0.875rem;
}

.pf-import__browse {
    appearance: none;
    background: var(--pf-chrome-bg);
    border: 1px solid var(--pf-border-strong);
    border-radius: var(--pf-radius-md);
    color: var(--pf-text);
    padding: 0.4375rem 0.875rem;
    font: inherit;
    font-weight: 500;
    cursor: pointer;
    transition:
        border-color 0.18s var(--pf-ease),
        background 0.18s var(--pf-ease),
        color 0.18s var(--pf-ease),
        transform 0.18s var(--pf-ease);
}

.pf-import__browse:hover {
    border-color: var(--pf-accent);
    color: var(--pf-accent);
    transform: translateY(-1px);
}

.pf-import__browse:active {
    transform: translateY(0);
}

/* "OR" divider between drop zone and paste pad - two thin lines flanking
   a centred label. */
.pf-import__or {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    color: var(--pf-text-subtle);
    font-size: 0.75rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

.pf-import__or-line {
    flex: 1;
    height: 1px;
    background: var(--pf-border);
}

.pf-import__paste-label {
    display: flex;
    flex-direction: column;
    gap: 0.375rem;
}

.pf-import__paste-title {
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--pf-text-muted);
    text-transform: uppercase;
    letter-spacing: 0.04em;
}

.pf-import__paste {
    width: 100%;
    box-sizing: border-box;
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-md);
    color: var(--pf-text);
    font-family: var(--pf-font-mono);
    font-size: 0.8125rem;
    line-height: 1.45;
    padding: 0.625rem 0.75rem;
    resize: vertical;
    transition:
        border-color 0.18s var(--pf-ease),
        box-shadow 0.18s var(--pf-ease);
}

.pf-import__paste:focus {
    outline: none;
    border-color: var(--pf-accent);
    box-shadow: 0 0 0 3px var(--pf-accent-ring);
}

.pf-import__status {
    padding: 0.625rem 0.875rem;
    border-radius: var(--pf-radius-md);
    font-size: 0.875rem;
    line-height: 1.45;
    border: 1px solid var(--pf-border);
    background: var(--pf-surface-2);
    color: var(--pf-text);
}

.pf-import__status--error {
    border-left: 3px solid var(--pf-danger);
}

.pf-import__status--warn {
    border-left: 3px solid var(--pf-warn);
}

.pf-import__status--success {
    border-left: 3px solid var(--pf-accent);
}

.pf-import__footer {
    justify-content: flex-end;
    gap: 0.5rem;
    border-top: 1px solid var(--pf-border);
}

.pf-import__secondary {
    appearance: none;
    background: transparent;
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-md);
    color: var(--pf-text-muted);
    padding: 0.4375rem 0.875rem;
    font: inherit;
    font-weight: 500;
    cursor: pointer;
    transition:
        background 0.18s var(--pf-ease),
        color 0.18s var(--pf-ease),
        border-color 0.18s var(--pf-ease);
}

.pf-import__secondary:hover {
    background: var(--pf-surface-2);
    color: var(--pf-text);
    border-color: var(--pf-border-strong);
}

/* ─── Section content (Docs + Advanced) ─────────────────────
   Shared shell for reusable config sections. Docs pages hide the
   internal header and lean on the outer page heading; Advanced keeps
   the local title/subtitle above the live controls. 
   
   NOTE: sections contain dropdowns (FluentSelect) which need to escape
   card boundaries; we set overflow: visible and ensure parent cards
   and grids don't clip content. */
.pf-section {
    display: flex;
    flex-direction: column;
    gap: 1.25rem;
    overflow: visible;
}

.pf-section__head {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    padding-bottom: 1rem;
    border-bottom: 1px solid var(--pf-border);
}

.pf-section__title {
    margin: 0;
    font-size: clamp(1.5rem, 2vw, 1.875rem);
    line-height: 1.1;
    font-weight: 700;
    color: var(--pf-text);
    letter-spacing: -0.02em;
}

.pf-section__subtitle {
    margin: 0;
    max-width: 68ch;
    color: var(--pf-text-muted);
    font-size: 1rem;
    line-height: 1.6;
}

.pf-section__docs-link {
    align-self: flex-start;
    color: var(--pf-accent);
    text-decoration: none;
    font-weight: 600;
    transition: color 0.2s var(--pf-ease);
}

.pf-section__docs-link:hover {
    color: var(--pf-accent-strong);
}

.pf-section__intro {
    padding: 1.25rem 1.375rem;
    background: var(--pf-card-bg);
    border: 1px solid var(--pf-card-border);
    border-radius: var(--pf-radius-lg);
    box-shadow: var(--pf-card-highlight), var(--pf-card-shadow);
}

.pf-section__intro> :first-child,
.pf-section__intro p:first-child,
.pf-section__intro h1:first-child,
.pf-section__intro h2:first-child,
.pf-section__intro h3:first-child {
    margin-top: 0;
}

.pf-section__intro> :last-child {
    margin-bottom: 0;
}

.pf-section__intro h1,
.pf-section__intro h2,
.pf-section__intro h3,
.pf-section__intro h4 {
    margin: 1.5rem 0 0.75rem;
    color: var(--pf-text);
    line-height: 1.2;
    letter-spacing: -0.02em;
}

.pf-section__intro h2 {
    font-size: clamp(1.25rem, 1.5vw, 1.5rem);
}

.pf-section__intro h3 {
    font-size: 1.0625rem;
}

.pf-section__intro p,
.pf-section__intro li {
    color: var(--pf-text-muted);
    font-size: 0.975rem;
    line-height: 1.75;
}

.pf-section__intro p {
    margin: 0.75rem 0;
}

.pf-section__intro ul,
.pf-section__intro ol {
    margin: 0.875rem 0;
    padding-left: 1.3rem;
}

.pf-section__intro li+li {
    margin-top: 0.55rem;
}

.pf-section__intro strong {
    color: var(--pf-text);
    font-weight: 700;
}

.pf-section__intro code {
    font-family: var(--pf-font-mono);
    font-size: 0.9em;
    padding: 0.14rem 0.34rem;
    border-radius: 6px;
    color: var(--pf-accent-strong);
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
}

/* Docs/intro images: constrain size, add card treatment, and let the following
   `<em>` line act as a caption so scraped reference screenshots don't blow up
   the page width. Images are rendered as block so the caption sits below. */
.pf-section__intro img {
    display: block;
    max-width: 100%;
    height: auto;
    margin: 0.75rem 0 0.25rem;
    padding: 0.375rem;
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-md);
}

.pf-section__intro p>img+em,
.pf-section__intro p>img:first-child+em {
    display: block;
    color: var(--pf-text-muted);
    font-size: 0.86rem;
    margin-bottom: 0.75rem;
}

.pf-section__options {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    overflow: visible;
}

.pf-option {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
    padding: 1rem 1.125rem;
    background: var(--pf-card-bg);
    border: 1px solid var(--pf-card-border);
    border-radius: var(--pf-radius-lg);
    box-shadow: var(--pf-card-highlight), var(--pf-card-shadow);
    position: relative;
    overflow: visible;
}

.pf-option__head {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.75rem;
}

.pf-option__toggle-head {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
}

.pf-section--advanced .pf-option {
    padding: 0.75rem 0.9rem;
    background: rgba(var(--pf-bg-rgb), 0.55);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-md);
    box-shadow: none;
}

.pf-section--advanced .pf-option:hover {
    border-color: var(--pf-border-strong);
}

.pf-section--advanced .pf-option--toggle {
    gap: 0.35rem;
}

.pf-option__label {
    margin: 0;
    color: var(--pf-text);
    font-size: 0.98rem;
    line-height: 1.35;
    font-weight: 700;
}

.pf-option__learn-more {
    flex: 0 0 auto;
    color: var(--pf-accent);
    text-decoration: none;
    font-size: 0.875rem;
    font-weight: 600;
    white-space: nowrap;
}

.pf-option__learn-more:hover {
    color: var(--pf-accent-strong);
}

.pf-option__description {
    margin: 0;
    color: var(--pf-text-muted);
    font-size: 0.92rem;
    line-height: 1.5;
}

.pf-section--advanced .pf-option__description {
    line-height: 1.45;
}

.pf-docs-entry>.pf-section {
    margin-top: 0.5rem;
}

/* Docs reading view: tighter vertical rhythm than the default .pf-section__intro,
   which is tuned for the Advanced-mode context where there is also a form below.
   Docs pages are pure reading, so we match the rest of the app's body typography
   (0.875rem / line-height 1.45) instead of the looser reading preset. */
.pf-docs-entry>div>*+*,
.pf-docs-entry .pf-section {
    gap: 0.5rem;
}

.pf-docs-entry .pf-section__intro {
    padding: 0.75rem 1rem;
}

.pf-docs-entry .pf-section__intro p,
.pf-docs-entry .pf-section__intro li {
    font-size: 0.875rem;
    line-height: 1.5;
}

.pf-docs-entry .pf-section__intro p {
    margin: 0.35rem 0;
}

.pf-docs-entry .pf-section__intro ul,
.pf-docs-entry .pf-section__intro ol {
    margin: 0.5rem 0;
    padding-left: 1.15rem;
}

.pf-docs-entry .pf-section__intro li+li {
    margin-top: 0.2rem;
}

.pf-docs-entry .pf-section__intro h2 {
    font-size: 1.125rem;
    margin: 0.75rem 0 0.2rem;
}

.pf-docs-entry .pf-section__intro h3 {
    font-size: 1rem;
    margin: 0.625rem 0 0.15rem;
}

.pf-docs-entry .pf-section__intro h4 {
    font-size: 0.95rem;
    margin: 0.5rem 0 0.15rem;
}

.pf-docs-entry .pf-section__intro> :first-child,
.pf-docs-entry .pf-section__intro> :first-child> :first-child {
    margin-top: 0;
}

.pf-docs-entry .pf-option {
    padding: 0.65rem 0.85rem;
    gap: 0.35rem;
}

.pf-docs-entry .pf-option__description {
    font-size: 0.82rem;
    line-height: 1.45;
}

/* The main h1 + subtitle pair at the top of a Docs page is currently rendered by
   FluentLabel as <h2 class="fluent-typography"> at 32px - too heavy for a docs
   reference. Scale it down to the size we use for section titles elsewhere so the
   page reads like polished reference, not landing-page marketing. The selector
   targets FluentLabel's first heading inside the fluent-grid-item that holds the
   main content column; intro <h1>/<h2> from markdown are left alone. */
.pf-docs-entry fluent-grid-item>.fluent-typography,
.pf-docs-entry fluent-grid-item>h1.fluent-typography,
.pf-docs-entry fluent-grid-item>h2.fluent-typography,
.pf-docs-entry>div>fluent-grid>fluent-grid-item h2.fluent-typography:first-of-type {
    font-size: clamp(1.5rem, 2vw, 1.875rem) !important;
    line-height: 1.15 !important;
    font-weight: 700 !important;
}

.pf-docs-entry fluent-label[typo="Body"] .fluent-typography,
.pf-docs-entry .fluent-typography+.fluent-typography {
    font-size: 0.92rem;
}

/* Advanced page layout: allow all content to flow naturally.
   FluentGrid is a flex row; default align-items (stretch) makes the main
   content column grow to match the tall left navigation. Pin items to the
   top explicitly so the content card hugs its own height. */
fluent-grid,
fluent-grid-item,
fluent-card {
    overflow: visible !important;
}

.pf-advanced-shell .fluent-grid {
    align-items: flex-start !important;
}

.pf-advanced-shell fluent-grid-item>div {
    align-self: flex-start;
}

.pf-advanced-shell fluent-card {
    height: auto;
}

/* ─── FluentSelect dropdown sizing ───────────────────────────
   Long option lists (editions, time zones, components) have to stay
   scrollable inside a fixed max-height box - otherwise the listbox
   runs past the option card it's in and the panel beneath. Rules hit
   every FluentSelect (not just .pf-select) because custom section
   components mount bare FluentSelect and also need this behaviour.

   Three selector paths cover the Fluent v4 shadow DOM surface:
   1. ::part(listbox) / ::part(positioning-region) - primary
   2. role="listbox" - fallback for browsers that don't expose parts yet
   3. fluent-select itself - make sure its stacking context can extend
      beyond the surrounding card. */
fluent-select::part(listbox),
fluent-select::part(positioning-region) {
    max-height: 15rem !important;
    overflow-y: auto !important;
    overflow-x: hidden !important;
}

[role="listbox"] {
    max-height: 15rem !important;
    overflow-y: auto !important;
    overflow-x: hidden !important;
    /* Must sit above the pf-option card (z-index 1) and the sticky nav card
       (position sticky stacks at 0) so the list never hides behind either. */
    z-index: 50;
}

/* FluentSelect's listbox is rendered inside the custom element, so the element
   itself must not clip. Belt-and-braces with the global card override. */
fluent-select {
    overflow: visible !important;
}

/* Smooth scrolling for dropdown options */
[role="listbox"]::-webkit-scrollbar {
    width: 0.5rem;
}

[role="listbox"]::-webkit-scrollbar-track {
    background: transparent;
}

[role="listbox"]::-webkit-scrollbar-thumb {
    background: var(--pf-border-strong);
    border-radius: 4px;
}

[role="listbox"]::-webkit-scrollbar-thumb:hover {
    background: var(--pf-text-muted);
}

@media (max-width: 700px) {
    .pf-option__head {
        flex-direction: column;
    }

    .pf-option__learn-more {
        white-space: normal;
    }
}

/* ─── Compact segmented switcher ─────────────────────────────
   Glass pill with a sliding indicator: a single accent-filled
   "puck" slides horizontally under whichever option is active. */
.pf-switcher {
    position: relative;
    display: inline-flex;
    padding: 0.25rem;
    background: var(--pf-chrome-bg);
    backdrop-filter: blur(12px) saturate(160%);
    -webkit-backdrop-filter: blur(12px) saturate(160%);
    border: 1px solid var(--pf-chrome-border);
    border-radius: var(--pf-radius-pill);
    line-height: 1;
    font-family: var(--pf-font-sans);
    isolation: isolate;
}

.pf-switcher__indicator {
    position: absolute;
    top: 0.25rem;
    bottom: 0.25rem;
    left: 0;
    width: 0;
    opacity: 0;
    /* JS fades it in after first measurement */
    background: var(--pf-accent-gradient);
    border-radius: var(--pf-radius-pill);
    box-shadow:
        0 2px 8px rgba(var(--pf-accent-rgb), 0.35),
        inset 0 1px 0 rgba(255, 255, 255, 0.2);
    transition:
        transform 0.35s var(--pf-ease-out),
        width 0.35s var(--pf-ease-out),
        opacity 0.2s var(--pf-ease);
    z-index: -1;
    pointer-events: none;
}

.pf-switcher__btn {
    appearance: none;
    -webkit-appearance: none;
    background: transparent;
    border: 0;
    color: var(--pf-text-muted);
    font-family: inherit;
    font-size: 0.8125rem;
    font-weight: 500;
    padding: 0.375rem 0.75rem;
    border-radius: var(--pf-radius-pill);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
    transition: color 0.25s var(--pf-ease);
    white-space: nowrap;
    position: relative;
    z-index: 1;
}

.pf-switcher--icon .pf-switcher__btn {
    padding: 0.375rem 0.5rem;
    min-width: 2rem;
    justify-content: center;
}

.pf-switcher__btn:hover:not(.is-active) {
    color: var(--pf-text);
}

.pf-switcher__btn.is-active {
    color: #ffffff;
}

.pf-switcher__btn:focus-visible {
    outline: 2px solid var(--pf-accent-ring);
    outline-offset: 2px;
}

.pf-switcher__icon {
    font-size: 0.95rem;
    line-height: 1;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

/* SVG children inside switcher icons inherit color from the button. */
.pf-switcher__icon svg {
    display: block;
    color: currentColor;
    transition: color 0.25s var(--pf-ease);
}

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

/* ─── Hero title gradient text ───────────────────────────────
   Notes:
   1. `line-height: 1.2` + `padding: 0.15em 0` give gradient-clipped
      text room to breathe so ascenders (h, l, f, t) don't get cut.
   2. Blazor's <Router><FocusOnNavigate Selector="h1"/> programmatically
      focuses the <h1> on every route change with tabindex=-1. The default
      browser focus ring is a harsh blue rectangle - kill it for mouse
      nav, keep a soft ring for `:focus-visible` (keyboard-only). */
.pf-hero-title {
    background: linear-gradient(135deg,
            var(--pf-text) 0%,
            var(--pf-accent) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: transparent;
    letter-spacing: -0.02em;
    line-height: 1.2;
    padding: 0.15em 0;
    outline: none;
}

.pf-hero-title:focus,
.pf-hero-title:focus-visible {
    outline: none;
}

/* Catch-all: ANY programmatically-focused element (tabindex=-1) is
   non-interactive by design - skip every outline variant. Blazor's
   FocusOnNavigate still works for screen readers because focus DOES
   move; it's only the visual ring we hide. */
[tabindex="-1"]:focus,
[tabindex="-1"]:focus-visible {
    outline: none !important;
}

/* ─── Loading screen - wavy flight path ───────────────────
   Lives in #loading-overlay (outside #app) so Blazor's mount
   doesn't tear it down mid-animation. Index.html keeps it visible
   until Blazor boots + MIN_DISPLAY_MS elapses, then fades. */

#loading-overlay {
    position: fixed;
    inset: 0;
    background: var(--pf-bg);
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    opacity: 1;
    visibility: visible;
    transition: opacity 0.5s ease, visibility 0.5s;
}

#loading-overlay.is-done {
    opacity: 0;
    visibility: hidden;
    pointer-events: none;
}

.preflight-loader {
    position: relative;
    top: auto;
    left: auto;
    /* Fixed geometry so the SVG wave (400x60 viewBox) and the plane's
       pixel-based offset-path share the same coordinate space. The CSS
       offset-path on .plane uses literal pixel coords (0..400), so the
       loader's pre-transform width MUST stay at 25rem (= 400px) for the
       plane to land on the runway. On narrow viewports we apply a uniform
       transform: scale() instead of shrinking width - that keeps the
       coordinate space intact while still fitting the screen.

       Height bumped from 6.25rem → 8rem so the plane's 14px drop-shadow
       halo has breathing room above the wave peak and below the trough -
       the previous height clipped the glow on tall waves. overflow:visible
       is explicit (defensive) so any descendant filter spill renders even
       if a future layout adds a clipping ancestor. */
    width: 25rem;
    height: 8rem;
    overflow: visible;
    z-index: 2;
    /* Uniform downscale so the 400px SVG fits any viewport. The 2rem
       safety margin on each side keeps the loader off the screen edges
       on phones (looked stretched edge-to-edge before). transform-origin
       defaults to center, and #loading-overlay's flex centering keeps
       the box anchored - no translate() needed. */
    transform: scale(min(1, calc((100vw - 4rem) / 25rem)));
    transform-origin: center center;
}

.runway-wave {
    position: absolute;
    /* Centered vertically inside the 8rem loader (8 - 3.75) / 2 ≈ 2.125rem,
       which leaves ~1rem above the wave peaks for the plane's drop-shadow
       halo and ~1rem below the troughs before the status text. */
    top: 2.125rem;
    left: 0;
    width: 25rem;
    height: 3.75rem;
    display: block;
    overflow: visible;
    fill: none;
    stroke-linecap: round;
}

/* Optional faint guide-line so the user can see where the plane WILL fly.
   Kept very low contrast (opacity ~0.08) so it does NOT read as "trail
   already drawn ahead of the plane" - the bright trail rendered on top
   is the one that actually tracks progress. No fade-in animation: the
   guide should be there from frame 1 and never compete for attention. */
.runway-track {
    stroke: var(--pf-border-strong);
    stroke-width: 1.5;
    opacity: 0.08;
}

/* Progressive fill trail (0 → 100% one-shot). It's a status bar - once
   it reaches the end, stays there. `forwards` fill-mode pins the end state
   even after the animation completes, through the overlay fade-out.

   A horizontal mask gradient fades the trail's tail so the oldest part of
   the flight path dissolves behind the plane - visually separates the
   glowing plane from the line, which were blending into one violet smear. */
/* Register --pf-trail-progress as a typed <number> so it interpolates
   smoothly under transitions. Without @property, custom properties are
   opaque strings to CSS - the consumer property still transitions, but
   var-driven calc() results sometimes pin to discrete computed values
   in WebView2 / older Chromium, which is exactly the asymmetry that
   made the trail "snap" while the plane glided behind it. */
@property --pf-trail-progress {
    syntax: "<number>";
    inherits: true;
    initial-value: 0;
}

/* Honest deterministic fill: the runway draws itself in lockstep with
   ACTUAL Blazor boot progress, not a hardcoded 2.4s timeline.

   Blazor WebAssembly publishes its load progress to two CSS custom
   properties on <html>:
     --blazor-load-percentage       - e.g.  "47%"   (length-percentage)
     --blazor-load-percentage-text  - e.g.  "47%"   (string for content:)
   The status text below consumes the second one. The trail fill below
   uses --pf-trail-progress (unitless 0..100), which a small JS bridge
   in index.html mirrors from --blazor-load-percentage in the SAME
   frame Blazor writes it (MutationObserver, not polled).

   The bridge ALSO pins --pf-trail-length-px to the path's real total
   length (~416px). We then drive stroke-dasharray as an ABSOLUTE pixel
   length (`progress/100 * length`) instead of relying on the path's
   pathLength="100" attribute to rescale unitless dash values - because
   Chromium / WebView2 does not apply pathLength scaling to length-
   typed dash values produced by var()/calc(). With "30px 100px" on a
   416px curve the pattern repeats four times (visible drawn segments
   at 0-30, 130-160, 260-290, 390-416), which is exactly the "line ahead
   of the plane" symptom. Hard pixel math sidesteps the bug. */
.runway-trail {
    stroke: url(#runwayGradient);
    stroke-width: 2;
    stroke-linecap: round;
    /* ON segment = progress% of the curve's actual length. The OFF
       segment is set far longer than the path so the dash pattern can
       never wrap onto a second visible repetition. */
    stroke-dasharray: calc(var(--pf-trail-progress, 0) / 100 * var(--pf-trail-length-px, 416px)) 9999px;
    stroke-dashoffset: 0;
    /* Smooth fade-in at the very start of the runway. This mask is
       STATIC (not progress-dependent) - it always fades the leftmost
       portion of the SVG box so the trail never starts abruptly at
       x=0. Combined with the dasharray reveal above, the result is:
       trail grows from start to plane, leftmost ~12% softened. */
    -webkit-mask-image: linear-gradient(to right,
            rgba(0, 0, 0, 0) 0%,
            rgba(0, 0, 0, 1) 12%,
            rgba(0, 0, 0, 1) 100%);
    mask-image: linear-gradient(to right,
            rgba(0, 0, 0, 0) 0%,
            rgba(0, 0, 0, 1) 12%,
            rgba(0, 0, 0, 1) 100%);
    filter: drop-shadow(0 0 4px var(--pf-accent));
    opacity: 0.9;
    /* Match the plane's offset-distance transition so the trail tip and
       the plane glide together (both 0.35s, same easing). Transitioning
       --pf-trail-progress (typed as <number> via @property above) makes
       any consumer of var(--pf-trail-progress) interpolate smoothly. */
    transition: --pf-trail-progress 0.35s cubic-bezier(0.4, 0, 0.2, 1);
}

/* Plane follows the EXACT same path the SVG drew. Both use the 400x60
   coordinate space, so at 100% offset-distance the plane lands on the
   right-most crest. offset-rotate: auto tilts the plane with tangent.
   Inline SVG with gradient fill - bulletproof visibility across themes. */
.plane {
    position: absolute;
    /* Match .runway-wave's top so offset-path coordinates stay aligned -
       the plane's offset-path uses the same 0..400 / 0..60 space as the
       wave SVG, so both must anchor at the same loader-relative y. */
    top: 2.125rem;
    left: 0;
    width: 1.75rem;
    height: 1.75rem;
    display: block;
    overflow: visible;
    pointer-events: none;

    offset-path: path("M 0 30 Q 50 5, 100 30 T 200 30 T 300 30 T 400 30");
    /* SVG plane is drawn nose-up (local -Y). +90deg rotates it CW so the
       nose points along the tangent direction (nose-forward flight). */
    offset-rotate: auto 90deg;
    offset-anchor: center;

    /* The plane's position rides Blazor's actual load percentage - same
       source of truth as the runway-trail fill above. At 0% it sits at
       the start of the runway; at 100% it has reached the right-most
       crest; in between it advances along the cubic curve via
       `offset-path`. Pulse glow keeps running independently so the
       brand mark never feels frozen.

       Two-layer drop-shadow (small white highlight + larger accent halo)
       lifts the plane off the violet trail underneath so the nose always
       reads clearly. */
    filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.95)) drop-shadow(0 0 14px var(--pf-accent));
    offset-distance: var(--blazor-load-percentage, 0%);
    transition: offset-distance 0.35s cubic-bezier(0.4, 0, 0.2, 1);
    animation: planePulse 1.8s ease-in-out infinite;
}

/* (planeFly @keyframes removed - plane's offset-distance is now driven
   directly by var(--blazor-load-percentage) with a CSS transition.) */

@keyframes planePulse {

    0%,
    100% {
        filter: drop-shadow(0 0 3px rgba(255, 255, 255, 0.95)) drop-shadow(0 0 12px var(--pf-accent));
    }

    50% {
        filter: drop-shadow(0 0 4px rgba(255, 255, 255, 1)) drop-shadow(0 0 22px var(--pf-accent-strong, var(--pf-accent)));
    }
}

.runway-status {
    position: absolute;
    bottom: 0;
    left: 0;
    right: 0;
    text-align: center;
    font-size: 0.8125rem;
    font-family: var(--pf-font-mono);
    color: var(--pf-text-subtle);
    letter-spacing: 0.02em;
    min-height: 1em;
}

.runway-status:after {
    content: var(--blazor-load-percentage-text, "Preparing the runway…");
}

/* ─── Error banner ─────────────────────────────────────── */
#blazor-error-ui {
    background: #f59e0b;
    color: #0f172a;
    bottom: 0;
    box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.12);
    display: none;
    left: 0;
    padding: 0.75rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
    font-family: var(--pf-font-sans);
}

#blazor-error-ui .dismiss {
    cursor: pointer;
    position: absolute;
    right: 0.75rem;
    top: 0.5rem;
}

/* ─── XML live preview (Advanced page sidebar) ─────────────
   A compact code surface with an accent-bordered left edge and a
   dedicated toolbar. Three icon buttons (copy / download / expand)
   replace the old mixed-appearance FluentButtons so the chrome reads
   as a single component instead of a stealth/accent collision. */
.pf-xml-panel {
    display: flex;
    flex-direction: column;
    gap: 0.625rem;
}

.pf-xml-toolbar {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 2rem;
    flex-wrap: wrap;
}

.pf-xml-toolbar__title {
    font-weight: 600;
    font-size: 0.8125rem;
    color: var(--pf-text);
    letter-spacing: 0.01em;
}

.pf-xml-toolbar__meta {
    font-family: var(--pf-font-mono);
    font-size: 0.6875rem;
    color: var(--pf-text-subtle);
    padding: 0.125rem 0.4rem;
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-sm);
    white-space: nowrap;
}

.pf-xml-toolbar__actions {
    margin-left: auto;
    display: inline-flex;
    gap: 0.25rem;
}

.pf-xml-iconbtn {
    appearance: none;
    -webkit-appearance: none;
    background: var(--pf-chrome-bg);
    border: 1px solid var(--pf-border-strong);
    color: var(--pf-text-muted);
    width: 1.75rem;
    height: 1.75rem;
    padding: 0;
    border-radius: var(--pf-radius-sm);
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    backdrop-filter: blur(10px);
    transition:
        color 0.15s var(--pf-ease),
        border-color 0.15s var(--pf-ease),
        background 0.15s var(--pf-ease),
        transform 0.15s var(--pf-ease),
        box-shadow 0.2s var(--pf-ease);
}

.pf-xml-iconbtn:hover {
    color: var(--pf-accent);
    border-color: var(--pf-accent);
    transform: translateY(-1px);
    box-shadow: 0 2px 8px var(--pf-accent-ring);
}

.pf-xml-iconbtn:active {
    transform: translateY(0);
}

.pf-xml-iconbtn:focus-visible {
    outline: 2px solid var(--pf-accent-ring);
    outline-offset: 2px;
}

.pf-xml-iconbtn.is-copied {
    color: var(--pf-accent);
    border-color: var(--pf-accent);
    background: var(--pf-accent-soft);
}

.pf-xml-iconbtn svg {
    width: 0.875rem;
    height: 0.875rem;
    display: block;
    color: currentColor;
}

/* Code surface - sidebar variant. Left accent border ties it visually to
   the app palette; monospace with relaxed leading makes deep trees legible. */
.pf-xml-code {
    margin: 0;
    padding: 0.75rem 0.875rem;
    font-family: var(--pf-font-mono);
    font-size: 0.7188rem;
    line-height: 1.55;
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-left: 3px solid var(--pf-accent);
    border-radius: var(--pf-radius-md);
    overflow: auto;
    max-height: 32.5rem;
    white-space: pre;
    color: var(--pf-text);
    tab-size: 2;
}

.pf-xml-code code {
    font-family: inherit;
    font-size: inherit;
    line-height: inherit;
    color: inherit;
    background: transparent;
    text-shadow: none;
}

.pf-xml-code::-webkit-scrollbar {
    width: 0.625rem;
    height: 0.625rem;
}

.pf-xml-code::-webkit-scrollbar-track {
    background: transparent;
}

.pf-xml-code::-webkit-scrollbar-thumb {
    background: var(--pf-border-strong);
    border-radius: 5px;
    border: 2px solid var(--pf-surface-2);
}

.pf-xml-code::-webkit-scrollbar-thumb:hover {
    background: var(--pf-accent);
}

/* ─── Advanced code editor (textarea + Prism overlay) ────
   A <pre class="pf-xml-code pf-code-editor__view"> sits absolutely behind a
   transparent textarea. Both share font metrics, padding, line-height and
   `white-space: pre` so a character at row R column C lines up across layers.
   JS keeps the <pre>'s scrollTop/scrollLeft mirrored to the textarea's. */
.pf-code-editor {
    position: relative;
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-left: 3px solid var(--pf-accent);
    border-radius: var(--pf-radius-md);
    overflow: hidden;
    transition: border-color 0.15s var(--pf-ease);
}

.pf-code-editor:focus-within {
    border-color: var(--pf-accent-strong);
    box-shadow: 0 0 0 2px var(--pf-accent-ring);
}

/* Highlighted mirror. Reuses .pf-xml-code's palette (tokens are coloured by
   the shared .pf-xml-code .token.* rules below) but disables its own border,
   max-height and radius since the wrapper owns those. */
.pf-code-editor__view {
    position: absolute;
    inset: 0;
    margin: 0;
    background: transparent;
    border: none;
    border-radius: 0;
    max-height: none;
    pointer-events: none;
    overflow: hidden;
}

.pf-code-editor__view code {
    display: block;
    min-height: 100%;
}

/* Transparent textarea on top. Caret/selection remain visible because the
   text itself (rendered transparent) lines up 1:1 with the Prism-painted
   characters behind it. */
.pf-code-editor__input {
    position: relative;
    display: block;
    width: 100%;
    padding: 0.75rem 0.875rem;
    margin: 0;
    font-family: var(--pf-font-mono);
    font-size: 0.7188rem;
    line-height: 1.55;
    color: transparent;
    caret-color: var(--pf-text);
    background: transparent;
    border: none;
    outline: none;
    resize: vertical;
    white-space: pre;
    overflow: auto;
    tab-size: 2;
    min-height: 15rem;
}

.pf-code-editor__input::selection {
    color: transparent;
    background: rgba(139, 92, 246, 0.35);
}

.pf-code-editor__input::placeholder {
    color: var(--pf-text-muted);
    opacity: 0.6;
}

/* ─── Prism token theme (scoped to .pf-xml-code) ──────────
   We don't pull Prism's default CSS - it targets token classes with
   generic greens/blues that fight the indigo/violet palette. Instead
   we paint each token in a palette-native colour. Scoping to
   .pf-xml-code keeps this from leaking onto <code> elsewhere. */
.pf-xml-code .token.comment,
.pf-xml-code .token.prolog,
.pf-xml-code .token.cdata {
    color: var(--pf-text-subtle);
    font-style: italic;
}

.pf-xml-code .token.doctype {
    color: var(--pf-text-muted);
    font-weight: 500;
}

.pf-xml-code .token.punctuation {
    color: var(--pf-text-muted);
}

/* Light theme: indigo tag / fuchsia attr / burnt-orange value - works against
   a white/slate surface. Dark theme: the sidebar's card background carries a
   violet tint, so the indigo/violet tag colour that worked on light blends
   into the chrome and becomes unreadable. We shift to three distinct hues
   (cool sky for tags, warm amber for attr-names, soft green for strings) so
   every token stands off the violet surface regardless of theme. */
.pf-xml-code .token.tag {
    color: var(--pf-accent-strong);
    font-weight: 500;
}

html.theme-dark .pf-xml-code .token.tag {
    color: #7dd3fc;
}

/* sky-300 */

.pf-xml-code .token.tag .token.punctuation {
    color: var(--pf-accent);
    opacity: 0.75;
}

html.theme-dark .pf-xml-code .token.tag .token.punctuation {
    color: #7dd3fc;
    opacity: 0.55;
}

.pf-xml-code .token.attr-name {
    color: #c026d3;
}

/* fuchsia-600 */
html.theme-dark .pf-xml-code .token.attr-name {
    color: #fbbf24;
}

/* amber-400 */

.pf-xml-code .token.attr-value,
.pf-xml-code .token.string {
    color: #c2410c;
    /* orange-700 */
}

html.theme-dark .pf-xml-code .token.attr-value,
html.theme-dark .pf-xml-code .token.string {
    color: #86efac;
    /* green-300 */
}

.pf-xml-code .token.attr-value .token.punctuation {
    color: inherit;
    opacity: 0.55;
}

.pf-xml-code .token.entity,
.pf-xml-code .token.namespace {
    color: var(--pf-accent);
}

html.theme-dark .pf-xml-code .token.entity,
html.theme-dark .pf-xml-code .token.namespace {
    color: #f472b6;
    /* pink-400 */
}

/* ─── Additional token types for non-XML languages (JSON / batch /
   powershell / ini / vbnet). Reuse the XML palette so the CodeEditor
   looks consistent regardless of which Prism grammar produced it. */
.pf-xml-code .token.keyword,
.pf-xml-code .token.selector,
.pf-xml-code .token.important,
.pf-xml-code .token.atrule,
.pf-xml-code .token.class-name {
    color: var(--pf-accent-strong);
    font-weight: 500;
}

html.theme-dark .pf-xml-code .token.keyword,
html.theme-dark .pf-xml-code .token.selector,
html.theme-dark .pf-xml-code .token.important,
html.theme-dark .pf-xml-code .token.atrule,
html.theme-dark .pf-xml-code .token.class-name {
    color: #c4b5fd;
    /* violet-300 */
}

.pf-xml-code .token.property,
.pf-xml-code .token.variable,
.pf-xml-code .token.constant,
.pf-xml-code .token.symbol {
    color: #c026d3;
}

html.theme-dark .pf-xml-code .token.property,
html.theme-dark .pf-xml-code .token.variable,
html.theme-dark .pf-xml-code .token.constant,
html.theme-dark .pf-xml-code .token.symbol {
    color: #fbbf24;
    /* amber-400 */
}

.pf-xml-code .token.function,
.pf-xml-code .token.builtin {
    color: var(--pf-accent);
    font-weight: 500;
}

html.theme-dark .pf-xml-code .token.function,
html.theme-dark .pf-xml-code .token.builtin {
    color: #67e8f9;
    /* cyan-300 */
}

.pf-xml-code .token.number,
.pf-xml-code .token.boolean,
.pf-xml-code .token.null,
.pf-xml-code .token.regex {
    color: #c2410c;
}

html.theme-dark .pf-xml-code .token.number,
html.theme-dark .pf-xml-code .token.boolean,
html.theme-dark .pf-xml-code .token.null,
html.theme-dark .pf-xml-code .token.regex {
    color: #f0abfc;
    /* fuchsia-300 */
}

.pf-xml-code .token.operator,
.pf-xml-code .token.url {
    color: var(--pf-text);
    opacity: 0.8;
}

/* ─── XML expand modal ────────────────────────────────────
   Overlay uses the shared .pf-overlay backdrop-blur, then centres a
   wide dialog (up to 1200px) that takes most of the viewport so a
   large autounattend.xml can actually breathe. */
.pf-xml-modal.pf-overlay {
    align-items: center;
    padding: 4vh 4vw;
}

.pf-xml-dialog {
    width: min(75rem, 100%);
    max-height: 92vh;
    background: var(--pf-modal-bg);
    backdrop-filter: blur(24px) saturate(180%);
    -webkit-backdrop-filter: blur(24px) saturate(180%);
    border: 1px solid var(--pf-modal-border);
    border-radius: var(--pf-radius-lg);
    box-shadow:
        var(--pf-card-highlight),
        0 32px 90px rgba(0, 0, 0, 0.35),
        0 0 100px var(--pf-accent-ring);
    display: flex;
    flex-direction: column;
    overflow: hidden;
    opacity: 0;
    transform: translateY(-16px) scale(0.96);
    transition:
        opacity 0.32s var(--pf-ease-out),
        transform 0.4s var(--pf-ease-out);
}

.pf-overlay.is-open .pf-xml-dialog {
    opacity: 1;
    transform: translateY(0) scale(1);
}

.pf-xml-dialog__header {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.875rem 1.125rem;
    border-bottom: 1px solid var(--pf-border);
}

.pf-xml-dialog__title {
    font-weight: 600;
    font-size: 0.9375rem;
    color: var(--pf-text);
}

.pf-xml-dialog__actions {
    margin-left: auto;
    display: inline-flex;
    gap: 0.25rem;
}

.pf-xml-dialog .pf-xml-code {
    margin: 0.875rem;
    max-height: none;
    flex: 1;
    min-height: 0;
    font-size: 0.8125rem;
    line-height: 1.6;
    border-left-width: 4px;
}

/* ─── XML preview FAB ─────────────────────────────────────
   A compact floating action button pinned to the bottom-right of the viewport.
   Replaces the old sticky 3rd-column preview panel - the form now has that
   width to breathe in, and the XML is one click (or Ctrl+Shift+X) away. */
.pf-xml-fab {
    position: fixed;
    right: 1.5rem;
    bottom: 1.5rem;
    z-index: 40;
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.625rem 0.875rem;
    border-radius: 999px;
    background: var(--pf-chrome-bg);
    border: 1px solid var(--pf-border-strong);
    color: var(--pf-text);
    cursor: pointer;
    backdrop-filter: blur(12px) saturate(160%);
    -webkit-backdrop-filter: blur(12px) saturate(160%);
    box-shadow:
        0 8px 24px rgba(0, 0, 0, 0.18),
        0 2px 6px rgba(0, 0, 0, 0.08);
    transition:
        transform 0.18s var(--pf-ease),
        background 0.18s var(--pf-ease),
        border-color 0.18s var(--pf-ease),
        box-shadow 0.25s var(--pf-ease);
}

.pf-xml-fab:hover {
    transform: translateY(-1px);
    background: var(--pf-surface-2);
    border-color: var(--pf-accent);
    box-shadow:
        0 12px 28px rgba(0, 0, 0, 0.22),
        0 0 18px var(--pf-accent-ring);
}

.pf-xml-fab:active {
    transform: translateY(0);
}

.pf-xml-fab:focus-visible {
    outline: 2px solid var(--pf-accent);
    outline-offset: 2px;
}

.pf-xml-fab__icon {
    display: inline-flex;
    color: var(--pf-accent);
}

.pf-xml-fab__icon svg {
    width: 1.125rem;
    height: 1.125rem;
}

.pf-xml-fab__label {
    font-weight: 600;
    font-size: 0.8125rem;
    letter-spacing: 0.02em;
}

.pf-xml-fab__count {
    font-family: var(--pf-font-mono);
    font-size: 0.6875rem;
    color: var(--pf-text-subtle);
    padding: 0.125rem 0.4rem;
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-sm);
}

.pf-xml-fab.is-pulsing {
    animation: xmlFabPulse 0.9s var(--pf-ease);
}

.pf-xml-fab.is-pulsing .pf-xml-fab__count {
    animation: xmlFabCountBlink 0.9s var(--pf-ease);
}

@keyframes xmlFabPulse {
    0% {
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18), 0 0 0 0 var(--pf-accent-ring);
    }

    50% {
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18), 0 0 0 10px transparent;
    }

    100% {
        box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18), 0 0 0 0 transparent;
    }
}

@keyframes xmlFabCountBlink {

    0%,
    100% {
        color: var(--pf-text-subtle);
    }

    30% {
        color: var(--pf-accent);
    }
}

@media (prefers-reduced-motion: reduce) {

    .pf-xml-fab.is-pulsing,
    .pf-xml-fab.is-pulsing .pf-xml-fab__count {
        animation: none;
    }
}

@media (max-width: 600px) {
    .pf-xml-fab {
        right: 0.75rem;
        bottom: 0.75rem;
        padding: 0.5rem 0.75rem;
    }

    .pf-xml-fab__label {
        display: none;
    }
}

/* ─── Responsive tweaks ────────────────────────────────── */
@media (max-width: 820px) {
    .pf-breadcrumbs {
        display: none;
    }

    .pf-icon-btn {
        padding: 0.375rem 0.5rem;
    }
}

@media (max-width: 600px) {

    /* Hide language labels - just flags on tight mobile header */
    .pf-switcher:not(.pf-switcher--icon) .pf-switcher__btn>span:not(.pf-switcher__icon) {
        display: none;
    }

    .pf-switcher:not(.pf-switcher--icon) .pf-switcher__btn {
        padding: 0.375rem 0.5rem;
    }

    .layout {
        padding: 0.375rem !important;
        gap: 0.375rem !important;
    }

    .layout>.header {
        top: 0.375rem !important;
        padding: 0 0.75rem !important;
    }
}

@media (max-width: 420px) {
    .pf-icon-btn {
        display: none;
    }

    /* Ctrl+K shortcut still works via kbd */
}

/* ─── Motion reduction ───────────────────────────────────
   WAI guidance: kill decorative/hover motion but keep progress
   indicators visible. A user with reduced-motion preference still
   expects to see the loading spinner / plane - just not flashy
   card-lifts or transform transitions.
   Explicit opt-in list instead of a universal `*` rule so the
   loader animations survive. */
@media (prefers-reduced-motion: reduce) {

    fluent-card.pf-card--interactive,
    fluent-card.pf-card--interactive::before,
    fluent-button[appearance="accent"]::part(control),
    .pf-switcher__indicator,
    .pf-switcher__btn,
    .fluent-nav-link,
    .pf-brand,
    .pf-author,
    .pf-footer-link {
        transition-duration: 0.01ms !important;
    }

    /* Page-enter is driven by WAAPI in js/theme.js and downgrades to a
       plain opacity fade when matchMedia reports reduce=true. No CSS
       rule is needed here.

       Theme crossfade on colored properties is motion-free - it stays
       on for reduced-motion users. View Transitions (which do a paint
       snapshot crossfade, still motion-free) also stay on. */

    fluent-card.pf-card--interactive:hover {
        transform: none !important;
    }

    fluent-button[appearance="accent"]:hover::part(control),
    .fluent-nav-link:hover {
        transform: none !important;
    }
}

/* ─── Section group sub-heading ──────────────────────────────
   Separates logical clusters of checkboxes (e.g. Explorer,
   System, Edge) inside a single section. Uses the same muted
   text tone as option descriptions so it reads as metadata,
   not as a second-level header competing with the section
   title. */
.pf-section__group-heading {
    margin: 1.25rem 0 0.25rem;
    color: var(--pf-text-muted);
    font-size: 0.8125rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

/* The first group heading in a section doesn't need the top
   margin - the section's own spacing already handles it. */
.pf-section__options>.pf-section__group-heading:first-child {
    margin-top: 0;
}

/* ─── Radio group ────────────────────────────────────────────
   Used by OptionKind.Radio (and the bespoke lock-keys /
   sticky-keys forms). Vertical stack of labelled radios with
   enough spacing that the click target isn't cramped. */
.pf-radio-group {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

.pf-radio {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
    color: var(--pf-text);
    font-size: 0.95rem;
    line-height: 1.4;
}

.pf-radio input[type="radio"] {
    accent-color: var(--pf-accent);
    width: 1rem;
    height: 1rem;
    margin: 0;
    cursor: pointer;
}

.pf-radio__label {
    user-select: none;
}

/* ─── Checkbox group (CheckboxGroup kind) ────────────────────
   Stacked checkboxes where each item corresponds to one
   InlineValues entry. Uses the same rhythm as pf-radio-group
   so both feel like members of the same control family. */
.pf-checkbox-group {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.pf-checkbox-row {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    cursor: pointer;
    color: var(--pf-text);
    font-size: 0.95rem;
    line-height: 1.4;
}

.pf-checkbox-row__label {
    user-select: none;
}

/* ─── Textarea ───────────────────────────────────────────────
   Full-width, resizable-vertical only so a long script stays
   within the option card. */
.pf-textarea {
    width: 100%;
}

/* ─── Wizard step runway ─────────────────────────────────────
   Takes the loading-screen plane-on-runway motif and shrinks it into a
   horizontal step indicator. Progress flows left → right along the same
   S-curve: the track is a faint guide, the trail a gradient accent that
   fades toward its tail, and the plane rides the curve at --pf-step-progress
   (0-100%). 36px tall, 240px wide by default - fits next to "Step 2 of 5". */
.pf-step-runway {
    --pf-step-progress: 0%;
    position: relative;
    width: 15rem;
    height: 2.25rem;
    /* Allow the runway to shrink (down to ~9rem) on narrow viewports so it
       stays inside the wrapping step-header row instead of pushing siblings
       out. The plane's offset-path uses the SVG's 240x36 viewBox via
       preserveAspectRatio="none", so non-uniform scaling is fine. */
    flex: 0 1 15rem;
    min-width: 9rem;
    max-width: 100%;
}

.pf-step-runway__wave {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    overflow: visible;
    fill: none;
    stroke-linecap: round;
}

.pf-step-runway__track {
    stroke: var(--pf-border-strong);
    stroke-width: 1.5;
    opacity: 0.35;
}

/* The trail grows from 0 → progress% by masking its stroke with a left→right
   linear gradient. Dash-based reveals are brittle across path shapes; a mask
   is exact at every percentage and also gives us the fading tail for free. */
.pf-step-runway__trail {
    stroke: url(#pfStepTrailGradient);
    stroke-width: 2.5;
    stroke-linecap: round;
    /* All stops are fractions of the current progress so the gradient stays
       monotonic at every step. Fixed absolute stops (e.g. "30%") overshoot
       the progress tip at low percentages and blanked the line out entirely,
       leaving only the filter drop-shadow visible as a violet blob. */
    -webkit-mask-image: linear-gradient(to right,
            rgba(0, 0, 0, 0) 0%,
            rgba(0, 0, 0, 0.35) calc(var(--pf-step-progress) * 0.15),
            rgba(0, 0, 0, 0.9) calc(var(--pf-step-progress) * 0.55),
            rgba(0, 0, 0, 1) calc(var(--pf-step-progress) - 0.5%),
            rgba(0, 0, 0, 0) var(--pf-step-progress));
    mask-image: linear-gradient(to right,
            rgba(0, 0, 0, 0) 0%,
            rgba(0, 0, 0, 0.35) calc(var(--pf-step-progress) * 0.15),
            rgba(0, 0, 0, 0.9) calc(var(--pf-step-progress) * 0.55),
            rgba(0, 0, 0, 1) calc(var(--pf-step-progress) - 0.5%),
            rgba(0, 0, 0, 0) var(--pf-step-progress));
    filter: drop-shadow(0 0 3px var(--pf-accent));
    transition: -webkit-mask-image 0.5s var(--pf-ease-out),
        mask-image 0.5s var(--pf-ease-out);
}

.pf-step-runway__plane {
    position: absolute;
    top: 0;
    left: 0;
    width: 1.375rem;
    height: 1.375rem;
    pointer-events: none;
    color: var(--pf-accent);
    /* Ride the same S-curve at the current progress. Match the SVG's 240x36
       coordinate space so the plane lands on the crest. */
    offset-path: path("M 2 18 Q 32 4, 62 18 T 122 18 T 182 18 T 238 18");
    offset-rotate: auto 90deg;
    offset-anchor: center;
    offset-distance: var(--pf-step-progress);
    filter: drop-shadow(0 0 2px rgba(255, 255, 255, 0.9)) drop-shadow(0 0 10px var(--pf-accent));
    transition: offset-distance 0.5s var(--pf-ease-out);
}

@media (prefers-reduced-motion: reduce) {

    .pf-step-runway__trail,
    .pf-step-runway__plane {
        transition: none;
    }
}

/* ─── Docs index: plain filter input above the cards grid ────
   Reuses the pf-nav-search primitive without the wrapping card - on the
   index page the input is a single standalone control, not a toolbar.
   Auto margins centre it inside the page's max-width so the filter sits
   over the middle of the cards grid rather than hugging the left edge. */
.pf-docs-search {
    max-width: 32.5rem;
    margin: 0 auto 1rem;
}

/* ─── Themed global scrollbars ───────────────────────────────
   The default Chrome/Edge scrollbar reads as "system OS chrome" which
   clashes with the bespoke violet-on-dark palette. The custom rule below
   paints a thin accent-gradient thumb on a translucent track so scrollbars
   feel like part of the app. Firefox uses scrollbar-color / width - a close
   visual match without pseudo-elements. */
*::-webkit-scrollbar {
    width: 0.625rem;
    height: 0.625rem;
}

*::-webkit-scrollbar-track {
    background: rgba(var(--pf-bg-rgb), 0.4);
    border-radius: 10px;
}

*::-webkit-scrollbar-thumb {
    background: linear-gradient(180deg,
            rgba(var(--pf-accent-rgb), 0.85) 0%,
            rgba(var(--pf-accent-rgb), 0.55) 100%);
    border: 2px solid transparent;
    background-clip: padding-box;
    border-radius: 10px;
    min-height: 2.25rem;
    transition: background 0.2s var(--pf-ease);
}

*::-webkit-scrollbar-thumb:hover {
    background: linear-gradient(180deg,
            var(--pf-accent) 0%,
            var(--pf-accent-strong) 100%);
    background-clip: padding-box;
}

*::-webkit-scrollbar-corner {
    background: transparent;
}

/* Firefox */
* {
    scrollbar-color: rgba(var(--pf-accent-rgb), 0.65) rgba(var(--pf-bg-rgb), 0.35);
    scrollbar-width: thin;
}

/* ─── Go-to-top plane button ─────────────────────────────────
   Appears bottom-left after the user scrolls past ~400px. Clicking it
   scrolls the page back to top with a brief "takeoff" animation on the
   plane glyph (tilt + translate). Mirrors the loading screen motif. */
.pf-go-top {
    position: fixed;
    right: 1.5rem;
    /* Default: corner. The body:has(.pf-xml-fab) rule below pushes us up
       on /advanced where the XML preview FAB takes the corner instead.
       Sitting in the corner everywhere else avoids overlapping inline
       page controls (e.g. wizard Back/Next/Finish buttons at the bottom
       of /wizard/* on small screens). */
    bottom: 1.5rem;
    width: 3.25rem;
    height: 3.25rem;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 0;
    /* Solid accent gradient instead of glass - the previous translucent
       chrome with subtle border washed out on light backgrounds and read
       as "decorative", not "press me". Now it pops as a dedicated CTA. */
    background: var(--pf-accent-gradient);
    border: 0;
    border-radius: 50%;
    box-shadow:
        0 1px 0 rgba(255, 255, 255, 0.18) inset,
        0 8px 24px rgba(var(--pf-accent-rgb), 0.42),
        0 2px 6px rgba(0, 0, 0, 0.18);
    /* White plane glyph with its own gradient drop-shadow gives the
       button a "lit launchpad" feel without burying the icon in a halo. */
    color: #ffffff;
    cursor: pointer;
    z-index: 60;
    transition:
        transform 0.25s var(--pf-ease-out),
        box-shadow 0.3s var(--pf-ease-out),
        opacity 0.25s var(--pf-ease),
        filter 0.2s var(--pf-ease);
    opacity: 1;
}

.pf-go-top.is-visible {
    display: inline-flex;
}

/* On /advanced the XML-preview FAB occupies the bottom-right corner, so
   stack the go-top above it instead of overlapping. */
body:has(.pf-xml-fab) .pf-go-top {
    bottom: 5.5rem;
}

@media (max-width: 600px) {
    .pf-go-top {
        right: 1rem;
        bottom: 1rem;
        width: 2.5rem;
        height: 2.5rem;
    }

    body:has(.pf-xml-fab) .pf-go-top {
        bottom: 4.25rem;
    }
}

.pf-go-top:hover {
    transform: translateY(-4px) scale(1.04);
    filter: brightness(1.1) saturate(1.1);
    box-shadow:
        0 1px 0 rgba(255, 255, 255, 0.22) inset,
        0 14px 36px rgba(var(--pf-accent-rgb), 0.55),
        0 4px 10px rgba(0, 0, 0, 0.2);
}

.pf-go-top:active {
    transform: translateY(0) scale(0.96);
    transition:
        transform 0.1s var(--pf-ease),
        box-shadow 0.1s var(--pf-ease),
        filter 0.1s var(--pf-ease);
}

.pf-go-top:focus-visible {
    outline: 2px solid #ffffff;
    outline-offset: 3px;
}

.pf-go-top:active .pf-go-top__plane {
    animation: pfGoTopTakeoff 0.55s cubic-bezier(0.4, 0, 0.2, 1);
}

.pf-go-top__plane {
    width: 1.5rem;
    height: 1.5rem;
    display: block;
    /* Nose-up default orientation so the glyph looks ready to launch. */
    transform: rotate(-45deg);
    /* On the new accent-gradient background a halo isn't needed - instead
       give the glyph a soft inner glow + a subtle drop shadow underneath
       so it reads as engraved on the button. */
    filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.25));
}

@keyframes pfGoTopTakeoff {
    0% {
        transform: rotate(-45deg) translate(0, 0);
        opacity: 1;
    }

    60% {
        transform: rotate(-45deg) translate(12px, -18px);
        opacity: 0.4;
    }

    61% {
        transform: rotate(-45deg) translate(-12px, 18px);
        opacity: 0;
    }

    80% {
        transform: rotate(-45deg) translate(-12px, 18px);
        opacity: 0;
    }

    100% {
        transform: rotate(-45deg) translate(0, 0);
        opacity: 1;
    }
}

@media (prefers-reduced-motion: reduce) {
    .pf-go-top:active .pf-go-top__plane {
        animation: none;
    }
}

/* ─── Sidebar toolbar (filter + view toggle) ──────────────
   Sits above the groups; small search input + single icon button to
   flip between grouped and flat. Uses native <input type="search"> so
   the OS-provided clear icon survives (plus our explicit ✕ when focus
   is lost). Keeps the surface dense but not cramped. */
.pf-nav-toolbar {
    display: flex;
    align-items: stretch;
    gap: 0.375rem;
    padding: 0.25rem 0.375rem 0.5rem;
    border-bottom: 1px solid var(--pf-border);
    margin-bottom: 0.25rem;
}

.pf-nav-search {
    position: relative;
    flex: 1 1 auto;
    min-width: 0;
    display: flex;
    align-items: center;
}

.pf-nav-search__icon {
    position: absolute;
    left: 0.5rem;
    top: 50%;
    transform: translateY(-50%);
    font-size: 0.85rem;
    color: var(--pf-text-subtle);
    pointer-events: none;
}

.pf-nav-search__input {
    width: 100%;
    padding: 0.375rem 1.625rem 0.375rem 1.75rem;
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-sm);
    color: var(--pf-text);
    font-size: 0.86rem;
    font-family: inherit;
    outline: none;
    transition: border-color 0.15s var(--pf-ease), background 0.15s var(--pf-ease);
}

.pf-nav-search__input::placeholder {
    color: var(--pf-text-muted);
    opacity: 0.7;
}

.pf-nav-search__input:focus {
    border-color: var(--pf-accent);
    background: var(--pf-bg);
}

.pf-nav-search__input::-webkit-search-cancel-button {
    display: none;
}

.pf-nav-search__clear {
    position: absolute;
    right: 0.25rem;
    top: 50%;
    transform: translateY(-50%);
    width: 1.25rem;
    height: 1.25rem;
    border: none;
    background: transparent;
    color: var(--pf-text-muted);
    font-size: 0.75rem;
    cursor: pointer;
    border-radius: 3px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

.pf-nav-search__clear:hover {
    color: var(--pf-text);
    background: var(--pf-surface-2);
}

.pf-nav-toggle {
    flex: 0 0 auto;
    width: 2rem;
    height: 2rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-sm);
    color: var(--pf-text-muted);
    font-size: 0.95rem;
    cursor: pointer;
    transition: color 0.15s var(--pf-ease), background 0.15s var(--pf-ease), border-color 0.15s var(--pf-ease);
}

.pf-nav-toggle:hover {
    color: var(--pf-text);
    border-color: var(--pf-border-strong);
}

.pf-nav-toggle.is-active {
    color: var(--pf-accent-strong);
    border-color: var(--pf-accent);
    background: rgba(var(--pf-accent-rgb), 0.12);
}

.pf-nav-flat {
    list-style: none;
    margin: 0;
    padding: 2px 0.25rem;
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.pf-nav-flat__empty {
    padding: 0.75rem 0.625rem;
    text-align: center;
    color: var(--pf-text-muted);
    font-size: 0.85rem;
}

/* Prev/Next nav at the bottom of each Advanced section - same palette as the
   Advanced.Preview modal buttons for visual continuity. */
.pf-section-nav {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    margin-top: 1.5rem;
    padding-top: 1rem;
    border-top: 1px solid var(--pf-border);
}

/* ─── Advanced sidebar (grouped nav) ──────────────────────
   The 28 sections live under ~6 collapsible groups so the list reads
   as chapters, not a wall of links. Native <details> handles open/close;
   the group owning the active section opens automatically (razor-side). */
.pf-nav-card {
    padding: 0.375rem !important;
}

.pf-nav-groups {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.pf-nav-group {
    border-radius: var(--pf-radius-sm);
    background: transparent;
}

.pf-nav-group[open] {
    background: rgba(var(--pf-accent-rgb, 139, 92, 246), 0.06);
}

.pf-nav-group__summary {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.5rem 0.75rem;
    list-style: none;
    cursor: pointer;
    color: var(--pf-text-muted);
    font-size: 0.82rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    border-radius: var(--pf-radius-sm);
    user-select: none;
    transition: color 0.15s var(--pf-ease), background 0.15s var(--pf-ease);
}

.pf-nav-group__summary::-webkit-details-marker {
    display: none;
}

.pf-nav-group__summary::marker {
    content: '';
}

.pf-nav-group__summary::after {
    content: '▾';
    margin-left: auto;
    font-size: 0.75rem;
    color: var(--pf-text-subtle);
    transform: rotate(-90deg);
    transition: transform 0.2s var(--pf-ease);
}

.pf-nav-group[open]>.pf-nav-group__summary::after {
    transform: rotate(0deg);
}

.pf-nav-group__summary:hover {
    color: var(--pf-text);
    background: rgba(var(--pf-accent-rgb, 139, 92, 246), 0.1);
}

.pf-nav-group__icon {
    font-size: 0.95rem;
    line-height: 1;
}

.pf-nav-group__title {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.pf-nav-group__count {
    font-size: 0.7rem;
    font-weight: 600;
    color: var(--pf-text-subtle);
    padding: 1px 0.375rem;
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-radius: 10px;
    min-width: 1.25rem;
    text-align: center;
}

.pf-nav-group__list {
    list-style: none;
    margin: 0;
    padding: 2px 0.25rem 0.375rem 1.75rem;
    display: flex;
    flex-direction: column;
    gap: 1px;
}

.pf-nav-group__link {
    display: block;
    padding: 0.375rem 0.625rem;
    border-radius: var(--pf-radius-sm);
    color: var(--pf-text-muted);
    text-decoration: none;
    font-size: 0.9rem;
    line-height: 1.3;
    transition: color 0.15s var(--pf-ease), background 0.15s var(--pf-ease);
}

.pf-nav-group__link:hover {
    color: var(--pf-text);
    background: rgba(var(--pf-accent-rgb, 139, 92, 246), 0.08);
}

.pf-nav-group__link.active {
    color: var(--pf-accent-strong);
    background: rgba(var(--pf-accent-rgb, 139, 92, 246), 0.18);
    font-weight: 600;
}

/* ─── Loading skeleton placeholders ───────────────────────
   Shown in place of a real control while its data source is still
   fetching (e.g. FluentSelect populated from a 130-entry JSON). The
   shimmer is a shifting linear-gradient overlay; `prefers-reduced-motion`
   replaces it with a static tint so the animation doesn't trigger users
   who've opted out of motion. */
.pf-skeleton {
    display: block;
    border-radius: var(--pf-radius-md);
    background: linear-gradient(90deg,
            var(--pf-surface-2) 0%,
            rgba(var(--pf-accent-rgb, 139, 92, 246), 0.08) 50%,
            var(--pf-surface-2) 100%);
    background-size: 200% 100%;
    animation: pfSkeletonShimmer 1.4s ease-in-out infinite;
    border: 1px solid var(--pf-border);
}

.pf-skeleton--select {
    height: 2.5rem;
    width: 100%;
}

@keyframes pfSkeletonShimmer {
    0% {
        background-position: 100% 0;
    }

    100% {
        background-position: -100% 0;
    }
}

@media (prefers-reduced-motion: reduce) {
    .pf-skeleton {
        animation: none;
        background: var(--pf-surface-2);
    }
}

/* ─── Custom scripts section ──────────────────────────────
   Each phase is a card: title + description on the left, "Add script"
   on the right. Slots grow below as the user adds them; empty phases
   show a dashed placeholder so the section's shape reads immediately. */
.pf-scripts-phase {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    padding: 1rem 1.125rem;
    background: rgba(var(--pf-bg-rgb), 0.55);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-lg);
}

.pf-scripts-phase__head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 1rem;
    flex-wrap: wrap;
}

.pf-scripts-phase__title {
    margin: 0 0 0.2rem;
    font-size: 1.05rem;
    font-weight: 700;
    color: var(--pf-text);
}

.pf-scripts-phase__desc {
    margin: 0;
    max-width: 68ch;
    color: var(--pf-text-muted);
    font-size: 0.9rem;
    line-height: 1.5;
}

.pf-scripts-phase__empty {
    padding: 0.75rem 1rem;
    color: var(--pf-text-muted);
    background: transparent;
    border: 1px dashed var(--pf-border);
    border-radius: var(--pf-radius-md);
    font-size: 0.9rem;
    text-align: center;
}

.pf-script-slot {
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    padding: 0.85rem 0.95rem;
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-md);
}

.pf-script-slot__head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.pf-script-slot__num {
    font-family: var(--pf-font-mono);
    font-size: 0.85rem;
    color: var(--pf-text-muted);
    min-width: 1.625rem;
}

.pf-components {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
}

.pf-component-slot {
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    padding: 0.85rem 0.95rem;
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-md);
}

.pf-component-slot__head {
    display: flex;
    align-items: center;
    gap: 0.6rem;
}

.pf-component-slot__num {
    font-family: var(--pf-font-mono);
    font-size: 0.85rem;
    color: var(--pf-text-muted);
    min-width: 1.625rem;
}

.pf-scripts-global {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
    padding: 1rem 1.125rem;
    margin-top: 0.5rem;
    background: rgba(var(--pf-bg-rgb), 0.55);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-md);
}

/* ─── Users section ────────────────────────────────────────
   Four self-contained blocks (accounts / first-logon / expiration /
   lockout). Each `.pf-users__block` already has `.pf-option` framing
   from the shared Advanced palette; we override the default compact
   padding + add breathing room between blocks so the long form
   doesn't feel crammed. */
.pf-users {
    gap: 1.25rem;
}

.pf-section--advanced .pf-users__block {
    padding: 1.125rem 1.25rem;
    display: flex;
    flex-direction: column;
    gap: 0.875rem;
}

.pf-section--advanced .pf-users__block+.pf-users__block {
    margin-top: 0.25rem;
}

.pf-users__block .pf-option__description {
    margin: 0;
}

/* Accounts table: wraps so narrow viewports scroll rather than squish
   the cells. Header row uses the muted palette so it reads as chrome. */
.pf-users__table-wrap {
    /* No overflow clipping: setting overflow-x: auto here implicitly coerced
       overflow-y to auto (CSS spec - can't mix visible with a scrolling axis),
       which chopped the Group dropdown's listbox at the table's bottom edge.
       The table is narrow enough on any real viewport that horizontal overflow
       isn't an issue; if it ever happens the page scrolls instead of the wrap. */
    overflow: visible;
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-md);
    background: rgba(var(--pf-bg-rgb), 0.4);
}

.pf-users__table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
    font-size: 0.92rem;
}

.pf-users__table th,
.pf-users__table td {
    padding: 0.55rem 0.7rem;
    text-align: left;
    vertical-align: middle;
}

.pf-users__table th {
    font-size: 0.78rem;
    font-weight: 600;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: var(--pf-text-muted);
    background: var(--pf-surface-2);
    border-bottom: 1px solid var(--pf-border);
}

.pf-users__table tbody tr+tr td {
    border-top: 1px solid var(--pf-border);
}

.pf-users__table-actions {
    width: 2.75rem;
    text-align: center !important;
}

.pf-users__password {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    width: 100%;
}

.pf-users__icon-btn {
    flex: 0 0 auto;
    width: 1.875rem;
    height: 1.875rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    background: transparent;
    color: var(--pf-text-muted);
    border: 1px solid transparent;
    border-radius: var(--pf-radius-sm);
    cursor: pointer;
    transition: color 0.15s var(--pf-ease), background 0.15s var(--pf-ease), border-color 0.15s var(--pf-ease);
}

.pf-users__icon-btn:hover {
    color: var(--pf-text);
    background: var(--pf-surface-2);
    border-color: var(--pf-border);
}

.pf-users__icon-btn:focus-visible {
    outline: 2px solid var(--pf-accent-ring);
    outline-offset: 2px;
}

.pf-users__icon-btn--danger:hover {
    color: #ef4444;
    border-color: #ef4444;
}

.pf-users__icon-btn svg {
    width: 1rem;
    height: 1rem;
    display: block;
}

.pf-users__empty {
    padding: 0.875rem 1rem;
    color: var(--pf-text-muted);
    background: rgba(var(--pf-bg-rgb), 0.4);
    border: 1px dashed var(--pf-border);
    border-radius: var(--pf-radius-md);
    font-size: 0.92rem;
}

.pf-users__add-btn {
    align-self: flex-start;
}

.pf-users__sub-field {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    padding-left: 1.25rem;
    border-left: 2px solid var(--pf-border);
    max-width: 26.25rem;
}

.pf-users__sub-label {
    font-size: 0.88rem;
    font-weight: 600;
    color: var(--pf-text);
}

.pf-users__checkbox-stack {
    display: flex;
    flex-direction: column;
    gap: 0.55rem;
    margin-top: 0.25rem;
}

.pf-users__hint {
    display: block;
    color: var(--pf-text-muted);
    font-size: 0.8rem;
    line-height: 1.4;
    padding-left: 1.75rem;
}

/* Give the shared option-card grid a touch more breathing room so that
   radio groups, number fields and checkbox clusters don't visually run
   into each other. 0.75 was noticeably tight; 1rem reads as a clear
   divider without wasting vertical space. */
.pf-section--advanced .pf-section__options {
    gap: 1rem;
}

/* Consistent pad+gap on the RadioGroup rendered by SectionView so the
   FluentRadio rows aren't wedged against the description line above. */
.pf-section--advanced fluent-radio-group.pf-radio-group {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    margin-top: 0.1rem;
}

/* When a radio group carries illustrative images (taskbar-search etc), give each
   row more breathing room so the image doesn't crowd the next option. */
.pf-section--advanced fluent-radio-group.pf-radio-group--images {
    gap: 0.75rem;
}

.pf-radio__with-image {
    display: inline-flex;
    align-items: center;
    gap: 0.625rem;
}

.pf-radio__image {
    width: 8.75rem;
    height: auto;
    max-height: 2.25rem;
    display: block;
    border-radius: 4px;
    border: 1px solid var(--pf-border);
    background: var(--pf-surface-2);
    padding: 2px;
    object-fit: contain;
}

/* ─── 404 page ─────────────────────────────────────────────────
   Hero composition: "4 [icon] 4" where the centre 0 is the actual
   app icon. Stays on-brand and gives the user three on-ramps
   (home / wizard / docs) instead of just a sad "go home". */
.pf-404 {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 1.25rem;
    padding: clamp(2rem, 6vw, 4.5rem) 1rem 4rem;
    max-width: 720px;
    margin: 0 auto;
    text-align: center;
    position: relative;
    isolation: isolate;
}

.pf-404__hero {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: clamp(0.75rem, 2vw, 1.5rem);
    line-height: 1;
    user-select: none;
}

.pf-404__digit {
    font-family: var(--pf-font-sans);
    font-weight: 800;
    font-size: clamp(6rem, 18vw, 11.25rem);
    letter-spacing: -0.05em;
    background: var(--pf-accent-gradient);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    /* Soft purple glow so the gradient text reads against any theme. */
    text-shadow: 0 8px 28px rgba(var(--pf-accent-rgb), 0.18);
}

.pf-404__icon {
    display: inline-block;
    width: clamp(5rem, 15vw, 9.5rem);
    height: clamp(5rem, 15vw, 9.5rem);
    /* Bob the icon gently to suggest it's hovering, not lost. */
    animation: pf-404-bob 3.6s var(--pf-ease-out) infinite;
    filter: drop-shadow(0 14px 28px rgba(var(--pf-accent-rgb), 0.32));
}

.pf-404__icon svg {
    width: 100%;
    height: 100%;
    display: block;
}

@keyframes pf-404-bob {

    0%,
    100% {
        transform: translateY(0) rotate(0);
    }

    50% {
        transform: translateY(-10px) rotate(-3deg);
    }
}

/* Subtle dashed flight trail behind the hero - visually echoes the
   one in the loading overlay so the page feels native. */
.pf-404__trail {
    position: absolute;
    top: clamp(1rem, 4vw, 2.5rem);
    left: 0;
    right: 0;
    width: 100%;
    height: 80px;
    z-index: -1;
    color: var(--pf-accent);
    opacity: 0.32;
    pointer-events: none;
}

.pf-404__title {
    margin: 0.5rem 0 0;
    font-family: var(--pf-font-sans);
    font-size: clamp(1.5rem, 3.2vw, 2.25rem);
    font-weight: 700;
    color: var(--pf-text);
    letter-spacing: -0.01em;
}

.pf-404__message {
    margin: 0;
    max-width: 32rem;
    font-size: clamp(0.9375rem, 1.4vw, 1.0625rem);
    color: var(--pf-text-muted);
    line-height: 1.55;
}

.pf-404__actions {
    display: flex;
    flex-wrap: wrap;
    justify-content: center;
    gap: 0.625rem;
    margin-top: 0.75rem;
}

/* Respect users who opted out of motion. */
@media (prefers-reduced-motion: reduce) {
    .pf-404__icon {
        animation: none;
    }
}

/* ─── View Transitions API ────────────────────────────────────
   Browser-native snapshot crossfade between routes, driven from
   js/view-transitions.js (it intercepts internal anchor clicks and
   wraps Blazor's navigation in document.startViewTransition()).

   The default UA crossfade is 250ms linear "old fades out, new fades
   in" - it works but reads as plain. We replace it with directional
   slide+fade keyframes so navigation feels intentional: the page that
   leaves drifts up and dissolves, the page that enters lifts in from
   below. Mirrors the wave-of-paper feel of the rest of the brand.

   Falls back silently in browsers that don't implement the spec
   (Firefox at time of writing) - the pseudo-elements simply don't
   exist there and Blazor's normal click handler runs the navigation
   without any crossfade. No flicker, just instant nav. */
::view-transition-old(root) {
    animation: 0.32s cubic-bezier(0.4, 0, 1, 1) both pfViewOldOut;
}

::view-transition-new(root) {
    animation: 0.42s cubic-bezier(0.16, 1, 0.3, 1) 0.08s both pfViewNewIn;
}

@keyframes pfViewOldOut {
    to {
        opacity: 0;
        transform: translateY(-12px) scale(0.99);
    }
}

@keyframes pfViewNewIn {
    from {
        opacity: 0;
        transform: translateY(18px) scale(0.992);
    }
}

@media (prefers-reduced-motion: reduce) {

    ::view-transition-old(root),
    ::view-transition-new(root) {
        animation-duration: 0.18s;
        animation-delay: 0s;
    }

    @keyframes pfViewOldOut {
        to {
            opacity: 0;
        }
    }

    @keyframes pfViewNewIn {
        from {
            opacity: 0;
        }
    }
}
