/*
 * The Cartel primitives. Every value here comes from tokens.css; a raw colour
 * or font stack below is a defect. /styleguide renders each one in every state.
 */

/* ── Base ──────────────────────────────────────────────────────────────── */

*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  text-size-adjust: 100%;

  /*
   * No zoom gesture at all on a phone (COMBAT-61). `pan-x pan-y` is
   * `manipulation` minus pinch: panning in both axes survives, so every page
   * still scrolls, and both gestures that change the scale are gone. Installed
   * to a home screen there is no browser chrome to aim at, so a page a thumb
   * pinched or double-tapped by accident sits at 200% with no obvious way back.
   * It also takes the ~300ms the browser spends waiting to see whether a tap was
   * the first of two.
   *
   * On the root rather than on the controls, because a reader double-taps text —
   * touch-action resolves up the ancestor chain, so this covers the document
   * without touching any element individually.
   *
   * Paired with `user-scalable=no` in the layouts rather than replacing it: the
   * meta is what an installed iOS shell obeys, this is what covers the tab where
   * iOS ignores the meta. This costs WCAG 1.4.4, which .impeccable.md records as
   * a knowing exception rather than leaving it to contradict principle 4.
   */
  touch-action: pan-x pan-y;

  /*
   * And no scroll chaining: it is what makes the fixed marquee and ringside feel
   * like they slide, and what turns a thumb reading to the top of a log into a
   * pull-to-refresh.
   */
  overscroll-behavior: none;
}

body {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
  margin: 0;
  background-color: var(--ink);
  color: var(--texto);
  font: 400 var(--text-body) / var(--leading-body) var(--font-body);
  text-wrap: pretty;
}

body::before {
  content: "";
  position: fixed;
  z-index: -1;
  inset: 0;
  background-image: var(--poster);
  background-repeat: no-repeat;
  pointer-events: none;
}

/* Keeps the footer at the bottom of a short page instead of mid-viewport. */
main {
  flex: 1;
}

/* Everything in this system stacks with a flex gap, never with a margin. */
h1,
h2,
h3,
h4,
p,
figure,
blockquote,
dl,
dd {
  margin: 0;
}

img {
  max-width: 100%;
}

/* Every component here sets its own display, which would outrank [hidden]. */
[hidden] {
  display: none !important;
}

:focus-visible {
  outline: var(--border) solid var(--amarillo);
  outline-offset: 2px;
}

::selection {
  background: var(--amarillo);
  color: var(--ink);
}

/* The replay animation must have a still path. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 1ms !important;
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}

/* ── Layout ────────────────────────────────────────────────────────────── */

.shell {
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
  max-width: var(--shell);
  margin-inline: auto;
  padding: var(--space-4) var(--space-3) var(--space-7);
}

.masthead {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
  padding-block: var(--space-2);
}

.masthead > * {
  min-width: 0;
}

.section {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2);
}


/* ── Type roles ────────────────────────────────────────────────────────── */

.wordmark {
  font-family: var(--font-display);
  font-size: var(--text-body);
  font-weight: 400;
  letter-spacing: var(--track-wordmark);
  text-transform: uppercase;
  color: var(--amarillo);
}

.wordmark__tld {
  color: var(--rosa);
}

.display {
  font-family: var(--font-display);
  font-size: var(--text-display);
  font-weight: 400;
  line-height: var(--leading-tight);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  text-wrap: balance;
  overflow-wrap: anywhere;
}

.display--hero {
  font-size: var(--text-hero);
}

.title {
  font-size: var(--text-title);
  font-weight: 800;
  line-height: var(--leading-tight);
  text-wrap: balance;
}

.eyebrow {
  font-variant-numeric: tabular-nums;
  font-family: var(--font-data);
  font-size: var(--text-micro);
  font-weight: 700;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--dim);
}

/*
 * Every HP value, token count, level, price and table column — and the
 * addresses and slugs that read as data too. Bungee and Archivo both set
 * proportional figures by default, so numbers that have to line up wear this
 * role rather than the display or body face. Monospace breaks at neither a dot
 * nor an at-sign, so the role wraps anywhere or an email pushes a 320px card
 * off the screen.
 */
.data {
  font-family: var(--font-data);
  font-size: var(--text-data);
  font-variant-numeric: tabular-nums;
  overflow-wrap: anywhere;
}

/*
 * The address a code was sent to, or the one you are about to sign in as. It is
 * the whole content of those two screens rather than a field on them, so the
 * prototype prints it centred in the marquee colour — a receipt, not a label.
 * `.data` already wraps it anywhere, which is what keeps a long address inside a
 * 320px panel.
 */
.address {
  margin: 0;
  color: var(--amarillo);
  text-align: center;
}

.muted {
  color: var(--dim);
}

/* What a postura, a piece of equipment or a level gap moves. The sign carries
   the meaning; colour only doubles it. */
.delta {
  font-family: var(--font-data);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.delta--up {
  color: var(--menta);
}

.delta--down {
  color: var(--rosa);
}

/* ── Locker ────────────────────────────────────────────────────────────── */

/*
 * The vestidor's board: a door per room, two abreast. It replaced a column of
 * eight identical ghost buttons, which told a player only that other pages
 * exist — a door with its own ink, its own mark and a count on it says what is
 * behind it and whether it is worth opening tonight.
 *
 * Eight doors, eight inks, and the inks are named for themselves. The first
 * draft borrowed the five atributo hues through `.stat--<key>`, which meant a
 * blue door had to answer for what velocidad is and two doors had to share —
 * there are five atributos and eight rooms. These carry no meaning at all, the
 * way `.card--lila` and its siblings do not: a sequence is the one thing colour
 * marks in this app without also saying something about stakes.
 */
.locker {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
}

/* A door is a slab like everything else on this page: no radius, hard offset,
   and a tap drives it into its own shadow instead of nudging it. */
.locker__door {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  padding: var(--space-3);
  background: var(--well);
  border: var(--border) solid var(--door, var(--line));
  color: var(--texto);
  text-decoration: none;
  box-shadow: var(--shadow-cast);
  transition: transform var(--motion-fast) var(--ease),
    box-shadow var(--motion-fast) var(--ease);
}

.locker__door:active {
  transform: translate(var(--space-1), var(--space-1));
  box-shadow: var(--shadow-cast-pressed);
}

.locker__door--rosa { --door: var(--rosa); }
.locker__door--azul { --door: var(--azul); }
.locker__door--menta { --door: var(--menta); }
.locker__door--lila { --door: var(--lila); }
.locker__door--amarillo { --door: var(--amarillo); }
.locker__door--naranja { --door: var(--naranja); }
.locker__door--turquesa { --door: var(--turquesa); }
.locker__door--lima { --door: var(--lima); }

/* The mark carries the ink, so the label does not have to. Two things in one
   hue at one size is two things shouting. */
.locker__mark {
  display: inline-flex;
  color: var(--door, var(--dim));
}

.locker__label {
  font: var(--text-micro) / var(--leading-tight) var(--font-display);
  letter-spacing: var(--track-display);
  text-transform: uppercase;

  /* A jauría's name and a player's own words end up here. */
  overflow-wrap: anywhere;
}

.locker__note {
  color: var(--dim);
  font-size: var(--text-micro);
}

/* ── Card ──────────────────────────────────────────────────────────────── */

/*
 * A slab, not a card (ADR 0015). The radius is gone, the border is ink rather
 * than line, and the shadow is a hard offset with no blur: a blur reads as
 * elevation and a printed poster has none — a panel is a second sheet of paper
 * laid on the first. The halftone is a background layer rather than an
 * ::before, because this is a flex column and a pseudo-element would take a
 * slot and a gap of its own.
 */
.card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-3);
  background-color: var(--card);
  background-image: var(--halftone);
  background-size: var(--halftone-grid);
  border: var(--border-poster) solid var(--ink);
  box-shadow: var(--shadow-poster);
}

/* The marquee border bills what it holds the way a printed cartel does: the
   landing's fixture fight, the arena's Reto del Día, the billing above a
   scouting sheet, and a jauría's week once it is won. */
.card--marquee {
  border-color: var(--amarillo);
}

/* Consequence, on the panels that carry it: the same reading .flash--alert and
   .chip--alert already establish. */
.card--alert {
  border-color: var(--rosa);
}

/*
 * A panel printed in one of the palette's other inks, edge and banner in the
 * same colour. Creation is what these are for and so far the only caller: four
 * numbered steps, each one its own ink, so a player scrolling a long form can
 * see which of the four they are standing in without reading the number.
 *
 * They are named for the colour rather than for a meaning, because unlike
 * .card--marquee and .card--alert they carry none — a sequence is the one thing
 * in the app that colour marks without also saying something about stakes.
 */
.card--lila {
  border-color: var(--lila);
}

.card--azul {
  border-color: var(--azul);
}

.card--menta {
  border-color: var(--menta);
}

/* Amarillo, for the setup panels standing above the pet: they are the one thing
   on that page a player is meant to finish and be rid of, and a warm edge is
   what makes a dismissable notice findable without giving it a banner as loud
   as the panels it sits over. `.card--marquee` also draws amarillo, and means
   something else — what it holds is *billed*. */
