**Minor release.** New display components (`stage-card`, `activity-row`),
an `intent` color axis on `stat` and `progress`, deterministic
`avatar from-initials`, badge `tooltip` / `leadingIcon` support, a
`md-compact` size tier on inputs and buttons, and chart tooltip value
formatting (`valueDecimals` / `valuePrefix` / `valueSuffix`). Plus
reading-primitive `boundary` scoping, a `hero tightOnMobile` option,
menubar / navigation-menu dropdown positioning fixes, sortable-table
fixes, and a round of mobile-layout polish. Additive-only — every
existing template renders identically. No back-compat breaks.

### Added

- **New `<x-wirekit::stage-card>` component.** A pipeline / kanban / roadmap stage card: an intent-colored left stripe, a faintly intent-tinted body, an optional item-count pill (announced as "N items"), and an optional progress bar. Replaces the per-column inline-style block dashboards previously hand-rolled. `role="group"` + `aria-label` from the stage `label`.
- **New `<x-wirekit::activity-row>` component.** An activity-feed / timeline / changelog row: a kind-colored leading dot, optional bold actor, content, right-aligned timestamp, and a `badge` slot. The `kind` → color map (commit / merge / deploy / comment / system / user) is extensible via `config('wirekit.components.activity-row.kinds')`; unknown kinds fall back to a muted dot. The dot is decorative; the kind is exposed as a visually-hidden label.
- **`<x-wirekit::stat intent="…">` KPI-tile chrome.** Setting `intent` (primary / success / warning / danger / info / neutral) gives the stat an intent-colored left stripe + a faintly tinted body and turns it into a labeled `role="group"`. Default (no `intent`) renders the existing plain card surface unchanged.
- **`<x-wirekit::progress intent="…">`.** `intent` is now the canonical color axis (matching badge / button / alert), adding `info` and `neutral`. `variant` keeps working as a back-compat alias; `intent` wins when both are set.
- **`<x-wirekit::avatar from-initials>`.** Derives a deterministic, theme-independent background color from the initials hash (AA-contrast background + white text) so the same person always renders the same color. `WireKit::avatarPaletteFor($key)` exposes the same palette for custom inline chips.
- **`size="md-compact"` on `<x-wirekit::input>`, `<x-wirekit::select>`, and `<x-wirekit::button>`.** A 2.25rem middle tier between `sm` and `md` for dense list/filter toolbars.
- **`<x-wirekit::badge>` gains `tooltip` and `leadingIcon`.** `tooltip` sets a discoverable native `title`; `leadingIcon` renders a decorative status glyph before the label. Every badge also gains a subtle depth shadow (inset ring + faint drop shadow) so it reads less flat.
- **`<x-wirekit::card.body :padded="false">`.** Opt out of body padding for edge-to-edge content (a flush hero image or full-bleed table).
- **`<x-wirekit::dropdown>` accepts an optional `<x-slot:trigger>` named slot.** When the slot is supplied, the parent auto-wraps the trigger element in `<x-wirekit::dropdown.trigger>` and the default slot in `<x-wirekit::dropdown.panel>`, so the canonical composition no longer has to be repeated per dropdown. The explicit-sub-component form keeps working unchanged — use whichever reads better for the page. Both forms produce identical rendered HTML and ARIA wiring.
- **Reading-* `boundary` prop on `reading-progress`, `reading-spine`, and `reading-bookmark`.** Default `null` preserves the existing viewport-pinned behavior. Pass `boundary="container"` to scope the primitive to its nearest positioned ancestor instead — useful when embedding a reading surface inside a modal body, sidebar pane, preview iframe, or Livewire panel. The progress bar swaps to `position: sticky`; the spine and bookmark swap to Tailwind `absolute`. Documented under "Scoping a primitive to its parent" with the required wrapper recipe.
- **`<x-wirekit::hero tightOnMobile>` opt-in prop.** When `true` on a `size="lg"` hero, mobile padding drops from `--space-wk-section-md` (5 rem) to `--space-wk-section-sm` (3 rem) while desktop keeps `--space-wk-section-lg` (7 rem) — useful for dark-variant heroes with gradient overlays where the previous 5 rem mobile padding showed as a visible dead-zone on iPhone-class viewports. No-op for `size="sm"` and `size="md"` (mobile already runs at the tightest tier).
- **New `docs/strict-validation.md` page.** A context-matrix reference for WireKit's prop-validation gate: when does an invalid value throw vs log + fall back, how to grep the Laravel log for silent typos that shipped, the env-knob recipe to opt staging into fail-loudly mode, and a Pest test pattern for asserting strict-mode rejection.
- **`<x-wirekit-chart>` gains `valueDecimals`, `valuePrefix`, and `valueSuffix` (ApexCharts).** Round raw float y-values in the tooltip to N decimal places and wrap them with a unit or currency affix — `valueDecimals="2" valueSuffix=" ms"` turns a hovered `50.523626895740676` into `50.52 ms`. All three default to `null` (raw value, no affix), so existing charts are unchanged. No-op on the Chart.js adapter.
- **`.wk-reading-content` responsive article wrapper class.** Wrap the `<article>` in a reading-spine composition with `class="wk-reading-content"` and it reserves the spine's inline-end gutter **only at `md`+** (where the spine is visible) while using the **full width below `md`** (where `reading-spine` defaults to `hideBelow="md"` and is hidden). Replaces the hand-rolled inline `max-width: calc(100% - 12rem)` / `padding-right: 12rem` reserve, which — being an inline style — could not `@media` and so stayed applied on phones, collapsing the article to a single character per line. Override the gutter with `--reading-content-spine-gutter` (default `12rem`).

