/* =============================================================================
 *  DotMatrix — progressive fill model.
 *
 *  Cells light up one at a time in `pattern` order, stay lit until the
 *  cycle resets, then repeat. Implementation uses ONE CSS animation per
 *  matrix that drives a registered custom property `--dm-phase` from
 *  0 → 1 (fill), holds it at 1 (hold), then snaps back to 0 (reset)
 *  for a rest period.
 *
 *  Each cell carries a `--dm-threshold` (its position in the fill
 *  sequence, 0..1) and computes its own `::before` opacity as a step
 *  across `phase - threshold`, multiplied by `--dm-edge-sharpness` so
 *  the edge can range from "instant snap" to "warm fade".
 *
 *  Perf shape: N matrices = N animations total (not N × cells). Cell
 *  opacity is computed at paint time by calc(); the compositor batches
 *  updates via the shared phase var. With `contain: layout style paint`
 *  each cell is isolated so work doesn't cascade.
 * ============================================================================= */

@property --dm-phase {
	syntax: '<number>';
	initial-value: 0;
	inherits: true;
}

.dm-root {
	display: grid;
	width: 100%;
	gap: var(--dm-gap, 5px);
	padding: var(--dm-pad, 0.25rem);
	border-radius: 0.5rem;
	background-color: transparent;

	/* Skip render work when the matrix is offscreen. */
	content-visibility: auto;
	contain-intrinsic-size: auto 200px;
	transform: translateZ(0);

	--dm-on: var(--foreground);
	--dm-off: color-mix(in oklch, var(--foreground) 12%, transparent);
	--dm-glow: color-mix(in oklch, var(--foreground) 50%, transparent);
}

/* ─── Cell: base OFF layer — neutral dim ──────────────────────────────── */

.dm-cell {
	position: relative;
	aspect-ratio: 1 / 1;
	min-width: 4px;
	min-height: 4px;
	border-radius: var(--dm-radius, 32%);
	background-color: color-mix(in oklch, var(--foreground) 10%, transparent);
	opacity: var(--dm-off-opacity, 0.45);
	transform: scale(var(--dm-off-scale, 0.9));
	contain: layout style paint;

	--dm-threshold: 0;
	--dm-stagger-delay: 0ms;
}

/* ─── Pseudo: ON layer — LED look, fades in as phase crosses threshold ─ */

.dm-cell::before {
	content: '';
	position: absolute;
	inset: 0;
	border-radius: inherit;
	background-color: var(--dm-on);
	background-image:
		radial-gradient(
			circle at 32% 26%,
			rgb(255 255 255 / 0.35) 0%,
			rgb(255 255 255 / 0.12) 18%,
			transparent 48%
		),
		radial-gradient(
			circle at 70% 78%,
			rgb(0 0 0 / 0.22) 0%,
			transparent 60%
		);
	box-shadow:
		inset 0 0 1px rgb(255 255 255 / 0.55),
		inset 0 0 0 1px color-mix(in oklch, var(--dm-on) 65%, transparent),
		inset 0 -1px 2px color-mix(in oklch, var(--dm-on) 40%, black 20%);
	opacity: 0;
	pointer-events: none;
}

.dm-root[data-variant='glow'] .dm-cell::before {
	box-shadow:
		inset 0 0 1px rgb(255 255 255 / 0.55),
		inset 0 0 0 1px color-mix(in oklch, var(--dm-on) 65%, transparent),
		inset 0 -1px 2px color-mix(in oklch, var(--dm-on) 40%, black 20%),
		0 0 2px var(--dm-glow),
		0 0 6px var(--dm-glow),
		0 0 12px color-mix(in oklch, var(--dm-glow) 55%, transparent);
}

/* ─── Running: phase animates on root, cells derive state from it ────── */

.dm-root[data-status='running'] {
	animation-name: var(--dm-anim-phase);
	animation-duration: var(--dm-cycle-total, 2600ms);
	animation-iteration-count: infinite;
	animation-timing-function: linear;
	animation-fill-mode: both;
}

/* Lit fraction for THIS cell — 0 when off, 1 when fully filled, ramped
 * near the threshold by `--dm-edge-sharpness`. Reused by both the pseudo
 * opacity (the LED glow) and the cell's scale (grows when lit). */
