/* Base styles and color tokens for every region. Module-specific styles may live here,
   in one block per module (marked below), so siblings editing different modules touch
   different hunks. Print styles are print.css's. */

:root {
  color-scheme: light dark;
  --bg: #fbfaf7;
  --surface: #ffffff;
  --text: #1c1b19;
  --muted: #5d5a54;
  --border: #d9d5cc;
  --accent: #1f5fa8;
  --accent-text: #ffffff;
  --banner-bg: #fff3c4;
  --banner-text: #4a3800;
  --banner-border: #e0c25a;
  --error: #a3241b;
  --focus: #1f5fa8;
  --public: #6a3d9a;
  --public-bg: #f7f2fc;
  /* The comparison grid's option colors (#562): the options other than the picks' own,
     which take --accent. No red, so a measure's two answers are never red against blue.
     Orange, green, rose, gold, then cyan and brown (#582, so a field of six named
     candidates doesn't repeat one). No blue either, which would put a blue beside the
     rose. */
  --grid-option-0: #a84a00;
  --grid-option-1: #2c7a1f;
  --grid-option-2: #a3335f;
  --grid-option-3: #7a6200;
  --grid-option-4: #00748a;
  --grid-option-5: #7d5226;
  /* The share card's colors (share-og/lib/render.mjs's light palette and its highlights
     panel), for the highlights editor's picture of it (#667, card-preview.js). The card is
     an image in these colors whatever the page's look or scheme, so the dark scheme keeps
     them. */
  --card-bg: #fbfaf7;
  --card-text: #1c1b19;
  --card-muted: #5d5a54;
  --card-border: #d9d5cc;
  --card-accent: #0a6b66;
  --card-wash: #ddf1ee;
  --card-dot-0: #17a398;
  --card-dot-1: #4f9d5d;
  --card-dot-2: #f5b83d;
  --card-dot-3: #ec7a35;
  --radius: 8px;
  --gap: 1rem;
  --measure: 46rem;
  --font: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #141412;
    --surface: #1d1c1a;
    --text: #ece9e2;
    --muted: #a7a297;
    --border: #3a3833;
    --accent: #8ab8ec;
    --accent-text: #0f1d2e;
    --banner-bg: #3b2f00;
    --banner-text: #fbe7a1;
    --banner-border: #7a6420;
    --error: #ff9a8f;
    --focus: #8ab8ec;
    --public: #c9a7f0;
    --public-bg: #231d2c;
    --grid-option-0: #ffa35c;
    --grid-option-1: #8fd67a;
    --grid-option-2: #f5a3c3;
    --grid-option-3: #e6c94f;
    --grid-option-4: #6fd2e4;
    --grid-option-5: #dcb38a;
  }
}

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 1rem/1.5 var(--font);
}

a {
  color: var(--accent);
}

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

.skip-link {
  position: absolute;
  left: 0.5rem;
  top: -3rem;
  padding: 0.5rem 0.75rem;
  background: var(--accent);
  color: var(--accent-text);
  border-radius: var(--radius);
  z-index: 10;
}

.skip-link:focus {
  top: 0.5rem;
}

.site-header,
.notices,
main,
.site-footer {
  max-width: var(--measure);
  margin: 0 auto;
  padding: 0 var(--gap);
}

.site-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem var(--gap);
  padding-block: 0.75rem;
  border-bottom: 1px solid var(--border);
}

.site-name {
  color: var(--text);
  font-weight: 700;
  text-decoration: none;
}

/* --- auth.js (#account, in the header) --- */
.account {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: flex-end;
  gap: 0.375rem 0.5rem;
  max-width: 28rem;
  margin-left: auto;
  font-size: 0.875rem;
}

/* display: flex would otherwise override the hidden attribute. */
.account[hidden] {
  display: none;
}

/* The rebuilt part of the region; its children lay out as the region's own. */
.account-body {
  display: contents;
}

.account-label {
  color: var(--muted);
  overflow-wrap: anywhere;
}

.account-action {
  padding: 0.25rem 0.75rem;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--accent);
  font: inherit;
  cursor: pointer;
}

.account-action[data-action="google"],
.account-action[data-action="confirm-sign-out"] {
  background: var(--accent);
  color: var(--accent-text);
}

.account-action[aria-disabled="true"] {
  opacity: 0.6;
  cursor: progress;
}

.account-notice,
.account-error {
  flex-basis: 100%;
  margin: 0;
  text-align: right;
}

.account-notice {
  color: var(--muted);
}

.account-error {
  color: var(--error);
}

@media (max-width: 40rem) {
  .account {
    justify-content: flex-start;
    max-width: none;
    margin-left: 0;
  }

  .account-notice,
  .account-error {
    text-align: left;
  }
}

.site-footer {
  margin-top: 3rem;
  padding-block: 1.5rem;
  border-top: 1px solid var(--border);
  color: var(--muted);
  font-size: 0.875rem;
}

/* The sprue.works wordmark in the footer (Mari, on PR #685): the name in bold, its
   ".works" in the text's own weight, kept on one line. */
.wordmark {
  font-weight: 700;
  white-space: nowrap;
}

.wordmark-tld {
  font-weight: 400;
}

.banner {
  margin: var(--gap) 0 0;
  padding: 0.75rem var(--gap);
  border: 1px solid var(--banner-border);
  border-radius: var(--radius);
  background: var(--banner-bg);
  color: var(--banner-text);
}

.mount-error {
  color: var(--error);
}

/* --- look.js (#look, in the header; #396) --- */
.look {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  font-size: 0.875rem;
}

/* display: flex would otherwise override the hidden attribute (no guide open). */
.look[hidden] {
  display: none;
}

.look label {
  color: var(--muted);
}

.look select {
  min-height: 2.25rem;
  padding: 0.25rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font: inherit;
}

/* --- facts.js --- */
.election-name {
  margin: 1.5rem 0 0.25rem;
  font-size: 1.75rem;
  line-height: 1.2;
}

.election-meta {
  margin: 0;
  color: var(--muted);
}

/* home.js's key dates and official links, under the start box (#526). The dates are a
   two-column table, the date then what happens. */
.key-dates {
  width: 100%;
  margin: 0;
  border-collapse: collapse;
}

.key-dates caption,
.official-links-title {
  margin: 0 0 0.5rem;
  color: var(--muted);
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.25;
  text-align: left;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.key-dates th,
.key-dates td {
  padding: 0.5rem 0.75rem 0.5rem 0;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
}

.key-dates thead th {
  padding-top: 0;
  color: var(--muted);
  font-size: 0.875rem;
  font-weight: 600;
}

.key-dates tbody th {
  white-space: nowrap;
  font-weight: 600;
}

.key-dates td {
  padding-right: 0;
}

/* The official links: buttons, quieter than the builder's (#526). */
.official-links-title {
  margin-top: 1.5rem;
}

.official-links {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.official-link {
  display: flex;
  align-items: center;
  height: 100%;
  box-sizing: border-box;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--accent);
  font-weight: 600;
  line-height: 1.3;
  text-decoration: none;
}

.official-link:hover {
  background: var(--accent);
  color: var(--accent-text);
}

.election-more {
  margin: 0.25rem 0 0;
  font-size: 0.9375rem;
}

/* --- home.js (#513) --- */
.home {
  margin-top: 1.5rem;
  display: grid;
  gap: var(--gap);
}

.home-title {
  margin: 0 0 0.5rem;
  font-size: 1.25rem;
  line-height: 1.25;
}

.home-start,
.home-examples,
.home-example-banner {
  padding: var(--gap);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.home-start > :last-child,
.home-examples > :last-child,
.home-example-banner > :last-child {
  margin-bottom: 0;
}

.home-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
}

.home-returning {
  color: var(--muted);
}

.home-button {
  display: inline-block;
  padding: 0.5rem 1rem;
  border-radius: var(--radius);
  background: var(--accent);
  color: var(--accent-text);
  font-weight: 600;
  text-decoration: none;
}

.home-button:hover {
  text-decoration: underline;
}

/* The public guides keep their own color here too, as in compare.js's box. */
.home-examples,
.home-example-banner {
  border-color: var(--public);
  border-left-width: 4px;
  background: var(--public-bg);
}

.home-examples-framing,
.home-example-kicker {
  color: var(--muted);
}

/* A public guide's dates, source and coverage (#546), set like the summary box's lines. */
.home-example-meta {
  margin: 0;
  color: var(--muted);
  font-size: 0.875rem;
  overflow-wrap: anywhere;
}

.home-example-coverage {
  margin-top: 0.125rem;
}

.home-example-kicker {
  margin: 0 0 0.25rem;
  font-size: 0.875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.home-example-list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0.75rem 0 0;
  padding: 0;
  list-style: none;
}

.home-example {
  display: inline-block;
  padding: 0.375rem 0.75rem;
  border: 1px solid var(--public);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--public);
  text-decoration: none;
}

.home-example:hover {
  text-decoration: underline;
}

.home-notice {
  margin: 0;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--banner-border);
  border-radius: var(--radius);
  background: var(--banner-bg);
  color: var(--banner-text);
}

/* --- about.js (#529) --- */
.about {
  margin-top: 1.5rem;
}

.about-title {
  margin: 0 0 0.75rem;
  font-size: 1.5rem;
  line-height: 1.25;
}

.about-pending {
  color: var(--muted);
  font-style: italic;
}