### Fixed

- **`<x-wirekit::table alpine-sort>` now actually reorders rows when a header is clicked.** The Alpine sort read the table body from the clicked `<th>` instead of the table root, so it found no `<tbody>` and bailed out — the sort-direction arrow flipped but the rows never moved. Clicking a sortable header now sorts the rows as expected.
- **`<x-wirekit::table alpine-sort>` sorts date and other non-numeric columns correctly.** Numeric detection used `parseFloat`, which read a leading number out of any string — so every ISO date (`2025-01-15`) collapsed to its year (`2025`), making a whole date column compare as equal and never reorder. Values are now only compared numerically when the entire cell is a number; dates, sizes (`12px`), prices (`$5`), and counts (`3 items`) fall through to locale-aware comparison, which orders ISO dates chronologically.
- **`<x-wirekit::data-list>` no longer renders a doubled border under the last row.** The inter-row separator was an inline `border-bottom` on every item, including the last — which doubled against the container's own bottom border into a 2px seam. The separator now omits the final row.
- **Muted text stays legible on `<x-wirekit::hero>` / `<x-wirekit::cta>` `variant="dark"` and `variant="accent"`.** A nested `<x-wirekit::text variant="muted">` previously resolved against the light-theme muted color on the near-black surface — invisible. It now reads a high-contrast muted color scoped to the dark/accent surface, auto-adapting to any theme.
- **Raw `<td>` / `<th>` cells inside `<x-wirekit::table>` now receive readable padding.** Previously a raw table (cells not wrapped in the `table.*` sub-components) rendered cramped flush-to-border; raw cells now get the same padding `table.td` uses, complementing the existing debug-mode warning. An edge-to-edge `card.body` pads a flush raw table's cells too.
- **`<x-wirekit::range-slider>` is more usable on touch devices.** The thumb grows to a comfortable 28px on coarse pointers, `touch-action: none` stops a horizontal drag from scrolling the page, and discrete sliders (`step > 1`) with a readable step count render snap tick-marks.
- **`wirekit:export-json` manifest no longer false-positively flags class-side public properties as required template slots.** Class-based components (currently `<x-wirekit-chart>`) expose public properties like `$alpineComponent` and `$chartConfig` that the Blade template references as `{{ $name }}`. The slot-detection layer treated these as `<x-slot:...>` requirements, so the manifest's chart entry emitted `{name: alpineComponent, required: true}` — leading AI tooling to wrap chart content in `<x-slot:alpineComponent>` and produce broken Blade. The exclude is applied to both `wirekit:export-json` and the `.wirekit-schema.json` file written by `wirekit:install`.
- **`<x-wirekit::tabs>` tab bar is now horizontally scrollable on narrow viewports.** The tablist previously used a non-scrolling `inline-flex`, so a layout with five or more tabs overflowed the viewport on mobile with no way to reach the off-screen tabs. The tablist now caps at the container width and scrolls (the standard mobile tab-bar pattern); tabs keep their natural label width (`shrink-0` + `whitespace-nowrap`) instead of squishing. Desktop rendering is unchanged. Keyboard access is preserved — `role="tablist"` with arrow-key navigation already owns its keyboard model.
- **`<x-wirekit::data-list>` labels and values wrap long single tokens instead of overflowing.** A `<dt>` carried `white-space: nowrap`, so a long single-word label (e.g. a long compound noun, a URL, or a file path) bled past its 33% track and pushed the row wider than its container on narrow viewports. Both `<dt>` and `<dd>` now use `overflow-wrap: anywhere` + `min-width: 0`.
- **`<x-wirekit::menubar>` and `<x-wirekit::navigation-menu>` dropdown panels now open at their trigger even inside a transformed ancestor.** Both teleport their `position: fixed` panels to `<body>` (like modal / drawer / context-menu / tooltip), so a menubar or navigation-menu nested inside any element with a `transform` / `filter` / `perspective` / `contain` (which establishes a containing block for fixed descendants) no longer opens its dropdown far from the trigger. Keyboard navigation, outside-click close, and hover behavior are unchanged.
- **`<x-wirekit::sparkline>` no longer clips sibling cards in a multi-column KPI grid on mobile.** A block-mode sparkline now carries `min-width: 0; overflow: hidden` so the ApexCharts canvas's fixed intrinsic width can't floor a surrounding CSS-grid track — previously a `repeat(2, 1fr)` KPI grid kept both columns at the chart's ~300px width and pushed the right-hand cells off-screen on narrow viewports.
- **`<x-wirekit::toolbar>` wraps to a second row on narrow viewports instead of cramming.** The leading (search) cluster used `min-w-0`, so on a phone the search field collapsed toward zero width while the filter selects and action button squeezed beside it. The leading cluster now keeps a usable minimum width (`min-w-[min(100%,14rem)]`, growing to fill spare space) and the default-slot wrapper carries `flex-wrap justify-between`, so the filters and actions drop to a second row once they no longer fit beside a usable search field. Desktop layout is unchanged.

