@tailwind base;
@tailwind components;
@tailwind utilities;

@layer base {
  :root {
    --ink-950: #0f1115;
    --ink-900: #1a1d23;
    --ink-800: #2b2f36;
    --ink-700: #3f444d;
    --ink-500: #6b7280;
    --ink-400: #9ca3af;
    --ink-300: #d3d6db;
    --ink-200: #e4e6ea;
    --ink-100: #eef0f2;
    --ink-50: #f7f8f9;
    --surface: #ffffff;
    /* Gauge Teal — action roles only. */
    --brand-700: #0f6e68;
    --brand-800: #0b5751;
    --brand-tint: #e9f4f2;
    --brand-ondark: #56c4ba;
    --good: #15803d;
    --good-tint: #f0faf3;
    --warn: #b45309;
    --warn-tint: #fdf8ef;
    --bad: #b91c1c;
    --bad-tint: #fdf3f2;
    --radius-card: 16px;
    --radius-control: 10px;
  }

  html {
    @apply scroll-smooth bg-white antialiased;
  }

  body {
    @apply bg-white font-sans text-[15px] leading-[1.6] text-ink-700;
  }

  h1,
  h2,
  h3,
  h4 {
    @apply text-ink-900;
    text-wrap: balance;
  }

  h1 {
    @apply font-semibold tracking-[-0.025em];
  }

  h2 {
    @apply font-semibold tracking-[-0.02em];
  }

  h3,
  h4 {
    @apply font-semibold;
  }

  ::selection {
    @apply bg-ink-200 text-ink-950;
  }
}

