/* alert.templ */
/* Entrance animation */
		@keyframes alert-enter {
			from {
				opacity: 0;
				transform: translateY(4px);
			}
			to {
				opacity: 1;
				transform: translateY(0);
			}
		}

		/* Base */
		.alert {
			position: relative;
			display: grid;
			width: 100%;
			gap: 0.25rem;
			border-radius: var(--radius-lg);
			border: 1px solid var(--border);
			padding: 0.625rem 0.75rem;
			text-align: left;
			font-size: 0.875rem;
			line-height: 1.25rem;
			/* Entrances ease out — same overshoot curve as the rest of the family */
			animation: alert-enter 250ms cubic-bezier(0.22, 1.1, 0.36, 1) both;
		}

		.alert:has(> svg) {
			grid-template-columns: auto 1fr;
			column-gap: 0.5rem;
		}

		.alert > svg {
			grid-row: span 2;
			translate: 0 0.125rem;
			color: currentColor;
			width: 1rem;
			height: 1rem;
		}

		/*
		 * An action is a real grid column, not an absolutely positioned box
		 * over a fixed 4.5rem gutter — a reserved gutter either wastes space
		 * or lets a wider action overlap the text. The action is placed
		 * explicitly (last column, all rows), so title and description
		 * auto-place around it.
		 */
		.alert:has([data-slot="alert-action"]) {
			grid-template-columns: 1fr auto;
			column-gap: 0.5rem;
		}

		.alert:has(> svg):has([data-slot="alert-action"]) {
			grid-template-columns: auto 1fr auto;
			column-gap: 0.5rem;
		}

		/* Variant: default */
		.alert-default {
			background-color: var(--card);
			color: var(--card-foreground);
		}

		/* Variant: destructive */
		.alert-destructive {
			background-color: color-mix(in oklch, var(--destructive) 5%, var(--card));
			color: var(--destructive);
		}

		.alert-destructive .alert-description {
			color: color-mix(in oklch, var(--destructive) 90%, transparent);
		}

		.alert-destructive > svg {
			color: currentColor;
		}

		/* AlertTitle */
		.alert-title {
			font-weight: 500;
			line-height: 1.375;
		}

		.alert:has(> svg) .alert-title {
			grid-column-start: 2;
		}

		.alert-title a {
			text-decoration: underline;
			text-underline-offset: 3px;
		}

		.alert-title a:hover {
			color: var(--foreground);
		}

		/* AlertDescription */
		.alert-description {
			font-size: 0.875rem;
			color: var(--muted-foreground);
			text-wrap: balance;
		}

		@media (min-width: 768px) {
			.alert-description {
				text-wrap: pretty;
			}
		}

		.alert-description a {
			text-decoration: underline;
			text-underline-offset: 3px;
		}

		.alert-description a:hover {
			color: var(--foreground);
		}

		.alert-description p:not(:last-child) {
			margin-bottom: 1rem;
		}

		/* AlertAction — last column, centered across every text row */
		.alert-action {
			grid-column: -2;
			grid-row: 1 / -1;
			align-self: center;
			justify-self: end;
		}

		/* Dark mode */
		.dark .alert-destructive {
			background-color: color-mix(in oklch, var(--destructive) 10%, var(--card));
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.alert {
				animation: none;
			}
		}

/* avatar.templ */
/* Avatar: Base */
		.avatar {
			position: relative;
			display: inline-flex;
			width: 2rem;
			height: 2rem;
			flex-shrink: 0;
			border-radius: 9999px;
			user-select: none;
			vertical-align: middle;
		}

		/* Subtle border overlay for definition against backgrounds */
		.avatar::after {
			content: "";
			position: absolute;
			inset: 0;
			border-radius: 9999px;
			border: 1px solid var(--border);
			mix-blend-mode: darken;
			pointer-events: none;
		}

		/* Size: sm */
		.avatar-sm {
			width: 1.5rem;
			height: 1.5rem;
		}

		/* Size: lg */
		.avatar-lg {
			width: 2.5rem;
			height: 2.5rem;
		}

		/* Avatar Image */
		.avatar-image {
			aspect-ratio: 1 / 1;
			width: 100%;
			height: 100%;
			border-radius: 9999px;
			object-fit: cover;
		}

		/* Hide broken image when src fails to load */
		.avatar-image[data-error="true"] {
			display: none;
		}

		/* Avatar Fallback */
		.avatar-fallback {
			display: flex;
			width: 100%;
			height: 100%;
			align-items: center;
			justify-content: center;
			border-radius: 9999px;
			background-color: var(--muted);
			color: var(--muted-foreground);
			font-size: 0.875rem;
			font-weight: 500;
			line-height: 1;
		}

		.avatar-sm .avatar-fallback {
			font-size: 0.75rem;
		}

		.avatar-lg .avatar-fallback {
			font-size: 1rem;
		}

		/* Fallback hidden when image loaded successfully */
		.avatar-image:not([data-error="true"]) ~ .avatar-fallback {
			display: none;
		}

		/* Avatar Badge */
		.avatar-badge {
			position: absolute;
			right: 0;
			bottom: 0;
			z-index: 10;
			display: inline-flex;
			align-items: center;
			justify-content: center;
			border-radius: 9999px;
			background-color: var(--primary);
			color: var(--primary-foreground);
			box-shadow: 0 0 0 2px var(--background);
			user-select: none;
		}

		/* Badge sizes per avatar size */
		.avatar[data-size="sm"] .avatar-badge {
			width: 0.5rem;
			height: 0.5rem;
		}
		.avatar[data-size="sm"] .avatar-badge svg {
			display: none;
		}

		.avatar[data-size="default"] .avatar-badge {
			width: 0.625rem;
			height: 0.625rem;
		}
		.avatar[data-size="default"] .avatar-badge svg {
			width: 0.5rem;
			height: 0.5rem;
		}

		.avatar[data-size="lg"] .avatar-badge {
			width: 0.75rem;
			height: 0.75rem;
		}
		.avatar[data-size="lg"] .avatar-badge svg {
			width: 0.5rem;
			height: 0.5rem;
		}

		/* Avatar Group */
		.avatar-group {
			display: flex;
		}
		.avatar-group > .avatar {
			margin-left: -0.5rem;
			box-shadow: 0 0 0 2px var(--background);
		}
		.avatar-group > .avatar:first-child {
			margin-left: 0;
		}

		/* Avatar Group Count */
		.avatar-group-count {
			position: relative;
			display: flex;
			width: 2rem;
			height: 2rem;
			flex-shrink: 0;
			align-items: center;
			justify-content: center;
			border-radius: 9999px;
			background-color: var(--muted);
			color: var(--muted-foreground);
			font-size: 0.875rem;
			font-weight: 500;
			margin-left: -0.5rem;
			box-shadow: 0 0 0 2px var(--background);
		}

		.avatar-group-count svg {
			width: 1rem;
			height: 1rem;
		}

		/* Group count adapts to avatar sizes in the group */
		.avatar-group:has(.avatar-sm) .avatar-group-count {
			width: 1.5rem;
			height: 1.5rem;
			font-size: 0.75rem;
		}
		.avatar-group:has(.avatar-sm) .avatar-group-count svg {
			width: 0.75rem;
			height: 0.75rem;
		}

		.avatar-group:has(.avatar-lg) .avatar-group-count {
			width: 2.5rem;
			height: 2.5rem;
		}
		.avatar-group:has(.avatar-lg) .avatar-group-count svg {
			width: 1.25rem;
			height: 1.25rem;
		}

		/* Image load transition */
		.avatar-image {
			opacity: 0;
			transition: opacity 200ms ease-out;
		}
		.avatar-image[data-loaded="true"] {
			opacity: 1;
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.avatar-image {
				transition-duration: 75ms;
				opacity: 1;
			}
		}

		/* Dark mode */
		.dark .avatar::after {
			mix-blend-mode: lighten;
		}

/* badge.templ */
/* Base */
		.badge {
			display: inline-flex;
			height: 1.5rem;
			width: fit-content;
			flex-shrink: 0;
			align-items: center;
			justify-content: center;
			gap: 0.25rem;
			overflow: hidden;
			border-radius: 9999px;
			border: 1px solid transparent;
			padding: 0.125rem 0.5rem;
			font-size: 0.75rem;
			line-height: 1rem;
			font-weight: 500;
			white-space: nowrap;
			transition-property: background-color, color, border-color, box-shadow, opacity, transform;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
			/*
			 * Shadow slot — badges are labels, not elevated surfaces, so solid
			 * variants get only the lit-edge insets (no drop shadow), matching
			 * checked checkbox/radio faces. Rings compose on top of the slot.
			 */
			--badge-shadow: 0 0 rgba(0, 0, 0, 0);
			box-shadow: var(--badge-shadow);
		}

		.badge-default,
		.badge-secondary {
			--badge-shadow: var(--highlight-top), var(--shade-bottom);
		}

		/* The badge itself is never focusable — an interactive badge is a span
		   inside a link, so the ring has to be drawn from the anchor's focus. */
		.badge:focus-visible,
		a:focus-visible > .badge {
			border-color: var(--ring);
			box-shadow: var(--badge-shadow), 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
			outline: none;
		}
		a:focus-visible:has(> .badge) {
			outline: none;
		}

		.badge[aria-invalid="true"] {
			border-color: var(--destructive);
			box-shadow: var(--badge-shadow), 0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
		}

		.badge svg {
			pointer-events: none;
			width: 0.75rem !important;
			height: 0.75rem !important;
		}

		/* Icon padding: more breathing room on the side with an icon */
		.badge:has(> [data-icon="inline-start"]) {
			padding-left: 0.375rem;
		}
		.badge:has(> [data-icon="inline-end"]) {
			padding-right: 0.375rem;
		}

		/* Interactive badges (inside links). The bleed takes the 1.5rem badge to a
		   2rem hit target without changing where it sits (Fitts). */
		a:has(> .badge) {
			position: relative;
			display: inline-flex;
		}
		a:has(> .badge)::before {
			content: "";
			position: absolute;
			inset: -0.25rem;
		}
		a:active > .badge {
			transform: scale(0.97);
			transition-duration: 100ms;
		}

		/* Variant: default */
		.badge-default {
			background-color: var(--primary);
			color: var(--primary-foreground);
		}
		a:hover > .badge-default {
			background-color: color-mix(in oklch, var(--primary) 80%, transparent);
		}

		/* Variant: secondary */
		.badge-secondary {
			background-color: var(--secondary);
			color: var(--secondary-foreground);
		}
		a:hover > .badge-secondary {
			background-color: color-mix(in oklch, var(--secondary) 80%, transparent);
		}

		/* Variant: destructive */
		.badge-destructive {
			background-color: color-mix(in oklch, var(--destructive) 15%, transparent);
			color: var(--destructive);
		}
		.badge-destructive:focus-visible,
		a:focus-visible > .badge-destructive {
			box-shadow: var(--badge-shadow), 0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
		}
		a:hover > .badge-destructive {
			background-color: color-mix(in oklch, var(--destructive) 25%, transparent);
		}

		/* Variant: outline */
		.badge-outline {
			border-color: var(--border);
			color: var(--foreground);
		}
		/* Hover keeps full-contrast text — dropping to muted would lose legibility
		   at the exact moment the user signals intent */
		a:hover > .badge-outline {
			background-color: var(--muted);
			color: var(--foreground);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.badge {
				transition-duration: 75ms;
			}
			a:active > .badge {
				transform: none;
			}
		}

		/* Dark mode */
		.dark .badge-destructive {
			background-color: color-mix(in oklch, var(--destructive) 20%, transparent);
		}
		.dark a:hover > .badge-destructive {
			background-color: color-mix(in oklch, var(--destructive) 30%, transparent);
		}
		.dark .badge-destructive:focus-visible,
		.dark a:focus-visible > .badge-destructive {
			box-shadow: var(--badge-shadow), 0 0 0 3px color-mix(in oklch, var(--destructive) 40%, transparent);
		}
		.dark .badge[aria-invalid="true"] {
			box-shadow: var(--badge-shadow), 0 0 0 3px color-mix(in oklch, var(--destructive) 40%, transparent);
		}

/* breadcrumb.templ */
/* Base nav */
		.breadcrumb {
			font-size: 0.875rem;
			line-height: 1.25rem;
		}

		/* List */
		.breadcrumb-list {
			display: flex;
			flex-wrap: wrap;
			align-items: center;
			gap: 0.375rem;
			overflow-wrap: break-word;
			list-style: none;
			padding: 0;
			margin: 0;
			color: var(--muted-foreground);
		}

		/* Item */
		.breadcrumb-item {
			display: inline-flex;
			align-items: center;
			gap: 0.25rem;
		}

		/* Link */
		.breadcrumb-link {
			color: var(--muted-foreground);
			text-decoration: none;
			transition-property: color;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
		}

		.breadcrumb-link:hover {
			color: var(--foreground);
		}

		/* Current page */
		.breadcrumb-page {
			font-weight: 400;
			color: var(--foreground);
		}

		/* Separator */
		.breadcrumb-separator {
			display: inline-flex;
			align-items: center;
		}

		.breadcrumb-separator svg {
			width: 0.875rem;
			height: 0.875rem;
			color: var(--muted-foreground);
		}

		/* Ellipsis */
		.breadcrumb-ellipsis {
			display: flex;
			align-items: center;
			justify-content: center;
			width: 1.25rem;
			height: 1.25rem;
		}

		.breadcrumb-ellipsis svg {
			width: 1rem;
			height: 1rem;
			color: var(--muted-foreground);
		}

		/* Screen reader only */
		.breadcrumb .sr-only {
			position: absolute;
			width: 1px;
			height: 1px;
			padding: 0;
			margin: -1px;
			overflow: hidden;
			clip: rect(0, 0, 0, 0);
			white-space: nowrap;
			border-width: 0;
		}

		/* Dark mode */
		.dark .breadcrumb-link:hover {
			color: var(--foreground);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.breadcrumb-link {
				transition-duration: 0ms;
			}
		}