/* --- guide.js --- */
.guide {
  margin-top: 1.5rem;
  padding: var(--gap);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.guide-framing {
  margin-top: 0;
  color: var(--muted);
  font-size: 0.9375rem;
}

.guide > :last-child {
  margin-bottom: 0;
}

.guide-title {
  margin: 0 0 0.5rem;
  font-size: 1.375rem;
  line-height: 1.25;
  overflow-wrap: anywhere;
}

.guide-field {
  margin: 0.75rem 0;
}

.guide-field label {
  display: block;
  font-weight: 600;
}

.guide-title-input,
.guide-style-input,
.pick-reason-input,
.pick-note-input {
  width: 100%;
  margin-top: 0.25rem;
  padding: 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font: inherit;
}

/* The guide's look (#396): a select, as wide as its longest option. */
.guide-style-input {
  display: block;
  width: auto;
  min-height: 2.5rem;
}

.pick-note-input {
  resize: vertical;
}

.guide-hint,
.pick-hint,
.pick-count {
  margin: 0.25rem 0 0;
  color: var(--muted);
  font-size: 0.875rem;
}

.guide-notice,
.pick-notice {
  margin: 0.5rem 0;
  padding: 0.5rem 0.75rem;
  border-left: 0.25rem solid var(--banner-border);
  background: var(--banner-bg);
  color: var(--banner-text);
}

.guide-save-status {
  min-height: 1.5em;
  color: var(--muted);
}

/* Empty until a pick can't be added past the cap (a live region, so it stays in place). */
.guide-limit {
  margin: 0;
}

.guide-limit:not(:empty) {
  margin: 0.5rem 0;
  padding: 0.5rem 0.75rem;
  border-left: 0.25rem solid var(--banner-border);
  background: var(--banner-bg);
  color: var(--banner-text);
}

.guide-save-status.is-error {
  color: var(--error);
}

.guide-action,
.rank-action {
  min-height: 2.25rem;
  padding: 0.25rem 0.75rem;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  background: transparent;
  color: var(--accent);
  font: inherit;
  cursor: pointer;
}

.guide-action[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-text);
}

.rank-action[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
}

.guide-report {
  font-size: 0.875rem;
}

/* "Delete this guide" and its in-page confirmation. */
.guide-delete,
.guide-delete-confirm {
  border-color: var(--error);
  color: var(--error);
}

.guide-confirm {
  margin: 0.75rem 0;
  padding: 0.75rem var(--gap);
  border: 1px solid var(--error);
  border-radius: var(--radius);
}

.guide-confirm p {
  margin: 0.25rem 0;
}

.guide-delete-error {
  color: var(--error);
}

.guide-print {
  margin: 0.75rem 0 0;
}

/* Each pick slot's line for the printed cheat sheet: print.css shows it, on a guide view. */
.pick-print {
  display: none;
}

.my-guides-title {
  margin: 1rem 0 0.25rem;
  font-size: 1rem;
}

.my-guides-list {
  margin: 0;
  padding-left: 1.25rem;
  overflow-wrap: anywhere;
}

/* Share and fork: the owner's link, "Make this my guide", "Copied from …", and the offer
   to copy a guest's guides after a switch to an existing Google account. */
.guide-action[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
}

.guide-share {
  margin: 0.75rem 0;
}

.guide-share label {
  display: block;
  font-weight: 600;
}

.guide-share-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.25rem;
}

.guide-share-url {
  flex: 1 1 16rem;
  min-width: 0;
  padding: 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font: inherit;
}

.guide-share-status,
.guide-fork-status,
.fork-offer-status {
  margin: 0.25rem 0 0;
  color: var(--muted);
  font-size: 0.875rem;
}

.guide-fork-status.is-error {
  color: var(--error);
}

.guide-fork-row {
  margin: 0.75rem 0 0;
}

.guide-provenance {
  margin: 0 0 0.5rem;
  color: var(--muted);
  overflow-wrap: anywhere;
}

.fork-offer {
  margin: 0 0 1rem;
  padding: 0.75rem;
  border: 1px solid var(--accent);
  border-radius: var(--radius);
}

.fork-offer-title {
  margin: 0 0 0.5rem;
  font-size: 1.125rem;
}

.fork-offer-list {
  margin: 0.5rem 0;
  padding-left: 1.25rem;
  overflow-wrap: anywhere;
}

.fork-offer-choices {
  padding-left: 0;
  list-style: none;
}

.fork-offer-actions {
  margin: 0.75rem 0 0;
}

/* The display order (#357): the note on a guide whose author set its order, and the
   owner's editor (guide-order.js), which reuses the ranking editor's rows and buttons. */
.guide-order-note {
  margin: 0 0 0.5rem;
  color: var(--muted);
}

.guide-order {
  margin: 0.75rem 0;
}

.guide-order > summary,
.order-options > summary {
  min-height: 2.25rem;
  cursor: pointer;
}

.guide-order > summary {
  font-weight: 600;
}

.order-list {
  margin: 0.5rem 0;
  padding: 0;
  list-style: none;
}

/* One per tab and category (#512): a contest moves only within its own. */
.order-section-title {
  margin: 0.75rem 0 0;
  font-weight: 600;
}

.order-options {
  margin: 0 0 0.25rem 1rem;
}

.order-options > summary {
  color: var(--muted);
}

.order-actions {
  margin: 0.5rem 0 0;
}

/* The highlights (#667; the share card's picks, #561): the owner's editor (guide-card.js),
   a line in #guide whose button opens it in a modal dialog (the chooser's box, below), with
   the order editor's rows and buttons. The rule opens the first highlight after the share
   card's last line, so it says which ones make the card. */
.guide-card {
  margin: 0.75rem 0;
}

.card-line {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.75rem;
  margin: 0;
}

.card-status {
  color: var(--muted);
}

.card-dialog-title {
  margin: 0 0 0.25rem;
  font-size: 1.125rem;
}

/* The previews (#667): beside the list on a wide screen, the dialog wider for them; on a
   phone, in their own dialog over this one, which the Preview button opens. */
.card-previews-inline {
  display: none;
}

