/* ------------------------------------------------------------------
   The seat panel, over the board.

   A bottom sheet on a phone and a card beside the map on anything wider —
   the same panel either way, because the difference is where a thumb is, not
   what the player needs to read.

   It sits in its own layer outside .g-inner, so the page's padding and the
   sticky navigation have no say over it.
   ------------------------------------------------------------------ */

.g-panel-layer { display: none; }
.g-panel-layer.is-open {
  display: block;
  position: fixed;
  inset: 0;
  z-index: 40;
}

/*
 * The scrim is light, because the page is paper.
 *
 * A dark scrim over ivory reads as the lights going out. This is barely a
 * tint — enough to say the board is not what you are looking at, not enough
 * to stop you looking at it, which is the whole reason the panel opens over
 * the map instead of replacing it.
 */
.g-panel-scrim {
  position: absolute;
  inset: 0;
  background: rgba(18, 32, 54, 0.16);
  backdrop-filter: blur(1px);
}

/* ------------------------------------------------------------ the panel */

.dp {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  max-height: 76vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;

  border: 1px solid var(--rule);
  border-bottom: 0;
  border-radius: 16px 16px 0 0;
  background: var(--paper-2);
  box-shadow: 0 -8px 30px rgba(18, 32, 54, 0.14);
  animation: dp-rise 0.22s ease-out;
}

@keyframes dp-rise {
  from { transform: translateY(14px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

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

/* --------------------------------------------------------------- head */

.dp-head {
  position: relative;
  flex: none;
  padding: 15px 52px 12px 16px;
  border-bottom: 1px solid var(--rule-soft);
  background: var(--paper);
}

.dp-name {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.34rem;
  font-weight: 600;
  line-height: 1.1;
  letter-spacing: -0.02em;
  color: var(--navy);
  text-wrap: balance;
}
.dp-where {
  margin: 2px 0 0;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--navy-3);
}

.dp-close {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 34px;
  height: 34px;
  min-height: 34px;
  display: grid;
  place-items: center;
  border: 1px solid var(--rule);
  border-radius: 50%;
  background: var(--paper-2);
  color: var(--navy-2);
  font-size: 0.8rem;
  line-height: 1;
  cursor: pointer;
}
.dp-close:hover { border-color: var(--gold); color: var(--navy); }
.dp-close:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

/*
 * The seat in one line: what state it is in, and who is ahead.
 *
 * The word first and the party second, because "contested" is the thing that
 * changes what you do and the name is the detail.
 */
.dp-state {
  display: flex;
  align-items: baseline;
  gap: 10px;
  margin-top: 10px;
  padding-top: 9px;
  border-top: 1px solid var(--rule-soft);
}
.dp-state-word {
  font-size: 0.64rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--navy-3);
}
.dp-state-leading .dp-state-word { color: var(--warn-good); }
.dp-state-contested .dp-state-word { color: var(--gold); }
.dp-state-won .dp-state-word { color: var(--blue); }

.dp-state-who {
  display: inline-flex;
  align-items: baseline;
  gap: 6px;
  font-size: 0.84rem;
  font-weight: 600;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
}
.dp-state-who.is-quiet { font-weight: 400; color: var(--navy-3); }
.dp-state-dot {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--party, var(--navy-3));
  transform: translateY(-1px);
}

/* --------------------------------------------------------------- body */

.dp-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 14px 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 14px;
}

.dp-block { display: flex; flex-direction: column; gap: 8px; }
.dp-block-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}
.dp-block-note {
  font-size: 0.58rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--navy-3);
}
.dp-block-title {
  margin: 0;
  font-size: 0.6rem;
  font-weight: 600;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--navy-3);
}

/* ---------------------------------------------------------- the field */

/*
 * One row per party. Four columns and no chart.
 *
 * A tick or a ring says whether they are in it, the party says who, the
 * figure says what they put in this round, and the last word says where they
 * stand. It reads across in that order because that is the order the
 * questions arrive in.
 */
.dp-field {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  overflow: hidden;
}

.dp-row {
  display: grid;
  grid-template-columns: 20px minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  background: var(--paper-2);
}
.dp-row + .dp-row { border-top: 1px solid var(--rule-soft); }