/* button.templ */
/* Base */
		.btn {
			position: relative;
			display: inline-flex;
			flex-shrink: 0;
			align-items: center;
			justify-content: center;
			gap: 0.375rem;
			border-radius: var(--radius-lg);
			border: 1px solid transparent;
			/* No background-clip: padding-box here — the face must paint under
			   the transparent border, or the 1px gap shows the page through as
			   a light halo ring between the face and the cut shadow. */
			font-size: 0.875rem;
			line-height: 1.25rem;
			font-weight: 500;
			white-space: nowrap;
			transition-property: background-color, color, border-color, box-shadow, opacity, transform;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
			outline: none;
			user-select: none;
			cursor: pointer;
			text-decoration: none;
			color: inherit;
			/*
			 * Shadow slots — flat variants (ghost, link) keep the no-op value;
			 * raised variants opt in below. The ring is its own slot so the two
			 * compose in both directions: a ring never flattens the button, and
			 * a press never drops the ring. Rings set the slot, not box-shadow.
			 */
			--btn-shadow: 0 0 rgba(0, 0, 0, 0);
			--btn-shadow-pressed: 0 0 rgba(0, 0, 0, 0);
			--btn-ring: 0 0 rgba(0, 0, 0, 0);
			box-shadow: var(--btn-shadow), var(--btn-ring);
		}

		/*
		 * Raised variants get the full six-technique anatomy via
		 * --shadow-control: cut, ambient + top inner highlights, bottom
		 * shade, three layered depth shadows (visual-button-shadow-anatomy).
		 */
		.btn-default,
		.btn-outline,
		.btn-secondary,
		.btn-destructive {
			--btn-shadow: var(--shadow-control);
			--btn-shadow-pressed: var(--shadow-control-pressed);
		}

		/* Tinted faces swap to the cut-free variant (see base.templ) */
		.btn-secondary,
		.btn-destructive {
			--btn-shadow: var(--shadow-control-tint);
			--btn-shadow-pressed: var(--shadow-control-tint-pressed);
		}

		/* The raised faces that keep their own color get a quiet bevel.
		   Each face shows the half it can: the white crest reads on dark
		   faces, the shaded base on light ones — so one gradient serves
		   both modes. The primary face gets a stronger cut below. */
		.btn-outline,
		.btn-secondary,
		.btn-destructive {
			background-image: linear-gradient(
				to bottom,
				rgba(255, 255, 255, 0.06),
				rgba(255, 255, 255, 0) 45%,
				rgba(16, 24, 40, 0.05)
			);
		}

		.btn:focus-visible {
			border-color: var(--ring);
			--btn-ring: 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		.btn:disabled {
			pointer-events: none;
			opacity: 0.5;
		}

		.btn[aria-invalid="true"] {
			border-color: var(--destructive);
			--btn-ring: 0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
		}

		.btn svg {
			pointer-events: none;
			flex-shrink: 0;
			width: 1rem;
			height: 1rem;
		}

		.btn:active:not(:disabled),
		.btn.btn-pressed {
			transform: scale(0.97);
			transition-duration: 120ms;
			/* Depth collapses with the press — the button meets the surface.
			   The ring slot rides along so a keyboard press keeps its focus
			   ring, which this rule outweighs on specificity. */
			box-shadow: var(--btn-shadow-pressed), var(--btn-ring);
		}

		/* Variant: default */
		.btn-default {
			background-color: var(--primary);
			color: var(--primary-foreground);
			/* Technique 6, turned up into a full bevel: a white crest that
			   fades by mid-face and a shaded base rounding the bottom edge.
			   background-image, so the hover background-color still shows. */
			background-image: linear-gradient(
				to bottom,
				rgba(255, 255, 255, 0.12),
				rgba(255, 255, 255, 0.03) 40%,
				rgba(255, 255, 255, 0) 55%,
				rgba(16, 24, 40, 0.1)
			);
			/* Technique 5 — light text gets a hairline shadow for contrast */
			text-shadow: 0 1px 1px rgba(16, 24, 40, 0.2);
		}
		.btn-default:hover {
			background-color: color-mix(in oklch, var(--primary) 80%, transparent);
		}

		/* Variant: outline */
		.btn-outline {
			border-color: var(--border);
			background-color: var(--background);
		}
		.btn-outline:hover {
			background-color: var(--muted);
			color: var(--foreground);
		}
		.btn-outline[aria-expanded="true"] {
			background-color: var(--muted);
			color: var(--foreground);
		}

		/* Variant: secondary */
		.btn-secondary {
			background-color: var(--secondary);
			color: var(--secondary-foreground);
		}
		.btn-secondary:hover {
			background-color: color-mix(in oklch, var(--secondary) 80%, transparent);
		}
		.btn-secondary[aria-expanded="true"] {
			background-color: var(--secondary);
			color: var(--secondary-foreground);
		}

		/* Variant: ghost */
		.btn-ghost {
			background-color: transparent;
		}
		.btn-ghost:hover {
			background-color: var(--muted);
			color: var(--foreground);
		}
		.btn-ghost[aria-expanded="true"] {
			background-color: var(--muted);
			color: var(--foreground);
		}

		/* Variant: destructive */
		.btn-destructive {
			background-color: color-mix(in oklch, var(--destructive) 10%, transparent);
			color: var(--destructive);
		}
		.btn-destructive:hover {
			background-color: color-mix(in oklch, var(--destructive) 20%, transparent);
		}
		.btn-destructive:focus-visible {
			border-color: color-mix(in oklch, var(--destructive) 40%, transparent);
			--btn-ring: 0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
		}

		/* Variant: link */
		.btn-link {
			background-color: transparent;
			color: var(--primary);
			text-underline-offset: 4px;
		}
		.btn-link:hover {
			text-decoration: underline;
		}

		/* Size: default */
		.btn-size-default {
			height: 2.25rem;
			gap: 0.375rem;
			padding-left: 0.75rem;
			padding-right: 0.75rem;
		}

		/* Size: xs */
		.btn-size-xs {
			height: 1.75rem;
			gap: 0.25rem;
			border-radius: min(var(--radius-md), 10px);
			padding-left: 0.5rem;
			padding-right: 0.5rem;
			font-size: 0.75rem;
			line-height: 1rem;
		}
		.btn-size-xs svg {
			width: 0.75rem;
			height: 0.75rem;
		}
		/* Fitts — the 28px box is under the 32px minimum, so the pointer
		   target is expanded past the paint box without changing layout */
		.btn-size-xs::after,
		.btn-size-icon-xs::after {
			content: '';
			position: absolute;
			inset: -2px;
		}

		/* Size: sm */
		.btn-size-sm {
			height: 2rem;
			gap: 0.25rem;
			border-radius: min(var(--radius-md), 12px);
			padding-left: 0.625rem;
			padding-right: 0.625rem;
		}
		.btn-size-sm svg {
			width: 0.875rem;
			height: 0.875rem;
		}

		/* Size: lg */
		.btn-size-lg {
			height: 2.5rem;
			gap: 0.375rem;
			padding-left: 0.75rem;
			padding-right: 0.75rem;
		}

		/* Size: icon */
		.btn-size-icon {
			width: 2.25rem;
			height: 2.25rem;
			padding: 0;
		}

		/* Size: icon-xs */
		.btn-size-icon-xs {
			width: 1.75rem;
			height: 1.75rem;
			padding: 0;
			border-radius: min(var(--radius-md), 10px);
		}
		.btn-size-icon-xs svg {
			width: 0.75rem;
			height: 0.75rem;
		}

		/* Size: icon-sm */
		.btn-size-icon-sm {
			width: 2rem;
			height: 2rem;
			padding: 0;
			border-radius: min(var(--radius-md), 12px);
		}

		/* Size: icon-lg */
		.btn-size-icon-lg {
			width: 2.5rem;
			height: 2.5rem;
			padding: 0;
		}

		/* Rounded */
		.btn.rounded-full {
			border-radius: 9999px;
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.btn {
				transition-duration: 75ms;
			}
			.btn:active:not(:disabled),
			.btn.btn-pressed {
				transform: none;
			}
		}

		/* Dark mode */
		.dark .btn-outline {
			border-color: var(--input);
			background-color: color-mix(in oklch, var(--input) 30%, transparent);
		}
		.dark .btn-outline:hover {
			background-color: color-mix(in oklch, var(--input) 50%, transparent);
		}
		.dark .btn-ghost:hover {
			background-color: color-mix(in oklch, var(--muted) 50%, transparent);
		}
		.dark .btn-destructive {
			background-color: color-mix(in oklch, var(--destructive) 20%, transparent);
		}
		.dark .btn-destructive:hover {
			background-color: color-mix(in oklch, var(--destructive) 30%, transparent);
		}
		.dark .btn-destructive:focus-visible {
			--btn-ring: 0 0 0 3px color-mix(in oklch, var(--destructive) 40%, transparent);
		}
		.dark .btn[aria-invalid="true"] {
			border-color: color-mix(in oklch, var(--destructive) 50%, transparent);
			--btn-ring: 0 0 0 3px color-mix(in oklch, var(--destructive) 40%, transparent);
		}
		/* Dark-mode primary is a light face with dark text — a dark text
		   shadow would smear, not add contrast */
		.dark .btn-default {
			text-shadow: none;
		}

/* calendar.templ */
/* ── Root ── */
		.cal {
			--cal-cell: 2.2rem;
			--cal-radius: var(--radius-md);
			display: inline-flex;
			flex-direction: column;
			background: var(--background);
			padding: 0.5rem;
			width: fit-content;
			user-select: none;
		}

		/* ── Navigation ── */
		.cal-nav {
			display: flex;
			align-items: center;
			justify-content: space-between;
			gap: 0.25rem;
			margin-bottom: 0.25rem;
		}

		.cal-nav-btn {
			width: var(--cal-cell);
			height: var(--cal-cell);
		}

		.cal-title {
			font-size: 0.875rem;
			font-weight: 500;
			text-align: center;
			flex: 1;
			/* Tabular figures — the year must not reflow between months */
			font-variant-numeric: tabular-nums;
		}

		/* ── Grid ── */
		.cal-grid {
			width: 100%;
			border-collapse: collapse;
		}

		.cal-head {
			font-size: 0.8rem;
			font-weight: 400;
			color: var(--muted-foreground);
			text-align: center;
			width: var(--cal-cell);
			height: var(--cal-cell);
			padding: 0;
		}

		.cal-cell {
			padding: 1px;
			text-align: center;
		}

		/* ── Day button ── */
		.cal-day {
			position: relative;
			display: inline-flex;
			align-items: center;
			justify-content: center;
			width: var(--cal-cell);
			height: var(--cal-cell);
			border-radius: var(--cal-radius);
			border: 1px solid transparent;
			background: transparent;
			font-size: 0.875rem;
			line-height: 1;
			font-weight: 400;
			cursor: pointer;
			outline: none;
			padding: 0;
			font-family: inherit;
			color: var(--foreground);
			/* Tabular figures — a date grid is a numeric table; proportional
			   digits make the columns visibly ragged */
			font-variant-numeric: tabular-nums;
			transition-property: background-color, color, border-color, box-shadow;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
		}

		/* Anticipation — hover hints interactivity before click */
		.cal-day:hover:not(:disabled):not([data-selected]) {
			background: var(--muted);
		}

		.cal-day:focus-visible {
			border-color: var(--ring);
			box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		/* Follow Through — press feedback mirrors Button component */
		.cal-day:active:not(:disabled) {
			transform: scale(0.97);
			transition-duration: 100ms;
		}

		/*
		 * Von Restorff — today gets a unique dot indicator that persists
		 * across hover and selection states, acting as a spatial anchor
		 * the user can always find (Working Memory).
		 */
		.cal-day[data-today]::after {
			content: '';
			position: absolute;
			bottom: 2px;
			left: 50%;
			transform: translateX(-50%);
			width: 4px;
			height: 4px;
			border-radius: 50%;
			background: var(--primary);
			transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		.cal-day[data-today]:not([data-selected]) {
			font-weight: 500;
		}

		/* Selected today — dot inverts to stay visible (Selective Attention) */
		.cal-day[data-today][data-selected]::after {
			background: var(--primary-foreground);
		}

		/* Selected */
		.cal-day[data-selected] {
			background: var(--primary);
			color: var(--primary-foreground);
			font-weight: 500;
		}

		.cal-day[data-selected]:hover {
			background: color-mix(in oklch, var(--primary) 90%, transparent);
		}

		/*
		 * Squash & Stretch — selection pops with spring easing
		 * to confirm the tap registered (Doherty Threshold < 400 ms).
		 */
		.cal-day[data-selecting] {
			animation: cal-pop 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
		}

		@keyframes cal-pop {
			/* Deformation stays inside the subtle 0.95-1.05 band; the overshoot
			   easing supplies the confirmation, not the depth of the squash. */
			from { transform: scale(0.95); }
			to   { transform: scale(1); }
		}

		/* Outside days — Law of Similarity: muted color signals "not this month" */
		.cal-day[data-outside] {
			color: var(--muted-foreground);
		}

		.cal-day[data-outside][data-selected] {
			background: color-mix(in oklch, var(--primary) 60%, transparent);
			color: var(--primary-foreground);
		}

		/* Disabled */
		.cal-day:disabled {
			color: var(--muted-foreground);
			opacity: 0.5;
			pointer-events: none;
			cursor: default;
		}

		.cal-day:disabled[data-today]::after {
			background: var(--muted-foreground);
		}

		/*
		 * ── Month transition ──
		 * Staging + Solid Drawing — directional slide communicates
		 * which way in time the user moved, maintaining spatial
		 * continuity with the prev/next button positions.
		 */

		/* Title slides in from navigation direction */
		.cal[data-direction="next"] .cal-title {
			animation: cal-slide-next 150ms cubic-bezier(0.22, 1.1, 0.36, 1);
		}
		.cal[data-direction="prev"] .cal-title {
			animation: cal-slide-prev 150ms cubic-bezier(0.22, 1.1, 0.36, 1);
		}

		/* Grid rows slide in with staggered delays
		   (Follow Through & Overlapping Action — parts settle
		   at slightly different times for organic feel) */
		.cal[data-direction="next"] .cal-week,
		.cal[data-direction="prev"] .cal-week {
			animation-duration: 150ms;
			animation-timing-function: cubic-bezier(0.22, 1.1, 0.36, 1);
			animation-fill-mode: backwards;
		}
		.cal[data-direction="next"] .cal-week { animation-name: cal-slide-next; }
		.cal[data-direction="prev"] .cal-week { animation-name: cal-slide-prev; }

			.cal[data-direction] .cal-week:nth-child(1) { animation-delay: 0ms; }
			.cal[data-direction] .cal-week:nth-child(2) { animation-delay: 10ms; }
			.cal[data-direction] .cal-week:nth-child(3) { animation-delay: 20ms; }
			.cal[data-direction] .cal-week:nth-child(4) { animation-delay: 30ms; }
			.cal[data-direction] .cal-week:nth-child(5) { animation-delay: 40ms; }
			.cal[data-direction] .cal-week:nth-child(6) { animation-delay: 50ms; }

		@keyframes cal-slide-next {
			from { opacity: 0; transform: translateX(8px); }
			to   { opacity: 1; transform: translateX(0); }
		}
		@keyframes cal-slide-prev {
			from { opacity: 0; transform: translateX(-8px); }
			to   { opacity: 1; transform: translateX(0); }
		}

		/* ── Dark mode ── */
		.dark .cal-day:hover:not(:disabled):not([data-selected]) {
			background: color-mix(in oklch, var(--muted) 50%, transparent);
		}

		.dark .cal-day[data-selected] {
			background: var(--primary);
			color: var(--primary-foreground);
		}

		.dark .cal-day[data-selected]:hover {
			background: color-mix(in oklch, var(--primary) 80%, transparent);
		}

		.dark .cal-day[data-today]::after {
			background: var(--primary);
		}

		.dark .cal-day[data-today][data-selected]::after {
			background: var(--primary-foreground);
		}

		/* ── Reduced motion — instant state changes, no transform (Timing) ── */
		@media (prefers-reduced-motion: reduce) {
			.cal-day {
				transition: none;
			}
			.cal-day:active:not(:disabled) {
				transform: none;
			}
			.cal-day[data-selecting] {
				animation: none;
			}
			.cal[data-direction] .cal-title,
			.cal[data-direction] .cal-week {
				animation: none;
			}
		}

/* card.templ */
/* Card base */
		.card {
			display: flex;
			flex-direction: column;
			gap: 1rem;
			overflow: hidden;
			border-radius: var(--radius-xl);
			background-color: var(--card);
			padding-top: 1rem;
			padding-bottom: 1rem;
			font-size: 0.875rem;
			line-height: 1.25rem;
			color: var(--card-foreground);
			/* Layered resting depth (visual-layered-shadows) — the ring plus
			   three soft layers; --shadow-card flips for dark mode. */
			box-shadow: var(--shadow-card);
			outline: none;
			/* Hover feedback belongs in the 120-180ms band. The shadow is
			   transitioned directly rather than via a pseudo-element overlay:
			   the card needs overflow: hidden to clip full-bleed images to its
			   corners, which would also clip an overlay's shadow. */
			transition-property: box-shadow, transform;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
		}

		.card:has([data-slot="card-footer"]) {
			padding-bottom: 0;
		}

		.card:has(> img:first-child) {
			padding-top: 0;
		}

		.card > img:first-child {
			border-top-left-radius: var(--radius-xl);
			border-top-right-radius: var(--radius-xl);
		}

		.card > img:last-child {
			border-bottom-left-radius: var(--radius-xl);
			border-bottom-right-radius: var(--radius-xl);
		}

		/* Focus-visible */
		.card:focus-visible {
			box-shadow:
				var(--shadow-card),
				0 0 0 1px var(--ring),
				0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		/* Interactive card */
		.card-interactive {
			cursor: pointer;
		}

		.card-interactive:hover {
			/* Hover lifts one elevation step (visual-shadow-matches-elevation) */
			box-shadow: var(--shadow-card-hover);
			transform: translateY(-1px);
		}

		.card-interactive:active,
		.card-interactive[data-pressed] {
			transform: translateY(0);
			transition-duration: 100ms;
		}

		/* Card size: sm */
		.card[data-size="sm"] {
			gap: 0.75rem;
			padding-top: 0.75rem;
			padding-bottom: 0.75rem;
		}

		.card[data-size="sm"]:has([data-slot="card-footer"]) {
			padding-bottom: 0;
		}

		/* CardHeader */
		.card-header {
			display: grid;
			grid-auto-rows: min-content;
			align-items: start;
			gap: 0.25rem;
			border-top-left-radius: var(--radius-xl);
			border-top-right-radius: var(--radius-xl);
			padding-left: 1rem;
			padding-right: 1rem;
		}

		.card[data-size="sm"] .card-header {
			padding-left: 0.75rem;
			padding-right: 0.75rem;
		}

		.card-header:has([data-slot="card-action"]) {
			grid-template-columns: 1fr auto;
		}

		.card-header:has([data-slot="card-description"]) {
			grid-template-rows: auto auto;
		}

		.card-header.border-b {
			padding-bottom: 1rem;
			border-bottom: 1px solid var(--border);
		}

		.card[data-size="sm"] .card-header.border-b {
			padding-bottom: 0.75rem;
		}

		/* CardTitle */
		.card-title {
			font-size: 1rem;
			line-height: 1.375;
			font-weight: 500;
		}

		.card[data-size="sm"] .card-title {
			font-size: 0.875rem;
			line-height: 1.25rem;
		}

		/* CardDescription */
		.card-description {
			font-size: 0.875rem;
			color: var(--muted-foreground);
		}

		/* CardAction */
		.card-action {
			grid-column-start: 2;
			grid-row: span 2 / span 2;
			grid-row-start: 1;
			align-self: start;
			justify-self: end;
		}

		/* CardContent */
		.card-content {
			padding-left: 1rem;
			padding-right: 1rem;
		}

		.card[data-size="sm"] .card-content {
			padding-left: 0.75rem;
			padding-right: 0.75rem;
		}

		/* CardFooter */
		.card-footer {
			display: flex;
			align-items: center;
			gap: 0.5rem;
			border-bottom-left-radius: var(--radius-xl);
			border-bottom-right-radius: var(--radius-xl);
			border-top: 1px solid var(--border);
			background-color: color-mix(in oklch, var(--muted) 50%, transparent);
			padding: 1rem;
		}

		.card[data-size="sm"] .card-footer {
			padding: 0.75rem;
		}

		/* Dark mode is covered by the --shadow-card tokens themselves */

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.card {
				transition-duration: 0ms;
			}

			.card-interactive:hover {
				transform: none;
			}

			.card-interactive:active,
			.card-interactive[data-pressed] {
				transform: none;
			}
		}

/* carousel.templ */
/* Screen reader only — scoped to carousel */
		.carousel .sr-only {
			position: absolute;
			width: 1px;
			height: 1px;
			padding: 0;
			margin: -1px;
			overflow: hidden;
			clip: rect(0, 0, 0, 0);
			white-space: nowrap;
			border-width: 0;
		}

		/* Carousel base */
		.carousel {
			outline: none;
		}
		.carousel:focus-visible {
			outline: 2px solid var(--ring);
			outline-offset: 2px;
			border-radius: var(--radius-lg);
		}

		/* Body — positioning context for nav buttons */
		.carousel-body {
			position: relative;
		}

		/* Viewport — clip boundary with cursor hint */
		.carousel-viewport {
			overflow: hidden;
			border-radius: var(--radius-lg);
			-webkit-user-select: none;
			user-select: none;
		}
		/* Tell the browser which axis we own — prevents page scroll fighting the drag */
		.carousel[data-orientation="horizontal"] .carousel-viewport {
			touch-action: pan-y pinch-zoom;
		}
		.carousel[data-orientation="vertical"] .carousel-viewport {
			touch-action: pan-x pinch-zoom;
		}
		.carousel-viewport[data-draggable] {
			cursor: grab;
		}
		.carousel-viewport[data-dragging] {
			cursor: grabbing;
		}

		/*
		 * Content track — spring curve overshoots slightly on arrival.
		 * During drag [data-dragging] kills transition for 1:1 finger tracking.
		 */
		.carousel-content {
			display: flex;
			will-change: transform;
			transition: transform 280ms cubic-bezier(0.34, 1.4, 0.64, 1);
		}
		.carousel[data-orientation="vertical"] .carousel-content {
			flex-direction: column;
		}
		.carousel-viewport[data-dragging] .carousel-content {
			transition: none;
		}

		/* Item — sized by JS */
		.carousel-item {
			flex: none;
		}

		/* Navigation buttons */
		.carousel .carousel-prev,
		.carousel .carousel-next {
			position: absolute;
			border-radius: 9999px;
			touch-action: manipulation;
			z-index: 1;
		}

		/* Horizontal button positions — outside content */
		.carousel[data-orientation="horizontal"] .carousel-prev {
			top: 50%;
			left: -2.5rem;
			transform: translateY(-50%);
		}
		.carousel[data-orientation="horizontal"] .carousel-next {
			top: 50%;
			right: -2.5rem;
			transform: translateY(-50%);
		}
		.carousel[data-orientation="horizontal"] .carousel-prev:hover:not(:disabled) {
			transform: translateY(-50%) translateX(-1px);
		}
		.carousel[data-orientation="horizontal"] .carousel-next:hover:not(:disabled) {
			transform: translateY(-50%) translateX(1px);
		}

		/* Vertical button positions — outside content */
		.carousel[data-orientation="vertical"] .carousel-prev {
			top: -2.5rem;
			left: 50%;
			transform: translateX(-50%) rotate(90deg);
		}
		.carousel[data-orientation="vertical"] .carousel-next {
			bottom: -2.5rem;
			left: 50%;
			transform: translateX(-50%) rotate(90deg);
		}
		.carousel[data-orientation="vertical"] .carousel-prev:hover:not(:disabled) {
			transform: translateX(-50%) rotate(90deg) translateX(-1px);
		}
		.carousel[data-orientation="vertical"] .carousel-next:hover:not(:disabled) {
			transform: translateX(-50%) rotate(90deg) translateX(1px);
		}

		/* Vertical layout: dots/counter beside viewport without shrinking content */
		.carousel[data-orientation="vertical"] {
			position: relative;
		}

		/* Dot indicators */
		.carousel-dots {
			display: flex;
			justify-content: center;
			align-items: center;
			gap: 0.25rem;
			padding-top: 0.75rem;
		}
		.carousel-dot {
			position: relative;
			display: flex;
			align-items: center;
			justify-content: center;
			width: 1.5rem;
			height: 1.5rem;
			border: none;
			padding: 0;
			cursor: pointer;
			background: transparent;
			outline: none;
		}
		/* Fitts — the visual dot is 8px inside a 24px button; this pushes the
		   pointer target to 32px without changing the layout. */
		.carousel-dot::after {
			content: "";
			position: absolute;
			inset: -0.25rem;
		}
		.carousel-dot::before {
			content: "";
			display: block;
			width: 0.5rem;
			height: 0.5rem;
			border-radius: 9999px;
			background-color: var(--border);
			transition:
				width 250ms cubic-bezier(0.34, 1.4, 0.64, 1),
				background-color 200ms ease;
		}
		.carousel-dot:hover::before {
			background-color: var(--muted-foreground);
		}
		.carousel-dot:focus-visible {
			outline: 2px solid var(--ring);
			outline-offset: 0;
			border-radius: 9999px;
		}
		.carousel-dot[data-active]::before {
			width: 1.25rem;
			background-color: var(--foreground);
		}

		/* Vertical dots: column beside viewport */
		.carousel[data-orientation="vertical"] .carousel-dots {
			position: absolute;
			right: -2rem;
			top: 0;
			bottom: 0;
			flex-direction: column;
			justify-content: center;
			padding-top: 0;
		}
		.carousel[data-orientation="vertical"] .carousel-dot[data-active]::before {
			width: 0.5rem;
			height: 1.25rem;
		}

		/* Counter */
		.carousel-counter {
			text-align: center;
			padding-top: 0.5rem;
			font-size: 0.75rem;
			line-height: 1rem;
			color: var(--muted-foreground);
			font-variant-numeric: tabular-nums;
		}

		/* Vertical counter */
		.carousel[data-orientation="vertical"] .carousel-counter {
			position: absolute;
			right: -2.5rem;
			top: 0;
			bottom: 0;
			display: flex;
			align-items: center;
			padding-top: 0;
			writing-mode: vertical-lr;
			text-orientation: mixed;
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.carousel-content {
				transition: none;
			}
			.carousel-dot::before {
				transition: none;
			}
			.carousel[data-orientation="horizontal"] .carousel-prev:hover:not(:disabled) {
				transform: translateY(-50%);
			}
			.carousel[data-orientation="horizontal"] .carousel-next:hover:not(:disabled) {
				transform: translateY(-50%);
			}
			.carousel[data-orientation="vertical"] .carousel-prev:hover:not(:disabled) {
				transform: translateX(-50%) rotate(90deg);
			}
			.carousel[data-orientation="vertical"] .carousel-next:hover:not(:disabled) {
				transform: translateX(-50%) rotate(90deg);
			}
		}

		/* Dark mode */
		.dark .carousel-dot::before {
			background-color: color-mix(in oklch, var(--foreground) 25%, transparent);
		}
		.dark .carousel-dot:hover::before {
			background-color: color-mix(in oklch, var(--foreground) 50%, transparent);
		}
		.dark .carousel-dot[data-active]::before {
			background-color: var(--foreground);
		}

/* chart.templ */
/* === Chart Container === */
		.chart {
			position: relative;
			display: flex;
			flex-direction: column;
			width: 100%;
			font-size: 0.75rem;
			line-height: 1rem;
		}

		.chart svg {
			display: block;
			width: 100%;
			height: auto;
			overflow: visible;
		}

		/* === Grid & Axes === */
		.chart-grid-line {
			stroke: var(--border);
			stroke-width: 1;
			stroke-dasharray: 4 4;
			opacity: 0.5;
		}

		.chart-axis-line {
			stroke: var(--border);
			stroke-width: 1;
		}

		.chart-axis-tick {
			fill: var(--muted-foreground);
			font-size: 0.7rem;
			font-family: inherit;
			/* Tabular figures — axis labels are a column of numbers */
			font-variant-numeric: tabular-nums;
		}

		/* === Bars === */
		.chart-bar {
			transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}
		.chart-bar:hover {
			opacity: 0.8;
		}

		/* === Keyboard focus ===
		   Data marks are one roving tab stop per chart; arrow keys walk them.
		   The ring is drawn on the mark itself so the focused datum is obvious
		   even when its tooltip is off to one side. */
		.chart-hit {
			cursor: crosshair;
		}
		.chart-bar:focus-visible,
		.chart-slice:focus-visible,
		.chart-hit:focus-visible {
			outline: 2px solid var(--ring);
			outline-offset: 1px;
		}

		/* === Lines === */
		.chart-line {
			fill: none;
			stroke-width: 2;
			stroke-linecap: round;
			stroke-linejoin: round;
		}

		/* === Area === */
		.chart-area {
			opacity: 0.15;
		}

		/* === Dots === */
		.chart-dot {
			stroke-width: 2;
			stroke: var(--background);
			opacity: 0;
			transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}
		.chart-dot-active {
			opacity: 1;
		}

		/* === Pie / Donut === */
		.chart-slice {
			transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1);
			stroke: var(--background);
			stroke-width: 2;
		}
		.chart-slice:hover {
			opacity: 0.8;
		}
		.chart-donut-label {
			fill: var(--foreground);
			font-size: 1.5rem;
			font-weight: 600;
			dominant-baseline: central;
			/* Tabular figures — the centre total is a dashboard stat */
			font-variant-numeric: tabular-nums;
		}
		.chart-donut-sublabel {
			fill: var(--muted-foreground);
			font-size: 0.75rem;
			dominant-baseline: central;
		}

		/* === Tooltip === */
		.chart-tooltip {
			position: absolute;
			z-index: 50;
			pointer-events: none;
			min-width: 8rem;
			border-radius: var(--radius-lg);
			border: 1px solid color-mix(in oklch, var(--border) 50%, transparent);
			background-color: var(--background);
			padding: 0.375rem 0.625rem;
			font-size: 0.75rem;
			line-height: 1rem;
			color: var(--foreground);
			/* Popover-tier elevation; the token flips for dark mode */
			box-shadow: var(--shadow-popover);
			/*
			 * Hidden via visibility, not display: display can't transition, so
			 * toggling it in the same frame killed the fade in both directions.
			 * visibility keeps the box measurable for posTip() and is delayed
			 * out so the fade finishes before the tooltip stops hit-testing.
			 */
			visibility: hidden;
			opacity: 0;
			transform: translateY(2px);
			transition: opacity 120ms ease-out, transform 120ms ease-out,
				visibility 0s linear 120ms;
		}

		.chart-tooltip[data-visible] {
			visibility: visible;
			opacity: 1;
			transform: translateY(0);
			transition: opacity 120ms ease-out, transform 120ms ease-out,
				visibility 0s linear 0s;
		}

		.chart-tooltip-label {
			font-weight: 500;
			margin-bottom: 0.25rem;
		}

		.chart-tooltip-row {
			display: flex;
			align-items: center;
			gap: 0.5rem;
			padding: 0.0625rem 0;
		}

		.chart-tooltip-indicator {
			display: inline-block;
			width: 0.5rem;
			height: 0.5rem;
			flex-shrink: 0;
			border-radius: 2px;
		}

		.chart-tooltip-name {
			color: var(--muted-foreground);
			flex: 1;
		}

		.chart-tooltip-value {
			font-weight: 500;
			font-variant-numeric: tabular-nums;
			font-family: 'Geist Mono', ui-monospace, monospace;
		}

		/* === Legend === */
		.chart-legend {
			display: flex;
			align-items: center;
			justify-content: center;
			flex-wrap: wrap;
			gap: 1rem;
			padding-top: 0.75rem;
		}

		.chart-legend-item {
			display: flex;
			align-items: center;
			gap: 0.375rem;
			font-size: 0.75rem;
			line-height: 1rem;
			color: var(--muted-foreground);
		}

		.chart-legend-color {
			width: 0.5rem;
			height: 0.5rem;
			flex-shrink: 0;
			border-radius: 2px;
		}

		/* === Entrance Animations === */
		@keyframes chart-bar-enter {
			from { transform: scaleY(0); }
			to   { transform: scaleY(1); }
		}

		@keyframes chart-line-enter {
			from { stroke-dashoffset: var(--line-length); }
			to   { stroke-dashoffset: 0; }
		}

		@keyframes chart-slice-enter {
			from { opacity: 0; transform: scale(0.96); }
			to   { opacity: 1; transform: scale(1); }
		}

		@keyframes chart-area-enter {
			from { opacity: 0; }
			to   { opacity: 0.15; }
		}

		.chart[data-animate] .chart-bar {
			/*
			 * transform-box defaults to view-box on SVG elements, which would
			 * resolve "bottom" to the bottom of the whole viewBox — 36px below
			 * the axis, inside the label gutter. fill-box scopes the origin to
			 * each bar so they grow off the baseline instead of up through it.
			 */
			transform-box: fill-box;
			transform-origin: center bottom;
			animation: chart-bar-enter 280ms cubic-bezier(0.34, 1.3, 0.64, 1) both;
			animation-delay: calc(var(--i, 0) * 30ms);
		}

		.chart[data-animate] .chart-line {
			stroke-dasharray: var(--line-length);
			stroke-dashoffset: var(--line-length);
			animation: chart-line-enter 300ms ease-out both;
		}

		.chart[data-animate] .chart-area {
			opacity: 0;
			animation: chart-area-enter 250ms ease-out 80ms both;
		}

		.chart[data-animate] .chart-slice {
			/*
			 * Deliberately left on the default view-box: every slice must scale
			 * about the shared pie centre, which renderPie places at the viewBox
			 * centre. fill-box here would burst the slices apart individually.
			 */
			transform-origin: center;
			animation: chart-slice-enter 250ms cubic-bezier(0.34, 1.3, 0.64, 1) both;
			animation-delay: calc(var(--i, 0) * 40ms);
		}

		/* === Dark Mode === */
		.dark .chart-grid-line {
			opacity: 0.3;
		}

		/* === Reduced Motion === */
		@media (prefers-reduced-motion: reduce) {
			.chart[data-animate] .chart-bar,
			.chart[data-animate] .chart-line,
			.chart[data-animate] .chart-area,
			.chart[data-animate] .chart-slice {
				animation: none;
			}
			.chart-bar,
			.chart-dot,
			.chart-slice,
			.chart-tooltip {
				transition: none;
			}
			.chart-area {
				opacity: 0.15;
			}
		}

/* checkbox.templ */
/* Wrapper */
		.checkbox-wrapper {
			position: relative;
			display: inline-flex;
			width: 1.125rem;
			height: 1.125rem;
			flex-shrink: 0;
		}

		/* Native input: transparent overlay for interaction */
		.checkbox-input {
			position: absolute;
			inset: -0.5rem -0.75rem;
			opacity: 0;
			cursor: pointer;
			margin: 0;
			z-index: 1;
		}

		.checkbox-input:disabled {
			cursor: not-allowed;
		}

		/* Visual checkbox */
		.checkbox-visual {
			display: inline-flex;
			align-items: center;
			justify-content: center;
			width: 1.125rem;
			height: 1.125rem;
			border-radius: 4px;
			border: 1px solid var(--input);
			background: transparent;
			color: inherit;
			/* Field elevation — a hairline lift off the page */
			box-shadow: var(--shadow-field);
			transition-property: color, background-color, border-color, box-shadow, transform;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
		}

		/* Hover on checkbox box (Anticipation — hint before action), matching
		   the Radio and Switch treatment */
		.checkbox-input:not(:disabled):not(:checked):hover + .checkbox-visual {
			border-color: color-mix(in oklch, var(--primary) 50%, var(--input));
		}

		/* Pressed feedback */
		.checkbox-input:active:not(:disabled) + .checkbox-visual {
			transform: scale(0.97);
			transition-duration: 100ms;
		}

		/* Focus visible */
		.checkbox-input:focus-visible + .checkbox-visual {
			border-color: var(--ring);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		/* Disabled */
		.checkbox-input:disabled + .checkbox-visual {
			opacity: 0.5;
		}

		/* Disabled via parent group */
		[data-disabled="true"] .checkbox-input {
			pointer-events: none;
		}
		[data-disabled="true"] .checkbox-visual {
			opacity: 0.5;
		}

		/* Invalid */
		.checkbox-input[aria-invalid="true"] + .checkbox-visual {
			border-color: var(--destructive);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
		}

		/* Checked — the filled face gets the lit-edge treatment */
		.checkbox-input:checked + .checkbox-visual {
			border-color: var(--primary);
			background-color: var(--primary);
			color: var(--primary-foreground);
			box-shadow: var(--highlight-top), var(--shade-bottom), var(--shadow-field);
		}

		/* Invalid + Checked */
		.checkbox-input[aria-invalid="true"]:checked + .checkbox-visual {
			border-color: var(--primary);
		}

		/* Indicator */
		.checkbox-indicator {
			display: grid;
			place-content: center;
			color: currentColor;
			opacity: 1;
			transform: scale(1);
			transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1),
				transform 150ms cubic-bezier(0.34, 1.56, 0.64, 1);
		}

		.checkbox-indicator svg {
			width: 0.875rem;
			height: 0.875rem;
		}

		/* Hide indicator when unchecked */
		.checkbox-input:not(:checked) + .checkbox-visual .checkbox-indicator {
			opacity: 0;
			transform: scale(0.5);
		}

		/* Dark mode */
		.dark .checkbox-visual {
			background-color: color-mix(in oklch, var(--input) 30%, transparent);
		}

		.dark .checkbox-input:not(:disabled):not(:checked):hover + .checkbox-visual {
			border-color: color-mix(in oklch, var(--primary) 60%, var(--input));
		}

		.dark .checkbox-input[aria-invalid="true"] + .checkbox-visual {
			border-color: color-mix(in oklch, var(--destructive) 50%, transparent);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 40%, transparent);
		}

		.dark .checkbox-input:checked + .checkbox-visual {
			background-color: var(--primary);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.checkbox-visual {
				transition-duration: 75ms;
			}
			.checkbox-indicator {
				transition: none;
			}
			.checkbox-input:active:not(:disabled) + .checkbox-visual {
				transform: none;
			}
		}

/* collapsible.templ */
/* Base */
		.collapsible {
			border-radius: var(--radius-lg);
			border: 1px solid var(--border);
			background-color: var(--card);
			color: var(--card-foreground);
			font-size: 0.875rem;
			line-height: 1.25rem;
		}

		/* Trigger */
		.collapsible-trigger {
			display: flex;
			align-items: center;
			justify-content: space-between;
			gap: 0.5rem;
			padding: 0.75rem 1rem;
			font-weight: 500;
			cursor: pointer;
			user-select: none;
			list-style: none;
			outline: none;
			border-radius: var(--radius-lg);
			transition-property: background-color, border-radius;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
		}

		/* Remove default marker */
		.collapsible-trigger::-webkit-details-marker {
			display: none;
		}
		.collapsible-trigger::marker {
			content: "";
		}

		.collapsible-trigger:hover {
			background-color: var(--muted);
		}

		.collapsible-trigger:focus-visible {
			box-shadow: 0 0 0 2px var(--background), 0 0 0 4px var(--ring);
		}

		/* Chevron icon -- spring easing for follow-through */
		.collapsible-trigger svg {
			width: 1rem;
			height: 1rem;
			flex-shrink: 0;
			color: var(--muted-foreground);
			transition: transform 250ms cubic-bezier(0.34, 1.4, 0.64, 1);
		}

		.collapsible[open] > .collapsible-trigger {
			border-bottom-left-radius: 0;
			border-bottom-right-radius: 0;
		}

		.collapsible[open] > .collapsible-trigger svg[data-slot="collapsible-chevron"] {
			transform: rotate(180deg);
		}

		/* Restore border-radius during close */
		.collapsible[data-closing] > .collapsible-trigger {
			border-bottom-left-radius: var(--radius-lg);
			border-bottom-right-radius: var(--radius-lg);
			transition: border-radius 120ms cubic-bezier(0.4, 0, 0.7, 0.2);
		}

		/* Chevron animates back on close — faster ease-in for asymmetry */
		.collapsible[data-closing] > .collapsible-trigger svg[data-slot="collapsible-chevron"] {
			transform: rotate(0deg);
			transition: transform 120ms cubic-bezier(0.4, 0, 0.7, 0.2);
		}

		/*
		 * Content — asymmetric enter/exit (overlapping action).
		 * Enter: spring easing with translateY arc (200ms).
		 * Exit: snappy ease-in, faster for responsiveness (120ms).
		 */
		.collapsible-content {
			padding: 0.75rem 1rem;
			border-top: 1px solid var(--border);
			opacity: 1;
			transform: translateY(0);
			transition:
				opacity 200ms cubic-bezier(0.22, 1.1, 0.36, 1),
				transform 200ms cubic-bezier(0.22, 1.1, 0.36, 1);
		}

		@starting-style {
			.collapsible[open] > .collapsible-content {
				opacity: 0;
				transform: translateY(-4px);
			}
		}

		/* Exit animation */
		.collapsible[data-closing] > .collapsible-content {
			animation: collapsible-out 120ms cubic-bezier(0.4, 0, 0.7, 0.2) forwards;
			transition: none;
		}

		@keyframes collapsible-out {
			from { opacity: 1; transform: translateY(0); }
			to   { opacity: 0; transform: translateY(-4px); }
		}

		/* Dark mode */
		.dark .collapsible-trigger:hover {
			background-color: color-mix(in oklch, var(--muted) 50%, transparent);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.collapsible-trigger,
			.collapsible-trigger svg,
			.collapsible-content {
				transition-duration: 0ms;
			}
			.collapsible[open]:not([data-closing]) > .collapsible-content,
			.collapsible[data-closing] > .collapsible-content {
				animation: none;
			}
		}

/* combobox.templ */
/* ── Root ── */
		.cbox {
			position: relative;
			display: inline-flex;
		}

		/* ── Input wrapper ── */
		.cbox-input-wrap {
			display: inline-flex;
			align-items: center;
			width: fit-content;
			border-radius: var(--radius-lg);
			border: 1px solid var(--input);
			background-color: transparent;
			/* Field elevation — a hairline lift off the page (--shadow-field) */
			box-shadow: var(--shadow-field);
			transition-property: color, background-color, border-color, box-shadow, opacity;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
			/* Same resting height as Input, NativeSelect, SelectTrigger and
			   InputGroup: sibling fields on one row must line up */
			height: 2.25rem;
		}

		/* Anticipation — hover hints at interactivity before click (Jakob's Law) */
		.cbox-input-wrap:hover:not(:has(:disabled)) {
			border-color: color-mix(in oklch, var(--ring) 40%, transparent);
		}

		/* The ring follows the text input, not any focus inside the wrapper:
		   the clear button draws its own ring, and lighting both would put two
		   focus indicators on screen at once (staging-one-focal-point). */
		.cbox-input-wrap:has(.cbox-input:focus-visible) {
			border-color: var(--ring);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		/*
		 * Secondary Action — expanded wrapper gets subtle bg tint so the user
		 * sees a visual connection between the input and the open panel
		 * (Law of Common Region + Working Memory).
		 */
		.cbox[data-open] .cbox-input-wrap {
			border-color: var(--ring);
			background-color: color-mix(in oklch, var(--muted) 50%, transparent);
		}

		.cbox-input-wrap:has(:disabled) {
			pointer-events: none;
			opacity: 0.5;
			cursor: not-allowed;
		}

		.cbox-input-wrap[aria-invalid="true"],
		.cbox-input-wrap:has([aria-invalid="true"]) {
			border-color: var(--destructive);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
		}

		/* ── Input ── */
		.cbox-input {
			flex: 1;
			min-width: 0;
			width: 10rem;
			height: 100%;
			background: transparent;
			border: none;
			outline: none;
			padding: 0 0.625rem;
			/* 1rem until md — a focused control under 16px makes iOS Safari
			   zoom the viewport. Same guard as Input and Textarea. */
			font-size: 1rem;
			line-height: 1.5rem;
			color: inherit;
		}

		@media (min-width: 768px) {
			.cbox-input {
				font-size: 0.875rem;
				line-height: 1.25rem;
			}
		}

		.cbox-input::placeholder {
			color: var(--muted-foreground);
		}

		/* ── Trigger button ── */
		.cbox-trigger {
			display: flex;
			align-items: center;
			justify-content: center;
			flex-shrink: 0;
			width: 1.5rem;
			height: 1.5rem;
			padding: 0;
			border: none;
			background: transparent;
			color: var(--muted-foreground);
			cursor: pointer;
			border-radius: min(var(--radius-md), 10px);
			transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1),
				transform 150ms cubic-bezier(0.4, 0, 0.2, 1);
			outline: none;
			margin-right: 0.125rem;
		}

		.cbox-trigger svg {
			width: 1rem;
			height: 1rem;
			pointer-events: none;
		}

		/* Fitts — 24px boxes; the pseudo-element lifts the pointer target to
		   32px without pushing the field taller */
		.cbox-trigger,
		.cbox-clear {
			position: relative;
		}
		.cbox-trigger::after,
		.cbox-clear::after {
			content: '';
			position: absolute;
			inset: -0.25rem;
		}

		/* Secondary Action — chevron follows hover to reinforce affordance */
		.cbox-trigger:hover {
			color: var(--foreground);
		}

		/* Squash & Stretch — subtle scale on press (physics-active-state) */
		.cbox-trigger:active:not(:disabled) {
			transform: scale(0.97);
			transition-duration: 100ms;
		}

		/* Chevron rotates when open (Anticipation — confirms the state) */
		.cbox[data-open] .cbox-trigger {
			transform: rotate(180deg);
			color: var(--foreground);
		}

		/* ── Clear button ── */
		.cbox-clear {
			display: none;
			align-items: center;
			justify-content: center;
			flex-shrink: 0;
			width: 1.5rem;
			height: 1.5rem;
			padding: 0;
			border: none;
			background: transparent;
			color: var(--muted-foreground);
			cursor: pointer;
			border-radius: min(var(--radius-md), 10px);
			outline: none;
			margin-right: 0.125rem;
			/* Slow In & Slow Out — fade in when appearing */
			opacity: 0.5;
			transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1),
				opacity 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		.cbox-clear svg {
			width: 0.75rem;
			height: 0.75rem;
			pointer-events: none;
		}

		.cbox-clear:hover {
			color: var(--foreground);
			opacity: 1;
		}

		/* Keyboard-reachable: it is the only way to clear a committed value,
		   so it carries a focus ring of its own */
		.cbox-clear:focus-visible {
			color: var(--foreground);
			opacity: 1;
			box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		/* Squash & Stretch — subtle scale on press (physics-active-state) */
		.cbox-clear:active:not(:disabled) {
			transform: scale(0.97);
			transition-duration: 100ms;
		}

		.cbox[data-has-value] .cbox-clear {
			display: flex;
		}

		/* Only yield the chevron to the clear button when a clear button is
		   actually rendered (ShowClear). Without it, the chevron must stay
		   visible so there is always a way to reopen the dropdown. */
		.cbox[data-has-value]:has(.cbox-clear) .cbox-trigger {
			display: none;
		}

		/* ── Content / Popup ── */
		.cbox-content {
			position: absolute;
			z-index: 50;
			min-width: 100%;
			max-height: 20rem;
			overflow-y: auto;
			overscroll-behavior: contain;
			border-radius: var(--radius-md);
			background: var(--popover);
			padding: 0.25rem;
			color: var(--popover-foreground);
			font-size: 0.875rem;
			line-height: 1.25rem;
			box-shadow: var(--shadow-popover);
			outline: none;
			/*
			 * Directional slide via custom properties (Solid Drawing).
			 * --cbox-slide drives the directional translate so the panel
			 * emerges FROM the trigger edge, not just fades in place (Staging).
			 * --cbox-base-x holds the alignment offset (e.g. -50% for centering)
			 * and is composed into the keyframes so the base position is never
			 * lost while the animation owns the transform property.
			 */
			--cbox-slide: 0px;
			--cbox-base-x: 0px;
			transform: translateX(var(--cbox-base-x));
		}

		/* Hidden when closed */
		.cbox-content[data-state="closed"] {
			pointer-events: none;
			visibility: hidden;
			opacity: 0;
		}

		/*
		 * Side positioning + directional slide offsets.
		 * transform-origin anchors scale to the trigger edge so the panel
		 * appears to grow outward — maintaining spatial continuity (Solid Drawing).
		 * --cbox-slide drives the directional translate so the panel
		 * emerges FROM the input, not just fades in place (Staging).
		 */

		/* Side: bottom (default) */
		.cbox-content[data-side="bottom"] {
			top: calc(100% + 4px);
			transform-origin: top;
			--cbox-slide: -4px;
		}
		.cbox-content[data-side="top"] {
			bottom: calc(100% + 4px);
			transform-origin: bottom;
			--cbox-slide: 4px;
		}

		/* Align */
		.cbox-content[data-side="bottom"][data-align="start"],
		.cbox-content[data-side="top"][data-align="start"] {
			left: 0;
		}
		.cbox-content[data-side="bottom"][data-align="center"],
		.cbox-content[data-side="top"][data-align="center"] {
			left: 50%;
			--cbox-base-x: -50%;
		}
		.cbox-content[data-side="bottom"][data-align="end"],
		.cbox-content[data-side="top"][data-align="end"] {
			right: 0;
		}

		/*
		 * Animations — asymmetric enter/exit with directional slide.
		 * Enter: spring easing + directional translate for organic arc (150ms).
		 * Exit: snappy accelerate-out, less travel distance than enter so the
		 *   leaving panel doesn't compete for attention (Follow Through + Timing).
		 */

		/* Open (Slow In & Slow Out — spring overshoot on enter) */
		.cbox-content[data-state="open"] {
			animation: cbox-in 150ms cubic-bezier(0.22, 1.1, 0.36, 1);
		}

		/* Close (faster, subtler exit — content yields focus back to input) */
		.cbox-content[data-closing] {
			pointer-events: none;
			animation: cbox-out 100ms cubic-bezier(0.4, 0, 0.7, 0.2) forwards;
		}

		@keyframes cbox-in {
			from {
				opacity: 0;
				transform: translateX(var(--cbox-base-x))
					scale(0.96)
					translateY(var(--cbox-slide));
			}
			to {
				opacity: 1;
				transform: translateX(var(--cbox-base-x))
					scale(1)
					translateY(0);
			}
		}

		@keyframes cbox-out {
			from {
				opacity: 1;
				transform: translateX(var(--cbox-base-x))
					scale(1)
					translateY(0);
			}
			to {
				opacity: 0;
				transform: translateX(var(--cbox-base-x))
					scale(0.97)
					translateY(calc(var(--cbox-slide) * 0.5));
			}
		}

		/* ── Group ── */
		.cbox-group {
			padding: 0.125rem 0;
		}

		/* ── Label ── */
		.cbox-label {
			padding: 0.25rem 0.5rem;
			font-size: 0.75rem;
			font-weight: 500;
			color: var(--muted-foreground);
		}

		/* Chunking — labels after separators get extra top space
		   to form visually distinct groups (Law of Proximity) */
		.cbox-separator + .cbox-group > .cbox-label:first-child,
		.cbox-separator + .cbox-label {
			padding-top: 0.375rem;
		}

		/* ── Item ── */
		.cbox-item {
			position: relative;
			display: flex;
			cursor: default;
			align-items: center;
			gap: 0.375rem;
			/* Concentric with the panel: outer radius minus the 0.25rem pad */
			border-radius: calc(var(--radius-md) - 4px);
			padding: 0.3125rem 2rem 0.3125rem 0.5rem;
			font-size: 0.875rem;
			line-height: 1.25rem;
			outline: none;
			user-select: none;
		}

		/* Hidden when filtered out */
		.cbox-item[data-hidden] {
			display: none;
		}

		/*
		 * Hover fades; keyboard does not. The transition lives on the :hover
		 * rule so ArrowUp/ArrowDown moves the highlight instantly.
		 */
		.cbox-item:hover:not([data-disabled="true"]):not([data-hidden]) {
			background: var(--accent);
			color: var(--accent-foreground);
			transition: background-color 80ms cubic-bezier(0.4, 0, 0.2, 1),
				color 80ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		.cbox-item[data-highlighted] {
			background: var(--accent);
			color: var(--accent-foreground);
		}

		/* Selected state */
		.cbox-item[data-selected] {
			font-weight: 500;
		}

		/* Disabled */
		.cbox-item[data-disabled="true"] {
			pointer-events: none;
			opacity: 0.5;
		}

		/* SVG inside items */
		.cbox-item svg {
			pointer-events: none;
			flex-shrink: 0;
			width: 1rem;
			height: 1rem;
		}

		/* ── Item text ── */
		.cbox-item-text {
			display: flex;
			flex: 1;
			flex-shrink: 0;
			align-items: center;
			gap: 0.5rem;
			white-space: nowrap;
		}

		/* ── Item indicator (check) ── */
		.cbox-item-indicator {
			pointer-events: none;
			position: absolute;
			right: 0.5rem;
			display: flex;
			align-items: center;
			justify-content: center;
			opacity: 0;
			transform: scale(0.5);
			transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1),
				transform 150ms cubic-bezier(0.34, 1.56, 0.64, 1);
		}

		.cbox-item[data-selected] .cbox-item-indicator {
			opacity: 1;
			transform: scale(1);
		}

		.cbox-item-indicator svg {
			width: 0.875rem;
			height: 0.875rem;
		}

		/* ── Separator ── */
		.cbox-separator {
			margin: 0.25rem -0.25rem;
			height: 1px;
			background: var(--border);
		}

		/* ── Empty ── */
		.cbox-empty {
			display: none;
			width: 100%;
			justify-content: center;
			padding: 1.5rem 0.5rem;
			text-align: center;
			font-size: 0.875rem;
			color: var(--muted-foreground);
		}

		.cbox-content[data-empty] .cbox-empty {
			display: flex;
		}

		/*
		 * Hide groups/separators when all items inside are filtered out.
		 * Keeps the panel clean while typing (Law of Praegnanz — simple,
		 * orderly form; Cognitive Load — remove noise).
		 */
		.cbox-group:has([data-slot="combobox-item"]):not(:has([data-slot="combobox-item"]:not([data-hidden]))) {
			display: none;
		}
		/* Separator before a fully-hidden group */
		.cbox-separator:has(+ .cbox-group:not(:has([data-slot="combobox-item"]:not([data-hidden])))) {
			display: none;
		}
		/* Separator after a fully-hidden group */
		.cbox-group:not(:has([data-slot="combobox-item"]:not([data-hidden]))) + .cbox-separator {
			display: none;
		}

		/* ── Dark mode ── */
		.dark .cbox-input-wrap {
			border-color: var(--input);
			background-color: color-mix(in oklch, var(--input) 30%, transparent);
		}
		.dark .cbox-input-wrap:hover:not(:has(:disabled)) {
			background-color: color-mix(in oklch, var(--input) 50%, transparent);
		}
		.dark .cbox[data-open] .cbox-input-wrap {
			background-color: color-mix(in oklch, var(--input) 50%, transparent);
		}
		.dark .cbox-input-wrap[aria-invalid="true"],
		.dark .cbox-input-wrap:has([aria-invalid="true"]) {
			border-color: color-mix(in oklch, var(--destructive) 50%, transparent);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 40%, transparent);
		}
		/* ── Reduced motion — instant show/hide, no transform (Timing) ── */
		@media (prefers-reduced-motion: reduce) {
			.cbox-input-wrap,
			.cbox-trigger,
			.cbox-clear,
			.cbox-item,
			.cbox-item-indicator {
				transition: none;
			}
			.cbox-trigger:active:not(:disabled),
			.cbox-clear:active:not(:disabled) {
				transform: none;
			}
			.cbox-content[data-state="open"],
			.cbox-content[data-closing] {
				animation: none;
			}
		}

/* command.templ */
/* ── Command root ── */
		.cmd {
			display: flex;
			width: 100%;
			height: 100%;
			flex-direction: column;
			overflow: hidden;
			border-radius: var(--radius-xl);
			background: var(--popover);
			padding: 0.25rem;
			color: var(--popover-foreground);
			/* Popover-tier elevation; dark mode previously had a one-off
			   shadow here while light mode had none — the token covers both */
			box-shadow: var(--shadow-popover);
		}

		/*
		 * ── Command Dialog overrides ──
		 * Staging: dialog sits in the upper third so the user's eye
		 * doesn't travel far — the command palette is a transient tool,
		 * not a destination (Serial Position + Selective Attention).
		 */
		.cmd-dialog .dialog-panel {
			max-width: calc(100% - 2rem);
			padding: 0;
			overflow: hidden;
			gap: 0;
		}

		/*
		 * Fixed-height shell so the search bar never moves
		 * when results filter in/out. The list scrolls internally.
		 */
		.cmd-dialog .cmd {
			height: 20rem;
			/* The dialog panel owns the elevation */
			box-shadow: none;
		}

		@media (min-width: 640px) {
			.cmd-dialog .dialog-panel {
				max-width: 28rem;
			}
		}

		.cmd-dialog[open] {
			align-items: flex-start;
			padding-top: 8vh;
		}

		/* ── Input wrapper ── */
		.cmd-input-wrapper {
			padding: 0.25rem;
			padding-bottom: 0;
		}

		/*
		 * ── Input group (search icon + input) ──
		 * Law of Common Region: border + background create a
		 * shared surface so icon and input read as one control.
		 * Anticipation: border/shadow transitions hint at interactivity
		 * before the user types.
		 */
		.cmd-input-group {
			display: flex;
			height: 2rem;
			align-items: center;
			border-radius: var(--radius-lg);
			border: 1px solid color-mix(in oklch, var(--input) 30%, transparent);
			background: color-mix(in oklch, var(--input) 30%, transparent);
			padding: 0 0.5rem;
			/* border-color only — box-shadow draws the focus ring, and a focus
			   ring must appear instantly rather than fade in over 150ms. */
			transition: border-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		.cmd-input-group:focus-within {
			border-color: var(--ring);
			box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		/* ── Input ── */
		.cmd-input {
			flex: 1;
			width: 100%;
			font-size: 0.875rem;
			line-height: 1.25rem;
			background: transparent;
			border: none;
			outline: none;
			color: inherit;
		}

		.cmd-input::placeholder {
			color: var(--muted-foreground);
		}

		.cmd-input:disabled {
			cursor: not-allowed;
			opacity: 0.5;
		}

		/*
		 * ── Search icon ──
		 * Selective Attention: icon starts muted, transitions to foreground
		 * on focus to confirm the input is active (Anticipation).
		 */
		.cmd-input-icon {
			display: flex;
			align-items: center;
			justify-content: center;
			flex-shrink: 0;
			order: -1;
			padding-right: 0.5rem;
			color: var(--muted-foreground);
			transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		.cmd-input-icon svg {
			width: 1rem;
			height: 1rem;
			opacity: 0.5;
			transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		/* Anticipation: icon wakes up on focus to confirm active state */
		.cmd-input-group:focus-within .cmd-input-icon {
			color: var(--foreground);
		}

		.cmd-input-group:focus-within .cmd-input-icon svg {
			opacity: 1;
		}

		/* ── List ── */
		.cmd-list {
			flex: 1;
			min-height: 0;
			max-height: 18rem;
			overflow-x: hidden;
			overflow-y: auto;
			overscroll-behavior: contain;
			scroll-padding: 0.25rem;
			outline: none;
		}

		/* ── Empty state ── */
		.cmd-empty {
			padding: 1.5rem 0;
			text-align: center;
			font-size: 0.875rem;
			color: var(--muted-foreground);
			display: none;
		}

		.cmd-empty[data-visible] {
			display: block;
			animation: cmd-empty-in 150ms cubic-bezier(0.33, 1, 0.68, 1);
		}

		@keyframes cmd-empty-in {
			from { opacity: 0; transform: translateY(-2px); }
			to   { opacity: 1; transform: translateY(0); }
		}

		/*
		 * ── Group ──
		 * Chunking (Miller's Law): groups break the list into
		 * scannable chunks so users process 2-4 groups instead
		 * of N individual items.
		 */
		.cmd-group {
			overflow: hidden;
			padding: 0.25rem;
			color: var(--foreground);
		}

		/* Hidden when all items filtered out */
		.cmd-group[data-hidden] {
			display: none;
		}

		/*
		 * ── Group heading ──
		 * Law of Proximity: extra bottom padding separates the heading
		 * from items just enough to read as a label, not an item.
		 */
		.cmd-group-heading {
			padding: 0.375rem 0.5rem 0.25rem;
			font-size: 0.75rem;
			font-weight: 500;
			color: var(--muted-foreground);
			letter-spacing: 0.01em;
		}

		/*
		 * ── Item ──
		 * Fitts's Law: min-height 2rem ensures comfortable mouse
		 * targets; padding gives 32px+ total height per item.
		 * Slow In & Slow Out: 80ms bg transition is fast enough
		 * to feel instant but smooth enough to avoid flicker.
		 */
		.cmd-item {
			position: relative;
			display: flex;
			cursor: default;
			align-items: center;
			gap: 0.5rem;
			min-height: 2rem;
			/* Concentric with the panel: the item sits 0.5rem in from the .cmd
			   edge (0.25rem panel pad + 0.25rem group pad), so 0.75rem - 0.5rem */
			border-radius: calc(var(--radius-md) - 4px);
			padding: 0.375rem 0.5rem;
			font-size: 0.875rem;
			line-height: 1.25rem;
			outline: none;
			user-select: none;
			transition: transform 100ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		/*
		 * The highlight fades only when the pointer put it there. Arrow-key
		 * navigation lands on items that are not hovered, so it moves the
		 * highlight instantly — keyboard must never wait on an animation.
		 */
		.cmd-item:hover[data-highlighted] {
			transition: background-color 80ms cubic-bezier(0.4, 0, 0.2, 1),
				color 80ms cubic-bezier(0.4, 0, 0.2, 1),
				transform 100ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		/* Hidden when filtered out */
		.cmd-item[data-hidden] {
			display: none;
		}

		/* Highlighted (keyboard / hover) */
		.cmd-item[data-highlighted] {
			background: var(--muted);
			color: var(--foreground);
		}

		/*
		 * Squash & Stretch: subtle scale on press gives
		 * physical weight to the selection (physics-active-state).
		 * 100ms matches button press timing for consistency.
		 */
		.cmd-item:active:not([data-disabled="true"]) {
			transform: scale(0.98);
			transition-duration: 60ms;
		}

		/* Disabled */
		.cmd-item[data-disabled="true"] {
			pointer-events: none;
			opacity: 0.5;
		}

		/* SVGs inside items */
		.cmd-item svg {
			pointer-events: none;
			flex-shrink: 0;
			width: 1rem;
			height: 1rem;
			color: var(--muted-foreground);
			transition: color 80ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		/* Follow Through: icon color follows highlight for visual unity */
		.cmd-item[data-highlighted] svg {
			color: var(--foreground);
		}

		/*
		 * ── Shortcut ──
		 * Working Memory: kbd-like styling uses recognition over recall —
		 * users see the shortcut and recognise a key combination
		 * without reading it as plain text (Von Restorff).
		 */
		.cmd-shortcut {
			margin-left: auto;
			display: inline-flex;
			align-items: center;
			height: 1.25rem;
			padding: 0 0.25rem;
			border-radius: 0.25rem;
			border: 1px solid var(--border);
			background: color-mix(in oklch, var(--muted) 60%, transparent);
			font-size: 0.625rem;
			font-weight: 500;
			font-family: inherit;
			letter-spacing: 0.05em;
			color: var(--muted-foreground);
			transition: color 80ms cubic-bezier(0.4, 0, 0.2, 1),
				border-color 80ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		/* Follow Through: shortcut badge follows highlight state */
		.cmd-item[data-highlighted] .cmd-shortcut {
			color: var(--foreground);
			border-color: color-mix(in oklch, var(--foreground) 20%, transparent);
		}

		/* ── Separator ── */
		.cmd-separator {
			margin: 0.25rem -0.25rem;
			height: 1px;
			background: var(--border);
		}

		/* ── Dark mode ── */
		.dark .cmd-input-group {
			border-color: color-mix(in oklch, var(--input) 50%, transparent);
			background: color-mix(in oklch, var(--input) 40%, transparent);
		}

		.dark .cmd-shortcut {
			background: color-mix(in oklch, var(--muted) 40%, transparent);
			border-color: color-mix(in oklch, var(--foreground) 15%, transparent);
		}

		/* ── Reduced motion ── */
		@media (prefers-reduced-motion: reduce) {
			.cmd-item {
				transition: none;
			}
			/* Must repeat the hover selector: it outranks the bare class
			   above, so listing only .cmd-item would leave it animating. */
			.cmd-item:hover[data-highlighted] {
				transition: none;
			}
			.cmd-item:active:not([data-disabled="true"]) {
				transform: none;
			}
			.cmd-input-group,
			.cmd-input-icon,
			.cmd-input-icon svg,
			.cmd-item svg,
			.cmd-shortcut {
				transition: none;
			}
			.cmd-empty[data-visible] {
				animation: none;
			}
		}

/* context-menu.templ */
/* ── Trigger ── */
		.ctxm-trigger {
			user-select: none;
		}

		/* ── Content (positioned fixed at cursor) ── */
		.ctxm-content {
			position: fixed;
			z-index: 10000;
			min-width: 12rem;
			max-height: calc(100vh - 2rem);
			overflow-x: hidden;
			overflow-y: auto;
			border-radius: var(--radius-md);
			background: var(--popover);
			padding: 0.25rem;
			color: var(--popover-foreground);
			font-size: 0.875rem;
			line-height: 1.25rem;
			box-shadow: var(--shadow-popover);
			outline: none;
			transform-origin: top left;
		}

		/* Hidden when closed */
		.ctxm-content[data-state="closed"] {
			pointer-events: none;
			visibility: hidden;
			opacity: 0;
		}

		/*
		 * timing-no-entrance-context-menu — context menus appear instantly
		 * (no entrance animation). Matches native OS behavior (Jakob's Law).
		 * Exit-only animation gives feedback that the action was acknowledged.
		 */

		/* Close animation (exit only) */
		.ctxm-content[data-closing] {
			pointer-events: none;
			animation: ctxm-out 100ms cubic-bezier(0.4, 0, 0.7, 0.2) forwards;
		}

		@keyframes ctxm-out {
			from {
				opacity: 1;
				transform: scale(1);
			}
			to {
				opacity: 0;
				transform: scale(0.95);
			}
		}

		/* ── Sub content ── */
		.ctxm-sub-content {
			position: fixed;
			z-index: 10000;
			min-width: 8rem;
			border-radius: var(--radius-md);
			background: var(--popover);
			padding: 0.25rem;
			color: var(--popover-foreground);
			font-size: 0.875rem;
			line-height: 1.25rem;
			box-shadow: var(--shadow-popover);
			outline: none;
		}

		.ctxm-sub-content[data-state="closed"] {
			pointer-events: none;
			visibility: hidden;
			opacity: 0;
		}

		/*
		 * No entrance animation — a context menu is already a response to a
		 * deliberate invocation, so it appears instantly; its submenus are part
		 * of the same menu and follow the same rule
		 * (timing-no-entrance-context-menu). The exit still animates: it is
		 * feedback that the menu is going away.
		 */
		.ctxm-sub-content[data-closing] {
			pointer-events: none;
			animation: menu-sub-out 100ms cubic-bezier(0.4, 0, 0.7, 0.2) forwards;
		}

		.ctxm-sub-content[data-side="left"][data-closing] {
			animation-name: menu-sub-out-left;
		}

		/* Dark mode is covered by the --shadow-popover token itself
		   (item styles + shared sub keyframes live in shared.templ) */

		/* ── Reduced motion (content animation only; items/sub-content in shared) ── */
		@media (prefers-reduced-motion: reduce) {
			.ctxm-content[data-closing] {
				animation: none;
			}
		}

/* dark-mode.templ */
.dark-mode-toggle {
			position: fixed;
			bottom: 1rem;
			right: 1rem;
			display: flex;
			align-items: center;
			gap: 0.5rem;
			background: var(--card);
			border: 1px solid var(--border);
			border-radius: var(--radius-lg);
			padding: 0.5rem 0.75rem;
			box-shadow: var(--shadow-raised);
			z-index: 50;
		}

/* date-picker.templ */
/* ── Date Picker ── */

		/* Trigger button — outline style, left-aligned text */
		.dp-trigger {
			justify-content: flex-start;
			min-width: 15rem;
			font-weight: 400;
		}

		/* Placeholder state — muted like input placeholder */
		.dp-trigger-placeholder {
			color: var(--muted-foreground);
		}

		/* Label inside trigger */
		[data-slot="dp-label"] {
			flex: 1;
			text-align: left;
		}

		/* Popover content — auto width to fit calendar */
		.dp-trigger + [data-slot="popover-content"] {
			width: auto;
			padding: 0.25rem;
		}

/* dialog.templ */
/* Dialog: hidden by default, flex when open */
		.dialog:not([open]) {
			display: none;
		}
		.dialog {
			position: fixed;
			inset: 0;
			width: 100%;
			height: 100%;
			max-width: none;
			max-height: none;
			margin: 0;
			padding: 0;
			border: none;
			background: transparent;
			overflow-y: auto;
			overscroll-behavior: contain;
			z-index: 50;
		}
		.dialog[open] {
			display: flex;
			align-items: safe center;
			justify-content: center;
		}

		/* Backdrop: shared modal scrim, so dialog and drawer stage alike */
		.dialog::backdrop {
			background: var(--backdrop);
			-webkit-backdrop-filter: blur(var(--backdrop-blur));
			backdrop-filter: blur(var(--backdrop-blur));
		}

		/* Panel: the visible dialog box */
		.dialog-panel {
			position: relative;
			width: 100%;
			max-width: calc(100% - 2rem);
			max-height: calc(100% - 2rem);
			margin: auto;
			overflow-y: auto;
			display: grid;
			gap: 1rem;
			border-radius: var(--radius-xl);
			background: var(--background);
			color: var(--foreground);
			padding: 1rem;
			font-size: 0.875rem;
			line-height: 1.25rem;
			/* Top of the elevation scale (visual-shadow-matches-elevation) */
			box-shadow: var(--shadow-modal);
			outline: none;
		}

		@media (min-width: 640px) {
			.dialog-panel {
				max-width: 24rem;
			}
		}

		/* Close X button — extended hit area for Fitts's Law (min 44px).
		   Radius is concentric with the panel: outer radius minus the
		   0.5rem inset (visual-concentric-radius). */
		.dialog-close-x {
			position: absolute;
			top: 0.5rem;
			right: 0.5rem;
			border-radius: calc(var(--radius-xl) - 0.5rem);
		}
		.dialog-close-x::after {
			content: '';
			position: absolute;
			inset: -0.5rem;
		}

		/* Header */
		.dialog-header {
			display: flex;
			flex-direction: column;
			gap: 0.5rem;
		}

		/* Footer */
		.dialog-footer {
			margin: 0 -1rem -1rem -1rem;
			display: flex;
			flex-direction: column-reverse;
			gap: 0.5rem;
			border-radius: 0 0 var(--radius-xl) var(--radius-xl);
			border-top: 1px solid var(--border);
			background: color-mix(in oklch, var(--muted) 50%, transparent);
			padding: 1rem;
		}

		@media (min-width: 640px) {
			.dialog-footer {
				flex-direction: row;
				justify-content: flex-end;
			}
		}

		/* Title */
		.dialog-title {
			font-size: 1rem;
			line-height: 1;
			font-weight: 500;
			margin: 0;
		}

		/* Description */
		.dialog-description {
			font-size: 0.875rem;
			color: var(--muted-foreground);
			margin: 0;
		}
		.dialog-description a {
			text-decoration: underline;
			text-underline-offset: 3px;
		}
		.dialog-description a:hover {
			color: var(--foreground);
		}

		/*
		 * Animations — asymmetric enter/exit with overlapping action.
		 * Enter: spring easing + slight translateY for organic arc (200ms panel, 150ms backdrop).
		 * Exit: snappy ease-in, faster than enter for responsiveness (120ms panel, 100ms backdrop).
		 */

		/* Open */
		.dialog[open] .dialog-panel {
			animation: dialog-panel-in 200ms cubic-bezier(0.22, 1.1, 0.36, 1);
		}
		.dialog[open]::backdrop {
			animation: dialog-backdrop-in 150ms cubic-bezier(0.33, 1, 0.68, 1);
		}

		/* Close — pointer-events off so a panel on its way out cannot be
		   clicked (the tooltip and context menu do the same) */
		.dialog[data-closing] .dialog-panel {
			pointer-events: none;
			animation: dialog-panel-out 120ms cubic-bezier(0.4, 0, 0.7, 0.2) forwards;
		}
		.dialog[data-closing]::backdrop {
			animation: dialog-backdrop-out 100ms cubic-bezier(0.4, 0, 1, 1) forwards;
		}

		@keyframes dialog-panel-in {
			from { opacity: 0; transform: scale(0.96) translateY(6px); }
			to   { opacity: 1; transform: scale(1) translateY(0); }
		}
		@keyframes dialog-panel-out {
			from { opacity: 1; transform: scale(1) translateY(0); }
			to   { opacity: 0; transform: scale(0.97) translateY(3px); }
		}
		@keyframes dialog-backdrop-in {
			from { opacity: 0; }
			to   { opacity: 1; }
		}
		@keyframes dialog-backdrop-out {
			from { opacity: 1; }
			to   { opacity: 0; }
		}


		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.dialog[open] .dialog-panel,
			.dialog[data-closing] .dialog-panel {
				animation: none;
			}
			.dialog[open]::backdrop,
			.dialog[data-closing]::backdrop {
				animation: none;
			}
		}

/* drawer.templ */
/* Drawer: hidden by default, flex when open */
		.drawer:not([open]) {
			display: none;
		}
		.drawer {
			position: fixed;
			inset: 0;
			width: 100%;
			height: 100%;
			max-width: none;
			max-height: none;
			margin: 0;
			padding: 0;
			border: none;
			background: transparent;
			/* clip, not hidden: a hidden box is still programmatically scrollable,
			   so showModal()'s initial focus scrolls the dialog toward the
			   still-translated panel (right/bottom only — negative-edge overflow
			   isn't scrollable), which fights the slide-in animation. clip makes
			   this box a non-scroll-container, so focus can't scroll it. */
			overflow: hidden;
			overflow: clip;
			overscroll-behavior: contain;
			z-index: 50;
		}
		.drawer[open] {
			display: flex;
		}

		/* Direction alignment */
		.drawer-bottom[open] {
			align-items: flex-end;
			justify-content: center;
		}
		.drawer-top[open] {
			align-items: flex-start;
			justify-content: center;
		}
		.drawer-left[open] {
			align-items: stretch;
			justify-content: flex-start;
		}
		.drawer-right[open] {
			align-items: stretch;
			justify-content: flex-end;
		}

		/* Backdrop: shared modal scrim, so drawer and dialog stage alike */
		.drawer::backdrop {
			background: var(--backdrop);
			-webkit-backdrop-filter: blur(var(--backdrop-blur));
			backdrop-filter: blur(var(--backdrop-blur));
		}

		/* Panel: the visible drawer box */
		.drawer-panel {
			position: relative;
			display: flex;
			flex-direction: column;
			background: var(--background);
			color: var(--foreground);
			font-size: 0.875rem;
			line-height: 1.25rem;
			outline: none;
			overflow-y: auto;
			/* Top of the elevation scale (visual-shadow-matches-elevation) */
			box-shadow: var(--shadow-modal);
		}

		/* Bottom panel */
		.drawer-bottom .drawer-panel {
			width: 100%;
			max-height: 80vh;
			margin-top: 6rem;
			border-top-left-radius: var(--radius-xl);
			border-top-right-radius: var(--radius-xl);
			border-top: 1px solid var(--border);
		}

		/* Top panel */
		.drawer-top .drawer-panel {
			width: 100%;
			max-height: 80vh;
			margin-bottom: 6rem;
			border-bottom-left-radius: var(--radius-xl);
			border-bottom-right-radius: var(--radius-xl);
			border-bottom: 1px solid var(--border);
		}

		/* Left panel */
		.drawer-left .drawer-panel {
			height: 100%;
			width: 75%;
			max-width: 24rem;
			border-right: 1px solid var(--border);
			border-top-right-radius: var(--radius-xl);
			border-bottom-right-radius: var(--radius-xl);
		}

		/* Right panel */
		.drawer-right .drawer-panel {
			height: 100%;
			width: 75%;
			max-width: 24rem;
			border-left: 1px solid var(--border);
			border-top-left-radius: var(--radius-xl);
			border-bottom-left-radius: var(--radius-xl);
		}

		/*
		 * Handle bar — drag target first, close target second.
		 * A handle is the universal "drag me down" signifier, so it drags
		 * (see drawerScript) as well as closing on tap; affording a gesture
		 * it did not have was the mismatch (ux-jakobs-familiar-patterns).
		 * The entire padded region is interactive (data-drawer-close):
		 * 16px + 4px bar + 12px = a 32px strip across the full drawer
		 * width (ux-fitts-target-size). touch-action: none keeps the drag
		 * from being stolen by panel scrolling.
		 */
		.drawer-handle {
			display: flex;
			justify-content: center;
			padding: 1rem 0 0.75rem;
			flex-shrink: 0;
			cursor: grab;
			touch-action: none;
		}
		.drawer-handle:active {
			cursor: grabbing;
		}
			.drawer-handle span {
				display: block;
				width: 100px;
				height: 4px;
				border-radius: 9999px;
				background-color: var(--muted-foreground);
				opacity: 0.4;
				/* Secondary action: handle scales in with spring after panel arrives */
				transform: scaleX(1);
				transition: transform 200ms cubic-bezier(0.34, 1.4, 0.64, 1) 80ms;
			}

		/* Close X button — extended hit area for Fitts's Law (min 44px).
		   Radius is concentric with the panel: outer radius minus the
		   0.5rem inset (visual-concentric-radius). */
		.drawer-close-x {
			position: absolute;
			top: 0.5rem;
			right: 0.5rem;
			border-radius: calc(var(--radius-xl) - 0.5rem);
		}
		.drawer-close-x::after {
			content: '';
			position: absolute;
			inset: -0.5rem;
		}

		/* Header — stages attention on title after panel settles */
		.drawer-header {
			display: flex;
			flex-direction: column;
			gap: 0.5rem;
			padding: 1rem;
			/* Overlapping action: content fades in after panel slide */
			opacity: 1;
			transform: translateY(0);
			transition: opacity 150ms ease-out 60ms, transform 150ms ease-out 60ms;
		}
		.drawer-bottom .drawer-header,
		.drawer-top .drawer-header {
			text-align: center;
		}
		@media (min-width: 768px) {
			.drawer-bottom .drawer-header,
			.drawer-top .drawer-header {
				text-align: left;
			}
		}

		/* Footer — Law of Common Region: visually distinct action zone */
		.drawer-footer {
			margin-top: auto;
			display: flex;
			flex-direction: column;
			gap: 0.5rem;
			border-top: 1px solid var(--border);
			background: color-mix(in oklch, var(--muted) 50%, transparent);
			padding: 1rem;
			/* Overlapping action: footer reveals after header */
			opacity: 1;
			transform: translateY(0);
			transition: opacity 150ms ease-out 100ms, transform 150ms ease-out 100ms;
		}

		/* Title */
		.drawer-title {
			font-size: 1rem;
			line-height: 1;
			font-weight: 500;
			margin: 0;
		}

		/* Description */
		.drawer-description {
			font-size: 0.875rem;
			color: var(--muted-foreground);
			margin: 0;
		}

		/*
		 * Animations
		 *
		 * Staging: backdrop leads (150ms), panel follows with spring easing (280ms).
		 * Panel slides at full opacity — real drawers don't ghost while moving.
		 * Exit: snappy ease-in (150ms panel, 120ms backdrop), faster than enter.
		 * Content: @starting-style stagger creates follow-through inside the panel.
		 */

		/* === Enter: panel slides (spring easing with micro-overshoot) === */
		.drawer-bottom[open] .drawer-panel {
			animation: drawer-slide-up-in 280ms cubic-bezier(0.22, 1.12, 0.36, 1);
		}
		.drawer-top[open] .drawer-panel {
			animation: drawer-slide-down-in 280ms cubic-bezier(0.22, 1.12, 0.36, 1);
		}
		.drawer-left[open] .drawer-panel {
			animation: drawer-slide-right-in 280ms cubic-bezier(0.22, 1.12, 0.36, 1);
		}
		.drawer-right[open] .drawer-panel {
			animation: drawer-slide-left-in 280ms cubic-bezier(0.22, 1.12, 0.36, 1);
		}

		/* Enter: backdrop leads panel (faster, sets the stage) */
		.drawer[open]::backdrop {
			animation: drawer-backdrop-in 150ms cubic-bezier(0.33, 1, 0.68, 1);
		}

		/* === Exit: panel slides out (snappy, no spring) ===
		   pointer-events off so a panel on its way out cannot be clicked. */
		.drawer[data-closing] .drawer-panel {
			pointer-events: none;
		}
		.drawer-bottom[data-closing] .drawer-panel {
			animation: drawer-slide-up-out 150ms cubic-bezier(0.4, 0, 0.7, 0.2) forwards;
		}
		.drawer-top[data-closing] .drawer-panel {
			animation: drawer-slide-down-out 150ms cubic-bezier(0.4, 0, 0.7, 0.2) forwards;
		}
		.drawer-left[data-closing] .drawer-panel {
			animation: drawer-slide-right-out 150ms cubic-bezier(0.4, 0, 0.7, 0.2) forwards;
		}
		.drawer-right[data-closing] .drawer-panel {
			animation: drawer-slide-left-out 150ms cubic-bezier(0.4, 0, 0.7, 0.2) forwards;
		}

		/* Exit: backdrop follows panel (slightly delayed dissolve) */
		.drawer[data-closing]::backdrop {
			animation: drawer-backdrop-out 100ms cubic-bezier(0.4, 0, 1, 1) forwards;
		}

		/* Panel keyframes — pure transform, no opacity (drawers don't ghost) */
		@keyframes drawer-slide-up-in {
			from { transform: translateY(100%); }
			to   { transform: translateY(0); }
		}
		/* Exit starts from wherever the finger left the panel
		   (--drawer-drag-y is stamped on touchend), so a flung-away drawer
		   continues the drag instead of snapping back to zero first. It is
		   0px for every close that did not come from a drag. */
		@keyframes drawer-slide-up-out {
			from { transform: translateY(var(--drawer-drag-y, 0px)); }
			to   { transform: translateY(100%); }
		}
		@keyframes drawer-slide-down-in {
			from { transform: translateY(-100%); }
			to   { transform: translateY(0); }
		}
		@keyframes drawer-slide-down-out {
			from { transform: translateY(0); }
			to   { transform: translateY(-100%); }
		}
		@keyframes drawer-slide-right-in {
			from { transform: translateX(-100%); }
			to   { transform: translateX(0); }
		}
		@keyframes drawer-slide-right-out {
			from { transform: translateX(0); }
			to   { transform: translateX(-100%); }
		}
		@keyframes drawer-slide-left-in {
			from { transform: translateX(100%); }
			to   { transform: translateX(0); }
		}
		@keyframes drawer-slide-left-out {
			from { transform: translateX(0); }
			to   { transform: translateX(100%); }
		}

		/* Backdrop keyframes */
		@keyframes drawer-backdrop-in {
			from { opacity: 0; }
			to   { opacity: 1; }
		}
		@keyframes drawer-backdrop-out {
			from { opacity: 1; }
			to   { opacity: 0; }
		}

		/*
		 * Follow-through: content reveals after panel settles.
		 * Header staggered at 60ms, footer at 100ms via transition-delay above.
		 * Handle scales in with spring at 80ms (secondary action).
		 */
		@starting-style {
			.drawer[open] .drawer-header {
				opacity: 0;
				transform: translateY(6px);
			}
			.drawer[open] .drawer-footer {
				opacity: 0;
				transform: translateY(6px);
			}
			.drawer[open] .drawer-handle span {
				transform: scaleX(0.4);
			}
		}

		/* Dark mode is covered by the --shadow-modal token itself */
		.dark .drawer-handle span {
			background-color: var(--muted-foreground);
			opacity: 0.3;
		}
		.dark .drawer-footer {
			background: color-mix(in oklch, var(--muted) 30%, transparent);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.drawer[open] .drawer-panel,
			.drawer[data-closing] .drawer-panel {
				animation: none;
			}
			.drawer[open]::backdrop,
			.drawer[data-closing]::backdrop {
				animation: none;
			}
			.drawer-header,
			.drawer-footer {
				transition: none;
			}
			.drawer-handle span {
				transition: none;
			}
		}

/* dropdown-menu.templ */
/* ── Root ── */
		.ddm {
			position: relative;
			display: inline-flex;
		}

		/* ── Trigger ── */
		.ddm-trigger {
			cursor: pointer;
		}

		/* ── Content ── */
		.ddm-content {
			position: absolute;
			z-index: 50;
			min-width: 12rem;
			border-radius: var(--radius-md);
			/* Same elevation tier as popover and command, so it draws from the
			   same surface tokens — a theme that pulls --popover away from
			   --background must move every floating panel together. */
			background: var(--popover);
			padding: 0.25rem;
			color: var(--popover-foreground);
			font-size: 0.875rem;
			line-height: 1.25rem;
			box-shadow: var(--shadow-popover);
			outline: none;
			/*
			 * --ddm-base-* holds the alignment offset (e.g. -50% for centering).
			 * --ddm-slide-* holds the directional entrance offset per side.
			 * Both are composed into the keyframes so the base position is
			 * never lost while the animation owns the transform property.
			 */
			--ddm-base-x: 0px;
			--ddm-base-y: 0px;
			--ddm-slide-x: 0px;
			--ddm-slide-y: 0px;
			transform: translate(var(--ddm-base-x), var(--ddm-base-y));
		}

		/* Hidden when closed */
		.ddm-content[data-state="closed"] {
			pointer-events: none;
			visibility: hidden;
			opacity: 0;
		}

		/*
		 * Side positioning + directional slide offsets.
		 * transform-origin anchors the scale to the trigger edge and
		 * --ddm-slide-* starts the panel on the trigger side, so it grows
		 * outward FROM the trigger instead of drifting the same direction
		 * regardless of where it is anchored (Solid Drawing + Staging).
		 * Travel is 2px against the popover 4px: this panel is lighter and
		 * 30ms quicker, so it needs less runway.
		 */

		/* Side: bottom (default) */
		.ddm-content[data-side="bottom"] {
			top: calc(100% + 4px);
			transform-origin: top;
			--ddm-slide-y: -2px;
		}
		.ddm-content[data-side="top"] {
			bottom: calc(100% + 4px);
			transform-origin: bottom;
			--ddm-slide-y: 2px;
		}
		.ddm-content[data-side="left"] {
			right: calc(100% + 4px);
			top: 0;
			transform-origin: right;
			--ddm-slide-x: 2px;
		}
		.ddm-content[data-side="right"] {
			left: calc(100% + 4px);
			top: 0;
			transform-origin: left;
			--ddm-slide-x: -2px;
		}

		/* Align: start (default) */
		.ddm-content[data-side="bottom"][data-align="start"],
		.ddm-content[data-side="top"][data-align="start"] {
			left: 0;
		}
		.ddm-content[data-side="bottom"][data-align="center"],
		.ddm-content[data-side="top"][data-align="center"] {
			left: 50%;
			--ddm-base-x: -50%;
		}
		.ddm-content[data-side="bottom"][data-align="end"],
		.ddm-content[data-side="top"][data-align="end"] {
			right: 0;
		}
		.ddm-content[data-side="left"][data-align="start"],
		.ddm-content[data-side="right"][data-align="start"] {
			top: 0;
		}
		.ddm-content[data-side="left"][data-align="center"],
		.ddm-content[data-side="right"][data-align="center"] {
			top: 50%;
			--ddm-base-y: -50%;
		}
		.ddm-content[data-side="left"][data-align="end"],
		.ddm-content[data-side="right"][data-align="end"] {
			top: auto;
			bottom: 0;
		}

		/* Open animation */
		.ddm-content[data-state="open"] {
			animation: ddm-in 150ms cubic-bezier(0.22, 1.1, 0.36, 1);
		}

		/* Close animation */
		.ddm-content[data-closing] {
			pointer-events: none;
			animation: ddm-out 100ms cubic-bezier(0.4, 0, 0.7, 0.2) forwards;
		}

		@keyframes ddm-in {
			from {
				opacity: 0;
				transform: translate(var(--ddm-base-x), var(--ddm-base-y))
					scale(0.95)
					translate(var(--ddm-slide-x), var(--ddm-slide-y));
			}
			to {
				opacity: 1;
				transform: translate(var(--ddm-base-x), var(--ddm-base-y))
					scale(1);
			}
		}

		/* Exit covers half the entrance distance — the leaving panel should
		   yield attention, not draw a second trip across the screen. */
		@keyframes ddm-out {
			from {
				opacity: 1;
				transform: translate(var(--ddm-base-x), var(--ddm-base-y))
					scale(1);
			}
			to {
				opacity: 0;
				transform: translate(var(--ddm-base-x), var(--ddm-base-y))
					scale(0.95)
					translate(
						calc(var(--ddm-slide-x) * 0.5),
						calc(var(--ddm-slide-y) * 0.5)
					);
			}
		}

		/* ── Sub content ── */
		.ddm-sub {
			position: relative;
		}

		.ddm-sub-content {
			position: absolute;
			z-index: 50;
			min-width: 8rem;
			border-radius: var(--radius-md);
			background: var(--popover);
			padding: 0.25rem;
			color: var(--popover-foreground);
			font-size: 0.875rem;
			line-height: 1.25rem;
			box-shadow: var(--shadow-popover);
			outline: none;
		}

		.ddm-sub-content[data-state="closed"] {
			pointer-events: none;
			visibility: hidden;
			opacity: 0;
		}

		/* Sub positioning */
		.ddm-sub-content[data-side="right"] {
			left: 100%;
			top: -0.1875rem;
		}
		.ddm-sub-content[data-side="left"] {
			right: 100%;
			top: -0.1875rem;
		}

		.ddm-sub-content[data-state="open"] {
			animation: menu-sub-in 150ms cubic-bezier(0.22, 1.1, 0.36, 1);
		}
		.ddm-sub-content[data-closing] {
			pointer-events: none;
			animation: menu-sub-out 100ms cubic-bezier(0.4, 0, 0.7, 0.2) forwards;
		}

		.ddm-sub-content[data-side="left"][data-state="open"] {
			animation-name: menu-sub-in-left;
		}
		.ddm-sub-content[data-side="left"][data-closing] {
			animation-name: menu-sub-out-left;
		}

		/* Dark mode is covered by the --shadow-popover token itself
		   (item styles + shared sub keyframes live in shared.templ) */

		/* ── Reduced motion (content animation only; items/sub-content in shared) ── */
		@media (prefers-reduced-motion: reduce) {
			.ddm-content[data-state="open"],
			.ddm-content[data-closing] {
				animation: none;
			}
		}

/* empty.templ */
/* Empty base */
		.empty {
			display: flex;
			width: 100%;
			min-width: 0;
			flex: 1;
			flex-direction: column;
			align-items: center;
			justify-content: center;
			gap: 1rem;
			border-radius: var(--radius-xl);
			border: 1px dashed var(--border);
			padding: 1.5rem;
			text-align: center;
			/* balance is a heading treatment; the description opts back out
			   to pretty below (type-text-wrap-balance-headings) */
			text-wrap: balance;
		}

		/* EmptyHeader */
		.empty-header {
			display: flex;
			max-width: 24rem;
			flex-direction: column;
			align-items: center;
			gap: 0.5rem;
		}

		/* EmptyMedia base */
		.empty-media {
			margin-bottom: 0.5rem;
			display: flex;
			flex-shrink: 0;
			align-items: center;
			justify-content: center;
		}

		.empty-media svg {
			pointer-events: none;
			flex-shrink: 0;
		}

		/* EmptyMedia variant: icon */
		.empty-media-icon {
			display: flex;
			width: 2rem;
			height: 2rem;
			flex-shrink: 0;
			align-items: center;
			justify-content: center;
			border-radius: var(--radius-lg);
			background-color: var(--muted);
			color: var(--foreground);
		}

		.empty-media-icon svg {
			width: 1rem;
			height: 1rem;
		}

		/* EmptyTitle */
		.empty-title {
			font-size: 0.875rem;
			font-weight: 500;
			letter-spacing: -0.01em;
		}

		/* EmptyDescription */
		.empty-description {
			font-size: 0.875rem;
			line-height: 1.625;
			color: var(--muted-foreground);
			/* Body copy, not a heading — pretty trims orphans instead */
			text-wrap: pretty;
		}

		.empty-description a {
			text-decoration: underline;
			text-underline-offset: 3px;
		}

		.empty-description a:hover {
			color: var(--foreground);
		}

		/* EmptyContent */
		.empty-content {
			display: flex;
			width: 100%;
			max-width: 24rem;
			min-width: 0;
			flex-direction: column;
			align-items: center;
			gap: 0.625rem;
			font-size: 0.875rem;
			text-wrap: balance;
		}

		/* Dark mode needs no overrides here — every color above is a token
		   that already flips. */

/* error.templ */
/*
		 * Peak-end: the error page is the worst page to leave unpolished, so it
		 * gets the same Empty-state treatment as any other dead end in the app.
		 */
		.error-main {
			display: flex;
			align-items: center;
			justify-content: center;
			min-height: 100dvh;
			padding: 1.5rem;
		}

		.error-empty {
			max-width: 28rem;
			flex: none;
			border: none;
		}

		/* Von Restorff — the status code is the one number worth reporting */
		.error-code {
			font-size: 0.75rem;
			line-height: 1rem;
			font-weight: 500;
			font-variant-numeric: tabular-nums;
			letter-spacing: 0.08em;
			color: var(--muted-foreground);
		}

/* field.templ */
/* ── FieldSet ── */
		.field-set {
			display: flex;
			flex-direction: column;
			gap: 1rem;
			border: none;
			margin: 0;
			padding: 0;
		}
		.field-set:has(> [data-slot="checkbox-group"]),
		.field-set:has(> [data-slot="radio-group"]) {
			gap: 0.75rem;
		}

		/* ── FieldLegend ── */
		.field-legend {
			margin-bottom: 0.375rem;
			font-weight: 500;
			text-wrap: balance;
		}
		.field-legend[data-variant="label"] {
			font-size: 0.875rem;
			line-height: 1.25rem;
		}
		.field-legend[data-variant="legend"] {
			font-size: 1rem;
			line-height: 1.5rem;
		}

		/* ── FieldGroup ── */
		.field-group {
			container-type: inline-size;
			container-name: field-group;
			display: flex;
			width: 100%;
			flex-direction: column;
			gap: 1.25rem;
		}
		.field-group .field-group {
			gap: 1rem;
		}

		/* ── Field ── */
		.field {
			display: flex;
			width: 100%;
			gap: 0.5rem;
			transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}
		/* Invalid: wrapper convention (data-invalid, from the Invalid prop) or
		   control convention (aria-invalid on the input, as input/input-group
		   style off) — either one lights up the whole field */
		.field[data-invalid="true"],
		.field:has([aria-invalid="true"]) {
			color: var(--destructive);
		}

		/* Orientation: vertical */
		.field[data-orientation="vertical"] {
			flex-direction: column;
		}
		.field[data-orientation="vertical"] > * {
			width: 100%;
		}
		.field[data-orientation="vertical"] > .sr-only {
			width: auto;
		}

		/* Orientation: horizontal */
		.field[data-orientation="horizontal"] {
			flex-direction: row;
			align-items: center;
		}
		.field[data-orientation="horizontal"]:has(> [data-slot="field-content"]) {
			align-items: flex-start;
		}
		.field[data-orientation="horizontal"] > [data-slot="field-label"],
		.field[data-orientation="horizontal"] > [data-slot="field-title"] {
			flex: 1 1 auto;
		}
		.field[data-orientation="horizontal"]:has(> [data-slot="field-content"]) [role="checkbox"],
		.field[data-orientation="horizontal"]:has(> [data-slot="field-content"]) [role="radio"] {
			margin-top: 1px;
		}

		/* Orientation: responsive (uses container query) */
		.field[data-orientation="responsive"] {
			flex-direction: column;
		}
		.field[data-orientation="responsive"] > * {
			width: 100%;
		}
		.field[data-orientation="responsive"] > .sr-only {
			width: auto;
		}
		@container field-group (min-width: 28rem) {
			.field[data-orientation="responsive"] {
				flex-direction: row;
				align-items: center;
			}
			.field[data-orientation="responsive"] > * {
				width: auto;
			}
			.field[data-orientation="responsive"]:has(> [data-slot="field-content"]) {
				align-items: flex-start;
			}
			.field[data-orientation="responsive"] > [data-slot="field-label"],
			.field[data-orientation="responsive"] > [data-slot="field-title"] {
				flex: 1 1 auto;
			}
			.field[data-orientation="responsive"]:has(> [data-slot="field-content"]) [role="checkbox"],
			.field[data-orientation="responsive"]:has(> [data-slot="field-content"]) [role="radio"] {
				margin-top: 1px;
			}
		}

		/* ── FieldContent ── */
		.field-content {
			display: flex;
			flex: 1;
			flex-direction: column;
			gap: 0.125rem;
			line-height: 1.375;
		}

		/* ── FieldLabel ── */
		.field-label {
			display: flex;
			width: fit-content;
			gap: 0.5rem;
			line-height: 1.375;
			font-size: 0.875rem;
			font-weight: 500;
			user-select: none;
			cursor: pointer;
			transition-property: opacity, background-color, border-color;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
		}
		.field[data-disabled="true"] .field-label {
			pointer-events: none;
			opacity: 0.5;
		}
		/* Card-style highlight when containing checked inputs. Scoped to the card
		   layout below — on a plain inline label the tint has no border or
		   padding to sit in and reads as a rectangle glued to the text. */
		.field-label:has(> [data-slot="field"]):has(:checked) {
			border-color: color-mix(in oklch, var(--primary) 30%, transparent);
			background-color: color-mix(in oklch, var(--primary) 5%, transparent);
		}
		/* Card layout when wrapping a nested field */
		.field-label:has(> [data-slot="field"]) {
			border-radius: var(--radius-lg);
			border: 1px solid var(--border);
			width: 100%;
			flex-direction: column;
		}
		.field-label > [data-slot="field"] {
			padding: 0.625rem;
		}
		/* Dark mode */
		.dark .field-label:has(> [data-slot="field"]):has(:checked) {
			border-color: color-mix(in oklch, var(--primary) 20%, transparent);
			background-color: color-mix(in oklch, var(--primary) 10%, transparent);
		}

		/* ── FieldTitle ── */
		.field-title {
			display: flex;
			width: fit-content;
			align-items: center;
			gap: 0.5rem;
			font-size: 0.875rem;
			line-height: 1.375;
			font-weight: 500;
			transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}
		.field[data-disabled="true"] .field-title {
			opacity: 0.5;
		}

		/* ── FieldDescription ── */
		.field-description {
			text-align: left;
			font-size: 0.875rem;
			line-height: 1.5;
			font-weight: 400;
			color: var(--muted-foreground);
			text-wrap: pretty;
		}
		.field[data-orientation="horizontal"] .field-description {
			text-wrap: balance;
		}
		[data-variant="legend"] + .field-description,
		[data-variant="label"] + .field-description {
			margin-top: -0.375rem;
		}
		.field-description:last-child {
			margin-top: 0;
		}
		.field-description:nth-last-child(2) {
			margin-top: -0.25rem;
		}
		.field-description a {
			text-decoration: underline;
			text-underline-offset: 4px;
			transition: color 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}
		.field-description a:hover {
			color: var(--primary);
		}

		/* ── FieldSeparator ── */
		.field-separator {
			position: relative;
			margin-top: -0.5rem;
			margin-bottom: -0.5rem;
			height: 1.25rem;
			font-size: 0.875rem;
		}
		.field-separator-line {
			position: absolute;
			left: 0;
			right: 0;
			top: 50%;
			border: none;
			height: 1px;
			background-color: var(--border);
			margin: 0;
		}
		.field-separator-content {
			position: relative;
			display: block;
			margin-left: auto;
			margin-right: auto;
			width: fit-content;
			background-color: var(--background);
			padding-left: 0.5rem;
			padding-right: 0.5rem;
			color: var(--muted-foreground);
		}
		.field-separator-content:empty {
			display: none;
		}

		/* ── FieldError ── */
		.field-error {
			font-size: 0.875rem;
			font-weight: 400;
			color: var(--destructive);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.field,
			.field-label,
			.field-title,
			.field-description a {
				transition-duration: 75ms;
			}
		}

/* input-group.templ */
/* InputGroup: Base */
		.input-group {
			position: relative;
			display: flex;
			height: 2.25rem;
			width: 100%;
			min-width: 0;
			align-items: center;
			border-radius: var(--radius-lg);
			border: 1px solid var(--input);
			cursor: text;
			/* Field elevation — the group is one control (Law of Common Region) */
			box-shadow: var(--shadow-field);
			transition-property: color, background-color, border-color, box-shadow, opacity;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
			outline: none;
		}

		/* Auto height for textarea */
		.input-group:has(> textarea),
		.input-group.input-group-has-textarea {
			height: auto;
		}

		/* Focus ring belongs to the text control only. An embedded
		   InputGroupButton keeps its own ring (the --btn-ring slot survives the
		   flattening below), so ringing the whole group on any focus-within
		   would put two focus indicators on screen at once. */
		.input-group:has(.input-group-control:focus-visible),
		.input-group:has(.input-group-control-textarea:focus-visible) {
			border-color: var(--ring);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		/* Invalid styling */
		.input-group:has([aria-invalid="true"]) {
			border-color: var(--destructive);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
		}

		/* Disabled styling */
		.input-group[data-disabled="true"] {
			background-color: color-mix(in oklch, var(--input) 50%, transparent);
			opacity: 0.5;
			pointer-events: none;
		}

		/* InputGroupAddon: Base */
		.input-group-addon {
			display: flex;
			height: auto;
			cursor: text;
			align-items: center;
			justify-content: center;
			gap: 0.5rem;
			padding-top: 0.375rem;
			padding-bottom: 0.375rem;
			font-size: 0.875rem;
			font-weight: 500;
			color: var(--muted-foreground);
			user-select: none;
			transition-property: color;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
		}

		/* Secondary action: addon content shifts to foreground on focus */
		.input-group:focus-within .input-group-addon {
			color: var(--foreground);
		}

		/* Keep addon muted on invalid -- don't compete with error signal */
		.input-group:has([aria-invalid="true"]) .input-group-addon {
			color: var(--muted-foreground);
		}

		.input-group[data-disabled="true"] .input-group-addon {
			opacity: 0.5;
		}

		.input-group-addon svg:not([class*="size-"]) {
			width: 1rem;
			height: 1rem;
		}

		/* Addon: inline-start */
		.input-group-addon-inline-start {
			order: -1;
			padding-left: 0.5rem;
		}

		/* Addon: inline-end */
		.input-group-addon-inline-end {
			order: 999;
			padding-right: 0.5rem;
		}


		/* Input inside group: remove borders and shadows */
		.input-group .input-group-control {
			flex: 1;
			border-radius: 0;
			border: 0;
			background-color: transparent;
			box-shadow: none;
			outline: none;
		}

		.input-group .input-group-control:focus-visible {
			box-shadow: none;
			border-color: transparent;
		}

		.input-group .input-group-control:disabled {
			background-color: transparent;
		}

		.input-group .input-group-control[aria-invalid="true"] {
			box-shadow: none;
		}

		/* Adjust input padding when addon is present */
		.input-group:has(> .input-group-addon-inline-start) > .input-group-control {
			padding-left: 0.375rem;
		}

		.input-group:has(> .input-group-addon-inline-end) > .input-group-control {
			padding-right: 0.375rem;
		}


		/* Textarea inside group */
		.input-group .input-group-control-textarea {
			flex: 1;
			resize: none;
			border-radius: 0;
			border: 0;
			background-color: transparent;
			padding: 0.5rem 0.625rem;
			box-shadow: none;
			outline: none;
		}

		.input-group .input-group-control-textarea:focus-visible {
			box-shadow: none;
			border-color: transparent;
		}

		.input-group .input-group-control-textarea:disabled {
			background-color: transparent;
		}

		.input-group .input-group-control-textarea[aria-invalid="true"] {
			box-shadow: none;
		}

		/* InputGroupButton — flat inside the field: the wrapper owns the
		   elevation, so the shadow slots are zeroed. Zeroing the slots is the
		   whole fix; a box-shadow: none here would also swallow the composed
		   focus ring, which has to survive. */
		.input-group-btn {
			--btn-shadow: 0 0 rgba(0, 0, 0, 0);
			--btn-shadow-pressed: 0 0 rgba(0, 0, 0, 0);
		}

		.input-group-btn-size-xs {
			height: 1.5rem;
			gap: 0.25rem;
			border-radius: calc(var(--radius-md) - 3px);
			padding-left: 0.375rem;
			padding-right: 0.375rem;
		}

		.input-group-btn-size-xs svg:not([class*="size-"]) {
			width: 0.875rem;
			height: 0.875rem;
		}

		/* Fitts — 24px boxes get a 32px pointer target via a pseudo-element,
		   so the addon stays visually compact inside the field */
		.btn.input-group-btn-size-xs::after,
		.btn.input-group-btn-size-icon-xs::after {
			content: '';
			position: absolute;
			inset: -0.25rem;
		}

		.input-group-btn-size-sm {
			/* inherits from button sm */
		}

		.input-group-btn-size-icon-xs {
			width: 1.5rem;
			height: 1.5rem;
			border-radius: calc(var(--radius-md) - 3px);
			padding: 0;
		}

		.input-group-btn-size-icon-sm {
			width: 2rem;
			height: 2rem;
			padding: 0;
		}

		/* InputGroupText */
		.input-group-text {
			display: flex;
			align-items: center;
			gap: 0.5rem;
			font-size: 0.875rem;
			color: inherit;
		}

		.input-group-text svg {
			pointer-events: none;
		}

		.input-group-text svg:not([class*="size-"]) {
			width: 1rem;
			height: 1rem;
		}

		/* Dark mode */
		.dark .input-group {
			background-color: color-mix(in oklch, var(--input) 30%, transparent);
		}

		.dark .input-group[data-disabled="true"] {
			background-color: color-mix(in oklch, var(--input) 80%, transparent);
		}

		.dark .input-group:has([aria-invalid="true"]) {
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 40%, transparent);
		}

		.dark .input-group .input-group-control {
			background-color: transparent;
		}

		.dark .input-group .input-group-control:disabled {
			background-color: transparent;
		}

		.dark .input-group .input-group-control-textarea {
			background-color: transparent;
		}

		.dark .input-group .input-group-control-textarea:disabled {
			background-color: transparent;
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.input-group,
			.input-group-addon {
				transition-duration: 75ms;
			}
		}

/* input.templ */
/* Base */
		.input {
			height: 2.25rem;
			width: 100%;
			min-width: 0;
			border-radius: var(--radius-lg);
			border: 1px solid var(--input);
			background-color: transparent;
			padding: 0.25rem 0.75rem;
			font-size: 1rem;
			line-height: 1.5rem;
			/* Field elevation — a hairline lift off the page (--shadow-field) */
			box-shadow: var(--shadow-field);
			transition-property: color, background-color, border-color, box-shadow, opacity;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
			outline: none;
		}

		.input::file-selector-button {
			display: inline-flex;
			height: 1.5rem;
			border: 0;
			background-color: transparent;
			font-size: 0.875rem;
			line-height: 1.25rem;
			font-weight: 500;
			color: var(--foreground);
		}

		.input::placeholder {
			color: var(--muted-foreground);
		}

		.input:focus-visible {
			border-color: var(--ring);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		.input:disabled {
			pointer-events: none;
			cursor: not-allowed;
			background-color: color-mix(in oklch, var(--input) 50%, transparent);
			opacity: 0.5;
		}

		.input[aria-invalid="true"] {
			border-color: var(--destructive);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
		}

		@media (min-width: 768px) {
			.input {
				font-size: 0.875rem;
				line-height: 1.25rem;
			}
		}

		/* Dark mode */
		.dark .input {
			background-color: color-mix(in oklch, var(--input) 30%, transparent);
		}
		.dark .input:disabled {
			background-color: color-mix(in oklch, var(--input) 80%, transparent);
		}
		.dark .input[aria-invalid="true"] {
			border-color: color-mix(in oklch, var(--destructive) 50%, transparent);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 40%, transparent);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.input {
				transition-duration: 75ms;
			}
		}

/* kbd.templ */
/* Kbd
		   Keycap affordance: border + bottom shadow mimics a physical key,
		   triggering instant recognition (Mental Model / Jakob's Law).
		   Distinct from badges via the shadow depth cue (Von Restorff). */
		.kbd {
			pointer-events: none;
			display: inline-flex;
			height: 1.25rem;
			width: fit-content;
			min-width: 1.25rem;
			align-items: center;
			justify-content: center;
			gap: 0.25rem;
			border-radius: calc(var(--radius) - 4px);
			border: 1px solid var(--border);
			background-color: var(--muted);
			padding-left: 0.375rem;
			padding-right: 0.375rem;
			font-family: 'Geist', ui-sans-serif, system-ui, sans-serif;
			/* Slashed zero — a keycap must not leave Cmd-0 and Cmd-O ambiguous */
			font-variant-numeric: slashed-zero tabular-nums;
			font-size: 0.75rem;
			line-height: 1rem;
			font-weight: 500;
			color: var(--muted-foreground);
			user-select: none;
			/* Keycap anatomy: lit top edge, shaded bottom inner edge, a hard
			   1px bottom lip, and a soft drop (visual-layered-shadows) */
			box-shadow:
				var(--highlight-top),
				var(--shade-bottom),
				0 1px 0 0 color-mix(in oklch, var(--foreground) 10%, transparent),
				var(--shadow-field);
		}

		.kbd svg {
			width: 0.75rem;
			height: 0.75rem;
		}

		/* Inside tooltip: adapt to dark surface (Cognitive Load — immediate readability) */
		[data-slot="tooltip-content"] .kbd {
			border-color: color-mix(in oklch, var(--background) 25%, transparent);
			background-color: color-mix(in oklch, var(--background) 20%, transparent);
			color: var(--background);
			box-shadow: 0 1px 0 0 color-mix(in oklch, var(--background) 15%, transparent);
		}

		/* Kbd Group (Chunking — related keys grouped with consistent spacing) */
		.kbd-group {
			display: inline-flex;
			align-items: center;
			gap: 0.25rem;
		}

		/* Dark mode — the keycap shadow atoms flip via the elevation tokens */
		.dark .kbd {
			border-color: color-mix(in oklch, var(--foreground) 12%, transparent);
		}

		.dark [data-slot="tooltip-content"] .kbd {
			border-color: color-mix(in oklch, var(--background) 15%, transparent);
			background-color: color-mix(in oklch, var(--background) 10%, transparent);
			box-shadow: 0 1px 0 0 color-mix(in oklch, var(--background) 8%, transparent);
		}

/* label.templ */
/* Base */
		.label {
			display: flex;
			align-items: center;
			gap: 0.5rem;
			font-size: 0.875rem;
			line-height: 1;
			font-weight: 500;
			user-select: none;
			transition-property: color, opacity;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
		}

		/* Disabled via parent group */
		[data-disabled="true"] .label {
			pointer-events: none;
			opacity: 0.5;
		}

		/* Disabled via sibling input — either order. The subsequent-sibling pair
		   covers input-then-label; the :has() twin covers label-then-input,
		   which is the more common markup order. */
		.input:disabled ~ .label,
		.label:has(~ .input:disabled) {
			cursor: not-allowed;
			opacity: 0.5;
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.label {
				transition-duration: 75ms;
			}
		}

/* native-select.templ */
/* Base wrapper */
		.native-select-wrapper {
			position: relative;
			width: fit-content;
		}
		.native-select-wrapper:has(select:disabled) {
			opacity: 0.5;
		}

		/* Base select */
		.native-select {
			height: 2.25rem;
			width: 100%;
			min-width: 0;
			appearance: none;
			border-radius: var(--radius-lg);
			border: 1px solid var(--input);
			background-color: transparent;
			/* Field elevation — a hairline lift off the page (--shadow-field) */
			box-shadow: var(--shadow-field);
			padding: 0.25rem 2rem 0.25rem 0.625rem;
			/* 1rem until md — a focused control under 16px makes iOS Safari
			   zoom the viewport. Same guard as Input and Textarea. */
			font-size: 1rem;
			line-height: 1.5rem;
			transition-property: color, background-color, border-color, box-shadow, opacity;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
			outline: none;
			user-select: none;
		}

		.native-select:hover:not(:disabled) {
			border-color: color-mix(in oklch, var(--ring) 40%, transparent);
		}

		.native-select:focus-visible {
			border-color: var(--ring);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		.native-select:disabled {
			pointer-events: none;
			cursor: not-allowed;
		}

		.native-select[aria-invalid="true"] {
			border-color: var(--destructive);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
		}

		/* Size: sm */
		.native-select[data-size="sm"] {
			height: 2rem;
			padding-top: 0.125rem;
			padding-bottom: 0.125rem;
			border-radius: min(var(--radius-md), 10px);
		}

		@media (min-width: 768px) {
			.native-select {
				font-size: 0.875rem;
				line-height: 1.25rem;
			}
		}

		/* Chevron icon */
		.native-select-icon {
			position: absolute;
			top: 50%;
			right: 0.625rem;
			transform: translateY(-50%);
			pointer-events: none;
			user-select: none;
			color: var(--muted-foreground);
			display: flex;
			align-items: center;
			justify-content: center;
			transition-property: color, transform;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
		}
		.native-select-icon svg {
			width: 1rem;
			height: 1rem;
		}

		/* Chevron reacts to hover and focus (secondary action / anticipation) */
		.native-select-wrapper:has(select:hover:not(:disabled)) .native-select-icon {
			color: var(--foreground);
		}
		/* Focus brightens the chevron but does NOT rotate it: rotation means
		   "open" everywhere else in the library, and tabbing through a form
		   must not animate a control that has not been opened. */
		.native-select-wrapper:has(select:focus-visible) .native-select-icon {
			color: var(--foreground);
		}

		/* Dark mode */
		.dark .native-select {
			background-color: color-mix(in oklch, var(--input) 30%, transparent);
		}
		.dark .native-select:hover {
			background-color: color-mix(in oklch, var(--input) 50%, transparent);
		}
		.dark .native-select[aria-invalid="true"] {
			border-color: color-mix(in oklch, var(--destructive) 50%, transparent);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 40%, transparent);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.native-select,
			.native-select-icon {
				transition-duration: 75ms;
			}
		}

/* pagination.templ */
/* Pagination nav */
		.pagination {
			margin-left: auto;
			margin-right: auto;
			display: flex;
			width: 100%;
			justify-content: center;
		}

		/* Content list — tight gap keeps the page run reading as one group
		   (ux-proximity-grouping); the targets themselves carry the size */
		.pagination-content {
			display: flex;
			align-items: center;
			gap: 0.25rem;
			list-style: none;
			padding: 0;
			margin: 0;
		}

		/* Link base */
		.pagination-link {
			text-decoration: none;
			cursor: pointer;
			/* Tabular figures — page numbers keep an even rhythm and the
			   active page does not shift width when it goes bold */
			font-variant-numeric: tabular-nums;
		}

		/*
		 * Active page.
		 *
		 * The current page is the one item in the row that CANNOT be clicked,
		 * so it must not carry the strongest press affordance. A raised face
		 * (border + elevation) inverted the signal: the dead element looked
		 * the most interactive, and every live target looked flat beside it
		 * (ux-similarity-consistency, visual-shadow-matches-elevation).
		 *
		 * A tinted chip is not available either — --muted, --accent and
		 * --secondary are all the same value as the ghost hover wash, so a
		 * chip would render identically to simply hovering a neighbour.
		 *
		 * So the current page is marked rather than raised: full-strength
		 * foreground, heavier weight, and a rule underneath that no hover
		 * state can produce. Three stacked cues, zero elevation
		 * (ux-von-restorff-emphasis).
		 */
		.pagination-link[data-active="true"] {
			font-weight: 600;
			color: var(--foreground);
			pointer-events: none;
			cursor: default;
			/* Belt and braces: never inherit a raised face from .btn */
			background-color: transparent;
			border-color: transparent;
			--btn-shadow: 0 0 rgba(0, 0, 0, 0);
			--btn-shadow-pressed: 0 0 rgba(0, 0, 0, 0);
		}

		/* The marker itself — a pseudo-element, not an extra node
		   (pseudo-over-dom-node); .btn is already position: relative. */
		.pagination-link[data-active="true"]::after {
			content: '';
			position: absolute;
			left: 50%;
			/* Anchored to the centre, not the bottom edge: the box grows to
			   2.75rem on touch, and a bottom-anchored rule would drift away
			   from the digits it belongs to. */
			top: 50%;
			width: 1.125rem;
			height: 2px;
			transform: translate(-50%, 0.625rem);
			border-radius: 9999px;
			background-color: var(--primary);
		}

		/* Disabled boundary state — Cognitive Load: prevent impossible actions */
		.pagination-link[aria-disabled="true"] {
			pointer-events: none;
			opacity: 0.5;
			cursor: default;
		}

		/* Previous / Next */
		.pagination-prev {
			padding-left: 0.375rem;
		}
		.pagination-next {
			padding-right: 0.375rem;
		}

		/* Chevron anticipation — 12 Principles: hint at direction */
		.pagination-prev svg,
		.pagination-next svg {
			transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}
		.pagination-prev:not([aria-disabled="true"]):hover svg {
			transform: translateX(-2px);
		}
		.pagination-next:not([aria-disabled="true"]):hover svg {
			transform: translateX(2px);
		}

		/* Text hidden on small screens */
		@media (max-width: 639px) {
			.pagination-text {
				display: none;
			}
		}
		@media (min-width: 640px) {
			.pagination-text {
				display: block;
			}
		}

		/*
		 * Mobile touch targets — Fitts's Law: min 44px on touch devices.
		 * Prev/Next drop their label at this width, so they become icon-only
		 * buttons and need the same bump as the numbers sitting beside them;
		 * without it the two boundary controls end up the smallest targets in
		 * the row. Their asymmetric padding also goes away with the label,
		 * or the lone caret sits off-center.
		 */
		@media (max-width: 639px) {
			.pagination-link.btn-size-icon {
				width: 2.75rem;
				height: 2.75rem;
			}
			.pagination-prev,
			.pagination-next {
				width: 2.75rem;
				height: 2.75rem;
				padding-left: 0;
				padding-right: 0;
			}
		}

		/* Ellipsis — Selective Attention: muted to signal non-interactive */
		.pagination-ellipsis {
			display: flex;
			width: 2.25rem;
			height: 2.25rem;
			align-items: center;
			justify-content: center;
			color: var(--muted-foreground);
		}

		.pagination-ellipsis svg {
			width: 1rem;
			height: 1rem;
		}

		/* Screen reader only */
		.pagination .sr-only {
			position: absolute;
			width: 1px;
			height: 1px;
			padding: 0;
			margin: -1px;
			overflow: hidden;
			clip: rect(0, 0, 0, 0);
			white-space: nowrap;
			border-width: 0;
		}

		/* Dark mode — the marker rides --primary and --foreground, which flip
		   themselves; only the disabled wash needs a nudge. */
		.dark .pagination-link[aria-disabled="true"] {
			opacity: 0.4;
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.pagination-prev svg,
			.pagination-next svg {
				transition: none;
			}
			.pagination-prev:hover svg,
			.pagination-next:hover svg {
				transform: none;
			}
		}

/* popover.templ */
/* ── Root ── */
		.popover {
			position: relative;
			display: inline-flex;
		}

		/* ── Trigger ── */
		.popover-trigger {
			cursor: pointer;
		}

		/* ── Content ── */
		.popover-content {
			position: absolute;
			z-index: 50;
			width: 18rem;
			display: flex;
			flex-direction: column;
			gap: 0.625rem;
			border-radius: var(--radius-lg);
			background: var(--popover);
			padding: 0.625rem;
			color: var(--popover-foreground);
			font-size: 0.875rem;
			line-height: 1.25rem;
			box-shadow: var(--shadow-popover);
			outline: none;
			/*
			 * --popover-base-* holds the alignment offset (e.g. -50% for centering).
			 * --popover-slide-* holds the directional animation offset per-side.
			 * Both are composed into the keyframes so the base position is never lost.
			 */
			--popover-base-x: 0px;
			--popover-base-y: 0px;
			--popover-slide-x: 0px;
			--popover-slide-y: 0px;
			transform: translate(var(--popover-base-x), var(--popover-base-y));
		}

		/* Hidden when closed */
		.popover-content[data-state="closed"] {
			pointer-events: none;
			visibility: hidden;
			opacity: 0;
		}

		/*
		 * Side positioning + directional slide offsets.
		 * transform-origin anchors scale to the trigger edge so the panel
		 * appears to grow outward — maintaining spatial continuity (Solid Drawing).
		 * --popover-slide-* drives the directional translate so the panel
		 * emerges FROM the trigger, not just fades in place (Staging).
		 */

		/* Side: bottom (default) */
		.popover-content[data-side="bottom"] {
			top: calc(100% + 4px);
			transform-origin: top;
			--popover-slide-y: -4px;
		}
		.popover-content[data-side="top"] {
			bottom: calc(100% + 4px);
			transform-origin: bottom;
			--popover-slide-y: 4px;
		}
		.popover-content[data-side="left"] {
			right: calc(100% + 4px);
			top: 0;
			transform-origin: right;
			--popover-slide-x: 4px;
		}
		.popover-content[data-side="right"] {
			left: calc(100% + 4px);
			top: 0;
			transform-origin: left;
			--popover-slide-x: -4px;
		}

		/* Align */
		.popover-content[data-side="bottom"][data-align="start"],
		.popover-content[data-side="top"][data-align="start"] {
			left: 0;
		}
		.popover-content[data-side="bottom"][data-align="center"],
		.popover-content[data-side="top"][data-align="center"] {
			left: 50%;
			--popover-base-x: -50%;
		}
		.popover-content[data-side="bottom"][data-align="end"],
		.popover-content[data-side="top"][data-align="end"] {
			right: 0;
		}
		.popover-content[data-side="left"][data-align="start"],
		.popover-content[data-side="right"][data-align="start"] {
			top: 0;
		}
		.popover-content[data-side="left"][data-align="center"],
		.popover-content[data-side="right"][data-align="center"] {
			top: 50%;
			--popover-base-y: -50%;
		}
		.popover-content[data-side="left"][data-align="end"],
		.popover-content[data-side="right"][data-align="end"] {
			top: auto;
			bottom: 0;
		}

		/*
		 * Animations — asymmetric enter/exit with directional slide.
		 * Enter: spring easing + directional translate for organic arc (180ms).
		 *   Slightly longer than dropdown (150ms) because popovers carry richer
		 *   content — the extra 30ms lets the eye settle (Timing + Staging).
		 * Exit: snappy accelerate-out, less travel distance than enter so the
		 *   leaving panel doesn't compete for attention (Follow Through + Timing).
		 */

		/* Open (Slow In & Slow Out — spring overshoot on enter) */
		.popover-content[data-state="open"] {
			animation: popover-in 180ms cubic-bezier(0.22, 1.1, 0.36, 1);
		}

		/* Close (faster, subtler exit — content yields focus back to trigger) */
		.popover-content[data-closing] {
			pointer-events: none;
			animation: popover-out 120ms cubic-bezier(0.4, 0, 0.7, 0.2) forwards;
		}

		@keyframes popover-in {
			from {
				opacity: 0;
				transform: translate(var(--popover-base-x), var(--popover-base-y))
					scale(0.96)
					translate(var(--popover-slide-x), var(--popover-slide-y));
			}
			to {
				opacity: 1;
				transform: translate(var(--popover-base-x), var(--popover-base-y))
					scale(1);
			}
		}

		@keyframes popover-out {
			from {
				opacity: 1;
				transform: translate(var(--popover-base-x), var(--popover-base-y))
					scale(1);
			}
			to {
				opacity: 0;
				transform: translate(var(--popover-base-x), var(--popover-base-y))
					scale(0.97)
					translate(
						calc(var(--popover-slide-x) * 0.5),
						calc(var(--popover-slide-y) * 0.5)
					);
			}
		}

		/* ── Header ── */
		.popover-header {
			display: flex;
			flex-direction: column;
			gap: 0.125rem;
			font-size: 0.875rem;
		}

		/* ── Title ── */
		.popover-title {
			font-weight: 500;
		}

		/* ── Description ── */
		.popover-description {
			color: var(--muted-foreground);
		}

		/* ── Reduced motion — instant show/hide, no transform (Timing) ── */
		@media (prefers-reduced-motion: reduce) {
			.popover-content[data-state="open"],
			.popover-content[data-closing] {
				animation: none;
			}
		}

/* progress.templ */
/* Keyframes */
		@keyframes progress-shimmer {
			100% {
				transform: translateX(100%);
			}
		}

		@keyframes progress-indeterminate {
			0% {
				left: -40%;
				width: 40%;
			}
			50% {
				left: 20%;
				width: 50%;
			}
			100% {
				left: 100%;
				width: 40%;
			}
		}

		/* Base */
		.progress {
			display: flex;
			flex-wrap: wrap;
			gap: 0.75rem;
		}

		/* Track */
		.progress-track {
			position: relative;
			display: flex;
			height: 0.375rem;
			width: 100%;
			align-items: center;
			overflow: hidden;
			border-radius: 9999px;
			background-color: var(--muted);
			/* The track is a recessed well the fill travels along */
			box-shadow: var(--shadow-well);
		}

		/* Indicator */
		.progress-indicator {
			position: relative;
			height: 100%;
			border-radius: 9999px;
			background-color: var(--primary);
			/* Lit top edge lifts the fill out of the well */
			box-shadow: var(--highlight-top);
			overflow: hidden;
			/*
			 * Linear, deliberately. Progress is the one place easing is wrong:
			 * the bar represents real work, so its motion must stay 1:1 with
			 * the value it reports (easing-linear-only-progress).
			 */
			transition: width 300ms linear;
		}

		/* Shimmer – secondary action communicating "in motion" */
		.progress-indicator::after {
			content: '';
			position: absolute;
			inset: 0;
			transform: translateX(-100%);
			background: linear-gradient(
				90deg,
				transparent 0%,
				color-mix(in oklch, var(--primary-foreground) 15%, transparent) 40%,
				color-mix(in oklch, var(--primary-foreground) 25%, transparent) 60%,
				transparent 100%
			);
			/* 1.5s matches the skeleton shimmer and the indeterminate slide —
			   the same effect must not run at two different speeds */
			animation: progress-shimmer 1.5s ease-in-out infinite;
		}

		/* Complete: stop shimmer, settle into solid fill (peak-end rule) */
		.progress[data-state="complete"] .progress-indicator::after {
			animation: none;
			display: none;
		}

		/* Indeterminate: own sliding animation (staging) */
		.progress[data-state="indeterminate"] .progress-indicator {
			position: absolute;
			width: 40%;
			animation: progress-indeterminate 1.5s cubic-bezier(0.65, 0, 0.35, 1) infinite;
		}

		.progress[data-state="indeterminate"] .progress-indicator::after {
			animation: none;
			display: none;
		}

		/* Label */
		.progress-label {
			font-size: 0.875rem;
			line-height: 1.25rem;
			font-weight: 500;
		}

		/* Value */
		.progress-value {
			margin-left: auto;
			font-size: 0.875rem;
			line-height: 1.25rem;
			color: var(--muted-foreground);
			font-variant-numeric: tabular-nums;
		}

		/* Dark mode – subtler shimmer against dark backgrounds */
		.dark .progress-indicator::after {
			background: linear-gradient(
				90deg,
				transparent 0%,
				color-mix(in oklch, var(--primary-foreground) 8%, transparent) 40%,
				color-mix(in oklch, var(--primary-foreground) 12%, transparent) 60%,
				transparent 100%
			);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.progress-indicator {
				transition-duration: 75ms;
			}

			.progress-indicator::after {
				animation: none;
				display: none;
			}

			.progress[data-state="indeterminate"] .progress-indicator {
				animation: none;
				width: 50%;
				left: 25%;
			}
		}

/* radio-group.templ */
/* Radio Group */
		.radio-group {
			display: grid;
			width: 100%;
			gap: 0.25rem;
		}

		/* Radio Label */
		.radio-label {
			display: inline-flex;
			width: fit-content;
			align-items: center;
			gap: 0.5rem;
			padding: 0.25rem 0;
			cursor: pointer;
		}

		.radio-label-text {
			font-size: 0.875rem;
			line-height: 1.25rem;
			font-weight: 500;
			user-select: none;
		}

		/* Wrapper */
		.radio-wrapper {
			position: relative;
			display: inline-flex;
			width: 1.125rem;
			height: 1.125rem;
			flex-shrink: 0;
		}

		/* Native input: transparent overlay for interaction */
		.radio-input {
			position: absolute;
			inset: -0.5rem -0.75rem;
			opacity: 0;
			cursor: pointer;
			margin: 0;
			z-index: 1;
		}

		.radio-input:disabled {
			cursor: not-allowed;
		}

		/* Visual radio */
		.radio-visual {
			display: inline-flex;
			align-items: center;
			justify-content: center;
			width: 1.125rem;
			height: 1.125rem;
			border-radius: 50%;
			border: 1px solid var(--input);
			background: transparent;
			color: inherit;
			/* Field elevation — a hairline lift off the page */
			box-shadow: var(--shadow-field);
			transition: border-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
				background-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
				color 100ms cubic-bezier(0.4, 0, 0.2, 1),
				box-shadow 150ms cubic-bezier(0.4, 0, 0.2, 1),
				transform 100ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		/* Hover on radio circle (Anticipation — hint before action) */
		.radio-input:not(:disabled):not(:checked):hover + .radio-visual {
			border-color: color-mix(in oklch, var(--primary) 50%, var(--input));
		}

		/* Pressed feedback */
		.radio-input:active:not(:disabled) + .radio-visual {
			transform: scale(0.97);
			transition-duration: 100ms;
		}

		/* Focus visible */
		.radio-input:focus-visible + .radio-visual {
			border-color: var(--ring);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		/* Disabled */
		.radio-input:disabled + .radio-visual {
			opacity: 0.5;
		}

		/* Disabled label row */
		.radio-label:has(.radio-input:disabled) {
			cursor: not-allowed;
			pointer-events: none;
		}
		.radio-label:has(.radio-input:disabled) .radio-label-text {
			opacity: 0.5;
		}

		/* Invalid */
		.radio-input[aria-invalid="true"] + .radio-visual {
			border-color: var(--destructive);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
		}

		/* Checked — background fills first (Staging); lit-edge on the face */
		.radio-input:checked + .radio-visual {
			border-color: var(--primary);
			background-color: var(--primary);
			color: var(--primary-foreground);
			box-shadow: var(--highlight-top), var(--shade-bottom), var(--shadow-field);
		}

		/* Invalid + Checked */
		.radio-input[aria-invalid="true"]:checked + .radio-visual {
			border-color: var(--primary);
		}

		/* Indicator */
		.radio-indicator {
			display: flex;
			align-items: center;
			justify-content: center;
			width: 1.125rem;
			height: 1.125rem;
		}

		/* Dot — delayed pop-in after background fill (Follow Through) */
		.radio-dot {
			display: block;
			width: 0.5rem;
			height: 0.5rem;
			border-radius: 50%;
			background-color: var(--primary-foreground);
			opacity: 1;
			transform: scale(1);
			transition: opacity 120ms 40ms cubic-bezier(0.4, 0, 0.2, 1),
				transform 200ms 40ms cubic-bezier(0.34, 1.56, 0.64, 1);
		}

		/* Hide dot when unchecked — scale from 0 for a satisfying pop (Appeal) */
		.radio-input:not(:checked) + .radio-visual .radio-dot {
			opacity: 0;
			transform: scale(0);
			transition-delay: 0ms;
		}

		/* Dark mode */
		.dark .radio-visual {
			background-color: color-mix(in oklch, var(--input) 30%, transparent);
		}

		.dark .radio-input:not(:disabled):not(:checked):hover + .radio-visual {
			border-color: color-mix(in oklch, var(--primary) 60%, var(--input));
		}

		.dark .radio-input[aria-invalid="true"] + .radio-visual {
			border-color: color-mix(in oklch, var(--destructive) 50%, transparent);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 40%, transparent);
		}

		.dark .radio-input:checked + .radio-visual {
			background-color: var(--primary);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.radio-visual {
				transition-duration: 75ms;
			}
			.radio-dot {
				transition: none;
			}
			.radio-input:active:not(:disabled) + .radio-visual {
				transform: none;
			}
		}

/* resizable.templ */
/* Resizable Panel Group */
		.resizable-group {
			display: flex;
			height: 100%;
			width: 100%;
		}
		.resizable-group-vertical {
			flex-direction: column;
		}

		/* Resizable Panel */
		.resizable-panel {
			overflow: hidden;
			flex-shrink: 0;
			flex-grow: 0;
		}

		/* Resizable Handle
		   [Fitts's Law] 16px hit area via ::after for comfortable targeting
		   [timing-under-300ms] 150ms transitions for hover/active feedback
		   [easing-entrance-ease-out] ease-out for state appearance */
		.resizable-handle {
			position: relative;
			display: flex;
			align-items: center;
			justify-content: center;
			flex-shrink: 0;
			width: 1px;
			background-color: var(--border);
			outline: none;
			cursor: col-resize;
			touch-action: none;
			user-select: none;
			transition: background-color 150ms ease-out;
		}

		/* [Fitts's Law] Generous invisible hit area -- 16px wide */
		.resizable-handle::after {
			content: "";
			position: absolute;
			inset: 0;
			left: 50%;
			width: 16px;
			transform: translateX(-50%);
		}

		.resizable-handle:focus-visible {
			background-color: var(--ring);
			box-shadow: 0 0 0 2px color-mix(in oklch, var(--ring) 30%, transparent);
		}

		/* [Aesthetic-Usability] Soft intermediate hover before full active */
		.resizable-handle:hover {
			background-color: color-mix(in oklch, var(--primary) 50%, transparent);
		}

		/* [physics-active-state] Active drag gets full primary color */
		.resizable-handle.resizable-handle-active {
			background-color: var(--primary);
			transition-duration: 50ms;
		}

		/* [Flow] Boundary feedback -- hitting min/max is normal operation, not
		   an error, so the handle dims back toward its resting border color
		   ("stuck") instead of flashing the destructive red used for
		   irreversible actions elsewhere in the kit. */
		.resizable-handle.resizable-handle-at-limit {
			background-color: color-mix(in oklch, var(--primary) 35%, var(--border));
			transition-duration: 100ms;
		}

		/* Horizontal group handle (separator between top/bottom) */
		.resizable-handle-vertical {
			width: 100%;
			height: 1px;
			cursor: row-resize;
		}
		.resizable-handle-vertical::after {
			content: "";
			position: absolute;
			inset: 0;
			top: 50%;
			left: 0;
			width: 100%;
			height: 16px;
			transform: translateY(-50%);
		}

		/* Dragging state: change cursor globally */
		body.resizable-dragging-col {
			cursor: col-resize !important;
			user-select: none;
		}
		body.resizable-dragging-row {
			cursor: row-resize !important;
			user-select: none;
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.resizable-handle {
				transition: none;
			}
		}

		/* Dark mode */
		.dark .resizable-handle:hover {
			background-color: color-mix(in oklch, var(--primary) 60%, transparent);
		}
		.dark .resizable-handle.resizable-handle-active {
			background-color: var(--primary);
		}

/* select.templ */
/* ── Root ── */
		.sel {
			position: relative;
			display: inline-flex;
		}

		/* ── Trigger ── */
		.sel-trigger {
			display: inline-flex;
			width: fit-content;
			align-items: center;
			justify-content: space-between;
			gap: 0.375rem;
			border-radius: var(--radius-lg);
			border: 1px solid var(--input);
			background-color: transparent;
			/* Field elevation — a hairline lift off the page (--shadow-field) */
			box-shadow: var(--shadow-field);
			padding: 0.5rem 0.5rem 0.5rem 0.625rem;
			font-size: 0.875rem;
			line-height: 1.25rem;
			white-space: nowrap;
			transition-property: color, background-color, border-color, box-shadow, opacity, transform;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
			outline: none;
			user-select: none;
			cursor: pointer;
			height: 2.25rem;
		}

		/* Anticipation — hover hints at interactivity before click (Jakob's Law) */
		.sel-trigger:hover:not(:disabled) {
			border-color: color-mix(in oklch, var(--ring) 40%, transparent);
		}

		/* Squash & Stretch — shared press spec with Button, Checkbox, Radio,
		   Switch, Tabs and Calendar: scale(0.97) over 100ms (timing-consistent) */
		.sel-trigger:active:not(:disabled) {
			transform: scale(0.97);
			transition-duration: 100ms;
		}

		.sel-trigger:focus-visible {
			border-color: var(--ring);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		/*
		 * Secondary Action — expanded trigger gets subtle bg tint so the user
		 * sees a visual connection between the trigger and the open panel
		 * (Law of Common Region + Working Memory).
		 */
		.sel-trigger[aria-expanded="true"] {
			border-color: var(--ring);
			background-color: color-mix(in oklch, var(--muted) 50%, transparent);
		}

		.sel-trigger:disabled {
			pointer-events: none;
			opacity: 0.5;
			cursor: not-allowed;
		}

		.sel-trigger[aria-invalid="true"] {
			border-color: var(--destructive);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
		}

		/* Size: sm */
		.sel-trigger-sm {
			height: 2rem;
			border-radius: min(var(--radius-md), 10px);
		}

		/* ── Trigger icon ── */
		.sel-trigger-icon {
			display: flex;
			align-items: center;
			justify-content: center;
			pointer-events: none;
			color: var(--muted-foreground);
			flex-shrink: 0;
			transition: transform 150ms cubic-bezier(0.4, 0, 0.2, 1),
				color 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}
		.sel-trigger-icon svg {
			width: 1rem;
			height: 1rem;
		}

		/* Secondary Action — chevron follows hover/focus to reinforce affordance */
		.sel-trigger:hover:not(:disabled) .sel-trigger-icon {
			color: var(--foreground);
		}

		/* Chevron rotates when open (Anticipation in reverse — confirms the state) */
		.sel-trigger[aria-expanded="true"] .sel-trigger-icon {
			transform: rotate(180deg);
			color: var(--foreground);
		}

		/* ── Value ── */
		.sel-value {
			display: flex;
			flex: 1;
			align-items: center;
			gap: 0.375rem;
			text-align: left;
			overflow: hidden;
			text-overflow: ellipsis;
			white-space: nowrap;
		}

		/* Placeholder — shown via CSS when no value is selected.
		   JS replaces the pseudo-element with real text on selection. */
		.sel-value[data-placeholder]:empty::before {
			content: attr(data-placeholder);
			color: var(--muted-foreground);
		}

		/* ── Content ── */
		.sel-content {
			position: absolute;
			z-index: 50;
			min-width: 100%;
			max-height: 20rem;
			overflow-y: auto;
			overscroll-behavior: contain;
			border-radius: var(--radius-md);
			background: var(--popover);
			padding: 0.25rem;
			color: var(--popover-foreground);
			font-size: 0.875rem;
			line-height: 1.25rem;
			box-shadow: var(--shadow-popover);
			outline: none;
			/*
			 * --sel-base-x holds the alignment offset (e.g. -50% for centering).
			 * --sel-slide holds the directional animation offset per-side.
			 * Both are composed into the keyframes so the base position is never lost.
			 */
			--sel-base-x: 0px;
			--sel-slide: 0px;
			transform: translateX(var(--sel-base-x));
		}

		/* Hidden when closed */
		.sel-content[data-state="closed"] {
			pointer-events: none;
			visibility: hidden;
			opacity: 0;
		}

		/*
		 * Side positioning + directional slide offsets.
		 * transform-origin anchors scale to the trigger edge so the panel
		 * appears to grow outward — maintaining spatial continuity (Solid Drawing).
		 * --sel-slide drives the directional translate so the panel
		 * emerges FROM the trigger, not just fades in place (Staging).
		 */

		/* Side: bottom (default) */
		.sel-content[data-side="bottom"] {
			top: calc(100% + 4px);
			transform-origin: top;
			--sel-slide: -4px;
		}
		.sel-content[data-side="top"] {
			bottom: calc(100% + 4px);
			transform-origin: bottom;
			--sel-slide: 4px;
		}

		/* Align */
		.sel-content[data-side="bottom"][data-align="start"],
		.sel-content[data-side="top"][data-align="start"] {
			left: 0;
		}
		.sel-content[data-side="bottom"][data-align="center"],
		.sel-content[data-side="top"][data-align="center"] {
			left: 50%;
			--sel-base-x: -50%;
		}
		.sel-content[data-side="bottom"][data-align="end"],
		.sel-content[data-side="top"][data-align="end"] {
			right: 0;
		}

		/*
		 * Animations — asymmetric enter/exit with directional slide.
		 * Enter: spring easing + directional translate for organic arc (150ms).
		 * Exit: snappy accelerate-out, less travel distance than enter so the
		 *   leaving panel doesn't compete for attention (Follow Through + Timing).
		 */

		/* Open (Slow In & Slow Out — spring overshoot on enter) */
		.sel-content[data-state="open"] {
			animation: sel-in 150ms cubic-bezier(0.22, 1.1, 0.36, 1);
		}

		/* Close (faster, subtler exit — content yields focus back to trigger) */
		.sel-content[data-closing] {
			pointer-events: none;
			animation: sel-out 100ms cubic-bezier(0.4, 0, 0.7, 0.2) forwards;
		}

		@keyframes sel-in {
			from {
				opacity: 0;
				transform: translateX(var(--sel-base-x))
					scale(0.96)
					translateY(var(--sel-slide));
			}
			to {
				opacity: 1;
				transform: translateX(var(--sel-base-x))
					scale(1);
			}
		}

		@keyframes sel-out {
			from {
				opacity: 1;
				transform: translateX(var(--sel-base-x))
					scale(1);
			}
			to {
				opacity: 0;
				transform: translateX(var(--sel-base-x))
					scale(0.97)
					translateY(calc(var(--sel-slide) * 0.5));
			}
		}

		/* ── Group ── */
		.sel-group {
			padding: 0.125rem 0;
		}

		/* ── Label ── */
		.sel-label {
			padding: 0.25rem 0.5rem;
			font-size: 0.75rem;
			font-weight: 500;
			color: var(--muted-foreground);
		}

		/* Chunking — labels after separators get extra top space
		   to form visually distinct groups (Law of Proximity) */
		.sel-separator + .sel-group > .sel-label:first-child,
		.sel-separator + .sel-label {
			padding-top: 0.375rem;
		}

		/* ── Item ── */
		.sel-item {
			position: relative;
			display: flex;
			cursor: default;
			align-items: center;
			gap: 0.375rem;
			/* Concentric with the panel: outer radius minus the 0.25rem pad */
			border-radius: calc(var(--radius-md) - 4px);
			padding: 0.3125rem 2rem 0.3125rem 0.5rem;
			font-size: 0.875rem;
			line-height: 1.25rem;
			outline: none;
			user-select: none;
		}

		/*
		 * Hover fades; keyboard does not. The transition lives on the :hover
		 * rule so pointer highlighting stays smooth while ArrowUp/ArrowDown
		 * moves the highlight instantly — keyboard navigation must never wait
		 * on an animation.
		 */
		.sel-item:hover:not([data-disabled="true"]) {
			background: var(--accent);
			color: var(--accent-foreground);
			transition: background-color 80ms cubic-bezier(0.4, 0, 0.2, 1),
				color 80ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		.sel-item[data-highlighted] {
			background: var(--accent);
			color: var(--accent-foreground);
		}

		/* Selected state */
		.sel-item[data-selected] {
			font-weight: 500;
		}

		/* Disabled */
		.sel-item[data-disabled="true"] {
			pointer-events: none;
			opacity: 0.5;
		}

		/* SVG inside items */
		.sel-item svg {
			pointer-events: none;
			flex-shrink: 0;
			width: 1rem;
			height: 1rem;
		}

		/* ── Item text ── */
		.sel-item-text {
			display: flex;
			flex: 1;
			flex-shrink: 0;
			align-items: center;
			gap: 0.5rem;
			white-space: nowrap;
		}

		/* ── Item indicator (check) ── */
		.sel-item-indicator {
			pointer-events: none;
			position: absolute;
			right: 0.5rem;
			display: flex;
			align-items: center;
			justify-content: center;
			opacity: 0;
			transform: scale(0.5);
			transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1),
				transform 150ms cubic-bezier(0.34, 1.56, 0.64, 1);
		}

		.sel-item[data-selected] .sel-item-indicator {
			opacity: 1;
			transform: scale(1);
		}

		.sel-item-indicator svg {
			width: 0.875rem;
			height: 0.875rem;
		}

		/* ── Separator ── */
		.sel-separator {
			margin: 0.25rem -0.25rem;
			height: 1px;
			background: var(--border);
		}

		/* ── Dark mode ── */
		.dark .sel-trigger {
			border-color: var(--input);
			background-color: color-mix(in oklch, var(--input) 30%, transparent);
		}
		.dark .sel-trigger:hover:not(:disabled) {
			background-color: color-mix(in oklch, var(--input) 50%, transparent);
		}
		.dark .sel-trigger[aria-expanded="true"] {
			background-color: color-mix(in oklch, var(--input) 50%, transparent);
		}
		.dark .sel-trigger[aria-invalid="true"] {
			border-color: color-mix(in oklch, var(--destructive) 50%, transparent);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 40%, transparent);
		}
		/* ── Reduced motion — instant show/hide, no transform (Timing) ── */
		@media (prefers-reduced-motion: reduce) {
			.sel-trigger,
			.sel-trigger-icon,
			.sel-item,
			.sel-item-indicator {
				transition: none;
			}
			.sel-trigger:active:not(:disabled) {
				transform: none;
			}
			.sel-content[data-state="open"],
			.sel-content[data-closing] {
				animation: none;
			}
		}

/* separator.templ */
.separator {
			flex-shrink: 0;
			background-color: var(--border);
			transition: background-color 150ms ease;
		}

		.separator-horizontal {
			height: 1px;
			width: 100%;
		}

		.separator-vertical {
			width: 1px;
			align-self: stretch;
		}

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

/* shared.templ */
/* ── Thin scrollbar utility (opt in with class="... ui-scroll-thin") ── */
		.ui-scroll-thin {
			scrollbar-width: thin;
			scrollbar-color: color-mix(in oklch, var(--foreground) 15%, transparent) transparent;
		}
		.ui-scroll-thin::-webkit-scrollbar {
			width: 6px;
		}
		.ui-scroll-thin::-webkit-scrollbar-track {
			background: transparent;
		}
		.ui-scroll-thin::-webkit-scrollbar-thumb {
			background: color-mix(in oklch, var(--foreground) 15%, transparent);
			border-radius: 3px;
		}
		.ui-scroll-thin::-webkit-scrollbar-thumb:hover {
			background: color-mix(in oklch, var(--foreground) 25%, transparent);
		}
		.dark .ui-scroll-thin {
			scrollbar-color: color-mix(in oklch, var(--foreground) 20%, transparent) transparent;
		}
		.dark .ui-scroll-thin::-webkit-scrollbar-thumb {
			background: color-mix(in oklch, var(--foreground) 20%, transparent);
		}
		.dark .ui-scroll-thin::-webkit-scrollbar-thumb:hover {
			background: color-mix(in oklch, var(--foreground) 30%, transparent);
		}

		/* ── Menu item base (item, checkbox/radio item, sub-trigger) ── */
		.ddm-item, .ctxm-item,
		.ddm-checkbox-item, .ctxm-checkbox-item,
		.ddm-radio-item, .ctxm-radio-item,
		.ddm-sub-trigger, .ctxm-sub-trigger {
			position: relative;
			display: flex;
			cursor: default;
			align-items: center;
			gap: 0.375rem;
			/* Concentric with the panel: outer radius minus the 0.25rem pad */
			border-radius: calc(var(--radius-md) - 4px);
			padding: 0.3125rem 0.5rem;
			font-size: 0.875rem;
			line-height: 1.25rem;
			outline: none;
			user-select: none;
		}

		/*
		 * The highlight fades only when the pointer put it there. Arrow keys
		 * land on items the pointer is nowhere near, and keyboard navigation
		 * must never wait on an animation — so the transition is scoped to
		 * the hovered item and keyboard moves swap instantly.
		 */
		.ddm-item:hover[data-highlighted], .ctxm-item:hover[data-highlighted],
		.ddm-checkbox-item:hover[data-highlighted], .ctxm-checkbox-item:hover[data-highlighted],
		.ddm-radio-item:hover[data-highlighted], .ctxm-radio-item:hover[data-highlighted],
		.ddm-sub-trigger:hover[data-highlighted], .ctxm-sub-trigger:hover[data-highlighted] {
			transition: background-color 80ms cubic-bezier(0.4, 0, 0.2, 1),
				color 80ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		/* Highlighted state (keyboard/hover) */
		.ddm-item[data-highlighted], .ctxm-item[data-highlighted],
		.ddm-checkbox-item[data-highlighted], .ctxm-checkbox-item[data-highlighted],
		.ddm-radio-item[data-highlighted], .ctxm-radio-item[data-highlighted],
		.ddm-sub-trigger[data-highlighted], .ctxm-sub-trigger[data-highlighted] {
			background: var(--muted);
			color: var(--foreground);
		}

		/* Highlighted — icons follow the highlight color */
		.ddm-item[data-highlighted] svg, .ctxm-item[data-highlighted] svg,
		.ddm-checkbox-item[data-highlighted] svg, .ctxm-checkbox-item[data-highlighted] svg,
		.ddm-radio-item[data-highlighted] svg, .ctxm-radio-item[data-highlighted] svg,
		.ddm-sub-trigger[data-highlighted] svg, .ctxm-sub-trigger[data-highlighted] svg {
			color: var(--foreground);
		}
		.ddm-item[data-variant="destructive"][data-highlighted] svg,
		.ctxm-item[data-variant="destructive"][data-highlighted] svg {
			color: var(--destructive);
		}

		/* Destructive variant */
		.ddm-item[data-variant="destructive"],
		.ctxm-item[data-variant="destructive"] {
			color: var(--destructive);
		}
		.ddm-item[data-variant="destructive"][data-highlighted],
		.ctxm-item[data-variant="destructive"][data-highlighted] {
			background: color-mix(in oklch, var(--destructive) 10%, transparent);
			color: var(--destructive);
		}

		/* Inset */
		.ddm-item[data-inset="true"], .ctxm-item[data-inset="true"],
		.ddm-checkbox-item[data-inset="true"], .ctxm-checkbox-item[data-inset="true"],
		.ddm-radio-item[data-inset="true"], .ctxm-radio-item[data-inset="true"],
		.ddm-sub-trigger[data-inset="true"], .ctxm-sub-trigger[data-inset="true"],
		.ddm-label[data-inset="true"], .ctxm-label[data-inset="true"] {
			padding-left: 1.75rem;
		}

		/* Disabled */
		.ddm-item[data-disabled="true"], .ctxm-item[data-disabled="true"],
		.ddm-checkbox-item[data-disabled="true"], .ctxm-checkbox-item[data-disabled="true"],
		.ddm-radio-item[data-disabled="true"], .ctxm-radio-item[data-disabled="true"],
		.ddm-sub-trigger[data-disabled="true"], .ctxm-sub-trigger[data-disabled="true"] {
			pointer-events: none;
			opacity: 0.5;
		}

		/* SVG inside items */
		.ddm-item svg, .ctxm-item svg,
		.ddm-checkbox-item svg, .ctxm-checkbox-item svg,
		.ddm-radio-item svg, .ctxm-radio-item svg,
		.ddm-sub-trigger svg, .ctxm-sub-trigger svg {
			pointer-events: none;
			flex-shrink: 0;
			width: 1rem;
			height: 1rem;
		}

		/* ── Checkbox / radio item indicator ── */
		.ddm-checkbox-item, .ctxm-checkbox-item,
		.ddm-radio-item, .ctxm-radio-item {
			padding-right: 2rem;
		}

		.ddm-item-indicator, .ctxm-item-indicator {
			pointer-events: none;
			position: absolute;
			right: 0.5rem;
			display: flex;
			align-items: center;
			justify-content: center;
			opacity: 0;
			transform: scale(0.5);
			transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1),
				transform 150ms cubic-bezier(0.34, 1.56, 0.64, 1);
		}

		.ddm-checkbox-item[data-checked="true"] .ddm-item-indicator,
		.ctxm-checkbox-item[data-checked="true"] .ctxm-item-indicator,
		.ddm-radio-item[data-checked="true"] .ddm-item-indicator,
		.ctxm-radio-item[data-checked="true"] .ctxm-item-indicator {
			opacity: 1;
			transform: scale(1);
		}

		.ddm-item-indicator svg, .ctxm-item-indicator svg {
			width: 0.875rem;
			height: 0.875rem;
		}

		/* ── Sub trigger chevron ── */
		.ddm-sub-trigger, .ctxm-sub-trigger {
			padding-right: 0.375rem;
		}

		/* Secondary Action — chevron hints at submenu direction, rotates on open */
		.ddm-sub-trigger > svg:last-child,
		.ctxm-sub-trigger > svg:last-child {
			margin-left: auto;
			transition: transform 150ms cubic-bezier(0.34, 1.56, 0.64, 1);
		}
		.ddm-sub-trigger[data-open="true"] > svg:last-child,
		.ctxm-sub-trigger[data-open="true"] > svg:last-child {
			transform: rotate(90deg);
		}
		.ddm-sub-trigger[data-open="true"],
		.ctxm-sub-trigger[data-open="true"] {
			background: var(--muted);
			color: var(--foreground);
		}

		/* ── Label ── */
		.ddm-label, .ctxm-label {
			padding: 0.25rem 0.5rem;
			font-size: 0.75rem;
			font-weight: 500;
			color: var(--muted-foreground);
		}
		/* Chunking — labels after a separator get extra top space */
		.ddm-separator + .ddm-label,
		.ctxm-separator + .ctxm-label {
			padding-top: 0.375rem;
		}

		/* ── Separator ── */
		.ddm-separator, .ctxm-separator {
			margin: 0.25rem -0.25rem;
			height: 1px;
			background: var(--border);
		}

		/* ── Shortcut ── */
		.ddm-shortcut, .ctxm-shortcut {
			margin-left: auto;
			font-size: 0.75rem;
			letter-spacing: 0.1em;
			color: var(--muted-foreground);
		}
		.ddm-item[data-highlighted] .ddm-shortcut,
		.ctxm-item[data-highlighted] .ctxm-shortcut,
		.ddm-checkbox-item[data-highlighted] .ddm-shortcut,
		.ctxm-checkbox-item[data-highlighted] .ctxm-shortcut,
		.ddm-radio-item[data-highlighted] .ddm-shortcut,
		.ctxm-radio-item[data-highlighted] .ctxm-shortcut,
		.ddm-sub-trigger[data-highlighted] .ddm-shortcut,
		.ctxm-sub-trigger[data-highlighted] .ctxm-shortcut {
			color: var(--foreground);
		}
		.ddm-item[data-variant="destructive"][data-highlighted] .ddm-shortcut,
		.ctxm-item[data-variant="destructive"][data-highlighted] .ctxm-shortcut {
			color: var(--destructive);
		}

		/* ── Dark mode — destructive highlight ── */
		.dark .ddm-item[data-variant="destructive"][data-highlighted],
		.dark .ctxm-item[data-variant="destructive"][data-highlighted] {
			background: color-mix(in oklch, var(--destructive) 20%, transparent);
		}

		/* ── Sub-content enter/exit keyframes (shared) ── */
		@keyframes menu-sub-in {
			from { opacity: 0; transform: translateX(-4px) scale(0.97); }
			to { opacity: 1; transform: translateX(0) scale(1); }
		}
		@keyframes menu-sub-out {
			from { opacity: 1; transform: translateX(0) scale(1); }
			to { opacity: 0; transform: translateX(-4px) scale(0.97); }
		}
		@keyframes menu-sub-in-left {
			from { opacity: 0; transform: translateX(4px) scale(0.97); }
			to { opacity: 1; transform: translateX(0) scale(1); }
		}
		@keyframes menu-sub-out-left {
			from { opacity: 1; transform: translateX(0) scale(1); }
			to { opacity: 0; transform: translateX(4px) scale(0.97); }
		}

		/* ── Reduced motion — items and sub-content ── */
		@media (prefers-reduced-motion: reduce) {
			.ddm-item, .ctxm-item,
			.ddm-checkbox-item, .ctxm-checkbox-item,
			.ddm-radio-item, .ctxm-radio-item,
			.ddm-sub-trigger, .ctxm-sub-trigger,
			.ddm-sub-trigger > svg:last-child,
			.ctxm-sub-trigger > svg:last-child,
			.ddm-item-indicator, .ctxm-item-indicator {
				transition: none;
			}
			/* Must repeat the hover selectors: they outrank the bare class
			   above, so listing only the base would leave them animating. */
			.ddm-item:hover[data-highlighted], .ctxm-item:hover[data-highlighted],
			.ddm-checkbox-item:hover[data-highlighted], .ctxm-checkbox-item:hover[data-highlighted],
			.ddm-radio-item:hover[data-highlighted], .ctxm-radio-item:hover[data-highlighted],
			.ddm-sub-trigger:hover[data-highlighted], .ctxm-sub-trigger:hover[data-highlighted] {
				transition: none;
			}
			.ddm-sub-content[data-state="open"], .ctxm-sub-content[data-state="open"],
			.ddm-sub-content[data-closing], .ctxm-sub-content[data-closing] {
				animation: none;
			}
		}

/* skeleton.templ */
@keyframes skeleton-shimmer {
			100% {
				transform: translateX(100%);
			}
		}

		.skeleton {
			position: relative;
			overflow: hidden;
			border-radius: var(--radius-md);
			background-color: var(--muted);
		}

		.skeleton::after {
			content: '';
			position: absolute;
			inset: 0;
			transform: translateX(-100%);
			background: linear-gradient(
				90deg,
				transparent 0%,
				color-mix(in oklch, var(--background) 40%, transparent) 40%,
				color-mix(in oklch, var(--background) 60%, transparent) 60%,
				transparent 100%
			);
			animation: skeleton-shimmer 1.5s ease-in-out infinite;
		}

		.skeleton-circle {
			border-radius: 9999px;
		}

		@media (prefers-reduced-motion: reduce) {
			.skeleton::after {
				animation: none;
				display: none;
			}
			.skeleton {
				opacity: 0.7;
			}
		}

		.dark .skeleton::after {
			background: linear-gradient(
				90deg,
				transparent 0%,
				color-mix(in oklch, var(--foreground) 5%, transparent) 40%,
				color-mix(in oklch, var(--foreground) 8%, transparent) 60%,
				transparent 100%
			);
		}

/* slider.templ */
/* Base */
		.slider {
			position: relative;
			display: flex;
			width: 100%;
			touch-action: none;
			user-select: none;
			align-items: center;
			height: 1.25rem;
		}

		.slider-disabled {
			opacity: 0.5;
			pointer-events: none;
		}

		/* Native input: transparent overlay for interaction
		   Fitts's Law — extend vertically beyond visual bounds for easier targeting */
		.slider-input {
			position: absolute;
			inset: -0.5rem 0;
			width: 100%;
			opacity: 0;
			cursor: pointer;
			margin: 0;
			z-index: 2;
		}

		.slider-input:disabled {
			cursor: not-allowed;
		}

		/* Track */
		.slider-track {
			position: relative;
			width: 100%;
			height: 0.25rem;
			overflow: hidden;
			border-radius: 9999px;
			background-color: var(--muted);
			/* The track is a recessed well the thumb travels in */
			box-shadow: var(--shadow-well);
		}

		/* Range (filled portion) */
		.slider-range {
			position: absolute;
			height: 100%;
			border-radius: 9999px;
			background-color: var(--primary);
			/* No width transition: the thumb is positioned with `left`, which is
			   not transitioned, so easing the fill desynced the two halves of a
			   single value. Keyboard steps should land instantly anyway. */
		}

		/* Thumb — spring return for satisfying settle after drag */
		.slider-thumb {
			position: absolute;
			width: 0.75rem;
			height: 0.75rem;
			border-radius: 9999px;
			border: 1px solid var(--ring);
			background-color: var(--background);
			/* The thumb floats above the well it slides in */
			box-shadow: var(--shadow-thumb);
			transform: translateX(-50%);
			pointer-events: none;
			z-index: 1;
			transition:
				box-shadow 150ms cubic-bezier(0.4, 0, 0.2, 1),
				transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
		}

		/* Hover — anticipation: soft ring hints at interactivity */
		.slider:not(.slider-disabled):hover .slider-thumb {
			box-shadow: var(--shadow-thumb), 0 0 0 4px color-mix(in oklch, var(--ring) 20%, transparent);
		}

		/* Focus-visible — accessible ring, stronger than hover */
		.slider-input:focus-visible ~ .slider-thumb {
			box-shadow: var(--shadow-thumb), 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		/* Active — peak engagement: scale + wider ring (staging) */
		.slider-input:active:not(:disabled) ~ .slider-thumb {
			box-shadow: var(--shadow-thumb), 0 0 0 5px color-mix(in oklch, var(--ring) 30%, transparent);
			transform: translateX(-50%) scale(1.05);
			transition-duration: 100ms;
		}

		/* Dragging — disable transitions for zero-latency direct manipulation */
		.slider-dragging .slider-range,
		.slider-dragging .slider-thumb {
			transition: none;
		}

		/* Dark mode */
		.dark .slider-track {
			background-color: color-mix(in oklch, var(--muted) 80%, transparent);
		}

		.dark .slider-thumb {
			background-color: white;
			border-color: var(--ring);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.slider-range {
				transition: none;
			}
			.slider-thumb {
				transition: none;
			}
			.slider-input:active:not(:disabled) ~ .slider-thumb {
				transform: translateX(-50%);
			}
		}

/* sound-settings.templ */
.sound-settings {
			display: flex;
			flex-direction: column;
			gap: 0.75rem;
			width: 100%;
			max-width: 20rem;
		}

		.sound-settings-row {
			display: flex;
			align-items: center;
			justify-content: space-between;
			gap: 0.75rem;
		}

		/* The slider needs room; the label and readout bracket it */
		.sound-settings-volume .slider {
			flex: 1;
		}

		/* Muted state reads as inactive rather than disappearing */
		.sound-settings[data-muted] .sound-settings-volume {
			opacity: 0.5;
		}

		.sound-settings-value {
			min-width: 2.5rem;
			text-align: right;
			font-size: 0.75rem;
			line-height: 1rem;
			/* Tabular figures — the readout must not jitter as it counts */
			font-variant-numeric: tabular-nums;
			color: var(--muted-foreground);
		}

/* spinner.templ */
@keyframes spin {
			from { transform: rotate(0deg); }
			to { transform: rotate(360deg); }
		}
		.spinner {
			width: 1rem;
			height: 1rem;
			animation: spin 1s linear infinite;
		}
		@media (prefers-reduced-motion: reduce) {
			.spinner {
				animation-duration: 2s;
			}
		}

/* switch.templ */
/* Wrapper */
		.switch-wrapper {
			position: relative;
			display: inline-flex;
			flex-shrink: 0;
		}

		/* Size: default */
		.switch-size-default {
			width: 32px;
			height: 18.4px;
		}

		/* Size: sm */
		.switch-size-sm {
			width: 24px;
			height: 14px;
		}
		/* Fitts — the shared overlay inset leaves the sm track at a 30px
		   effective height; widen it just for this size to clear 32px */
		.switch-size-sm .switch-input {
			inset: -0.5625rem -0.75rem;
		}

		/* Native input: transparent overlay for interaction */
		.switch-input {
			position: absolute;
			inset: -0.5rem -0.75rem;
			opacity: 0;
			cursor: pointer;
			margin: 0;
			z-index: 1;
		}

		.switch-input:disabled {
			cursor: not-allowed;
		}

		/* Track — secondary action: bg fills quickly while thumb slides */
		.switch-track {
			display: inline-flex;
			width: 100%;
			height: 100%;
			align-items: center;
			border-radius: 9999px;
			border: 1px solid transparent;
			background-color: var(--input);
			/* The track is a recessed well the thumb travels in */
			box-shadow: var(--shadow-well);
			transition:
				background-color 120ms cubic-bezier(0.4, 0, 0.2, 1),
				border-color 150ms cubic-bezier(0.4, 0, 0.2, 1),
				box-shadow 150ms cubic-bezier(0.4, 0, 0.2, 1),
				transform 100ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		/* Hover: Anticipation — hint before action */
		.switch-input:not(:disabled):not(:checked):hover + .switch-track {
			background-color: color-mix(in oklch, var(--input) 80%, var(--foreground));
		}

		.switch-input:not(:disabled):checked:hover + .switch-track {
			background-color: color-mix(in oklch, var(--primary) 80%, transparent);
		}

		/* Pressed feedback */
		.switch-input:active:not(:disabled) + .switch-track {
			transform: scale(0.97);
			transition-duration: 100ms;
		}

		/* Track: checked */
		.switch-input:checked + .switch-track {
			background-color: var(--primary);
		}

		/* Track: focus-visible */
		.switch-input:focus-visible + .switch-track {
			border-color: var(--ring);
			box-shadow: var(--shadow-well), 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		/* Track: disabled */
		.switch-input:disabled + .switch-track {
			cursor: not-allowed;
			opacity: 0.5;
		}

		/* Track: invalid */
		.switch-input[aria-invalid="true"] + .switch-track {
			border-color: var(--destructive);
			box-shadow: var(--shadow-well), 0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
		}

		/* Thumb — primary action: spring slide draws attention (Staging) */
		.switch-thumb {
			pointer-events: none;
			display: block;
			border-radius: 9999px;
			background-color: var(--background);
			/* The thumb floats above the well it slides in */
			box-shadow: var(--shadow-thumb);
			transition: transform 200ms cubic-bezier(0.34, 1.56, 0.64, 1);
		}

		/* Thumb: default size */
		.switch-size-default .switch-thumb {
			width: 1rem;
			height: 1rem;
		}

		/* Thumb: sm size */
		.switch-size-sm .switch-thumb {
			width: 0.75rem;
			height: 0.75rem;
		}

		/* Thumb: unchecked */
		.switch-input:not(:checked) + .switch-track .switch-thumb {
			transform: translateX(0);
		}

		/* Thumb: checked */
		.switch-input:checked + .switch-track .switch-thumb {
			transform: translateX(calc(100% - 2px));
		}

		/* Dark mode */
		.dark .switch-track {
			background-color: color-mix(in oklch, var(--input) 80%, transparent);
		}

		.dark .switch-input:not(:disabled):not(:checked):hover + .switch-track {
			background-color: var(--input);
		}

		.dark .switch-input:checked + .switch-track {
			background-color: var(--primary);
		}

		.dark .switch-input:checked + .switch-track .switch-thumb {
			background-color: var(--primary-foreground);
		}

		.dark .switch-input:not(:checked) + .switch-track .switch-thumb {
			background-color: var(--foreground);
		}

		.dark .switch-input[aria-invalid="true"] + .switch-track {
			border-color: color-mix(in oklch, var(--destructive) 50%, transparent);
			box-shadow: var(--shadow-well), 0 0 0 3px color-mix(in oklch, var(--destructive) 40%, transparent);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.switch-track {
				transition-duration: 75ms;
			}
			.switch-thumb {
				transition: none;
			}
			.switch-input:active:not(:disabled) + .switch-track {
				transform: none;
			}
		}

/* table.templ */
/* Container — the static frame the edge fades hang off. It must not be the
		   scroller: an absolutely positioned fade inside a scroll container is
		   part of the scrolled content and drifts away from the edge, so the
		   scrolling lives on .tbl-scroll instead. */
		.tbl-container {
			position: relative;
			width: 100%;
		}
		.tbl-scroll {
			width: 100%;
			overflow-x: auto;
			-webkit-overflow-scrolling: touch;
			/* Set a max-height on .tbl-container (via Class) to make this the
			   vertical scroller too — that is what lets the header pin. */
			max-height: inherit;
		}

		/* Edge fades hint at the content clipped either side (Selective Attention) */
		.tbl-container::before,
		.tbl-container::after {
			content: "";
			position: absolute;
			top: 0;
			bottom: 0;
			z-index: 2;
			width: 2rem;
			pointer-events: none;
			opacity: 0;
			transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}
		.tbl-container::before {
			left: 0;
			background: linear-gradient(to right, var(--background), transparent);
		}
		.tbl-container::after {
			right: 0;
			background: linear-gradient(to left, var(--background), transparent);
		}
		.tbl-container.has-overflow-left::before,
		.tbl-container.has-overflow-right::after {
			opacity: 1;
		}

		/* Table */
		.tbl {
			width: 100%;
			caption-side: bottom;
			font-size: 0.875rem;
			line-height: 1.25rem;
			border-collapse: collapse;
			/* Tabular figures — digits in a column must align and must not
			   reflow the cell when a value changes (Geist ships tnum) */
			font-variant-numeric: tabular-nums;
		}

		/* Header — pins to the top once .tbl-scroll has a constrained height, and
		   carries more visual weight either way (Working Memory, Make Structure
		   Obvious) */
		.tbl-header {
			position: sticky;
			top: 0;
			z-index: 1;
			background-color: var(--background);
			transition: box-shadow 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}
		.tbl-header tr {
			border-bottom: 2px solid var(--border);
		}
		/* Soft shade only while rows are actually passing beneath the header —
		   an unscrolled header sits flat in the page and casts nothing */
		.tbl-container.has-overflow-top .tbl-header {
			box-shadow: var(--shadow-field);
		}

		/* Body — zebra striping for scanability (Chunking, Law of Similarity) */
		.tbl-body tr:nth-child(even) {
			background-color: color-mix(in oklch, var(--muted) 25%, transparent);
		}
		.tbl-body tr:last-child {
			border-bottom: 0;
		}

		/* Footer */
		.tbl-footer {
			border-top: 2px solid var(--border);
			background-color: color-mix(in oklch, var(--muted) 50%, transparent);
			font-weight: 500;
		}
		.tbl-footer > tr:last-child {
			border-bottom: 0;
		}

		/* Row — hover/focus/selected (Staging, Selective Attention) */
		.tbl-row {
			border-bottom: 1px solid var(--border);
			transition-property: background-color, color, box-shadow;
			transition-duration: 120ms;
			transition-timing-function: cubic-bezier(0.2, 0, 0.2, 1);
		}
		.tbl-body .tbl-row:hover {
			background-color: color-mix(in oklch, var(--muted) 50%, transparent);
		}
		.tbl-row:focus-visible {
			outline: none;
			box-shadow: inset 3px 0 0 var(--ring);
			background-color: color-mix(in oklch, var(--muted) 40%, transparent);
		}
		.tbl-row[data-state="selected"] {
			background-color: var(--muted);
			box-shadow: inset 3px 0 0 var(--primary);
		}

		/* Head cell — muted color to distinguish from data (Visual Hierarchy) */
		.tbl-head {
			height: 2.75rem;
			padding-left: 0.5rem;
			padding-right: 0.5rem;
			text-align: left;
			vertical-align: middle;
			font-weight: 600;
			font-size: 0.75rem;
			line-height: 1rem;
			letter-spacing: 0.025em;
			white-space: nowrap;
			color: var(--muted-foreground);
		}
		.tbl-head:has([role="checkbox"]) {
			padding-right: 0;
		}

		/* Data cell */
		.tbl-cell {
			padding: 0.625rem;
			vertical-align: middle;
			white-space: nowrap;
		}
		.tbl-cell:has([role="checkbox"]) {
			padding-right: 0;
		}

		/* Caption */
		.tbl-caption {
			margin-top: 1rem;
			font-size: 0.875rem;
			line-height: 1.25rem;
			color: var(--muted-foreground);
		}

		/* Dark mode zebra adjustment */
		.dark .tbl-body tr:nth-child(even) {
			background-color: color-mix(in oklch, var(--muted) 15%, transparent);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.tbl-row,
			.tbl-header,
			.tbl-container::before,
			.tbl-container::after {
				transition-duration: 75ms;
			}
		}

/* tabs.templ */
/* Root */
		.tabs {
			display: flex;
			gap: 0.5rem;
		}
		.tabs[data-orientation="horizontal"] {
			flex-direction: column;
		}
		/* vertical = row (default flex-direction) */

		/* List — base (Law of Common Region: shared container groups triggers) */
		.tabs-list {
			position: relative;
			display: inline-flex;
			width: fit-content;
			align-items: center;
			justify-content: center;
			border-radius: var(--radius-lg);
			padding: 3px;
			color: var(--muted-foreground);
		}
		.tabs[data-orientation="horizontal"] > .tabs-list {
			height: 2.5rem;
		}
		.tabs[data-orientation="vertical"] > .tabs-list {
			height: fit-content;
			flex-direction: column;
		}

		/* List — default variant */
		.tabs-list-default {
			background-color: var(--muted);
		}

		/* List — line variant */
		.tabs-list-line {
			gap: 0.25rem;
			background-color: transparent;
			border-radius: 0;
		}

		/*
		 * Sliding indicator — Staging (primary animation draws the eye to
		 * the state change) + Solid Drawing (spatial continuity across the bar).
		 * Hidden until JS positions it; triggers carry their own fallback styles.
		 */
		.tabs-indicator {
			position: absolute;
			top: 0;
			left: 0;
			pointer-events: none;
			opacity: 0;
			z-index: 0;
		}

		/* JS-enhanced: indicator visible, transition enabled */
		.tabs-list[data-animated] .tabs-indicator {
			opacity: 1;
			/* Follow Through — spring curve creates slight overshoot on arrival */
			transition:
				transform 250ms cubic-bezier(0.34, 1.56, 0.64, 1),
				width 250ms cubic-bezier(0.34, 1.56, 0.64, 1),
				height 250ms cubic-bezier(0.34, 1.56, 0.64, 1);
		}

		/* Default variant indicator — background pill slides behind triggers */
		.tabs-list-default .tabs-indicator {
			border-radius: var(--radius-md);
			background-color: var(--background);
			box-shadow: var(--shadow-raised);
		}

		/* Line variant indicator — thin bar (Staging: singular focal point) */
		/* Horizontal: underline at bottom of list */
		.tabs[data-orientation="horizontal"] .tabs-list-line .tabs-indicator {
			top: auto;
			bottom: 0;
			left: 0;
			height: 2px;
			border-radius: 1px;
			background-color: var(--foreground);
		}
		/* Vertical: sideline at right of list */
		.tabs[data-orientation="vertical"] .tabs-list-line .tabs-indicator {
			top: 0;
			left: auto;
			right: 0;
			width: 2px;
			border-radius: 1px;
			background-color: var(--foreground);
		}

		/* Trigger — base (Fitts's Law: adequate padding for comfortable targets) */
		.tabs-trigger {
			position: relative;
			z-index: 1;
			display: inline-flex;
			height: calc(100% - 1px);
			flex: 1;
			align-items: center;
			justify-content: center;
			gap: 0.375rem;
			border-radius: var(--radius-md);
			border: 1px solid transparent;
			padding: 0.25rem 0.75rem;
			font-size: 0.875rem;
			line-height: 1.25rem;
			font-weight: 500;
			white-space: nowrap;
			color: color-mix(in oklch, var(--foreground) 60%, transparent);
			background: none;
			cursor: pointer;
			outline: none;
			/* Slow In & Slow Out — eased color/bg transitions.
			   box-shadow and border-color are deliberately absent: together they
			   draw the focus ring, and a focus ring must appear instantly. Fading
			   only one of the two made two thirds of the ring snap and the rest
			   drift in over 150ms. */
			transition-property: color, background-color, transform;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
		}
		.tabs[data-orientation="vertical"] .tabs-trigger {
			width: 100%;
			justify-content: flex-start;
		}

		/* Anticipation — hover hint before committing to the action */
		.tabs-trigger:hover:not(:disabled):not([aria-disabled="true"]) {
			color: var(--foreground);
		}
		.tabs-list-line .tabs-trigger:hover:not(:disabled):not([aria-disabled="true"]):not([data-active]) {
			background-color: color-mix(in oklch, var(--muted) 50%, transparent);
		}

		/* Pressed feedback — Squash and Stretch: subtle scale communicates weight */
		.tabs-trigger:active:not(:disabled):not([aria-disabled="true"]) {
			transform: scale(0.97);
			transition-duration: 100ms;
		}

		.tabs-trigger:focus-visible {
			border-color: var(--ring);
			box-shadow: 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
			outline: 1px solid var(--ring);
		}
		.tabs-trigger:disabled,
		.tabs-trigger[aria-disabled="true"] {
			pointer-events: none;
			opacity: 0.5;
		}
		.tabs-trigger svg {
			pointer-events: none;
			flex-shrink: 0;
		}
		.tabs-trigger svg:not([class*='size-']) {
			width: 1rem;
			height: 1rem;
		}

		/*
		 * Active trigger — no-JS fallback (Cognitive Load: works without scripts).
		 * When JS enhances with [data-animated], the indicator handles the visual
		 * so trigger bg/shadow are removed to avoid doubling.
		 */

		/* Default variant: active trigger fallback */
		.tabs-list-default .tabs-trigger[data-active] {
			background-color: var(--background);
			color: var(--foreground);
			box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05);
		}
		.tabs-list-default[data-animated] .tabs-trigger[data-active] {
			background-color: transparent;
			box-shadow: none;
		}

		/* Line variant: active trigger */
		.tabs-list-line .tabs-trigger {
			background-color: transparent;
		}
		.tabs-list-line .tabs-trigger[data-active] {
			background-color: transparent;
			color: var(--foreground);
			box-shadow: none;
		}

		/* Line underline/sideline via ::after — no-JS fallback */
		.tabs-trigger::after {
			content: "";
			position: absolute;
			background-color: var(--foreground);
			opacity: 0;
			transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}
		.tabs[data-orientation="horizontal"] .tabs-trigger::after {
			left: 0;
			right: 0;
			bottom: -5px;
			height: 2px;
		}
		.tabs[data-orientation="vertical"] .tabs-trigger::after {
			top: 0;
			bottom: 0;
			right: -4px;
			width: 2px;
		}
		.tabs-list-line .tabs-trigger[data-active]::after {
			opacity: 1;
		}

		/* JS-enhanced: indicator replaces ::after */
		[data-animated] .tabs-trigger::after {
			display: none;
		}

		/*
		 * Content — Secondary Action: subtle fade-in supports the main indicator
		 * slide without stealing focus.
		 */
		.tabs-content {
			flex: 1;
			font-size: 0.875rem;
			line-height: 1.25rem;
			outline: none;
		}
		.tabs-content:not([data-active]) {
			display: none;
		}
		.tabs-content[data-active] {
			animation: tabs-content-in 150ms cubic-bezier(0, 0, 0.2, 1);
		}

		/* Timing — secondary action is shorter and subtler than the primary slide */
		@keyframes tabs-content-in {
			from {
				opacity: 0;
				transform: translateY(2px);
			}
			to {
				opacity: 1;
				transform: translateY(0);
			}
		}

		/* Dark mode */
		.dark .tabs-trigger {
			color: var(--muted-foreground);
		}
		.dark .tabs-trigger:hover:not(:disabled):not([aria-disabled="true"]) {
			color: var(--foreground);
		}
		.dark .tabs-list-default .tabs-trigger[data-active] {
			border-color: var(--border);
			background-color: var(--background);
			color: var(--foreground);
		}
		.dark .tabs-list-default[data-animated] .tabs-trigger[data-active] {
			border-color: transparent;
			background-color: transparent;
		}
		.dark .tabs-list-default .tabs-indicator {
			border: 1px solid var(--border);
			background-color: var(--background);
			box-shadow: none;
		}
		.dark .tabs-list-line .tabs-trigger[data-active] {
			border-color: transparent;
			background-color: transparent;
			color: var(--foreground);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.tabs-trigger {
				transition-duration: 0ms;
			}
			.tabs-trigger:active:not(:disabled) {
				transform: none;
			}
			.tabs-trigger::after {
				transition-duration: 0ms;
			}
			.tabs-list[data-animated] .tabs-indicator {
				transition: none;
			}
			.tabs-content[data-active] {
				animation: none;
			}
		}

/* textarea.templ */
/* Base */
		.textarea {
			display: flex;
			field-sizing: content;
			min-height: 4rem;
			width: 100%;
			border-radius: var(--radius-lg);
			border: 1px solid var(--input);
			background-color: transparent;
			padding: 0.5rem 0.625rem;
			font-size: 1rem;
			line-height: 1.5rem;
			/* Field elevation — a hairline lift off the page (--shadow-field) */
			box-shadow: var(--shadow-field);
			transition-property: color, background-color, border-color, box-shadow, opacity;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
			outline: none;
			resize: vertical;
		}

		.textarea::placeholder {
			color: var(--muted-foreground);
		}

		.textarea:focus-visible {
			border-color: var(--ring);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		.textarea:disabled {
			pointer-events: none;
			cursor: not-allowed;
			background-color: color-mix(in oklch, var(--input) 50%, transparent);
			opacity: 0.5;
		}

		.textarea[aria-invalid="true"] {
			border-color: var(--destructive);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
		}

		@media (min-width: 768px) {
			.textarea {
				font-size: 0.875rem;
				line-height: 1.25rem;
			}
		}

		/* Dark mode */
		.dark .textarea {
			background-color: color-mix(in oklch, var(--input) 30%, transparent);
		}
		.dark .textarea:disabled {
			background-color: color-mix(in oklch, var(--input) 80%, transparent);
		}
		.dark .textarea[aria-invalid="true"] {
			border-color: color-mix(in oklch, var(--destructive) 50%, transparent);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 40%, transparent);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.textarea {
				transition-duration: 75ms;
			}
		}

/* time-picker.templ */
/* ── Root ── */
		.tp {
			display: inline-flex;
			align-items: center;
			gap: 0.125rem;
			user-select: none;
		}

		/* ── Field wrapper ── */
		.tp-field {
			display: flex;
			flex-direction: column;
			align-items: center;
			gap: 0.125rem;
			width: 2rem;
		}

		/* ── Label ── */
		.tp-label {
			font-size: 0.625rem;
			font-weight: 500;
			text-transform: uppercase;
			letter-spacing: 0.05em;
			color: var(--muted-foreground);
			line-height: 1;
			white-space: nowrap;
		}

		/* ── Input ── */
		.tp-input {
			width: 2rem;
			height: 2rem;
			border-radius: var(--radius-md);
			border: 1px solid var(--input);
			background-color: transparent;
			/* Field elevation — a hairline lift off the page (--shadow-field) */
			box-shadow: var(--shadow-field);
			text-align: center;
			font-size: 0.875rem;
			font-weight: 500;
			font-family: inherit;
			font-variant-numeric: tabular-nums;
			color: var(--foreground);
			outline: none;
			padding: 0;
			transition-property: border-color, box-shadow, background-color;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
		}

		.tp-input::placeholder {
			color: var(--muted-foreground);
			font-weight: 400;
		}

		.tp-input:focus-visible {
			border-color: var(--ring);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		.tp-input:disabled {
			pointer-events: none;
			cursor: not-allowed;
			opacity: 0.5;
			background-color: color-mix(in oklch, var(--input) 50%, transparent);
		}

		.tp-input[aria-invalid="true"] {
			border-color: var(--destructive);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 20%, transparent);
		}

		/* ── Separator ── */
		.tp-separator {
			font-size: 1rem;
			font-weight: 500;
			color: var(--foreground);
			line-height: 1;
			padding-top: 0.75rem;
		}

		/* ── AM/PM toggle ── */
		.tp-period {
			width: 2rem;
			height: 2rem;
			margin-top: 0.75rem;
			border-radius: var(--radius-md);
			border: 1px solid var(--input);
			background-color: transparent;
			/* Field elevation — a hairline lift off the page (--shadow-field) */
			box-shadow: var(--shadow-field);
			font-size: 0.75rem;
			font-weight: 600;
			font-family: inherit;
			text-transform: uppercase;
			/* Uppercase needs the extra tracking back, like the sibling label */
			letter-spacing: 0.05em;
			color: var(--foreground);
			cursor: pointer;
			outline: none;
			padding: 0;
			transition-property: border-color, box-shadow, background-color, color;
			transition-duration: 150ms;
			transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
		}

		.tp-period:hover {
			background-color: var(--muted);
		}

		.tp-period:focus-visible {
			border-color: var(--ring);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--ring) 50%, transparent);
		}

		.tp-period:disabled {
			pointer-events: none;
			opacity: 0.5;
		}

		/* ── Dark mode ── */
		.dark .tp-input {
			background-color: color-mix(in oklch, var(--input) 30%, transparent);
		}
		.dark .tp-input:disabled {
			background-color: color-mix(in oklch, var(--input) 80%, transparent);
		}
		.dark .tp-input[aria-invalid="true"] {
			border-color: color-mix(in oklch, var(--destructive) 50%, transparent);
			box-shadow: var(--shadow-field), 0 0 0 3px color-mix(in oklch, var(--destructive) 40%, transparent);
		}
		.dark .tp-period {
			background-color: color-mix(in oklch, var(--input) 30%, transparent);
		}
		.dark .tp-period:hover {
			background-color: color-mix(in oklch, var(--input) 50%, transparent);
		}

		/* ── Reduced motion ── */
		@media (prefers-reduced-motion: reduce) {
			.tp-input,
			.tp-period {
				transition-duration: 75ms;
			}
		}

/* toast.templ */
/* Toaster container */
		.toaster {
			position: fixed;
			bottom: 1rem;
			right: 1rem;
			z-index: 100;
			display: flex;
			flex-direction: column-reverse;
			gap: 0.5rem;
			max-width: 24rem;
			width: 100%;
			pointer-events: none;
		}

		@media (max-width: 480px) {
			.toaster {
				left: 1rem;
				right: 1rem;
				max-width: none;
			}
		}

		/* Toast item */
		.toast {
			pointer-events: auto;
			position: relative;
			display: flex;
			align-items: center;
			gap: 0.5rem;
			width: 100%;
			border-radius: var(--radius-lg);
			border: 1px solid var(--border);
			background-color: var(--popover);
			color: var(--popover-foreground);
			padding: 0.75rem 2.25rem 0.75rem 1rem;
			font-size: 0.875rem;
			line-height: 1.25rem;
			/* Popover-tier elevation; the token flips for dark mode */
			box-shadow: var(--shadow-popover);
			animation: toast-enter 250ms cubic-bezier(0.22, 1.1, 0.36, 1);
			touch-action: pan-x;
			user-select: none;
			will-change: transform, opacity;
			transition: transform 150ms cubic-bezier(0.33, 1, 0.68, 1);
		}

		.toast[data-swiping] {
			transition: none;
		}

		.toast[data-dismissing] {
			animation: toast-exit 200ms cubic-bezier(0.33, 0, 0.67, 0) forwards;
		}

		.toast[data-swipe-dismiss] {
			animation: toast-swipe-exit 150ms cubic-bezier(0.33, 0, 0.67, 0) forwards;
		}

		/* When title is present, align icon to top */
		.toast:has(.toast-title) {
			align-items: flex-start;
		}

		/* Toast icon */
		.toast > svg:first-child {
			flex-shrink: 0;
			width: 1rem;
			height: 1rem;
		}

		.toast:has(.toast-title) > svg:first-child {
			translate: 0 0.125rem;
		}

		/* Toast content */
		.toast-content {
			flex: 1;
			min-width: 0;
		}

		.toast-title {
			font-weight: 500;
		}

		.toast-title + .toast-description {
			margin-top: 0.125rem;
		}

		.toast-description {
			color: var(--muted-foreground);
		}

		/* Toast close button */
		.toast-close {
			position: absolute;
			right: 0.25rem;
			width: 2.25rem;
			height: 2.25rem;
			display: flex;
			align-items: center;
			justify-content: center;
			border-radius: var(--radius-lg);
			border: none;
			background: transparent;
			color: var(--muted-foreground);
			cursor: pointer;
			opacity: 0.4;
			transition: opacity 150ms, background-color 150ms;
		}

		.toast-close svg {
			width: 0.75rem;
			height: 0.75rem;
		}

		.toast:hover .toast-close {
			opacity: 1;
		}

		.toast-close:hover {
			background-color: var(--muted);
			opacity: 1;
		}

		/* Variant: success */
		.toast-success > svg:first-child {
			color: var(--success);
		}

		/* Variant: info */
		.toast-info > svg:first-child {
			color: var(--info);
		}

		/* Variant: warning */
		.toast-warning {
			background-color: color-mix(in oklch, var(--warning) 5%, var(--popover));
		}
		.toast-warning > svg:first-child {
			color: var(--warning);
		}

		/* Variant: error */
		.toast-error {
			background-color: color-mix(in oklch, var(--destructive) 5%, var(--popover));
			border-color: color-mix(in oklch, var(--destructive) 25%, var(--border));
		}
		.toast-error > svg:first-child {
			color: var(--destructive);
		}

		/*
		 * Animations — asymmetric enter/exit.
		 * Enter: spring overshoot + translateY for organic arc (250ms).
		 * Exit: gentle fade + scale-down, faster than enter (200ms).
		 * Swipe-exit: fast follow-through in swipe direction (150ms).
		 */

		@keyframes toast-enter {
			from {
				opacity: 0;
				transform: translateY(12px) scale(0.96);
			}
			to {
				opacity: 1;
				transform: translateY(0) scale(1);
			}
		}

		@keyframes toast-exit {
			0% {
				opacity: 1;
				transform: translateY(0) scale(1);
			}
			100% {
				opacity: 0;
				transform: translateY(8px) scale(0.96);
			}
		}

		/*
		 * Swipe exit starts from wherever the finger left the toast
		 * (--toast-swipe-x / --toast-swipe-o are stamped on touchend), so the
		 * dismissal continues the drag instead of snapping back to zero first.
		 */
		@keyframes toast-swipe-exit {
			from {
				opacity: var(--toast-swipe-o, 1);
				transform: translateX(var(--toast-swipe-x, 0px));
			}
			to {
				opacity: 0;
				transform: translateX(110%);
			}
		}

		/* Dark mode */
		.dark .toast-warning {
			background-color: color-mix(in oklch, var(--warning) 8%, var(--popover));
		}

		.dark .toast-error {
			background-color: color-mix(in oklch, var(--destructive) 10%, var(--popover));
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.toast {
				animation: none;
				transition: none;
			}
			.toast[data-dismissing],
			.toast[data-swipe-dismiss] {
				animation: none;
				opacity: 0;
			}
		}

/* tooltip.templ */
/* Base */
		.tooltip {
			position: relative;
			display: inline-flex;
			--tooltip-delay: 400ms;
		}

		.tooltip-trigger {
			display: inline-flex;
			align-items: center;
		}

		.tooltip-content {
			position: absolute;
			z-index: 50;
			display: inline-flex;
			width: max-content;
			max-width: 20rem;
			align-items: center;
			gap: 0.375rem;
			border-radius: var(--radius-md);
			background-color: var(--foreground);
			padding: 0.375rem 0.75rem;
			font-size: 0.75rem;
			line-height: 1rem;
			font-weight: 500;
			color: var(--background);
			/* Popover-tier elevation — matters most in dark mode, where the
			   light chip needs the shadow to sit above the page */
			box-shadow: var(--shadow-popover);
			pointer-events: none;
			opacity: 0;
			visibility: hidden;
			/* Exit: fast accelerate-out, no delay (Timing + Slow In & Slow Out) */
			transition:
				opacity 100ms cubic-bezier(0.4, 0, 1, 1),
				transform 100ms cubic-bezier(0.4, 0, 1, 1),
				visibility 0s linear 100ms;
		}

		/* Show on hover — delayed to filter accidental triggers (Jakob's Law) */
		.tooltip:hover > .tooltip-content {
			opacity: 1;
			visibility: visible;
			pointer-events: auto;
			/* Enter: decelerate-in after delay (Staging + Selective Attention) */
			transition:
				opacity 150ms cubic-bezier(0, 0, 0.2, 1) var(--tooltip-delay),
				transform 150ms cubic-bezier(0, 0, 0.2, 1) var(--tooltip-delay),
				visibility 0s linear var(--tooltip-delay);
		}

		/* Show on keyboard focus — no delay for intentional action (Doherty Threshold) */
		.tooltip:focus-within > .tooltip-content {
			opacity: 1;
			visibility: visible;
			pointer-events: auto;
			transition:
				opacity 150ms cubic-bezier(0, 0, 0.2, 1),
				transform 150ms cubic-bezier(0, 0, 0.2, 1),
				visibility 0s linear 0s;
		}

		/* Dismissed via Escape — instant hide, no transition (Cognitive Load) */
		.tooltip[data-tooltip-dismissed] > .tooltip-content {
			opacity: 0 !important;
			visibility: hidden !important;
			transition: none !important;
		}

		/* Side: top (default) */
		.tooltip-content[data-side="top"] {
			bottom: 100%;
			left: 50%;
			transform: translateX(-50%) translateY(0.25rem) scale(0.95);
			margin-bottom: 0.25rem;
		}
		.tooltip:hover > .tooltip-content[data-side="top"],
		.tooltip:focus-within > .tooltip-content[data-side="top"] {
			transform: translateX(-50%) translateY(0) scale(1);
		}

		/* Side: bottom */
		.tooltip-content[data-side="bottom"] {
			top: 100%;
			left: 50%;
			transform: translateX(-50%) translateY(-0.25rem) scale(0.95);
			margin-top: 0.25rem;
		}
		.tooltip:hover > .tooltip-content[data-side="bottom"],
		.tooltip:focus-within > .tooltip-content[data-side="bottom"] {
			transform: translateX(-50%) translateY(0) scale(1);
		}

		/* Side: left */
		.tooltip-content[data-side="left"] {
			right: 100%;
			top: 50%;
			transform: translateY(-50%) translateX(0.25rem) scale(0.95);
			margin-right: 0.25rem;
		}
		.tooltip:hover > .tooltip-content[data-side="left"],
		.tooltip:focus-within > .tooltip-content[data-side="left"] {
			transform: translateY(-50%) translateX(0) scale(1);
		}

		/* Side: right */
		.tooltip-content[data-side="right"] {
			left: 100%;
			top: 50%;
			transform: translateY(-50%) translateX(-0.25rem) scale(0.95);
			margin-left: 0.25rem;
		}
		.tooltip:hover > .tooltip-content[data-side="right"],
		.tooltip:focus-within > .tooltip-content[data-side="right"] {
			transform: translateY(-50%) translateX(0) scale(1);
		}

		/* Arrow base */
		/*
		 * Arrow — purely decorative, so it is a pseudo-element rather than a
		 * real node in every tooltip instance.
		 */
		.tooltip-content::after {
			content: '';
			position: absolute;
			width: 0.5rem;
			height: 0.5rem;
			background-color: var(--foreground);
			border-radius: 1px;
			transform: rotate(45deg);
		}

		/* Arrow: top */
		.tooltip-content[data-side="top"]::after {
			bottom: -0.25rem;
			left: 50%;
			margin-left: -0.25rem;
		}

		/* Arrow: bottom */
		.tooltip-content[data-side="bottom"]::after {
			top: -0.25rem;
			left: 50%;
			margin-left: -0.25rem;
		}

		/* Arrow: left */
		.tooltip-content[data-side="left"]::after {
			right: -0.25rem;
			top: 50%;
			margin-top: -0.25rem;
		}

		/* Arrow: right */
		.tooltip-content[data-side="right"]::after {
			left: -0.25rem;
			top: 50%;
			margin-top: -0.25rem;
		}

		/* Reduced motion — instant show/hide, keep delay to prevent noise */
		@media (prefers-reduced-motion: reduce) {
			.tooltip-content {
				transition-duration: 0ms;
			}
			.tooltip:hover > .tooltip-content {
				transition:
					opacity 0ms linear var(--tooltip-delay),
					transform 0ms linear var(--tooltip-delay),
					visibility 0s linear var(--tooltip-delay);
			}
			.tooltip:focus-within > .tooltip-content {
				transition-duration: 0ms;
			}
		}

/* typography.templ */
/* --- Typography --- */

		/* H1 */
		.typo-h1 {
			scroll-margin-top: 5rem;
			font-size: 2.25rem;
			line-height: 2.5rem;
			font-weight: 800;
			letter-spacing: -0.025em;
			text-wrap: balance;
			color: var(--foreground);
		}

		/* H2 */
		.typo-h2 {
			scroll-margin-top: 5rem;
			border-bottom: 1px solid var(--border);
			padding-bottom: 0.5rem;
			font-size: 1.875rem;
			line-height: 2.25rem;
			font-weight: 600;
			letter-spacing: -0.025em;
			color: var(--foreground);
			transition: border-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		/* H3 */
		.typo-h3 {
			scroll-margin-top: 5rem;
			font-size: 1.5rem;
			line-height: 2rem;
			font-weight: 600;
			letter-spacing: -0.025em;
			color: var(--foreground);
		}

		/* H4 */
		.typo-h4 {
			scroll-margin-top: 5rem;
			font-size: 1.25rem;
			line-height: 1.75rem;
			font-weight: 600;
			letter-spacing: -0.025em;
			color: var(--foreground);
		}

		/* Paragraph */
		.typo-p {
			line-height: 1.75;
			color: var(--foreground);
			/* Protects the last line from a lone orphan word */
			text-wrap: pretty;
		}
		.typo-p + .typo-p {
			margin-top: 1.5rem;
		}

		/* Blockquote */
		.typo-blockquote {
			margin-top: 1.5rem;
			border-left: 2px solid var(--border);
			padding-left: 1.5rem;
			font-style: italic;
			color: var(--foreground);
			transition: border-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}

		/* Table */
		.typo-table-wrapper {
			margin-top: 1.5rem;
			margin-bottom: 1.5rem;
			width: 100%;
			overflow-y: auto;
		}
		.typo-table-wrapper table {
			width: 100%;
		}
		.typo-table-wrapper thead tr {
			margin: 0;
			border-bottom: 1px solid var(--border);
			padding: 0;
		}
		.typo-table-wrapper th {
			border-bottom: 1px solid var(--border);
			padding: 0.5rem 1rem;
			text-align: left;
			font-weight: 700;
			color: var(--foreground);
		}
		.typo-table-wrapper td {
			padding: 0.5rem 1rem;
			text-align: left;
			color: var(--foreground);
			/* Tabular figures — numeric cells align down the column */
			font-variant-numeric: tabular-nums;
		}
		.typo-table-wrapper tbody tr {
			border-bottom: 1px solid var(--border);
			transition: background-color 150ms cubic-bezier(0.4, 0, 0.2, 1);
		}
		.typo-table-wrapper tbody tr:last-child {
			border-bottom: 0;
		}
		.typo-table-wrapper tbody tr:hover {
			background-color: var(--muted);
		}

		/* List */
		.typo-list {
			margin-top: 1.5rem;
			margin-bottom: 1.5rem;
			margin-left: 1.5rem;
			list-style-type: disc;
		}
		.typo-list > li {
			margin-top: 0.5rem;
			color: var(--foreground);
		}

		/* Inline Code */
		.typo-code {
			position: relative;
			border-radius: var(--radius-md);
			background-color: var(--muted);
			padding-left: 0.3rem;
			padding-right: 0.3rem;
			padding-top: 0.2rem;
			padding-bottom: 0.2rem;
			font-family: 'Geist Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, 'Liberation Mono', 'Courier New', monospace;
			font-size: 0.875rem;
			font-weight: 600;
			color: var(--foreground);
		}

		/* Lead */
		.typo-lead {
			font-size: 1.25rem;
			line-height: 1.75rem;
			color: var(--muted-foreground);
		}

		/* Large */
		.typo-large {
			font-size: 1.125rem;
			line-height: 1.75rem;
			font-weight: 600;
			color: var(--foreground);
		}

		/* Small */
		.typo-small {
			font-size: 0.875rem;
			line-height: 1;
			font-weight: 500;
			color: var(--foreground);
		}

		/* Muted */
		.typo-muted {
			font-size: 0.875rem;
			line-height: 1.25rem;
			color: var(--muted-foreground);
		}

		/* Dark mode — rules and borders ride the tokens, which flip themselves;
		   only the row hover needs softening against the darker surface. */
		.dark .typo-table-wrapper tbody tr:hover {
			background-color: color-mix(in oklch, var(--muted) 50%, transparent);
		}

		/* Reduced motion */
		@media (prefers-reduced-motion: reduce) {
			.typo-h2,
			.typo-blockquote,
			.typo-table-wrapper tbody tr {
				transition-duration: 75ms;
			}
		}

/* app.templ */
/* Fixed viewport-filling shell so the DOCUMENT never scrolls -- only .view
		   scrolls internally. iOS Safari's Liquid Glass toolbar floats below
		   window.innerHeight; with a scrolling document, flow content paints into
		   that zone behind the translucent bar. A fixed shell keeps all content
		   above the bar, leaving only the solid html background-color in the zone.
		   position:fixed+inset:0 (NOT height:100dvh) gives .view a definite height
		   that iOS reliably scrolls -- the dvh version dead-locked scrolling. */
		.app {
			position: fixed;
			inset: 0;
			margin-inline: auto;
			max-width: 40rem;
			display: flex;
			flex-direction: column;
		}
		.view {
			flex: 1 1 auto;
			min-height: 0;
			overflow-y: auto;
			overscroll-behavior: contain;
			-webkit-overflow-scrolling: touch;
			padding: 1.6rem 1.1rem 1.1rem;
			display: flex;
			flex-direction: column;
			gap: 1rem;
			animation: viewIn 220ms cubic-bezier(0.2, 0, 0.1, 1);
		}
		/* .view's children (cards etc.) use overflow:hidden, which gives flex
		   items an automatic min-size of 0 -- without flex-shrink:0 the flex
		   layout squeezes/clips them to fit instead of letting .view scroll. */
		.view > * {
			flex-shrink: 0;
		}
		@keyframes viewIn {
			from { opacity: 0; transform: translateY(6px); }
			to { opacity: 1; transform: none; }
		}
		@media (prefers-reduced-motion: reduce) {
			.view { animation: none; }
		}

		/* Bottom tab bar -- in-flow flex child at the bottom of the fixed shell. */
		.tabbar {
			flex-shrink: 0;
			z-index: 30;
			display: flex;
			justify-content: space-around;
			gap: 0.25rem;
			padding: 0.5rem 0.5rem calc(0.5rem + env(safe-area-inset-bottom));
			background: var(--background);
			border-top: 1px solid var(--border);
		}
		.tab {
			flex: 1;
			max-width: 6rem;
			display: flex;
			align-items: center;
			justify-content: center;
			padding: 0.55rem 0.25rem;
			border-radius: var(--radius-md);
			color: var(--muted-foreground);
			opacity: 0.55;
			text-decoration: none;
			-webkit-tap-highlight-color: transparent;
			transition: opacity 160ms ease, color 160ms ease;
		}
		.tab svg { width: 1.5rem; height: 1.5rem; stroke-width: 2; }
		.tab:hover { opacity: 0.8; }
		.tab[aria-current="page"] {
			color: var(--foreground);
			opacity: 1;
		}
		.tab[aria-current="page"] svg { stroke-width: 2.4; }

		/* Headings inside views */
		.greeting {
			font-size: 1.5rem;
			font-weight: 800;
			line-height: 1.15;
			letter-spacing: -0.01em;
		}
		.view-subtitle { margin-top: 0.5rem; }
		.muted { color: var(--muted-foreground); font-size: 0.875rem; }

		/* Big primary action */
		.big-btn {
			width: 100%;
			justify-content: center;
			height: 3.5rem;
			font-size: 1.05rem;
			font-weight: 700;
			gap: 0.5rem;
		}
		.row-btns { display: flex; gap: 0.6rem; }
		.row-btns .btn { flex: 1; justify-content: center; }
		.seg { display: flex; gap: 0.5rem; margin-top: 0.6rem; }
		.seg .btn { flex: 1; justify-content: center; }
		.field-list { display: flex; flex-direction: column; gap: 0.4rem; }
		.field-chip {
			display: flex;
			align-items: center;
			justify-content: space-between;
			gap: 0.5rem;
			padding: 0.35rem 0.4rem 0.35rem 0.8rem;
			border: 1px solid var(--border);
			border-radius: var(--radius-md);
			background: color-mix(in oklch, var(--muted) 30%, transparent);
			font-size: 0.92rem;
			font-weight: 600;
		}
		.field-chip em { font-style: normal; font-weight: 400; font-size: 0.8rem; }

		/* Card component used as a <button> (interactive list rows / theme picker) */
		button.card { border: 0; font: inherit; color: var(--card-foreground); }
		.card-list { display: flex; flex-direction: column; gap: 0.6rem; }
		.list-card { flex-direction: row; align-items: center; justify-content: space-between; gap: 0.75rem; padding-left: 1rem; padding-right: 1rem; text-align: left; width: 100%; }
		.list-card-main { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
		.list-card-desc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
		.list-card .ic { flex-shrink: 0; color: var(--muted-foreground); }

		/* Exercise cards (active workout) — built on the Card component */
		.ex-card { gap: 0.75rem; }
		.ex-title { font-size: 1.05rem; font-weight: 700; }
		.ex-lastval { color: var(--foreground); font-weight: 600; }
		.ex-sets { display: flex; flex-direction: column; gap: 0.5rem; }

		.set-row {
			display: flex;
			align-items: flex-end;
			gap: 0.4rem;
			padding: 0.4rem;
			border-radius: var(--radius-md);
			background: color-mix(in oklch, var(--muted) 35%, transparent);
		}
		.set-row[data-done="true"] {
			background: color-mix(in oklch, var(--primary) 14%, transparent);
			outline: 1px solid color-mix(in oklch, var(--primary) 40%, transparent);
		}
		.set-num {
			width: 1.3rem;
			text-align: center;
			font-weight: 800;
			font-size: 0.85rem;
			color: var(--muted-foreground);
			padding-bottom: 0.5rem;
		}
		.set-fields { display: flex; flex-wrap: wrap; gap: 0.4rem; flex: 1; }
		.fld { display: flex; flex-direction: column; gap: 0.15rem; }
		.fld > span {
			font-size: 0.6rem;
			text-transform: uppercase;
			letter-spacing: 0.05em;
			color: var(--muted-foreground);
			font-weight: 700;
			padding-left: 0.15rem;
		}
		.fld.num { width: 4.5rem; }
		.fld.txt { flex: 1; min-width: 6rem; }
		.set-in { height: 2.5rem; text-align: center; font-weight: 700; font-size: 1rem; padding: 0.25rem; }
		.fld.txt .set-in { text-align: left; font-weight: 400; font-size: 0.9rem; }
		.set-ctl { display: flex; flex-direction: column; gap: 0.15rem; }

		.add-ex { display: flex; gap: 0.5rem; align-items: stretch; }
		.add-ex .input { flex: 1; }

		.finish-bar { padding-top: 0.4rem; }

		/* History: expandable workout card (Card surface, flush padding) */
		.wk-card { gap: 0; padding-top: 0; padding-bottom: 0; }
		.wk-head {
			display: flex;
			align-items: center;
			justify-content: space-between;
			gap: 0.5rem;
			padding: 0.85rem 1rem;
			cursor: pointer;
			width: 100%;
			text-align: left;
			background: transparent;
			border: 0;
			font: inherit;
			color: inherit;
		}
		.wk-body { display: flex; flex-direction: column; gap: 0.6rem; padding-bottom: 1rem; }
		.wk-ex { font-size: 0.85rem; }
		.wk-ex b { font-weight: 700; }
		.wk-sets { color: var(--muted-foreground); }

		.chart svg { width: 100%; height: auto; display: block; }

		.field-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.75rem 0; border-bottom: 1px solid var(--border); }
		.field-row:last-child { border-bottom: 0; }
		.theme-grid { display: flex; flex-direction: column; gap: 0.5rem; }
		.theme-opt { flex-direction: row; align-items: center; justify-content: space-between; gap: 0.75rem; padding-left: 1rem; padding-right: 1rem; width: 100%; text-align: left; }
		.theme-opt[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--primary); }
		.theme-opt-main { display: flex; flex-direction: column; gap: 0.15rem; min-width: 0; }
		.swatches { display: flex; gap: 0.25rem; }
		.swatch { width: 1.1rem; height: 1.1rem; border-radius: 999px; border: 1px solid color-mix(in oklch, var(--foreground) 20%, transparent); }

		/* Toast / celebration */
		.toast {
			position: fixed;
			top: 4rem;
			left: 50%;
			transform: translateX(-50%);
			z-index: 50;
			max-width: calc(100% - 2rem);
			padding: 0.85rem 1.25rem;
			border-radius: var(--radius-lg);
			background: var(--primary);
			color: var(--primary-foreground);
			font-weight: 800;
			font-size: 1rem;
			text-align: center;
			box-shadow: 0 10px 30px color-mix(in oklch, var(--foreground) 25%, transparent);
			animation: toastIn 240ms cubic-bezier(0.2, 0.9, 0.3, 1.3);
		}
		.toast[data-kind="pr"] {
			background: var(--accent);
			color: var(--accent-foreground);
			font-size: 1.15rem;
			animation: toastIn 240ms cubic-bezier(0.2, 0.9, 0.3, 1.3), pop 600ms 240ms;
		}
		.toast .combo { display: block; font-size: 0.75rem; opacity: 0.85; margin-top: 0.15rem; }
		@keyframes toastIn {
			from { opacity: 0; transform: translateX(-50%) translateY(-12px) scale(0.9); }
			to { opacity: 1; transform: translateX(-50%) translateY(0) scale(1); }
		}
		@keyframes pop {
			0%, 100% { transform: translateX(-50%) scale(1); }
			40% { transform: translateX(-50%) scale(1.06); }
		}
		@media (prefers-reduced-motion: reduce) {
			.toast { animation: none; }
		}

		.ic { width: 1.1rem; height: 1.1rem; }

/* login.templ */
.login-wrap {
			min-height: 100dvh;
			display: flex;
			align-items: center;
			justify-content: center;
			padding: 1.5rem;
		}
		.login-card {
			width: 100%;
			max-width: 22rem;
			display: flex;
			flex-direction: column;
			gap: 1rem;
		}
		.login-card h1 {
			font-size: 1.5rem;
			font-weight: 700;
			letter-spacing: -0.02em;
		}
		.login-card .login-sub {
			color: var(--muted-foreground);
			font-size: 0.875rem;
			margin-top: -0.5rem;
		}
		.login-error {
			color: var(--destructive);
			font-size: 0.875rem;
		}
		.login-form {
			display: flex;
			flex-direction: column;
			gap: 0.75rem;
		}

