/* Component layer — Apple-style system controls and News-inspired
   surfaces shared by both apps. Adds .btn / .input / .pill / .card /
   .field-label primitives, plus a global :focus-visible ring so every
   interactive element gets keyboard focus feedback without an opt-in.

   Apps mount with <body class="kid"> or <body class="admin">. The .kid
   scope overrides the primary CTA palette to warm orange while keeping
   the rest of the Geist palette intact. */

/* ── Reset & baseline ─────────────────────────────────────────────── */

html, body {
  font-family: var(--font-sans);
  color: var(--gray-1000);
  background: var(--background-200);
  min-height: 100%;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

[x-cloak] { display: none !important; }

/* Strip the browser's blue outline; the global :focus-visible rule
   below puts a proper two-layer ring back on every interactive node. */
button, [role="button"], a, input, select, textarea, [tabindex] {
  outline: none;
}

button, [role="button"], select, input[type="checkbox"], input[type="radio"] {
  cursor: pointer;
}

:where(button, a, input, textarea, select, [tabindex]):focus-visible {
  outline: none;
  box-shadow:
    0 0 0 2px var(--background-100),
    0 0 0 4px rgba(0, 113, 227, 0.36);
  border-radius: var(--radius-sm);
}

/* ── Buttons — Geist sizes: 32 / 40 / 48 px, padding 6 / 10 / 14, ─── */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  height: 40px;
  padding: 0 14px;
  font: var(--button-14);
  font-family: var(--font-sans);
  font-weight: 600;
  border-radius: var(--radius-sm);
  border: 0;
  cursor: pointer;
  white-space: nowrap;
  letter-spacing: 0;
  transition:
    background var(--duration-state) var(--ease-out),
    color var(--duration-state) var(--ease-out),
    box-shadow var(--duration-state) var(--ease-out),
    opacity var(--duration-state) var(--ease-out);
  user-select: none;
}
.btn svg,
.btn [x-html] svg,
.segmented-option svg {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}
.btn:disabled,
.btn[aria-disabled="true"] {
  background: var(--gray-100);
  color: var(--gray-700);
  cursor: not-allowed;
  box-shadow: none;
}

.btn-sm { height: 32px; padding: 0 10px; }
.btn-lg { height: 48px; padding: 0 18px; font: var(--button-16); font-family: var(--font-sans); font-weight: 600; }

.btn-primary {
  background: var(--blue-700);
  color: var(--background-100);
  box-shadow: 0 1px 2px rgba(0, 113, 227, 0.12);
}
.btn-primary:hover { background: var(--blue-800); }
.btn-primary:active { background: var(--blue-900); }

/* kid app — primary CTA stays warm; secondary / tertiary / error
   inherit the neutral Geist palette so the warm accent stays scarce. */
body.kid .btn-primary { background: var(--warm-700); color: #fff; }
body.kid .btn-primary:hover { background: var(--warm-800); }
body.kid .btn-primary:active { background: var(--warm-900); }

.btn-secondary {
  background: var(--background-100);
  color: var(--gray-1000);
  box-shadow: inset 0 0 0 1px var(--gray-alpha-400);
}
.btn-secondary:hover {
  background: var(--background-200);
}
.btn-secondary:active { background: var(--gray-100); }

.btn-tertiary {
  background: transparent;
  color: var(--gray-900);
}
.btn-tertiary:hover { background: var(--gray-alpha-100); color: var(--gray-1000); }
.btn-tertiary:active { background: var(--gray-alpha-200); }

.btn-error {
  background: var(--red-800);
  color: #fff;
}
.btn-error:hover { background: var(--red-900); }
.btn-error:active { background: var(--red-1000); }

/* Subtle press feedback for every button — capped at 1px so it stays
   physical without becoming bouncy. Disabled under reduced-motion. */
.btn:not(:disabled):not([aria-disabled="true"]):active {
  transform: translateY(0.5px);
}

/* Loading variant — hides label spacing isn't disrupted, swaps in a
   spinner using the inherited text color. Caller adds .btn-loading
   while keeping the button enabled or disabled at their discretion. */
.btn-loading {
  position: relative;
  color: transparent !important;
  pointer-events: none;
}
.btn-loading > * { visibility: hidden; }
.btn-loading::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  width: 16px;
  height: 16px;
  border-radius: 50%;
  border: 2px solid currentColor;
  border-top-color: transparent;
  color: var(--background-100);
  animation: kr-spin 0.8s linear infinite;
}
.btn-secondary.btn-loading::after,
.btn-tertiary.btn-loading::after { color: var(--gray-900); }
@keyframes kr-spin { to { transform: rotate(360deg); } }