/* Your own row is marked by a rule in your colour, not by a fill. */
.dp-row.is-you {
  background: var(--paper);
  box-shadow: inset 3px 0 0 var(--party);
}

.dp-mark {
  font-size: 0.8rem;
  line-height: 1;
  color: var(--navy-3);
  text-align: center;
}
.dp-row.is-leading .dp-mark,
.dp-row.is-contested .dp-mark,
.dp-row.is-won .dp-mark { color: var(--party); }

.dp-who { display: flex; flex-direction: column; gap: 2px; min-width: 0; }

/*
 * The party's name, which is now the line that identifies the row.
 *
 * It is a real name rather than three letters, so it can be long — and this
 * sits in a grid column that has to share the row with a figure and a status.
 * It truncates rather than wrapping: two lines here would set every row in
 * the table to a different height.
 */
.dp-party {
  font-size: 0.86rem;
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--navy);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.dp-name-small {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: 0.7rem;
  color: var(--navy-3);
  min-width: 0;
}

/* The abbreviation, demoted to a badge and tinted with the party's colour. */
.dp-badge {
  flex: none;
  padding: 1px 5px;
  border: 1px solid color-mix(in srgb, var(--party) 45%, transparent);
  border-radius: 3px;
  background: color-mix(in srgb, var(--party) 12%, transparent);
  color: color-mix(in srgb, var(--party) 70%, var(--navy));
  font-size: 0.62rem;
  font-weight: 700;
  letter-spacing: 0.06em;
}

.dp-holder {
  min-width: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.dp-spend {
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--navy-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/*
 * The position, as one word.
 *
 * Colour is doing real work here and is kept to three: the good green for a
 * lead, gold for a seat still in play, and nothing at all for the rest —
 * because "trailing" and "no bid" are not warnings, they are just facts.
 */
.dp-pos {
  min-width: 68px;
  text-align: right;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--navy-3);
  white-space: nowrap;
}
.dp-row.is-leading .dp-pos { color: var(--warn-good); }
.dp-row.is-contested .dp-pos { color: var(--gold); }
.dp-row.is-won .dp-pos { color: var(--blue); }

/* --------------------------------------------------------- the money */

/*
 * The money, pinned below the scroll.
 *
 * A phone has room for about two thirds of a seat, and what fell off the end
 * was the button. The field scrolls; what the player came to press does not
 * move.
 */
.dp-actions {
  flex: none;
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 12px 16px calc(14px + env(safe-area-inset-bottom, 0px));
  border-top: 1px solid var(--rule);
  background: var(--paper);
}

.dp-foot {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 12px;
  border-top: 1px solid var(--rule);
}
.dp-foot.is-locked { border-top-color: var(--rule-soft); }

.dp-purse {
  display: flex;
  gap: 18px;
  flex-wrap: wrap;
}
.dp-purse-fig { display: flex; flex-direction: column; gap: 1px; }
.dp-purse-label {
  font-size: 0.58rem;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--navy-3);
}
.dp-purse-value {
  font-family: var(--font-display);
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
}
.dp-purse-value.is-grant { color: var(--gold); }

.dp-spend-block {
  padding: 12px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--paper);
  gap: 12px;
}

/* The amounts the engine will actually accept, and no others. */
/*
 * One row, scrolling sideways if it has to.
 *
 * Wrapping made the pinned bar grow by a row every time the campaign got
 * richer, which pushed the button down the screen — the exact problem pinning
 * it was meant to solve.
 */
.dp-amounts {
  display: flex;
  gap: 7px;
  overflow-x: auto;
  scrollbar-width: none;
  -webkit-overflow-scrolling: touch;
  padding-bottom: 2px;
}
.dp-amounts::-webkit-scrollbar { display: none; }
.dp-amount { flex: 0 0 auto; }
.dp-amount {
  appearance: none;
  min-height: 40px;
  padding: 8px 13px;
  border: 1px solid var(--rule);
  border-radius: 999px;
  background: var(--paper-2);
  font-family: var(--font);
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--navy-2);
  font-variant-numeric: tabular-nums;
  cursor: pointer;
  transition: border-color 0.12s ease, color 0.12s ease;
}
.dp-amount:hover { border-color: var(--gold); color: var(--navy); }
.dp-amount:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }
.dp-amount.is-on {
  border-color: var(--gold);
  background: var(--wash-gold);
  color: var(--navy);
  box-shadow: inset 0 0 0 1px var(--gold);
}
.dp-amount[disabled] { opacity: 0.5; cursor: default; }