.card--amarillo {
  border-color: var(--amarillo);
}

/* Rosa joins them 2026-08-03, for `/pet`'s corner. `.card--alert` still owns
   rosa-as-consequence and this owns rosa-as-section; they are told apart by the
   banner rather than the edge, since a striped band is a heading and a bare
   rosa border is a warning. */
.card--rosa {
  border-color: var(--rosa);
}

/* Where a panel stands in an order the player walks: creation's four steps. The
   separator is punctuation between two labels, so it is printed here rather
   than read out as a word. */
.card__step {
  font-family: var(--font-data);
}

.card__step::after {
  content: "·";
  margin-inline: var(--space-1);
}

/* What the banner says about itself rather than about the panel: a count, a
   price, a rareza. It never competes with the billing beside it.

   It is stamped on its own ground rather than set straight onto the band: a
   banner is two passes of one ink at a diagonal, and data read across stripes
   is the one thing on a poster that has to stay legible at a glance. The plate
   also fixes the figure's colour, so a `.boost` inside it keeps meaning menta
   instead of landing on whichever ink the panel happens to wear. */
.card__note {
  margin-inline-start: auto;
  padding: var(--space-1) var(--space-2);
  background: var(--ink);
  color: var(--texto);
  font-family: var(--font-data);
  font-weight: 700;
  letter-spacing: var(--track-micro);
  white-space: nowrap;
}

/* What you can do to a panel, worn on the panel's own banner: the way out of a
   dismissable notice, the way into editing what the panel is about. It goes
   here rather than in the body because the body is the subject — a control
   beside a pet's name steals the width the name needed, and long names are
   where that shows first. */

/* Filled, because a banner is a printed band and the thing you can press on it
   should look stuck to it rather than printed with it. What says "button" in
   this system is a solid fill and a hard offset it presses into, which is how
   every `.btn` says it — this says it the same way at a quarter of the size.

   The fill is `--texto` rather than an ink of its own: the banner already wears
   a colour, and a control that changes colour with every panel it lands on is
   a different control on every page. White reads on all of them. */
.card__act {
  position: relative;
  display: inline-flex;
  padding: var(--space-1);
  background: var(--texto);
  border: var(--border) solid var(--ink);
  color: var(--ink);
  cursor: pointer;
  box-shadow: var(--shadow-cast);
  transition: transform var(--motion-fast) var(--ease),
    box-shadow var(--motion-fast) var(--ease);
}

.card__act:active {
  transform: translate(var(--space-1), var(--space-1));
  box-shadow: var(--shadow-cast-pressed);
}

/* The way out of a notice is not an invitation, so it keeps the fill off and
   sits at label weight: a dismissable panel should not put its loudest mark on
   the way to refusing it. */

/* The tap target the glyph does not have on its own, laid over the banner
   rather than inside it: a 44px control would set the height of every banner
   that carries one. */
.card__act::after {
  content: "";
  position: absolute;
  inset: 50% auto auto 50%;
  min-width: var(--tap);
  min-height: var(--tap);
  translate: -50% -50%;
}

/* The one that ends the panel takes the far edge, with nothing between it and
   the corner. An act that follows a `.card__note` is already pushed there by
   the note's own auto margin, and a second one would split the free space. */
.card__close {
  margin-inline-start: auto;

  /* The way out of a notice is not an invitation: a dismissable panel should not
     put its loudest mark on the way to refusing it. So it keeps the geometry and
     drops the gold, sitting at label weight beside the title. */
  background: none;
  border-color: currentcolor;
  color: inherit;
  box-shadow: none;
}

/*
 * The banner: the slab's own header, printed across it in two passes of the same
 * ink. It bleeds to the panel's edge, so it cancels the padding the card sets
 * and takes its own.
 *
 * Two spellings of one object. Eleven surfaces write `.card__banner` out; the
 * rest already opened with an `.eyebrow` as the card's first child, and under
 * Cabina that label *is* the banner — so it is banded here rather than rewritten
 * thirty times. The markup was already saying the right thing.
 *
 * Every panel in the app wears one, operational surfaces included: the order
 * book and the moderation queue are billed as loudly as the Reto del Día. That
 * reverses the quiet-operational-copy rule Cartel shipped with, and
 * .impeccable.md records the reversal rather than leaving the two in conflict.
 *
 * Loud is the default, stripes included. The banded eyebrow started out quiet
 * with `.card--marquee` opting into the stripes, which had the ratio backwards:
 * of the 94 banners in the approved prototype 53 are striped amarillo and only
 * 24 are quiet. Defaulting to quiet made most of the app whisper and left "loud
 * everywhere" true only of the surfaces that spell the banner out by hand.
 *
 * A card whose first child is something else keeps its eyebrows inline, which is
 * right — the moderation card opens on a row of chips, and the labels further
 * down are sub-headings inside a panel, not the panel's own billing.
 *
 * A panel pinned to an atributo — the shop's slots — wears that atributo's ink
 * by carrying `.stat--<abbr>`, so the band answers "which of the five is this"
 * before the heading is read. Everything else bills in amarillo.
 */
.card__banner,
.card > .eyebrow:first-child {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: calc(var(--space-4) * -1) calc(var(--space-3) * -1) 0;
  padding: var(--space-2) var(--space-3);
  background-image: var(--stat-band, var(--stripe-amarillo));
  color: var(--stat-on, var(--ink));
  font: var(--text-micro) / var(--leading-tight) var(--font-display);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  overflow-wrap: anywhere;
}

/*
 * The three tones a band comes in, on either spelling of it. A banded eyebrow
 * takes the same suffix the banner does — they are the same object, and a
 * surface should not have to know which way it was written to say "this one is
 * a count, not a billing".
 */
.card__banner--rosa,
.card > .eyebrow--rosa:first-child {
  background-image: var(--stripe-rosa);
  color: var(--texto);
}

.card__banner--menta,
.card > .eyebrow--menta:first-child {
  background-image: var(--stripe-menta);
  color: var(--ink);
}

/*
 * The two inks creation's step sequence spends. Both clear 8:1 against --ink,
 * so the banded label keeps the same reading the amarillo one has: this is a
 * printed band with the panel's name on it, not a coloured warning.
 */
.card__banner--azul,
.card > .eyebrow--azul:first-child {
  background-image: var(--stripe-azul);
  color: var(--ink);
}

.card__banner--lila,
.card > .eyebrow--lila:first-child {
  background-image: var(--stripe-lila);
  color: var(--ink);
}

/*
 * The one band that does not shout. A panel whose header is a label rather than
 * a billing — a count, a period, a table's name — takes this instead of a
 * colour, so the striped ones keep meaning something.
 */
.card__banner--quiet,
.card > .eyebrow--quiet:first-child {
  background-image: none;
  background-color: var(--well);
  color: var(--dim);
  font-family: var(--font-data);
  font-weight: 700;
  letter-spacing: var(--track-micro);
}

/*
 * The sticker treatment, kept from the rejected Álbum direction for the pet
 * card alone: it makes a photo feel like an object you own, which is the
 * identity mechanic immutable stats depend on (ADR 0010).
 */
.pet-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-2);
  background: var(--card);
  border: var(--sticker-border) solid var(--texto);
  border-radius: var(--radius-xs);
  box-shadow: var(--shadow-sticker);
}

.pet-card__photo {
  display: flex;
  align-items: center;
  justify-content: center;
  aspect-ratio: 1;
  overflow: hidden;
  background: var(--well);
  border-radius: var(--radius-xs);
  font-size: 3rem;
  line-height: 1;
}

.pet-card__photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Every podio a pet has finished on (COMBAT-31). A pet can collect one a
   season, so they wrap rather than push whatever holds them wide. */
.pet-card__badges {
  justify-content: center;
  max-width: 100%;
}

/*
 * The name and what it is billed at, on one line. A level is a footnote to a
 * name rather than a line of its own under it, and giving it one made a plate
 * whose photo, name and level each read as a separate announcement. Baselines
 * rather than centres: the mono of a level and the display face of a name have
 * different cap heights, and centring two of those reads as a misprint.
 */
.pet-card__billing {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
}

/* A name wraps; what it is billed at never does. */
.pet-card__billing > .data {
  flex: none;
}

.pet-card__name {
  min-width: 0;
  font-family: var(--font-display);
  font-size: var(--text-data);
  line-height: var(--leading-tight);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  overflow-wrap: anywhere;
}

/* ── Piece ─────────────────────────────────────────────────────────────── */

/*
 * One thing on a counter. A ruled row rather than a panel, so a counter of
 * three reads as a counter of three and not as three products stacked in a
 * window: a card in a card doubles every border, and that was what made the
 * shop unreadable (COMBAT-61). The rule is where the one above it ended.
 */
.piece {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding-block-start: var(--space-3);
  border-block-start: var(--border) solid var(--ink);
}

/* Unless the band is where the one above it ended: El Bar opens straight onto
   its stock, and a hairline 12px under a striped banner is the same line drawn
   twice. */
