/*
 * Yoshuko Status Pills — one component, six tones, one ink.
 *
 * A pill says what state a thing is in: a course is Draft, an import is Reading,
 * a roster row will be Skipped. It is the smallest piece of text in the studio
 * and one of the most read, so it is the piece least able to survive a colour
 * somebody picked without measuring.
 *
 * IT REPLACED `badge bg-*`, AND THE REASON IS NOT TIDINESS. Bootstrap's `bg-*`
 * utilities set a background and leave the ink to whoever writes the class, so
 * every pill was two independent decisions that had to agree. They stopped
 * agreeing on the course list: `bg-info text-dark` is #212529 on #2563EB —
 * **2.98:1**, against a 4.5:1 AA floor — and it was the FALLBACK arm of a table
 * that listed three of six import states, so the three states a creator watches
 * for longest all took it.
 *
 * Here the background is the ONLY choice. `--status-pill-bg` is the one thing a
 * tone sets; the ink is `--color-status-fg` for every tone without exception, and
 * every tone is dark enough to carry it at >= 5.76:1. There is no way to write a
 * pill whose two halves disagree, because there is only one half.
 *
 * ── Using one ─────────────────────────────────────────────────────────────────
 *
 *     <span class="status-pill status-pill--info">Importing</span>
 *
 * Tone by MEANING, never by colour:
 *
 *     --neutral   nothing is happening and nothing is wrong: draft, expired
 *     --info      the system is working on it: uploading, reading, importing
 *     --success   it landed: published, done, added
 *     --warning   look at this before relying on it: archived, needs a check
 *     --danger    it did not happen: failed, refused, skipped
 *     --action    it is waiting on YOU: ready to choose, approval needed
 *
 * Tones are also kept perceptually APART (>= ΔE 19 in CIELAB, asserted). Muting a
 * palette pulls every tone towards the same warm grey, and two pills a creator has
 * to tell apart are worth nothing if they cannot. That is why "waiting on you" is
 * a violet rather than the brand brown, which sat ΔE 10.7 from "draft".
 *
 * The tone names are read straight out of THIS FILE by
 * test_status_pill_contrast.py, which then measures each one. Adding a tone means
 * adding a token and a rule below; nothing else has to be told, and a tone that
 * fails AA or saturation cannot be merged.
 *
 * ── Where it is loaded ────────────────────────────────────────────────────────
 *
 * From the two shells (`base.html`, `app/base.html`), beside tokens.css — NOT
 * per page. Pills appear on about fifteen screens, and a component stylesheet a
 * page forgets to include renders correctly-structured markup with no styling at
 * all: an unstyled inline span that still says "Importing" and looks like a bug
 * nobody can reproduce. `_list_filter.html` documents the same failure. A shell
 * link cannot be forgotten by a new screen.
 */

.status-pill {
  /* The tone's own background. Neutral is the default so a pill with a
     misspelt tone class is muted and readable rather than transparent —
     white-on-nothing is the one failure mode that must not be reachable. */
  --status-pill-bg: var(--color-status-neutral);

  /* Ink is a variable so `--label` can be light-on-dark's opposite, and NOT so
     each tone can pick freely. Every tone that does not set it gets the one
     white, which is what makes a background the only decision a status tone
     has. The test reads whichever ink a tone actually resolves to and measures
     THAT — so a tone overriding this cannot escape the AA floor, it just has to
     clear it with its own pair. */
  --status-pill-fg: var(--color-status-fg);

  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 0.2em 0.62em;
  border-radius: var(--radius-full);

  /* .badge's 0.75em of the parent is unpredictable inside a table cell that has
     already stepped its own type down; an absolute size keeps every pill in the
     studio identical. 500 rather than 600: white on a mid-dark fill is already
     high-contrast, and bolding it as well makes a status read as a warning. */
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
  line-height: 1.4;
  white-space: nowrap;

  background-color: var(--status-pill-bg);
  color: var(--status-pill-fg);
}

/* A pill is a label, never a control. If one is ever made clickable it must stop
   inheriting link decoration, or it reads as a link that happens to be coloured. */
a.status-pill,
a.status-pill:hover {
  text-decoration: none;
  /* `--status-pill-fg`, NEVER `--color-status-fg`. The literal was written here
     first and it is wrong for exactly one tone — `--label`, which is the light
     one — so a label pill used as a link would be white on #EDE7E4 at 1.2:1,
     while every status tone kept working. A hardcoded ink inside a component
     whose whole point is that the ink follows the tone. */
  color: var(--status-pill-fg);
}

/* EVERY TONE RULE BELOW HAS THE SAME SPECIFICITY (0,1,0), so when an element
   carries two of them the winner is decided by ORDER IN THIS FILE and by nothing
   else — not by the order the classes appear in `class=`, and not by which one a
   script added last.
       <span class="status-pill status-pill--label">   toned in the markup
       el.classList.add('status-pill--success');       toned at runtime → LOSES
   That shipped once here, on the lesson editor's save-confirmation chip: it was
   given `--label` in the template, the script added `--success`, `--label` is
   defined last, and the chip reported every save in the same neutral colour. A
   control that renders and cannot change is worse than one that is missing.
   RULE: an element toned at runtime ships with NO tone class. `.status-pill`
   alone paints the neutral default, and the script's tone then wins because every
   rule here comes after it. */
.status-pill--neutral { --status-pill-bg: var(--color-status-neutral); }
.status-pill--info    { --status-pill-bg: var(--color-status-info); }
.status-pill--success { --status-pill-bg: var(--color-status-success); }
.status-pill--warning { --status-pill-bg: var(--color-status-warning); }
.status-pill--danger  { --status-pill-bg: var(--color-status-danger); }
.status-pill--action  { --status-pill-bg: var(--color-status-action); }

/* The one tone that is NOT a state. A cohort name, a policy version, the word
   "Assessment" beside a lesson — these are labels, and rendering a label in a
   status colour tells a creator something is wrong with their cohort. It is the
   only tone that sets its own ink, because it is the only one that is light.

   It replaces `bg-secondary-subtle text-secondary-emphasis` and
   `bg-primary-subtle text-primary-emphasis`. The second of those was a live
   defect rather than a style preference: Bootstrap's `*-subtle` backgrounds are
   COMPILED from Sass, not read from `--bs-primary-rgb`, so `bg-primary-subtle`
   painted #CFE2FF — Bootstrap blue — inside a brown app, on the chip marking a
   creator's default cohort. Exactly what `.alert-primary` already cost this
   codebase once. */
.status-pill--label {
  --status-pill-bg: var(--color-surface-sunken);
  --status-pill-fg: var(--color-fg);
  box-shadow: inset 0 0 0 1px var(--color-border);
}

/* A spinner sitting INSIDE the pill, for a state that is actively running.
   `currentColor`, so it is the pill's own ink and cannot be given a colour of
   its own that fails on a tone nobody checked it against. */
.status-pill__spinner {
  width: 0.7em;
  height: 0.7em;
  border: 1.5px solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: status-pill-spin 0.85s linear infinite;
}

@keyframes status-pill-spin {
  to { transform: rotate(360deg); }
}

/* Motion is decoration here — the WORD is what reports the state, and it is
   readable with the spinner frozen. */
@media (prefers-reduced-motion: reduce) {
  .status-pill__spinner { animation: none; }
}