/* Three figures, ruled apart, so the arithmetic is visible rather than told. */
.dp-sums { display: flex; flex-direction: column; }
.dp-sum {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  padding: 7px 0;
}
.dp-sum + .dp-sum { border-top: 1px solid var(--rule-soft); }
.dp-sum-label {
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  color: var(--navy-2);
}
.dp-sum-value {
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
}
.dp-sum.is-spend .dp-sum-value { color: var(--gold); }
.dp-sum.is-after { border-top-width: 1px; }
.dp-sum.is-after .dp-sum-label { color: var(--navy); }

.dp-go[disabled] { opacity: 0.55; cursor: default; }

.dp-more {
  appearance: none;
  min-height: 40px;
  border: 0;
  background: none;
  font-family: var(--font);
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--navy-3);
  text-decoration: underline;
  text-underline-offset: 3px;
  cursor: pointer;
}
.dp-more:hover { color: var(--gold); }

/* ---------------------------------------------------------- messages */

.dp-note {
  margin: 0;
  font-size: 0.76rem;
  line-height: 1.45;
  color: var(--navy-3);
}
.dp-note.is-bad { color: var(--warn-bad); }

.dp-locked {
  margin: 0;
  padding: 10px 12px;
  border-left: 3px solid var(--blue);
  background: var(--wash-blue);
  border-radius: 0 var(--radius-sm) var(--radius-sm) 0;
  font-size: 0.82rem;
  line-height: 1.45;
  color: var(--navy-2);
}
.dp-locked strong { color: var(--navy); }

.dp-flash {
  margin: 0;
  padding: 9px 12px;
  border-radius: var(--radius-sm);
  font-size: 0.82rem;
  font-weight: 600;
}
.dp-flash.is-good {
  background: var(--wash-good);
  color: var(--warn-good);
}
.dp-flash.is-bad {
  background: var(--wash-bad);
  color: var(--warn-bad);
}

/* ------------------------------------------------------------ wider */

/*
 * Beside the board, and never on top of it.
 *
 * This became a centred card from 760px up, on the reasoning that a sheet
 * across the bottom of a 1200px screen is a strip of paper with a hundred
 * pixels of content in it. The reasoning was right and the card was in the
 * wrong place: centred in the viewport it sat squarely over the district it
 * was about, so selecting a district hid the one thing the selection does.
 * On a phone the sheet leaves the board above it and the highlight is there
 * to look at; on a laptop there was nothing to see.
 *
 * The page is a 560px column down the middle, so there are two things to do
 * with the room either side of it, and which one depends on whether there is
 * any.
 */

/*
 * Tablet: still a sheet, but only as wide as the column it belongs to.
 *
 * Centred with margins rather than a transform, because the sheet's entrance
 * animates transform and the two would fight.
 */
@media (min-width: 620px) {
  .dp {
    left: 0;
    right: 0;
    width: min(560px, calc(100vw - 32px));
    margin-inline: auto;
  }
}

/*
 * Wide enough for both: the card goes to the right of the column.
 *
 * 1180px is where half the viewport clears the half-column plus a readable
 * card, which is the width at which putting it beside the board stops being
 * a squeeze. Below that the sheet above is the better answer, because a
 * narrow card wedged against the screen edge is further from the board than
 * a sheet under it.
 */
@media (min-width: 1180px) {
  .dp {
    top: 50%;
    bottom: auto;
    left: calc(50% + 300px);
    right: auto;
    margin-inline: 0;
    width: min(400px, calc(50vw - 320px));
    max-height: min(80vh, 680px);
    transform: translateY(-50%);
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    box-shadow: var(--shadow);
    animation: dp-beside 0.18s ease-out;
  }
  @keyframes dp-beside {
    from { opacity: 0; transform: translate(10px, -50%); }
    to { opacity: 1; transform: translate(0, -50%); }
  }
}