@layer components {
  /* Containers are selected by the job the page does. */
  .container-marketing {
    @apply mx-auto w-full max-w-marketing px-4 sm:px-6 lg:px-8;
  }

  .container-app {
    @apply mx-auto w-full max-w-app px-4 sm:px-6 lg:px-8;
  }

  .container-reading {
    @apply mx-auto w-full max-w-reading px-4 sm:px-6 lg:px-8;
  }

  .container-flow {
    @apply mx-auto w-full max-w-flow px-4 sm:px-6 lg:px-8;
  }

  .page-header {
    @apply flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between;
  }

  .page-kicker,
  .micro-label {
    @apply text-[11px] font-semibold uppercase tracking-[0.08em] text-ink-400;
  }

  .page-title {
    @apply text-[28px] font-semibold leading-tight tracking-[-0.025em] text-ink-950;
  }

  .page-support {
    @apply mt-1 text-[13px] text-ink-500;
  }

  /* Cards are opt-in for distinct choices, independent forms and action summaries. */
  .canvas-app {
    @apply min-h-screen bg-white;
  }

  .canvas-marketing {
    @apply min-h-screen bg-white;
  }

  .surface-card {
    @apply rounded-card border border-ink-200 bg-white p-6 shadow-sm;
  }

  /* Color identifies the next useful action; neutral cards only group content. */
  .surface-action {
    @apply rounded-card bg-brand-tint p-6;
  }

  .surface-action .text-ink-500,
  .surface-action .fcs-meta {
    @apply text-ink-700;
  }

  .surface-card-interactive {
    @apply surface-card transition-colors duration-150 hover:border-ink-300;
  }

  .surface-inset {
    @apply rounded-control bg-ink-50 p-3.5;
  }

  .surface-overlay {
    @apply rounded-card border border-ink-200 bg-white shadow-lg;
  }

  /* A surface-overlay carried by a native <dialog>, so the focus trap, Esc and
     the top layer come from the platform rather than from a controller. The
     panel keeps no padding of its own — the form inside owns it, which also
     means any click landing on the dialog box itself came from the backdrop.
     Below md it becomes a bottom sheet, the reachable position on a phone. */
  .dialog-panel {
    @apply max-h-[85dvh] w-full max-w-[26rem] overflow-y-auto rounded-card border border-ink-200 bg-white p-0 text-ink-700 shadow-lg;
  }

  .dialog-panel::backdrop {
    background-color: rgb(15 17 21 / 45%);
  }

  .dialog-panel[open] {
    animation: dialog-in 150ms ease-out;
  }

  @media (max-width: 767px) {
    .dialog-panel {
      @apply mb-0 mt-auto max-w-none rounded-b-none;
    }
  }

  /* Inverted surfaces are opt-in. Ordinary pages use typography and spacing. */
  .surface-anchor {
    @apply bg-ink-950 text-white;
  }

  .surface-anchor .page-kicker,
  .surface-anchor .micro-label,
  .surface-anchor .section-kicker,
  .surface-card-inverted .page-kicker,
  .surface-card-inverted .micro-label,
  .surface-card-inverted .section-kicker {
    @apply text-brand-ondark;
  }

  .surface-anchor .metric-hero-bar,
  .surface-card-inverted .metric-hero-bar {
    @apply bg-brand-ondark;
  }

  .surface-anchor-card {
    @apply rounded-card border border-ink-800 bg-ink-900 p-5;
  }

  .surface-card-inverted {
    @apply rounded-card border border-ink-950 bg-ink-950 p-6 text-white;
  }

  /* Scale carries hierarchy where color no longer can. */
  .display-title {
    @apply font-semibold text-ink-950;
    font-size: clamp(2.5rem, 5.6vw, 4.25rem);
    letter-spacing: -0.045em;
    line-height: 1;
    text-wrap: balance;
  }

  .display-muted {
    @apply text-ink-500;
  }

  .metric-hero {
    @apply text-[30px] font-semibold leading-none tracking-[-0.04em] text-ink-950;
    font-variant-numeric: tabular-nums;
  }

  .metric-hero-label {
    @apply mt-1 text-[10px] font-semibold uppercase tracking-[0.08em] text-ink-400;
  }

  .metric-hero-bar {
    @apply mt-1.5 h-[3px] w-7 rounded-full bg-ink-200;
  }

  /* Editorial section headers: left-aligned kicker + title.
     Centered headers are reserved for the hero and the closing CTA. */
  .section-header {
    @apply mb-10 max-w-2xl;
  }

  .section-kicker {
    @apply mb-3 block text-[11px] font-semibold uppercase tracking-[0.09em] text-ink-400;
  }

  .section-title {
    @apply text-[28px] font-semibold leading-[1.12] tracking-[-0.03em] text-ink-950 md:text-[34px];
  }

  .section-lead {
    @apply mt-3 text-base text-ink-500;
  }

  /* Hairline rows replace stacked insets inside a card. */
  .divider-list {
    @apply divide-y divide-ink-100;
  }

  .divider-row {
    @apply flex items-start gap-4 py-4 first:pt-0 last:pb-0;
  }

  .divider-index {
    @apply w-8 shrink-0 text-xl font-semibold leading-tight tracking-[-0.03em] text-ink-300;
    font-variant-numeric: tabular-nums;
  }

  /* One card divided by hairlines instead of N repeated cards. */
  .split-card {
    @apply grid overflow-hidden rounded-card border border-ink-200 bg-white divide-y divide-ink-100 md:divide-x md:divide-y-0;
  }

  .split-cell {
    @apply p-6;
  }

  .media-frame {
    @apply overflow-hidden rounded-card border border-ink-200 bg-white;
  }

  .surface-anchor .media-frame {
    @apply border-ink-800;
  }

  .surface-anchor h1,
  .surface-anchor h2,
  .surface-anchor h3,
  .surface-anchor h4,
  .surface-anchor .section-title,
  .surface-card-inverted h1,
  .surface-card-inverted h2,
  .surface-card-inverted h3,
  .surface-card-inverted h4 {
    @apply text-white;
  }

  .surface-anchor .section-lead,
  .surface-card-inverted .section-lead {
    @apply text-ink-400;
  }

  /* Product controls. */
  .btn,
  .btn-primary,
  .btn-secondary,
  .btn-ghost,
  .btn-inverted,
  .btn-inverted-secondary,
  .btn-danger,
  .btn-disabled {
    @apply inline-flex min-h-10 items-center justify-center gap-2 rounded-control border border-transparent px-[18px] py-2.5 text-sm font-semibold no-underline transition-colors duration-150 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-700/25 focus-visible:ring-offset-2;
  }

  .btn-primary {
    @apply bg-brand-700 text-white hover:bg-brand-800;
  }

  .btn-secondary {
    @apply border-ink-200 bg-white text-ink-900 hover:border-ink-400 hover:bg-ink-50;
  }

  .btn-ghost {
    @apply bg-transparent text-ink-700 hover:bg-ink-100 hover:text-ink-950;
  }

  /* Primary/secondary equivalents for use on an anchor surface. */
  .btn-inverted {
    @apply bg-white text-ink-950 hover:bg-ink-100 focus-visible:ring-white/40;
  }

  /* Inline text links are an action role, so they carry the brand color. */
  .link {
    @apply font-medium text-brand-700 no-underline transition-colors duration-150 hover:underline;
  }

  .btn-inverted-secondary {
    @apply border-ink-700 bg-transparent text-white hover:border-ink-500 hover:bg-ink-900 focus-visible:ring-white/40;
  }

  .btn-danger {
    @apply bg-bad text-white hover:bg-bad/90;
  }

  .btn-disabled,
  .btn:disabled,
  .btn-primary:disabled,
  .btn-secondary:disabled {
    @apply pointer-events-none cursor-not-allowed border-transparent bg-ink-100 text-ink-400 shadow-none;
  }

  .btn-lg {
    @apply min-h-[48px] rounded-xl px-[26px] py-3.5 text-[15px];
  }

  .field {
    @apply grid gap-1.5;
  }

  .field-label {
    @apply text-[13px] font-medium text-ink-900;
  }

  .field-help {
    @apply text-xs text-ink-500;
  }

  .field-error {
    @apply text-xs text-bad;
  }

  .input,
  .select,
  .textarea {
    @apply w-full rounded-control border-ink-300 bg-white px-3 py-2.5 text-[15px] text-ink-950 shadow-none placeholder:text-ink-400 focus:border-brand-700 focus:ring-[3px] focus:ring-brand-700/20;
    min-height: 44px;
  }

  .textarea {
    min-height: 88px;
  }

  .dropzone {
    @apply rounded-control border border-dashed border-ink-300 bg-ink-50 p-6 text-center transition-colors duration-150 hover:border-brand-700;
  }

  .status-pill {
    @apply inline-flex items-center gap-2 rounded-full bg-ink-100 px-3 py-1 text-xs font-medium text-ink-700;
  }

  .status-pill::before {
    @apply block h-1.5 w-1.5 shrink-0 rounded-full bg-ink-400;
    content: "";
  }

  .status-good::before {
    @apply bg-good;
  }

  .status-warn::before {
    @apply bg-warn;
  }

  .status-bad::before {
    @apply bg-bad;
  }

  .status-outline {
    @apply border border-ink-200 bg-white;
  }

  .finding-gauge {
    @apply mt-3 flex gap-1;
  }

  .finding-gauge > span {
    @apply h-1.5 min-w-0 flex-1 rounded-sm bg-ink-200;
  }

  .finding-gauge .finding-gauge-severe {
    @apply bg-bad;
  }

  .finding-gauge .finding-gauge-warn {
    @apply bg-warn;
  }

  .finding-gauge .finding-gauge-clear {
    @apply bg-ink-300;
  }

  .finding-gauge .finding-gauge-unknown {
    background: repeating-linear-gradient(135deg, var(--ink-200) 0 3px, var(--ink-50) 3px 6px);
  }

  .finding-row-tile {
    @apply flex h-[26px] w-[26px] flex-none items-center justify-center rounded-[8px] bg-ink-100;
  }

  .findings-count-list > .divider-row:first-child {
    @apply pt-4;
  }

  .technique-score {
    @apply flex h-16 w-16 shrink-0 flex-col items-center justify-center rounded-[14px] border border-ink-200 bg-white;
    font-variant-numeric: tabular-nums;
  }

  .technique-score-value {
    @apply text-[22px] font-semibold leading-none tracking-[-0.02em] text-ink-950;
  }

  .technique-score-label {
    @apply mt-1 text-[9px] font-semibold uppercase tracking-[0.08em] text-ink-400;
  }

  .technique-score-bar {
    @apply mt-1.5 h-[3px] w-[26px] rounded-full bg-ink-200;
  }

  .score-good .technique-score-bar {
    @apply bg-good;
  }

  .score-warn .technique-score-bar {
    @apply bg-warn;
  }

  .score-bad .technique-score-bar {
    @apply bg-bad;
  }

  /* Reading surfaces use the same tokens without flattening rich content. */
  .exercise-content {
    @apply font-sans leading-relaxed text-ink-700;
  }

  .exercise-content h1 {
    @apply mb-6 mt-8 text-3xl font-semibold leading-tight tracking-[-0.025em] text-ink-950 first:mt-0;
  }

  .exercise-content h2 {
    @apply mb-4 mt-8 text-2xl font-semibold tracking-[-0.02em] text-ink-900 first:mt-0;
  }

  .exercise-content h3 {
    @apply mb-3 mt-6 text-xl font-semibold text-ink-900 first:mt-0;
  }

  .exercise-content h4 {
    @apply mb-2 mt-4 text-lg font-medium text-ink-900 first:mt-0;
  }

  .exercise-content p,
  .exercise-content li {
    @apply text-base leading-7 text-ink-700;
  }

  .exercise-content p {
    @apply mb-4;
  }

  .exercise-content ul,
  .exercise-content ol {
    @apply mb-6 space-y-2;
  }

  .exercise-content ul li {
    @apply relative pl-6;
  }

  .exercise-content ul li::before {
    @apply absolute left-0 top-3 h-1.5 w-1.5 rounded-full bg-ink-950;
    content: '';
  }

  .exercise-content ol {
    @apply list-none;
    counter-reset: list-counter;
  }

  .exercise-content ol li {
    @apply relative pl-6;
    counter-increment: list-counter;
  }

  .exercise-content ol li::before {
    @apply absolute left-0 top-0 font-medium text-ink-900;
    content: counter(list-counter) ".";
  }

  .exercise-content ul ul,
  .exercise-content ol ol,
  .exercise-content ul ol,
  .exercise-content ol ul {
    @apply mb-2 mt-2;
  }

  .exercise-content ul ul li {
    @apply pl-4;
  }

  .exercise-content ol ol {
    @apply list-none;
    counter-reset: nested-counter;
  }

  .exercise-content ol ol li {
    @apply relative pl-8;
    counter-increment: nested-counter;
  }

  .exercise-content ol ol li::before {
    @apply absolute left-0 top-0 font-medium text-ink-900;
    content: counter(nested-counter) ".";
  }

  .exercise-content table {
    @apply my-6 w-full border-collapse border border-ink-300;
  }

  .exercise-content th {
    @apply border border-ink-300 bg-ink-50 px-4 py-2 text-left font-semibold text-ink-900;
  }

  .exercise-content td {
    @apply border border-ink-300 px-4 py-2 text-ink-700;
  }

  .exercise-content tr:nth-child(even) {
    @apply bg-ink-50;
  }

  .exercise-content dl {
    @apply my-6;
  }

  .exercise-content dt {
    @apply mb-1 font-semibold text-ink-900;
  }

  .exercise-content dd {
    @apply mb-4 ml-4 text-ink-700;
  }

  .exercise-content .highlight-box {
    @apply my-6 rounded-r-control border-l-4 border-warn bg-warn-tint p-4;
  }

  .exercise-content .highlight-box p {
    @apply mb-0 text-warn;
  }

  .exercise-content .warning-box {
    @apply my-6 rounded-r-control border-l-4 border-bad bg-bad-tint p-4;
  }

  .exercise-content .warning-box p {
    @apply mb-0 text-bad;
  }

  .exercise-content .success-box {
    @apply my-6 rounded-r-control border-l-4 border-good bg-good-tint p-4;
  }

  .exercise-content .success-box p {
    @apply mb-0 text-good;
  }

  .exercise-content blockquote {
    @apply my-6 rounded-r-control border-l-4 border-ink-300 bg-ink-50 py-4 pl-6;
  }

  .exercise-content blockquote p {
    @apply mb-0 italic text-ink-700;
  }

  .exercise-content strong {
    @apply font-semibold text-ink-900;
  }

  .exercise-content em {
    @apply italic text-ink-700;
  }

  .exercise-content code {
    @apply rounded bg-ink-100 px-2 py-1 font-mono text-sm text-ink-800;
  }

  .exercise-content pre {
    @apply my-6 overflow-x-auto rounded-control bg-ink-950 p-4 text-ink-100;
  }

  .exercise-content pre code {
    @apply bg-transparent p-0 text-ink-100;
  }

  .exercise-content a {
    @apply text-brand-700 underline decoration-brand-700/40 underline-offset-2 transition-colors duration-150 hover:decoration-brand-700;
  }

  .exercise-content img {
    @apply my-6 h-auto max-w-full rounded-control;
  }

  .exercise-content hr {
    @apply my-8 border-ink-200;
  }

  .exercise-content .trix-content {
    @apply text-ink-700;
  }

  @media (max-width: 768px) {
    .exercise-content h1 {
      @apply mb-4 text-2xl;
    }

    .exercise-content h2 {
      @apply mb-3 text-xl;
    }

    .exercise-content h3 {
      @apply mb-2 text-lg;
    }

    .exercise-content p,
    .exercise-content li {
      @apply text-sm leading-6;
    }
  }
}