.card-preview-caption {
  margin: 0.75rem 0 0.375rem;
  color: var(--muted);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.card-previews > .card-preview-caption:first-child {
  margin-top: 0;
}

.card-preview-summary > .guide-highlights {
  margin: 0;
}

@media (min-width: 56rem) {
  .card-dialog:not(.card-preview-dialog) {
    width: min(64rem, calc(100vw - 2rem));
  }

  .card-dialog-columns {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 1.5rem;
    align-items: start;
  }

  .card-previews-inline {
    display: block;
    position: sticky;
    top: 0;
  }

  .card-preview-open {
    display: none;
  }
}

/* The share card's picture: 1200×630 laid out in container units (1200px is 100cqw), at
   the renderer's coordinates (#675: the site line, the title, the picks' 2×4 grid on the
   highlights panel), so it scales with its box as the image does. */
.card-picture {
  container-type: inline-size;
  position: relative;
  aspect-ratio: 1200 / 630;
  overflow: hidden;
  border: 1px solid var(--card-border);
  border-radius: var(--radius);
  background: var(--card-bg);
  color: var(--card-text);
  font-family: var(--font);
}

.card-picture::before {
  content: "";
  position: absolute;
  inset: 0 auto 0 0;
  width: 1.333cqw;
  background: var(--card-accent);
}

.card-picture > * {
  margin: 0;
  margin-inline: 6cqw;
}

.card-picture-site {
  overflow: hidden;
  padding-top: 3.75cqw;
  color: var(--card-accent);
  font-size: 2.5cqw;
  font-weight: 700;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.card-picture-title {
  display: -webkit-box;
  margin-top: 1.5cqw;
  overflow: hidden;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  font-size: 4cqw;
  font-weight: 700;
  line-height: 1.208;
}

.card-picture-title-single {
  -webkit-line-clamp: 1;
}

.card-picture-lines {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 2cqw;
  margin-top: 1.5cqw;
  padding: 0.667cqw 2cqw 0;
  border-top: 0.5cqw solid transparent;
  border-image: linear-gradient(90deg, var(--card-dot-0), var(--card-dot-1) 35%, var(--card-dot-2) 65%, var(--card-dot-3)) 1;
  border-radius: 1cqw;
  background: var(--card-wash);
  list-style: none;
}

.card-picture-lines > li {
  display: grid;
  grid-template-columns: 2.5cqw minmax(0, 1fr);
  align-content: center;
  height: 7.167cqw;
  white-space: nowrap;
}

.card-picture-lines > li::before {
  content: "";
  grid-row: 1;
  align-self: center;
  width: 1.5cqw;
  height: 1.5cqw;
  border-radius: 50%;
  background: var(--card-dot-0);
}

.card-picture-lines > li:nth-child(4n + 2)::before {
  background: var(--card-dot-1);
}

.card-picture-lines > li:nth-child(4n + 3)::before {
  background: var(--card-dot-2);
}

.card-picture-lines > li:nth-child(4n + 4)::before {
  background: var(--card-dot-3);
}

.card-picture-label,
.card-picture-choice {
  grid-column: 2;
  overflow: hidden;
  text-overflow: ellipsis;
}

.card-picture-label {
  font-size: 2.167cqw;
  font-weight: 700;
}

.card-picture-choice {
  color: var(--card-accent);
  font-size: 2.5cqw;
}

.card-picture-more {
  margin-top: 1.333cqw;
  color: var(--card-muted);
  font-size: 2.167cqw;
}

/* Done stays in reach on a phone, where the dialog scrolls: held at its bottom edge. */
.card-dialog-actions {
  position: sticky;
  bottom: calc(-1 * var(--gap));
  display: flex;
  justify-content: flex-end;
  gap: 0.5rem;
  margin: 1rem calc(-1 * var(--gap)) calc(-1 * var(--gap));
  padding: 0.75rem var(--gap);
  border-top: 1px solid var(--border);
  background: var(--surface);
}

/* The chooser: the label on its own line, then the select, as wide as the dialog allows
   and no wider (a long contest title used to push it past a phone's edge), and Highlight. */
.card-add {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0.75rem 0 0;
}

.card-add > label {
  flex: 1 0 100%;
  font-weight: 600;
}

.card-select {
  flex: 1 1 12rem;
  min-width: 0;
  max-width: 100%;
  min-height: 2.5rem;
  padding: 0.375rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font: inherit;
  text-overflow: ellipsis;
}

.card-add > .guide-hint {
  flex: 1 0 100%;
}

.card-preview p,
.card-preview-list {
  margin: 0.5rem 0;
}

.card-skipped {
  color: var(--muted);
}

.card-add .guide-hint {
  margin: 0;
}

.card-rule {
  margin: 0.25rem 0 0.5rem;
  padding-top: 0.25rem;
  border-top: 2px dashed var(--border);
  color: var(--muted);
  font-size: 0.875rem;
}

.card-after-rule > .card-rule {
  flex: 1 0 100%;
}

/* The guide's highlights summary (#667, guide-highlights.js), in ballot.js's slot under
   "The ballot": a card with an accent edge, each race a row with an accent dot (no number:
   the list keeps the author's order without counting it out), its name, and the pick in the
   accent color, the whole row a link to its card. The Default
   look adds its colors (its block, at the end). Print leaves it out (print.css): every pick
   is already on the sheet. */
.ballot-highlights:empty {
  display: none;
}

.guide-highlights {
  margin: 0 0 var(--gap);
  padding: 0.75rem 1rem 0.5rem;
  border: 1px solid var(--accent);
  border-left-width: 0.375rem;
  border-radius: var(--radius);
  background: var(--surface);
}

.guide-highlights-title {
  margin: 0 0 0.5rem;
  color: var(--accent);
  font-size: 1.0625rem;
  font-weight: 800;
}

.guide-highlights-title::before {
  content: "★ ";
}

.guide-highlights-list {
  margin: 0;
  padding: 0;
  list-style: none;
}

.guide-highlights-list > li {
  border-top: 1px solid var(--border);
}

.guide-highlights-list > li:first-child {
  border-top: 0;
}

.guide-highlight {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 0.125rem 0.5rem;
  padding: 0.5rem 0;
  color: var(--text);
  text-decoration: none;
  overflow-wrap: anywhere;
}

.guide-highlight::before {
  content: "";
  flex: none;
  align-self: center;
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 999px;
  background: var(--accent);
}

.guide-highlight-label {
  font-weight: 700;
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
}

.guide-highlight:hover .guide-highlight-label {
  text-decoration-thickness: 2px;
}

.guide-highlight-choice {
  color: var(--accent);
  font-weight: 700;
}

/* A highlight's reason and note, under its line and lined up with its name (past the
   dot), as the race's card shows them. */
.guide-highlight-text {
  margin: -0.25rem 0 0.5rem 1.25rem;
}

.guide-highlight-text + .guide-highlight-text {
  margin-top: -0.25rem;
}

.guide-highlight-text .pick-reason,
.guide-highlight-text .pick-note {
  margin: 0;
}

/* A card a summary link landed on (guide.js gives it tabindex -1 and focuses it): a ring
   showing where the link went, by mouse too, and room above it under the page's top. */
.contest[tabindex="-1"] {
  scroll-margin-top: 1rem;
}

.contest[tabindex="-1"]:focus {
  outline: 3px solid var(--focus);
  outline-offset: 2px;
}

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Each contest's pick slot: the owner's editor, or the guide's pick for everyone else, set
   off from the contest. On a guide view the slot also holds its print-only line
   (.pick-print, hidden on screen), so the separator goes on what shows, never the slot.
   The pick is what a guide is for, so its rule is heavier than the details' (#528). */
.contest-pick > :not(.pick-print) {
  margin-top: 0.75rem;
  padding-top: 0.75rem;
  border-top: 2px solid var(--border);
}

.pick-choices-editor {
  margin: 0;
  padding: 0;
  border: 0;
}

.pick-choices-editor legend {
  padding: 0;
  font-size: 1.0625rem;
  font-weight: 700;
}

/* The picked option reads as the guide's pick (#528); #427's states never do. */
.pick-option:has(input:checked:not([value^=":"])) > label {
  font-weight: 700;
}

.pick-option {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  min-height: 2.25rem;
}

.pick-option input {
  width: 1.125rem;
  height: 1.125rem;
  margin: 0;
  accent-color: var(--accent);
}

/* The chooser's "any of these" checkbox (#537), at the row's end. */
.pick-any {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  margin-left: auto;
  color: var(--muted);
  font-size: 0.875rem;
  white-space: nowrap;
}

.rank-list {
  margin: 0.5rem 0;
  padding: 0;
  list-style: none;
}

.rank-item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.25rem 0.5rem;
  padding: 0.25rem 0;
  border-bottom: 1px solid var(--border);
}

.rank-label {
  font-weight: 600;
  overflow-wrap: anywhere;
}

.rank-buttons {
  display: flex;
  gap: 0.25rem;
}

/* "In any order" groups (#480): the toggle under each ranked candidate but the last, and a
   rule down the left of a group's rows. */
.rank-link-row {
  flex-basis: 100%;
}

.rank-link {
  min-height: 1.75rem;
  padding: 0 0.5rem;
  font-size: 0.875rem;
}

.rank-action.rank-link[aria-pressed="true"] {
  background: var(--accent);
  color: var(--accent-text);
}

.rank-grouped {
  padding-left: 0.5rem;
  border-left: 3px solid var(--accent);
}

.rank-pool {
  display: flex;
  flex-wrap: wrap;
  gap: 0.375rem;
  margin: 0.5rem 0;
  padding: 0;
  list-style: none;
}

.rank-empty,
.rank-full {
  margin: 0.25rem 0;
  color: var(--muted);
}

.pick-field {
  margin-top: 0.75rem;
}

.pick-field label,
.pick-note-field summary {
  font-weight: 600;
}

.pick-note-field {
  margin-top: 0.75rem;
}

.pick-note-field summary,
.pick-note summary {
  cursor: pointer;
  color: var(--accent);
}

.pick-heading {
  margin: 0;
  color: var(--text);
  font-size: 0.8125rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* The pick alone, big and in the accent color (#528): its "This guide's pick" heading is
   only read out. #427's states (No endorsement, Undecided) stay muted below. */
.pick-choices {
  margin: 0;
  color: var(--accent);
  font-size: 2rem;
  font-weight: 700;
  line-height: 1.2;
  overflow-wrap: anywhere;
}

.pick-ranking {
  font-size: 1.375rem;
  display: flex;
  flex-wrap: wrap;
  gap: 0 1.25rem;
  padding: 0;
  list-style: none;
}

/* No endorsement, No recommendation and Undecided: muted, and in italics, as the compare
   slots and the grid show them (.compare-none), for a visitor guide's pick and a public
   guide's position alike (#609: one read-only view). */
.pick-skip {
  color: var(--muted);
  font-size: 1.25rem;
  font-weight: 600;
  font-style: italic;
}

/* A vote-for-N pick's "(vote for 3)" (picks.js's .pick-seats, #609): whole on a line, so its
   "3)" never sits alone under "(vote for". inline-block still wraps inside it if it's wider
   than the line (a position's "; against …" after it). */
.pick-seats {
  display: inline-block;
}

.pick-reason {
  margin: 0.25rem 0 0;
  overflow-wrap: anywhere;
}

.pick-note {
  margin-top: 0.5rem;
}

.pick-note-body {
  overflow-wrap: anywhere;
}

.pick-note-body > :first-child {
  margin-top: 0.5rem;
}

.pick-note-body > :last-child {
  margin-bottom: 0;
}

/* Pick model v1.2 (#427): a multi-seat or ranked contest's candidates radio, which opens
   its chooser dialog, with Edit beside it once it has a pick; the dialog; the Hidden
   radio's line; the Undo; and a hidden contest. In the editor a hidden contest is
   collapsed to its title and its pick slot, the radio group with Hidden checked: the rest
   of ballot.js's article (meta, options, the compare slot, the details) is hidden around it. A
   browser without :has() shows the whole contest, with the group. */
.pick-candidates-label {
  overflow-wrap: anywhere;
}

/* The picked candidates are the contest's state when their radio is checked. */
.pick-candidates input:checked + .pick-candidates-label {
  font-weight: 600;
}

/* A small link-like button, so the radio row reads as one choice. */
.pick-edit {
  padding: 0 0.25rem;
  border: 0;
  background: none;
  color: var(--accent);
  font: inherit;
  text-decoration: underline;
  cursor: pointer;
}

.pick-chooser,
.card-dialog {
  width: min(32rem, calc(100vw - 2rem));
  max-height: calc(100vh - 2rem);
  padding: var(--gap);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
}

.pick-chooser::backdrop,
.card-dialog::backdrop {
  background: rgb(0 0 0 / 0.45);
}

.pick-chooser-title {
  margin: 0 0 0.25rem;
  font-size: 1.125rem;
}

.pick-chooser-actions {
  display: flex;
  gap: 0.5rem;
  margin: 1rem 0 0;
}

.pick-chooser-apply:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

/* The Hidden radio's line, under its label (past the radio). */
.pick-option:has(> .pick-option-hint) {
  flex-wrap: wrap;
  row-gap: 0;
}

.pick-option-hint {
  flex-basis: 100%;
  margin: -0.375rem 0 0.25rem calc(1.125rem + 0.5rem);
}

/* The Undo, beside the group. Without one it's empty and takes no room, but stays
   rendered (never display: none): a live region outside the accessibility tree isn't
   announced when it fills. */
.pick-undo {
  margin: 0.25rem 0 0;
  color: var(--muted);
  font-size: 0.875rem;
}

.pick-undo:empty {
  margin: 0;
}

.pick-undo-button {
  padding: 0 0.25rem;
  border: 0;
  background: none;
  color: var(--accent);
  font: inherit;
  font-weight: 600;
  text-decoration: underline;
  cursor: pointer;
}

.contest:has(> .contest-pick > .pick-editor-hidden) > :not(.contest-title, .contest-pick) {
  display: none;
}

.contest:has(> .contest-pick > .pick-editor-hidden) > .contest-title {
  color: var(--muted);
}

/* One list of the options per card (#520): with the owner's editor in the slot, its pick
   list is the card's list of the options, each with the candidate's party, so ballot.js's
   plain list and its order note (which the editor carries instead) are hidden. A read-only
   view keeps them. A browser without :has() shows both lists, as before #520. */
.contest:has(> .contest-pick > .pick-editor) > :is(.contest-options, .contest-order-note) {
  display: none;
}

/* In a picked row a look may color the row or its label (a chip, a sticker): the party
   takes that color, so it never sits muted on a background it wasn't checked against. */
.pick-option:has(input:checked:not([value^=":"])) .party {
  color: inherit;
}

/* A multi-seat or ranked contest's candidates, under its candidates radio, the picked ones
   marked with their rank or a check. */
.pick-candidate-list {
  margin: 0 0 0.375rem calc(1.125rem + 0.5rem);
  padding: 0;
  list-style: none;
  overflow-wrap: anywhere;
}

.pick-candidate-list .is-picked {
  font-weight: 600;
}

.pick-candidate-mark {
  display: inline-block;
  min-width: 1.75em;
}

.pick-order-note {
  margin: 0.25rem 0 0;
}

/* A hidden contest collapses to its radio group: no candidate list or order note. */
.pick-editor-hidden :is(.pick-candidate-list, .pick-order-note) {
  display: none;
}

.guide-hide-status {
  margin: 0.25rem 0 0;
  color: var(--muted);
  font-size: 0.875rem;
}

/* --- districts.js and district-lookup.js (#districts, "Find your districts"; #563) ---
   A plain box like #guide-compare's: the visitor guides' styling, never --public. */
.districts {
  margin-top: 1.5rem;
  padding: var(--gap);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.district-lookup > :last-child {
  margin-bottom: 0;
}

.district-lookup > summary {
  cursor: pointer;
  font-weight: 600;
}

.district-field {
  margin: 0.75rem 0 0;
}

.district-field label {
  display: block;
  font-weight: 600;
}

.district-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.25rem;
}

.district-input {
  flex: 1 1 16rem;
  min-width: 0;
  padding: 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font: inherit;
}

.district-status,
.district-hide-status {
  margin: 0.25rem 0 0;
  color: var(--muted);
  font-size: 0.875rem;
}

.district-status:empty,
.district-hide-status:empty {
  margin: 0;
}

.district-status.is-error {
  color: var(--error);
}

.district-choice-list {
  margin: 0.5rem 0 0;
  padding: 0;
  list-style: none;
}

.district-choice-list li + li {
  margin-top: 0.25rem;
}

.district-where {
  margin: 0.75rem 0 0.25rem;
  font-weight: 600;
}

.district-list {
  display: grid;
  grid-template-columns: max-content 1fr;
  gap: 0.25rem 1rem;
  margin: 0.25rem 0 0;
}

.district-list dt {
  font-weight: 600;
}

.district-list dd {
  margin: 0;
}

.district-list dd[data-status="unsure"],
.district-list dd[data-status="none"] {
  color: var(--muted);
}

@media (max-width: 30rem) {
  .district-list {
    grid-template-columns: 1fr;
  }

  .district-list dd {
    margin-bottom: 0.25rem;
  }
}

/* --- ballot.js --- */
.banner-draft {
  border-width: 2px 2px 2px 0.5rem;
}

.banner-draft p {
  margin: 0;
}

.banner-draft .banner-title {
  font-size: 1.125rem;
  font-weight: 700;
}

.ballot-title {
  margin: 0;
  font-size: 1.25rem;
}

.ballot-title {
  margin-top: 1.5rem;
}

.ballot-status {
  margin: 0.25rem 0 0;
  color: var(--muted);
}

.ballot-status {
  margin-bottom: var(--gap);
}

/* The tabs and categories (#512). The selected tab is marked by its weight, its text color
   and a bar under it, never by color alone. */
.ballot-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0 0.25rem;
  margin: 0 0 var(--gap);
  border-bottom: 1px solid var(--border);
}

.ballot-tab {
  margin: 0 0 -1px;
  padding: 0.5rem 1rem;
  border: 0;
  border-bottom: 3px solid transparent;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 1.0625rem;
  cursor: pointer;
}

.ballot-tab[aria-selected="true"] {
  border-bottom-color: var(--accent);
  color: var(--text);
  font-weight: 700;
}

.ballot-tab-count {
  font-weight: 400;
}

/* The categories inside a tab: a second, wrapping row of outlined tabs. The selected one has
   a thicker border and bold text, never color alone. */
.ballot-subtabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0 0 var(--gap);
}

.ballot-subtabs .ballot-tab {
  margin: 0;
  padding: 0.375rem 0.75rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  font-size: 0.9375rem;
}

.ballot-subtabs .ballot-tab[aria-selected="true"] {
  border: 2px solid var(--accent);
  padding: calc(0.375rem - 1px) calc(0.75rem - 1px);
}

/* Each tab's "show together" checkbox (#574), in both rows. In the page it's a group after its
   row of tabs (a tab can't hold another control), and with CSS anchor positioning it's drawn
   inside its tab's pill, before its name, in the room the tab keeps for it: the label covers
   the tab's left edge to the end of that room, the tab's full height, so a click there checks
   the box and a click on the name picks the tab. Without anchor positioning it's a row of
   named checkboxes under the tabs. */
.ballot-sections {
  position: relative;
}

.ballot-tab-checks {
  display: flex;
  flex-wrap: wrap;
  gap: 0 1rem;
  margin: calc(-0.5 * var(--gap)) 0 var(--gap);
  font-size: 0.9375rem;
}

.ballot-tab-check,
.ballot-select-all {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  min-height: 1.75rem;
  cursor: pointer;
}

.ballot-tab-check input,
.ballot-select-all input {
  margin: 0;
  accent-color: var(--accent);
  cursor: pointer;
}

/* "Select all": every category together. Without anchor positioning it ends the row of
   checkboxes under the top tabs. */
.ballot-select-all {
  margin-inline-start: auto;
  color: var(--muted);
}

/* A ring of the page's background round each box, so the box, checked or not, and its focus
   ring outside that ring keep their contrast on any tab's color, in every look
   (--accent and --focus on --bg are base pairings). */
:is(.ballot-tab-check, .ballot-select-all) input {
  border-radius: 2px;
  box-shadow: 0 0 0 2px var(--bg);
}

:is(.ballot-tab-check, .ballot-select-all) input:focus-visible {
  outline-offset: 2px;
}

.ballot-tab-room {
  display: none;
}

@supports (anchor-name: --a) and (top: anchor(top)) {
  .ballot-tab-checks {
    margin: 0;
  }

  /* ballot.js anchors the label to its tab (position-anchor) and its right edge to the
     tab's room (an inline right: anchor(…)). */
  .ballot-tab-check {
    position: absolute;
    top: anchor(top);
    bottom: anchor(bottom);
    left: anchor(left);
    z-index: 1;
    justify-content: center;
    min-height: 0;
  }

  .ballot-tab-check-text {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  /* "Select all" at the right end of the top row (ballot.js's inline right: anchor(…)), centred on the row's first tab (its
     position-anchor), in the room the row keeps for it while it's there (:root, so it
     outranks a look's padding for the row). */
  .ballot-select-all {
    position: absolute;
    top: anchor(top);
    bottom: anchor(bottom);
    z-index: 1;
    box-sizing: border-box;
    max-width: 8.5rem; /* the room the row keeps: every look's font fits on one line (#669); a wider one wraps rather than overlap a tab */
    padding-inline: 0.5rem;
  }

  :root .ballot-sections > .ballot-tabs[data-select-all] {
    padding-inline-end: 8.5rem;
  }

  /* On a phone it takes a line of its own under the top row, at its right end, on one line,
     so State and Local get the whole row (#669: squeezed beside them, it wrapped to
     "Select / all"). It's in the row's group, which follows the row, so it flows there,
     and stays last in the Tab order. */
  @media (max-width: 40rem) {
    .ballot-select-all {
      position: static;
      max-width: none;
      padding-inline: 0;
      white-space: nowrap;
    }

    :root .ballot-sections > .ballot-tabs[data-select-all] {
      padding-inline-end: 0;
    }

    .ballot-sections > .ballot-tab-checks:has(> .ballot-select-all) {
      margin: calc(-1 * var(--gap)) 0 calc(0.25 * var(--gap));
    }

    /* A tab with a box whose count says "undecided" (the editor's): its box, then its name
       over its count, the box centred on the two lines together, never between them (#669).
       A tab with no box (an empty one) keeps the count under its name as a block. */
    .ballot-tab:has(> .ballot-tab-room):has(> .ballot-tab-count[data-undecided]) {
      display: grid;
      grid-template-columns: auto auto;
      justify-content: start;
      align-items: center;
      text-align: start;
    }

    .ballot-tab:has(> .ballot-tab-room):has(> .ballot-tab-count[data-undecided]) > .ballot-tab-room {
      grid-row: 1 / span 2;
    }

    .ballot-tab:has(> .ballot-tab-room):has(> .ballot-tab-count[data-undecided]) > .ballot-tab-count {
      grid-column: 2;
    }
  }

  /* The room a tab keeps for its checkbox, first in its text, so every look's padding and
     borders (a selected tab's thicker one included) stay as they are. */
  .ballot-tab-room {
    display: inline-block;
    width: 1.375rem;
  }
}

/* While both category rows show, each says whose it is. */
.ballot-subrow-name {
  display: none;
}

.ballot-sections[data-view="together"] .ballot-subrow-name {
  display: block;
  margin: 0 0 0.25rem;
  color: var(--muted);
  font-size: 0.875rem;
  font-weight: 700;
}

/* While categories show together, each one's heading names it, since no tab does. Screen
   only: print.css lays the headings out the same whatever is checked. */
@media screen {
  .ballot-sections[data-view="together"] .ballot-group-title {
    display: block;
    margin: 0 0 0.5rem;
    font-size: 1.125rem;
  }

  .ballot-sections[data-view="together"] :is(.ballot-panel, .ballot-group):not([hidden]) ~ :is(.ballot-panel, .ballot-group):not([hidden]) {
    margin-top: calc(2 * var(--gap));
  }
}

/* On a touch screen or a phone-width one, every tab, both rows, and every checkbox is at least
   a 44px target. */
@media (pointer: coarse), (max-width: 40rem) {
  .ballot-tab,
  .ballot-tab-check,
  .ballot-select-all {
    min-height: 2.75rem;
  }
}

/* On a phone, State and Local share their row instead of stacking, so the row still reads as
   tabs in every look. A bare count ("State (40)") stays on the name's line (Mari, on PR #577:
   "if there's "undecided" wrapping is fine, if it's just "state (40)" it shouldn't wrap"). A
   count with "undecided" in it (the editor's), in both rows, goes on its own line under the
   name, smaller and lighter than the name, in the body's type rather than a look's display
   caps, and on one line: "(40 · 12 left)". The word "undecided" stays in the page for screen
   readers and copied text, visually hidden, and the empty, aria-hidden span after it draws
   "left" (#669). :root so these outrank a look's own rules for the tabs. */
@media (max-width: 40rem) {
  :root .ballot-tabs > .ballot-tab {
    flex: 1 1 0;
    padding-inline: 0.5rem;
  }

  .ballot-tabs .ballot-tab-count:not([data-undecided]) {
    white-space: nowrap;
  }

  :root .ballot-tab-count[data-undecided] {
    display: block;
    font-family: var(--font);
    font-size: 0.8125rem;
    font-weight: 400;
    letter-spacing: normal;
    line-height: 1.3;
    text-transform: none;
    white-space: nowrap;
  }

  /* .visually-hidden's rules. */
  .ballot-tab-count[data-undecided] .ballot-tab-word {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  .ballot-tab-count[data-undecided] .ballot-tab-short::before {
    content: "left";
  }
}

/* The heading for each category: print's (print.css), and on screen only while categories
   show together (#574, the screen block above); otherwise its tab names it. */
.ballot-group-title {
  display: none;
}

.ballot-empty {
  color: var(--muted);
}

.contests {
  margin: 0;
  padding: 0;
  list-style: none;
}

.contest {
  margin: 0 0 var(--gap);
  padding: var(--gap);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

/* Wide screens (#517): the ballot (its heading, tabs and cards) leaves the text measure for
   a wider band, centred under the rest of the page, and each category's cards flow into as
   many columns of at least --contest-min as fit (two from 60rem, three from 83rem).
   A grid, not CSS columns: the cards read and tab left to right, row by row, in ballot
   order, and a card that grows while it's edited never pushes another into a different
   column. Screen only, so print.css's sheet is unchanged. The band leaves a gutter of
   about --gap beside a classic scrollbar. */
@media screen and (min-width: 60rem) {
  /* The comparison grid (#562) takes the same band, so four columns fit beside the contest
     and the picks without scrolling. */
  .ballot,
  .guide-grid {
    --ballot-width: min(100vw - 2 * var(--gap) - 1rem, 90rem);
    width: var(--ballot-width);
    margin-left: calc((100% - var(--ballot-width)) / 2);
  }

  .contests {
    --contest-min: 26rem;
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(var(--contest-min), 1fr));
    align-items: start;
    gap: var(--gap);
  }

  .contests > li > .contest {
    margin-bottom: 0;
  }
}

.contest-title {
  margin: 0;
  font-size: 1.125rem;
  overflow-wrap: anywhere;
}

.contest-meta,
.contest-order-note,
.party {
  color: var(--muted);
}

.contest-meta,
.contest-order-note {
  margin: 0.25rem 0 0;
  font-size: 0.875rem;
}

/* The level and district at the card's top right, across from the title (#528). The
   title makes its own box beside them (a look that sets it inline wraps around them
   instead), and nothing below rides up beside a long one. */
.contest > .contest-meta {
  float: right;
  max-width: 50%;
  margin: 0.125rem 0 0 0.75rem;
  text-align: right;
}

.contest-title {
  display: flow-root;
}

.contest > .contest-meta ~ :not(.contest-title) {
  clear: both;
}

.contest-summary,
.contest-instruction {
  margin: 0.5rem 0 0;
}

.contest-instruction {
  font-weight: 600;
}

.contest-options {
  margin: 0.5rem 0 0;
  padding-left: 1.25rem;
}

/* The card's disclosure arrow, at its top right, before the level and district (#537): it
   opens the whole card, its details and the editor's advanced pick options. */
.contest > .contest-more {
  float: right;
  width: 1.75rem;
  height: 1.75rem;
  margin: -0.125rem -0.25rem 0 0.5rem;
  padding: 0;
  border: 1px solid transparent;
  border-radius: 0.25rem;
  background: none;
  color: var(--accent);
  cursor: pointer;
}

.contest > .contest-more::before {
  content: "";
  display: block;
  width: 0.5rem;
  height: 0.5rem;
  margin: 0 auto;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: translateY(-0.125rem) rotate(45deg);
  transition: transform 0.15s;
}

.contest > .contest-more[aria-expanded="true"]::before {
  transform: translateY(0.125rem) rotate(-135deg);
}

.contest > .contest-more:hover {
  border-color: var(--border);
}

@media (prefers-reduced-motion: reduce) {
  .contest > .contest-more::before {
    transition: none;
  }
}

.contest > .contest-more ~ :not(.contest-title, .contest-meta) {
  clear: both;
}

/* The contest's description and sources, last in the card and closed by default (#528). */
.contest-details {
  margin-top: 0.75rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--border);
}

/* A card whose details hold nothing but the public guides' slot (#543), and no guide covers
   the contest: the arrow would open nothing, so it goes. A candidate contest keeps it for
   the editor's advanced options, and its details always hold the candidate list. */
.contest:not(:has(> .contest-details > :not(.contest-compare-public:empty))) > .contest-more {
  display: none;
}

/* The public guides' positions come first in the details (#543), right under their rule. */
.contest-details > .contest-compare-public:not(:empty) {
  margin-top: 0.25rem;
}

/* The editor's advanced pick options ("Either of…", "any of these", #537) show in an open
   card, or while the pick uses them. */
.contest:not(.contest-open) .pick-advanced:not(.is-in-use) {
  display: none;
}

/* Viewing a guide (its read-only pick in the slot), the candidate list lives in the
   details instead of above the pick (#528), and a measure's Yes/No list, which the pick
   already answers, goes. A public guide's page does the same: its position is in the pick
   slot, as a .pick-view (#607). Everywhere else (the editor, no Firebase) the list stays
   on top and the details' copy is hidden. A browser without :has() keeps the list on top. */
.contest-details-candidates {
  display: none;
}

/* Viewing a guide, the pick is the card's headline right under the title, so no rule
   sets it off (Mari, 2026-10-10). Three classes outrank every look's two-class rule. */
.contest > .contest-pick > .pick-view {
  padding-top: 0;
  border-top: 0;
}

.contest:has(> .contest-pick > .pick-view) > :is(.contest-options, .contest-order-note) {
  display: none;
}

.contest:has(> .contest-pick > .pick-view) > .contest-details .contest-details-candidates {
  display: block;
}

/* A public guide's position as its page's pick (#607), drawn by picks.js's one read-only
   view (#609), so a ranking is the view's list (.pick-ranking) and No endorsement its muted
   italics. The organization's name, which the visitor guide's view hides as "This guide's
   pick", shows over it, with the chip after it (below); the chip's tooltip hangs from that
   line, not the whole view, which may go on to a reminder or a note. */
/* #607's one-line public ranking, kept for a page that mixes files across a deploy: an
   older compare.js still draws it. Remove it a deploy or two after #609. */
.pick-public-ranking {
  font-size: 1.375rem;
  white-space: pre-wrap;
}

.pick-public-source {
  position: relative;
  margin-bottom: 0.25rem;
  overflow-wrap: anywhere;
}

/* In the public guides' purple (Mari, on PR #608: "go back to purple here"), so the position
   reads as the organization's, like its box everywhere else: the --public box's rule and
   tint, and its words in --public. :root and the extra class outrank the Default look's
   tinted panel for a visitor guide's pick (.contest-pick > .pick-view, below). No endorsement
   and No recommendation keep the muted state. */
:root .contest > .contest-pick > .pick-view.pick-view-public {
  padding: 0.5rem 0.75rem;
  border-left: 0.25rem solid var(--public);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--public-bg);
}

.pick-view-public > .pick-choices:not(.pick-skip) {
  color: var(--public);
}

/* The chip goes on the organization's line, after its name (Mari, on PR #608: "move the
   chips onto the 'DSA SF' line, so that the actual endorsement/recommendation is less
   likely to wrap"), in the purple, in the chip's own case rather than the heading's
   capitals, centered on the line. */
.pick-public-source .compare-tag-wrap {
  margin-left: 0.375rem;
  vertical-align: middle;
}

.pick-public-source .compare-tag {
  color: var(--public);
  letter-spacing: normal;
  text-transform: none;
}

.contest-details-options {
  margin: 0.5rem 0 0;
  padding-left: 1.25rem;
}

.contest-sources {
  margin: 0.5rem 0 0;
  padding: 0;
  list-style: none;
  font-size: 0.875rem;
  color: var(--muted);
  overflow-wrap: anywhere;
}

/* --- compare.js (#public-guides, and each contest's [data-slot="compare"]) ---
   A public voter guide is an organization's published positions, never a visitor's: its
   own color, ruled and tinted, sets it apart from the visitor guides (#guide, the pick
   slots, and the compared guide's .compare-visitor block in the slot), so neither passes
   for the other: the --public tokens in :root. Matching and differing look alike apart
   from their words: the comparison doesn't score the guides. */
.public-guides {
  margin-top: 1.5rem;
  padding: var(--gap);
  border: 1px solid var(--public);
  border-left-width: 0.375rem;
  border-radius: var(--radius);
  background: var(--public-bg);
}

.public-guides-title {
  margin: 0;
  font-size: 1.25rem;
}

.public-guides-framing {
  margin: 0.25rem 0 0;
  color: var(--muted);
  font-size: 0.9375rem;
}

/* The guides' list, closed by default (#543) so it doesn't push the ballot down. */
.public-guides-more {
  margin-top: 0.75rem;
}

.public-guides-more > summary {
  min-height: 2.25rem;
  font-weight: 600;
  cursor: pointer;
}

.public-guides-list {
  margin: 0.25rem 0 0;
  padding: 0;
  list-style: none;
}

.public-guide {
  padding: 0.5rem 0;
  border-top: 1px solid var(--border);
}

.public-guide p {
  margin: 0;
}

.public-guide-name {
  font-weight: 600;
  overflow-wrap: anywhere;
}

.public-guide-meta {
  color: var(--muted);
  font-size: 0.875rem;
  overflow-wrap: anywhere;
}

/* Empty until the ballot has rendered. */
.public-guide-summary:empty {
  display: none;
}

/* Empty when no public guide covers the contest and no compared guide picked in it. */
.contest-compare:not(:empty) {
  margin-top: 0.75rem;
}

/* The public guides' box. Before #393 the slot held their lines directly, so a page load
   that mixes this file with an older compare.js across a deploy boxes the slot itself. */
.contest-compare:has(> .compare-list),
.compare-public,
.compare-visitor {
  padding: 0.5rem 0.75rem;
  border-left: 0.25rem solid var(--public);
  border-radius: 0 var(--radius) var(--radius) 0;
  background: var(--public-bg);
}

/* The compared visitor guide (#393): the visitor guides' plain colors, never --public. */
.compare-visitor {
  border-left-color: var(--border);
  background: var(--surface);
  font-size: 0.9375rem;
}

.compare-public + .compare-visitor {
  margin-top: 0.5rem;
}

.compare-visitor .compare-item {
  margin: 0.25rem 0 0;
}

/* A saved set (#581): the author's guides first, then "Other public voter guides" under a
   label of its own; and the saved visitor guides' block before the compared one's. */
.compare-list + .compare-title,
.compare-visitor + .compare-visitor {
  margin-top: 0.5rem;
}

.compare-title,
.compare-visitor-title {
  margin: 0;
  color: var(--public);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.compare-visitor-title {
  color: var(--muted);
}

/* The public guides' list from before #582 (a .compare-list of lines), for a load that
   mixes this file with an older compare.js across a deploy. */
.compare-list {
  margin: 0.25rem 0 0;
  padding: 0;
  list-style: none;
  font-size: 0.9375rem;
}

/* Each card's table of the public guides' positions (#582): two columns, the guide's name
   and, in one cell, its position with the comparison on a line under it, so it fits a
   phone's card with no sideways scroll. Rows are ruled like the grid's. The name column
   takes about a third of the card, and never less than its longest word: it wraps with
   break-word, not .compare-item's anywhere, which would let the table shrink the column
   until a name breaks mid-word ("Placeholde/r" at 360px). The position's cell keeps
   anywhere, so a long label can't widen the table past the card. */
.compare-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
}

/* A guide's saved comparisons (#581) get a table of their own, then the others'. */
.compare-table + .compare-table {
  margin-top: 0.75rem;
}

/* The gap the old list's top margin made, between the title and the first guide. */
.compare-table > caption {
  padding-bottom: 0.25rem;
  text-align: left;
}

.compare-table th,
.compare-table td {
  padding: 0.3125rem 0;
  border-top: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
}

.compare-table th {
  width: 36%;
  overflow-wrap: break-word;
  padding-right: 0.75rem;
}

.compare-table tr:first-child > * {
  border-top: 0;
}

.compare-table .compare-status {
  display: block;
  margin-top: 0.0625rem;
}

/* A chip's tooltip hangs from the position's cell, its nearest positioned ancestor, not
   the row: WebKit hasn't always made a positioned <tr> a containing block. The cell spans
   the row's height, so the tip still opens under the row. */
.compare-table .compare-position {
  position: relative;
}

.compare-item {
  overflow-wrap: anywhere;
}

.compare-guide {
  font-weight: 600;
}

.compare-status {
  color: var(--muted);
}

/* A position that's our reading of the guide's text (schema v1.7's `interpreted`): an
   asterisk by its choice, and the note under the slot's list it points to. Plain, like the
   rest of the line: it qualifies the position, it doesn't score it. */
.compare-interpreted {
  margin-left: 0.0625rem;
}

.compare-note {
  margin: 0.25rem 0 0;
  color: var(--muted);
  font-size: 0.8125rem;
}

/* A position's chip in a guide of recommendations (schema v1.10, #571): "Endorsed" or
   "Recommended", a small tag before the choice. Like the asterisk, it qualifies the
   position and doesn't score it, so it keeps the line's own colour, in a bordered pill
   rather than a status colour; "Endorsed" is bold. Its dashed underline says it has a
   tooltip (.compare-tip), shown on hover or focus (tag-chip.js's tagChip()), under the line.
   The same chip and tooltip show in the comparison grid's cells (#602). */
.compare-tag-wrap {
  display: inline-block;
}

/* The tooltip hangs from the whole line, not the chip, so it never runs past the card on
   a narrow screen. */
.compare-item {
  position: relative;
}

.compare-tag {
  display: inline-block;
  margin: 0;
  padding: 0 0.375rem;
  border: 1px solid currentColor;
  border-radius: 999px;
  background: none;
  color: inherit;
  font: inherit;
  font-size: 0.75rem;
  line-height: 1.4;
  vertical-align: 0.0625rem;
  text-decoration: underline dashed;
  text-underline-offset: 0.15em;
  cursor: help;
}

.compare-tag-endorsed {
  font-weight: 600;
}

.compare-tip {
  position: absolute;
  top: 100%;
  left: 0;
  z-index: 2;
  width: max-content;
  max-width: min(20rem, 100%);
  padding: 0.375rem 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--text);
  font-size: 0.8125rem;
  font-style: normal;
  font-weight: 400;
  line-height: 1.35;
  white-space: normal;
  visibility: hidden;
  /* Hiding waits a moment, so the pointer can cross the rest of the line to the tip. */
  transition: visibility 0s linear 0.4s;
}