/* Narrow phones: the candidate name is the first thing that can go. */
@media (max-width: 359px) {
  .dp-head { padding-right: 48px; }
  .dp-row { grid-template-columns: 18px minmax(0, 1fr) auto; gap: 8px; }
  .dp-spend { display: none; }
  .dp-name-small { font-size: 0.66rem; }
}

/*
 * Available, this spend, remaining — one line, beside the button.
 *
 * They were three ruled rows in the scrolling half, which put the arithmetic
 * above the fold and the button below it. Pinned together they are read as
 * one thing, which is what they are.
 */
.dp-tally {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 8px;
  padding: 9px 11px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--paper-2);
}
.dp-tally-fig { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.dp-tally-label {
  font-size: 0.55rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--navy-3);
}
.dp-tally-value {
  font-size: 0.86rem;
  font-weight: 700;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.dp-tally-fig.is-spend .dp-tally-value { color: var(--gold); }
.dp-tally-fig.is-after .dp-tally-value { color: var(--navy); }

@media (max-width: 359px) {
  .dp-tally-value { font-size: 0.76rem; }
}

/*
 * Grant, kept visibly apart from the purse.
 *
 * Available cash is a balance; grant potential is a judgement about
 * opportunity. A player who read the second as the first would be spending
 * against a number that was never money, so they do not share a row.
 */
.dp-grant {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
  padding: 10px 12px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--paper);
}
.dp-grant-fig { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.dp-grant-label {
  font-size: 0.56rem;
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--navy-3);
}
.dp-grant-value {
  font-size: 0.9rem;
  font-weight: 700;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.dp-grant-value.is-high { color: var(--warn-good); }
.dp-grant-value.is-medium { color: var(--gold); }
.dp-grant-value.is-low { color: var(--navy-3); }

/* ------------------------------------------------------------------
   The district panel.

   The same panel as a seat, answering the district's own questions: how much
   of it each campaign holds, what the grant needs, and what one sum across
   the whole of it would cost.
   ------------------------------------------------------------------ */

.dp-area .dp-row {
  grid-template-columns: 20px minmax(0, 1fr) auto auto;
}

/* Seats held, and how many of those are settled for good. */
.dp-seats-held {
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/*
 * The seats themselves, when the district is not the right unit.
 *
 * A district spend spreads evenly; taking one particular seat off somebody is
 * a different move, and this is the way to it.
 */
.dp-seats {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 6px;
}

.dp-seat {
  appearance: none;
  min-height: 44px;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  padding: 7px 10px;
  border: 1px solid var(--rule);
  border-radius: var(--radius-sm);
  background: var(--paper-2);
  font-family: var(--font);
  text-align: left;
  cursor: pointer;
  transition: border-color 0.12s ease;
}
.dp-seat:hover { border-color: var(--gold); }
.dp-seat:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; }

/* The same four states the board uses, in the same colours. */
.dp-seat.is-leading,
.dp-seat.is-contested { box-shadow: inset 3px 0 0 var(--party); }
.dp-seat.is-won {
  box-shadow: inset 3px 0 0 var(--party);
  background: var(--paper);
}
.dp-seat.is-open { opacity: 0.78; }

.dp-seat-name {
  font-size: 0.76rem;
  font-weight: 600;
  color: var(--navy);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 100%;
}
.dp-seat-who {
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--navy-3);
}
.dp-seat.is-leading .dp-seat-who,
.dp-seat.is-contested .dp-seat-who,
.dp-seat.is-won .dp-seat-who { color: var(--party); }

@media (max-width: 359px) {
  .dp-seats { grid-template-columns: minmax(0, 1fr); }
}

/* ==========================================================================
   The district campaign
   --------------------------------------------------------------------------
   A district is the unit. Twelve positions, everybody on one track, and a
   button that takes the next one.

   Built to the same pattern as the grant contests: small circular faces,
   short rows, a clean horizontal track. The faces are deliberately small —
   the comparison between four campaigns is the point, and four large cards
   cannot be compared without scrolling.
   ========================================================================== */