.card__banner + .piece {
  padding-block-start: 0;
  border-block-start: 0;
}

/*
 * A worn piece reads across instead of down: the slot, the piece in it and what
 * it adds on one side, and the one thing you can do about it on the other. Five
 * stacked rows put the kit page's headers a screen apart, and the answer the
 * page is opened for — what is in each of the five slots — was the one thing it
 * made you scroll for.
 */
.piece--worn {
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
}

.piece__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}

/* `button_to` is a form, so the form is the flex item and the call rides inside
   it. It keeps its own width against a long piece name: the name is what wraps,
   because it is the part that can afford to. */
.piece--worn > form {
  flex: none;
}

/* Name left, price right: a counter is scanned down its right edge, which is
   the whole reason the price is billed here as well as in the chips below. */
.piece__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
}

.piece__name {
  font-family: var(--font-display);
  font-size: var(--text-body);
  line-height: var(--leading-tight);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  overflow-wrap: anywhere;
}

.piece__cost {
  display: inline-flex;
  flex: none;
  align-items: center;
  gap: var(--space-1);
  font-family: var(--font-display);
  font-size: var(--text-body);
  font-variant-numeric: tabular-nums;
  color: var(--amarillo);
}

/* ── Billing ───────────────────────────────────────────────────────────── */

/* Two fighters abreast with the mark between them: the landing's intro, the
   arena's, and the replay. Promoted out of the landing when the replay became
   its second caller. The corners stretch to a shared height rather than
   centring, so whatever a 20-character name does to one of them, the two cards
   stay billed on the same line. */
.vs {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-2);
}

.vs > * {
  min-width: 0;
}

/*
 * A fighter as a cartel bills one: the face at mug size with the name and the
 * level set *beneath* it, not a sticker with the name inside its border. The
 * sticker is what the corner and the álbum use, where the photo is the subject;
 * here two of them abreast ate the panel and left the VS between two walls.
 *
 * This reverses the line in ADR 0015 that kept the name inside the border on
 * the grounds that the prototype's own billing draws them together at that
 * size. It does not — it draws a small mug with a caption under it — and the
 * ADR's own rule is that where the two disagree, the prototype wins.
 */
.mug {
  display: flex;
  align-items: center;
  justify-content: center;
  width: var(--mug);
  aspect-ratio: 1;
  overflow: hidden;
  background: var(--well);
  border: var(--border-mug) solid var(--texto);
  box-shadow: var(--shadow-sticker);
  font-size: 2rem;
  line-height: 1;
}

.mug img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* The corner's own, which takes whatever its column gives it rather than the
   billing size — this is the one place on the page the photo is the subject. */
.mug--lg {
  width: 100%;
  font-size: 3rem;
}

.billing {
  display: grid;
  justify-items: center;
  gap: var(--space-2);
  min-width: 0;
  text-align: center;
}

.billing__name {
  font-family: var(--font-display);
  font-size: var(--text-micro);
  line-height: var(--leading-tight);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  overflow-wrap: anywhere;
}

/* What a padrino bought, worn at billing size too (ADR 0010). The sticker gilds
   the name; a mug has no name inside it, so the frame is what carries it. */
.mug--padrino {
  border-color: var(--amarillo);
}

.mug--holo {
  background:
    linear-gradient(var(--well), var(--well)) padding-box,
    var(--holo) border-box;
  border-color: transparent;
}

/* Gold, and thrown at the wall behind it: the mark is the loudest thing on the
   card in the prototype, and rosa reads as damage everywhere else in the app. */
.vs__mark {
  font-family: var(--font-display);
  font-size: var(--text-hero);
  letter-spacing: var(--track-display);
  color: var(--amarillo);
  text-shadow: var(--shadow-mark);
}

/* ── Button ────────────────────────────────────────────────────────────── */

/*
 * A pressed button, in the literal sense: it sits on a hard offset shadow and
 * a tap drives it into that shadow instead of nudging it a pixel. Poster type
 * rather than body type — a call to action on a cartel is set in the display
 * face, and these labels are ours and short.
 */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--tap);
  padding: var(--space-2) var(--space-4);
  border: var(--border-poster) solid var(--ink);
  max-width: 100%;
  font: var(--text-micro) / var(--leading-tight) var(--font-display);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  text-align: center;
  text-decoration: none;

  /* A button label can carry a name a player authored: a pet's, a jauría's. */
  overflow-wrap: anywhere;
  cursor: pointer;
  box-shadow: var(--shadow-cast);
  transition: transform var(--motion-fast) var(--ease),
    box-shadow var(--motion-fast) var(--ease), filter var(--motion) var(--ease);
}

/* Travelling exactly the shadow's own offset is what makes it read as the
   button meeting the paper rather than as a shrink. */
.btn:active {
  transform: translate(var(--space-1), var(--space-1));
  box-shadow: var(--shadow-cast-pressed);
}

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

/* Amarillo unless the control is buying one atributo, in which case it is that
   atributo's — the piece, its boost chip and the button that buys it read as
   one thing. `--stat-on` is what carries on the fill: rosa is the one of the
   five that ink cannot sit on. */
.btn--primary {
  background: var(--stat-ink, var(--amarillo));
  color: var(--stat-on, var(--ink));
}

/* A call that belongs to its panel rather than to the night: the build's own
   ink, on the build's own panel, so what it acts on is legible before the
   label is read. */
.btn--lila {
  background: var(--lila);
  color: var(--ink);
}

/* A call that belongs to a rosa panel, and only to one: the volante offer in
   `/pet`'s corner. It is the fill without the bell's size or its voice, which
   is what keeps the two apart on a page that could carry both. */
.btn--rosa {
  background: var(--rosa);
  color: var(--texto);
}

/*
 * The one button that rings the bell: committing to a fight, and nothing else.
 * It is the loudest control in the system — the damage colour at a size nothing
 * else takes — so a screen may never offer two of them. What makes it the bell
 * is the scale and the voice; `.btn--rosa` shares its ink and neither of those.
 */
.btn--bell {
  min-height: calc(var(--tap) + var(--space-4));
  background: var(--rosa);
  color: var(--texto);
  font-size: var(--text-body);
}

/*
 * The quiet one. No shadow and no poster type: a page has one or two calls and
 * a great many ways out, and if every one of them is stamped on the paper then
 * none of them is.
 */
.btn--ghost {
  background: var(--card);
  border-width: var(--border);
  border-color: var(--line);
  color: var(--texto);
  font: 700 var(--text-data) / var(--leading-tight) var(--font-data);
  letter-spacing: var(--track-micro);
  box-shadow: none;
}

.btn--ghost:active {
  transform: translateY(1px);
  box-shadow: none;
}

/* Consequence, in the damage colour — the reading .flash--alert and
   .chip--alert already establish. It never outranks the primary: on the one
   page that offers an irreversible action, the safe way out keeps the amarillo
   weight and this one only has to stop reading like a nav link. */
.btn--danger {
  background: var(--card);
  border-width: var(--border);
  border-color: var(--rosa);
  color: var(--rosa);
  box-shadow: none;
}

.btn--danger:active {
  transform: translateY(1px);
  box-shadow: none;
}

/* An amarillo ring on the amarillo button reads as a halo, not a focus state. */
.btn--primary:focus-visible {
  outline-color: var(--texto);
}

/* Patchy signal is the median session; every action can be in flight. */
.btn[aria-busy="true"] {
  cursor: progress;
  pointer-events: none;
}

.btn[aria-busy="true"]::before {
  content: "";
  width: 1em;
  height: 1em;
  border: var(--border) solid currentcolor;
  border-block-start-color: transparent;
  border-radius: var(--radius-pill);
  animation: btn-spin 600ms linear infinite;
}

@keyframes btn-spin {
  to {
    transform: rotate(1turn);
  }
}

.btn--block {
  width: 100%;
}

/*
 * A run of calls is one group, not four sections. `.shell` spaces what it holds
 * at --space-5, which is right between a card and whatever follows it and too
 * much between two buttons stacked one on the next — at that distance a way on,
 * a way back and a way out read as three unrelated offers. The negative margin
 * takes the shell's own gap back down to --space-3 for the run, and only where
 * one call directly follows another: a paragraph or a card between them ends
 * the run and keeps the full rhythm.
 */
.shell > .btn + .btn {
  margin-block-start: calc(var(--space-3) - var(--space-5));
}

@media (hover: hover) {
  .btn--primary:hover:not(:disabled),
  .btn--bell:hover:not(:disabled) {
    filter: brightness(1.08);
  }

  .btn--ghost:hover:not(:disabled) {
    border-color: var(--dim);
  }
}

/* ── HP bar ────────────────────────────────────────────────────────────── */

.bar {
  height: 11px;
  overflow: hidden;
  background: var(--well);
  border: var(--border-hair) solid var(--line);
  border-radius: var(--radius-pill);
}

.bar__fill {
  display: block;
  width: 100%;
  height: 100%;
  background: var(--menta);
  transform: scaleX(var(--hp, 1));
  transform-origin: left center;
  transition: transform var(--motion) var(--ease);
}