/* Square icon-only button — sized to match neighboring controls.
   Stays in the same family as .btn so focus rings, disabled state
   and active press feedback all apply automatically. */
.btn-icon {
  width: 40px;
  padding: 0;
  flex-shrink: 0;
}
.btn-icon.btn-sm { width: 32px; }
.btn-icon.btn-xs { width: 28px; height: 28px; }
.btn-icon.btn-lg { width: 48px; }
.btn-icon svg { width: 18px; height: 18px; }
.btn-icon.btn-sm svg { width: 16px; height: 16px; }
.btn-icon.btn-xs svg { width: 14px; height: 14px; }

/* ── Input / select / textarea ────────────────────────────────────── */

.input {
  display: block;
  width: 100%;
  height: 40px;
  padding: 0 12px;
  font: var(--label-14);
  font-family: var(--font-sans);
  color: var(--gray-1000);
  background: var(--background-100);
  border-radius: var(--radius-sm);
  border: 0;
  box-shadow: inset 0 0 0 1px var(--gray-alpha-400);
  transition:
    background var(--duration-state) var(--ease-out),
    box-shadow var(--duration-state) var(--ease-out);
}
.input::placeholder { color: var(--gray-700); }
.input:hover { box-shadow: inset 0 0 0 1px var(--gray-alpha-500); }
.input:focus {
  box-shadow:
    inset 0 0 0 1px var(--blue-700),
    0 0 0 4px rgba(0, 113, 227, 0.12);
}
.input:disabled {
  background: var(--gray-100);
  color: var(--gray-700);
  cursor: not-allowed;
}
.input-sm { height: 32px; padding: 0 10px; font: var(--label-13); font-family: var(--font-sans); }
.input-lg { height: 48px; padding: 0 14px; font: var(--label-16); font-family: var(--font-sans); }
textarea.input { height: auto; padding-top: 10px; padding-bottom: 10px; line-height: 1.5; }

.field-label {
  display: block;
  font: var(--label-13);
  font-family: var(--font-sans);
  color: var(--gray-800);
  margin-bottom: 8px;
  letter-spacing: 0;
  font-weight: 600;
}
.field-hint {
  font: var(--label-12);
  font-family: var(--font-sans);
  color: var(--gray-700);
  margin-top: 8px;
}

/* Inline validation states — sit on top of .input. Append .input-error
   to the same node and the focus ring switches to a red glow so the
   field stays the field, not a new component. */
.input-error,
.input.input-error {
  box-shadow:
    inset 0 0 0 1px var(--red-700),
    var(--ring-error);
}
.input-error:focus,
.input.input-error:focus {
  box-shadow:
    inset 0 0 0 1px var(--red-700),
    var(--ring-error);
}
.input-success,
.input.input-success {
  box-shadow:
    inset 0 0 0 1px var(--green-700),
    var(--ring-success);
}

/* The small line of red text that sits under a field. Pair an icon
   (icons.circleX or similar) before the message via a flex parent. */
.field-error {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  font: var(--label-12);
  font-family: var(--font-sans);
  color: var(--red-900);
  margin-top: 6px;
  animation: kr-field-error-in var(--duration-state) var(--ease-out) both;
}
.field-error svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  margin-top: 1px;
}
@keyframes kr-field-error-in {
  from { transform: translateY(-2px); opacity: 0; }
  to   { transform: translateY(0);    opacity: 1; }
}

/* ── Pills (status, count) ────────────────────────────────────────── */