.dm-root[data-status='running'] .dm-cell {
	opacity: 1;
	transform: scale(
		calc(
			var(--dm-off-scale, 0.88) +
			(var(--dm-on-scale, 1) - var(--dm-off-scale, 0.88)) *
			clamp(
				0,
				(var(--dm-phase) - var(--dm-threshold)) *
					var(--dm-edge-sharpness, 50),
				1
			)
		)
	);
}

.dm-root[data-status='running'] .dm-cell::before {
	opacity: clamp(
		0,
		calc(
			(var(--dm-phase) - var(--dm-threshold)) *
			var(--dm-edge-sharpness, 50)
		),
		1
	);
}

/* ─── Accent cells ────────────────────────────────────────────────────── */

.dm-cell[data-accent='true'] {
	opacity: 1;
	transform: scale(1);
}

.dm-cell[data-accent='true']::before {
	background-color: var(--primary);
	background-image: none;
	box-shadow:
		inset 0 0 1px rgb(255 255 255 / 0.5),
		0 0 3px color-mix(in oklch, var(--primary) 70%, transparent),
		0 0 7px color-mix(in oklch, var(--primary) 50%, transparent);
	opacity: 1 !important;
}

/* ─── Lifecycle FINAL animations ──────────────────────────────────────── */

@keyframes dm-success-pop {
	0% {
		transform: scale(0.94);
		filter: brightness(0.7);
	}
	45% {
		transform: scale(1.18);
		filter: brightness(1.4);
	}
	100% {
		transform: scale(1);
		filter: brightness(1);
	}
}

@keyframes dm-fail-strobe {
	0% {
		transform: scale(1);
		filter: brightness(1);
	}
	22% {
		transform: scale(1.22);
		filter: brightness(1.55) saturate(1.4);
	}
	55% {
		transform: scale(0.92);
		filter: brightness(0.75);
	}
	100% {
		transform: scale(0.97);
		filter: brightness(1);
	}
}

@keyframes dm-cancelled-dim {
	0% {
		transform: scale(1);
		opacity: 1;
	}
	100% {
		transform: scale(0.9);
		opacity: 0.55;
	}
}

.dm-root[data-status='success'] {
	animation: none;
}
.dm-root[data-status='success'] .dm-cell {
	opacity: 1;
	transform: scale(1);
	animation: dm-success-pop 520ms cubic-bezier(0.2, 0, 0.2, 1) both;
	animation-delay: var(--dm-stagger-delay, 0ms);
}
.dm-root[data-status='success'] .dm-cell::before {
	opacity: 1;
}

.dm-root[data-status='failed'] {
	animation: none;
}
.dm-root[data-status='failed'] .dm-cell {
	opacity: 1;
	animation: dm-fail-strobe 560ms cubic-bezier(0.2, 0, 0.2, 1) both;
	animation-delay: var(--dm-stagger-delay, 0ms);
}
.dm-root[data-status='failed'] .dm-cell::before {
	background-color: var(--destructive);
	box-shadow:
		inset 0 0 1px rgb(255 255 255 / 0.5),
		inset 0 0 0 1px color-mix(in oklch, var(--destructive) 55%, transparent),
		inset 0 -1px 2px color-mix(in oklch, var(--destructive) 40%, black 20%);
	opacity: 1;
}
.dm-root[data-status='failed'][data-variant='glow'] .dm-cell::before {
	box-shadow:
		inset 0 0 1px rgb(255 255 255 / 0.55),
		inset 0 0 0 1px color-mix(in oklch, var(--destructive) 55%, transparent),
		inset 0 -1px 2px color-mix(in oklch, var(--destructive) 40%, black 20%),
		0 0 3px color-mix(in oklch, var(--destructive) 70%, transparent),
		0 0 9px color-mix(in oklch, var(--destructive) 55%, transparent);
}

.dm-root[data-status='cancelled'] {
	animation: none;
}
.dm-root[data-status='cancelled'] .dm-cell {
	background-color: color-mix(in oklch, var(--foreground) 22%, transparent);
	opacity: 0.65;
	animation: dm-cancelled-dim 380ms cubic-bezier(0.4, 0, 0.8, 0.6) both;
	animation-delay: var(--dm-stagger-delay, 0ms);
}
.dm-root[data-status='cancelled'] .dm-cell::before {
	opacity: 0;
}