.bar__fill--hurt {
  background: var(--amarillo);
}

.bar__fill--critical {
  background: var(--rosa);
}

/* A jauría's weekly goal borrows the track and fills toward a target instead
   of draining from one, so it carries its own fraction and the marquee yellow
   that bills a win everywhere else. */
.bar__fill--goal {
  background: var(--amarillo);
  transform: scaleX(var(--progress, 0));
}

/* How far today got on one misión. Menta whether it is finished or not: this
   track only ever fills, so there is no bad news for a colour to carry, and the
   band above it is what changes when the thing is won. */
.bar__fill--mission {
  background: var(--menta);
  transform: scaleX(var(--progress, 0));
}

/* The same track carrying the walk to the next level. It reads --xp rather than
   --hp so a replay can never scale a progress bar by accident. */

/* Lila, the way the prototype fills it: XP is the one gauge in the app that is
   not a fight going well or badly, so it stays out of the amarillo/rosa/menta
   reading the rest of the bars share. */
.bar__fill--xp {
  background: var(--lila);
  transform: scaleX(var(--xp, 0));
}

/* ── Stat bar ──────────────────────────────────────────────────────────── */

/*
 * How a rival is read on the scouting sheet: five bars against the lifetime cap,
 * so a build is a shape before it is five numbers. This is what the approved
 * prototype scouts with, and the prototype is the reference (ADR 0015).
 *
 * Two fills on one track. The kit's sits behind and reads the total, the
 * allocated points sit in front — so what a piece of equipment adds is the strip
 * of menta showing past the lila, which is the same two-number reading
 * `.boost` gives beside the figure.
 */
.statbar {
  display: grid;

  /* Each row is its own grid, so the figure's column needs a floor or a
     two-digit atributo shortens that row's track and the five bars stop
     lining up with each other. */
  grid-template-columns: minmax(0, 5.5rem) 1fr minmax(2.75rem, auto);
  align-items: center;
  gap: var(--space-2);
}

.statbar__key {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--dim);
}

.statbar__track {
  position: relative;
  height: 12px;
  overflow: hidden;
  background: var(--well);
  border: var(--border-hair) solid var(--line);
}

/*
 * Every atributo is read in the ink of the thing it buys in the ring — the same
 * ink the fight log already spends on that thing. Fuerza is the damage a hit
 * does, velocidad is the dodge, resistencia is the HP bar, enfoque is the
 * crítico and inteligencia is the counter. A bar, a slot in the tienda and a
 * boost badge all take the colour from here, so the five mean one thing
 * wherever they turn up.
 *
 * `--stat-on` is what reads on top of the band: rosa is the one of the five
 * that ink cannot sit on.
 */
.stat--str {
  --stat-ink: var(--rosa);
  --stat-band: var(--stripe-rosa);
  --stat-on: var(--texto);
}

.stat--spd {
  --stat-ink: var(--azul);
  --stat-band: var(--stripe-azul);
  --stat-on: var(--ink);
}

.stat--end {
  --stat-ink: var(--menta);
  --stat-band: var(--stripe-menta);
  --stat-on: var(--ink);
}

.stat--foc {
  --stat-ink: var(--amarillo);
  --stat-band: var(--stripe-amarillo);
  --stat-on: var(--ink);
}

.stat--int {
  --stat-ink: var(--lila);
  --stat-band: var(--stripe-lila);
  --stat-on: var(--ink);
}

/* The one slot pinned to no atributo, so it bands in no colour (tokens.css). It
   is a band and nothing else: a wildcard has no ink to lend a bar, a chip or a
   button, which is why it sets no --stat-ink. */
.stat--comodin {
  --stat-band: var(--stripe-comodin);
  --stat-on: var(--ink);
}

.statbar__fill {
  position: absolute;
  inset: 0;
  background: var(--stat-ink, var(--lila));
  transform: scaleX(var(--stat, 0));
  transform-origin: left center;
  transition: transform var(--motion) var(--ease);
}

/* What the kit adds, mixed from the atributo's own ink toward the page's
   lightest value rather than given a colour of its own: a strip showing past
   the allocated points reads as more of the same stat, and it has to stay
   legible against all five — including menta, which is one of them. */
.statbar__fill--kit {
  background: color-mix(in srgb, var(--stat-ink, var(--lila)) 35%, var(--texto));
}

.statbar__value {
  display: flex;
  align-items: baseline;
  justify-content: flex-end;
  gap: var(--space-1);
  font-family: var(--font-data);
  font-size: var(--text-data);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* ── Fight log ─────────────────────────────────────────────────────────── */

/*
 * One modifier per resolver event type (CONTEXT.md): exchange, hit, dodge,
 * counter, snack, trait, end. A critical is a flagged hit, not its own event.
 * The emoji markers are game content, not chrome.
 */

/*
 * Set in the body face at reading size, not in mono at caption size. The
 * prototype's stack is 15px Archivo with only the chapter marks, the crítico
 * and the verdict switching to the display face — a fight is prose being read
 * aloud, and mono made it a printout of one. `.impeccable.md` called it "a
 * monospaced ruled stack"; the ruled half was right.
 */
.log {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  font: 400 var(--text-moment) / var(--leading-body) var(--font-body);
  font-variant-numeric: tabular-nums;
}

/*
 * A ruled stack (.impeccable.md), with the rule doing the colour-coding as well
 * as the words: the prototype marks every moment with a bar down its left edge,
 * which is what makes an asalto scannable without reading it. The hues are the
 * prototype's, which is the reference (ADR 0015) — a crítico is amarillo, not
 * rosa, because amarillo is what the last line of a won fight already wears.
 */
.log-line {
  padding-inline-start: var(--space-3);
  border-inline-start: var(--border) solid transparent;
  overflow-wrap: anywhere;
}

/*
 * An asalto is a chapter heading, so it is billed like one: the display face,
 * tracked and set in caps over a wash of the ink it is ruled in. It used to be
 * dim micro text against a hairline, which read as a caption apologising for
 * interrupting rather than as the bell it is.
 *
 * Amarillo on the rule is the third value on that channel: menta is your
 * corner, rosa the other, and amarillo is the house — the fight talking rather
 * than a fighter.
 */
.log-line--exchange {
  padding-block: var(--space-2);
  background: color-mix(in srgb, var(--amarillo) 7%, transparent);
  border-inline-start-color: var(--amarillo);
  font-family: var(--font-display);
  font-size: var(--text-micro);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
}

/*
 * Two questions, two channels. The rule down the leading edge says *whose
 * moment it is* — menta for the corner the viewer is watching for, rosa for the
 * other — which is what somebody scrolling a log back is actually asking. The
 * words keep the prototype's own hues for *what kind* of moment it was, so a
 * dodge still reads as a dodge without the two answers fighting over one
 * channel. The asaltos and the verdict take neither rule: they are the fight
 * talking rather than a fighter.
 */
.log-line--mine {
  border-inline-start-color: var(--menta);
}

.log-line--theirs {
  border-inline-start-color: var(--rosa);
}

.log-line--hit {
  color: var(--texto);
}

.log-line--dodge {
  color: var(--azul);
}

.log-line--counter {
  font-weight: 700;
  color: var(--lila);
}

/*
 * A botana and a rasgo firing are one kind of event to a reader — something
 * went off that was not a punch — and the words name which. They had an ink
 * each in the prototype because it had a spare one; menta is not spare any
 * more. Nothing on this channel may spend menta or rosa: those two answer the
 * other question, and a menta line with a menta rule says its colour twice and
 * its meaning once.
 */
.log-line--snack,
.log-line--trait {
  font-weight: 700;
  color: var(--naranja);
}

/*
 * A crítico is billed, not enlarged. The prototype sets it *smaller* than the
 * stack around it — 14px against 15 — and lets the display face and a wash of
 * amarillo do the work, which is why two of them in a row read as two big
 * moments rather than as the log shouting. Scaling it to --text-display made
 * every ordinary line look like a footnote to it.
 */
.log-line.is-crit {
  padding-block: var(--space-2);
  background: color-mix(in srgb, var(--amarillo) 10%, transparent);
  color: var(--amarillo);
  font-family: var(--font-display);
  font-size: var(--text-data);
}

.log-line--end {
  font-weight: 700;
  color: var(--amarillo);
}

/* ── Verdict ───────────────────────────────────────────────────────────── */

/*
 * The card a fight ends on: what happened, in the biggest type the system has,
 * over the pet it happened to. Menta for a win and rosa for a loss, which is
 * the reading the whole app already has — menta is the healthy bar, the
 * confirmation and the misión met; rosa is damage and defeat. It used to be
 * amarillo for a win, on the grounds that amarillo is victory: it is not, it is
 * what is *billed*, which is why it lands on the fixture, the marquee and the
 * asalto. A verdict is not an announcement, it is an outcome.
 *
 * Ink on menta clears 11:1, so the call keeps the same weight it had on the
 * marquee's yellow.
 */
.verdict {
  align-items: center;
  padding-block: var(--space-6);
  text-align: center;
  box-shadow: var(--shadow-slab);

  /* The bands are the ink laid over the fill, so they are mixed from --sombra
     rather than declared: the same overlay has to work on amarillo and rosa. */
  background-image: repeating-linear-gradient(
    var(--stripe-angle),
    color-mix(in srgb, var(--sombra) 9%, transparent) 0 14px,
    transparent 14px 28px
  );
  animation: verdict-stamp var(--beat) var(--ease-slam);
}

.verdict--won {
  background-color: var(--menta);
  border-color: var(--ink);
  color: var(--ink);
}

.verdict--lost {
  background-color: var(--rosa);
  border-color: var(--ink);
  color: var(--texto);
}

/* On a solid fill the mug's white edge disappears; ink is what holds the photo
   off the poster it is printed on. */
.verdict .mug {
  border-color: var(--ink);
}

/* The mark over the call: the venue's name and the level this was fought at,
   set as the small print of a printed ticket. */
.verdict__mark,
.verdict__billing {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  opacity: 0.75;
}

/* The two names it was fought between, billed rather than captioned. */
.verdict__billing {
  font-family: var(--font-display);
  font-size: var(--text-data);
  letter-spacing: var(--track-display);
  opacity: 1;
}

.verdict__call {
  font-size: var(--text-verdict);
}

/* The reason under the call — por nocaut, por decisión — reads as a caption of
   the fill it sits on, not as body copy that happens to be dark. */
.verdict__why {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  font-weight: 700;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  opacity: 0.75;
}

/* Landing past the mark and settling back: the fight is over and the page says
   so in one movement rather than fading it in. */
@keyframes verdict-stamp {
  from {
    opacity: 0;
    transform: scale(1.14);
  }
}



/* ── Payout ────────────────────────────────────────────────────────────── */

/* What a fight paid, one line per payout. The numbers are set at display size
   because this is the screen the daily ritual ends on and they have to read at
   arm's length — but in the data face, which is the only one of the three that
   sets tabular figures (ADR 0010). The label beside them stays quiet. */
.payout {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin: 0;
  padding: 0;
  list-style: none;
}

.payout__line {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-3);
}