.pill {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  height: 22px;
  padding: 0 8px;
  font: var(--button-12);
  font-family: var(--font-sans);
  border-radius: var(--radius-full);
  white-space: nowrap;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.46);
}
.pill-neutral { background: var(--gray-100);  color: var(--gray-900); }
.pill-success { background: var(--green-100); color: var(--green-900); }
.pill-warning { background: var(--amber-100); color: var(--amber-900); }
.pill-error   { background: var(--red-100);   color: var(--red-900); }
.pill-info    { background: var(--blue-100);  color: var(--blue-900); }
.pill-violet  { background: var(--violet-100, #ede9fe); color: var(--violet-900, #4c1d95); }
.pill-warm    { background: var(--warm-100);  color: var(--warm-900); }
.pill-solid {
  background: var(--gray-1000);
  color: var(--background-100);
}

/* ── Cards & surfaces ─────────────────────────────────────────────── */

/* Default card: restrained system surface. Individual repeated items,
   modals, and genuinely framed tools can use it; larger page sections
   should stay as open layouts or bands. */
.card {
  background: var(--background-100);
  border-radius: var(--radius-md);
  box-shadow:
    inset 0 0 0 1px var(--gray-alpha-300),
    var(--shadow-raised);
  overflow: hidden;
}
/* Surface that should visually "lift" — hero, modal, important panels.
   Combines a 1px border with a soft 2-layer shadow. */
.surface-elevated {
  background: var(--background-100);
  border-radius: var(--radius-lg);
  box-shadow:
    inset 0 0 0 1px var(--gray-alpha-300),
    var(--shadow-popover);
}
/* Subtle inset surface — for grouped panels inside a card. */
.surface-200 {
  background: var(--background-200);
}

/* Reading prose container — Apple News standard width. */
.reading-column {
  max-width: 680px;
  margin-left: auto;
  margin-right: auto;
}
.reading-column-wide {
  max-width: 760px;
  margin-left: auto;
  margin-right: auto;
}

/* A single 1px top hairline — for grouping related rows without the
   "card-in-card" feel. */
.section-hairline {
  border-top: 1px solid var(--gray-alpha-400);
}

.metric-card {
  background: var(--background-100);
  border-radius: var(--radius-md);
  padding: 20px;
  box-shadow:
    inset 0 0 0 1px var(--gray-alpha-300),
    var(--shadow-raised);
  transition:
    transform var(--duration-state) var(--ease-out),
    box-shadow var(--duration-state) var(--ease-out);
}
.metric-card-interactive {
  cursor: pointer;
}
.metric-card-interactive:hover {
  transform: translateY(-1px);
  box-shadow:
    inset 0 0 0 1px var(--gray-alpha-500),
    var(--shadow-popover);
}

.metric-label {
  font: var(--label-12);
  font-family: var(--font-sans);
  color: var(--gray-700);
  font-weight: 600;
  letter-spacing: 0;
}

.metric-value {
  font: var(--heading-32);
  font-family: var(--font-sans);
  font-weight: 700;
  color: var(--gray-1000);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0;
}

.subtile {
  background: var(--background-200);
  border-radius: var(--radius-sm);
  box-shadow: inset 0 0 0 1px var(--gray-alpha-200);
}

/* Segmented control — used for in-page tab strips (read/parent toggle,
   pending/mastered words tab, article keywords/practice/quiz tabs).
   Inspired by macOS NSSegmentedControl + iOS UISegmentedControl. */
.segmented {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  background: var(--gray-alpha-100);
  border-radius: 10px;
  box-shadow: inset 0 0 0 1px var(--gray-alpha-300);
}
.segmented-option {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px;
  border-radius: 8px;
  font: var(--button-14);
  font-family: var(--font-sans);
  font-weight: 600;
  color: var(--gray-900);
  background: transparent;
  border: 0;
  cursor: pointer;
  transition:
    background var(--duration-state) var(--ease-out),
    color var(--duration-state) var(--ease-out),
    box-shadow var(--duration-state) var(--ease-out);
  white-space: nowrap;
}
.segmented-option:hover { color: var(--gray-1000); }
.segmented-option[data-active="true"],
.segmented-option[aria-selected="true"] {
  background: var(--background-100);
  color: var(--gray-1000);
  box-shadow:
    0 1px 2px rgba(15, 23, 42, 0.06),
    inset 0 0 0 1px rgba(255, 255, 255, 0.64);
}

table {
  border-collapse: separate;
  border-spacing: 0;
}

thead th {
  font-weight: 600;
}

tbody tr {
  transition: background var(--duration-state) var(--ease-out);
}

/* ── Scrollbars — match new neutral palette ───────────────────────── */

.kr-scroll::-webkit-scrollbar { width: 10px; height: 10px; }
.kr-scroll::-webkit-scrollbar-track { background: transparent; }
.kr-scroll::-webkit-scrollbar-thumb {
  background: var(--gray-alpha-400);
  border-radius: 9999px;
  border: 2px solid transparent;
  background-clip: padding-box;
}
.kr-scroll::-webkit-scrollbar-thumb:hover {
  background: var(--gray-alpha-500);
  background-clip: padding-box;
}

/* ── Typography helpers — for places Tailwind can't easily reach ──── */

.display-56  { font: var(--display-56);  font-family: var(--font-sans); letter-spacing: 0; }
.display-44  { font: var(--display-44);  font-family: var(--font-sans); letter-spacing: 0; }
.display-36  { font: var(--display-36);  font-family: var(--font-sans); letter-spacing: 0; }
.heading-32  { font: var(--heading-32);  font-family: var(--font-sans); letter-spacing: 0; }
.heading-28  { font: var(--heading-28);  font-family: var(--font-sans); letter-spacing: 0; }
.heading-24  { font: var(--heading-24);  font-family: var(--font-sans); letter-spacing: 0; }
.heading-20  { font: var(--heading-20);  font-family: var(--font-sans); letter-spacing: 0; }
.heading-17  { font: var(--heading-17);  font-family: var(--font-sans); letter-spacing: 0; }
.heading-16  { font: var(--heading-16);  font-family: var(--font-sans); letter-spacing: 0; }
.label-17    { font: var(--label-17);    font-family: var(--font-sans); }
.label-14    { font: var(--label-14);    font-family: var(--font-sans); }
.label-13    { font: var(--label-13);    font-family: var(--font-sans); }
.label-12    { font: var(--label-12);    font-family: var(--font-sans); }
.prose-21    { font: var(--prose-21);    font-family: var(--font-sans); }
.prose-19    { font: var(--prose-19);    font-family: var(--font-sans); }
.prose-17    { font: var(--prose-17);    font-family: var(--font-sans); }
.copy-16     { font: var(--copy-16);     font-family: var(--font-sans); }
.copy-14     { font: var(--copy-14);     font-family: var(--font-sans); }
.copy-13     { font: var(--copy-13);     font-family: var(--font-sans); }
.mono        { font-family: var(--font-mono); font-variant-numeric: tabular-nums; }

/* Make all <select> follow the same input chrome */
select.input { padding-right: 32px; appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, var(--gray-900) 50%),
                    linear-gradient(135deg, var(--gray-900) 50%, transparent 50%);
  background-position: calc(100% - 16px) 50%, calc(100% - 12px) 50%;
  background-size: 4px 4px, 4px 4px;
  background-repeat: no-repeat;
}

@media (max-width: 640px) {
  .display-36 { font-size: 30px; line-height: 36px; }
  .prose-21 { font-size: 19px; line-height: 31px; }
  .segmented {
    max-width: 100%;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-mask-image: linear-gradient(to right, transparent, #000 12px, #000 calc(100% - 12px), transparent);
            mask-image: linear-gradient(to right, transparent, #000 12px, #000 calc(100% - 12px), transparent);
  }
  .segmented::-webkit-scrollbar { display: none; }
}

/* ─────────────────────────────────────────────────────────────────
   Toast — global notification surface, driven by window.KR.toast().
   The host sits at the top-right of the viewport on desktop and shifts
   to a centered band on phones so the close target stays within thumb
   reach. Each toast is its own row with an icon, message, and close.
   ───────────────────────────────────────────────────────────────── */

.toast-host {
  position: fixed;
  top: 20px;
  right: 20px;
  z-index: 9999;
  display: flex;
  flex-direction: column;
  gap: 10px;
  pointer-events: none;
  max-width: min(420px, calc(100vw - 32px));
}

.toast {
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  min-width: 260px;
  max-width: 420px;
  padding: 12px 14px 12px 14px;
  border-radius: var(--radius-md);
  background: var(--gray-1000);
  color: var(--background-100);
  font: var(--label-14);
  font-family: var(--font-sans);
  box-shadow: var(--shadow-floating);
  animation: kr-toast-in var(--duration-popover) var(--ease-spring) both;
}
.toast.is-leaving {
  animation: kr-toast-out var(--duration-state) var(--ease-in-out) both;
}
.toast-icon {
  width: 18px;
  height: 18px;
  flex-shrink: 0;
  margin-top: 1px;
  display: grid;
  place-items: center;
}
.toast-icon svg { width: 18px; height: 18px; }
.toast-body {
  flex: 1;
  min-width: 0;
  line-height: 1.45;
  word-wrap: break-word;
}
.toast-close {
  margin: -2px -4px -2px 4px;
  width: 24px;
  height: 24px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-xs);
  background: transparent;
  color: inherit;
  opacity: 0.6;
  border: 0;
  cursor: pointer;
  transition: opacity var(--duration-state) var(--ease-out),
              background var(--duration-state) var(--ease-out);
}
.toast-close:hover { opacity: 1; background: rgba(255, 255, 255, 0.12); }
.toast-close svg { width: 14px; height: 14px; }

.toast-info    { background: var(--gray-1000); color: var(--background-100); }
.toast-info    .toast-icon { color: var(--blue-500); }
.toast-success { background: var(--green-800); color: #fff; }
.toast-success .toast-icon { color: var(--green-300); }
.toast-warning { background: var(--amber-800); color: #fff; }
.toast-warning .toast-icon { color: var(--amber-200); }
.toast-error   { background: var(--red-800); color: #fff; }
.toast-error   .toast-icon { color: var(--red-300); }

@keyframes kr-toast-in {
  from { transform: translateY(-12px) scale(0.96); opacity: 0; }
  to   { transform: translateY(0)     scale(1);    opacity: 1; }
}
@keyframes kr-toast-out {
  from { transform: translateY(0)    scale(1);    opacity: 1; }
  to   { transform: translateY(-8px) scale(0.98); opacity: 0; }
}

@media (max-width: 640px) {
  .toast-host {
    top: auto;
    right: 16px;
    left: 16px;
    bottom: 16px;
    max-width: none;
    align-items: stretch;
  }
  .toast { min-width: 0; max-width: none; }
}

/* ─────────────────────────────────────────────────────────────────
   Confirm dialog — used by window.KR.confirm(). Two-layer animation:
   scrim fade, card scale+fade. Closing also goes through a leaving
   keyframe so the dialog never blinks off-screen.
   ───────────────────────────────────────────────────────────────── */

.confirm-host {
  position: fixed;
  inset: 0;
  z-index: 10000;
  display: none;
  align-items: center;
  justify-content: center;
  padding: 16px;
}
.confirm-scrim {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  padding: 16px;
  background: rgba(0, 0, 0, 0.4);
  backdrop-filter: blur(10px);
  -webkit-backdrop-filter: blur(10px);
  animation: kr-scrim-in var(--duration-popover) var(--ease-in-out) both;
}
.confirm-scrim.is-leaving {
  animation: kr-scrim-out var(--duration-state) var(--ease-in-out) both;
}
.confirm-card {
  position: relative;
  width: 100%;
  max-width: 420px;
  max-height: calc(100vh - 32px);
  overflow-y: auto;
  background: var(--background-100);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-modal);
  padding: 24px 24px 18px;
  animation: kr-modal-in var(--duration-popover) var(--ease-spring) both;
}
.confirm-card.is-leaving {
  animation: kr-modal-out var(--duration-state) var(--ease-in-out) both;
}
.confirm-title {
  font: var(--heading-17);
  font-family: var(--font-sans);
  color: var(--gray-1000);
  margin-bottom: 6px;
}
.confirm-body {
  font: var(--copy-14);
  font-family: var(--font-sans);
  color: var(--gray-900);
  line-height: 1.55;
  white-space: pre-wrap;
}
.confirm-actions {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 20px;
}
.confirm-hint {
  margin-right: auto;
  font: var(--label-12);
  font-family: var(--font-sans);
  color: var(--gray-700);
  display: flex;
  align-items: center;
  gap: 6px;
}

@keyframes kr-scrim-in  { from { opacity: 0; } to { opacity: 1; } }
@keyframes kr-scrim-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes kr-modal-in  {
  from { transform: translateY(8px) scale(0.96); opacity: 0; }
  to   { transform: translateY(0)   scale(1);    opacity: 1; }
}
@keyframes kr-modal-out {
  from { transform: translateY(0)   scale(1);    opacity: 1; }
  to   { transform: translateY(4px) scale(0.98); opacity: 0; }
}

/* ─────────────────────────────────────────────────────────────────
   Keyboard hint chip — used in confirm dialog footer and inline help.
   ───────────────────────────────────────────────────────────────── */
.kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  font: var(--label-12);
  font-family: var(--font-sans);
  font-weight: 600;
  color: var(--gray-900);
  background: var(--background-100);
  border-radius: 4px;
  box-shadow:
    inset 0 0 0 1px var(--gray-alpha-400),
    0 1px 0 var(--gray-alpha-300);
}

/* ─────────────────────────────────────────────────────────────────
   Skeleton — gradient sweep placeholder. Use in place of
   `subtile animate-pulse` for higher-fidelity loading states. Author
   the shape via Tailwind sizing (h-*, w-*, rounded-*) on the same
   node and the sweep adapts automatically.
   ───────────────────────────────────────────────────────────────── */
.skeleton {
  position: relative;
  overflow: hidden;
  background: var(--gray-alpha-200);
  border-radius: var(--radius-sm);
}
.skeleton::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    90deg,
    transparent 0%,
    rgba(255, 255, 255, 0.55) 50%,
    transparent 100%
  );
  transform: translateX(-100%);
  animation: kr-skeleton-sweep 1.4s linear infinite;
}
.skeleton-line   { height: 12px; border-radius: var(--radius-xs); }
.skeleton-line-lg{ height: 16px; border-radius: var(--radius-xs); }
.skeleton-block  { height: 120px; border-radius: var(--radius-md); }
@keyframes kr-skeleton-sweep { to { transform: translateX(100%); } }

/* ─────────────────────────────────────────────────────────────────
   Card interactive — pointer-friendly card variant. Pairs with .card
   or stands alone (overview insight tiles, learner cards). Lifts on
   hover and reads as pressable without going so far as a button look.
   ───────────────────────────────────────────────────────────────── */
.card-interactive {
  position: relative;
  background: var(--background-100);
  border-radius: var(--radius-md);
  box-shadow:
    inset 0 0 0 1px var(--gray-alpha-300),
    var(--shadow-soft);
  transition:
    transform var(--duration-state) var(--ease-out),
    box-shadow var(--duration-state) var(--ease-out),
    border-color var(--duration-state) var(--ease-out);
  cursor: pointer;
  text-align: left;
}
.card-interactive:hover {
  transform: translateY(-1px);
  box-shadow:
    inset 0 0 0 1px var(--gray-alpha-500),
    var(--shadow-raised);
}
.card-interactive:active {
  transform: translateY(0);
}

/* ─────────────────────────────────────────────────────────────────
   Sortable table — sticky header + clickable headers with a chevron
   that flips on direction.
   ───────────────────────────────────────────────────────────────── */
thead.is-sticky th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: var(--background-200);
  box-shadow: inset 0 -1px 0 var(--gray-alpha-400);
}
.table-sort-th {
  cursor: pointer;
  user-select: none;
  transition: background var(--duration-state) var(--ease-out),
              color var(--duration-state) var(--ease-out);
}
.table-sort-th:hover { background: var(--gray-100); color: var(--gray-1000); }
.table-sort-th[aria-sort="ascending"],
.table-sort-th[aria-sort="descending"] {
  color: var(--gray-1000);
}
.table-sort-th .sort-indicator {
  display: inline-flex;
  align-items: center;
  margin-left: 4px;
  width: 12px;
  height: 12px;
  vertical-align: -2px;
  opacity: 0.4;
  transition: opacity var(--duration-state) var(--ease-out),
              transform var(--duration-state) var(--ease-out);
}
.table-sort-th[aria-sort="ascending"] .sort-indicator,
.table-sort-th[aria-sort="descending"] .sort-indicator { opacity: 1; }
.table-sort-th .sort-indicator svg { width: 12px; height: 12px; }

/* Sortable rows — a tiny inset blue strip on hover gives a focal
   point matched to the admin's blue accent. */
.row-interactive {
  transition: background var(--duration-state) var(--ease-out),
              box-shadow var(--duration-state) var(--ease-out);
}
.row-interactive:hover {
  background: rgba(0, 113, 227, 0.04);
  box-shadow: inset 2px 0 0 var(--blue-700);
}

/* ─────────────────────────────────────────────────────────────────
   Reduced-motion guard for the keyframes introduced above. The
   tokens.css block already squashes durations; this guard is the
   belt-and-braces for animations that loop indefinitely.
   ───────────────────────────────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .toast,
  .toast.is-leaving,
  .confirm-scrim,
  .confirm-card,
  .skeleton::after,
  .btn-loading::after,
  .field-error {
    animation: none !important;
  }
}