### Changed

- **`<x-wirekit::reading-progress>`, `<x-wirekit::reading-spine>`, `<x-wirekit::reading-bookmark>` `boundary` prop now accepts CSS-selector strings** in addition to `'container'`. Pass a selector like `boundary="#article"` and the Alpine init logs a `console.warn` when no ancestor matches — runtime verification that the developer's intended ancestor exists. The default (`null`) and `'container'` shapes are unchanged.
- **`<x-wirekit::reading-shell>` gains a `boundary` prop** that pass-through to every composed primitive — set once on the shell and the entire reading surface stays inside the parent positioned ancestor.
- **`<x-wirekit::icon>` accessibility hardening** — when the underlying SVG package source ships a baked-in `aria-hidden="true"` on the SVG root (blade-heroicons does this), the component now strips that attribute and re-emits exactly one resolved aria-hidden value matching the developer's intent. Fixes a screen-reader regression where icons marked informative via `aria-label` / `role="img"` were silently skipped by assistive tech.
- **`IconResolver` fallthrough** now correctly resolves bare heroicon-family names like `<x-wirekit::icon name="pencil-square">` to `heroicon-m-pencil-square`. Previously the fallthrough probed a binding key that has never existed in blade-ui-kit/blade-icons, so every such call threw an "Unknown icon alias" exception.
- **`wirekit:doctor` (`wirekit:verify`) gains an environment-tier silent-typo log scan.** Walks `storage/logs/laravel*.log` for `WireKit [...]` ERROR / WARNING lines emitted by the prop-validation fallback path and surfaces them as a WARN with example lines. SAFE-DEGRADE at every failure mode (missing log file, non-file LOG_CHANNEL, custom log channels) — the helper NEVER blocks the doctor's exit code. Opt-out via `wirekit.doctor.scan_logs` config or `WIREKIT_DOCTOR_SCAN_LOGS=false`.
- **`wirekit:export-json` manifest entries carry a new `component_kind` field** (`anonymous` | `class`) so downstream AI tooling can branch on the value when generating composition shapes — anonymous components accept `<x-slot:name>`; class-based components accept only constructor-mapped props.
- **Hero docs ship a new "Tight mobile padding for `size='lg'`" subsection** demonstrating the `tightOnMobile` prop with a preview, plus a new `tightOnMobile` row on the Props table.
- **Reading docs gain a new "Scoping a primitive to its parent" subsection** with a contained-surface preview block and a 3-row behavior matrix covering `null` / `'container'` / `'<css-selector>'` boundary shapes.
- **Dropdown docs gain a new "Composition forms" section** walking both the quick-form (`<x-slot:trigger>`) and the explicit-sub-component path with previews and a do-not-mix tip.
- **Icon docs + Variants & Intents docs** carry explicit cross-reference notes documenting that the four status names (`info`, `success`, `warning`, `danger`) appear deliberately in both the icon-alias namespace and the canonical intent-value enum — the shared keyword is a feature, not a namespace collision.
- **Chart docs Troubleshooting section** names the silent-failure mode (chart adapter bundle not loaded at all → Alpine references an unknown factory) and contrasts it with the in-DOM advisory panel rendered when the adapter bundle IS loaded but the underlying chart library (`window.ApexCharts` / `window.Chart`) is missing. Chart.js + ApexCharts missing-library `console.error` now emits ONCE per page-load (deduplicated) instead of once per chart instance.