@layer utilities {
  .metric-numeral {
    font-variant-numeric: tabular-nums;
  }

  .animate-pulse {
    animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
  }
}

/* These element rules intentionally follow Tailwind utilities so legacy views
   inherit the guide while they move onto the named components above. */
input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="submit"]):not([type="button"]),
select,
textarea {
  min-height: 44px;
  border-color: var(--ink-300);
  border-radius: var(--radius-control);
  background-color: var(--surface);
  color: var(--ink-950);
  box-shadow: none !important;
  font-family: inherit;
  font-size: 15px;
}

input:not([type="hidden"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):not([type="submit"]):not([type="button"]):focus,
select:focus,
textarea:focus {
  border-color: var(--brand-700) !important;
  outline: none;
  box-shadow: 0 0 0 3px rgb(15 110 104 / 20%) !important;
  --tw-ring-color: transparent !important;
}

input:disabled,
select:disabled,
textarea:disabled {
  cursor: not-allowed;
  border-color: var(--ink-200);
  background-color: var(--ink-50);
  color: var(--ink-400);
}

input[type="checkbox"],
input[type="radio"] {
  border-color: var(--ink-300);
  color: var(--brand-700) !important;
  accent-color: var(--brand-700);
}

input[type="checkbox"]:focus,
input[type="radio"]:focus {
  --tw-ring-color: rgb(15 110 104 / 25%) !important;
}

a:focus-visible,
button:focus-visible,
[role="button"]:focus-visible,
summary:focus-visible {
  outline: none;
  box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px rgb(15 110 104 / 30%);
}

a:hover,
button:hover,
[role="button"]:hover {
  transform: none !important;
}

a[class*="px-"][class*="py-"]:not(.rounded-full),
button[class*="px-"][class*="py-"]:not(.rounded-full),
input[type="submit"] {
  border-radius: var(--radius-control) !important;
}

.font-bold,
.font-extrabold,
.font-black {
  font-weight: 600 !important;
}

/* Legacy badges inherit the one-pill pattern even before their templates are
   converted to status-pill. Existing child dots are reused when present. */
.rounded-full[class*="px-"].bg-good-tint,
.rounded-full[class*="px-"].bg-warn-tint,
.rounded-full[class*="px-"].bg-bad-tint,
.rounded-full[class*="px-"].bg-ink-50,
.rounded-full[class*="px-"].bg-ink-100 {
  display: inline-flex;
  align-items: center;
  gap: .5rem;
  background-color: var(--ink-100) !important;
  color: var(--ink-700) !important;
  font-size: .75rem;
  font-weight: 500 !important;
}

.rounded-full[class*="px-"].bg-good-tint:not(:has(.bg-good))::before,
.rounded-full[class*="px-"].bg-warn-tint:not(:has(.bg-warn))::before,
.rounded-full[class*="px-"].bg-bad-tint:not(:has(.bg-bad))::before,
.rounded-full[class*="px-"].bg-ink-50:not(:has([class*="bg-ink-"]))::before,
.rounded-full[class*="px-"].bg-ink-100:not(:has([class*="bg-ink-"]))::before {
  width: .375rem;
  height: .375rem;
  flex: none;
  border-radius: 9999px;
  background: var(--ink-400);
  content: "";
}

.rounded-full[class*="px-"].bg-good-tint:not(:has(.bg-good))::before { background: var(--good); }
.rounded-full[class*="px-"].bg-warn-tint:not(:has(.bg-warn))::before { background: var(--warn); }
.rounded-full[class*="px-"].bg-bad-tint:not(:has(.bg-bad))::before { background: var(--bad); }

/* Older cards often used a Tailwind ring as a border. Render those as the
   single L1 hairline instead of introducing another depth treatment. */
[class*="ring-1"][class*="ring-ink-200"][class*="bg-white"] {
  border: 1px solid var(--ink-200);
  --tw-ring-offset-shadow: 0 0 #0000 !important;
  --tw-ring-shadow: 0 0 #0000 !important;
}

.prose {
  --tw-prose-body: var(--ink-700);
  --tw-prose-headings: var(--ink-950);
  --tw-prose-lead: var(--ink-500);
  --tw-prose-links: var(--brand-700);
  --tw-prose-bold: var(--ink-900);
  --tw-prose-counters: var(--ink-500);
  --tw-prose-bullets: var(--ink-400);
  --tw-prose-hr: var(--ink-200);
  --tw-prose-quotes: var(--ink-900);
  --tw-prose-quote-borders: var(--ink-300);
  --tw-prose-captions: var(--ink-500);
  --tw-prose-code: var(--ink-900);
  --tw-prose-pre-code: var(--ink-100);
  --tw-prose-pre-bg: var(--ink-950);
  --tw-prose-th-borders: var(--ink-300);
  --tw-prose-td-borders: var(--ink-200);
}

@keyframes pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: .5; }
}