/* The tip's host (tag-chip.js's TIP_HOST) carries the click and Escape states. .compare-item
   stays beside it for a compare.js from before #602 across a deploy, which set them there,
   and .compare-tag beside .tag-tip-trigger for one from before #610, which didn't set the
   latter; both can go after a deploy or two. A tip stays shown while the pointer is on it or
   the keyboard is in it (a link in a note, or the scrolling tip itself). */
:is(.compare-tag, .tag-tip-trigger):is(:hover, :focus-visible) + .compare-tip,
.compare-tip:is(:hover, :focus-within),
.tag-tip-host[data-tip-open] .compare-tip,
.compare-item[data-tip-open] .compare-tip {
  visibility: visible;
  transition-delay: 0s;
}

.tag-tip-host[data-tip-dismissed] .compare-tip,
.compare-item[data-tip-dismissed] .compare-tip {
  visibility: hidden;
  transition-delay: 0s;
}

/* No endorsement, a public guide's or a visitor guide's, whatever its status (matches,
   compatible, differs or unpicked; #428). The data-status values are compare.js's
   before #428 ("no-position" and
   "no-endorsement", and #427's "no-recommendation" before them): for a page that mixes
   files across a deploy. */
.compare-choice.compare-none,
.compare-item:is([data-status="no-position"], [data-status="no-endorsement"], [data-status="no-recommendation"]) .compare-choice {
  font-style: italic;
}