/*
  * Short, because the board is the point.
  *
  * The panel is about a district on a map, and a sheet that covers the map to
  * talk about it leaves the player deciding blind. It is a card over the
  * board now, not a screen instead of it.
  */
.dp-area { max-height: 56vh; }

/* ------------------------------------------------------------------ head */

.ab-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 16px 10px;
  border-bottom: 1px solid var(--rule);
}
.ab-head-text { flex: 1 1 auto; min-width: 0; }

.ab-title {
  margin: 0;
  font-family: var(--font-display);
  font-size: 1.42rem;
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.01em;
  color: var(--navy);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ab-close {
  flex: none;
  width: 32px;
  height: 32px;
  display: grid;
  place-items: center;
  border: 1px solid var(--rule);
  border-radius: 50%;
  background: var(--paper-2);
  color: var(--navy-2);
  font-size: 0.82rem;
  cursor: pointer;
}
.ab-close:hover { color: var(--navy); border-color: var(--navy-3); }

/* ------------------------------------------------------------------ body */

.ab-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  padding: 10px 16px 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/*
 * Four figures, on one line.
 *
 * How big the district is, what a move costs, what holding it pays, and how
 * the vote stands. There is no population and no count of ballots because the
 * game holds neither, and an invented figure attached to a real district
 * would be worse than none.
 */
.ab-stats {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 6px;
  padding: 8px 2px 10px;
  border-bottom: 1px solid var(--rule-soft);
}
.ab-stat { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.ab-stat-value {
  font-family: var(--font-display);
  font-size: 0.92rem;
  font-weight: 700;
  color: var(--navy);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ab-stat-label {
  font-size: 0.5rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--navy-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ab-field-title {
  margin: 2px 0 0;
  font-size: 0.56rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--navy-3);
}

/* ------------------------------------------------------------- the rows */

.ab-runners { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.ab-runners li { list-style: none; }

/*
 * One campaign a row: a face, a name, a chart, and a column for the button.
 *
 * The button's column exists on every row whether that row uses it or not.
 * That is what keeps the charts starting and ending at the same place down
 * the panel, and what keeps the button still while the fill beside it grows.
 */
.ab-runner {
  display: grid;
  /*
   * The name column is sized for the longest abbreviation a generated party
   * can have, not the shortest. At 78px a five-letter code came out as "UP…",
   * which is not a name.
   */
  grid-template-columns: 100px minmax(0, 1fr) 58px;
  align-items: center;
  gap: 8px;
  padding: 6px 9px;
  border: 1px solid var(--rule);
  border-left: 3px solid transparent;
  border-radius: 9px;
  background: var(--paper-2);
}

/* The face and the name lead to that campaign's page. Nothing else does. */
.ab-runner-id {
  appearance: none;
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr);
  align-items: center;
  gap: 7px;
  min-width: 0;
  padding: 0;
  border: 0;
  background: none;
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
}
.ab-runner-id:focus-visible { outline: 2px solid var(--blue); outline-offset: 2px; border-radius: 6px; }
.ab-runner-id:hover .ab-runner-code { text-decoration: underline; }
.ab-runner.is-leading { border-left-color: var(--party); }
.ab-runner.is-won { border-left-color: var(--gold); }
.ab-runner.is-you { border-color: var(--gold); border-left-color: var(--gold); }

.ab-runner-face .portrait {
  display: block;
  border-radius: 50%;
  border: 2px solid var(--party);
}

.ab-runner-who { display: flex; flex-direction: column; gap: 0; min-width: 0; }
.ab-runner-code {
  font-family: var(--font-display);
  font-size: 0.84rem;
  font-weight: 700;
  color: var(--navy);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ab-runner-state {
  font-size: 0.46rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--navy-3);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.ab-runner-state.is-leading { color: var(--warn-good); }
.ab-runner-state.is-won { color: var(--gold-deep); }
.ab-runner-state.is-tied { color: var(--navy-2); }

/* ------------------------------------------------------------ the chart */

/*
 * Twelve points, always all twelve, and never a number.
 *
 * The whole chart is on screen at every width: it is a fixed bar divided into
 * twelve, and nothing about it scrolls or scales. How far a campaign has come
 * is the length of the coloured part — printing "7 / 12" beside a bar that
 * already says seven is the same fact twice, and the bar is the one that
 * reads at a glance.
 */
.ab-chart {
  position: relative;
  display: block;
  height: 14px;
  border-radius: 4px;
  background: var(--paper-3);
  border: 1px solid var(--rule);
  overflow: hidden;
}

/*
 * One element, whose width animates.
 *
 * Colouring twelve separate cells would step from one to the next; a single
 * fill moves, and the movement is the feedback when somebody presses Spend.
 */
.ab-chart-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 0;
  background: var(--party);
  transition: width 0.36s cubic-bezier(0.22, 0.61, 0.36, 1);
}

/* The divisions, drawn over the fill so twelve stay countable when it grows. */
.ab-notch {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 1px;
  margin-left: -0.5px;
  background: var(--paper-2);
  opacity: 0.9;
}

/* ----------------------------------------------------------- the button */

/*
 * One word, one place.
 *
 * It says Spend and nothing else: what a move costs is on the strip above,
 * and a price inside a button pressed a dozen times is a number re-read a
 * dozen times. It lives in its own grid column, so it does not move when the
 * fill beside it grows.
 */
.ab-runner-act { display: flex; justify-content: flex-end; }

.ab-spend {
  appearance: none;
  width: 100%;
  padding: 6px 4px;
  border: 1px solid var(--warn-good);
  border-radius: 8px;
  background: var(--warn-good);
  color: #fff;
  font-family: var(--font);
  font-size: 0.58rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  cursor: pointer;
  transition: filter 0.14s ease;
}
.ab-spend:hover:not(:disabled) { filter: brightness(1.08); }
.ab-spend:focus-visible { outline: 2px solid var(--navy); outline-offset: 2px; }
.ab-spend.is-off,
.ab-spend:disabled {
  background: var(--paper-3);
  border-color: var(--rule);
  color: var(--navy-3);
  cursor: default;
}

/*
 * What the charts above it mean, in words.
 *
 * The same line and the same treatment as the grant page's `.gc-item-state`,
 * because it is the same statement about the same kind of contest: leading is
 * not owning, and level at the top is nobody's.
 */
.ab-state {
  margin: 2px 0 0;
  font-size: 0.54rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--navy-3);
}
.ab-state.is-leading { color: var(--warn-good); }
.ab-state.is-owner { color: var(--gold-deep); }
.ab-state.is-tied { color: var(--gold-deep); }

/* Why it is off, when it is. One line, never a paragraph. */
.ab-why {
  margin: 0;
  font-size: 0.64rem;
  color: var(--navy-3);
}
.ab-why.is-rule { color: var(--gold-deep); font-weight: 600; }

.ab-note { margin: 0; font-size: 0.76rem; line-height: 1.45; color: var(--navy-2); }

.ab-flash {
  margin: 0;
  padding: 7px 11px;
  border-radius: 9px;
  font-size: 0.74rem;
  font-weight: 600;
}
.ab-flash.is-good { background: var(--wash-gold); color: var(--gold-deep); }
.ab-flash.is-bad {
  background: color-mix(in srgb, var(--warn-bad) 10%, transparent);
  color: var(--warn-bad);
}

/* --------------------------------------------------------------- narrow */

@media (max-width: 390px) {
  .ab-title { font-size: 1.26rem; }
  .ab-head { padding: 12px 13px 8px; }
  .ab-body { padding: 9px 13px 12px; gap: 9px; }
  .ab-stat-value { font-size: 0.84rem; }
  .ab-stat-label { font-size: 0.46rem; letter-spacing: 0.08em; }
  /*
   * The same three columns, tighter.
   *
   * This used to redefine the row as three columns, which was the old shape —
   * it would have taken the button's column away on exactly the screens the
   * panel is designed for, and the button would have moved as the fill grew.
   */
  .ab-runner { padding: 5px 8px; gap: 6px; grid-template-columns: 92px minmax(0, 1fr) 52px; }
  .ab-runner-id { grid-template-columns: 22px minmax(0, 1fr); gap: 6px; }
  .ab-runner-face .portrait { width: 22px; height: 22px; }
}

@media (prefers-reduced-motion: reduce) {
  .ab-chart-fill { transition: none; }
}