@keyframes dialog-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
    transition-duration: .01ms !important;
  }
}

/* Text-first pages: white canvas, 44rem measure, and fine rules between
   records. Cards group distinct choices, independent forms and current actions. */
@layer components {
  .fcs-page {
    @apply mx-auto w-full max-w-[44rem] px-5 pb-20 pt-12 sm:px-6 sm:pt-16;
  }

  /* One slot above the h1, kicker or breadcrumb, at one size, so the h1
     starts at the same y on every page in the flow. */
  .fcs-title {
    @apply text-[13px] font-medium text-ink-500;
  }

  .fcs-breadcrumb {
    @apply flex flex-wrap items-center gap-x-1.5 text-[13px] font-medium text-ink-500;
  }

  .fcs-breadcrumb a {
    @apply text-ink-500 no-underline underline-offset-2 hover:text-ink-900 hover:underline;
  }

  .fcs-breadcrumb-sep {
    @apply text-ink-300;
  }

  .fcs-breadcrumb-current {
    @apply text-ink-900;
  }

  /* Opts out of the base heading's balanced wrapping: balancing a sentence
     shortens every line, which reads as a manual break someone chose. */
  .fcs-heading {
    @apply mt-2 text-[26px] font-semibold leading-[1.15] tracking-[-0.025em] text-ink-950 sm:text-[30px];
    text-wrap: wrap;
  }

  .fcs-suggestion {
    @apply mt-2 text-[28px] font-semibold leading-[1.15] tracking-[-0.025em] text-ink-950 sm:text-[34px];
    text-wrap: wrap;
  }

  .fcs-reason {
    @apply mt-5 text-[17px] leading-[1.65] text-ink-700;
  }

  /* A real section heading, not a label: the space above it separates one
     section from the last, so nothing under it needs more than mt-3/mt-4. */
  .fcs-subheading {
    @apply mt-12 text-[19px] font-semibold tracking-[-0.01em] text-ink-950;
    text-wrap: wrap;
  }

  /* A step or claim title inside a section, not a new section: it opens 24px
     down, 16px when it is the first thing under the section heading. */
  .fcs-h3 {
    @apply mt-6 text-[16px] font-semibold text-ink-950;
    text-wrap: wrap;
  }

  h2.fcs-subheading + .fcs-h3 {
    @apply mt-4;
  }

  .fcs-h3 + .fcs-body {
    @apply mt-1.5;
  }

  .fcs-body {
    @apply mt-3 text-base leading-[1.7] text-ink-700;
  }

  .fcs-muted {
    @apply text-ink-500;
  }

  .fcs-note {
    @apply mt-4 text-sm text-ink-500;
  }

  .fcs-status {
    @apply mt-3 text-sm font-medium text-ink-700;
  }

  .fcs-evidence-primary {
    @apply mt-6;
  }

  .fcs-evidence-secondary {
    @apply mt-4;
  }

  .fcs-evidence-media {
    @apply max-h-[70vh] w-full rounded-control bg-ink-950 object-contain;
  }

  .fcs-evidence-caption {
    @apply mt-3 flex flex-col gap-1 text-sm leading-6 text-ink-500;
  }

  .fcs-evidence-label {
    @apply font-medium text-ink-900;
  }

  .fcs-evidence-grid {
    @apply mt-2 grid gap-6 sm:grid-cols-2;
  }

  .fcs-details {
    @apply mt-10 border-t border-ink-200 pt-4 text-base text-ink-700;
  }

  .fcs-details > summary {
    @apply min-h-[44px] cursor-pointer py-2 font-medium text-ink-900;
  }

  .fcs-errors {
    @apply mt-4 rounded-control border border-bad/30 bg-bad/5 px-4 py-3 text-sm text-ink-900;
  }

  .fcs-form {
    @apply mt-6 grid gap-6;
  }

  .fcs-field {
    @apply grid gap-2;
  }

  [data-controller~="creation-flow"] [hidden] {
    display: none !important;
  }

  .creation-flow-progress {
    @apply mb-7 flex items-center gap-3 text-[13px] font-medium text-ink-500;
  }

  .creation-flow-progress progress {
    @apply h-1.5 flex-1 appearance-none overflow-hidden rounded-full bg-ink-100;
  }

  .creation-flow-progress progress::-webkit-progress-bar {
    @apply bg-ink-100;
  }

  .creation-flow-progress progress::-webkit-progress-value {
    @apply bg-brand-700;
  }

  .creation-flow-progress progress::-moz-progress-bar {
    @apply bg-brand-700;
  }

  .fcs-inline {
    @apply flex items-center gap-3;
  }

  .fcs-input-num {
    @apply max-w-[10rem];
  }

  .fcs-check {
    @apply flex items-center gap-2 text-sm text-ink-700;
  }

  .fcs-optional {
    @apply ml-1 font-normal text-ink-400;
  }

  .fcs-drop {
    @apply cursor-pointer py-10 focus:outline-none focus-visible:ring-2 focus-visible:ring-brand-700/25;
  }

  .fcs-file {
    @apply block w-full text-sm text-ink-700;
  }

  .fcs-drop-title {
    @apply text-base font-semibold text-ink-900;
  }

  .fcs-drop-hint {
    @apply mt-1 text-sm text-ink-500;
  }

  .fcs-preview {
    @apply mt-4 grid gap-2;
  }

  .fcs-preview-video {
    @apply max-h-72 w-full rounded-control bg-ink-950 object-contain;
  }

  .fcs-preview-row {
    @apply flex items-center justify-between gap-3 text-sm text-ink-500;
  }

  .fcs-actions {
    @apply mt-2 flex flex-wrap items-center gap-3;
  }

  /* A leading ghost button hangs its 18px padding into the margin so its
     label sits on the column edge like the text above it. */
  .fcs-actions > .btn-ghost:first-child,
  .fcs-actions > form:first-child > .btn-ghost {
    @apply -ml-[18px];
  }

  .fcs-secondary {
    @apply mt-4 flex flex-wrap gap-2;
  }

  .fcs-options {
    @apply grid gap-2;
  }

  /* A radio option is a full-width row; the input stays focusable but the row
     draws the state, so the choice reads as one element. */
  .fcs-option {
    @apply relative flex min-h-[48px] cursor-pointer items-center rounded-control border border-ink-200 bg-white px-4 py-3 text-[15px] text-ink-900 transition-colors hover:border-ink-400;
  }

  .fcs-option input {
    @apply absolute h-4 w-4 opacity-0;
  }

  .fcs-option:has(input:checked) {
    @apply border-brand-700 bg-brand-700/5 ring-1 ring-brand-700;
  }

  .fcs-option:has(input:focus-visible) {
    @apply ring-2 ring-brand-700/40;
  }

  /* An option with a description stacks title over it without a modifier
     class, so templates only name the two parts. */
  .fcs-option:has(.fcs-option-desc) {
    @apply flex-col items-start gap-0.5;
  }

  .fcs-option-title {
    @apply font-medium;
  }

  .fcs-option-desc {
    @apply text-sm text-ink-500;
  }

  /* The bounded "Something else" field revealed under the options. */
  .fcs-free {
    @apply mt-1 grid gap-2;
  }

  .fcs-inline-link {
    @apply border-0 bg-transparent p-0 text-sm font-medium text-brand-700 underline-offset-2 hover:underline;
  }

  /* Tappable rows spaced by their own padding. The hover wash is the
     affordance; the negative margin keeps labels on the column edge. */
  .fcs-rows {
    @apply mt-8;
  }

  .fcs-subheading + .fcs-rows {
    @apply mt-2;
  }

  .fcs-row {
    @apply -mx-3 flex min-h-[52px] items-center justify-between gap-4 rounded-control px-3 py-3 text-base font-medium text-ink-900 no-underline transition-colors hover:bg-ink-50;
  }

  .fcs-row-button {
    @apply w-[calc(100%+1.5rem)] border-0 bg-transparent text-left;
  }

  .fcs-row-meta {
    @apply flex items-center gap-2 text-sm font-normal text-ink-500;
  }

  .fcs-chevron {
    @apply text-lg leading-none text-ink-400;
  }

  /* The plan offer on a result: a tinted panel between the summary and the
     rows. Two tiles choose the plan; one button follows the choice. */
  .fcs-offer {
    @apply mt-6 rounded-card bg-brand-tint p-[18px];
  }

  /* The meta line stays right-aligned, and drops under the heading on a
     phone too narrow for both. */
  .fcs-offer-head {
    @apply flex flex-wrap items-baseline justify-between gap-x-3 gap-y-0.5;
  }

  .fcs-offer-title {
    @apply whitespace-nowrap text-[17px] leading-snug tracking-[-0.01em] text-ink-950;
  }

  .fcs-offer-meta {
    @apply ml-auto whitespace-nowrap text-[13px] leading-5 text-ink-700;
  }

  .fcs-offer-choices {
    @apply mt-3.5 grid grid-cols-2 gap-2.5;
  }

  .fcs-offer-tile {
    @apply relative grid w-full cursor-pointer gap-0.5 rounded-xl border-[1.5px] border-transparent bg-white p-3 text-left focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-brand-700;
  }

  .fcs-offer-tile[aria-pressed="true"] {
    @apply border-brand-700;
    box-shadow: 0 0 0 3px rgb(15 110 104 / 0.12);
  }

  .fcs-offer-name {
    @apply text-[13px] font-semibold leading-5 text-ink-700;
  }

  .fcs-offer-price {
    @apply text-xl font-bold leading-7 tracking-[-0.02em] tabular-nums text-ink-950;
  }

  .fcs-offer-unit {
    @apply text-xs font-medium tracking-normal text-ink-500;
  }

  .fcs-offer-per {
    @apply text-xs font-semibold leading-4 tabular-nums text-brand-700;
  }

  .fcs-offer-badge {
    @apply absolute -top-[9px] right-2.5 rounded-full bg-brand-700 px-2 py-[3px] text-[11px] font-bold leading-none tracking-[0.02em] text-white;
  }

  /* A set that is still being checked or analyzed, on the Your sets page. */
  .fcs-set-progress {
    @apply mt-4 flex items-start gap-3;
  }

  .fcs-set-progress .fcs-spinner {
    @apply mt-1 h-4 w-4;
  }

  .fcs-set-progress-title {
    @apply text-base font-medium text-ink-900;
  }

  /* Work in progress on a row: a small spinner beside a short label. */
  .fcs-row-status {
    @apply inline-flex items-center gap-2;
  }

  .fcs-spinner {
    @apply inline-block h-3.5 w-3.5 shrink-0 rounded-full border-2 border-ink-200 border-t-brand-700;
    animation: fcs-viewer-spin 0.9s linear infinite;
  }

  @media (prefers-reduced-motion: reduce) {
    .fcs-spinner { animation: none; }
  }

  .fcs-dot {
    @apply inline-block h-2.5 w-2.5 shrink-0 rounded-full bg-ink-300;
  }

  /* One modifier per verdict, never teal (the action colour). The two greys
     differ so a neutral observation does not read as a gap in the review. */
  .fcs-dot-issue {
    @apply bg-warn;
  }

  .fcs-dot-pass {
    @apply bg-good;
  }

  .fcs-dot-observe {
    @apply bg-ink-400;
  }

  .fcs-dot-unclear {
    @apply bg-ink-300;
  }

  .fcs-checks .fcs-row {
    @apply flex-col items-stretch gap-2 sm:flex-row sm:items-center sm:gap-4;
  }

  .fcs-check-copy {
    @apply flex min-w-0 flex-1 flex-col gap-0.5;
  }

  .fcs-check-title {
    @apply font-medium text-ink-900;
  }

  .fcs-check-summary {
    @apply text-sm font-normal leading-5 text-ink-500;
  }

  .fcs-status-label {
    @apply text-sm font-medium text-ink-700;
  }

  .fcs-checks .fcs-row-meta {
    @apply min-w-0 flex-none justify-start text-left sm:justify-end sm:text-right;
  }

  .fcs-checks .fcs-chevron {
    @apply ml-auto sm:ml-0;
  }

  .fcs-evidence-claim {
    @apply font-medium text-ink-900;
  }

  /* The one line with numbers under a drill name: weight and tabular figures,
     and the paragraph after it sits a little closer than usual. */
  .fcs-dose {
    @apply mt-2 text-[15px] font-semibold tabular-nums text-ink-900;
  }

  .fcs-dose + .fcs-body {
    @apply mt-3.5;
  }

  /* Label/value pairs read as a pair by their own gap; no rules between.
     Values break anywhere so a long token cannot widen a 360px page. */
  .fcs-facts {
    @apply mt-4 grid gap-y-4 text-base;
  }

  .fcs-heading + .fcs-facts {
    @apply mt-6;
  }

  .fcs-facts > div {
    @apply grid gap-1 sm:grid-cols-[10rem_minmax(0,1fr)] sm:gap-x-6;
  }

  /* leading-6 matches the dd line box so the pair shares a baseline. */
  .fcs-facts dt {
    @apply text-sm leading-6 text-ink-500;
  }

  .fcs-facts dd {
    @apply min-w-0 text-base text-ink-900;
    overflow-wrap: anywhere;
  }

  .fcs-sources {
    @apply mt-3 grid gap-3;
    overflow-wrap: anywhere;
  }

  .fcs-source-title {
    @apply block text-sm font-medium text-ink-900;
  }

  .fcs-source-summary {
    @apply block text-sm leading-6 text-ink-500;
  }

  /* The original video plays in place under its line, same recipe as the
     upload preview. */
  .fcs-clip-player {
    @apply mt-2 max-h-72 w-full rounded-control bg-ink-950 object-contain;
  }
}