/* --- guide-compare.js (#guide-compare: comparing with another visitor guide, #393) ---
   A visitor guide's region, so the visitor guides' plain box (like #guide), never the
   --public tokens. */
/* The comparison grid (#562) is a visitor guides' box too. */
.guide-compare,
.guide-grid {
  margin-top: 1.5rem;
  padding: var(--gap);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.guide-compare > :last-child {
  margin-bottom: 0;
}

.guide-compare-heading {
  margin: 0;
  font-size: 1.25rem;
}

.guide-compare-field {
  margin: 0.75rem 0 0;
}

.guide-compare-field label {
  display: block;
  font-weight: 600;
}

.guide-compare-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.25rem;
}

.guide-compare-input {
  flex: 1 1 16rem;
  min-width: 0;
  padding: 0.5rem;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--bg);
  color: var(--text);
  font: inherit;
}

.guide-compare-status {
  margin: 0.25rem 0 0;
  color: var(--muted);
  font-size: 0.875rem;
}

.guide-compare-status.is-error {
  color: var(--error);
}

.guide-compare-result:not(:empty) {
  margin-top: 0.75rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--border);
}

.guide-compare-title {
  margin: 0;
  font-size: 1.0625rem;
  overflow-wrap: anywhere;
}

.guide-compare-summary {
  margin: 0.5rem 0 0;
  font-weight: 600;
}