.dm-root[data-status='idle'] {
	animation: none;
}
.dm-root[data-status='idle'] .dm-cell::before {
	opacity: 0;
}

/* ─── Theme-token palettes ────────────────────────────────────────────── */

.dm--foreground {
	--dm-on: var(--foreground);
	--dm-off: color-mix(in oklch, var(--foreground) 12%, transparent);
	--dm-glow: color-mix(in oklch, var(--foreground) 55%, transparent);
}

.dm--primary {
	--dm-on: var(--primary);
	--dm-off: color-mix(in oklch, var(--primary) 14%, transparent);
	--dm-glow: color-mix(in oklch, var(--primary) 65%, transparent);
}

.dm--destructive {
	--dm-on: var(--destructive);
	--dm-off: color-mix(in oklch, var(--destructive) 14%, transparent);
	--dm-glow: color-mix(in oklch, var(--destructive) 65%, transparent);
}

.dm--warning {
	--dm-on: var(--warning);
	--dm-off: color-mix(in oklch, var(--warning) 14%, transparent);
	--dm-glow: color-mix(in oklch, var(--warning) 65%, transparent);
}

/* ─── Named palettes (OKLCH for stability) ────────────────────────────── */

.dm--cyan {
	--dm-on: oklch(90% 0.2 195);
	--dm-off: oklch(40% 0.08 195 / 0.4);
	--dm-glow: oklch(80% 0.25 195 / 0.95);
}
.dm--magenta {
	--dm-on: oklch(85% 0.25 330);
	--dm-off: oklch(40% 0.08 330 / 0.4);
	--dm-glow: oklch(75% 0.3 330 / 0.95);
}
.dm--yellow {
	--dm-on: oklch(95% 0.2 90);
	--dm-off: oklch(50% 0.08 90 / 0.4);
	--dm-glow: oklch(90% 0.25 90 / 0.95);
}
.dm--green {
	--dm-on: oklch(90% 0.25 145);
	--dm-off: oklch(40% 0.08 145 / 0.4);
	--dm-glow: oklch(80% 0.3 145 / 0.95);
}
.dm--orange {
	--dm-on: oklch(85% 0.22 50);
	--dm-off: oklch(45% 0.08 50 / 0.4);
	--dm-glow: oklch(75% 0.28 50 / 0.95);
}
.dm--blue {
	--dm-on: oklch(80% 0.22 260);
	--dm-off: oklch(40% 0.08 260 / 0.4);
	--dm-glow: oklch(70% 0.28 260 / 0.95);
}
.dm--red {
	--dm-on: oklch(70% 0.25 25);
	--dm-off: oklch(40% 0.08 25 / 0.4);
	--dm-glow: oklch(60% 0.3 25 / 0.95);
}
.dm--purple {
	--dm-on: oklch(75% 0.22 300);
	--dm-off: oklch(40% 0.08 300 / 0.4);
	--dm-glow: oklch(65% 0.28 300 / 0.95);
}
.dm--teal {
	--dm-on: oklch(82% 0.18 175);
	--dm-off: oklch(40% 0.08 175 / 0.4);
	--dm-glow: oklch(72% 0.24 175 / 0.95);
}
.dm--pink {
	--dm-on: oklch(80% 0.2 350);
	--dm-off: oklch(45% 0.08 350 / 0.4);
	--dm-glow: oklch(70% 0.26 350 / 0.95);
}
.dm--lime {
	--dm-on: oklch(88% 0.22 120);
	--dm-off: oklch(45% 0.08 120 / 0.4);
	--dm-glow: oklch(80% 0.28 120 / 0.95);
}
.dm--white {
	--dm-on: oklch(98% 0 0);
	--dm-off: oklch(50% 0 0 / 0.3);
	--dm-glow: oklch(95% 0 0 / 0.85);
}

/* ─── Reduced motion — freeze animations ─────────────────────────────── */

@media (prefers-reduced-motion: reduce) {
	.dm-root[data-status='running'],
	.dm-root[data-status='success'] .dm-cell,
	.dm-root[data-status='failed'] .dm-cell,
	.dm-root[data-status='cancelled'] .dm-cell {
		animation: none !important;
	}
	.dm-cell {
		transform: none;
	}
}