@layer components {
  .fcs-section {
    @apply text-[19px] font-semibold leading-snug tracking-[-0.01em] text-ink-950;
    text-wrap: wrap;
  }
  .fcs-item-title { @apply text-[15px] font-medium leading-[1.45] text-ink-950; }
  .fcs-meta { @apply text-[13px] leading-5 text-ink-500 tabular-nums; }
  .fcs-list { @apply space-y-1; }
  .fcs-item {
    @apply -mx-3 flex items-start gap-4 rounded-control px-3 py-5 no-underline transition-colors hover:bg-ink-50 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-700/25;
  }
  .fcs-value { @apply shrink-0 pt-px text-right text-[17px] font-semibold leading-6 tabular-nums text-ink-950; }
  .fcs-settings-section { @apply grid gap-4 py-7 sm:grid-cols-[10rem_1fr] sm:gap-10; }
  .fcs-article {
    @apply prose max-w-none text-[17px] leading-[1.7] text-ink-700
      prose-headings:font-semibold prose-headings:tracking-[-0.015em] prose-headings:text-ink-950
      prose-h1:text-[30px] prose-h1:leading-tight prose-h2:mt-12 prose-h2:text-[22px] prose-h3:mt-8 prose-h3:text-[18px]
      prose-p:my-4 prose-li:my-1 prose-a:font-medium prose-a:text-brand-700 prose-a:no-underline hover:prose-a:underline prose-img:rounded-control;
  }
  .fcs-article-report { @apply text-base; }
}