.guide-compare-result .guide-actions {
  margin: 0.75rem 0 0;
}

/* --- guide-grid.js (#guide-grid: the comparison grid, #562) ---
   The visitor guides' plain box, like #guide-compare; a public guide's column heading and
   picker entry carry the --public color, a visitor guide's the plain one. The cells' status
   words are plain, so matching and differing look alike apart from their words. The table
   scrolls sideways in its own box (.guide-grid-scroller), never the page, with the contest
   column held at its left edge. The box itself shares guide-compare.js's rule. */
.guide-grid-heading {
  margin: 0;
  font-size: 1.25rem;
}

.guide-grid > .guide-hint {
  margin: 0.25rem 0 0;
}

.guide-grid-more {
  margin-top: 0.75rem;
}

.guide-grid-more > summary {
  min-height: 2.25rem;
  font-weight: 600;
  cursor: pointer;
}

.guide-grid-picker {
  margin: 0.5rem 0 0;
  padding: 0;
  border: 0;
  min-width: 0;
}

.guide-grid-picker legend {
  padding: 0;
  font-weight: 600;
}

.guide-grid-options {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr));
  gap: 0.25rem 1rem;
  margin: 0.25rem 0 0;
  padding: 0;
  list-style: none;
}

.guide-grid-option {
  display: flex;
  align-items: baseline;
  gap: 0.375rem;
  min-height: 2.25rem;
  overflow-wrap: anywhere;
}

