h1:focus {
    outline: none;
}

/* Smooth in-page anchor scrolling. Only affects the document scroller (the public landing, whose
   nav/footer links jump to #features etc.); the Panel scrolls inside <main overflow-y-auto>, which
   this does not touch. */
html {
    scroll-behavior: smooth;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid #e50000;
}

.validation-message {
    color: #e50000;
}

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.darker-border-checkbox.form-check-input {
    border-color: #929292;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* BbRichTextEditor's "insert link" dialog is a BbDialog portaled to the same host as a
   DialogService dialog (news, addon), both at z-50, so it gets covered. It is not a descendant of
   the editor; we identify it by the link URL input it contains and lift the whole thing above the
   surrounding dialog. The content panel (which holds #link-url) goes on top; its overlay/backdrop is
   the adjacent .fixed sibling (either side) and sits just below the panel so it covers the news
   dialog without hiding the link panel. Adjacency keeps the news dialog's own overlay untouched. */
.fixed:has(#link-url),
.fixed:has(.link-url) {
    z-index: 9999 !important;
}
.fixed:has(+ .fixed #link-url),
.fixed:has(#link-url) + .fixed {
    z-index: 9998 !important;
}

/* Sidebar items are clickable: show the pointer cursor (BlazorBlueprint renders them as
   <button>/<a>, which default to the arrow/text cursor). */
[data-sidebar="menu-button"],
[data-sidebar="menu-sub-button"],
[data-sidebar="menu-action"],
[data-sidebar="trigger"],
[data-sidebar="group-label"][role="button"] {
    cursor: pointer;
}

/* BlazorBlueprint hardcodes a few radii that ignore --radius: the sidebar items
   (--sidebar-*-radius, .375rem) and the inset container (rounded-xl -> --radius-xl, .75rem).
   Tie them to --radius so the theme's radius governs the sidebar too. !important because
   this file loads before blazorblueprint.css. */
:root {
    --radius-xl: var(--radius) !important;
    --sidebar-menu-button-radius: var(--radius) !important;
    --sidebar-sub-button-radius: var(--radius) !important;
    --sidebar-badge-radius: var(--radius) !important;
    --sidebar-group-label-radius: var(--radius) !important;
}

/* Shared prose typography for rendered Markdown (blog article + the editor's live preview).
   Global (not component-scoped) so both the public page and the MarkdownSourceField preview render
   identically. Class-qualified selectors outrank Tailwind preflight's h1-h6 / list resets. */
.markdown-body {
    font-size: 0.975rem;
    line-height: 1.75;
    color: var(--foreground);
    max-width: 100%;
    /* Break only a token too long to fit (e.g. a long URL); normal words wrap at spaces. */
    overflow-wrap: break-word;
}

.markdown-body * { max-width: 100%; }
.markdown-body > *:first-child { margin-top: 0; }

.markdown-body h1,
.markdown-body h2,
.markdown-body h3,
.markdown-body h4 {
    font-weight: 600;
    line-height: 1.3;
    margin-top: 2rem;
    margin-bottom: 0.75rem;
    letter-spacing: -0.01em;
}

.markdown-body h1 { font-size: 1.75rem; }
.markdown-body h2 { font-size: 1.4rem; }
.markdown-body h3 { font-size: 1.2rem; }
.markdown-body h4 { font-size: 1.05rem; }

.markdown-body p,
.markdown-body ul,
.markdown-body ol,
.markdown-body blockquote,
.markdown-body pre,
.markdown-body table {
    margin-top: 0;
    margin-bottom: 1.1rem;
}

.markdown-body a {
    color: var(--primary);
    text-decoration: underline;
    text-underline-offset: 2px;
}

/* ++text++ underline (Markdig EmphasisExtras -> <ins>) and ==text== highlight (-> <mark>). */
.markdown-body ins {
    text-decoration: underline;
    text-underline-offset: 2px;
}

.markdown-body mark {
    background: color-mix(in srgb, var(--primary) 25%, transparent);
    color: inherit;
    padding: 0.05em 0.2em;
    border-radius: 0.2rem;
}

.markdown-body ul,
.markdown-body ol { padding-left: 1.5rem; }
.markdown-body ul { list-style: disc; }
.markdown-body ol { list-style: decimal; }
.markdown-body li { margin-bottom: 0.35rem; }

.markdown-body img {
    max-width: 100%;
    height: auto;
    border-radius: 0.5rem;
    margin: 1.25rem 0;
}

.markdown-body blockquote {
    border-left: 3px solid var(--border);
    padding-left: 1rem;
    color: var(--muted-foreground);
    font-style: italic;
}

.markdown-body code {
    font-family: var(--font-mono, ui-monospace, monospace);
    font-size: 0.875em;
    background: var(--muted);
    padding: 0.15em 0.35em;
    border-radius: 0.3rem;
}

.markdown-body pre {
    background: var(--muted);
    padding: 1rem;
    border-radius: 0.5rem;
    overflow-x: auto;
}

.markdown-body pre code {
    background: transparent;
    padding: 0;
}

.markdown-body hr {
    border: 0;
    border-top: 1px solid var(--border);
    margin: 2rem 0;
}

.markdown-body table {
    width: 100%;
    border-collapse: collapse;
}

.markdown-body th,
.markdown-body td {
    border: 1px solid var(--border);
    padding: 0.5rem 0.75rem;
    text-align: left;
}
/* ============================================================================
   Landing (marketing) mobile overrides — see docs/RESPONSIVE.md (norm N10).
   The landing is styled with inline px values; these class hooks + !important
   media rules are the only way to adapt it below 700px without touching the
   desktop rendering (rules only fire on small viewports). Global (not scoped)
   because LandingFooter/LandingNav also render outside Landing.razor's scope.
   ========================================================================== */

@media (max-width: 700px) {
    /* Every landing section root carries .landing-section: shrink the fixed
       32px side padding that inline styles would otherwise lock in. */
    .landing-section {
        padding-left: 20px !important;
        padding-right: 20px !important;
    }

    /* Hero: fluid type (max = the inline desktop value, so >=700px is identical)
       and less dead space above the fold. */
    .landing-hero {
        padding-top: 84px !important;
    }

    .landing-hero-title {
        font-size: clamp(30px, 9vw, 52px) !important;
        letter-spacing: -0.02em !important;
    }

    .landing-hero-subtitle {
        font-size: clamp(16px, 4.5vw, 19px) !important;
    }

    /* CTA card: its 64/40px inner padding compounds with the section padding. */
    .landing-cta-inner {
        padding: 40px 20px !important;
    }

    /* Tier comparison table: keep columns readable and scroll horizontally
       instead of crushing them (wrapper already has overflow:auto). */
    .landing-compare-table {
        min-width: 560px;
    }

    .landing-compare-scroll {
        -webkit-overflow-scrolling: touch;
    }
}

/* Landing nav burger: only exists on phones (the center links are hidden below md).
   !important because app.css loads before the Tailwind bundles that style BbButton. */
.landing-nav-burger {
    display: none !important;
}

@media (max-width: 767px) {
    .landing-nav-burger {
        display: inline-flex !important;
    }
}

/* Safety: if the burger panel was left open and the viewport grows past md, hide it. */
@media (min-width: 768px) {
    .landing-nav-mobile {
        display: none !important;
    }
}

/* ============================================================================
   BbDataTable mobile safety net (norm N5, docs/RESPONSIVE.md).
   BlazorBlueprint 3.11 renders the table as <div class="rounded-md border">
   <table class="w-full caption-bottom text-sm"> with NO scroll container, so a
   wide table stretches the whole page on phones. The component only exposes
   `Class` (which also wraps the toolbar/pagination), so the horizontal scroll
   is applied here to the lib's own table wrapper instead.
   ========================================================================== */
.rounded-md.border:has(> table.caption-bottom) {
    overflow-x: auto;
}

/* The table's toolbar (search + column picker + ToolbarActions) and pagination bar
   are non-wrapping flex rows in the lib; on phones the trailing action button and
   the page-size picker clip off-screen. Wrapping is a no-op at desktop widths.
   Matched on the root + wrapper (not the <table>) because the EmptyTemplate
   replaces the table entirely and the toolbar must still wrap then. */
.w-full.space-y-4:has(> .rounded-md.border) > .flex.items-center.justify-between,
.w-full.space-y-4:has(> .rounded-md.border) > .flex.items-center.justify-between > .flex,
.w-full.space-y-4:has(> .rounded-md.border) > nav.flex.items-center.justify-between {
    flex-wrap: wrap;
    row-gap: 0.5rem;
}

/* ============================================================================
   Public statically-rendered pages (landing, blog, legal).
   These routes are [ExcludeFromInteractiveRouting] so search engines get real
   HTML; everything below replaces behaviour that used to rely on the Blazor
   circuit. Rules live here rather than in a scoped .razor.css because scoped
   CSS is tied to a component, and the lightbox is now built by js/public.js.
   ========================================================================== */

/* Image lightbox (was LandingLightbox.razor + its scoped css). */
.lightbox {
    position: fixed;
    inset: 0;
    z-index: 100;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 4vmin;
    background: rgba(0, 0, 0, .8);
    backdrop-filter: blur(6px);
    cursor: zoom-out;
    outline: none;
    animation: lb-fade .18s ease-out;
}

.lightbox img {
    max-width: 100%;
    max-height: 100%;
    border-radius: var(--radius);
    box-shadow: 0 30px 90px -20px rgba(0, 0, 0, .8);
    animation: lb-zoom .22s ease-out;
}

@keyframes lb-fade {
    from { opacity: 0; }
}

@keyframes lb-zoom {
    from { transform: scale(.94); opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
    .lightbox, .lightbox img { animation: none; }
}

/* CSS-only burger disclosure: a hidden checkbox drives the mobile panel, so the
   nav needs no circuit. The checkbox is never focusable itself - the <label>
   carries the button semantics. */
.landing-nav-toggle {
    position: absolute;
    opacity: 0;
    pointer-events: none;
    width: 0;
    height: 0;
}

.landing-nav-mobile {
    display: none;
}

.landing-nav-toggle:checked ~ .landing-nav-mobile {
    display: flex;
}

/* Swap the burger/close glyphs from the same checkbox state. */
.landing-nav-burger .landing-nav-icon-close,
.landing-nav-toggle:checked ~ nav .landing-nav-burger .landing-nav-icon-open {
    display: none;
}

.landing-nav-toggle:checked ~ nav .landing-nav-burger .landing-nav-icon-close {
    display: inline-flex;
}

/* Keyboard affordance: the label is the real control, so mirror focus styling. */
.landing-nav-toggle:focus-visible ~ nav .landing-nav-burger {
    outline: 2px solid var(--ring);
    outline-offset: 2px;
}

/* Landing FAQ: native <details> disclosure replacing BbAccordion (no circuit on this page). */
.landing-faq {
    display: flex;
    flex-direction: column;
    gap: 8px;
}

.landing-faq-item {
    border: 1px solid var(--border);
    border-radius: var(--radius);
    background: var(--card);
    overflow: hidden;
}

.landing-faq-question {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    padding: 18px 20px;
    font-size: 16px;
    font-weight: 600;
    line-height: 1.4;
    cursor: pointer;
    list-style: none;
}

/* Hide the default disclosure marker in every engine. */
.landing-faq-question::-webkit-details-marker {
    display: none;
}

.landing-faq-question:hover {
    color: var(--primary);
}

.landing-faq-chevron {
    flex-shrink: 0;
    color: var(--muted-foreground);
    transition: transform .18s ease-out;
}

.landing-faq-item[open] .landing-faq-chevron {
    transform: rotate(180deg);
}

.landing-faq-answer {
    padding: 0 20px 20px;
    font-size: 15px;
    line-height: 1.6;
    color: var(--muted-foreground);
}

@media (prefers-reduced-motion: reduce) {
    .landing-faq-chevron { transition: none; }
}