@layer components {
  /* Admin tables and editors have a wider workspace than consumer reading. */
  .admin-page {
    @apply mx-auto w-full min-w-0 max-w-[80rem] px-5 py-10 sm:px-6 lg:px-8;
  }
  .admin-page-form { @apply max-w-[56rem]; }
  .admin-page-catalog { @apply max-w-[72rem]; }
  .admin-heading { @apply text-[28px] font-semibold leading-tight tracking-[-0.025em] text-ink-950; }
  .admin-section { @apply text-lg font-semibold leading-snug tracking-[-0.01em] text-ink-950; }
  .admin-panel { @apply rounded-card border border-ink-200 bg-white; }
  .admin-table { @apply w-full text-sm; }
  .admin-table thead { @apply bg-ink-50/60; }
  .admin-table th { @apply px-4 py-3 font-medium normal-case tracking-normal; }
  .admin-table th:not(.text-right):not(.text-center) { @apply text-left; }
  .admin-table td { @apply px-4 py-3 align-top; }
  .admin-table tbody > tr + tr { @apply border-t border-ink-100; }
  .admin-table tbody tr { @apply transition-colors hover:bg-ink-50/60; }
  .admin-page pre { @apply max-w-full; }
  .admin-page input:not([type="checkbox"]):not([type="radio"]):not([type="submit"]):not([type="file"]):not([type="hidden"]),
  .admin-page select,
  .admin-page textarea {
    @apply rounded-control border-ink-300 text-sm shadow-none focus:border-brand-700 focus:ring-brand-700/20;
  }
  .admin-page input[type="checkbox"], .admin-page input[type="radio"] {
    @apply text-brand-700 focus:ring-brand-700/25;
  }
}

/* Claude Fable 5.1: iteration 3 evidence viewer */
/* Evidence viewer and check layout. Replaces the iteration 2 section at the
   end of app/assets/stylesheets/application.tailwind.css; it relies only on
   the existing tokens in :root and overrides nothing outside .fcs-check-*,
   .fcs-viewer-* and the truncation rules for .fcs-checks rows, apart from
   bounding legacy evidence media. */