.payout__value {
  font-family: var(--font-data);
  font-size: var(--text-display);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--amarillo);
}

.payout__value--xp {
  color: var(--menta);
}

/* What the day paid on top of the fight, said in the ink the payout wears
   rather than in the grey of the rule beside it. */
.payout__extra {
  color: var(--menta);
}

/* A call the player has not answered yet — points waiting to be placed. Set in
   the ink of the thing this panel wants next rather than in the grey it shares
   with every sentence that is only context. */

/* Points waiting to be placed are the one thing on this page that goes stale by
   being ignored, so the line is plated rather than merely coloured: a sentence
   in the panel's ink still reads as prose, and prose is what a player scrolls
   past. Outlined against the solid button below it, so the two read as one call
   with an order — what is waiting, then what to do about it — instead of two
   lila slabs competing. */
.pending {
  /* A tag, sized to what it says. The card is a stretching column, so a notice
     left to itself goes full-bleed — and a full-width box with centred text
     above a full-width button is a button, whatever its border is doing. It
     centres itself the way `.chip--padrino` does, for the same reason. */
  align-self: center;
  max-width: 100%;
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  border: var(--border) dashed var(--amarillo);
  color: var(--amarillo);
  font-weight: 700;
}

/* The lone sentence a panel ends on, rather than a caption under the block
   above it. `.pending` is the same shape one panel down and centres for the
   same reason: with nothing beside it, a left edge reads as the start of a
   column that never arrives. */
.closing {
  text-align: center;
}

/* A name with a mark after it. Flex so a long name cannot squash the glyph, and
   so the two stay on one baseline at any length. Nothing else joins them: what
   a player can *do* about the name lives on the banner, where it costs the name
   no width. */
.titled {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-width: 0;
  overflow-wrap: anywhere;
}

/* Worn beside a name: what a padrino bought, at the name's own scale. A mark
   rather than a tag, so it cannot be read as a second rango (`padrino_mark`). */
.mark {
  flex: none;
}

.mark--padrino {
  color: var(--amarillo);
}

/*
 * The two numbers a fight paid, billed side by side (ADR 0015). This is the
 * screen the daily ritual ends on and they are the reward, so they are boxed
 * and centred rather than listed — a payout read down a column of labels is an
 * invoice.
 *
 * `column-reverse` puts the number above its label without reordering the
 * markup, so a screen reader still hears "Fichas, +12" in that order.
 *
 * The figures are set in the display face, which is what the prototype bills
 * them in. Tabular numerals are what `.payout__value` normally exists for, and
 * they are not needed here: each figure stands alone in its own centred box,
 * with nothing above or below it to line up against.
 */
.payout--pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
}

.payout--pair .payout__line {
  flex-direction: column-reverse;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-3) var(--space-2);
  background: var(--ink);
  border: var(--border) solid var(--line);
  text-align: center;
}

.payout--pair .payout__value {
  font-family: var(--font-display);
  letter-spacing: var(--track-display);
}

/* The label under a billed number is a caption of it, not a row header. */
.payout--pair .payout__line > :first-child {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  font-weight: 700;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--dim);
}

/* ── Form field ────────────────────────────────────────────────────────── */

.field {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.field__label {
  font-size: var(--text-data);
  font-weight: 600;
}

/* A field is a hole cut in the poster: sunken ground, a thick ink edge, no
   radius. The boundary keeps --dim rather than --ink, because on a control the
   border is the whole affordance and has to clear the 3:1 non-text floor. */
.field__input {
  width: 100%;
  min-height: var(--tap);
  padding: 0 var(--space-3);
  background: var(--well);
  border: var(--border-poster) solid var(--dim);
  color: var(--texto);
  font: 400 var(--text-body) / 1 var(--font-body);
}

.field__input::placeholder {
  color: var(--dim);
}

.field__hint {
  font-size: var(--text-data);
  color: var(--dim);
}

.field__error {
  font-size: var(--text-data);
  font-weight: 600;
  color: var(--rosa);
}

/* A field under a cooldown. The hint beside it carries the date it opens again
   (ADR 0013); the dash is the half of that signal colour does not carry. A
   readonly field still holds the pet's real name and stays in the tab order, so
   it keeps body contrast and its 3:1 boundary — only the genuinely inactive
   file input dims. Both sit above .field--invalid, which has the same
   specificity and has to win when a locked field comes back with an error. */
.field__input[readonly] {
  border-style: dashed;
  cursor: not-allowed;
}

.field__input:disabled {
  border-color: var(--line);
  border-style: dashed;
  color: var(--dim);

  /* iOS Safari paints its own colour on a disabled control and ignores `color`. */
  -webkit-text-fill-color: var(--dim);
  cursor: not-allowed;
}

.field--invalid .field__input {
  border-color: var(--rosa);
}

/* The shorthand above sets a leading of 1, which clips a file input's button. */
.field__input[type="file"] {
  padding-block: var(--space-2);
  line-height: var(--leading-body);
}

/* Same shorthand, same problem from the other side: a leading of 1 stacks the
   lines of a multi-line field on top of each other. Qualified by element the
   way the file input is, so no paragraph field can be added without it. */
textarea.field__input {
  padding-block: var(--space-2);
  line-height: var(--leading-body);
  resize: vertical;
}

/* The six digits copied out of a sign-in mail. It wears the data role because
   that is what it is, and it is set large and tracked out because it is read
   off one screen and typed into another — on a phone, one handed, usually with
   the mail app still half covering the field. */
.field__input--code {
  font-family: var(--font-data);
  font-size: var(--text-display);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--track-display);
  text-align: center;
}

/* ── Picker ────────────────────────────────────────────────────────────── */

/*
 * A <select> the poster can print. The native control hands its open list to
 * the OS — system font, system chrome, on the one field in the app nothing
 * here can reach — so picker_controller paints a listbox over it and leaves
 * the real select in the DOM as the value the form posts.
 *
 * Everything below is behind `--ready`, which the controller adds. Until then
 * the select wears .field__input and works exactly as it always has: the
 * module not arriving costs the paint, never the field (ADR 0009).
 */
.picker {
  position: relative;
}

/*
 * Not `display: none`. A required select the browser cannot render is one it
 * refuses to anchor a validation bubble to — the marketplace ask would fail
 * silently on submit — so it stays laid over the trigger, invisible and
 * untappable, and the bubble comes up where the control appears to be.
 */
.picker--ready > select {
  position: absolute;
  inset: 0;
  width: 100%;
  opacity: 0;
  pointer-events: none;
}

.picker__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  width: 100%;
  min-height: var(--tap);
  padding: var(--space-2) var(--space-3);
  background: var(--well);
  border: var(--border-poster) solid var(--dim);
  color: var(--texto);
  font: 400 var(--text-body) / var(--leading-tight) var(--font-body);
  text-align: start;
  cursor: pointer;
}

.picker__trigger:focus-visible {
  outline: var(--border) solid var(--amarillo);
  outline-offset: 2px;
}

.field--invalid .picker__trigger {
  border-color: var(--rosa);
}