.guide-grid-option.guide-grid-public label {
  color: var(--public);
}

/* An added visitor guide's Remove: a small link-like button at the end of its row. */
.guide-grid-remove,
.guide-grid-forget {
  padding: 0 0.25rem;
  border: 0;
  background: none;
  color: var(--accent);
  font: inherit;
  font-size: 0.875rem;
  text-decoration: underline;
  cursor: pointer;
}

/* The link box for adding a visitor guide, under the list (guide-compare.js's field). */
.guide-grid-add .guide-hint {
  margin: 0;
}

.guide-grid-count {
  margin: 0.5rem 0 0;
  color: var(--muted);
  font-size: 0.875rem;
}

/* The columns the guide's author saved (#581): the line over the grid, in every view, and
   the editor's note under the picker, with its "Remove them from this guide". */
.guide-grid-saved {
  margin: 0.5rem 0 0;
}

.guide-grid-save {
  margin: 0.75rem 0 0;
}

.guide-grid-save .guide-hint {
  margin: 0 0 0.25rem;
}

.guide-grid-save-status {
  margin: 0.25rem 0 0;
  color: var(--muted);
  font-size: 0.875rem;
}

/* The box and the heading row's copy (#589) share one grid cell, the copy over the table's
   own headings. A grid item's width doesn't widen the track (minmax(0, 1fr)), so the wide box
   overhangs the cell as it overhangs the region. */
.guide-grid-frame {
  /* --bleed: the page's width, less 1rem for a classic scrollbar, as the ballot's band
     allows, so the page never scrolls sideways. */
  --bleed: calc(100vw - 1rem);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  margin: 0.75rem 0 0;
}

.guide-grid-frame > * {
  grid-area: 1 / 1;
}

/* position: relative holds the cells' visually hidden text (position: absolute) inside the
   box, or it would escape the clip and widen the page. */
.guide-grid-scroller {
  position: relative;
  width: 100%;
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

/* A table wider than the box, once it's scrolled sideways, widens the box to the page's
   width, centred under the region (Mari, on #567: "do the overflow only when needed and
   animate into it after you start scrolling horizontally"). guide-grid.js sets the class. */
.guide-grid-scroller.is-wide {
  width: var(--bleed);
  margin-left: calc((100% - var(--bleed)) / 2);
  border-inline-width: 0;
  border-radius: 0;
}

/* The heading row's copy (#589; guide-grid.js builds it): the box scrolls sideways, so it's
   the scroll container of anything sticky inside it, and the table's own headings can't stick
   to the page. The copy is outside the box, inside its border, over the table's own headings,
   and sticks to the top of the page until the table's bottom passes. It clips sideways, and
   guide-grid.js scrolls it with the box. Clicks, wheels and swipes go through it (inert, and
   pointer-events for a browser without inert), to the table's own headings at rest. */
.guide-grid-head {
  position: sticky;
  top: 0;
  z-index: 2;
  align-self: start;
  width: calc(100% - 2px);
  margin: 1px 1px 0;
  overflow: hidden;
  /* The box's rounded corners, inside its border, so the copy doesn't square them off. */
  border-radius: max(0px, var(--radius) - 1px) max(0px, var(--radius) - 1px) 0 0;
  pointer-events: none;
}

.guide-grid-frame:has(> .guide-grid-scroller.is-wide) > .guide-grid-head {
  width: var(--bleed);
  margin-inline: calc((100% - var(--bleed)) / 2) 0;
  border-radius: 0;
}

.guide-grid-head .guide-grid-table {
  table-layout: fixed;
}

.guide-grid-head th {
  box-sizing: border-box;
}

/* A heading of the table's own that has the keyboard shows through: the copy hides while it
   does (the browser scrolls it into view, where the copy sits over it). A link clicked with
   the mouse keeps focus without :focus-visible, so the copy stays. */
.guide-grid-frame:has(> .guide-grid-scroller thead :focus-visible) > .guide-grid-head {
  visibility: hidden;
}

@media (prefers-reduced-motion: no-preference) {
  .guide-grid-scroller,
  .guide-grid-head {
    transition: width 0.25s ease-out, margin-left 0.25s ease-out, border-radius 0.25s ease-out;
  }
}

.guide-grid-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  font-size: 0.875rem;
}

.guide-grid-table th,
.guide-grid-table td {
  min-width: 9rem;
  padding: 0.375rem 0.5rem;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
  overflow-wrap: anywhere;
}

/* An opaque background, so the rows don't show through the heading row's copy (#589). */
.guide-grid-table thead th {
  background: var(--bg);
}

.guide-grid-table thead th.guide-grid-public {
  border-top: 0.25rem solid var(--public);
  background: var(--public-bg);
  color: var(--public);
}

.guide-grid-table thead th.guide-grid-public a {
  color: inherit;
}

/* The contest column stays in view while the rest scrolls sideways. */
.guide-grid-table .guide-grid-contest {
  position: sticky;
  left: 0;
  z-index: 1;
  min-width: 8rem;
  background: var(--surface);
  font-weight: 600;
}

.guide-grid-table thead .guide-grid-contest {
  background: var(--bg);
}