@layer components {
  /* Legacy stills and clips: bounded like the viewer instead of 70vh. */
  .fcs-evidence-media {
    max-height: min(45vh, 440px);
    max-height: min(45svh, 440px);
  }

  /* A check reads explanation, evidence, then any legacy supplemental evidence. */
  .fcs-check-media { margin-top: 1.5rem; min-width: 0; }
  .fcs-check-more { margin-top: 1rem; }

  /* Why-page check rows: the subtitle keeps to one line and the dot and
     chevron keep their place beside the title however narrow the screen. */
  .fcs-checks .fcs-row { flex-direction: row; align-items: center; }
  .fcs-checks .fcs-check-copy { flex: 1 1 auto; min-width: 0; }
  .fcs-checks .fcs-check-title { display: block; }
  .fcs-checks .fcs-check-summary {
    display: block;
    overflow: hidden;
    white-space: nowrap;
    text-overflow: ellipsis;
  }
  .fcs-checks .fcs-row-meta {
    display: inline-flex;
    flex: none;
    align-items: center;
    gap: 0.5rem;
    min-height: 1.5rem;
  }

  .fcs-viewer [hidden] { display: none !important; }

  /* Portrait evidence beside the explanation from 64rem: the page widens,
     the explanation keeps a readable measure on the left, and the viewer
     takes the height it needs on the right. */
  @media (min-width: 64rem) {
    .fcs-page.fcs-check-split { max-width: 62rem; }

    .fcs-check-split .fcs-check-layout {
      display: grid;
      grid-template-columns: minmax(0, 1fr) auto;
      column-gap: 3rem;
      align-items: start;
    }

    .fcs-check-split .fcs-check-media { margin-top: 0.5rem; }
  }

  /* The picture's height is whatever the screen has left once the rest of
     the viewer is counted, so the whole inspector (rep buttons, picture,
     caption, scrubber and buttons) is in view together: from the top of the
     page when it sits beside the explanation, and under the sticky header
     once scrolled to otherwise. --fcs-viewer-chrome is the viewer's height
     without the picture: 9rem, plus 3.5rem for the rep buttons and 1.75rem
     for a legend. Zooming in stays within the same height. */
  .fcs-viewer {
    --fcs-viewer-chrome: calc(9rem + var(--fcs-viewer-reps-h, 0rem) + var(--fcs-viewer-legend-h, 0rem));
    --fcs-viewer-stage-h: clamp(14rem, calc(100vh - 4.5rem - var(--fcs-viewer-chrome)), 30rem);
    --fcs-viewer-stage-h: clamp(14rem, calc(100svh - 4.5rem - var(--fcs-viewer-chrome)), 30rem);
    --fcs-viewer-detail-h: var(--fcs-viewer-stage-h);
    --fcs-viewer-w: min(100%, max(22rem, calc(var(--fcs-viewer-stage-h) * var(--fcs-viewer-full-ar))));
    display: grid;
    justify-items: center;
    gap: 0.75rem;
    min-width: min(100%, 18rem);
  }

  .fcs-viewer-multi { --fcs-viewer-reps-h: 3.5rem; }
  .fcs-viewer-with-legend { --fcs-viewer-legend-h: 1.75rem; }

  @media (min-width: 48rem) {
    .fcs-viewer {
      --fcs-viewer-stage-h: clamp(14rem, calc(100vh - 4.5rem - var(--fcs-viewer-chrome)), 34rem);
      --fcs-viewer-stage-h: clamp(14rem, calc(100svh - 4.5rem - var(--fcs-viewer-chrome)), 34rem);
    }
  }

  /* Beside the explanation the viewer starts about 9.5rem down the page. */
  @media (min-width: 64rem) {
    .fcs-check-split .fcs-viewer {
      --fcs-viewer-stage-h: clamp(16rem, calc(100vh - 10.5rem - var(--fcs-viewer-chrome)), 36rem);
      --fcs-viewer-stage-h: clamp(16rem, calc(100svh - 10.5rem - var(--fcs-viewer-chrome)), 36rem);
      width: min(30rem, max(22rem, calc(var(--fcs-viewer-stage-h) * var(--fcs-viewer-full-ar))));
    }
  }

  /* The rep buttons, the picture and its controls share one width, so they
     read as one object rather than a small picture over wide controls. */
  .fcs-viewer-reps,
  .fcs-viewer-region {
    width: var(--fcs-viewer-w);
  }

  .fcs-viewer-reps {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.375rem;
  }

  /* Two sets: a set switch beside one row of the chosen set's reps. The row
     scrolls sideways instead of wrapping, so the picture never moves down. */
  .fcs-viewer-reps-sets {
    flex-wrap: nowrap;
    gap: 0.5rem;
  }

  .fcs-viewer-setswitch {
    display: inline-flex;
    flex-shrink: 0;
    gap: 0.125rem;
    padding: 0.125rem;
    border: 1px solid var(--ink-200);
    border-radius: var(--radius-control);
  }

  .fcs-viewer-set-button {
    min-height: 2.5rem;
    padding: 0 0.625rem;
    border: 0;
    border-radius: calc(var(--radius-control) - 2px);
    background: transparent;
    color: var(--ink-500);
    font: inherit;
    font-size: 0.875rem;
    font-weight: 500;
    white-space: nowrap;
    cursor: pointer;
    touch-action: manipulation;
  }

  .fcs-viewer-set-button[aria-pressed="true"] {
    background: var(--ink-100);
    color: var(--ink-900);
  }

  .fcs-viewer-rep-scroll {
    display: flex;
    flex: 1;
    min-width: 0;
    gap: 0.375rem;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scrollbar-width: none;
  }

  .fcs-viewer-rep-scroll::-webkit-scrollbar { display: none; }

  .fcs-viewer-rep-scroll .fcs-viewer-rep { flex-shrink: 0; }

  .fcs-viewer-reps-label {
    margin-right: 0.25rem;
    font-size: 0.875rem;
    color: var(--ink-500);
  }

  .fcs-viewer-rep {
    min-width: 2.75rem;
    min-height: 2.75rem;
    border: 1px solid var(--ink-200);
    border-radius: var(--radius-control);
    background: var(--surface);
    color: var(--ink-900);
    font: inherit;
    font-weight: 500;
    font-variant-numeric: tabular-nums;
    cursor: pointer;
    touch-action: manipulation;
  }

  .fcs-viewer-rep[aria-pressed="true"] {
    border-color: var(--brand-700);
    background: var(--brand-700);
    color: #fff;
  }

  .fcs-viewer-region {
    display: grid;
    gap: 0.5rem;
    border-radius: var(--radius-control);
  }

  .fcs-viewer-region:focus { outline: none; }

  .fcs-viewer-region:focus-visible {
    outline: 2px solid var(--brand-700);
    outline-offset: 4px;
  }

  /* The stage takes the shape of the full frame or the fixed detail crop,
     never wider than the full frame nor taller than the height budget. */
  .fcs-viewer-stage {
    position: relative;
    justify-self: center;
    width: min(100%, calc(var(--fcs-viewer-stage-h) * var(--fcs-viewer-full-ar)), calc(var(--fcs-viewer-detail-h) * var(--fcs-viewer-ar)));
    aspect-ratio: var(--fcs-viewer-ar);
    overflow: hidden;
    border-radius: var(--radius-control);
    background: var(--ink-950);
  }

  .fcs-viewer-stage-playable { cursor: pointer; }

  .fcs-viewer-canvas {
    position: absolute;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
  }

  /* The canvas always has the source aspect ratio, so fill is exact. The
     video is never display: none. The frame held during a Marks swap sits
     over it, and the key frame's still sits over both until the clip shows
     a frame. */
  .fcs-viewer-media {
    position: absolute;
    inset: 0;
    display: block;
    width: 100%;
    height: 100%;
    object-fit: fill;
    transition: opacity 120ms;
  }

  .fcs-viewer-freeze { z-index: 1; }

  .fcs-viewer-poster { z-index: 2; }

  /* The last rep's still, held while the next rep's key frame loads. */
  .fcs-viewer-media-stale { opacity: 0.45; }

  /* The play invitation on a "play" viewer's picture. Positioned with the
     translate property: the global button:hover rule resets transform. */
  .fcs-viewer-stage-play {
    position: absolute;
    left: 50%;
    top: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.5rem;
    height: 3.5rem;
    padding: 0;
    border: 0;
    border-radius: 9999px;
    background: rgb(15 17 21 / 62%);
    color: #fff;
    z-index: 3;
    translate: -50% -50%;
    cursor: pointer;
    transition: background-color 120ms;
  }

  .fcs-viewer-stage-play:hover { background: rgb(15 17 21 / 78%); }

  .fcs-viewer-stage-play svg {
    width: 1.5rem;
    height: 1.5rem;
    margin-left: 0.1875rem;
    fill: currentColor;
  }

  .fcs-viewer-stage[aria-busy="true"]::after {
    content: "";
    position: absolute;
    z-index: 3;
    top: 0.625rem;
    right: 0.625rem;
    width: 1rem;
    height: 1rem;
    border: 2px solid rgb(255 255 255 / 35%);
    border-top-color: #fff;
    border-radius: 9999px;
    animation: fcs-viewer-spin 0.8s linear infinite;
  }

  /* Caption sits under the picture, never on it: the moment on the left, the
     position on the right, wrapping onto two lines when the column is tight. */
  .fcs-viewer-caption {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.125rem 1rem;
    font-size: 0.875rem;
    line-height: 1.45;
    color: var(--ink-500);
    font-variant-numeric: tabular-nums;
  }

  .fcs-viewer-caption-rep {
    min-width: 0;
    font-weight: 500;
    color: var(--ink-900);
  }

  .fcs-viewer-status {
    display: inline-flex;
    align-items: baseline;
    gap: 0.5rem;
  }

  .fcs-viewer-position { white-space: nowrap; }

  .fcs-viewer-retry {
    padding: 0;
    border: 0;
    background: none;
    color: var(--brand-700);
    font: inherit;
    font-weight: 500;
    text-decoration: underline;
    text-underline-offset: 2px;
    cursor: pointer;
  }

  .fcs-viewer-controls {
    display: grid;
    gap: 0.5rem;
  }

  /* Frame steps and play on the left, speed and zoom on the right. Hidden
     controls leave no gap. */
  .fcs-viewer-transport {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 0.625rem;
  }

  .fcs-viewer-buttons {
    display: flex;
    flex: none;
    gap: 0.375rem;
  }

  .fcs-viewer-icon-button {
    display: inline-flex;
    flex: none;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    border: 1px solid var(--ink-200);
    border-radius: var(--radius-control);
    background: var(--surface);
    color: var(--ink-900);
    cursor: pointer;
    touch-action: manipulation;
    transition: border-color 120ms, background-color 120ms;
  }

  .fcs-viewer-icon-button svg {
    width: 1.25rem;
    height: 1.25rem;
    fill: currentColor;
  }

  .fcs-viewer-detail svg { fill: none; }

  /* Holding a step button repeats; keep the long press from selecting text
     or opening a callout. */
  .fcs-viewer-step {
    user-select: none;
    -webkit-user-select: none;
    -webkit-touch-callout: none;
  }

  .fcs-viewer-icon-button:hover:not(:disabled),
  .fcs-viewer-rep:hover:not([aria-pressed="true"]) {
    border-color: var(--ink-400);
  }

  .fcs-viewer-icon-button:focus-visible,
  .fcs-viewer-key:focus-visible,
  .fcs-viewer-rep:focus-visible,
  .fcs-viewer-retry:focus-visible,
  .fcs-viewer-stage-play:focus-visible,
  .fcs-viewer-scrub input:focus-visible {
    outline: 2px solid var(--brand-700);
    outline-offset: 2px;
  }

  /* Play is the one filled action; the two glyphs swap with its state. */
  .fcs-viewer-play {
    border-color: var(--brand-700);
    background: var(--brand-700);
    color: #fff;
  }

  .fcs-viewer-play:hover:not(:disabled) {
    border-color: var(--brand-800);
    background: var(--brand-800);
  }

  .fcs-viewer-play .fcs-viewer-icon-play { margin-left: 0.125rem; }
  .fcs-viewer-play[data-state="playing"] .fcs-viewer-icon-play,
  .fcs-viewer-play[data-state="paused"] .fcs-viewer-icon-pause { display: none; }

  .fcs-viewer-detail[aria-pressed="true"] {
    border-color: var(--brand-700);
    background: var(--brand-tint);
    color: var(--brand-800);
  }

  .fcs-viewer-detail[aria-pressed="true"] .fcs-viewer-icon-zoom-in { display: none; }

  .fcs-viewer-speed {
    font-size: 0.8125rem;
    font-weight: 600;
    font-variant-numeric: tabular-nums;
  }

  /* Marks is a text toggle; pressed means the marks are showing. */
  .fcs-viewer-marks {
    width: auto;
    padding: 0 0.75rem;
    font-size: 0.8125rem;
    font-weight: 600;
  }

  .fcs-viewer-marks[aria-pressed="true"] {
    border-color: var(--brand-700);
    background: var(--brand-tint);
    color: var(--brand-800);
  }

  /* Names what the marks show, in their own colours, under the picture. */
  .fcs-viewer-legend {
    display: flex;
    flex-wrap: wrap;
    gap: 0.25rem 1rem;
    margin: 0;
    padding: 0;
    list-style: none;
    font-size: 0.8125rem;
    color: var(--ink-700);
  }

  .fcs-viewer-legend li {
    display: inline-flex;
    align-items: center;
    gap: 0.375rem;
  }

  .fcs-viewer-swatch {
    display: inline-block;
    flex: none;
    background: var(--fcs-viewer-swatch);
    box-shadow: 0 0 0 1.5px rgb(20 20 20 / 85%);
  }

  .fcs-viewer-swatch-line {
    width: 1.125rem;
    height: 0.25rem;
    border-radius: 9999px;
  }

  .fcs-viewer-swatch-dot {
    width: 0.625rem;
    height: 0.625rem;
    border-radius: 9999px;
  }

  /* The scrubber: a native range across the full width over the rep's real
     frames, so even a long rep leaves each frame a usable share of the
     track. The key frame is marked under the track; the marker jumps there. */
  .fcs-viewer-scrub {
    position: relative;
    display: flex;
    align-items: center;
    min-height: 2.25rem;
    padding-bottom: 0.75rem;
  }

  .fcs-viewer-scrub input {
    width: 100%;
    margin: 0;
    accent-color: var(--brand-700);
    touch-action: manipulation;
  }

  /* The marker's box never moves; only the triangle inside it lifts and
     grows on hover, so the pointer never slips off it. Both are placed with
     the translate property, which the global button:hover transform reset
     leaves alone. */
  .fcs-viewer-key {
    position: absolute;
    bottom: 0;
    width: 1.5rem;
    height: 1.25rem;
    padding: 0;
    border: 0;
    border-radius: 4px;
    background: none;
    translate: -50% 0;
    cursor: pointer;
    touch-action: manipulation;
  }

  .fcs-viewer-key::before {
    content: "";
    position: absolute;
    left: 50%;
    bottom: 0.25rem;
    width: 0;
    height: 0;
    border-left: 0.3125rem solid transparent;
    border-right: 0.3125rem solid transparent;
    border-bottom: 0.375rem solid var(--ink-900);
    translate: -50% 0;
    transform-origin: 50% 100%;
    transition: translate 120ms ease-out, scale 120ms ease-out, border-bottom-color 120ms;
  }

  .fcs-viewer-key:hover::before,
  .fcs-viewer-key:focus-visible::before {
    translate: -50% -2px;
    scale: 1.35;
    border-bottom-color: var(--brand-700);
  }
}

@keyframes fcs-viewer-spin {
  to { transform: rotate(360deg); }
}