.picker__trigger:disabled {
  border-color: var(--line);
  border-style: dashed;
  color: var(--dim);
  cursor: not-allowed;
}

/* An include_blank option is a real answer here — no country, never — so it
   reads as the placeholder it is rather than as a chosen value. */
.picker__value--blank {
  color: var(--dim);
}

.picker__chevron {
  flex: none;
  color: var(--amarillo);
  transition: transform var(--motion) var(--ease);
}

.picker__trigger[aria-expanded="true"] .picker__chevron {
  transform: rotate(180deg);
}

.picker__trigger:disabled .picker__chevron {
  color: var(--dim);
}

.picker__scrim {
  position: fixed;
  z-index: 100;
  inset: 0;
  background: color-mix(in srgb, var(--ink) 82%, transparent);
}

/*
 * A sheet on a phone, because a list of thirty countries opened from the top of
 * the screen is a list read at the top of the screen and reached at the bottom.
 * It comes up off the bottom edge where the thumb already is.
 */
.picker__panel {
  position: fixed;
  z-index: 101;
  inset-inline: 0;
  inset-block-end: 0;
  display: flex;
  flex-direction: column;
  max-height: 70dvh;
  background-color: var(--card);
  background-image: var(--halftone);
  background-size: var(--halftone-grid);
  border-block-start: var(--border-poster) solid var(--amarillo);
  animation: picker-in var(--motion) var(--ease);
}

@keyframes picker-in {
  from {
    transform: translateY(100%);
  }
}

.picker__banner {
  flex: none;
  padding: var(--space-2) var(--space-3);
  background-image: var(--stripe-amarillo);
  color: var(--ink);
  font: var(--text-micro) / var(--leading-tight) var(--font-display);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  overflow-wrap: anywhere;
}

.picker__list {
  overflow-y: auto;
  margin: 0;
  padding: 0;
  list-style: none;
  overscroll-behavior: contain;
}

.picker__option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: var(--tap);
  padding: var(--space-2) var(--space-3);
  border-block-end: var(--border-hair) solid var(--line);
  cursor: pointer;
}

.picker__option:last-child {
  border-block-end: 0;
}

/* Where the keyboard is, which is not the same question as what is chosen —
   the two are only the same row until the first arrow key. */
.picker__option--active {
  background: var(--poster-hi);
  box-shadow: inset var(--border-poster) 0 0 var(--amarillo);
}

.picker__option[aria-selected="true"] {
  color: var(--amarillo);
  font-weight: 600;
}

/* Colour is never the only signal of what is chosen — the same rule the choice
   card is built on, and the reason the mark is in the row at all. */
.picker__check {
  flex: none;
  opacity: 0;
}

.picker__option[aria-selected="true"] .picker__check {
  opacity: 1;
}

/*
 * On a pointer, the sheet is wrong: the list is anchored to a trigger the
 * cursor is already on, and throwing it to the bottom of a desktop viewport
 * moves the answer further from the question. The scrim stays to catch the
 * click outside; it just stops painting.
 */
@media (width >= 560px) and (hover: hover) {
  .picker__scrim {
    background: transparent;
  }

  .picker__panel {
    position: absolute;
    inset-block: calc(100% + var(--space-1)) auto;
    max-height: 20rem;
    border: var(--border-poster) solid var(--amarillo);
    box-shadow: var(--shadow-cast);
    animation: none;
  }
}

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

  .picker__chevron {
    transition: none;
  }
}

/* The page must not travel while a sheet is open on it: the thumb dragging the
   list would drag the surface behind it, and the anchored panel is positioned
   against a trigger that would scroll out from under it. */
.picker-open {
  overflow: hidden;
}

.fieldset {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
}

/* A legend is not a flex item — it sits above the anonymous content box — so
   the fieldset's row-gap never reaches it and the space has to be its own. */
.fieldset > legend {
  padding: 0;
  margin-block-end: var(--space-3);
}

/* ── Choice ────────────────────────────────────────────────────────────── */

/*
 * A radio stretched over its own panel: the whole card is the tap target, the
 * control stays a real radio for the keyboard and the screen reader, and the
 * check mark means colour is never the only signal of what is chosen.
 */
.choice {
  position: relative;
  display: block;
}

.choice__input {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  margin: 0;
  opacity: 0;
  cursor: pointer;
}

.choice__body {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  padding: var(--space-3);
  background: var(--card);
  border: var(--border) solid var(--line);
  transition: border-color var(--motion) var(--ease);
}

.choice__input:checked + .choice__body {
  border-color: var(--amarillo);
}

.choice__input:focus-visible + .choice__body {
  outline: var(--border) solid var(--amarillo);
  outline-offset: 2px;
}

/* An amarillo ring on the amarillo border reads as a halo, not a focus state,
   and the checked card is the first one a keyboard lands on. */
.choice__input:checked:focus-visible + .choice__body {
  outline-color: var(--texto);
}

.choice__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
}

/*
 * `anywhere` belongs on labels a player authored, and these four are ours:
 * equilibrado, agresivo, defensivo, táctico, out of the locale files. Against a
 * word we ship it does not prevent a bad line, it makes one — EQUILIBRADO lost
 * its last letter to the line below and read as a typo rather than as a name.
 * `hyphens` is what a word too long for its box actually wants, and it breaks
 * where the language says to, with the hyphen that says a break happened.
 */
.choice__name {
  padding-inline-end: var(--space-5);
  font-family: var(--font-display);
  font-size: var(--text-body);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  hyphens: auto;
}

/*
 * In the corner rather than in the name's line. In the flow it took a fixed
 * bite out of the width the name had to fit inside, visible or not — it only
 * ever fades — so the longest postura was short by exactly the width of a mark
 * that was not being shown.
 */
.choice__check {
  position: absolute;
  inset-block-start: var(--space-3);
  inset-inline-end: var(--space-3);
  color: var(--amarillo);
  opacity: 0;
}

.choice__input:checked + .choice__body .choice__check {
  opacity: 1;
}

/*
 * The corner: a fighter billed across rather than down. The photo at 110px
 * beside the name, the tags and the card they have run up — which is how the
 * approved prototype prints a pet on its own page, and it keeps the build below
 * on the same screen as the face it belongs to.
 */
.fighter {
  display: grid;
  grid-template-columns: 110px 1fr;
  align-items: center;
  gap: var(--space-3);
}

.fighter > div:last-child {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  min-width: 0;
}

/* The gauge's own label line: what it measures on the left, where it has got to
   on the right, both above the track. */
.gauge-label {
  display: flex;
  justify-content: space-between;
  gap: var(--space-3);
}

/* ── Point spread ──────────────────────────────────────────────────────── */

.spread {
  display: grid;
  grid-template-columns: 1fr auto;
  align-items: center;
  gap: var(--space-2) var(--space-3);
}

.spread__value {
  width: 5rem;
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.spread__left {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--space-2);
}

/* Overspent is the only state the number cannot explain on its own. */
.spread__remaining.is-over {
  font-weight: 700;
  color: var(--rosa);
}

/*
 * The creation counter (ADR 0015): five cells across the panel, each one a
 * figure over the pair of keys that moves it. Nothing here is a preset — the
 * player places every point — so the grid is the whole control and it has to
 * hold five columns at 360px. The field itself is the number, kept a real
 * number field so a keyboard and a form without JavaScript both still work.
 */
.nums {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: var(--space-1);
}

.nums__cell {
  display: grid;
  justify-items: center;
  gap: var(--space-1);
  min-width: 0;
}

.nums__key {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  letter-spacing: var(--track-micro);
  color: var(--dim);
}

.nums__value {
  width: 100%;
  min-width: 0;
  padding: var(--space-2) 0;
  background: var(--ink);
  border: var(--border) solid var(--line);
  color: var(--texto);
  font: var(--text-title) / 1 var(--font-display);
  text-align: center;

  /* A spinner beside a stepper is two of the same control in one cell. */
  appearance: textfield;
}

.nums__value::-webkit-inner-spin-button {
  appearance: none;
}

.nums__value:focus-visible {
  border-color: var(--amarillo);
  outline: none;
}

/* A box this visit has moved off its floor. Only a level-up has a floor to move
   off, so this never fires at creation, where every box starts at zero. */
.nums__value.is-raised {
  color: var(--menta);
  border-color: var(--menta);
}

.nums__pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 2px;
  width: 100%;
}

/* Under the 44px tap floor on purpose: five stats across a 360px screen is what
   this panel is, and the pair sits under a figure with nothing else near it. */
.nums__step {
  min-height: 30px;
  background: var(--well);
  border: var(--border) solid var(--line);
  color: var(--texto);
  font: var(--text-body) / 1 var(--font-display);
  cursor: pointer;
}

.nums__step:hover {
  border-color: var(--amarillo);
  color: var(--amarillo);
}

/* What is still unplaced, printed at the size of the decision it is. */
.tally {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: var(--space-2);
}

.tally__value {
  font-family: var(--font-display);
  font-size: var(--text-display);
  color: var(--menta);
}