.guide-grid-section th {
  padding-top: 0.75rem;
  background: var(--bg);
  font-size: 0.8125rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

/* A header spans the row; its words stay at the left edge as the row scrolls. */
.guide-grid-section th > span {
  position: sticky;
  left: 0.5rem;
}

.guide-grid-status {
  display: block;
  margin-top: 0.125rem;
  color: var(--muted);
}

.guide-grid-absent {
  color: var(--muted);
}

/* Each option's name in its row's color (#562; Mari, on #567): the picks' choices in the
   accent, bold and underlined, so they stand out even in a look whose accent is near one
   of the other colors; the rest in --grid-option-0 to -5, the same in every column. Each
   card's comparison table (#582) uses the same class (option-colors.js), so an option is
   the same color there. */
/* .guide-grid-option-name is the class before #582, for a cached guide-grid.js across a
   deploy. */
:is(.option-name, .guide-grid-option-name) {
  font-weight: 600;
}

:is(.option-name, .guide-grid-option-name)[data-color="pick"] {
  color: var(--accent);
  font-weight: 700;
  text-decoration: underline 0.125em;
  text-underline-offset: 0.15em;
}

:is(.option-name, .guide-grid-option-name)[data-color="0"] { color: var(--grid-option-0); }
:is(.option-name, .guide-grid-option-name)[data-color="1"] { color: var(--grid-option-1); }
:is(.option-name, .guide-grid-option-name)[data-color="2"] { color: var(--grid-option-2); }
:is(.option-name, .guide-grid-option-name)[data-color="3"] { color: var(--grid-option-3); }
:is(.option-name, .guide-grid-option-name)[data-color="4"] { color: var(--grid-option-4); }
:is(.option-name, .guide-grid-option-name)[data-color="5"] { color: var(--grid-option-5); }

/* No endorsement and Undecided, as the compare slot shows No endorsement. */
.guide-grid-choice.compare-none {
  font-style: italic;
}

.guide-grid-note:empty {
  display: none;
}

/* A position's chip and its tooltip in a grid cell (#602), the cards' own (tag-chip.js): the
   tip hangs from its cell, at most the cell's width, over the heading row's copy. The grid's
   scrolling box clips it and counts it in its scrollable overflow, hidden or not, so a
   hidden tip is scaled to nothing (its layout size stays real, for tag-chip.js's place()
   to measure), and one that wouldn't fit below its cell opens upward (`data-tip-up`). The
   scale steps with the visibility, after the same delay. */
.guide-grid-table td.tag-tip-host {
  position: relative;
}

.guide-grid-table .compare-tip {
  z-index: 3;
  transform: scale(0);
  transform-origin: top left;
  transition: visibility 0s linear 0.4s, transform 0s linear 0.4s;
}

.guide-grid-table :is(.compare-tag, .tag-tip-trigger):is(:hover, :focus-visible) + .compare-tip,
.guide-grid-table .compare-tip:is(:hover, :focus-within),
.guide-grid-table .tag-tip-host[data-tip-open] .compare-tip {
  transform: none;
  transition-delay: 0s;
}

.guide-grid-table .tag-tip-host[data-tip-dismissed] .compare-tip {
  transform: scale(0);
  transition-delay: 0s;
}

.tag-tip-host[data-tip-up] .compare-tip {
  top: auto;
  bottom: 100%;
}

/* A pick's reason and note in a grid cell (#610; guide-grid.js's renderChoice()): the
   choice's own words are the button, plain text with a dashed underline that says it has a
   tooltip (Mari, on PR #611: no separate capsule), and its tip scrolls inside itself past
   a height a phone can show (tag-chip.js's `.tag-tip-scroll`), so a long note never adds to
   the page's or the grid's scroll. A pick's own solid underline (the option's name in the
   accent, above) gives way to the dashed one inside it, so the words carry one line, not
   two; the pick stays bold and in the accent. The reason and the note each have a label,
   and render as the card's view renders them. */
.guide-grid-text-button {
  display: inline;
  margin: 0;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  text-align: inherit;
  text-decoration: underline dashed;
  text-decoration-thickness: 1px;
  text-underline-offset: 0.2em;
  cursor: help;
}

.guide-grid-text-button .option-name {
  text-decoration: none;
}

.tag-tip-scroll {
  max-height: min(16rem, 50vh);
  overflow-y: auto;
  overscroll-behavior: contain;
}

.tag-tip-label {
  margin: 0;
  color: var(--muted);
  font-size: 0.75rem;
  font-weight: 600;
}

.tag-tip-scroll .pick-reason {
  margin: 0 0 0.375rem;
}

.tag-tip-scroll .pick-reason:last-child {
  margin-bottom: 0;
}

.tag-tip-scroll .pick-note-body > :first-child {
  margin-top: 0;
}

/* --- The Default look (#534) ---
   What a guide shows when its author picked no look, and what the builder and home page
   show (look.js names the look in <html data-style>; the home page has none). Every rule
   here is gated on that, so the other looks (app/themes/, which layer over base.css) never
   see it, and on screen, so print.css's sheet is unchanged.

   Teal, marigold, tangerine and leaf: no red-vs-blue pairing, and no purple, which stays
   the public guides' (--public). A picked Yes and a picked No look the same, and #427's
   states (values starting with ":") are never highlighted. The card stripes rotate by
   position in the list, so a color says nothing about a contest.

   tests/themes.test.js checks these tokens with base.css's pairings plus:
   contrast: --text on --dl-pick-bg 4.5
   contrast: --muted on --dl-pick-bg 4.5
   contrast: --accent on --dl-pick-bg 4.5
   contrast: --focus on --dl-pick-bg 3
   contrast: --muted on --dl-wash-sun 4.5
   contrast: --muted on --dl-wash-teal 4.5
   contrast: --accent on --dl-wash-teal 4.5
   contrast: --text on --dl-wash-teal 4.5
   contrast: --focus on --dl-wash-teal 3
   contrast: --focus on --public-bg 3 */
:root:is([data-style="default"], :not([data-style])) {
  --bg: #fbf8f2;
  --surface: #ffffff;
  --text: #1d1b18;
  --muted: #59554d;
  --border: #e3dccd;
  --accent: #0a6b66;
  --accent-text: #ffffff;
  --focus: #0a6b66;
  --radius: 12px;
  --dl-teal: #17a398;
  --dl-sun: #f5b83d;
  --dl-tangerine: #ec7a35;
  --dl-leaf: #4f9d5d;
  --dl-wash-sun: #fcefd4;
  --dl-wash-teal: #ddf1ee;
  --dl-pick-bg: #e0f3f0;
  --dl-shadow: #3a2d1414;
}

@media (prefers-color-scheme: dark) {
  :root:is([data-style="default"], :not([data-style])) {
    --bg: #15130f;
    --surface: #1f1c17;
    --text: #efe9de;
    --muted: #b1aa9c;
    --border: #3b362d;
    --accent: #5fd0c4;
    --accent-text: #0b1f1d;
    --focus: #5fd0c4;
    --dl-teal: #39c2b4;
    --dl-sun: #f2c14e;
    --dl-tangerine: #f39257;
    --dl-leaf: #6cbf7a;
    --dl-wash-sun: #2a2214;
    --dl-wash-teal: #10282a;
    --dl-pick-bg: #173431;
    --dl-shadow: #00000066;
  }
}

@media screen {
  /* A ribbon across the top, and a soft wash of two colors behind the header. */
  :root:is([data-style="default"], :not([data-style])) body {
    background:
      linear-gradient(90deg, var(--dl-teal), var(--dl-leaf) 35%, var(--dl-sun) 65%, var(--dl-tangerine)) top / 100% 0.375rem no-repeat,
      radial-gradient(60rem 22rem at 0% 0%, var(--dl-wash-sun), var(--bg)) top left / 100% 30rem no-repeat,
      var(--bg);
  }

  :root:is([data-style="default"], :not([data-style])) .site-header {
    padding-top: 1.125rem;
    border-bottom: 0;
  }

  /* The site name gets a little mark of the four colors. */
  :root:is([data-style="default"], :not([data-style])) .site-name {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    font-size: 1.0625rem;
    font-weight: 800;
    letter-spacing: -0.01em;
  }

  :root:is([data-style="default"], :not([data-style])) .site-name::before {
    content: "";
    width: 1.125rem;
    height: 1.125rem;
    border-radius: 0.3rem;
    background: conic-gradient(var(--dl-teal) 0 25%, var(--dl-leaf) 0 50%, var(--dl-sun) 0 75%, var(--dl-tangerine) 0);
    transform: rotate(-8deg);
  }

  :root:is([data-style="default"], :not([data-style])) .election-name {
    font-size: 2.125rem;
    font-weight: 800;
    letter-spacing: -0.02em;
  }

  /* A short ribbon under the election's name. */
  :root:is([data-style="default"], :not([data-style])) .election-name::after {
    content: "";
    display: block;
    width: 4.5rem;
    height: 0.3125rem;
    margin-top: 0.5rem;
    border-radius: 999px;
    background: linear-gradient(90deg, var(--dl-teal), var(--dl-sun), var(--dl-tangerine));
  }

  :root:is([data-style="default"], :not([data-style])) .site-footer {
    border-top: 0;
  }

  /* The visitor guide's boxes and the cards: rounder, and lifted off the page. */
  :root:is([data-style="default"], :not([data-style])) :is(.guide, .guide-compare, .home-start, .contest, .pick-chooser, .card-dialog) {
    border-color: var(--border);
    box-shadow: 0 1px 2px var(--dl-shadow), 0 10px 24px -14px var(--dl-shadow);
  }

  :root:is([data-style="default"], :not([data-style])) :is(.guide, .home-start) {
    border-top: 0.3125rem solid var(--dl-teal);
  }

  :root:is([data-style="default"], :not([data-style])) .home-start {
    background: linear-gradient(135deg, var(--dl-wash-teal), var(--surface) 70%);
  }

  :root:is([data-style="default"], :not([data-style])) :is(.guide-title, .home-title, .ballot-title) {
    font-weight: 800;
    letter-spacing: -0.01em;
  }

  /* Each card's top stripe, rotating through the four colors by position. */
  :root:is([data-style="default"], :not([data-style])) .contest {
    border-top: 0.3125rem solid var(--dl-teal);
  }

  :root:is([data-style="default"], :not([data-style])) .contests > li:nth-child(4n + 2) > .contest {
    border-top-color: var(--dl-sun);
  }

  :root:is([data-style="default"], :not([data-style])) .contests > li:nth-child(4n + 3) > .contest {
    border-top-color: var(--dl-tangerine);
  }

  :root:is([data-style="default"], :not([data-style])) .contests > li:nth-child(4n + 4) > .contest {
    border-top-color: var(--dl-leaf);
  }

  /* A measure's designation reads as a tag. */
  :root:is([data-style="default"], :not([data-style])) .contest-designation {
    padding: 0 0.375rem;
    border-radius: 0.375rem;
    background: var(--dl-wash-teal);
  }

  /* The picked choice is a tinted chip: the same for every choice, never for #427's states. */
  :root:is([data-style="default"], :not([data-style])) .pick-option {
    margin-inline: -0.5rem;
    padding-inline: 0.5rem;
    border-radius: 0.625rem;
  }

  :root:is([data-style="default"], :not([data-style])) .pick-option:has(input:checked:not([value^=":"])) {
    background: var(--dl-pick-bg);
  }

  /* Someone else's guide (and the owner's preview): the pick on a tinted panel. */
  :root:is([data-style="default"], :not([data-style])) .contest-pick > .pick-view {
    padding: 0.75rem;
    border-top: 0;
    border-radius: 0.625rem;
    background: var(--dl-pick-bg);
  }

  /* Buttons are pills. */
  :root:is([data-style="default"], :not([data-style])) :is(.home-button, .account-action, .guide-action, .rank-action, .official-link, .home-example) {
    border-radius: 999px;
  }

  :root:is([data-style="default"], :not([data-style])) .official-link {
    padding-inline: 1rem;
  }

  :root:is([data-style="default"], :not([data-style])) .home-button {
    padding: 0.625rem 1.25rem;
    box-shadow: 0 6px 16px -8px var(--accent);
  }

  /* The highlights summary (#667): a teal wash under the look's ribbon of four colors, and
     the dots taking the colors in turn, by position like the card stripes. */
  :root:is([data-style="default"], :not([data-style])) .guide-highlights {
    border: 0;
    padding-top: 1rem;
    background:
      linear-gradient(90deg, var(--dl-teal), var(--dl-leaf) 35%, var(--dl-sun) 65%, var(--dl-tangerine)) top / 100% 0.3125rem no-repeat,
      var(--dl-wash-teal);
    box-shadow: 0 10px 24px -18px var(--dl-shadow);
  }

  :root:is([data-style="default"], :not([data-style])) .guide-highlight::before {
    background: var(--dl-teal);
  }

  :root:is([data-style="default"], :not([data-style])) .guide-highlights-list > li:nth-child(4n + 2) .guide-highlight::before {
    background: var(--dl-leaf);
  }

  :root:is([data-style="default"], :not([data-style])) .guide-highlights-list > li:nth-child(4n + 3) .guide-highlight::before {
    background: var(--dl-sun);
  }

  :root:is([data-style="default"], :not([data-style])) .guide-highlights-list > li:nth-child(4n) .guide-highlight::before {
    background: var(--dl-tangerine);
  }
}
