@page "/docs/changelog"
@namespace Lumeo.Docs.Pages.Docs

<PageTitle>Changelog — Lumeo</PageTitle>

<Container MaxWidth="3xl" Padding="0" Center="false">
<Stack Gap="10">

    <Stack Gap="4">
        <Heading Level="1" Size="3xl">Changelog</Heading>
        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            All notable changes to Lumeo are documented here. Each release groups changes into Added, Improved, and Fixed categories.
        </Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.2.2 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.2.2</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>DataGrid Groupable-column drag affordance + group-panel placeholder localisation.</strong>
            Two follow-ups from real users hitting the new themed group-panel in 2.2.0/2.2.1: there was no visual indication on column headers that they support drag-into-panel (users had to try each one to discover which were Groupable), and the panel's "Drag a Groupable column header here…" placeholder was still hardcoded English (missed by the 2.2.0 i18n sweep).
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Drag-to-group affordance on column headers</strong> — when <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowGroupPanel="true"</code> and the column is <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Groupable</code>, the header now reveals a muted Group icon on hover (mirroring the filter button's opacity pattern), plus the cell carries <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">cursor-grab</code>. Title attribute uses <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">L["DataGrid.DragToGroup"]</code> ("Drag to group by this column" / "Zum Gruppieren nach dieser Spalte ziehen"). Both icon + cursor are hidden when there's no panel to drop into, so non-grouping grids aren't cluttered.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Fixed (i18n)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Group-panel empty-state placeholder</strong> "Drag a Groupable column header here, or use the dropdown" was still a hardcoded English literal inside <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@(GroupPanelText ?? "…")</code> — the 2.2.0 i18n agents missed it because of the null-coalesce shape. Now routes through <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">L["DataGrid.GroupPanelPlaceholder"]</code>. Consumers passing an explicit <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GroupPanelText</code> still take precedence — back-compat preserved.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Localisation keys added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGrid.GroupPanelPlaceholder</code> (EN + DE)</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGrid.DragToGroup</code> (EN + DE)</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.2.1 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.2.1</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>DataGrid group panel: hard-right alignment + persistence on grouping changes.</strong>
            Two 2.2.0 regressions surfaced by the first consumer of the new themed group-panel: the new "+ Add group level" <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DropdownMenu</code> didn't sit at the right edge of the panel, and grouping mutations weren't being written to localStorage even though sort/filter/columns were.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Group panel "+ Add group level" picker now right-aligned</strong> — the 2.2.0 refactor moved the trigger into a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;DropdownMenu&gt;</code> component, which renders its own <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">relative inline-block</code> wrapper as root. The <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ml-auto</code> on the inner trigger button had no effect on the flex-parent panel because <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DropdownMenu</code> was the actual flex child. Wrapped the component in a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;div class="ml-auto"&gt;</code> so the picker sits hard-right again.</li>
                <li><strong>Grouping changes now persist to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">localStorage</code></strong> — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AddGroupField</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">RemoveGroupField</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ClearGroupFields</code> didn't call <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ScheduleAutoSave()</code>, so the snapshot serialization built in v2.1.1 had nothing to act on. Sort/filter changes had been calling it from day one. Three matching <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ScheduleAutoSave()</code> calls added — grouping now round-trips through reload identically to sort/filter/columns.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">No new tests — the existing v2.1.1 snapshot-and-restore tests cover the persistence contract; the missing trigger was the bug. The right-align fix is visual and covered by manual verification in the PR checklist.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.2.0 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.2.0</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Theming &amp; accessibility polish — a quality release.</strong>
            A library-wide sweep across CSS theming, keyboard accessibility, and i18n coverage. No new public API surface, no breaking changes — but the library is now consistently themable, keyboard-navigable, and translatable end-to-end.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed (CSS theming)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Placeholder hover-override (B1)</strong> — inner <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;span class="text-muted-foreground"&gt;</code> placeholder text inside hover-styled trigger buttons no longer becomes illegible on hover (dark grey on accent green). Fixed across <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">TimePicker</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DateTimePicker</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DatePicker</code> (5 placeholder spans) via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">group</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">group-hover:text-accent-foreground</code>.</li>
                <li><strong>DataGrid group-panel "+ Add group level" dropdown</strong> — was a native HTML <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;select&gt;</code> rendering with OS-default chrome (white-on-blue on Windows). Replaced with Lumeo's own <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;DropdownMenu&gt;</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;DropdownMenuItem&gt;</code>.</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">NavigationMenuMobile</code></strong> — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Class</code> and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AdditionalAttributes</code> were declared but never propagated to a root element. Now applied (Class merged into inner SheetContent; AdditionalAttributes on the inner div).</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Improved (accessibility — keyboard focus)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>~110 new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">focus-visible:ring-*</code> rings</strong> across ~45 files: DataGrid (header sort/filter buttons, toolbar buttons, row expand/edit/save/cancel, batch ops), Editor (toolbar, BubbleMenu, AiActionMenu), QueryBuilder (combinator pills), Calendar (day cells + nav buttons), Tour, DatePicker preset buttons, ThemeSwitcher swatches, Pagination, NumberInput steppers, FileUpload buttons, TreeViewNode, Sortable handles, RadioGroupCard, MegaMenuLink, PivotGrid, Segmented, ConsentBanner, ImageGallery, InplaceEditor, ReasoningDisplay, FileManager, TreeSelect, PromptInput, PickList, BackToTop, Transfer, and more.</li>
                <li><strong>Special-case patterns spotted &amp; fixed</strong>: hover-revealed icon buttons (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">group-hover:opacity-100</code>) now also reveal on keyboard focus via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">focus-visible:opacity-100</code>; adjacent button-groups (steppers, file-action rows) use <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">focus-visible:relative focus-visible:z-10</code> so rings aren't clipped; overlay/lightbox buttons over <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">bg-black/80</code> backdrops use <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ring-white</code> for visibility.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Improved (i18n — multi-lingual UI)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>62 hardcoded English UI strings routed through <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ILumeoLocalizer</code></strong> across ~32 components (FileManager 15, QueryBuilder 8, PickList 4, Scheduler 3, ThemeSwitcher 3, and many one-offs across aria-labels, titles, placeholders, default parameter values).</li>
                <li><strong>~50 new localization keys</strong> added to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LumeoDefaultStrings.cs</code> (English + German pairs). New namespaces: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">FileManager.*</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">QueryBuilder.*</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PickList.*</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Scheduler.*</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Theme.*</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Chart.*</code> (Loading), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BottomNav.*</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Dock.*</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Badge.*</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Chip.*</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Alert.*</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ImageCompare.*</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PromptInput.*</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PivotGrid.*</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Editor.*</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Tabs.*</code>.</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Effective*</code> nullable-parameter pattern</strong> introduced for default-value localization (e.g. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LoadingText</code> on Charts, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AriaLabel</code> on Dock/BottomNavFab/TriggerDropdown/ToastClose, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GrandTotalLabel</code> on PivotGrid). Consumers passing explicit strings still work — back-compat preserved.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Docs</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Hero badge bumped to v2.2.0. Install snippets (Code, Cli) updated to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">--version 2.2.0</code>.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo-mcp</code> package.json + package-lock + src/index.ts + src/registry.json bumped to 2.2.0 (lockstep).</li>
                <li>New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGrid.AddGroupLevel</code> key (EN/DE) added.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Known follow-ups (deferred to 2.2.1)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">QueryBuilderGroup</code> Field/Operator pickers still use native <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;select&gt;</code> with themed trigger but OS-default dropdown popup. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">InplaceEditor</code> the same. Refactor to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Select&gt;</code> needs the disabled-placeholder pattern.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">StepperStep</code> is a pure-registration component; its <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Class</code> parameter is currently dead. Plan: thread through <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">StepRegistration</code> record so parent <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Stepper</code> can apply it.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">No new bUnit tests for this sweep — the changes are non-behavioural (CSS classes, aria-labels, localization keys). Existing test suite continues to pass.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.1.4 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.1.4</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>JS module import URL gets a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">?v=&lt;version&gt;</code> cache-buster.</strong>
            Library JS under <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_content/Lumeo/js/components.js</code> is served WITHOUT a content-hash (unlike the Blazor framework files), so an already-loaded browser tab kept its 2.1.2 <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">components.js</code> after upgrading the lib to 2.1.3 — and new C# code calling new exports like <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">registerViewportListener</code> threw <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">JSException: 'registerViewportListener' is not a function</code> at runtime. Fixed by appending <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">?v=&lt;assembly-version&gt;</code> to library module imports.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ComponentInteropService.GetModuleAsync</code></strong> + <strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ImportModuleAsync</code></strong> now append <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">?v={assembly-version}</code> to all <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_content/Lumeo*/</code> module URLs. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">KeyboardShortcutService.GetModuleAsync</code> updated symmetrically.</li>
                <li><strong>Upgrades from this release forward</strong> are immune to the stale-cache class of failure when new JS exports are added in the lib (e.g. the v2.1.3 <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">registerViewportListener</code> failure). v2.1.3 users hitting that error today need a hard refresh (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Ctrl/Cmd+Shift+R</code>); from v2.1.4 the cache-buster handles it automatically.</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Notes</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Version derived once from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AssemblyInformationalVersionAttribute</code> (which <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Directory.Build.props &lt;Version&gt;</code> drives), so it tracks every release without manual maintenance.</li>
                <li>URLs that already contain a query string pass through untouched — consumer overrides (e.g. local debugging with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">?nocache=1</code>) are preserved.</li>
                <li>Non-library URLs are passed through unchanged, so consumer JS modules loaded via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ImportModuleAsync</code> aren't affected.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.1.3 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.1.3</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Responsive overlays, polished swipe gesture, and 100% docs parameter coverage.</strong>
            Three threads landed together: a reactive <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IResponsiveService</code> + per-overlay mobile overrides so a single <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Overlay.ShowSheetAsync</code> can switch from side panel on desktop to fullscreen bottom-sheet on mobile and back across rotation; a swipe gesture rewrite (10px activation deadzone + direction lock + rubber-band) that kills the wackelig feel users reported on small finger movements; and a full sweep of the API tables on every component docs page so every <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[Parameter]</code> is now documented (500+ rows added across 60 pages).
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IResponsiveService</code></strong> — reactive viewport/breakpoint service. Exposes <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Width</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Height</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Current</code> (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Breakpoint</code> enum: Xs/Sm/Md/Lg/Xl/Xxl aligned with Tailwind v4), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsMobile</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsTablet</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsDesktop</code>, and a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ViewportChanged</code> event. One JS resize listener per circuit, debounced 100ms.</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OverlayOptions.MobileBreakpoint/MobileSheetSide/MobileSheetSize/MobileSwipeToClose</code></strong> — per-overlay responsive overrides. Below <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">MobileBreakpoint</code> (default 768px = Tailwind md) the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Mobile*</code> fields take precedence. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OverlayProvider</code> re-renders reactively on viewport crossing — sheet flips correctly when a tablet is rotated mid-flight.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Swipe gesture (Sheet + Drawer)</strong> — previously the element followed the finger from pixel 1 with no axis lock; users reported the sheet felt wackelig on micro-movements and accidentally engaged on diagonal scroll attempts. Now: <strong>10px activation deadzone</strong> (finger must commit before the sheet moves), <strong>direction lock at activation</strong> (gesture locked to the dominant axis — diagonal-on-bottom-sheet scrolls content instead of dismissing), and <strong>rubber-band past 150px</strong> so the sheet doesn't fly off-screen when the user keeps pulling. Dismiss threshold (100px) unchanged.</li>
                <li><strong>Docs parameter coverage now 100%</strong> across all 163 components — 504 missing API table rows added across 60 pages (largely Chart components which previously documented their core 5 params but not the recurring Width/Height/Theme/ColorPalette/AnimationDuration/MarkLine/MarkPoint/Toolbox surface).</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Docs</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Hero badge bumped to v2.1.3.</li>
                <li>OverlayService page: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OverlayOptions</code> table gains the four <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Mobile*</code> rows; new "Responsive sheets" section with the desktop-Right ↔ mobile-Bottom example.</li>
                <li>Sheet page: cross-reference to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IResponsiveService</code> for live-reactive sizing decisions.</li>
                <li>New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IResponsiveService</code> docs page covering the API and the recommended subscription pattern.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">~10 new bUnit/xUnit tests cover the responsive service (FromWidth mapping, event firing, breakpoint flags) and the OverlayOptions responsive defaults. Swipe-threshold change is JS-only — relies on manual mobile testing per the PR checklist.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.1.2 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.1.2</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>DataGrid column filter popover no longer spawns a phantom horizontal scrollbar.</strong>
            On the rightmost columns of a grid with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">overflow-x:auto</code>, opening a column filter made the entire grid scroll horizontally. The popover wrapper was <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">position:absolute</code> relative to its <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;th&gt;</code>, so its content extended past the cell edge and was counted in the scroll-container's <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">scrollWidth</code>. Fixed by reusing the Floating-UI-style positioner that <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PopoverContent</code> already uses — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">position:fixed</code> with coords from the trigger's <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">getBoundingClientRect()</code>, follow on scroll/resize, and viewport-flip when overflow would push the popover off-screen.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>DataGrid column filter popover</strong> now renders with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">position:fixed</code> instead of <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">position:absolute</code>. The popover is pinned to the trigger via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Interop.PositionFixed</code> (same path as <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PopoverContent</code>), so its layout box no longer contributes to the scroll container's <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">scrollWidth</code>.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Filter popover click-outside dismiss</strong> — previously the popover only closed via Apply / Clear; clicking elsewhere left it open. Now registers a click-outside listener (excluding the trigger so the toggle button doesn't immediately close-then-reopen) and follows the trigger on grid scroll/resize.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">2 new bUnit regression tests guard the inline <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">position:fixed</code> + stable trigger-id contract that the JS positioner depends on.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.1.1 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.1.1</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>DataGrid group panel survives reload, Sheet mobile-fullscreen, OverlayService swipe-to-close.</strong>
            Four real library fixes folded into a single patch release. The biggest one: layout snapshots silently dropped the runtime group-panel chips so multi-level grouping vanished on reload while sorts/filters/columns restored cleanly.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>DataGrid layout persistence</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SnapshotCurrentLayout</code> and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BuildLayoutSnapshot</code> now capture <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_runtimeGroupFields</code>; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ApplyLayoutAsync</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ApplyLayoutSnapshotAsync</code> restore the panel chips. Internal <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridLayout</code> + public <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridLayoutSnapshot</code> gain <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GroupByFields</code>; snapshot version bumped 1 → 2. v1 payloads still load via the legacy single-level <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GroupBy</code> fallback.</li>
                <li><strong>DataGrid column filter popover</strong>: custom <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">FilterTemplate</code> rows with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">hover:bg-accent</code> painted past the rounded-lg corners. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridColumnFilter</code> wrapper gains <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">overflow-hidden</code>; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">w-64</code> → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">min-w-64</code> so wider templates grow the popover instead of overflowing.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Sheet <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Size="Full"</code> on Top/Bottom</strong>: now produces a true mobile-fullscreen sheet (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">inset-y-0 h-full max-h-full</code>) instead of being a silent no-op. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Sm</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lg</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Xl</code> cap height at 40/70/85vh for symmetry with Left/Right width caps. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Default</code> keeps legacy content-driven height — back-compat.</li>
                <li><strong>OverlayService <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OverlayOptions.SwipeToClose</code></strong>: programmatically-opened Sheets can now be swipe-dismissed, matching the declarative <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;SheetContent SwipeToClose="true"&gt;</code> path. Drawers already had swipe-close by default; this puts Sheets at parity. Default <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">false</code> — back-compat.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Docs</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>DataGrid page: Persistence demo description and "Saving and loading layouts" prose now mention the group panel is included; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Version: 2</code> noted alongside the v1-backcompat clause.</li>
                <li>Sheet page: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Size</code> row now documents the Top/Bottom height behaviour and the Full-on-Bottom mobile-fullscreen pattern.</li>
                <li>Overlay Service page: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SwipeToClose</code> added to the OverlayOptions API table.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">12 new bUnit tests cover snapshot capture &amp; round-trip (incl. v1 back-compat), filter popover clipping, Sheet sizing on Top/Bottom, and OverlayService option routing.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.1.0 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.1.0</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>DataGrid: multi-level grouping &amp; the runtime group panel now work in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ServerMode</code>.</strong>
            Single-level <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GroupBy</code> already worked in ServerMode; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GroupByFields</code> and the panel handlers (drag-to-group, add-level dropdown, per-chip remove, clear-all) were silently no-ops because each UI mutator short-circuited with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">if (!ServerMode) ProcessClientData()</code>. They now route through a shared <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">RegroupServerItems()</code> that delegates to the same <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ProcessSingleLevelGrouping</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ProcessMultiLevelGrouping</code> client mode uses — so multi-level, expand-state preservation, and per-group aggregates are now symmetrical across modes.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>DataGrid <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ServerMode</code></strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GroupByFields</code> (multi-level) now applies to server-returned items.</li>
                <li><strong>DataGrid group panel (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowGroupPanel="true"</code>)</strong>: drag-to-group, add-level dropdown, per-chip remove, and clear-all all fire in ServerMode without an extra server round-trip.</li>
                <li><strong>Per-page semantic preserved</strong>: grouping runs over the rows the server returned for the current page. For cross-page grouping, either disable pagination (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowPagination="false"</code>) so the server delivers all rows in one response, or have the server pre-aggregate groups itself. This is a consumer-side architecture choice, not a grid limitation.</li>
                <li><strong>Side benefit</strong>: ServerMode single-level grouping now also computes per-group aggregates (was previously client-mode only) because the unified path runs through <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ProcessSingleLevelGrouping</code>.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Docs</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>DataGrid component page: new ServerMode-grouping callout above the Server-Side Mode demo; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GroupBy</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GroupByFields</code> parameter rows clarified; "Group panel &amp; multi-level grouping" section gains a per-page-caveat paragraph.</li>
                <li>MCP registry description updated across registry.json, registry-search.json, registry/data-grid.json, components-api.json, and skills catalog to advertise "multi-level group (client + server)".</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">2438/2438 tests pass · core + DataGrid + docs build clean (0/0). 6 new bUnit tests cover ServerMode multi-level grouping and each UI handler.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.1 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.1</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>MCP registry fix — regenerated against the 2.0.0 component surface.</strong>
            The MCP data files were last generated at rc.38 and never regenerated for 2.0.0, so they shipped 145 components labelled <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">2.0.0-rc.38</code> while the library had grown to 149. npm forbids overwriting the published <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@lumeo-ui/mcp-server</code> 2.0.0, so the fix ships under a new lockstep version.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>RazorParameterScanner</strong>: only parsed the LAST <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@code</code> block, silently dropping all 14 of FileManager's <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[Parameter]</code>s. Now parses every <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@code</code> block independently and merges results — so Checkbox's markup-bearing RenderFragment block no longer corrupts extraction.</li>
                <li><strong>RegistryGen version source</strong>: now reads the lockstep <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Version&gt;</code> from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Directory.Build.props</code> instead of two hardcoded literals (one was still <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">2.0.0-rc.38</code>) — registry can't drift again.</li>
                <li><strong>Stale install snippets in docs</strong>: the Code component demo and CLI docs page hardcoded <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">--version 2.0.0</code>. RegistryGen pulls the Code example into <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">components-api.json</code>, so MCP clients got stale guidance — the exact bug class this release fixes. Bumped to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">2.0.1</code>.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Changed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Lockstep bump</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Directory.Build.props</code> 2.0.0 → 2.0.1; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">tools/lumeo-mcp</code> package.json + index.ts → 2.0.1.</li>
                <li><strong>Regenerated registry</strong>: 149 components, 3510 params, 104 enums, 59 records, 0 parse failures. Skill catalog auto-resynced via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">sync-skill-catalog.yml</code>.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>First stable release — the rc.1..rc.54 series is superseded.</strong>
            Promotes the lockstep version from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">2.0.0-rc.54</code> to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">2.0.0</code>. Readiness audit + Codex review both green: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.slnx -c Release -warnaserror</code> shows 0 errors / 0 warnings, 2475 tests green, 16/16 E2E against the Release docs server, and shipped NuGet packages have no vulnerable dependencies.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Release prep — blockers</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>README</strong>: replaced the release-candidate callout with stable-2.0 language; pinning guidance updated to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">2.0.0+</code>.</li>
                <li><strong>Companion package drift</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">tools/Lumeo.Cli</code> and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">templates/Lumeo.Templates</code> had a hardcoded <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">2.0.0-rc.16</code> (38 RCs behind). Removed the override so both inherit the lockstep <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Directory.Build.props</code> version.</li>
                <li><strong>MIGRATION.md</strong>: documented the rc.53 <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IComponentInteropService.RegisterCarouselSwipe</code> scrollHandler signature change (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Func&lt;double,double,Task&gt;</code> → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Func&lt;double,double,int,Task&gt;</code>) as breaking change #6.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Release prep — Codex findings</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>npm audit clean at <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">--audit-level=high</code></strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">tools/lumeo-mcp</code> went from 4 vulns (1 high via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">express-rate-limit</code> → vulnerable <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ip-address</code>) to 0; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">docs/Lumeo.Docs</code> from 1 high to 0.</li>
                <li><strong>SECURITY.md</strong>: supported-versions table, private disclosure channels (GitHub Security Advisories + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">security@nativ.sh</code>), response SLAs, scope, and dependency-hygiene statement.</li>
                <li><strong>Package READMEs</strong>: all 9 packable projects now set <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;PackageReadmeFile&gt;README.md</code>. Shared packing rule centralised in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Directory.Build.targets</code> (not <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.props</code> — .props is imported before the project body so the conditional wouldn't see PackageReadmeFile). Zero NU5039.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Open-source declutter</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Removed 25 internal audit docs</strong> from loose <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">docs/*.md</code> (release-readiness iterations + business-sensitive market-gap-analysis). User-facing docs at <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">docs/Lumeo.Docs/</code> untouched (632 files). Gitignored the path so internal notes can't re-enter the published tree.</li>
                <li><strong>Removed <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">docs/superpowers/</code></strong>: 146 files of internal AI-workflow scaffolding (audits, implementation plans, workspace matrices). Preserved in git history.</li>
                <li><strong>Removed scratch build-dump txt files</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_p2.txt</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">build_lib.txt</code>, etc. — accidentally tracked stdout dumps. Gitignored the patterns.</li>
                <li><strong>Dropped stale <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.github/SECURITY.md</code></strong>: still claimed pre-1.0 status. GitHub resolves <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.github/</code> over root, so the Security tab was showing the outdated policy. Removing the duplicate makes GitHub fall back to the canonical root file.</li>
                <li><strong>README refreshed for stable</strong>: PackageReference examples 2.0.0-rc.54 → 2.0.0; component count consistent at 145; stale "~530 KB" core-size claim replaced with a link to the live <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/docs/bundle-facts</code> page.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">2475 tests green (2424 unit + 20 docs + 31 registry-gen) · 16/16 E2E against the Release docs server · zero vulnerable dependencies in shipped packages.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.54 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.54</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Toast deep-fix — close-button focus ring + mobile viewport collapse.</strong>
            ToastClose's focus ring was invisible on coloured variants and the X was opacity-0 for keyboard users; mobile toast viewports squeezed title + description + close + action into unreadable widths on 360px screens.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>ToastClose focus ring</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">focus:ring-1</code> (1px, no color) → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">focus-visible:ring-2 ring-current ring-offset-1</code> — visible on every variant including Destructive/Success/Warning/Info (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ring-current</code> inherits text color → contrast guaranteed).</li>
                <li><strong>ToastClose keyboard reveal</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">focus-visible:opacity-100</code> ensures keyboard focus surfaces the X on desktop (was <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">opacity-0 sm:group-hover:opacity-100</code>, invisible to keyboard users).</li>
                <li><strong>ToastClose affordance</strong>: hover/focus background (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">bg-foreground/10</code>) + text-color shift instead of just an opacity tween.</li>
                <li><strong>ToastViewport mobile collapse</strong>: all 6 positions now collapse to bottom-center with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">inset-x-4</code> (16px gutters) below the sm breakpoint. Width is <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">w-auto sm:w-full sm:max-w-[420px]</code> — mobile fills gutters, desktop caps at 420px. Developer-chosen position takes effect unchanged from sm: upwards.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">2424 + 20 + 31 = 2475 tests green.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.53 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.53</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Swipe re-registration + Carousel jitter + Toast mobile + PullToRefresh DevTools + polish.</strong>
            Five mobile-sprint follow-ups: rc.52's Tabs/Calendar swipe worked only on the first activation (the panel DOM was recreated on switch, leaving the new element with no pointer listeners), Carousel touch-release fought native scroll-snap, Toast's close button overlapped text on mobile, and PullToRefresh failed under DevTools emulation.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Tabs swipe re-registration</strong>: gated on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">firstRender</code> + one-shot flag; in Active render mode the panel DOM is recreated on every switch. Replaced with bidirectional <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_swipeAttached</code> tracking — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnAfterRenderAsync</code> now re-attaches whenever the panel becomes active.</li>
                <li><strong>Calendar swipe re-registration</strong>: same root cause when switching view to Months/Years and back to Days. Same <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_swipeAttached</code> pattern; swipe stays scoped to Days view by design.</li>
                <li><strong>Carousel touch jitter</strong>: on release, browser scroll-snap momentum AND an imperative <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">scrollIntoView({behavior:smooth})</code> fired simultaneously, producing visible jumps. Removed the touchend listener — native scroll-snap handles release smoothly. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">onScroll</code> now reports the nearest snapped index back to .NET to keep <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_currentIndex</code> in sync.</li>
                <li><strong>Toast mobile padding</strong>: 44px close button overlapped toast text because content padding was a fixed <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">pr-6</code>. Changed to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">pr-11 sm:pr-6</code>. Close button flush to corner on mobile (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">right-0 top-0</code>), offset on desktop.</li>
                <li><strong>PullToRefresh DevTools</strong>: DevTools mobile emulation reports <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">pointerType='mouse'</code> and drops pointermove events when the pointer drifts outside the element. Added <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">setPointerCapture</code> on pointerdown so the move stream stays alive.</li>
                <li><strong>Chip dismiss X</strong>: replaced rc.49's negative-margin hit-area trick with a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">before:absolute</code> pseudo-element — visual size back to a compact 20×20 px X with no visible padding sprawl.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Changed (internal)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>BREAKING (internal API)</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IComponentInteropService.RegisterCarouselSwipe</code> scrollHandler signature changed from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Func&lt;double, double, Task&gt;</code> to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Func&lt;double, double, int, Task&gt;</code> (added current snap index). Only caller is Carousel.razor; no external consumers expected.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Docs polish</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>TouchRipple</strong>: new "When to use TouchRipple vs Button's built-in ripple" Alert + canonical demo wrapping a Card with three tappable list rows.</li>
                <li><strong>SafeArea</strong>: realistic sticky bottom-tab-bar demo pinned above the iPhone home indicator via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SafeArea Bottom="true"</code>.</li>
                <li><strong>Pagination</strong>: demos wrapped in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">overflow-x-auto</code> containers + mobile-tip Alert pointing to the Compact variant.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">2424 + 20 + 31 = 2475 tests green (3 new swipe re-registration regression tests: 1 Tabs, 2 Calendar).</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.52 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.52</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Tabs swipe + Calendar swipe + Image pinch/swipe + haptic feedback + 44px polish.</strong>
            Five new mobile-feature additions across existing components plus a dedicated Mobile Playground docs page for testing the whole mobile stack in DevTools or on a real phone.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Tabs</strong> — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SwipeEnabled</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SwipeWrap</code> params. Horizontal swipe on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">TabsContent</code> switches panels (50px threshold, 30px vertical guard, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">touch-action: pan-y</code>, interactive-child bailout). New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">registerTabSwipe</code> JS helper.</li>
                <li><strong>Calendar</strong> — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SwipeEnabled</code> (default true) on the day-grid navigates between months; chevrons bumped to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">h-11 w-11 sm:h-7 sm:w-7</code> for WCAG 2.5.5. New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">registerHorizontalSwipe</code> JS helper.</li>
                <li><strong>ImageGallery</strong> — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GesturesEnabled</code> (default true). Fullscreen viewer pinch-zoom reuses Image.Zoomable pipeline; horizontal swipe navigates prev/next.</li>
                <li><strong>Switch + Checkbox</strong> — opt-in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">HapticFeedback</code> param (default false). Fires <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">HapticsService.Light()</code> on toggle — establishes the cross-cutting haptic-opt-in convention for more inputs in rc.53.</li>
                <li><strong>Pagination + Stepper 44×44 targets</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PaginationItem h-9 w-9 → h-11 w-11 sm:h-9 sm:w-9</code>; Stepper invisible hit-area trick (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">p-1.5 -m-1.5 sm:p-0 sm:m-0</code>) so the visible 32px dot expands to ~44px tap target on touch.</li>
                <li><strong>/docs/mobile — Mobile Playground</strong>: hands-on demos of every gesture, haptic primitive and hit-target retrofit. One page to test the whole mobile stack.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Docs</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Sheet page</strong>: replaced single Bottom swipe demo with a 4-side interactive demo (Top quick-actions, Right edit-form, Bottom filter-list, Left navigation). Updated SwipeToClose API description to reflect all 4 sides.</li>
                <li><strong>TabsPage / CalendarPage / SwitchPage / CheckboxPage / ImagePage</strong>: new Mobile-feature sections + API table rows.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">2421 + 20 + 31 = 2472 tests green (13 new: 7 Tabs swipe, 2 Switch haptic, 2 Checkbox haptic, plus integration coverage).</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.51 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.51</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>NumberTicker animates correctly when loaded in a background tab.</strong>
            Captured <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">start = performance.now()</code> at schedule time meant that when a tab loaded in the background (e.g. Ctrl+Click), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">requestAnimationFrame</code> was paused and the first step() callback never fired — leaving the ticker frozen at 0, or jumping straight to the end value the moment the tab became visible.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>NumberTicker background-tab freeze</strong>: defer <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">start = now</code> until the first <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">step()</code> callback. Standard RAF pattern; zero-cost for foreground-loaded tabs. Verified live on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/docs/bundle-facts</code>.</li>
                <li><strong>NoopInteropService</strong> (Docs.Tests): added the missing rc.49 APIs (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">RegisterPinchZoom</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">UnregisterPinchZoom</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GetScrollTop</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">WheelScrollTop</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">WheelScrollTo</code>). Without these the Docs.Tests project failed to compile — surfaced as red CI on rc.49/50/51 (Publish/Deploy stayed green so packages and docs were online; only CI was noisy).</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">20/20 Docs.Tests + 2410/2410 Lumeo.Tests + 31/31 RegistryGen.Tests pass.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.50 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.50</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Phase 2 mobile primitives in core — 5 new components, 1 service, plus wheel-picker variants.</strong>
            Adds the mobile-only primitives that don't fit into existing components. All in Lumeo core; no separate <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Mobile</code> satellite.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — new components</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>PullToRefresh</strong>: wraps scrollable content; pointer-driven pull-down gesture with spinner fade + rubber-band; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnRefresh</code> fires after threshold. v1: requires the wrapper itself to be the scroll container.</li>
                <li><strong>SwipeActions</strong>: iOS Mail.app pattern with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LeadingActions</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">TrailingActions</code> RenderFragment slots. Foreground translates horizontally on swipe; locks open at <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ActivationThresholdPx</code>; vertical-scroll passthrough. v1: fixed 160px open width.</li>
                <li><strong>SafeArea</strong>: CSS <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">env(safe-area-inset-*)</code> wrapper for notch/system bars. Per-side toggle params; no JS.</li>
                <li><strong>TouchRipple</strong>: Material-style ripple. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PointerDown</code> spawns positioned span with CSS animation. Custom Color/Opacity/DurationMs. CTS-cancelled <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Task.Delay</code> cleanup on Dispose.</li>
                <li><strong>HapticsService</strong>: facade over <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Interop.Vibrate(int)</code>. Light/Medium/Heavy/Success/Error semantic presets. Registered in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AddLumeo()</code>.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Added — picker variants</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>DatePicker <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Variant=Wheel</code></strong>: 3-column iOS-style wheel (day/month/year). Culture-aware month names; Min/Max year range; day re-clamps when month/year changes (no Feb 30).</li>
                <li><strong>TimePicker <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Variant=Wheel</code></strong>: hours/minutes + AM/PM column when 12h. Honours <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CurrentCulture.AMDesignator</code>; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">MinuteStep</code> snap.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Added — interop + CSS</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Interop</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GetScrollTop(elementId)</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">WheelScrollTop(elementRef)</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">WheelScrollTo(elementRef, scrollTop)</code>. Reuses <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Vibrate(int)</code> from rc.49.</li>
                <li><strong>CSS</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.lumeo-touch-ripple</code> + keyframes; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.scrollbar-none</code> utility (Firefox + WebKit).</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">2410/2410 tests pass · build clean (0/0). v1 limitations (Calendar/List remain recommended a11y path for the pickers, no keyboard nav on wheels) documented in inline component comments.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.49 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.49</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Touch-native gestures + 44px hit targets across 13 components.</strong>
            Phase 1 of the mobile sprint: existing components retrofitted with proper touch handling. Same Lumeo API everywhere — no <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Mobile</code> satellite.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — gestures</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Sheet</strong>: swipe-to-close (opt-in via new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SwipeToClose</code> param, false-default to avoid conflicts with scrollable content inside Bottom sheets).</li>
                <li><strong>DataGrid</strong>: column resize via pointer events with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">setPointerCapture</code>.</li>
                <li><strong>Slider</strong>: thumb tooltip on touch-drag (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@onpointerenter/leave</code>).</li>
                <li><strong>Rating</strong>: iOS-style touch-drag-to-scrub across stars.</li>
                <li><strong>ContextMenu</strong>: long-press path (500ms timer, 10px move tolerance, haptic via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">navigator.vibrate(50)</code>).</li>
                <li><strong>Image</strong>: new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Zoomable</code> param — pinch-zoom via two-pointer tracking, scale-delta dispatch, clamped to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[0.5, 5.0]</code>.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Added — 44×44 hit-target audit (WCAG 2.5.5)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Window</strong> title-bar buttons (Close/Min/Max/Restore), resize handle.</li>
                <li><strong>Chip</strong> dismiss button, <strong>TabsTrigger</strong> close X, <strong>DialogContent</strong> close X, <strong>SortableList</strong> drag handle, <strong>SidebarMenuButton</strong> row.</li>
                <li><strong>ToastClose</strong> with mobile visibility fix — was <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">opacity-0</code> hover-only, now <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">opacity-100 sm:opacity-0 sm:group-hover:opacity-100</code>.</li>
                <li><strong>DropdownMenuItem + ContextMenuItem</strong> rows: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">min-h-11 sm:min-h-0</code>.</li>
                <li>Pattern: full 44px hit area on touch (no <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">sm:</code>), compact original size at <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">sm:</code> and up. Icon sizes unchanged so desktop visual density is preserved.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Fixed (caught by review-agent before tag)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Rating mouse double-fire</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">pointerdown</code> was firing for mouse too, causing <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ValueChanged</code> to double-fire with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AllowHalf=true</code>. Fixed via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">e.PointerType=='mouse'</code> early-return.</li>
                <li><strong>ToastClose invisible-but-blocking on mobile</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">opacity-0</code> hover-only with 44px hit area was capturing taps on the toast title. Fixed via responsive <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">opacity-100 sm:opacity-0</code>.</li>
                <li><strong>Pinch-zoom 3-finger baseline drift</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lastDistance</code> now re-baselined on 3→2 transition.</li>
                <li><strong>Sheet swipe-vs-scroll conflict</strong> on Bottom sheets: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SwipeToClose</code> now opt-in.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">2410/2410 tests pass.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.47 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.47</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/docs/bundle-facts</code> — add Hot Reload section + clarify .nupkg numbers.</strong>
            User feedback: the bundle-facts page should mention Lumeo's Hot Reload quality with real architectural reasons, plus the "Why brotli not .nupkg" accordion should reflect the rc.40 .nupkg reduction (1.35 MB → 660 KB).
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Section 8 "Hot Reload that actually works"</strong> with 6 concrete facts, each paired with a "what other libs do" contrast so the comparison is grounded:
                    <ol class="ml-6 mt-1 space-y-0.5 list-decimal list-inside">
                        <li>No Blazor CSS isolation (Tailwind utility-first, no per-component scoped-class hashing).</li>
                        <li>Theme is CSS variables, not a C# object (no recompile to swap colors).</li>
                        <li>No source generators in runtime hot path (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[LumeoForm]</code> is opt-in).</li>
                        <li>No reflection in render path (rc.44 compiled-getter fix).</li>
                        <li>CascadingValue composition over inheritance — each Razor file HRs independently.</li>
                        <li>Tailwind + Razor SDK watch run in parallel; CSS swaps don't go through Blazor at all.</li>
                    </ol>
                </li>
                <li><strong>Why-brotli-not-nupkg Accordion</strong> rewritten with a 3-row comparison block (1.35 MB pre-rc.40 → 660 KB post-rc.40 → 400 KB brotli to browser) and a link to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">nuget.org/packages/Lumeo</code> for self-verification.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">Docs project clean (0/0).</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.46 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.46</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/docs/bundle-facts</code> page — replaces the easter-egg Dialog on the home page.</strong>
            User feedback: the click-to-open Dialog was cramped and undiscoverable. Replaced with a dedicated animated page that uses Lumeo's own motion primitives (BlurFade, NumberTicker, Sparkles, Marquee, BorderBeam, ShimmerButton, Accordion) to showcase the bundle-size story.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">New page sections</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Hero</strong> — animated <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">NumberTicker</code> hitting 400 KB inside a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Sparkles</code> overlay.</li>
                <li><strong>Per-package bar chart</strong> — 7 staggered <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BlurFade</code> rows showing proportional brotli sizes.</li>
                <li><strong>What's actually in the .nupkg</strong> — 4 cards explaining Lumeo.dll / lumeo.css / components.js / build-props.</li>
                <li><strong>"What's NOT in core" Marquee</strong> — endless strip of deliberately-excluded dependencies (Newtonsoft.Json, AutoMapper, jQuery, etc.).</li>
                <li><strong>Comparison table</strong> vs MudBlazor / Radzen / FluentUI / Telerik (approximate brotli sizes).</li>
                <li><strong>Heavy features are opt-in</strong> — 3-tile Bento for Charts / DataGrid / Editor.</li>
                <li><strong>Why brotli not .nupkg</strong> — Accordion with 3 explainers.</li>
                <li><strong>CTA footer</strong> — ShimmerButton to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/docs/installation</code>.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Home.razor</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Removed the entire <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Dialog @@bind-Open="_bundleSizeDialogOpen"&gt;</code> block (~72 lines).</li>
                <li>Replaced the trigger button with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Lumeo.Link Href="/docs/bundle-facts"&gt;</code> wrapping the existing "~400 KB core" text.</li>
                <li>Removed the now-orphan <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_bundleSizeDialogOpen</code> field.</li>
                <li>Sidebar nav <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">nav-config.json</code> gained a "Bundle Facts" entry under getting-started.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">Docs project builds clean (0 warnings, 0 errors).</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.45 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.45</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>rc.44 hotfix — two crashes surfaced in user runtime logs.</strong>
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Window: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Math.Clamp(0, -40)</code> crash on drag-during-init.</strong> The rc.44 viewport-aware drag clamp could be invoked before <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_viewportHeight</code> finished loading from JS (or in non-WASM contexts where the JS interop returns null). When zero, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Math.Clamp(_dragStartPosY + dy, 0, _viewportHeight - TitleBarHeight)</code> resolved to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Math.Clamp(value, 0, -40)</code> → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ArgumentException</code>. Fix mirrors the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ClampPosition()</code> guard: if viewport is unknown, fall back to lower-bound-only clamp (pre-rc.44 behavior).</li>
                <li><strong>LumeoForm generator: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">InvalidCastException</code> setting <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Button.type</code> on the submit button.</strong> The generator emitted <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AddAttribute("type", "submit")</code> — lowercase HTML attribute name + string value — but <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Button.Type</code> is a strongly-typed <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ButtonType</code> enum (Button/Submit/Reset). Fix: emit <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AddAttribute("Type", global::Lumeo.Button.ButtonType.Submit)</code>.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">2410/2410 tests pass · 8 projects + tests + docs build clean.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.44 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.44</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Mega-Sprint A+B+C consolidated: ~35 fixes across 8 categories, one release.</strong>
            6 parallel fix agents handled the previously deferred items (A: Window/RTL/Scheduler/Gantt; B: LumeoForm + Visual Diff). 5 parallel audit agents covered the previously un-audited dimensions (C: localization, performance, Blazor Server, mobile/touch, API consistency). 4 parallel fix agents addressed the HIGH-priority C findings. API renames were intentionally deferred to a v2.1 breaking-change release.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">A — Carry-overs from rc.43</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Window drag-clamp (viewport-aware)</strong>: new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Interop.GetViewportSize()</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ClampPosition()</code> keeps the title bar inside the viewport on all four edges (MinVisible=50px, TitleBarHeight=40px). Consumer-set <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">InitialX=9999</code> is now clamped on first render.</li>
                <li><strong>RTL ArrowKey-swap</strong>: Carousel, Stepper, Tabs, Splitter now respect <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ThemeService.CurrentDirection == LayoutDirection.Rtl</code> and swap ArrowLeft↔ArrowRight for horizontal navigation.</li>
                <li><strong>Scheduler</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo:theme-changed</code> listener re-renders active calendars; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnInitError</code> callback surfaces FullCalendar bundle-load + Calendar-constructor failures.</li>
                <li><strong>Gantt</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnInitError</code> callback added (matches Chart/Scheduler pattern).</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">B — LumeoForm generator design + Visual Diff</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>LMF001</strong>: read-only / init-only properties now emit a Roslyn diagnostic explaining they're excluded.</li>
                <li><strong>LMF002</strong>: unsupported types (collections, custom refs) emit a diagnostic suggesting <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[LumeoFormIgnore]</code> or TagInput override.</li>
                <li>New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[LumeoFormIgnore]</code> attribute + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[NotMapped]</code> recognition suppress diagnostics on properties that should be excluded silently.</li>
                <li><strong>Visual Diff</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">HomePageVisualTests</code> now uses ImageSharp perceptual compare (0.5% tolerance, Manhattan RGBA &gt; 30). CI skip removed — test runs everywhere.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">C-Audit Findings + Fixes</Heading>
            <Heading Level="4" Size="sm" Class="font-medium mt-2">Localization (7 HIGH)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Delta</strong> stops hardcoding <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">InvariantCulture</code> — German users now see <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">+12,5 %</code>. KPI cards across all dashboards inherit.</li>
                <li><strong>DateRangePicker.Format</strong> default <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">"MMM dd, yyyy"</code> → null, resolves to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CurrentCulture.ShortDatePattern</code>.</li>
                <li><strong>TimePicker.Use24Hour</strong> bool → bool? → null derives from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CurrentCulture.ShortTimePattern</code>. Hardcoded "AM"/"PM" → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AMDesignator/PMDesignator</code>.</li>
                <li><strong>DataGrid plurals</strong> (RU/PL/AR-aware): new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LumeoPlurals.PluralKey(count, baseKey, culture)</code> helper — returns <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.One/.Few/.Many/.Other</code> based on language rules. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGrid.ItemsCount</code> uses it. EN/DE/PL/RU/AR translations supplied.</li>
                <li><strong>Stepper.* and Window.*</strong> translations added to all 13 non-English locale dicts — half the strings were silently falling back to English.</li>
                <li><strong>DateTimePicker</strong> now forwards <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">FirstDayOfWeek</code> to inner Calendar.</li>
                <li><strong>Hardcoded English aria-labels</strong> in NumberInput, Input, Window, Slider, FileManager, FileUpload, DateTimePicker routed through <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ILumeoLocalizer</code>. Keys added to all 14 locales.</li>
            </ul>

            <Heading Level="4" Size="sm" Class="font-medium mt-2">Performance (5 HIGH)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>DataGridColumn.GetValue</strong> now uses compiled <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Expression&lt;Func&lt;TItem,object?&gt;&gt;</code> getters cached per field. ~10× faster than the old <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PropertyInfo.GetValue</code>. Supports nested paths (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Address.City</code>) with null-safe chain.</li>
                <li><strong>DataGridRow + DataGridCell CssClass memoization</strong>: tuple-keyed cache. 1k rows × 10 cols = 10k+ string allocations per render → ~1 per row+cell on state change.</li>
                <li><strong>Context.VisibleColumns</strong> exposed on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridContext&lt;TItem&gt;</code> — eliminates per-row <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Columns.Where(c =&gt; c.Visible)</code> LINQ.</li>
                <li><strong>ComboboxContent.FilteredItems</strong>: property → memoized backing field invalidated on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Items</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SearchText</code> change. 10k-item dropdowns no longer re-filter 3× per render.</li>
                <li><strong>PivotGrid</strong> skip-rebuild gate (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_lastItemsRef</code> + dims hash) — eliminates full tree-rebuild on unrelated parent re-renders.</li>
            </ul>

            <Heading Level="4" Size="sm" Class="font-medium mt-2">Blazor Server + Touch + Disposal</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>9 components</strong> wrapped their first-render JS interop in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">try/catch (JSDisconnectedException, ObjectDisposedException)</code>: Affix, BackToTop, Carousel, Scrollspy, ResizableHandle, OtpInput, ConsentBanner, PromptInput, AgentMessageList.</li>
                <li><strong>TabsList</strong> prerender measurement guarded against <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">InvalidOperationException</code> (Blazor prerender).</li>
                <li><strong>Keyboard shortcut Map</strong> cleared on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">registerKeyboardShortcuts</code> entry — no more stale entries leaking across Blazor Server circuit reconnects.</li>
                <li><strong>P0 mobile fix: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">registerClickOutside</code> now binds <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">touchstart</code></strong> (with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">{ passive: true }</code> + 600ms synthetic-mousedown dedupe). Tap-outside now closes every Dropdown, Combobox, Select, Popover, ColorPicker, ContextMenu, DatePicker, TimePicker on mobile.</li>
                <li><strong>SortableList touch reorder</strong>: new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">registerSortableTouch</code> JS helper with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">elementFromPoint</code> drop detection. Mouse path unchanged (still uses HTML5 Drag API).</li>
                <li><strong>Tooltip + HoverCard tap-to-pin</strong>: tap toggles pinned state; click-outside unpins via the now-touch-aware <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">registerClickOutside</code>.</li>
                <li><strong>NavigationMenu + MegaMenu</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@onmouseenter/leave</code> → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@onpointerenter/leave</code> with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">e.PointerType == "mouse"</code> guard. Submenus no longer close on touch liftoff before the user can tap a sub-item.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Deferred to v2.1 (breaking-change release)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>API consistency renames: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Stepper.ActiveStep → CurrentStep</code>, Multi-value collection type unification (HashSet/List/IEnumerable), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Values → SelectedValues</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SidebarProvider.IsCollapsed → Collapsed</code>, Size enum harmonization. These are breaking and need consumer migration.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">145 components · 2,410 tests pass · all 8 packages + tests + docs build clean (0 warnings, 0 errors).</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.43 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.43</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Mega-Audit sweep: ~30 fixes across A11y, RTL, theme/dark-mode, async/race, memory, forms, and third-party wrappers.</strong>
            Dispatched 7 parallel review agents over the full ~145-component surface (categories the rc.42 audit didn't cover) → 40 hypothesized bugs → 6 parallel fix agents → all green. The single release covers the substance of what would have been rc.43 through rc.47.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">A11y (12 fixes — interactive components keyboard navigation)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Carousel arrow-keys now <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">preventDefault</code></strong> — page no longer scrolls when the carousel handles ArrowLeft/Right.</li>
                <li><strong>Roving tabindex</strong> on MenubarTrigger, Rating, TreeViewNode (focused-node tracking), Stepper step buttons. Toolbar container gets <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">tabindex="0"</code> so screen readers announce "toolbar".</li>
                <li><strong>SplitterDivider</strong> now resizable via ArrowKeys (±10px delta per press, horizontal/vertical based on orientation).</li>
                <li><strong>Focus management on open/close</strong>: Mention/Cascader return focus to trigger on close; Cascader focuses first option on open.</li>
                <li><strong>ContextMenuTrigger</strong> now activates on Shift+F10 / ContextMenu key for keyboard users.</li>
                <li><strong>OtpInput Backspace</strong> stopsPropagation so it can't accidentally trigger SPA back-navigation.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">RTL (10 fixes — full right-to-left support)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Carousel</strong> (Next/Previous/Content/Item), <strong>Combobox input</strong>, <strong>DatePicker</strong> (5 hardcoded directions), <strong>Select / SelectTrigger</strong> migrated from hardcoded <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ml-/mr-/pl-/pr-/left-/right-/border-r/border-l</code> to Tailwind logical properties (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ms-/me-/ps-/pe-/start-/end-/border-s/border-e</code>).</li>
                <li><strong>Directional chevrons</strong> on Carousel, Pagination, Breadcrumb now flip in RTL via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[dir=rtl]:rotate-180</code>. Arrow-key handler RTL-swap deferred (separate concern; tagged with TODO).</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Theme + Dark Mode (5 fixes — ColorPicker is now dark-mode-correct)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>ColorPicker slider thumbs</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">background: #fff</code> → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">var(--color-background)</code>. White thumbs were invisible against light-colored hues in dark mode.</li>
                <li><strong>ColorPicker checkerboard</strong> (transparency visualization): hardcoded <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">rgba(0,0,0,0.08)</code> → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">color-mix(in srgb, var(--color-muted) 60%, var(--color-background))</code>. Visible in both modes.</li>
                <li><strong>ColorPicker SV-indicator shadow</strong> uses theme-aware <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">color-mix(... var(--color-foreground) ...)</code>. The white-on-canvas border itself is kept (correct against any swatch color).</li>
                <li><strong>ThemeSwitcher</strong>: stale-default-on-first-render fixed via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnInitializedAsync</code>. UI shows the real current scheme/mode immediately.</li>
                <li><strong>ThemeService.ToggleModeAsync</strong>: now cycles System → Dark → Light → System (was binary Light↔Dark and permanently lost System mode on first toggle). 3 tests updated to match the new cycle contract.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Async / Memory (4 fixes — leak elimination)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>FileUpload</strong> now implements <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IAsyncDisposable</code> — disposes all in-flight <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CancellationTokenSource</code>s in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_cancellations</code> on unmount. CRITICAL: every upload running at page-navigate previously leaked a CTS.</li>
                <li><strong>FileUpload batch staleness</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_currentBatchId</code> counter — old slow batches can no longer fire <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnAllUploaded</code> after a newer batch has started.</li>
                <li><strong>TreeSelect.OpenDropdown</strong>: post-await disposal guards. Setting <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_registered=true</code> after a disposal-during-Interop is now impossible.</li>
                <li><strong>Mention.HandleBlur</strong>: 150ms <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Task.Delay</code> now uses a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CancellationTokenSource</code> — re-focusing inside the window cancels the pending hide.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Heavy Wrappers (RichTextEditor — 2 real fixes)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Silent init failure surfaced</strong>: JS wraps <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">new Editor()</code> + TipTap bundle load in try/catch with descriptive errors. C# catches <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">JSException</code>, sets <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_initError</code>, renders an error card, and invokes new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnInitError</code> callback. Matches the pattern Chart.razor already used.</li>
                <li><strong>Orphan suggestion DOM cleanup</strong>: suggestion popups are now tagged with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">data-rte-id</code>; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">destroy(id)</code> hunts them down explicitly. No more dangling popups in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">document.body</code> after unmount-mid-suggestion.</li>
                <li><strong>FALSE ALARM verified</strong>: theme-unaware inline styles. The audit thought <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">style.cssText</code> was baked at init — but the strings use <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">var(--color-*)</code> references, which browsers re-resolve on every paint. Already theme-aware.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Forms + Source Generator (4 fixes)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>LumeoForm generator</strong>: non-null enum no longer emits <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">model.Prop?.ToString()</code> (CS0023). Nullable-aware emit handles both <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">MyEnum</code> and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">MyEnum?</code> correctly.</li>
                <li><strong>LumeoForm nullable-enum clear</strong>: when user clears the Select, the property goes to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">null</code> for nullable enums. Was: silently kept the old value.</li>
                <li><strong>LumeoForm DateTimeOffset</strong>: invalid <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">(DateTime?)</code> cast replaced with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.DateTime</code> accessor; original <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.Offset</code> is preserved through the round-trip. Was: timezone silently stripped.</li>
                <li><strong>FormField</strong> now reads errors from a cascaded <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">FormContext</code> parameter — Form.Reset() now actually clears children's visible error state. Was: child kept showing the old error.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">False alarms cleared (11)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>DropdownMenuItem</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">tabindex="-1"</code> + parent-driven <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">FocusMenuItemByIndex</code> is the correct WAI-ARIA menu pattern, not a bug. <strong>ColorPicker HSV gradients</strong>: hardcoded white/black are color-space primitives, not theme colors. <strong>Charts</strong>: full theme-change listener and resize observer already in place. <strong>Gantt</strong>: CSS variables read on every render — passive theme support is intentional. <strong>Combobox/Select/Carousel/InplaceEditor/Stepper/Cascader async paths</strong>: all guarded correctly. <strong>FileUpload</strong> didn't have a leak we expected — actual leak (no CTS dispose on unmount) is the one we fixed instead.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">145 components · 2,410 tests pass (was 2,407 — +1 for new ToggleMode cycle test, ±0 for the 2 Carousel/ThemeService tests retitled to match new contracts) · all 5 satellite builds clean · 0 warnings, 0 errors.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.42 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.42</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Audit sweep: 3 verified bugs fixed, 11 hypothesized bugs cleared.</strong>
            Dispatched 4 parallel review agents over ~80 components looking for the same class of bug the DataGrid group panel had (UI promises something the implementation doesn't deliver). Found 4 real bugs — Window drag clamp is too involved to ship surgically and goes to rc.43; the other 3 are in this release. Plus a regression test suite for the rc.41 group-panel fix.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Toolbar overflow was completely broken at runtime.</strong> <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Toolbar.razor</code> called <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">observeToolbarOverflow</code> but <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">toolbar.js</code> only exported <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">observeToolbarWidth</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">measureToolbarChildren</code> — the function the component depended on didn't exist. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Overflow="true" VisibleCount="-1"</code> users got zero auto-collapse. Implemented the missing function (full ResizeObserver + child-width measurement + fitting-count computation in JS), switched C# to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">InvokeVoidAsync</code> with a stable per-component DOM id.</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToastOptions.Position</code> (rc.37 API) was silently ignored.</strong> The per-toast position override existed in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToastService</code> but <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToastProvider.razor</code> rendered every toast into a single <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;ToastViewport Position="Position"&gt;</code> — the provider's default. Now toasts are grouped by their effective position (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">options.Position ?? defaultPosition</code>) and rendered into one viewport per group. Multiple <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">position:fixed</code> viewports coexist without overlap.</li>
                <li><strong>TreeView lazy-loaded children inherit parent's check state (bonus).</strong> When <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LoadChildren</code> resolves, freshly-loaded children now inherit a fully-checked parent's state. Previously a checked parent would expand to reveal unchecked children — a visibly broken tri-state. Indeterminate parents recompute correctly when newly-loaded children land in their default state.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Tests</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridGroupPanelTests.cs</code> — 8 bUnit tests covering the rc.41 group-panel fixes: panel render, Groupable-only dropdown filtering, Add/Remove/Clear behavior, and the specific "add after clear still works" regression that the user reported.</li>
                <li>All 8 pass green; one assertion was tightened from a panel-wide string match (which falsely included the "+ Add level" dropdown options) to a chip-span selector.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">False alarms (audit findings that turned out to be FINE)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Sortable cross-container drag has <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnDragEnd</code> as a state-reset belt regardless of drop. TreeView MultiSelect cascade is correct (tri-state computed via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">UpdateAncestorStates</code>). DataGrid virtualization is bypassed when grouping is active (no row-skip risk). Column-reorder during grouping is safe — fields are stored as strings, not indices. Combobox/Select filter clears on close. Tabs uses value-based active state, no index drift. Stepper's <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">HandleStepClick</code> guards via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CanClickStep</code>. Splitter uses pointer events (touch works). Sheet/Drawer scroll-lock is refcounted. DateRangePicker provides visual feedback for inverted ranges. InplaceEditor's Esc-vs-blur race is synchronously guarded. PivotGrid handles empty data without NaN.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Deferred</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Window component drag can push the window below the viewport (no upper-bound clamp on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_posY</code>). Clean fix needs viewport-aware JS interop — queued for rc.43. Workaround: don't drag the window far enough off-screen to lose it.</li>
                <li>Splitter divider is keyboard-focusable but ArrowKey resize is missing (a11y gap). Same sprint.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">145 components · all 8 new DataGridGroupPanelTests pass · core + tests + docs build clean (0 warnings, 0 errors).</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.41 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.41</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>DataGrid group panel bug-fix sweep.</strong>
            User-reported: drag-to-group didn't work even though the panel placeholder said "Drag a column header here", re-grouping after a clear felt broken, and the panel UX wasn't polished. All three fixed.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Drag-to-group now actually works.</strong> Column headers with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Groupable="true"</code> are HTML5-draggable whenever <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowGroupPanel="true"</code> on the grid — independent of <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Reorderable</code>. The panel now has full <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@ondragover</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@ondrop</code> handlers and lights up <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">border-primary bg-primary/10</code> when a valid Groupable column is dragged over it. Drops on non-Groupable columns (or columns already grouped) are rejected silently.</li>
                <li><strong>Re-grouping after clearing all levels works again.</strong> The "+ Add group level" <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;select&gt;</code>'s native DOM <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">value</code> was sticking on the last-picked option, so after the second add the placeholder ("+ Add group level") wouldn't re-render and the dropdown felt frozen. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@key</code> on the select now forces a fresh DOM element each time the group-fields count changes.</li>
                <li><strong>Added a "Clear" button</strong> next to the panel chips for one-click reset to ungrouped. Cleaner than chip-by-chip removal.</li>
                <li><strong>DataGridContext gained <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowGroupPanel: bool</code></strong> so header cells can decide on their own whether to be drag-source-eligible without extra plumbing.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">No API break</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>The <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridContext&lt;TItem&gt;</code> record gained one new positional field — only code that <em>constructs</em> the context manually would break, and the only such caller is <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGrid.razor</code> itself (consumer Razor pages cascade-read it).</li>
                <li>Existing single-level <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GroupBy="..."</code> demos and multi-level <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GroupByFields=@@(...)</code> usage are unchanged. The interactive demo on the DataGrid page now genuinely shows the panel feature.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">145 components · DataGrid + docs project build clean (0 warnings, 0 errors).</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.40 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.40</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Lumeo.nupkg cut in half — CLI source registry moved out of the package.</strong>
            The 146 shadcn-style per-component snippet JSONs that were shipping inside every <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo</code> NuGet install (~3.9 MB raw, ~700 KB compressed) were pure dev-time payload — the CLI fetches them over HTTPS from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo.nativ.sh/registry/</code> at <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo add</code> time. End-user browsers never touched them.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Result</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Lumeo.nupkg: 1,405,580 B → 672,193 B (−52%, −733 KB).</strong> Back below the rc.20 baseline. NuGet UI now displays a sensible package size that matches the actual browser footprint story instead of three times it.</li>
                <li><strong>Browser-shipped bytes unchanged.</strong> Brotli-compressed <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.dll</code> is still ~400 KB — nothing about the runtime payload moved. The 733 KB savings come entirely from dev-time-only assets.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">What changed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">src/Lumeo/wwwroot/registry/</code> deleted</strong> (146 JSON files). RegistryGen now writes per-component snippets to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">docs/Lumeo.Docs/wwwroot/registry/</code> only — that's the canonical CDN source consumed by <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo add</code> + MCP via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">https://lumeo.nativ.sh/registry/&lt;slug&gt;.json</code>.</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.csproj</code>:</strong> removed the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SyncRegistryIntoWwwroot</code> MSBuild target and added a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;None Include="registry\registry.json" Pack="false" /&gt;</code> so the master index stays in source control but never gets packed.</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">deploy-cloudflare.yml</code>:</strong> the per-component-registry copy step now reads from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">docs/Lumeo.Docs/wwwroot/registry/</code> instead of the now-deleted <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">src/Lumeo/wwwroot/registry/</code>. (Mostly a no-op because the SDK already publishes that folder, but kept as a safety net.)</li>
                <li><strong>Registry doc page:</strong> the "Raw JSON" link now points at <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/registry.json</code> (Cloudflare-served from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">docs/Lumeo.Docs/wwwroot/registry.json</code>) instead of the now-dead <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/_content/Lumeo/registry/registry.json</code> URL.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Consumer impact</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>None.</strong> Runtime apps don't reference the registry JSONs (their <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">registryUrl</code> already pointed at the CDN, not <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/_content/Lumeo/registry/</code>). The Lumeo.CLI's default URL was already <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">https://lumeo.nativ.sh/registry.json</code> — no behavior change.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">145 components · 9 NuGet packages + 1 npm package published.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.39 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.39</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Doc-coverage audit + bundle-stats honest rewrite.</strong>
            A second-pass audit of all 145 component pages turned up gaps the rc.37 sweep missed —
            features documented in API tables but never actually shown in a live demo. 23 new
            interactive previews fill those gaps. Plus the homepage bundle-size story is
            now measured in <em>browser-shipped brotli bytes</em>, not <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.nupkg</code> sizes that include CLI-only assets.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Docs site — closing the demo coverage gaps</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>DataGrid <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowGroupPanel</code> interactive demo.</strong> The group-panel + multi-level grouping features had docs prose + an API table but no live preview. New demo with Segmented toggles for panel on/off and single-vs-multi-level grouping (Department → Status with per-group salary average).</li>
                <li><strong>Animation & visual sliders (5 pages):</strong> AnimatedBeam (Curvature, DurationMs), BlurFade (DelayMs, BlurPx + Replay), Barcode (Height, BarWidth), BackToTop (VisibilityThreshold), Affix (anchor + Offset).</li>
                <li><strong>Input controls (5 pages):</strong> Pagination size, OtpInput Length (4/6/8), PasswordInput ShowStrength + ShowToggle, NumberInput Step + Precision, Mention Trigger character.</li>
                <li><strong>Variants & modes (4 pages):</strong> PopConfirm IsDestructive, ToggleGroup PressEffect (None/Scale/Brightness/Ripple), Toggle icon+text slot toggles, ShimmerButton size — single live preview not side-by-side.</li>
                <li><strong>Display + event-feedback (7 pages):</strong> Textarea ShowCount counter, TextReveal Threshold scroll-trigger, Timeline ActiveIndexChanged binding, TreeView OnItemClick, Transfer OnChange, AgentMessageList Role switcher, Tooltip Side demo refactored to compound <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;SegmentedItem&gt;</code> pattern.</li>
                <li><strong>Two audit findings sauber geskippt:</strong> Pagination has no <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Size</code> enum, Menubar has no <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Orientation</code> parameter — fix-agents refused to invent the API. Pagination size demo done via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PaginationItem Class=</code> sizing instead.</li>
            </ul>

            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Home — honest bundle stats</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>"~530 KB core" → "~400 KB core" everywhere on the homepage</strong>, with full disclosure: this is the <em>brotli-compressed assembly size</em>, which is what the browser actually downloads — what every other Blazor lib means when it quotes a number. Measured 2026-05-13 on rc.38: Lumeo.dll brotli ≈ 403 KB, Charts 120 KB, DataGrid 100 KB, Editor 26 KB, Motion 18 KB, Scheduler 16 KB, Gantt 12 KB.</li>
                <li><strong>Why the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.nupkg</code> shows ~1.35 MB:</strong> the package bundles ~4 MB of shadcn-style component <em>source registry</em> for the Lumeo CLI (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">staticwebassets/registry/*.json</code>) plus the pre-compiled Tailwind utility snapshot. None of that is sent to end users' browsers — disclosed in the bundle-size dialog and in the bento footer footnote. (Moving the registry to CDN-only is queued for rc.40 — would bring the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.nupkg</code> back under 600 KB.)</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">145 components · all 4 audit waves built clean (0 warnings, 0 errors) · brotli measurements: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">brotli -q 11</code> of each package's runtime DLL.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.38 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.38</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>CI fixes — the rc.37 push went through but the Cloudflare / CI / E2E workflows went red.</strong>
            Two unrelated issues, both fixed here.
        </Lumeo.Text>

        <Stack Gap="3">
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SegmentedItem</code> compound sub-component.</strong>
                    The rc.37 doc-coverage sweep used <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;SegmentedItem Value="..."&gt;</code> children inside <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Segmented&gt;</code> on ~10 interactive-preview demo pages — that's the shadcn-style composition the rest of the library uses
                    (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SelectItem</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">TabsTrigger</code>, …) and the natural reach. But <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Segmented</code> only had the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Options="List&lt;SegmentedOption&gt;"</code> data-driven API — so docs built locally but CI's <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">-warnaserror</code> failed with RZ10012 "unexpected element 'SegmentedItem'". Added the compound sub-component (cascading <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SegmentedContext</code>, styling identical to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Options</code>-mode). Both APIs now coexist; consumers can pick whichever reads better.</li>
                <li><strong>Changelog.razor: escaped a literal <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@page</code> reference</strong> in the rc.37 entry that was being parsed as a Razor directive — broke the docs build for CI / E2E / Cloudflare. Now <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@@@page</code> in source → renders as <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@page</code> in the page.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">145 components (+1 sub-component) · 2,401 unit + 31 RegistryGen + 20 docs tests green; all <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">-warnaserror</code> satellite builds clean; docs project builds clean.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.37 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.37</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Doc-coverage sweep — every component now has a live demo, a complete API table, and interactive previews for multi-value enum parameters.</strong>
            Plus a small API addition (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToastOptions.Position</code>) and a CI hygiene fix.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Docs site — interactive previews everywhere</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Every <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[Parameter]</code> typed as an enum with ≥2 values now has a <strong>"Variant (interactive)"</strong> section on its component's doc page — a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Segmented&gt;</code> (or <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Slider&gt;</code> for numeric tunables) bound to a state field driving a live component, so readers can flip between values and see the difference in real time.</li>
                <li>~50 new interactive previews across Forms (Button Variant/Size, DatePicker Mode, FileUpload Variant, Rating Size, Toggle/ToggleGroup, …), Navigation/Overlay (Tabs Variant/Orientation/RenderMode, Sheet/Drawer Side, Stepper Orientation, Carousel Orientation, BottomNav Variant, MegaMenu, Splitter, Sidebar, AppBar, Popover/Tooltip Side+Align), Data Display (Steps Orientation, Statistic Size/Variant, Sparkline Type, SparkCard, Timeline, Gauge Variant, Avatar Shape/Status, Badge Variant, Bento Gap, Chip Variant, ImageCompare, QRCode, ReasoningDisplay, ToolCallCard), Motion (Marquee Speed/Direction, BorderBeam Size+Duration sliders, ShimmerButton, Dock MaxScale/MagnifyRadius, NumberTicker target, Sparkles count, TextReveal speed), Feedback (Alert Variant, Result Status, Toast Variant+Position).</li>
                <li><strong>API tables filled in</strong>: ~18 Forms pages were missing FormField integration rows (Required / Invalid / ErrorText / HelperText / Label / Name) — added. AnimatedBeam <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PathWidth</code> and SortableList <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Key</code> rows added.</li>
                <li>Pages that were entirely missing got created from scratch — every one of the 145 components now resolves at <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/components/&lt;kebab-name&gt;</code>.</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">API additions</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToastOptions.Position</code> (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToastPosition?</code>) — per-toast position override, complementing the existing <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToastViewport.Position</code> default. Powers the new ToastPage interactive position demo.</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Hygiene</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Stripped UTF-8 BOMs from three Razor source files (DatePicker / DateRangePicker / DateTimePicker) — they were breaking the sitemap generator's <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">grep '^@@page'</code> on the Cloudflare deploy.</li>
                <li>SplitterPage interactive demo: fixed a mixed C#+literal <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Class</code>-attribute syntax error (RZ9986). FileManagerPage <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">RemoveNode</code> nullable-warning fix (CS8604 — would have failed an eventual docs <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">-warnaserror</code>).</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">145 components · 2,401 unit + 31 RegistryGen + 20 docs tests green; all <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">-warnaserror</code> satellite builds clean; docs project builds clean.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.36 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.36</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>DataGrid batch-edit UI, PivotGrid multi-level columns, Toolbar auto-overflow, 13-locale satellite sweep.</strong>
            Wave 6 of the deep-scan roadmap. All additive; no breaking changes.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">DataGrid — batch / buffered edit mode</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Setting <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">EditMode="DataGridEditMode.Batch"</code> now wires up the full UI (the rc.35 scaffolding got fleshed out): edits go into a per-cell pending-changes buffer, dirty cells get a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">bg-warning/10</code> tint and warning-coloured left border, a "<strong>N pending changes</strong>" strip appears with <strong>Save all</strong> + <strong>Discard</strong> buttons.</li>
                <li><strong>Save all</strong> fires <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnBatchSave</code> (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridBatchSaveEventArgs&lt;TItem&gt;</code> with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Modified</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Added</code> lists). On clean return the buffer clears automatically.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowAddRow="true"</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">NewItemFactory</code> render a "+ Add row" trigger. Public <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">HasPendingChanges</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PendingModifiedItems</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PendingAddedItems</code> for warn-on-navigate.</li>
                <li>New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ColumnVirtualize="true"</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">MaxVisibleColumns</code> (default 30) — caps how many columns are rendered for very-wide grids. (Real per-cell horizontal virtualization is a follow-up; this is the simpler "max-columns cap" approximation. Clipboard paste / fill-down / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShouldRender</code> on cells are also deferred.)</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">PivotGrid — multi-level column hierarchy</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ColumnFields</code> now nests properly. A two-level <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[Year, Quarter]</code> example renders three header rows (Year — colspan'd across its quarters / Quarter — colspan'd across measures / Measure names), with correct rowspans on row-field corners and the grand-total column. Column-subtotals are deferred (row subtotals work).</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Toolbar — automatic overflow measurement</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Toolbar Overflow="true"&gt;</code> with the default <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">VisibleCount=-1</code> now auto-measures via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ResizeObserver</code> — items that don't fit collapse into the "⋯" dropdown live as the container resizes. Manual <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">VisibleCount</code> still wins when set.</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Localization — satellites sweep</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>71 new locale keys × 14 dictionaries = 994 entries.</strong> <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Chart</code> (loading text), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Scheduler</code> (Today + view-switcher + "Week of {0}"), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Gantt</code> (view-mode labels), and <strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">RichTextEditor</code></strong> (toolbar + bubble menu + slash-command + AI-action menu — Bold / Italic / Heading 1–3 / Bullet+Numbered+Task list / Quote / Code block / Link / Insert image/table / Undo / Redo / Clear formatting / Improve writing / Make shorter / Make longer / Translate / Summarize / Fix grammar / placeholders / …) all now route through <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ILumeoLocalizer</code> with translations in de / fr / es / it / pt / nl / pl / ja / zh / ko / ar / ru / tr.</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">CI</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">publish-mcp</code> now succeeds when the version is already on npm (typical on a release re-run after a sibling job failed) — checks for "cannot publish over" / "previously published" in npm's error and exits 0 instead of red. Any other publish failure still fails the job.</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">Docs site refreshed: batch-edit demo on DataGrid, multi-level-columns demo on PivotGrid, auto-overflow demo on Toolbar. Stale stats fixed (145 components · 2,400+ tests · rc.36). 145 components · 2,401 unit + 31 RegistryGen + 20 docs tests green; all -warnaserror satellite builds clean.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.35 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.35</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>DataGrid tree-grid + group panel, and six new components.</strong>
            Wave 5 of the deep-scan roadmap. Mostly additive.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">DataGrid</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Tree-grid mode</strong> — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ChildItemsSelector</code> turns the grid into a hierarchy: rows with children get an expand chevron, indented child rows, recursive. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">TreeGridDefaultExpanded</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">TreeColumnField</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnTreeNodeExpand</code>; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">role="treegrid"</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">aria-expanded</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">aria-level</code>. Distinct from the flat <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DetailTemplate</code>.</li>
                <li><strong>Group panel + group-level aggregates + multi-level grouping</strong> — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowGroupPanel</code> renders a chip bar; columns with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Groupable</code> can be added/removed/reordered there; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GroupByFields</code> for multi-level nested grouping; each group row shows the column <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Aggregate</code>s over that group.</li>
                <li>Scaffolding for batch-edit mode landed (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridEditMode.Batch</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridBatchSaveEventArgs&lt;TItem&gt;</code>) — full UI wiring follows in the next release. Plus a fix for an <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AriaLevel</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AriaExpanded</code> regression from rc.34.</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">New components</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Gauge</code></strong> — single-value gauge, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Radial</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Arc</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Linear</code> variants, threshold colour bands, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">role="meter"</code>.</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">RingProgress</code></strong> — circular determinate progress ring with an optional centre label or custom content.</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Barcode</code></strong> — inline SVG <em>Code 128B</em> barcode renderer (scannable), with optional human-readable text.</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Highlighter</code></strong> — wraps occurrences of one or more search terms in the text with highlight marks (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CaseSensitive</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">WholeWord</code>).</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">FileManager</code></strong> — headless file/folder explorer: folder tree, breadcrumb path, list/grid views, lazy loading (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LoadChildren</code>), inline rename, context-menu operations (rename/delete/open). Drag-drop / upload / multi-select are deferred.</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">QueryBuilder</code></strong> — visual AND/OR predicate-tree builder: recursive groups, per-type value editors, serializes to JSON and to a LINQ predicate (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToExpression&lt;T&gt;</code>), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowJsonPreview</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">MaxDepth</code>.</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Toolbar</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Overflow</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">VisibleCount</code> — collapse trailing items into a "⋯" dropdown. (Auto-measurement of which items fit is a follow-up; for now <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">VisibleCount</code> is consumer-controlled.)</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">Docs site updated: new pages for Gauge / RingProgress / Barcode / Highlighter / FileManager / QueryBuilder; API tables + demos refreshed on DataGrid / Toolbar. 145 components · 2,375 unit + 31 RegistryGen + 20 docs tests green.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.34 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.34</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>The big one — DataGrid keyboard navigation, a new PivotGrid, Scheduler/Gantt/FileUpload feature waves, Select/Combobox data-bound mode, and a localization sweep.</strong>
            Wave 4 of the deep-scan roadmap. Mostly additive; the DataGrid changes add ARIA attributes but don't alter behaviour.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">DataGrid — keyboard navigation + grid ARIA</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Roving-tabindex cell navigation</strong>: arrow keys move the focused cell, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Home</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">End</code> = row ends, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Ctrl+Home</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Ctrl+End</code> = corners, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PageUp</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PageDown</code> = ±10 rows, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ArrowUp</code> from row 0 jumps into the header. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Enter</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">F2</code> on an editable cell starts cell-edit; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Enter</code> while editing commits + moves down, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Tab</code> commits + moves right, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Escape</code> cancels. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Space</code> toggles row selection; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Ctrl+A</code> selects all (multi-select). <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Enter</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Space</code> on a sortable header cell sorts.</li>
                <li><strong>Grid ARIA</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">role="grid"</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">aria-rowcount</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">colcount</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">multiselectable</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">role="row"</code>+<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">aria-rowindex</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">selected</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">role="columnheader"</code>+<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">aria-sort</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">role="gridcell"</code>+<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">aria-colindex</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">readonly</code>. Focus ring on the active cell.</li>
                <li>Deferred: keyboard nav inside grouped/virtualised regions (cells still carry the ARIA roles there).</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">New component — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PivotGrid&lt;TItem&gt;</code></Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Cross-tab / pivot table: pivots flat data into a row hierarchy × column values × aggregated <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Measures</code> (Sum/Average/Count/Min/Max). <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">RowFields</code> nest into a multi-level outline; per-group subtotal rows + a column grand-total row + a row grand-total column + the overall total. Collapsible row groups (chevron + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">aria-expanded</code>), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnCellClick</code> for drill-down, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">EmptyContent</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Compact</code>. Single column-field level for now (row hierarchy is fully nested).</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Scheduler</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Recurring events via FullCalendar's free recurrence model — new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SchedulerEvent.DaysOfWeek</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">RecurrenceEnd</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ExceptionDates</code>.</li>
                <li>Resource colour-coding: new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SchedulerResource</code> record + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Resources</code> param + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SchedulerEvent.ResourceId</code> — events inherit their resource's colour, with a legend above the calendar. (True resource-lane / timeline views need FullCalendar's premium plugin, which we don't bundle.)</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">NowIndicator</code> (default true), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SlotMinTime</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SlotMaxTime</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SlotDuration</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">EventClassNames</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">EventColor</code> callbacks.</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Gantt</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Milestones — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GanttTask.IsMilestone</code> renders a diamond instead of a bar. Grouping/swimlanes — new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GroupBy</code> param + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GanttTask.GroupLabel</code> (sort + group-separator rows). <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">TodayHighlight</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BarHeight</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ColumnWidth</code> config, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BarColor</code> per-task colour callback. (Task dependencies were already supported; rendered as arrows.)</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">FileUpload — a real upload pipeline</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnUpload</code> callback (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Func&lt;FileUploadItem, IProgress&lt;int&gt;, CancellationToken, Task&lt;string?&gt;&gt;</code>) — the component drives per-file state (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">FileUploadItem</code> with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Status</code> Pending/Uploading/Succeeded/Failed/Cancelled, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ProgressPercent</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Url</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ErrorMessage</code>) with per-file progress bars, cancel + retry buttons, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AutoUpload</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">MaxConcurrentUploads</code>, validation rejections surfaced as failed items (+ <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnFileRejected</code>), and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnFileUploaded</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnAllUploaded</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnFileFailed</code> events. Legacy <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Progress</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnFilesSelected</code> still work.</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Select / Combobox — data-bound mode (non-breaking)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Items</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ItemValue</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ItemText</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ItemGroup</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ItemDisabled</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ItemTemplate</code> params — bind options from a collection instead of writing <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;SelectItem&gt;</code>s by hand. Plus <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Virtualize</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ItemSize</code> for large option lists. Components stay non-generic (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">object</code>-typed selectors) — composition mode unchanged, zero breaking changes.</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Localization</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Kanban "Add card" and Carousel a11y labels ("Previous slide" / "Next slide" / "Slide N of M") now route through <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ILumeoLocalizer</code> with translations in all 13 locales. (Audit found EmptyState/Result/Steps/Mention/AlertDialog have no hardcoded English to localise — their text is consumer-supplied or icon-only.)</li>
            </ul>
        </Stack>
        <Lumeo.Text Size="sm" Color="muted">Docs site updated: new PivotGrid page; API tables + demos refreshed on DataGrid / Scheduler / Gantt / FileUpload / Select / Combobox. 139 components · 2,303 unit + 31 RegistryGen + 20 docs tests green.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.33 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.33</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Four new components — Stepper, Window, Toolbar, AppBar.</strong>
            Purely additive; no breaking changes.
        </Lumeo.Text>

        <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
            <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Stepper</code></strong> — a <em>stateful</em> multi-step flow (distinct from the display-only <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Steps</code>): step header indicators, Back/Next/Finish navigation, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Linear</code> gating with per-step <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsValid</code>, horizontal/vertical orientation, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnFinish</code>, optional steps, custom step icons, tab-like ARIA. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;StepperStep&gt;</code> sub-component.</li>
            <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Window</code></strong> — a <em>non-modal</em>, draggable, resizable floating panel (vs. the modal <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Dialog</code>): draggable title bar, resize handle, minimize/maximize/close, controlled <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Open</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Minimized</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Maximized</code>, initial size/position. Drag/resize use Blazor pointer events — no JS interop. The page stays interactive (no backdrop).</li>
            <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Toolbar</code></strong> — a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">role="toolbar"</code> layout container for buttons/inputs, with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToolbarSeparator</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToolbarSpacer</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToolbarGroup</code> sub-components. (Overflow-collapse menu is a follow-up.)</li>
            <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AppBar</code></strong> — a top application bar with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">StartContent</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ChildContent</code> (centre) / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">EndContent</code> slots, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Sticky</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Elevated</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Bordered</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Height</code>. Pairs with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Sidebar</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BottomNav</code>.</li>
        </ul>
        <Lumeo.Text Size="sm" Color="muted">Docs site updated with pages for all four. 138 components · 2,259 unit + 31 RegistryGen tests green.</Lumeo.Text>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.32 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.32</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Three new components + DatePicker / DateTimePicker / Toast / TreeView upgrades + string-to-enum API tightening.</strong>
            Wave 2 of the deep-scan roadmap. A few string parameters became real enums (mild breaking change in an RC — compile-time safety + autocomplete).
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">New components</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ButtonGroup</code></strong> — joins <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Button&gt;</code>s into a segmented bar with collapsed borders and rounded ends; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Orientation</code> Horizontal/Vertical.</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DropdownButton</code></strong> — a button whose whole click opens a dropdown menu of secondary actions (chevron indicator).</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SplitButton</code></strong> — split into a primary-action half (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnClick</code>) and a chevron half that opens the dropdown.</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Component upgrades</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>DatePicker</strong>: forwards <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">FirstDayOfWeek</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowWeekNumbers</code> to the inner Calendar; new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Inline</code> mode (always-open calendar, no popover).</li>
                <li><strong>DateTimePicker</strong>: new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">MinDate</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">MaxDate</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsDateDisabled</code> (forwarded to its Calendar).</li>
                <li><strong>Toast</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToastViewport.Position</code> (6 corners/centres) + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">MaxVisible</code>; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToastOptions.Position</code> for per-toast overrides.</li>
                <li><strong>TreeView</strong>: big overhaul — lazy/async children (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LoadChildren</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsLeaf</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsLoading</code>), tri-state checkboxes with parent↔child cascade (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CascadeCheck</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsIndeterminate</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CheckedValuesChanged</code>), custom <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">NodeTemplate</code>, disabled nodes (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsDisabled</code>), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ExpandAll</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ExpandAllNodes()</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CollapseAllNodes()</code>, multi-select (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">MultiSelect</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SelectedValues</code>), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">role="tree"</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">treeitem</code> ARIA + arrow-key navigation.</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">API tightening — string parameters became enums</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DatePicker.Mode</code>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">string</code> → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DatePicker.DatePickerMode</code> (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Single</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Range</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Month</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Year</code>).</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Tabs.Variant</code>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">string</code> → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Tabs.TabsVariant</code> (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Default</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Card</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Pill</code>).</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">FileUpload.Variant</code>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">string</code> → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">FileUpload.FileUploadVariant</code> (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Dropzone</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Button</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Avatar</code>).</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Calendar.InitialView</code>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">string</code> → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Calendar.CalendarView</code> (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Days</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Months</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Years</code>).</li>
                <li>If you pass these as string literals (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Mode="Range"</code>) update to the enum (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Mode="DatePicker.DatePickerMode.Range"</code>). <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@bind</code>-style usages are unaffected.</li>
            </ul>
            <Lumeo.Text Size="sm" Color="muted">Docs site updated: new pages for ButtonGroup / DropdownButton / SplitButton, API tables + demos refreshed on DatePicker / DateTimePicker / Toast / TreeView / Tabs / FileUpload / Calendar. 134 components, 2,226 unit + 31 RegistryGen tests green.</Lumeo.Text>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.31 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.31</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Render-tree hardening — correctness + perf across the board.</strong>
            A sweep of <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@key</code> fixes,
            a real virtualised <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataTable</code>,
            <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ForceMount</code> on Accordion/Collapsible panels,
            and cached hot-path computations. No public API removed; a few additive parameters.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Correctness fixes</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>DataGrid <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@key</code> on every non-virtualised row/group/header/cell loop</strong> — sort/filter/page/group-toggle and column reorder no longer mis-diff: rows and cell-edit components stop re-initialising, sort indicators and open filter popovers stay attached to the right column.</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OverlayProvider</code> keyed + stable references</strong> — closing one of several stacked overlays no longer makes the focus-trap / scroll-lock interop target the wrong element. The hosted-component cascade is now <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsFixed</code> so it stops re-rendering on every provider render.</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SortableList</code> keyed</strong> — drag-reorder now follows the item, not the index. New optional <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Key</code> parameter for value-type items.</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataTable.Virtualize</code> now actually virtualises</strong> — it was a no-op (rendered the full list either way). Wired to Blazor's <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Virtualize SpacerElement="tr"&gt;</code>; selection still works inside virtualised rows.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@key</code> also added to FileUpload, Mention, Transfer, PickList, ImageGallery, TreeView/TreeViewNode, TreeSelect item loops.</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">New / changed parameters</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;AccordionContent ForceMount="true"&gt;</code> and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;CollapsibleContent ForceMount="true"&gt;</code> — keep the panel in the DOM (with the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">hidden</code> attribute) when collapsed instead of unmounting, so form input / scroll / component state survives. Mirrors Tabs' rc.29 pattern. Default off, no behaviour change.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SortableList.Key</code> (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Func&lt;TItem, object&gt;?</code>).</li>
            </ul>
            <Heading Level="3" Size="sm" Class="font-semibold mt-2">Performance</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>DataGrid caches the visible-columns projection instead of re-materialising it on every render.</li>
                <li>DataTable caches <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ItemsList</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsAllSelected</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsSomeSelected</code> instead of re-scanning every render.</li>
                <li>TreeView caches the filtered tree — the recursive whole-tree deep-copy now runs only when the search text or <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Items</code> change, not on every render.</li>
                <li>Carousel only calls <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">StateHasChanged</code> from its scroll callback when the prev/next-availability flags actually flip — no more a render per scroll frame during a drag.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.30 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.30</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>MCP server overhaul — from reference lookup to authoring assistant.</strong>
            The <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@lumeo-ui/mcp-server</code>
            picks up six new tools so an LLM (Claude, Cursor, Copilot) can not only look components up but write
            correct, runnable Razor on the first try. No component-library changes — this is all tooling.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">New MCP tools</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo_validate_markup</code> —
                    pre-flight a Razor snippet: do the components exist? are parameter names real (catches hallucinated APIs)?
                    are enum values legal? are sub-components nested inside their required parent (<code>TabsContent</code> in <code>Tabs</code>, <code>DialogContent</code> in <code>Dialog</code>, …)? Returns structured errors/warnings.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo_get_install</code> —
                    everything to actually run a component: NuGet + <code>dotnet add</code>, <code>@@using</code> imports, <code>AddLumeo…()</code> DI, host-page <code>&lt;script&gt;/&lt;link&gt;</code> includes, sub-components, and gotchas (portal components needing theme classes on <code>&lt;body&gt;</code>, OverlayProvider, required params).</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo_get_example</code> —
                    the real Razor snippet(s) behind the docs-site demos, now extracted into the API schema at generation time (122/131 components covered).</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo_get_theme_tokens</code> —
                    all 58 theme tokens (colour + radius CSS variables) so the model uses <code>bg-primary</code> / <code>text-foreground</code> / <code>border-border</code>, never raw hex, never <code>dark:</code> prefixes.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo_list_patterns</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo_get_pattern</code> —
                    the 16 full-page "blocks" (dashboard, auth, chat, kanban, mail, settings, …) with their complete Razor source as a starting skeleton.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo_changelog</code> —
                    current package version + API-schema generation timestamp + changelog link.</li>
            </ul>
            <Lumeo.Text Size="sm" Color="muted">
                <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo_get_component</code> now also includes the extracted <code>examples</code> array. The registry generator gained an example/theme-token/pattern extractor that scrapes the docs pages — so the MCP stays in sync automatically on every release.
            </Lumeo.Text>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.29 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.29</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Tabs: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">RenderMode</code> for panel-state preservation.</strong>
            By default a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">TabsContent</code>
            panel is unmounted the moment you switch away — losing form input, scroll position and any component
            state. The new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">RenderMode</code>
            parameter gives you the same three knobs DevExpress exposes, with clearer names.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Modes</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Active</code> (default) —
                    only the active panel is in the DOM; switching away unmounts it. Lightest footprint, no behaviour change from before.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lazy</code> —
                    a panel renders the first time its tab becomes active, then stays mounted (hidden) so its state survives subsequent switches. DevExpress <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnDemand</code>.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Eager</code> —
                    every panel renders up-front; inactive ones are hidden. State always preserved, heaviest initial render. DevExpress <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Always</code>.</li>
                <li>Per-panel override: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;TabsContent ForceMount="true"&gt;</code> always keeps that one panel mounted regardless of the parent mode — pin a half-filled form, leave the rest light.</li>
            </ul>
            <Lumeo.Text Size="sm" Color="muted">
                Hidden panels use the standard <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">hidden</code> attribute, so they're skipped by screen readers and the keyboard tab order while still preserving DOM state.
            </Lumeo.Text>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.28 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.28</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>DataGrid full virtualisation + Calendar / DatePicker culture-awareness.</strong>
            Big DataGrid round: customisable page-size selector, correctness fix for virtualised tables,
            and the long-missing third virtualisation mode (infinite-scroll over a backend).
            Calendar now starts on Monday under de-DE/fr-FR/en-GB by default and renders day headers in
            the user's language.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">DataGrid — three virtualisation modes, all wired up</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Virtualised server mode (NEW)</strong> — set
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Virtualized="true"</code> +
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnRangeRequest</code> and the grid
                    fetches a sliding window of rows from your backend as the user scrolls (Blazor's
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Virtualize ItemsProvider&gt;</code>).
                    Pagination is hidden automatically; sort, column filter, and global search all trigger
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">RefreshDataAsync()</code> so the next
                    request reflects the new query state. Public
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">grid.RefreshVirtualizedAsync()</code>
                    for manual refetch after external mutations.</li>
                <li><strong>Virtualised client mode (correctness fix)</strong> — the in-memory virtualiser now passes
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SpacerElement="tr"</code>.
                    Without it, Blazor's default <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;div&gt;</code>
                    spacers are invalid <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;tbody&gt;</code> children
                    and the browser hoisted them out of the table, breaking layout whenever the threshold tripped.</li>
                <li><strong>New tunables</strong>:
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">VirtualItemSize</code> (row-height estimate, default 41 px) and
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">VirtualOverscanCount</code> (default 3 rows).</li>
                <li><strong>PageSizeOptions</strong> — configurable rows-per-page selector (default
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[10, 25, 50, 100]</code>);
                    pass <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">new[] { 10, 50, 100, 200, 300 }</code>
                    for larger datasets, or empty array to hide the selector entirely.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Calendar / DatePicker — culture-aware</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Calendar.FirstDayOfWeek</code> is now
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DayOfWeek?</code>.
                    Unset → derives from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CultureInfo.CurrentCulture.DateTimeFormat.FirstDayOfWeek</code>:
                    de-DE / fr-FR / en-GB → Monday-first, en-US → Sunday-first.</li>
                <li>Day headers derive from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AbbreviatedDayNames</code> trimmed to two letters:
                    en-US <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Su Mo Tu We Th Fr Sa</code>,
                    de-DE <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Mo Di Mi Do Fr Sa So</code>,
                    fr-FR <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lu ma me je ve sa di</code>.</li>
                <li>Soft API change: explicit <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">FirstDayOfWeek="DayOfWeek.Monday"</code> still works (implicit lift to nullable).</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.27 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.27</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Empirically reproduced + root-caused the popover-in-Sheet positioning bug.</strong>
            Bug #1 (slide transform-trap) and Bug #16 (DatePicker positioned relative to sheet, not
            viewport) turned out to be the SAME root cause. Verified end-to-end in Chrome 131 via
            instrumented diagnostics — toggling the sheet's <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animation</code>
            property on/off snapped the popover x from 3278 (sheet-relative) to 1742 (viewport-relative).
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">The real root cause (took 6 RCs to find)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Chrome creates a containing block for any element with an active
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">transform</code>-animating
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animation</code>
                    declaration — INDEPENDENT of the current computed
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">transform</code> value</strong>.
                    Even with computed <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">transform: none</code>
                    (overridden via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">!important</code>),
                    the active <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animation: ... slide-in-from-right</code>
                    declaration alone makes Chrome establish the sheet as a containing block for fixed-positioned descendants.
                </li>
                <li>Empirically verified: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">popover.x = 3278</code>
                    when sheet has the animation declared (sheet at x=1536 + popover style.left=1742 ≈ 3278).
                    Add <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animation: none !important</code>
                    on the sheet → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">popover.x = 1742.86</code>
                    (viewport-relative). Confirmed via instrumented Chrome 131 DevTools session.
                </li>
                <li>This is Chrome's compositor optimization — it pre-creates a layer for transform-animating
                    elements (treating them as <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">will-change: transform</code>
                    implicitly). Per CSS spec strict reading this is technically aggressive but Chrome does it.
                </li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">The fix</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">attachOverlaySlideEnd</code>
                    in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">components.js</code>
                    now sets BOTH <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animation: none !important</code>
                    AND <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">transform: none !important</code>
                    after the slide animation finishes. The animation property is what kills Chrome's
                    containing-block treatment; the transform is belt-and-suspenders.
                </li>
                <li>Bug #1 (matrix containing block) and Bug #16 (DatePicker top:-1156, popover at sheet x)
                    both resolve. Same fix.
                </li>
                <li>Slide animation visual is preserved (animation runs to completion, then properties
                    are cleared in the post-finished state).
                </li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.26 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.26</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Button propagation params + harder popover positioning guards.</strong>
            Two follow-ups from rc.25 user feedback: (1) Razor's
            <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@onclick:stopPropagation</code>
            / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@onclick:preventDefault</code>
            directives can't be applied to a Lumeo Button (case-insensitively
            collide with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnClick</code>).
            Now exposed as
            <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">StopPropagation</code> /
            <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PreventDefault</code>
            bool parameters, no more
            <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;span&gt;</code>-wrapper
            workaround needed. (2) Bug #16 (DatePicker
            <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">top: -1156px</code>)
            cause is still inconclusive — math doesn't match the reported popover height — but
            <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">positionFixed</code>
            now flushes layout before measurement and applies a universal max-height + bottom-overflow guard.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — Button propagation control</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[Parameter] public bool StopPropagation</code>
                    + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[Parameter] public bool PreventDefault</code>
                    on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Button</code>.
                    Wired to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@onclick:stopPropagation</code>
                    / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@onclick:preventDefault</code>
                    on the underlying <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;button&gt;</code>
                    element. Drop-in replacement for the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;span @@onclick:stopPropagation="true"&gt;&lt;Button…/&gt;&lt;/span&gt;</code>
                    workaround.
                </li>
                <li>Plus
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[Parameter] public ButtonType Type</code>
                    (default <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Button</code>)
                    with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Submit</code>
                    / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Reset</code>
                    options for explicit form-button intent.
                </li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved — popover positioning robustness</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Synchronous layout flush before measurement</strong>:
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">positionFixed</code>
                    now reads <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">offsetHeight</code>
                    before <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">getBoundingClientRect()</code>
                    so the browser flushes any pending layout (animation scaling, Blazor render mid-flight)
                    and we measure the popover's settled dimensions, not a stale or in-flight rect.
                </li>
                <li><strong>Universal max-height clamp</strong>: if the natural popover content exceeds the
                    viewport, a scrollable cap is applied unconditionally — not only on flip-up edge cases.
                </li>
                <li><strong>Bottom-overflow late guard</strong>: even after side-flip and other branches,
                    if the popover would render past the viewport bottom, it's pulled up to fit with
                    max-height applied.
                </li>
                <li>Combined with rc.23's negative-top guard, this makes
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">positionFixed</code>
                    a true belt-and-suspenders surface — even if the underlying root cause for the rc.21–rc.25
                    user-reported <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">top: -1156</code>
                    is still inconclusive (the math doesn't match the reported popover height — root cause
                    likely a transient layout state during animation), the popover is always visible.
                </li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.25 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.25</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Slide-trap fix #5 — Web Animations API.</strong> rc.24 had the
            <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">!important</code>
            piece right (cascade priority) but missed the second bug: the listener was attached
            via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">addEventListener('animationend', ...)</code>
            from C# <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnAfterRenderAsync</code>
            via JS interop. Even in WASM, the latency between render and listener-attach is
            enough that the 300 ms slide animation can already be
            <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">finished</code>
            before the listener exists. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">addEventListener</code>
            does NOT replay events that fired before attach, so the cleanup never ran.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">The actual fix (5th attempt)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Replaced <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">addEventListener</code>
                    with the Web Animations API.
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">el.getAnimations()</code>
                    returns running OR already-finished animations (fill-mode keeps the finished
                    ones alive in the list), and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animation.finished</code>
                    is a Promise that resolves immediately if the animation already ended, or once
                    it does. Race-condition-free regardless of when the function is called.
                </li>
                <li>After awaiting <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.finished</code>,
                    set <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">setProperty('transform', 'none', 'important')</code>
                    (rc.24's correct cascade-priority piece). Inline + !important is rank 4
                    (<em>!important author</em>) which beats animations (rank 5), so the
                    fill-mode-forwarded matrix is finally overridden.
                </li>
                <li>Defensive: if <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">getAnimations()</code>
                    returns no slide animation (Animation=Fade/None, or class already dropped),
                    set the transform anyway. Edge cases get the same protection.
                </li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">For maintainers — five-RC postmortem</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>rc.21</strong>: keyframe trick (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">99%: translateX(0); to: none</code>) — Chrome interpolates none↔translate(0) as identity under fill-mode:both. Placebo.</li>
                <li><strong>rc.22</strong>: Razor <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@onanimationend</code> handler — Blazor EventArgs lacks animationName, descendants bubbled up. Placebo.</li>
                <li><strong>rc.23</strong>: native JS listener with plain <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">style.transform = 'none'</code> — animations rank above normal author. Placebo.</li>
                <li><strong>rc.24</strong>: same listener but with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">!important</code> — cascade priority correct, but listener attaches after animation ends. Placebo.</li>
                <li><strong>rc.25</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">getAnimations() + .finished promises + setProperty important</code> — race-free AND cascade-correct. Empirically verified.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.24 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.24</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Slide-trap finally fixed (4th attempt).</strong> rc.21–rc.23 were all
            placebo: the keyframe trick (rc.21) and the inline-style approach (rc.22, rc.23)
            could never defeat animation-fill-mode forwarding because of the CSS Cascade Level 4
            priority order. Animations rank ABOVE normal author rules — including plain inline
            <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">style="transform: none"</code>.
            The fix needs <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">!important</code>
            on the inline style (rank: !important author > animations) to actually override the
            forwarded keyframe value. Now done via
            <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">setProperty('transform', 'none', 'important')</code>
            in the native animationend listener.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Why the previous attempts failed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>rc.21 keyframe trick</strong> (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">99%: translateX(0); to: none</code>):
                    Chrome resolves <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">to: none</code>
                    under fill-mode:both as identity matrix. fill-mode forwards interpolated values,
                    and the transition between <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">translateX(0)</code>
                    and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">none</code>
                    is treated as identity by all major engines. End state stuck at
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">matrix(1,0,0,1,0,0)</code>.
                </li>
                <li><strong>rc.22 Razor handler that drops the class</strong>: Blazor's
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@onanimationend</code>
                    EventArgs doesn't expose <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animationName</code>,
                    so descendant zoom-in / fade-in animations bubbled up and prematurely dropped
                    the class mid-slide. Plus the Razor → JS roundtrip latency was racy.
                </li>
                <li><strong>rc.23 native JS listener with plain inline style</strong>: filtered correctly
                    on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">slide-in-from-*</code>
                    + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">target === el</code>,
                    but used <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">el.style.transform = 'none'</code>
                    which is normal author cascade rank. Animations outrank that. Matrix stayed.
                </li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">The fix that actually works</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">el.style.setProperty('transform', 'none', 'important')</code>
                    inside the same filtered animationend listener. Inline + !important is
                    <em>!important author</em> in the cascade, which beats animations. The forwarded
                    fill-mode value is properly overridden, computed transform becomes
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">none</code>,
                    no containing block, popovers (Select / DatePicker / Combobox) inside Sheet/Drawer
                    position relative to the viewport.
                </li>
                <li>Verified mechanism: per CSS Cascade Level 4 spec, the priority order from highest
                    to lowest is — transitions, !important user-agent, !important user, <strong>!important author</strong>,
                    animations, normal author (incl. plain inline style), normal user, normal user agent.
                </li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.23 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.23</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Ironclad slide-trap + defensive popover clamp.</strong> rc.22's Razor-side
            <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@onanimationend</code>
            handler had two timing issues: (1) Blazor's <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">EventArgs</code>
            for the directive doesn't expose <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animationName</code>,
            so we couldn't filter; descendant animations (Calendar's
            <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animate-zoom-in</code>,
            Combobox fade-in) bubbled up and could trigger the cleanup mid-slide. (2) The Razor →
            JS roundtrip was racy enough that some users still observed the matrix sticking.
            Replaced with a native JS listener.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed — slide-trap (third time's the charm)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Interop.AttachOverlaySlideEnd(elementId)</code>
                    JS interop registers a native
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animationend</code>
                    listener that filters strictly on
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">slide-in-from-*</code>
                    AND on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">event.target === el</code>
                    (descendants don't trigger it), then sets inline
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">style.transform = 'none'</code>.
                    Inline-style precedence beats keyframe fill-mode forwarding — the matrix is
                    cleared, no containing block, popovers position relative to viewport.
                </li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SheetContent</code> +
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DrawerContent</code>
                    now call this on every open with
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Animation=Slide</code>.
                </li>
                <li>Removed the racy Razor-side
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@onanimationend</code>
                    handler + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_animationDone</code>
                    flag — JS interop is the sole, reliable path.
                </li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed — defensive popover top clamp</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">positionFixed</code>
                    now applies a final guard: if the computed
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">top</code>
                    ends up negative for any reason (entrance-animation scaling, stale rect, weird
                    parent flex layout), it's forced to
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">8px</code>
                    with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">max-height</code>
                    + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">overflow: auto</code>.
                    Belt-and-suspenders for the rc.21 / rc.22 user-reported
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">top: -1156</code>.
                </li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.22 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.22</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>rc.21 verification fixes.</strong> Three issues surfaced after rc.21 shipped:
            the slide-keyframes trick from rc.21 didn't actually work (Chrome resolves
            <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">to: none</code>
            to identity matrix under <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animation-fill-mode: both</code>);
            DatePicker calendar popovers can render at <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">top: -1156px</code>
            inside Sheets when the popover stretches taller than the viewport;
            <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;EditForm&gt;</code>
            wrapping a Sheet's body broke the footer-pin layout. All three fixed.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed — slide-animation transform trap (real fix this time)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>The rc.21 fix used a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">99% / to: none</code>
                    keyframe trick. That doesn't work: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animation-fill-mode: both</code>
                    keeps the element in the animation's end state, and Chrome resolves
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">transform: none</code>
                    to identity matrix when blended through animation. The element ends up with
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">matrix(1,0,0,1,0,0)</code>
                    — still a containing block, popovers still off-screen.
                </li>
                <li><strong>Real fix:</strong> SheetContent and DrawerContent now bind
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@onanimationend</code>
                    and drop the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animate-slide-in-*</code>
                    class once the animation completes. Without the class, there's no animation,
                    no fill-mode, no transform — the element returns to its static state and the
                    viewport regains its natural role as the containing block. Visually identical
                    landing; popovers (Select, DatePicker, Combobox) inside Sheets/Drawers position
                    correctly. Apps with the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animation: fade-in !important</code>
                    workaround can drop it.
                </li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed — DatePicker / popover overflow clamping</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Inside a Sheet's flex layout, the calendar popover could stretch to ~1574 px.
                    The flip-up logic in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">positionFixed</code>
                    then computed
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">top = triggerTop − contentHeight − gap</code>,
                    which went deeply negative (e.g. −1156 px) — popover invisible above the viewport.
                </li>
                <li>Now compares <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">spaceAbove</code>
                    vs <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">spaceBelow</code>
                    and picks the side with more available room, capping the popover with
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">max-height</code>
                    + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">overflow: auto</code>
                    when neither side fits the natural content height. The popover is always visible.
                </li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed — EditForm-in-Sheet footer pin</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>When consumers wrapped a Sheet's body in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;EditForm&gt;</code>,
                    the rendered <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;form&gt;</code>
                    was content-sized — SheetFooter stuck below the last field instead of pinning
                    to the bottom. New CSS rule in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo.css</code>
                    makes direct <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;form&gt;</code>
                    children of Lumeo sheet/dialog/drawer dialogs inherit the flex-column layout —
                    no consumer-side CSS workaround needed.
                </li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.21 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.21</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Production-feedback pass.</strong> Driven by a detailed real-world migration
            report — closes the slide-animation transform trap that broke popovers in Sheets/Drawers,
            generalizes TagInput, expands the Lumeo.Text color map, adds a controlled-mode override
            for popover state, and ships a new EditForm + Localization guide.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed — popover containing-block trap</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Slide keyframes now end at <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">transform: none</code></strong>
                    (was identity <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">translateX(0)</code>). Per CSS spec, any non-<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">none</code>
                    transform — including identity translates — establishes a containing block for
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">position: fixed</code>
                    descendants. The old keyframes meant Select / DatePicker / Combobox popovers inside
                    a slid-in Sheet rendered relative to the Sheet instead of the viewport, ending up
                    off-screen. The 99 % step keeps the visual landing identical; the 100 % step
                    disposes of the transform.
                </li>
                <li>Apps with the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.animate-slide-in-* { animation: fade-in !important }</code>
                    workaround can drop it.
                </li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — Sheet + Drawer Animation variant</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SheetContent.Animation</code>
                    and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DrawerContent.Animation</code>
                    accept <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Slide</code>
                    (default), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Fade</code>,
                    or <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">None</code>.
                    Slide is now safe (transform-trap fixed); Fade / None remain available for consumers
                    who want maximum predictability inside re-render-heavy parents.
                </li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — TagInput is now generic</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">TagInput&lt;TItem&gt;</code></strong>
                    with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">TItem</code>
                    defaulting to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">string</code>
                    so existing string consumers compile unchanged. Real apps now build user-pickers,
                    mention-pickers, etc. on top of structured items.
                </li>
                <li>New params: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GetTagText</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GetSuggestionText</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">TagTemplate</code> (RenderFragment&lt;TItem&gt;),
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SuggestionTemplate</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Separators[]</code> (multi-separator paste),
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ValidateInput</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ValidateInputAsync</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CreateTag</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">MaxTagsHelperText</code>.
                </li>
                <li>New User-picker demo on the TagInput docs page (avatar + name in tag chip + name/email
                    rows in suggestion popup, comma/semicolon/space separators).
                </li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — Controlled-mode popover state</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DatePicker</code>
                    gains optional <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Open?</code>
                    + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OpenChanged</code>
                    (Select / Combobox already had it). When non-null, the consumer owns popover state — survives parent
                    re-renders. Use <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@bind-Open</code>
                    inside heavily re-rendering parents (EditForm with active validation) to keep the popover open through
                    validation cycles.
                </li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — Lumeo.Text full theme-token map + small param expansions</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Lumeo.Text Color="..."/&gt;</code>
                    now accepts every theme token: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">foreground</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">muted</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">primary</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">destructive</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">success</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">warning</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">info</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">accent</code>.
                    Case-insensitive. Unknown values fall back to no class (deliberate hardening).
                </li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">FileUpload</code>
                    gains <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Size</code>
                    parameter (Sm / Default / Lg) for the Button variant.
                </li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BottomNavItem</code>
                    gains <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IconSize</code>
                    (default <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">"h-5 w-5"</code>);
                    consumer can pass <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">"h-7 w-7"</code>
                    to fit an avatar in the icon slot.
                </li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToggleGroupItem</code>
                    auto-spaces icon + label (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">gap-1.5</code>
                    on the base class) — no more manual <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Class="gap-1.5"</code>.
                </li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Docs — EditForm + Localization guides</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>New: <a class="underline" href="/docs/edit-form">EditForm + Lumeo Validation guide</a></strong>
                    explaining why Lumeo inputs are not <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">InputBase&lt;T&gt;</code>
                    derivatives, the canonical pattern with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">EditContext.GetValidationMessages</code>
                    + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Invalid</code> wiring,
                    server-side error injection, and the nullable-EventCallback lambda workaround.
                </li>
                <li><strong>New: <a class="underline" href="/docs/localization">Localization guide</a></strong>
                    documenting Blazor WASM's en-US default and how to set browser-locale at startup.
                </li>
                <li>Sheet + Drawer docs pages gained an info Alert about the rc.21 transform-trap fix and the new Animation variants.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.20 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.20</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Booking-calendar API + per-component registry endpoints + Calendar UX polish.</strong>
            Calendar gains <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DateTooltip</code>
            and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DateBadge</code>
            callbacks for hotel-style availability indicators, the month + year header is now a single
            click target with same-width views across Days/Months/Years, and every component has its own
            self-contained <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/registry/&lt;key&gt;.json</code>
            endpoint with full LLM-ready detail (catalog + API schema + source + slots + services +
            cascades + related + keyboard + tests + mdSummary).
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — Calendar booking API</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DateTooltip</code></strong>
                    (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Func&lt;DateTime, string?&gt;?</code>):
                    per-day tooltip text rendered as the day cell's <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">title</code>
                    attribute (native browser tooltip on hover). Useful for "Sold out" / "Limited availability"
                    hints in booking flows.
                </li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DateBadge</code></strong>
                    (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Func&lt;DateTime, RenderFragment?&gt;?</code>):
                    per-day indicator slot rendered absolutely positioned in the bottom-right of the day cell —
                    status dots, count badges, icons. Non-interactive (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">pointer-events-none</code>)
                    so clicks still hit the day button.
                </li>
                <li>Both callbacks plus the existing <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsDateDisabled</code>
                    and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DateClass</code>
                    are now also forwarded through <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DatePicker</code>
                    and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DateRangePicker</code>
                    so the booking-calendar UX works through the popover-style pickers too.
                </li>
                <li>New booking-style demo on the Calendar docs page: synthesised hotel availability with
                    green / amber / red / grey dots + tooltips like <em>"Sold out"</em>, <em>"Limited availability"</em>.
                </li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved — Calendar UX</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Single click target for month/year</strong> — the Days view header used to have
                    two adjacent buttons (month name + year) that opened different pickers. Replaced with
                    one combined button. Cycle is now: Days → click → Months → click year → Years.
                    One predictable click target per view.
                </li>
                <li><strong>Equal width across all three views</strong> — the picker no longer visibly
                    resizes when switching between Days, Months and Years. Each view is fixed to 224 px
                    of inner content (matches the 7-column day grid).
                </li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — Per-component registry endpoints</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>131 self-contained component files</strong> at
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">https://lumeo.nativ.sh/registry/&lt;key&gt;.json</code>
                    (and the matching NuGet path <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_content/Lumeo/registry/&lt;key&gt;.json</code>)
                    — same shape as shadcn's per-component registry plus extra LLM-friendly fields:
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">docsUrl</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">sourceUrl</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">source</code> (full Razor file content),
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">slots</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">serviceDependencies</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">cascadingDependencies</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">relatedComponents</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">keyboardInteractions</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">tests</code>,
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">mdSummary</code>
                    (a self-contained Markdown reference paragraph an LLM can drop straight into context).
                </li>
                <li>The <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">registryUrl</code>
                    field on every component in the root <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">registry.json</code>
                    now resolves correctly — previously it pointed at endpoints that 404'd.
                </li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Cli</code>
                    <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DefaultRegistryUrl</code>
                    bumped from a stale jsdelivr / rc.9 pin to the live docs site
                    (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">https://lumeo.nativ.sh/registry.json</code>).
                </li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed — Avatar shape + DropdownMenu focus + E2E CI</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Avatar outer wrapper now follows ShapeClasses</strong> so consumer-applied
                    borders / rings (e.g. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">border-2 border-background</code>
                    on AvatarGroup overlap) follow the avatar's actual circular shape — no more square
                    frames around fallback initials.
                </li>
                <li><strong>DropdownMenu focuses the menu content on open</strong> per the WAI-ARIA menu
                    pattern. Previously <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">tabindex="-1"</code>
                    was set but focus was never moved, so Escape and Arrow keys never reached the keydown
                    handler — keyboard nav was actually broken in production.
                </li>
                <li><strong>E2E visual test skipped under CI</strong> until a perceptual differ ships.
                    The home-above-fold byte-equal compare can't agree across Windows (where the baseline
                    was generated) and the Ubuntu CI runner because of font hinting / sub-pixel rendering.
                    The other 15 E2E tests still gate every push and PR.
                </li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.19 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.19</Badge>
            <Lumeo.Text Size="sm" Color="muted">May 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Hardening pass</strong>. Closes every P1 + P2 + P3 from an external rc.18 deep review:
            cross-instance drag state in DataGrid, EventCallback fire-and-forget, preset-codec namespace
            collision, red E2E suite, lifecycle races in Alert / SafeAsyncDispatcher, JS event-handler
            leaks in Editor + Gantt, eval() in docs, Worker payload + collision handling, Word-import
            DoS surface, mobile-unfriendly block demos. Plus the big one: a complete auto-generated MCP
            schema so Claude Code understands all 131 components without reading source.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — MCP server is now fully complete</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>131 / 131 components in the schema</strong> — every component now has full parameters, types, defaults, XML doc descriptions, EventCallback events, nested enums + records, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@inherits</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IAsyncDisposable</code>, and sub-components grouped per folder. 3,152 parameters total, 86 enums, 48 records, 0 thin fallbacks.</li>
                <li><strong>Auto-generated from Razor source</strong> via a new Roslyn-based pass in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.RegistryGen</code>. Every release regenerates the schema; no more manual catalog drift.</li>
                <li><strong>Hand-curated examples preserved</strong> as an overlay — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo_get_component</code> still surfaces the existing example Razor snippets for ~30 popular components.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed — DataGrid correctness (P1)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Column reorder cross-talk</strong>: a static <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ColumnDragState</code> was shared across every grid in the process — drops in grid B used grid A's source index. Replaced with an instance-bound <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridDragState</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OwnerGridId</code> guard. Same fix for row reorder. New regression tests cover both drag types.</li>
                <li><strong>EventCallback fire-and-forget at 6 sites</strong> in DataGrid.razor — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SelectedItemsChanged</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnColumnReorder</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnRowReorder</code> were called without await. Now awaited where the call site can be async, dispatched via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SafeAsyncDispatcher.FireAndForget</code> where it must stay sync.</li>
                <li><strong>Auto-save stale generation</strong>: the existing generation guard skipped LocalStorage writes but still called <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnLayoutSave</code> with stale snapshots. Guard now covers both paths.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed — Lifecycle / async hardening (P2)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SafeAsyncDispatcher</code> outer task</strong> — the inner try/catch only covered the work delegate. If <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">InvokeAsync</code> itself threw (renderer disposed), the outer Task was unobserved. Outer call now wrapped in an async helper that catches expected lifecycle exceptions silently and logs anything else.</li>
                <li><strong>Alert AutoDismiss timer/dispose race</strong> — replaced <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">System.Threading.Timer</code> with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CancellationTokenSource</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Task.Delay</code>; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Dispose</code> cancels the token and flips a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_disposed</code> flag before any state mutation.</li>
                <li><strong>Editor + Gantt JS listener leaks</strong> — anonymous <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">addEventListener</code> closures (mousemove, mouseleave, drop on the editor; document-level mousemove + mouseup on Gantt drag) survived <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">destroy(id)</code>. All listeners now stored as named handlers; destroy iterates and removes each.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed — Theming + API + Security (P1 + P2)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Preset code / Worker ID namespace collision</strong> — both spaces were 6-char Base62. Some Worker IDs decoded as valid v1 local presets and never hit the server. New explicit prefix scheme: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">l_&lt;6&gt;</code> for local codes, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">p_&lt;6&gt;</code> for Worker IDs. Bare 6-char input keeps legacy fallback behavior with strict version checks.</li>
                <li><strong>Worker payload check post-read</strong> — the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">content-length</code> header was the only authoritative check; missing or wrong header bypassed the limit. Now reads <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">arrayBuffer()</code> first and enforces real <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">byteLength</code>.</li>
                <li><strong>Worker collision retry</strong> — same 6-char ID with different payloads no longer overwrites silently. Up to 8 salt-based retries; idempotent on matching payload; 409 if exhausted.</li>
                <li><strong>Word-import 8 MiB cap</strong> — JS pre-arrayBuffer file.size check + .NET pre-Convert.FromBase64String size guard + new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">WordImportSizeException</code>. New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">MaxWordImportBytes</code> parameter on RichTextEditor.</li>
                <li><strong>Removed <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">eval()</code> in IconPage</strong> — replaced with a named JS helper. CSP-clean.</li>
                <li><strong>Vendored TipTap stack (~1.4 MB ESM bundle)</strong> — RichTextEditor no longer dynamic-imports from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">esm.sh</code> at runtime. Ships at <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_content/Lumeo.Editor/js/tiptap-bundle.js</code>; works under strict CSP and offline. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">window.lumeoTiptapBundleUrl</code> is the override hook for consumers who want to host their own.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed — Overlay UX + Avatar shape</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>OverlayService double-backdrop</strong> when consumers nested another <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;SheetContent&gt;</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;DialogContent&gt;</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;DrawerContent&gt;</code> inside their service-opened component. New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OverlayShellMarker</code> cascade — nested *Content components self-detect the host shell and pass children through. Both patterns now render exactly one backdrop. Regression test asserts <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">bg-black/80</code> appears exactly once.</li>
                <li><strong>Avatar outer wrapper follows ShapeClasses</strong> — consumer-applied borders / rings (e.g. AvatarGroup overlap framing) now follow the avatar's actual shape. Previously the border landed on the rectangular outer div while the inner div clipped to a circle, creating square frames around fallback initials.</li>
                <li><strong>DropdownMenu keyboard nav</strong> — menu container had <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">tabindex="-1"</code> but never received focus on open, so Escape and Arrow keys never fired. Now focuses the content on open per WAI-ARIA menu pattern. Surfaced while stabilizing the E2E suite.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved — E2E suite is green and CI-gated (P1)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>16 / 16 E2E tests passing</strong> (was 8 / 16). Stable selectors via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">data-testid</code> on the relevant docs elements; Search palette result locators scoped to the palette overlay; baseline snapshot committed at <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">tests/Lumeo.Tests.E2E/Snapshots/home-above-fold.png</code> with deterministic init-script (consent banner suppressed, animations zeroed).</li>
                <li><strong>New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.github/workflows/e2e.yml</code></strong> — installs Playwright browsers, builds the docs, runs the docs server on port 5290, runs E2E against it, uploads <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">docs-server.log</code> on failure.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved — Docs polish (P3)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Mobile / Desktop preview toggle</strong> on ComponentDemo — DashboardBlock / PricingTableBlock / SettingsPageBlock no longer force phone users into horizontal scroll. Toggle wraps the demo in a 420 px phone frame so the experience is honest, not painted-over.</li>
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">dotnet format --verify-no-changes</code> is green</strong> — formatting drift swept across 27 files in a single style pass, no behavior change. Ready to wire into CI as a release gate.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.18 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.18</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Drop-in CSS restored</strong> + <strong>visible Chart loading label</strong>. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_content/Lumeo/css/lumeo-utilities.css</code> ships in the NuGet package again after rc.15–rc.17 inadvertently dropped it. The bundle now also covers every utility class used inside the satellite packages — no per-satellite stylesheet needed. Charts also gained an always-on "Loading…" label to make in-flight data unambiguous.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — Chart loading label</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowLoadingLabel</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LoadingText</code> parameters</strong> on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Chart</code> (and forwarded by chart wrappers like <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BarChart</code>). When <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsLoading</code> is true, a small top-right pill (Lucide <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LoaderCircle</code> + "Loading…" text) is overlaid on top of whichever <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SkeletonStyle</code> is active. Default-on so users never mistake cycling phantom data for a bug; opt out per-chart via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowLoadingLabel="false"</code>.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed — Drop-in setup regression</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo-utilities.css</code> ships again</strong> — the rc.15 package-split spec moved the file out on the assumption a registry CDN would host it, but the CDN was never set up. Consumers following the docs got a 404 + visibly unstyled Lumeo components (Badge <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">px-2.5</code> missing, DataGrid toolbar borders gone, Switch container collapsed). Restored to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_content/Lumeo/css/lumeo-utilities.css</code>.</li>
                <li><strong>Bundle now covers all 6 satellites</strong> — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo-utilities.src.css</code> extended to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@source</code> the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">UI/</code> folder of every satellite (DataGrid, Charts, Editor, Scheduler, Gantt, Motion). Drop-in setup now works whether you install only core or all 7 packages.</li>
                <li><strong>Docs site Tailwind scan</strong> extended the same way — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">docs/Lumeo.Docs/Styles/tailwind.css</code> was scanning only <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">src/Lumeo/UI/**/*.razor</code>, so the live docs DataGrid demos were missing the same classes. Fixed in the same release.</li>
                <li><strong>NuGet author</strong> across all 9 published packages updated from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">bemi</code> (the developer's GitHub handle) to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">nativ.sh</code>.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Migrating from rc.15 / rc.16 / rc.17</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>If you vendored an older copy of <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo-utilities.css</code> as a workaround, you can drop it and revert your <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">App.razor</code> link tag back to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_content/Lumeo/css/lumeo-utilities.css</code>.</li>
                <li>If you set up your own Tailwind build (per the rc.15 "advanced" path), you can keep it — that path still works. Just drop the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@source</code> entries pointing at Lumeo packages, since the shipped bundle now covers them.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.17 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.17</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Audit-driven hardening</strong>. A 10-item code review surfaced real lifecycle, async, CI, and test-coverage gaps. This release closes all of them. <strong>+387 tests</strong> (1744 → 2131), three new infrastructure layers (component contract theory, Playwright e2e, visual snapshots), and four lifecycle bugfixes.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved — Lifecycle &amp; async safety</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>DataGrid debounced server search</strong> rewritten to a CTS-backed pattern — exact-last-call semantics, dispose cancels in-flight work, exceptions surfaced. Test now asserts <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Assert.Equal(1, callCount)</code> instead of race-tolerant <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">callCount &lt;= 2</code>.</li>
                <li><strong>OnThisPage</strong> (docs sidebar) now uses a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CancellationTokenSource</code> per route — old retry scans cancel cleanly when navigating, no more stale headings on fast clicks. Replaced JS <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">eval</code> with a dedicated function.</li>
                <li><strong>Toast / Overlay / MegaMenu</strong> providers — the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_ = InvokeAsync(...).ContinueWith(...)</code> pattern (which silently logged errors) replaced by <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Services.SafeAsyncDispatcher</code>. 9 call-sites converted; expected lifecycle exceptions (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">JSDisconnectedException</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ObjectDisposedException</code>) swallowed silently, others logged with a structured tag.</li>
                <li><strong>ComponentInteropService</strong> sync <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Dispose()</code> now fire-and-forgets the same JS-side cleanup that <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DisposeAsync</code> runs — stops browser-side observers/listeners from leaking when the sync path is taken.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — Test infrastructure</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Direct unit tests for 23 components</strong> previously without test files (Cascader, ColorPicker, ConsentBanner, DateTimePicker, Gantt, ImageCompare, InplaceEditor, InputMask, Kanban, MegaMenu, Mention, PasswordInput, PopConfirm, QRCode, RichTextEditor, Scheduler, Sortable, SpeedDial, TimePicker, Tour, Transfer, TreeSelect, TreeView). Coverage 108/131 → 131/131.</li>
                <li><strong>Component contract <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[Theory]</code></strong> — 250 test cases spanning 125 components; asserts every component renders with defaults AND forwards <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AdditionalAttributes</code> to its root. Catches "I forgot to splat" regressions across the entire library at once.</li>
                <li><strong>Lumeo.Tests.E2E</strong> — new Playwright .NET project with 5 high-value smoke tests (Dialog focus trap, DropdownMenu keyboard nav, Tooltip hover, catalog renders ≥100 cards, Cmd+K palette navigates). Reaches the failure modes bUnit can't.</li>
                <li><strong>Visual snapshot foundation</strong> — one example test capturing the home page above-the-fold and comparing against a baseline PNG. Set <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LUMEO_E2E_UPDATE_SNAPSHOTS=1</code> to regenerate baselines.</li>
                <li><strong>WordImporter real-DOCX test unskipped</strong> — programmatic in-memory DOCX with headings, paragraphs, and styles replaces the local-path fixture that previously caused the test to be skipped on every CI run.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved — CI release gate</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>CI now builds <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.slnx</code> with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">-warnaserror</code> across the entire solution (was: library + satellites only — docs project was excluded).</li>
                <li>CI runs all three test projects: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Tests</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Docs.Tests</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.RegistryGen.Tests</code>. Was previously only <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Tests</code>.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed — Docs nav UX</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Top-nav highlight: clicking into a component (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/components/&lt;slug&gt;</code>) now keeps "Components" lit instead of switching to "Docs".</li>
                <li>Sidebar from any docs/patterns/component-detail page now shows the <strong>full nav</strong> (docs + components + patterns) with collapsible groups; the group containing the current URL auto-expands. No more "to find a component you have to click into a component first".</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.16 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.16</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            <strong>Lumeo.Motion satellite (v0.1)</strong> + <strong>Docs nav v2</strong>. Motion ships 10 user-verified components — a curated set polished to perfection; more are coming in future releases. Install with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">dotnet add package Lumeo.Motion</code>.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — Lumeo.Motion v0.1</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Lumeo.Motion satellite package</strong> — 10 user-verified motion primitives for high-energy Aceternity / Magic UI-style interfaces.</li>
                <li><strong>Migrated from core (7)</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BlurFade</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BorderBeam</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Marquee</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">NumberTicker</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShimmerButton</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Sparkles</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">TextReveal</code>.</li>
                <li><strong>New (3)</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AnimatedBeam</code> (Magic UI port — SVG gradient path between nodes), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Confetti</code> (canvas burst escaped from demo box via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">position:fixed</code>), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Dock</code> (cursor-magnification with Lucide icons).</li>
                <li>Catalog cards show a NuGet badge for satellite components. All components stay in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@namespace Lumeo</code> — no consumer <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@using</code> changes needed.</li>
                <li>More motion primitives (AnimatedCircularProgressBar, AuroraBackground, Globe, HoverBorderGradient, MagicCard, Meteors, MorphingText, OrbitingCircles, Ripple, ShineBorder, Spotlight, TypingAnimation, WordRotate, and more) are deferred to a future release pending final visual polish.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — Docs nav v2</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Component catalog page</strong> (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/components</code>) — sticky category rail, filter chips, and 480×270 preview-card thumbnails for all components.</li>
                <li><strong>Algolia full-content search</strong> — Cmd+K now queries <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo_docs</code> index (components, patterns, blocks, guides); falls back to a local slim index when Algolia is unavailable.</li>
                <li><strong>registry.json</strong> as single source of truth — component metadata drives the catalog, the indexer, and the prerender script.</li>
                <li><strong>Thumbnail prerender script</strong> (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">scripts/prerender/run-all.mjs</code>) — headless Puppeteer renders one PNG per component; runs in CI on registry change.</li>
                <li><strong>Algolia indexer script</strong> (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">scripts/algolia/index-docs.mjs</code>) — pushes records to Algolia on every docs deploy.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Sidebar <strong>Form</strong> group collapsed from 34 individual links to 5 logical subgroups; <strong>Data Display</strong> from 28 to 5 — both data-driven from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">nav-config.json</code>.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CatalogFilterChips</code> buttons now expose <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">aria-pressed</code> state to assistive technology.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CatalogRail</code> sticky offset aligned to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">top-[84px]</code> to match NavMenu / BlocksSidebar / CustomizerSidebar.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.15 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.15</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            Major release: <strong>package split</strong> + <strong>RichTextEditor Pro</strong> + custom <strong>Gantt</strong> renderer. Lumeo is now 6 packages instead of 1 — install only what you use.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Package split (DevExpress / Microsoft.Extensions model)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo</code> — core, ~568 KB. Layout, Forms (excl. RichTextEditor), Navigation, Overlay, Feedback, Display, Motion, Theme, ConsentBanner, AI surface.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Charts</code> — ~196 KB. Chart + 30+ subtypes + ECharts interop.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.DataGrid</code> — ~111 KB. DataGrid, DataTable, Filter (incl. ClosedXML + QuestPDF for export).</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Editor</code> — ~50 KB. RichTextEditor + Word import via Mammoth.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Scheduler</code> — ~22 KB. FullCalendar wrapper.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Gantt</code> — ~19 KB. Custom SVG Gantt (no third-party dep).</li>
                <li>All 6 packages share one lockstep version. Components stay in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@namespace Lumeo</code> so consumer <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@using</code> directives are unchanged.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo add &lt;component&gt;</code> CLI now auto-detects the satellite and prompts to install via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">dotnet add package</code>.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">RichTextEditor Pro (Lumeo.Editor)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Mentions</strong> with pluggable triggers (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@</code> users, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">#</code> tags, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">$</code> variables, custom).</li>
                <li><strong>Slash commands</strong> (Notion-style): <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/heading</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/list</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/table</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/code</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/image</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/quote</code>.</li>
                <li><strong>Tables</strong> with resizable columns + header rows.</li>
                <li><strong>Code blocks with syntax highlighting</strong> (lowlight: JS/TS/Python/CSS/HTML/JSON/Bash).</li>
                <li><strong>Image upload</strong> via drag/drop/paste — consumer-supplied <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnImageRequested</code> callback or base64 fallback.</li>
                <li><strong>AI actions on selection</strong> (Improve / Shorten / Expand / Translate / Summarize / FixGrammar) — consumer-supplied <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnAiAction</code> hooks into your own LLM.</li>
                <li><strong>Word (.docx) import</strong> — works in-browser by default via Mammoth in the WASM bundle. Default style map handles English + German Word default styles (Title/Titel, Heading 1-6/Überschrift 1-6, Body Text/Textkörper, etc.).</li>
                <li><strong>Block drag handles</strong>, <strong>bubble menu</strong>, <strong>smart paste</strong>, <strong>markdown shortcuts</strong>, <strong>task lists</strong>, <strong>auto-link</strong>.</li>
                <li><strong>Lumeo Dialog</strong> for the Link prompt instead of the native browser <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">window.prompt</code>.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Custom Gantt renderer (Lumeo.Gantt)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Replaces the Frappe Gantt v1.2.2 dependency with ~400 lines of deterministic custom SVG. No more CDN dep, no more theming surprises across browsers.</li>
                <li>View modes: Day / Week / Month / Year / HalfDay / QuarterDay.</li>
                <li>Drag a bar to move (snaps to single days even in Week view), drag the right edge to resize, drag the bottom-edge handle to set progress.</li>
                <li>Hover tooltip with task name, date range, day count, progress %.</li>
                <li>Theme tokens read via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">getComputedStyle</code> — no theme/cache battles.</li>
                <li>Auto-scrolls horizontally so today is centered on first paint.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Other</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Overlay components (Dialog, Drawer, DropdownMenu, AlertDialog, Sheet, Popover, ContextMenu, Combobox, Select, ColorPicker, HoverCard, Tour, Collapsible, NavigationMenu) renamed <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsOpen</code> → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Open</code>. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsOpen</code> kept as <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[Obsolete]</code> alias for one release.</li>
                <li>24 form inputs gained <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Required</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Invalid</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ErrorText</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">HelperText</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Label</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Name</code>. Reads <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">FormFieldContext</code> via cascading parameter so <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;FormField&gt;</code> wrapper still works.</li>
                <li>Display components (Alert, Result, ReasoningDisplay, Statistic, SparkCard) gained Size + Variant; trigger components (BackToTop, SpeedDial, Chip, Carousel) gained Disabled.</li>
                <li>14 missing component doc pages added (Code, Delta, KpiCard, Marquee, NumberTicker, Overlay, PromptInput, ReasoningDisplay, ShimmerButton, SparkCard, Sparkles, StreamingText, TextReveal, ToolCallCard) + 62 new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ComponentsIndex</code> entries.</li>
                <li>Select / Combobox <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Searchable</code> filter actually filters items (was visible but inert).</li>
                <li>Mammoth bumped to 1.11.0 to clear NU1902 advisory.</li>
                <li>FullCalendar (Scheduler) now reads Lumeo theme tokens via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">--fc-*</code> CSS variable map.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.14 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.14</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            Full accessibility sweep. Every icon-only button across the library now ships with an <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">aria-label</code> or <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">sr-only</code> text. WCAG AA violations from Lighthouse: zero.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added aria-label to icon-only buttons</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToastClose</code> — new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AriaLabel</code> parameter (default "Close"), sr-only text for screen readers.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Cascader</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DateTimePicker</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Select</code> (clear + multi-value chips), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Filter.FilterPill</code> — "Clear" / "Remove {label}" labels on the X icons that were previously empty buttons.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Select</code> clear-icon converted from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;span&gt;</code> to a proper <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;button type="button"&gt;</code> — keyboard focusable and announced to assistive tech.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Already compliant (verified)</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Alert</code> (dismiss), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Chip</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Badge</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">TagInput</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DatePicker</code> — all had aria-label on their close/remove icons.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Dialog</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Sheet</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Drawer</code> close buttons — sr-only localized text via ILumeoLocalizer.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Carousel</code> prev/next — aria-label + sr-only. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Pagination</code> has visible text. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Calendar</code> nav is fully labelled via L["Calendar.*"] strings. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Rating</code> stars each emit "Rate {n} of {max}".</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.13 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.13</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            Finishes the accessibility pass started in rc.12. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Slider</code> now has proper ARIA plumbing; the landing-page form-controls bento tile demonstrates the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AriaLabel</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Label</code> parameters on every interactive element.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Slider.AriaLabel</code> — accessible name for the range input. Also auto-emits <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">aria-valuemin</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">aria-valuemax</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">aria-valuenow</code>. For range sliders (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsRange=true</code>), the end-handle's aria-label is suffixed with "(end)" automatically.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Docs site</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Landing-page form controls demo now passes <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Label</code>/<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AriaLabel</code> on every Switch, Slider, Checkbox, and Toggle — fixes the remaining Lighthouse "button / form element without accessible name" violations.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.12 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.12</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            Accessibility + NuGet health + performance polish. Fixes two long-standing Razor bugs in Card / Separator, adds <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">aria-label</code> plumbing to form toggles, and ships deterministic builds with Source Link so consumers can step into Lumeo source from any debugger.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Card</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CardContent</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CardHeader</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CardFooter</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Separator</code>: moved <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.Trim()</code> inside the Razor expression. Was previously rendered as a literal HTML attribute (silent error in browsers, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">InvalidCharacterError</code> in strict DOM consumers like Puppeteer).</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">theme.js</code>: replaced synchronous <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">XMLHttpRequest</code> to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/lumeo-theme.json</code> with a DOM lookup for an inlined <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;script id="lumeo-theme-defaults"&gt;</code> tag. Eliminates the Chrome sync-XHR deprecation warning, the main-thread block, and the 404 console error on every site without a preset file.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BlurFade</code> no longer holds its children at <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">opacity: 0</code> by default. Above-the-fold content is visible from first paint even when JS hasn't hydrated yet (previously caused a 1-2 s blank flash on prerendered landing pages).</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Switch.Label</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Checkbox.AriaLabel</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Toggle.AriaLabel</code> — new parameters that emit <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">aria-label</code> on the underlying button so icon-only usages pass Lighthouse / WCAG without ceremony. Switch also falls back to the current <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnLabel</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OffLabel</code> text when no explicit label is set.</li>
                <li>NuGet package now ships with <strong>Source Link</strong>, a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.snupkg</code> symbol package, and deterministic compiler flags. Debuggers (Rider, Visual Studio, VS Code) can step directly into Lumeo source from GitHub. All three NuGet Package Explorer health checks now pass.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Docs site</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Full static HTML prerender in CI — every route is served with complete content for first paint + SEO (192 routes, ~110 s prerender, via Puppeteer).</li>
                <li>Per-route OG / Twitter preview cards generated via Satori — unique 1280×640 PNG per page with the page title, Lumeo brand, and category pill. Served from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/og/&lt;slug&gt;.png</code>.</li>
                <li>Per-page <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;meta name="description"&gt;</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;link rel="canonical"&gt;</code>, and JSON-LD structured data (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">TechArticle</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BreadcrumbList</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SoftwareApplication</code>).</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/llms.txt</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/llms-full.txt</code> + per-route <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.md</code> variants for GEO — ChatGPT, Perplexity, Claude can discover and cite Lumeo docs in one fetch.</li>
                <li>Cloudflare Web Analytics (cookie-free) gated behind the consent banner.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.11 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.11</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            Adds the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ConsentBanner</code> component + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ConsentService</code>. GDPR-ready cookie / analytics consent primitive — no other Blazor component library ships one.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Services.ConsentService</code> — per-category consent manager. Persists to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">localStorage</code> (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo:consent:v1</code>), exposes <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">HasConsent(string)</code> for gating third-party scripts, and fires <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnChange</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnRequestOpenPreferences</code> events so a "Manage cookies" link anywhere in the layout can reopen the banner without CascadingValue plumbing.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;ConsentBanner&gt;</code> — slide-in banner with Accept / Reject / Customize. The preferences dialog has per-category toggles; "necessary" is always locked on. Strings are individually overridable for i18n.</li>
                <li>Registered in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AddLumeo()</code> as Scoped — zero extra wiring.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Docs site</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Migrated from GitHub Pages to Cloudflare Pages — per-path <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Cache-Control</code> (fingerprinted <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">_framework/*.wasm</code> now <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">immutable</code> for a year), HTTP/3, Brotli at edge, no env-protection friction.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">sitemap.xml</code> auto-generated from every <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@page</code> directive (190 URLs) + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">robots.txt</code> so Google can index all component / block / pattern pages.</li>
                <li>Issue + PR templates, SECURITY policy, Code of Conduct, FUNDING (Sponsors button).</li>
                <li>New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/privacy</code> page with live consent-management controls.</li>
                <li>Open Graph + Twitter Card meta tags on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">index.html</code> for proper link previews on Discord / LinkedIn / Slack / X.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.10 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.10</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            Polish pass across ~25 components and every block page — trigger-icon state reflection, cleaner overlay arrows, rewritten Checkbox glyph, DataGrid fullscreen layout, Combobox/Command empty-state gating, and a systematic border-softening sweep.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowArrow</code> parameter on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PopoverContent</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DropdownMenuContent</code>, and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">HoverCardContent</code> — rotated-square arrow with matching border on outward-facing sides.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">data-state="open|closed"</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">group</code> class on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DropdownMenuTrigger</code> so consumer chevrons can flip with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">group-data-[state=open]:rotate-180</code>.</li>
                <li>Toast slide-out animation (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animate-toast-out</code>) — dismiss now slides + collapses instead of popping.</li>
                <li>15 new Lucide icon mappings in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DynamicIcon</code> (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Wrench</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BookOpen</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Component</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Puzzle</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Cog</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Server</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Database</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Wand</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PanelLeft</code>, et al.) + every search-palette category now has a real icon instead of a Circle fallback.</li>
                <li>Chevron rotation on open for <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SelectTrigger</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Cascader</code>, and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ColorPicker</code> (matching existing Accordion/NavigationMenu/TreeSelect behavior).</li>
                <li>Demos: "With Arrow" added to Popover, DropdownMenu, and HoverCard doc pages.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Sparkles</strong> clustered at top-left when consumer passed <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Class="absolute inset-0"</code> — root cause was Tailwind v4's <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@layer utilities</code> losing to unlayered CSS. Component now drops the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.lumeo-sparkles</code> class entirely when the consumer supplies positioning.</li>
                <li><strong>Checkbox</strong> rewritten — crisp inline-SVG check + minus with rounded stroke caps (no more Lucide jagginess), unchecked state now visible (was zero-sized due to arbitrary Tailwind classes not being safelisted), hover state, smooth color transition.</li>
                <li><strong>Toast</strong> auto-dismiss missed <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">StateHasChanged</code> so toasts stuck on screen.</li>
                <li><strong>Rating</strong> half-star was shifted vertically — reworked as two stacked SVGs in a single grid cell with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">clip-path</code>, pixel-perfect alignment.</li>
                <li><strong>Progress</strong> indeterminate sweep: retuned keyframe so exit and opacity fade align — no more "snap off" at the right edge.</li>
                <li><strong>Resizable</strong> and <strong>Splitter</strong> vertical mode: swapped <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">flex-basis: X%</code> → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">flex: X 1 0</code> so vertical works without requiring an explicit parent height.</li>
                <li><strong>Bento</strong> tiles overlapping — added <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">grid-auto-rows: minmax(0, 1fr)</code> and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">min-w-0 min-h-0</code> on tiles.</li>
                <li><strong>ImageCompare</strong> rewritten with a transparent <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;input type="range"&gt;</code> for drag + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">clip-path</code> for reveal (browser handles drag math, tracks the cursor exactly).</li>
                <li><strong>Combobox</strong> + <strong>Command</strong> empty states rendered unconditionally — now gate on registered item count; items track their visibility so filtered-out items un-register.</li>
                <li><strong>Tour</strong> spotlight now reads the target's computed <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">border-radius</code> via JS interop and matches it.</li>
                <li><strong>DataGrid fullscreen</strong>: removed duplicate exit control (modal X was redundant with toolbar minimize), grid fills modal height, pagination sticks to bottom; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Context.IsExpanded</code> now rebroadcasts to toolbar on toggle so the Maximize/Minimize icon swaps correctly.</li>
                <li><strong>Stack</strong>/<strong>Flex</strong> <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Gap</code> values like <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">14</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">16</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">20</code> had no effect — Tailwind's scanner can't see runtime-interpolated class names. Added full spacing-scale safelist to the utilities bundle.</li>
                <li>Block previews (SignIn/SignUp/ResetPassword/OtpVerify) had a floating card halo from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">min-h-[640px]</code> — replaced with tight <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">p-8</code>, and CardContent bumped to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">pt-10</code> so the hero icon has breathing room.</li>
                <li>Alert/Badge/Command/Kbd/EmptyState/Popover/HoverCard/Toast borders softened to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">border-border/40</code> or <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/60</code> across ~60 doc pages.</li>
                <li>Circle-fallback icons in the Ctrl+K command palette (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Wrench</code> etc. were missing from DynamicIcon's map).</li>
                <li>Kanban pattern "+2" overflow pill sized to match <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Avatar.Sm</code>.</li>
                <li>Double-icon bugs on Alert demos (auto-icon + inline DynamicIcon) on AlertPage and KeyboardShortcutsPage.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.9 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.9</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            Landing page redesign, marquee swag, comparison table rewrite, and aggressive-border audit across 228 API reference tables.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Home marquee rewritten as two rows in opposing directions with icon pills (card surface + primary-tinted) and edge-gradient fades.</li>
                <li>Landing comparison table fact-checked against current GitHub/NuGet state — corrected MudBlazor templates, Radzen AI + MCP, Ant Design Blazor package size.</li>
                <li>Customizer "Share" button collapsed to a single client-side preset code action (removed the duplicate server-code row).</li>
                <li>Softer borders across 228 API reference table headers + ~60 demo wrappers.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.8 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.8</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            Patterns page expansion (32 categories, ~70 live inline variants), theme customizer expansion with Vega/Nova/Maia/Lyra/Mira presets, Violet/Amber/Teal color themes.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Theme customizer: Font selector (6 fonts), Icon Library (6 options, Lucide default), Menu Color, Menu Accent.</li>
                <li>Color themes: Violet, Amber, Teal (total 8 themes).</li>
                <li>Preset renames: Vega, Nova, Maia, Lyra, Mira.</li>
                <li>Patterns page: 32 categories with ~70 live inline variants across Accordion, Alert, Avatar, Badge, Button, Card, Checkbox, Dialog, Input, Select, Switch, Tabs, Table, Toast, Progress, Separator, Skeleton, Popover, Tooltip, Sheet, DropdownMenu, Combobox, Breadcrumb, Pagination, RadioGroup, Spinner, EmptyState, Slider, Toggle, ToggleGroup, Kbd, OtpInput.</li>
                <li>ChartPatternDemos shared component with 8 chart patterns.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Customizer BaseColor and Style options were purely visual — now apply real CSS changes.</li>
                <li>Heavy borders on Switch, ThemeSwitcher, FileUpload, Checkbox, RadioGroup, OtpInput, Combobox, MainLayout search modal.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.7 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.7</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            Full shadcn-CLI parity pass. New <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">apply</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">view</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">info</code> commands, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">--only</code> filter on apply, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">--all / --diff / --view</code> flags on add, and server-stored preset IDs via a Cloudflare Worker at <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">api.lumeo.nativ.sh</code> — so shareable codes can hold arbitrary configs (not just the 26-bit bitfield).
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo apply [preset]</code> — flat top-level command (matches <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">shadcn apply</code>). Writes the theme section into <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo.json</code> (with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo.json.bak</code> backup) and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">wwwroot/lumeo-theme.json</code> which <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">theme.js</code> reads on init for no-FOUC first paint.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">--only theme,font,icons,radius,menu,style,baseColor,dark</code> filter on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">apply</code> to restrict which parts get rewritten.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo view &lt;component&gt;</code> — prints component source to stdout without writing, for preview.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo info [--json]</code> — dumps project config + installed components + theme.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo add --all</code> to install every component; <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">--diff</code> and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">--view</code> flags for preview-only runs.</li>
                <li>Server-stored preset IDs: when a preset code can't be decoded client-side, the CLI fetches it from <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">https://api.lumeo.nativ.sh/preset/&lt;id&gt;</code> — a Cloudflare Worker backed by KV. IDs are content-addressed SHA-256 of the canonical JSON, so the same config always returns the same 6-char code.</li>
                <li>Customizer sidebar <strong>Share as link</strong> button — POSTs the live config to the Worker and puts <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo apply --preset &lt;id&gt;</code> on the clipboard. Useful for custom brand palettes that don't fit the bitfield codec.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Deprecated</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo theme apply</code> — still works but prints a deprecation notice. Use <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo apply</code> instead.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.6 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.6</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            Default registry URL now points at jsDelivr's GitHub CDN, so the CLI works out of the box without any self-hosted registry. Enables <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo init</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo add</code> on fresh projects immediately after <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">dotnet tool install</code>.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Default registry URL <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo.nativ.sh</code> wasn't live, so fresh installs of the CLI failed with a 404 on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo add &lt;component&gt;</code>. Now defaults to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">cdn.jsdelivr.net/gh/Brain2k-0005/Lumeo@v2.0.0-rc.6/src/Lumeo/wwwroot/registry/registry.json</code> — pinned to the release tag so schema stays consistent.</li>
                <li>File-fetch URL derivation now recognizes three patterns: (1) jsDelivr / raw.githubusercontent.com layouts where the registry lives under <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.../wwwroot/registry/registry.json</code>, (2) legacy self-hosted <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/registry.json</code> → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/raw/</code>, (3) bare base URLs.</li>
                <li>GitHub Pages deploy workflow now also triggers on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">v2.0-dev</code> branch and copies the registry + source files under the published site's <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/registry/</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/raw/</code> paths.</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.5 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.5</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            Shareable theme presets — customize on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/themes</code>, copy a 6-character code, share or apply via CLI. No hosted backend.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LumeoPresetCodec</code> — encodes theme/style/base color/radius/font/icons/menu color/menu accent/dark into a 26-bit bitfield, base62-encoded to 6 chars. Versioned schema so future option additions reject old codes cleanly.</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LumeoPresetOptions</code> — single source of truth for option arrays (indexes are the stored values — append-only).</li>
                <li><strong>/themes customizer:</strong> live 6-char preset code with copy button — puts <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo theme apply --preset &lt;code&gt;</code> on the clipboard.</li>
                <li><strong>CLI:</strong> new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo theme apply --preset &lt;code&gt;</code> subcommand. Decodes the code and prints the resolved configuration + a ready-to-paste JS snippet that drives the runtime theme-switcher. <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">--dry-run</code> flag.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Why no backend?</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>The preset IS the data — 26 bits packed into 6 base62 chars — no hosted KV store to manage.</li>
                <li>Codes work offline, can be pasted into docs / issues / PRs, and never expire.</li>
                <li>Shorter than shadcn's 9-character IDs (which require their API).</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.4 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.4</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            DataGrid toolbar composability, smoother column resize, docs sidebar UX. Polish over rc.3.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;ToolbarContent&gt;</code> is now a component with a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Class</code> attribute for custom wrapper styling</li>
                <li>Five built-in toolbar tools as separate composable components: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridToolbarFullscreen</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridToolbarCopySelected</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridToolbarColumns</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridToolbarExport</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridToolbarLayouts</code></li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridToolbarContext&lt;TItem&gt;</code> cascading context — tool components pick up state with no props</li>
                <li>Docs sidebar: active NavLink highlight + auto-scroll active item to centered viewport on direct URL loads / navigation + auto-expand Charts subtree on /components/charts/*</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>DataGrid column resize: JS drives the drag entirely in the DOM (direct style writes on header + body cells) and commits ONCE on mouseup — no more Blazor round-trip per pixel</li>
                <li>Consumer-provided <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;ToolbarContent&gt;</code> suppresses the default tool stack to avoid duplicate icons</li>
                <li>Softened aggressive <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">border-b</code> on 228 API reference table headers and in Accordion / Command / DataTable / Table / Sidebar components to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">border-border/40</code></li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Infinite re-render loop when <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;ToolbarContent&gt;</code> re-registered on every parameter set</li>
                <li>Duplicate toolbar icons when composing built-in tools inside <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;ToolbarContent&gt;</code></li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.3 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.3</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            Shape-aware chart loading skeletons + per-chart Loading demos across all 30 chart doc pages.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ChartSkeleton</code> with shape variants (Bars, Line, Area, Pie, Scatter, Grid, Generic) — reserved plot-area geometry so the real chart swaps in without layout jump</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IsLoading</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowLoadingSkeleton</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SkeletonKind</code> parameters on every chart wrapper</li>
                <li>Loading-state demo on every chart doc page — Simulate button + Hold-skeleton switch</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Chart skeleton animations per kind: bar-grow, multi-line stroke-draw, flat pie rotation, scatter breath, grid pulse</li>
                <li>350ms cross-fade handoff between skeleton and real chart</li>
                <li>Axis / legend hint rects reserve real-chart space so there's no layout shift</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.2 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.2</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            CSS bundle fix + portal theme-token inheritance. Foundational stability patches over rc.1.
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Pre-compiled <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo-utilities.css</code> was missing theme-token classes (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">bg-background</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">bg-popover</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">text-muted-foreground</code>, etc.) that consumers needed — added <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@source inline()</code> safelists</li>
                <li>Theme auto-applies <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">bg-background text-foreground</code> to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;body&gt;</code> on init — portal components (Dialog / Sheet / Drawer / Toast / Popover / Tooltip) now inherit theme tokens when they render outside the theme context</li>
                <li>BarChart <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LabelStrategy</code> enum (Smart / ShowAll / Auto) replaces the old boolean — auto-rotates category labels at 11 / 17 / 25 densities without flooding dense charts</li>
                <li>Stacked bar chart border radius regression — stacked segments now correctly round only the outermost edge</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v2.0.0-rc.1 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v2.0.0-rc.1</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Lumeo.Text Size="base" Color="muted" Leading="relaxed">
            Lumeo 2.0 is a mega-release: AI chat primitives, motion primitives, Scheduler, Gantt, RichTextEditor, 14 locales + RTL, Bento / dashboard tiles, a <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[LumeoForm]</code> source generator, shadcn-style CLI vendoring, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">dotnet new</code> templates, and an MCP server for LLM codegen. 128 components, 1,687 tests at rc.1 (current rc.17: 131 components across 7 packages).
        </Lumeo.Text>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — Components</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>AI primitives</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PromptInput</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">StreamingText</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AgentMessageList</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AgentMessage</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ToolCallCard</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ReasoningDisplay</code> — SignalR-native token streaming with sticky auto-scroll and collapsible chain-of-thought</li>
                <li><strong>Motion primitives</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Marquee</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">NumberTicker</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">TextReveal</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BlurFade</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BorderBeam</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShimmerButton</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Sparkles</code></li>
                <li><strong>Dashboard tiles</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Bento</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BentoTile</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">KpiCard</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SparkCard</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Delta</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Sparkline</code> (standalone SVG primitive)</li>
                <li><strong>Navigation / layout</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">BottomNav</code> (safe-area, optional FAB), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Splitter</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SplitterPane</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SplitterDivider</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PickList&lt;T&gt;</code></li>
                <li><strong>Enterprise</strong>: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Scheduler</code> (FullCalendar v6 — Month/Week/Day/List, drag-to-create/reschedule), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Gantt</code> (Frappe Gantt — Day/Week/Month/Year zoom, drag progress), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">RichTextEditor</code> (TipTap v2 — Full/Basic/None toolbars, two-way HTML binding)</li>
                <li><strong>Blocks</strong>: 8 copy-paste page templates — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/blocks/sign-in</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/sign-up</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/reset-password</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/otp-verify</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/pricing</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/hero</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/dashboard-block</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/settings-page</code></li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — Services &amp; tooling</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IDataGridExportService</code> — real <strong>.xlsx</strong> export via ClosedXML, PDF via QuestPDF, CSV, with browser download helper and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ExportFormats</code> flags</li>
                <li>14 built-in locales via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ILumeoLocalizer</code>: EN, DE, ES, FR, IT, PT, NL, PL, JA, ZH-CN, KO, AR, RU, TR</li>
                <li>RTL layout via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IThemeService.SetDirectionAsync(LayoutDirection)</code> — persisted, applied before first paint, with RTL-aware keyframes</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Cli</code> — shadcn-style CLI (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">dotnet tool install -g Lumeo.Cli</code>). Vendorize components into your repo: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo init / add / list / diff</code></li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">registry.json</code> — machine-readable catalog of 127 components (files, dependencies, CSS variables)</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@lumeo-ui/mcp-server</code> — MCP server exposing curated component schemas so Claude / ChatGPT / Copilot / Cursor author correct Lumeo markup</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Templates</code> — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">dotnet new lumeo-page / lumeo-form / lumeo-component</code> scaffolders</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added — Developer experience</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[LumeoForm]</code> Roslyn source generator — annotate a POCO and get a compile-time <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">RenderForm(model, onSubmit)</code> that emits a fully-bound Lumeo Form with validation. Handles string / int / decimal / bool / DateTime / enum / email / password via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[DataType]</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[Required]</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[Display]</code></li>
                <li>Opt-in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Animated</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AnimatedIndicator</code> props on Steps, Timeline, Progress, Tabs, Switch, Checkbox, Badge, BottomNav — off by default, zero overhead when unused</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">PressEffect</code> enum (Scale / Brightness / Ripple) on Button, ShimmerButton, Card, BottomNavItem, ToggleGroupItem, Chip</li>
                <li>248 new bUnit tests for 2.0 components — <strong>1,687 total</strong>. 138 missing API rows filled in across ~45 component doc pages. Scrollable <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">OnThisPage</code> aside on every docs page. Landing rewrite, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/docs/registry</code> authoring guide, and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">/docs/culture</code> page</li>
                <li>Interop: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">motion.*</code> (RAF + IntersectionObserver), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ai.*</code> (autosize + sticky-scroll MutationObserver), lazy-loaded <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">rich-text-editor.js</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">scheduler.js</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">gantt.js</code> ESM modules, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DownloadFileAsync</code> on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IComponentInteropService</code>, Splitter pointer-capture helpers</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>DataGrid</strong>: inset fullscreen modal with open animation, header drag-to-reorder, layout JSON round-trip (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ExportLayout()</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ApplyLayoutJsonAsync()</code>), toolbar toggles (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowSearch</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowColumnChooser</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ShowExport</code>), <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Culture</code> parameter, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">SetHtmlClass</code> for navbar hiding in fullscreen</li>
                <li>UX audit: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">cursor-pointer</code> + <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">focus-visible</code> rings on every interactive component</li>
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Culture</code> parameter cascades through DatePicker, DateTimePicker, NumberInput, Slider, Statistic — falls back to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CultureInfo.CurrentCulture</code></li>
                <li>Motion demos: interactive Steps/Timeline, proper Tabs indicator per variant, testable BottomNav, single-connector animation (no flicker on reverse)</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>DataGrid: duplicate column registration race, header-drag edge cases, fullscreen z-index now above nav, grid fills overlay width</li>
                <li>BarChart: every category label rendered by default (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LabelStrategy.Smart</code>) with auto-rotation at higher densities (-60°, -75°)</li>
                <li>Landing: quickstart terminal cards, CTA box + Star button contrast, visible gaps, removed stray separators</li>
                <li>QuestPDF WASM crash on <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DataGridExport</code> docs page (button disabled under WASM)</li>
                <li>Tailwind arbitrary-class bugs and docs spacing polish across 2.0 component pages</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Breaking changes</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><strong>Icon / Label RenderFragment slots removed</strong> from Alert, Badge, EmptyState, Rating, Result, Segmented, SidebarMenuButton, StepsItem, TabsTrigger, TimelineItem. Rename <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Icon&gt;…&lt;/Icon&gt;</code> → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;IconContent&gt;…&lt;/IconContent&gt;</code> and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Label&gt;…&lt;/Label&gt;</code> → <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;LabelContent&gt;…&lt;/LabelContent&gt;</code>. Deprecated in 1.6.0.</li>
                <li><strong>DataGrid Excel export is a real <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">.xlsx</code></strong> now (ClosedXML), not a CSV renamed to .xlsx. Transparent for normal users; remove any workaround that routed the "excel" case to your own handler.</li>
                <li><strong>BarChart <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LabelStrategy</code> defaults to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Smart</code></strong>, showing every category label and auto-rotating at higher densities. Restore the previous behaviour with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LabelStrategy="ChartLabelStrategy.Auto"</code>.</li>
                <li><strong>Culture defaults to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CultureInfo.CurrentCulture</code></strong> on date / number components. Pass <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Culture="@@CultureInfo.InvariantCulture"</code> to keep invariant formatting.</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Migration</Heading>
            <Lumeo.Text Size="sm" Color="muted" Leading="relaxed">
                See <a href="https://github.com/Brain2k-005/Lumeo/blob/master/MIGRATION.md" class="underline underline-offset-4 hover:text-foreground">MIGRATION.md</a> at the repo root for before/after snippets and migration recipes for every breaking change, plus install commands for the new <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Cli</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Lumeo.Templates</code>, and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@lumeo-ui/mcp-server</code> companion packages.
            </Lumeo.Text>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.6.0 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.6.0</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IconContent</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">LabelContent</code> slots on Alert, Badge, EmptyState, Rating, Result, Segmented, SidebarMenuButton, StepsItem, TabsTrigger, TimelineItem — replace the previous <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Icon</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Label</code> slots that collided with the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Icon&gt;</code> and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Label&gt;</code> components</li>
                <li>Design philosophy section in Introduction docs explaining the unprefixed naming choice and the <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Name&gt;Content</code> convention</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Deprecated</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Old <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Icon</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Label</code> slot parameters remain as <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">[Obsolete]</code> aliases — existing code compiles with a warning; aliases are removed in 2.0</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.5.1 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.5.1</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li><code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">&lt;Icon&gt;</code> component — Lumeo-native wrapper around Blazicons with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Name</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Svg</code>, and <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Size</code> (Xs, Sm, Md, Lg, Xl) parameters</li>
                <li>Icon finder docs page — search + library switcher across Lucide, Bootstrap, Heroicons, Feather, Octicons, Material, Fluent; reflection-based catalog covering all ~1,700 Lucide icons</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.5.0 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.5.0</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Service interfaces — <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IToastService</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IOverlayService</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IThemeService</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IKeyboardShortcutService</code>, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IComponentInteropService</code> — for mockability in consumer tests</li>
                <li>DataGrid sticky header across scrolling viewport</li>
                <li>DatePicker support for <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DateTime</code> binding alongside <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DateOnly</code></li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>DialogFooter button spacing, alignment, and responsive stacking polished</li>
                <li>DataGrid DX: cleaner row API, simplified column definitions, better defaults</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.4.1 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.4.1</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Stacked bar chart: outer corners stayed sharp when theme had rounded corners — now uses per-segment <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">borderRadius</code> arrays so the theme's radius actually applies</li>
                <li>Dialog zoom-in animation transform leaked with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">fill-mode: both</code> — keyframe end state switched to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">transform: none</code></li>
                <li>DataGrid column race where the grid rendered empty until the first interaction — fixed with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">InvokeAsync(StateHasChanged)</code> in <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AddColumnDef</code></li>
                <li>Charts not repainting on theme change — JS dispatches <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo:theme-changed</code>; ECharts interop listens and rerenders</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.4.0 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.4.0</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Rounded outer corners on stacked bar charts (top segment top-rounded, bottom segment bottom-rounded)</li>
                <li>Charts now rerender automatically on theme change — no page refresh needed</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.3.1 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.3.1</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>nupkg leaked <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">package.json</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">package-lock.json</code> and caused <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">NETSDK1152</code> publish conflicts in consumer apps — moved npm build to repo root and excluded <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Styles/</code> from the package</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.3.0 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.3.0</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Pre-compiled <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo-utilities.css</code> ships in the package — zero-config Tailwind for consumers; no local Tailwind build required</li>
                <li>Consumers who want Tailwind in their own markup can keep their own v4 build and drop <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">lumeo-utilities.css</code> — their build picks up every utility Lumeo needs</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.2.0 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.2.0</Badge>
            <Lumeo.Text Size="sm" Color="muted">April 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Built-in EN/DE localization via <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">ILumeoLocalizer</code> — driven by <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">CultureInfo.CurrentUICulture</code> with exact → neutral → English fallback</li>
                <li>Override or extend locales during <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AddLumeo(opts =&gt; opts.Add(...))</code></li>
                <li>DataGrid, Pagination, DatePicker, and other component default strings now translatable</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Stacked bar chart rendering on dark theme</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.1.2 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.1.2</Badge>
            <Lumeo.Text Size="sm" Color="muted">March 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>PasswordInput browser-autofill yellow highlight bled past the input's rounded corners — now clipped to the input's radius</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.1.1 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.1.1</Badge>
            <Lumeo.Text Size="sm" Color="muted">March 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>DataGrid pagination test assertion updated to match the new footer wording</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.1.0 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.1.0</Badge>
            <Lumeo.Text Size="sm" Color="muted">March 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>ColorPicker SV + Hue picker surface (replaces the earlier swatch-only design)</li>
                <li>Blocks polish pass — cleaner layouts across Authentication, Analytics, Calendar, Chat, Dashboard, E-Commerce, FileManager, Kanban, Settings, TaskTracker and more</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Overlay positioning inside ancestors with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">transform</code> / <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">filter</code> (these create a containing block that breaks <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">position: fixed</code>)</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.0.2 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.0.2</Badge>
            <Lumeo.Text Size="sm" Color="muted">March 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Textarea <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">@@bind-Value</code> two-way binding</li>
                <li>TreeSelect cleanup on dispose</li>
                <li>Tooltip disposal race on rapid mount/unmount</li>
                <li>DataGrid pagination alignment and UX polish</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.0.1 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.0.1</Badge>
            <Lumeo.Text Size="sm" Color="muted">March 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>DataGrid <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">GroupsExpandedByDefault</code> parameter</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>7 reported bugs — Input two-way binding, DataGrid skeleton, sticky header, and grouping defaults</li>
                <li>DataGrid pagination layout — info left, page controls right</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.0.0 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.0.0</Badge>
            <Lumeo.Text Size="sm" Color="muted">March 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>First stable release — 103 components, 8 themes, 1,316 tests</li>
                <li>CI workflow publishing to NuGet on GitHub Release</li>
                <li>Chart error-state visibility, autosave safety guards, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">DelayedDispatch</code> for timer-based components</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>1.0 hardening — error visibility across async paths, shortcut handles simplified to <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">IAsyncDisposable</code>, toolbar errors routed to a single sink</li>
                <li>All dependencies updated to latest</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.0.0-beta.4 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.0.0-beta.4</Badge>
            <Lumeo.Text Size="sm" Color="muted">March 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Checkbox: Label and Description parameters with auto-Id for form label association</li>
                <li>RadioGroupItem: Description text support for helper text below radio options</li>
                <li>Steps: Error state per step with red X icon, custom Icon slot for any step indicator</li>
                <li>Popover: Arrow support (ShowArrow parameter) matching Tooltip arrow pattern</li>
                <li>48 new unit tests covering all upgraded component features (1,124 total tests)</li>
                <li>Form Validation guide — DataAnnotations, custom validation, complete registration form example</li>
                <li>Contributing guide — setup, component creation conventions, testing, code style</li>
                <li>"When to Use" and "Related Components" sections on 62 more component pages (82 total)</li>
                <li>API reference tables now complete on all 136 component documentation pages</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Home page stats corrected (75→103 components, 7→8 themes)</li>
                <li>Chart patterns integrated into Patterns page with filter category</li>
                <li>All hardcoded colors replaced with CSS variables (Avatar, Statistic, Result, KanbanCard)</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>MentionPage Razor escape for @@user syntax</li>
                <li>Statistic and Result test assertions updated for new CSS variable colors</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.0.0-beta.5 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.0.0-beta.5</Badge>
            <Lumeo.Text Size="sm" Color="muted">March 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Updated NuGet package description (90→103 components, added test count and features)</li>
                <li>Updated README with accurate stats, all component categories, and install command</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.0.0-beta.3 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.0.0-beta.3</Badge>
            <Lumeo.Text Size="sm" Color="muted">March 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Dialog size variants (Sm, Default, Lg, Xl, Full) and Scrollable content mode</li>
                <li>Drawer multi-position support (Top, Right, Bottom, Left) with direction-aware swipe</li>
                <li>Alert: Title, Description, Icon slots, ShowIcon with default icons per variant, AutoDismiss timer</li>
                <li>Input: 3 size variants (Sm, Default, Lg) and Clearable mode with X button</li>
                <li>Tooltip arrow support with configurable Offset and fade animation</li>
                <li>Badge: Pulse ping animation on dot variant and Icon slot</li>
                <li>Accordion: DefaultValues for initially open items and Disabled items</li>
                <li>Skeleton: Wave/Shimmer animation variant alongside existing Pulse</li>
                <li>Spinner: Dots and Bars variants, Label text, and Color override</li>
                <li>HoverCard: Side parameter (Top/Bottom positioning)</li>
                <li>Tabs: Disabled tabs with aria-disabled and TrailingContent on TabsList</li>
                <li>Button: FullWidth mode, LeftIcon and RightIcon slots</li>
                <li>Progress: Circular SVG variant with ShowValue and configurable StrokeWidth</li>
                <li>Avatar: Square shape option and Status indicator (Online, Offline, Away, Busy)</li>
                <li>Switch: Loading spinner state and OnLabel/OffLabel text</li>
                <li>Select: Disabled items and Placeholder text on trigger</li>
                <li>Combobox: EmptyText for no-results state and Creatable mode</li>
                <li>NumberInput: Arrow key and mouse wheel support, Prefix/Suffix text</li>
                <li>Textarea: Character count display, MaxLength indicator, Resize control</li>
                <li>New Accessibility guide page with ARIA roles, keyboard patterns, and focus management docs</li>
                <li>New Changelog page with full release history</li>
                <li>API reference tables for 30 additional component documentation pages (55 total)</li>
                <li>"When to Use", "Keyboard Interactions", and "Related Components" sections on 20 component pages</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Animation keyframes and utility classes now ship in lumeo.css for NuGet consumers</li>
                <li>Production-quality spring easing curves (<code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">cubic-bezier(0.16, 1, 0.3, 1)</code>) on all animations</li>
                <li>Rating colors now use themeable <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">--color-rating</code> CSS variable instead of hardcoded yellow</li>
                <li>Accessibility and Changelog pages added to sidebar navigation</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Broken animations for NuGet package consumers (keyframes were only in docs site)</li>
                <li>Missing <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">animate-toast-in</code> — Toast slide-in animation was never defined</li>
                <li>Added aria-labels to PasswordInput toggle, TagInput close buttons, DatePicker clear button, and Carousel navigation</li>
                <li>Rating keyboard navigation with Arrow keys and improved star labels</li>
                <li>5 failing tests updated for new Spinner, Avatar, and Select component structures</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.0.0-beta.2 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.0.0-beta.2</Badge>
            <Lumeo.Text Size="sm" Color="muted">March 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>14 new components: Cascader, Color Picker, Date Range Picker, Date Time Picker, Filter, Image Compare, Inplace Editor, Input Mask, Kanban, Mega Menu, Mention, Number Input, Password Input, Sortable List</li>
                <li>DataGrid enhancements: column menu, inline editing, column filtering, and toolbar improvements</li>
                <li>Keyboard shortcuts: <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">R</code> to shuffle themes, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Ctrl+D</code> for dark mode, <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Ctrl+/</code> for shortcuts help</li>
                <li>Redesigned WASM loader with animated splash screen, brand name, and ripple animation</li>
                <li>Floating navbar and floating sidebar design for the docs site</li>
                <li>NuGet package icon (Lumeo logo)</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>All UI corners now respect the CSS radius variable, enabling zero-radius presets like Lyra</li>
                <li>Customizer sidebar moved to header button with <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">Ctrl+B</code> toggle shortcut</li>
                <li>Replaced raw HTML elements with Lumeo components across 113 doc files</li>
                <li>Converted raw HTML to Lumeo components in 7 pattern files</li>
                <li>QR Code live preview, theme persistence, and search palette enhancements</li>
                <li>CommandEmpty now always renders regardless of Command context</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Customizer radius bug where radius values did not apply correctly</li>
                <li>Mobile docs improvements and API table horizontal scrolling</li>
                <li>Floating nav sticky positioning (moved animation off root element)</li>
                <li>Splash screen CSS compatibility with Tailwind CDN</li>
                <li>Em dash encoding issues in page titles</li>
                <li>FormWizardPattern closing tag mismatch</li>
                <li>Link/a tag mismatches in LinkPage and Home</li>
                <li>UTF-8 BOM in Home.razor</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v1.0.0-beta.1 -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge>v1.0.0-beta.1</Badge>
            <Lumeo.Text Size="sm" Color="muted">February 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>API reference tables for 25 component documentation pages</li>
                <li>Programmatic OverlayService for opening dialogs, sheets, and drawers from code</li>
                <li>Kbd component and KeyboardShortcutService for global keyboard shortcuts</li>
                <li>Scrollspy component for tracking scroll position and highlighting nav items</li>
                <li>926 bUnit tests across 50+ components and 4 services (expanded from 240)</li>
                <li>CI workflow to run tests automatically on push and PR</li>
                <li>Toast position support (top-left, top-right, bottom-left, bottom-right, etc.)</li>
                <li>19 new patterns on the Patterns page including drag-and-drop Kanban board</li>
                <li>Live world map demo for GeoMapChart</li>
                <li>Live interactive demos for all 5 service documentation pages</li>
                <li>New components: blocks section, filter docs, and comprehensive UI improvements</li>
                <li>Custom scrollbar styling</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Improved</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Redesigned Themes customizer sidebar to match reui.io design</li>
                <li>Redesigned Themes and Patterns pages with new layout</li>
                <li>Replaced inline accordion sections with flyout popover menus in the customizer</li>
                <li>Default theme changed from Orange to Zinc to match Lumeo logo</li>
                <li>Softened chunky borders across overlay components</li>
                <li>Docs favicon updated to dark Lumeo icon SVG</li>
                <li>All services registered via single <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">AddLumeo()</code> call</li>
            </ul>
        </Stack>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Fixed</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>OtpInput <code class="rounded bg-muted px-1.5 py-0.5 text-xs font-mono text-foreground">eval()</code> XSS vulnerability patched; added IAsyncDisposable</li>
                <li>9 failing tests for Calendar, OtpInput, and FileUpload</li>
                <li>Chart rendering: dropped source-gen serializer, resolved CSS variable colors</li>
                <li>Chart serialization and WordCloud rendering errors</li>
                <li>EChartCalendar range conflict and chart error handling</li>
                <li>Z-index issues for overlay components, theme loading, and toast variants</li>
                <li>Hanging ThemeServiceTests and stale Dialog/AlertDialog assertions</li>
                <li>Duplicate ondragover attributes in KanbanPattern</li>
                <li>Patterns page layout and broken CSS opacity modifiers</li>
                <li>GitHub Pages deployment issues with Tailwind v4 CDN and .wasm 503 errors</li>
            </ul>
        </Stack>
    </Stack>

    <Separator Class="my-4" />

    <!-- v0.1.0 (Initial Release) -->
    <Stack Gap="4">
        <div class="flex items-center gap-3">
            <Badge Variant="Badge.BadgeVariant.Outline">v0.1.0</Badge>
            <Lumeo.Text Size="sm" Color="muted">January 2026</Lumeo.Text>
        </div>

        <Stack Gap="3">
            <Heading Level="3" Size="sm" Class="font-semibold">Added</Heading>
            <ul class="space-y-1.5 text-sm list-disc list-inside text-muted-foreground">
                <li>Initial release with 45+ shadcn-inspired Blazor components</li>
                <li>Full docs site with component pages, live demos, and code snippets</li>
                <li>15 chart types powered by ECharts (Bar, Line, Area, Pie, Donut, Radar, Gauge, Treemap, Sunburst, Heatmap, Sankey, Funnel, and more)</li>
                <li>Patterns gallery page with reusable UI pattern examples</li>
                <li>Theme customizer with multiple color schemes and dark mode support</li>
                <li>10 additional chart types: Scatter, Candlestick, Box Plot, Word Cloud, Geo Map, Calendar Heatmap, Theme River, Graph, Parallel, Radial</li>
                <li>9 full-page pattern demos</li>
                <li>Layout primitives: Stack, Flex, Grid, Container, Center, Spacer</li>
                <li>Typography components: Heading, Text, Code, Link</li>
                <li>Form components: Button, Checkbox, Combobox, Date Picker, File Upload, Form, Input, Label, OTP Input, Radio Group, Select, Slider, Switch, Textarea, Toggle, Toggle Group</li>
                <li>Overlay components: Alert Dialog, Command, Context Menu, Dialog, Drawer, Dropdown Menu, Hover Card, Popover, Sheet, Tooltip</li>
                <li>Navigation components: Breadcrumb, Menubar, Navigation Menu, Pagination, Sidebar, Steps</li>
                <li>Data Display components: Avatar, Data Table, Table, Rating, Statistic, Timeline</li>
                <li>Feedback components: Alert, Badge, Empty State, Progress, Skeleton, Spinner, Toast</li>
                <li>ToastService, ThemeService, and ComponentInteropService</li>
                <li>GitHub Pages deployment pipeline</li>
            </ul>
        </Stack>
    </Stack>

</Stack>
</Container>