/* Overspent is the one state the figure cannot explain on its own — and the
   only thing left for colour to say here, now that the counter reads menta
   whatever it is counting down from. */
.tally__value.is-over {
  color: var(--rosa);
}

/* A balance rather than a countdown: amarillo, because fichas are amarillo
   everywhere they are counted, and with room for the coin beside the figure. */
.tally__value--fichas {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  color: var(--amarillo);
}

/* The one rule printed under a balance: what these fichas can be spent on, and
   at what price. It is centred because it belongs to the figure above it — set
   flush left it read as the panel's body text with a total floating over it. */
.tally__note {
  text-align: center;
}

/* Menta rather than --dim: this is the points counter, and menta is already the
   ink a healthy number is read in. It also puts the label in the same colour as
   the panel it is printed on. */
.tally__label {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--menta);
}

/*
 * Five glosses inside a panel, folded away until somebody asks for them. A
 * native <details> rather than a controller: the tap has to work whether or not
 * the module arrives (ADR 0009), and this one is the whole tutorial the presets
 * used to be.
 */
.guide {
  border-block-start: var(--border-hair) solid var(--line);
}

.guide[open] {
  padding-block-end: var(--space-2);
}

.guide__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  min-height: var(--tap);
  list-style: none;
  cursor: pointer;
}

.guide__head::-webkit-details-marker {
  display: none;
}

.guide__mark {
  color: var(--amarillo);
  transition: transform var(--motion) var(--ease);
}

.guide[open] .guide__mark {
  transform: rotate(90deg);
}

/*
 * A slab that opens. `.guide` folds something away *inside* a panel; this folds
 * the panel itself, so its `<summary>` wears the banner and the thing reads as
 * a section of the page rather than as small print between two of them. Use it
 * where the folded content is what a surface is for — the scouting sheet's own
 * numbers — and `.guide` where it is a footnote.
 *
 * A native <details>, so the tap needs no controller (ADR 0009). The banner's
 * bleed, type and stripe all come from `.card__banner`; only what a summary
 * needs is spelled here.
 */

/* A closed block still lays out its content box, so the panel's own bottom
   padding outlives what it was spacing and a shut fold wears a bar of empty
   card under its band. The banner already cancels the other three sides. */
.fold:not([open]) {
  gap: 0;
  padding-block-end: 0;
}

/* No tap-size floor: it made this band visibly fatter than every other banner
   on the page, and a band is already a full-width row well clear of the 24px
   target minimum. The floor is for controls that are only as big as their
   label. */
.fold__head {
  list-style: none;
  cursor: pointer;
}

.fold__head::-webkit-details-marker {
  display: none;
}

.fold__mark {
  margin-inline-start: auto;
  transition: transform var(--motion) var(--ease);
}

.fold[open] .fold__mark {
  transform: rotate(90deg);
}

/* Why the bell is shut. It sits under the button it explains, and goes away the
   moment it stops being true. */
.form-note {
  text-align: center;
  font-size: var(--text-data);
  color: var(--dim);
}

/*
 * The photo panel, where the sticker is the control: the file input sits in the
 * screen reader's layer and this is what a thumb lands on. The whole thing is a
 * <label>, so the picker opens with no controller involved and the input keeps
 * its own keyboard and its own announcement.
 *
 * Centred and large because the photo is the art (.impeccable.md) — it is the
 * one thing on this page the player already owns, and it should not be billed
 * as a thumbnail next to a browser widget.
 */
.dropzone {
  display: grid;
  justify-items: center;
  gap: var(--space-3);
  cursor: pointer;
}

.dropzone .pet-card {
  width: min(200px, 66%);
}

/* Cover, not contain: every stored variant is `resize_to_fill`, so a preview
   that letterboxed would be showing a crop the pet is never going to wear. */
.dropzone .pet-card__photo img {
  object-fit: cover;
}

.dropzone__call {
  font-family: var(--font-display);
  font-size: var(--text-data);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  color: var(--amarillo);
}

/* The input is visually hidden, so the ring has to be drawn by the thing that
   looks like the control. Focus-visible on the input, not focus-within on the
   label: a mouse click on a label focuses the input too, and a ring appearing
   on every tap is noise. */
.dropzone:has(.sr:focus-visible) .pet-card {
  outline: var(--border) solid var(--amarillo);
  outline-offset: 3px;
}

/* ── Flash ─────────────────────────────────────────────────────────────── */

/* One line of consequence after a redirect: signed out, link expired. The
   border carries the meaning, so the message stays readable at --texto. */
.flash-region {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  max-width: var(--shell);
  margin-inline: auto;
  padding: var(--space-4) var(--space-3) 0;
}

.flash {
  padding: var(--space-3);
  background: var(--card);
  border: var(--border-poster) solid var(--ink);
  box-shadow: var(--shadow-cast);
  font-weight: 600;
}

.flash--notice {
  border-color: var(--menta);
}

.flash--alert {
  border-color: var(--rosa);
}

/* ── Chip ──────────────────────────────────────────────────────────────── */

/* Square, like everything the poster prints: a pill is a soft shape and this
   system stopped having those (ADR 0015). The boundary thickens to match. */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2);
  background: var(--well);
  border: var(--border) solid var(--line);
  font: 700 var(--text-micro) / 1 var(--font-data);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--dim);
}

/* A state to find on a scrolling page, not a decoration: the moderation queue
   is scanned before it is read. The label says the same thing without it. */
.chip--alert {
  border-color: var(--rosa);
  color: var(--rosa);
}

/* A podio a closed season left on a pet (COMBAT-31), and the one chip that is a
   reward rather than a reading. Menta rather than amarillo, the victory colour,
   because amarillo on this same card is already the padrino's. A season is named
   in `config/seasons.yml`, so the label is as long as somebody authored it. */
.chip--temporada {
  max-width: 100%;
  border-color: var(--menta);
  color: var(--menta);
}

/*
 * The two readings a counter is scanned for. What a piece adds wears the colour
 * `.boost` already spends on exactly that, and what it costs wears the marquee
 * colour every ficha in the app wears — so a row of three chips is read by
 * colour before it is read by word.
 */

/* Menta unless the chip names an atributo, in which case it wears that
   atributo's ink — a "+1 ENF" that is not the colour of enfoque is a third
   thing to learn. */
.chip--boost {
  border-color: var(--stat-ink, var(--menta));
  color: var(--stat-ink, var(--menta));
}

/* A misión the day has already answered. The check is what carries it where
   menta on a chip could be mistaken for any other good number. */
.chip--done {
  border-color: var(--menta);
  color: var(--menta);
}

/*
 * The two readings a scouting sheet gives that are neither a size nor a
 * threat: the shape of a build and the one permanent thing about a fighter.
 * Lila is the ink the atributo bars are already drawn in, so an archetype —
 * which is a name for those five numbers — takes it; the rasgo takes azul,
 * the last hue in the palette that does not already mean something on a card.
 */
.chip--build {
  border-color: var(--lila);
  color: var(--lila);
}

.chip--trait {
  border-color: var(--azul);
  color: var(--azul);
}

.chip--cost {
  border-color: var(--amarillo);
  color: var(--amarillo);
}

/*
 * The rareza ladder, printed rather than coloured. Every hue in the palette
 * belongs to one of the five atributos, and the two chips a rareza always sits
 * beside are one of those and the amarillo price — so a hue ladder here would
 * have to say a colour that already means something else. What a poster still
 * has spare is how much ink the press laid: the rungs get louder, and común is
 * the bare chip because the commonest thing is the one nothing was spent on.
 */
.rareza--poco_comun {
  border-color: var(--texto);
  color: var(--texto);
}

.rareza--raro {
  background: var(--texto);
  border-color: var(--texto);
  color: var(--ink);
}

/* The one chip printed twice. A legendario is not for sale at any price — it
   only drops — so it is the loudest rung, and the band is the overlay the
   verdict lays over its fill rather than a second colour. */
.rareza--legendario {
  background-color: var(--texto);
  background-image: repeating-linear-gradient(
    var(--stripe-angle),
    color-mix(in srgb, var(--sombra) 16%, transparent) 0 var(--stripe-band),
    transparent var(--stripe-band) calc(var(--stripe-band) * 2)
  );
  border-color: var(--texto);
  color: var(--ink);
}

/* ── Rank ──────────────────────────────────────────────────────────────── */

/* Rango is the one thing on a pet that is purely earned and purely cosmetic
   (CONTEXT.md), so it gets the marquee treatment the numbers never do. One
   treatment for all five: the name is the reward, and a colour per rank would
   spend the palette's meanings on decoration. */
.rank {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--well);
  border: var(--border) solid var(--amarillo);
  font: var(--text-body) / var(--leading-tight) var(--font-display);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  color: var(--amarillo);
}

/* ── Glossary ──────────────────────────────────────────────────────────── */

/*
 * A term and what it does, stacked. The shape the prototype uses everywhere the
 * app explains itself — the landing's five stats, the guide and the archetypes at
 * level-up — and all three of those were two-column tables here.
 *
 * A table is the wrong instrument for it: the term is one word and the gloss is a
 * sentence, so the sentence wraps to three lines in its cell while the header
 * column sits nearly empty. `.impeccable.md` says density belongs in tables;
 * this is not density, it is prose with a label on it.
 */

