/* ─────────────────────────────────────────────────────────── */
/*  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);
    /* Pre-promote this fixed layer at load so the browser doesn't build its
       compositing layer LAZILY on the first scroll - creating fixed-element
       layers mid-scroll is a one-time hitch ("смикання при першому скролі"). */
    will-change: transform;
}

/* Very subtle noise texture for surface grit. SVG is data-embedded
   so it works offline and doesn't add a network request.

   NO mix-blend-mode here. A position:fixed, full-viewport layer with
   mix-blend-mode:overlay forces the browser to re-blend the ENTIRE viewport
   against the content beneath it on every scroll frame - on many GPUs that
   reads as the page "re-rendering / content re-animating" while scrolling
   (reported across pages, since this layer is global). Plain normal blending
   composites once and stays put. Opacity nudged up a touch to keep the grain
   visible without the blend mode. */
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.04;
    pointer-events: none;
    z-index: 0;
    /* Same reason as body::before - eager layer so the first scroll doesn't
       pay for building this fixed full-viewport layer. */
    will-change: transform;
}

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

#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;
    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.

       Flat fill (was a 135deg diagonal gradient): the diagonal made one corner
       visibly denser than the other, and the backdrop blur over the page's
       radial glow amplified it into an "uneven header". A single uniform alpha
       reads as one even pane of glass. */
    background: rgba(241, 245, 249, 0.58) !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;
    /* Pre-promote the sticky header's backdrop-filter to its own compositing
       layer at load. Without this the browser creates that layer LAZILY on the
       first scroll, and building + first-rasterizing the blur mid-scroll is a
       one-time hitch ("смикання при першому скролі"). will-change forces the
       layer up front so the first scroll is as smooth as every later one. */
    will-change: transform;
}

html.theme-dark .layout>.header,
html.theme-dark fluent-header.header {
    background: rgba(36, 40, 69, 0.58) !important;
}

@media (prefers-color-scheme: dark) {

    html:not(.theme-light) .layout>.header,
    html:not(.theme-light) fluent-header.header {
        background: rgba(36, 40, 69, 0.58) !important;
    }
}

/* No cloak strip above the header. The sticky header pins flush to the viewport
   top (top: 0), so scrolling content slides directly under its glass instead of
   peeking through a gap. Earlier attempts to keep a 0.75rem top gap and mask it
   (a flat fill, then an extended-glass pseudo) either killed the ambient
   body::before glow as a dark band or read as a separate frosted bar above the
   rounded island. Pinning flush removes the gap, so neither hack is needed. */

.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;
    /* Scrim + blur live on .is-open ONLY. These overlays are always mounted
       (so open/close can transition), and Chromium composites backdrop-filter
       even on a visibility:hidden / opacity:0 layer - 4 stacked hidden overlays
       therefore hazed and darkened the whole page (most visible against the
       header). Closed = paint nothing. */
    background: transparent;
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
    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),
        background 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;
    background: rgba(0, 0, 0, 0.5);
    backdrop-filter: blur(8px);
    -webkit-backdrop-filter: blur(8px);
    transition:
        opacity 0.28s var(--pf-ease),
        background 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);
}

/* Landing "How to use the file" - collapsible guide. The <details> is the card;
   the rendered article reuses .pf-section__intro for typography but drops its
   own card chrome so we don't nest a panel inside a panel. */
.pf-guide {
    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);
    overflow: hidden;
}

.pf-guide__summary {
    display: flex;
    align-items: center;
    gap: 0.875rem;
    padding: 1.125rem 1.375rem;
    cursor: pointer;
    list-style: none;
    user-select: none;
}

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

.pf-guide__icon {
    font-size: 1.5rem;
    line-height: 1;
}

.pf-guide__head {
    display: flex;
    flex-direction: column;
    gap: 0.15rem;
    min-width: 0;
}

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

.pf-guide__subtitle {
    color: var(--pf-text-muted);
    font-size: 0.875rem;
    line-height: 1.4;
}

.pf-guide__chevron {
    margin-left: auto;
    color: var(--pf-text-muted);
    font-size: 1.25rem;
    transition: transform 0.18s ease;
}

.pf-guide[open] .pf-guide__chevron {
    transform: rotate(180deg);
}

.pf-guide__body.pf-section__intro {
    border: 0;
    border-top: 1px solid var(--pf-card-border);
    border-radius: 0;
    background: transparent;
    box-shadow: none;
}

.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;
}