/* A list has to sit further from the line that introduces it than its own
   entries sit from each other, or the first term reads as part of the sentence
   above it. The entries stand --space-4 apart and the panel's gap adds
   --space-3, so this takes the leading past both rather than matching them. */
.glossary {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
  margin: 0;
  padding-block-start: var(--space-3);
}

.glossary__term {
  font: var(--text-micro) / var(--leading-tight) var(--font-display);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  color: var(--texto);
}

.glossary__gloss {
  margin: var(--space-1) 0 0;
  font-size: var(--text-body);
}

/* ── Table ─────────────────────────────────────────────────────────────── */

/* Dense surfaces scroll inside their own box; the page never does. */
.table-scroll {
  overflow-x: auto;
}

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-data);
}

/* Ink rules rather than hairlines: a printed table is ruled, and at this weight
   the grid survives the halftone under it. */
.table th,
.table td {
  padding: var(--space-2);
  border-block-end: var(--border) solid var(--ink);
  font-variant-numeric: tabular-nums;
  text-align: left;
}

.table th {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  font-weight: 700;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  color: var(--dim);
}

/* The head is the band the banner is elsewhere, at a table's scale. */
.table thead th {
  background: var(--well);
}

/*
 * The reader's own row, inverted rather than tinted. A board is scanned for one
 * line and a wash of amarillo at 7% is not what finds it; the row that matters
 * is printed in the marquee colour and everything in it takes the ink.
 */
.table tr[aria-current] th,
.table tr[aria-current] td {
  background: var(--amarillo);
  border-block-end-color: var(--amarillo-print);
  color: var(--ink);
}

.table .num {
  font-family: var(--font-data);
  text-align: right;
  white-space: nowrap;
}

/* Four columns of tracked mono caps and a row header as long as "Inteligencia"
   do not fit 320px at the padding above. A comparison the screen exists for
   may not be the thing that scrolls sideways. */
.table--dense th,
.table--dense td {
  padding-inline: var(--space-1);
}

/* A button that lives in a row rather than under a card. `.btn` breaks a label
   anywhere so a pet's or a jauría's name can wrap inside one; an action column
   is about 90px on a 320px phone, where that turns "Comprar" into two lines and
   the row into 54px. These labels are ours and each is one short word, so here
   the word stays whole and the row keeps a 44px tap target. */
.table .btn {
  padding-inline: var(--space-2);
  overflow-wrap: normal;
}

/* ── Stat boost ────────────────────────────────────────────────────────── */

/* What a pet's equipment adds to a stat, kept beside the number instead of
   folded into it: the 20 points are permanent and this half is not. It carries
   its own figures, so it stays aligned outside a table cell too. */
.boost {
  font-family: var(--font-data);
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  color: var(--menta);
}

/* ── Padrino ───────────────────────────────────────────────────────────── */

/*
 * What a one-time supporter tier buys, and the whole of what it buys: nothing
 * below moves a number anywhere (ADR 0011). Tier 1 gilds the name on the
 * sticker, tier 2 swaps the sticker's white border for the holographic sweep
 * .impeccable.md reserves for it. Both ride on the pet card, so every surface
 * that bills a pet wears them from the same rule.
 */
.pet-card--padrino .pet-card__name {
  color: var(--amarillo);
}

.pet-card--holo {
  background:
    linear-gradient(var(--card), var(--card)) padding-box,
    var(--holo) border-box;
  border-color: transparent;
}

/* The badge, on the two pages a pet is the subject of. Marquee yellow, which is
   what a victory already wears — but a chip, never `.rank`, because the rango
   is earned and this was bought (CONTEXT.md). It centres itself the way the
   jauría tag does: the card is a stretching column, so an inline pill left to
   itself goes full-bleed. */
.chip--padrino {
  align-self: center;
  max-width: 100%;
  border-color: var(--amarillo);
  color: var(--amarillo);
}

/* What a tier includes, read before a price is paid, with the tick outside the
   text column. `list-style: none` costs a list its semantics in VoiceOver, so
   the markup carries `role="list"` to put them back. */
.includes {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);

  /* The UA indent is 40px, which is 15% of the content column at 320px and
     leaves every line hanging off the label above it. */
  margin: 0;
  padding: 0;
  list-style: none;
}

.includes__item {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
}

/*
 * The lema de entrada a tier-2 padrino wrote, announced before the bell the way
 * a cartel bills a luchador. Quoted rather than shouted: the log is what the
 * page exists for, and sixty characters of somebody else's words must not
 * out-bill it.
 */
.lema {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  font-size: var(--text-data);
  line-height: var(--leading-body);
  color: var(--dim);
  overflow-wrap: anywhere;
}

.lema__who {
  font-family: var(--font-display);
  font-size: var(--text-micro);
  letter-spacing: var(--track-display);
  text-transform: uppercase;
  color: var(--amarillo);
}

/* ── The case against a verdict ────────────────────────────────────────── */

/*
 * What a hidden player wrote to the one human who hid them, read on the
 * moderation card and nowhere else. Prose rather than a table row: it is the
 * thing on that card which has to be read in full, at whatever length its
 * author chose, and nothing stops five hundred characters arriving without a
 * space in them.
 */
.case {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  line-height: var(--leading-body);
  overflow-wrap: anywhere;
}

.case__when {
  font-family: var(--font-data);
  font-size: var(--text-micro);
  color: var(--dim);
}

/* ── Empty state ───────────────────────────────────────────────────────── */

/* Empty states teach the next action; they never just say "nothing here". */
.empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-6) var(--space-4);
  text-align: center;
}

.empty__body {
  color: var(--dim);
}

/* ── Segmented control ─────────────────────────────────────────────────── */

/* One choice out of a few, all of them worth showing at once: a board's week
   against its season, the globe against a country. How many options there are
   is data rather than layout — a country earns its place by having players —
   so the track scrolls inside its own box and never wraps. Wrapped pills stop
   reading as one choice, and three rows of them push the thing the page is for
   below the fold. The options are links, so focus scrolls the track and it
   needs no tabindex of its own. */
.segmented {
  display: flex;
  overflow-x: auto;
  background: var(--well);
  border: var(--border-poster) solid var(--ink);
}

.segmented__option {
  display: inline-flex;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  min-height: var(--tap);
  padding-inline: var(--space-4);
  font: 700 var(--text-micro) / 1 var(--font-data);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  color: var(--dim);
  transition: color var(--motion) var(--ease);
}

.segmented__option[aria-current="page"] {
  background: var(--amarillo);
  color: var(--ink);
}

/* An amarillo ring on the amarillo fill reads as a halo, not a focus state. */
.segmented__option[aria-current="page"]:focus-visible {
  outline-color: var(--texto);
}

@media (hover: hover) {
  .segmented__option:not([aria-current="page"]):hover {
    color: var(--texto);
  }
}

/* ── Locale switch ─────────────────────────────────────────────────────── */

.site-footer {
  display: flex;
  justify-content: center;
  padding: var(--space-4) var(--space-3) var(--space-6);
}

/* The hairline is decoration elsewhere; here it is the whole affordance, so
   the boundary takes --dim and clears the 3:1 non-text floor. */
.locale-switch {
  display: flex;
  background: var(--well);
  border: var(--border) solid var(--dim);
}

/*
 * Equal halves, not two labels sized to their words: "Español" is shorter than
 * "English", and with the pill's gap gone the two butting together at different
 * widths read as one broken control rather than as a choice (COMBAT-61). A
 * switch has as many equal seats as it has options.
 */
.locale-switch__option {
  flex: 1;
  min-height: var(--tap);
  padding-inline: var(--space-4);
  background: none;
  border: 0;
  color: var(--dim);
  font: 700 var(--text-micro) / 1 var(--font-data);
  letter-spacing: var(--track-micro);
  text-transform: uppercase;
  cursor: pointer;
  transition: color var(--motion) var(--ease);
}

.locale-switch__option[aria-current="true"] {
  background: var(--amarillo);
  color: var(--ink);
}

/* An amarillo ring on the amarillo fill reads as a halo, not a focus state. */
.locale-switch__option[aria-current="true"]:focus-visible {
  outline-color: var(--texto);
}

@media (hover: hover) {
  .locale-switch__option:not([aria-current="true"]):hover {
    color: var(--texto);
  }
}

/* ── Visually hidden ───────────────────────────────────────────────────── */

/*
 * Read, never seen. For the word behind an abbreviation a component shows for
 * room's sake — `.statbar` bills "FUE" in 88px and owes a reader "Fuerza".
 *
 * `clip-path` rather than `display: none` or `width: 0`, both of which take the
 * text out of the accessibility tree along with the layout.
 */
.sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ── Icon ──────────────────────────────────────────────────────────────── */

/* IconHelper#icon is the only sanctioned glyph; shapes carry currentColor. */
.ic {
  flex: none;
  fill: none;
  vertical-align: -0.15em;
}