/* ─── Markdown article typography: links, code blocks, quotes, tables ───
   These elements previously fell back to raw browser defaults (ugly blue
   underlines, unstyled <pre>, borderless tables). Style them to the pf-*
   palette so rendered .md (docs, section intros, the deployment guide) reads
   like polished reference. */

/* Heading sizes - h2/h3 sized elsewhere; pin h1/h4 so the full scale is set. */
.pf-section__intro h1 {
    font-size: clamp(1.5rem, 2vw, 1.75rem);
}

.pf-section__intro h4 {
    font-size: 0.95rem;
}

/* Inline links: accent text with an offset underline that thickens on hover. */
.pf-section__intro a {
    color: var(--pf-accent-strong, var(--pf-accent));
    text-decoration: underline;
    text-decoration-color: color-mix(in srgb, var(--pf-accent) 45%, transparent);
    text-underline-offset: 0.18em;
    text-decoration-thickness: 1px;
    transition: color 0.15s var(--pf-ease), text-decoration-color 0.15s var(--pf-ease);
}

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

/* External links open a new tab (rewritten in RenderArticleHtml) - mark them
   with a small ↗ so it's clear they leave the app. */
.pf-section__intro a[target="_blank"]::after {
    content: "↗";
    display: inline-block;
    margin-left: 0.15em;
    font-size: 0.82em;
    vertical-align: 0.08em;
    opacity: 0.7;
}

/* Fenced code blocks: a proper panel, scrollable, no inline-pill chrome on the
   inner <code> (that styling is for inline spans only). */
.pf-section__intro pre {
    margin: 0.875rem 0;
    padding: 0.875rem 1rem;
    background: var(--pf-surface-2);
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-md);
    overflow-x: auto;
    font-family: var(--pf-font-mono);
    font-size: 0.85rem;
    line-height: 1.6;
}

.pf-section__intro pre code {
    padding: 0;
    border: 0;
    background: none;
    color: var(--pf-text);
    font-size: inherit;
}

/* Blockquotes: accent rule on the left, muted text. */
.pf-section__intro blockquote {
    margin: 0.875rem 0;
    padding: 0.5rem 0 0.5rem 1rem;
    border-left: 3px solid var(--pf-accent);
    color: var(--pf-text-muted);
    background: color-mix(in srgb, var(--pf-accent) 6%, transparent);
    border-radius: 0 var(--pf-radius-sm) var(--pf-radius-sm) 0;
}

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

/* Tables: collapsed borders, header band, zebra-free but padded cells. */
.pf-section__intro table {
    width: 100%;
    margin: 1rem 0;
    border-collapse: collapse;
    font-size: 0.9rem;
    border: 1px solid var(--pf-border);
    border-radius: var(--pf-radius-md);
    overflow: hidden;
}

.pf-section__intro th,
.pf-section__intro td {
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--pf-border);
    text-align: left;
    vertical-align: top;
}

.pf-section__intro th {
    background: var(--pf-surface-2);
    color: var(--pf-text);
    font-weight: 600;
}

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

/* Horizontal rule: a visible divider that fades at both ends. Plain
   border-top on --pf-border was nearly invisible against the card. */
.pf-section__intro hr {
    margin: 1.75rem 0;
    height: 1px;
    border: 0;
    background: linear-gradient(to right,
            transparent,
            var(--pf-border-strong) 15%,
            var(--pf-border-strong) 85%,
            transparent);
}

.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;
}

/* Docs reading column: a single outer surface. The intro prose and each option
   render *inside* it as flat blocks - subtle dividers, no raised card treatment -
   so the page reads as one panel rather than a stack of competing cards. */
.pf-docs-content {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.pf-docs-content__head {
    padding-bottom: 0.875rem;
    margin-bottom: 0.25rem;
    border-bottom: 1px solid var(--pf-border);
}

.pf-docs-content .pf-section__intro,
.pf-docs-content .pf-option {
    background: transparent;
    border: none;
    border-radius: 0;
    box-shadow: none;
    padding: 0.75rem 0;
}

/* Hairline separators between consecutive blocks instead of card edges. */
.pf-docs-content .pf-section__intro+.pf-section__options,
.pf-docs-content .pf-option+.pf-option {
    border-top: 1px solid var(--pf-border);
}

.pf-docs-content .pf-section__options {
    gap: 0;
}

/* 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 + Docs 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 (and vice versa).
   Pin items to the top explicitly so each card hugs its own content height. */
fluent-grid,
fluent-grid-item,
fluent-card {
    overflow: visible !important;
}

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

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

.pf-advanced-shell fluent-card,
.pf-docs-entry 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%);
    /* Explicit SVG filter (defined in index.html) instead of CSS drop-shadow:
       a path-level CSS drop-shadow is clipped to the default SVG filter region
       (-10%..120% of the path's bbox), which severed the halo at the wave's
       crests and troughs since they land on the bbox edge. #trailGlow uses a
       wide region so the glow renders fully at every peak and dip. */
    filter: url(#trailGlow);
    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.4rem 0.75rem 0.4rem 0.45rem;
    border-radius: 999px;
    /* Match the app's card surface (same bg/border/blur/shadow tokens) so the
       button reads as part of the same material language, not a stray overlay. */
    background: var(--pf-card-bg);
    border: 1px solid var(--pf-card-border);
    color: var(--pf-text);
    cursor: pointer;
    backdrop-filter: blur(14px) saturate(140%);
    -webkit-backdrop-filter: blur(14px) saturate(140%);
    box-shadow:
        var(--pf-card-highlight),
        var(--pf-card-shadow);
    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(-2px);
    border-color: var(--pf-card-border-hover);
    box-shadow:
        var(--pf-card-highlight),
        var(--pf-card-shadow-hover),
        0 0 16px 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;
}

/* Accent chip behind the glyph - gives the button a clear identity tile
   instead of a bare icon, echoing how accent is used elsewhere in the UI. */
.pf-xml-fab__icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.625rem;
    height: 1.625rem;
    border-radius: 50%;
    background: rgba(var(--pf-accent-rgb), 0.14);
    color: var(--pf-accent);
    flex: none;
}

.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". */
/* Step progress: a straight runway with an accent fill and a plane parked at
   the fill tip. The old wavy S-curve + offset-path plane read as a meaningless
   squiggle and never animated (each step is a full page navigation, so a CSS
   transition never saw the value change). A straight bar reads instantly as
   "progress", and a fill-in keyframe on mount makes every step feel alive. */
.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 2.5rem;
}

/* Search input lifts on hover / focus so the field reads as interactive -
   accent border + soft glow, matching the rest of the chrome. */
.pf-docs-search .pf-nav-search__input {
    transition: border-color 0.18s var(--pf-ease),
        background 0.18s var(--pf-ease),
        box-shadow 0.18s var(--pf-ease);
}

.pf-docs-search:hover .pf-nav-search__input {
    border-color: var(--pf-border-strong);
    background: var(--pf-bg);
}

.pf-docs-search .pf-nav-search__input:focus,
.pf-docs-search:focus-within .pf-nav-search__input {
    border-color: var(--pf-accent);
    box-shadow: 0 0 0 3px rgba(var(--pf-accent-rgb), 0.18);
}

/* ─── 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);
    }
}

/* Section-level indeterminate progress bar. Shown while a section's data
   (dropdown/checkbox lists) is still streaming in, so opening a heavier
   section reads as "loading" rather than a half-painted form. Pinned to the
   top edge of the section's card; a short accent sliver sweeps left→right. */
.pf-section {
    position: relative;
}

.pf-section__loading {
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 3px;
    overflow: hidden;
    border-radius: 3px 3px 0 0;
    background: rgba(var(--pf-accent-rgb, 139, 92, 246), 0.12);
    z-index: 3;
}

.pf-section__loading::before {
    content: "";
    position: absolute;
    top: 0;
    bottom: 0;
    left: 0;
    width: 40%;
    border-radius: inherit;
    background: linear-gradient(90deg,
            transparent 0%,
            var(--pf-accent) 50%,
            transparent 100%);
    animation: pfSectionLoading 1.1s ease-in-out infinite;
}

@keyframes pfSectionLoading {
    0% {
        transform: translateX(-110%);
    }

    100% {
        transform: translateX(310%);
    }
}

/* Intentionally still animates under reduced-motion: it's a transient
   progress affordance, and a frozen bar reads as "stuck". */

/* ─── 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.

   OPACITY-ONLY crossfade. Earlier versions slid/scaled the page (and tried
   isolating the content with view-transition-name), but ANY transform here
   warped or jerked the layout - sliding the header/footer, morphing the
   persistent content box between routes. A plain opacity dissolve is calm,
   never moves anything, and reads as "the page changed" without the jank. */
::view-transition-old(root),
::view-transition-new(root) {
    animation-duration: 0.22s;
    animation-timing-function: ease;
}
