/* ══ THE DIAL — one number sets this app's primary colour ═════════════════
 *
 * The vendored language hardcodes nominate.ai's orange (`--accent:#ff8100`).
 * The operator's standing requirement is a dial per app, so the accent family
 * is re-derived here from a single hue and the vendored literals are
 * overridden. Everything downstream — buttons, eyebrows, the accent half of
 * the headline, rung fills, chip hovers, the masthead tile — follows, because
 * the language resolves all of it through --accent and --gold.
 *
 * 30 ≈ the red of the mark · 55 nominate orange · 210 sky · 265 violet
 * oklch keeps perceived lightness steady across hues, so contrast survives
 * the turn of the dial rather than needing a re-check at every stop.
 */
:root {
  /* ── PUBLIC IS COPPER (operator, 2026-08-18: "here we introduce the copper
     theme"). Literals sampled from the copper eagle itself
     (skin/brand/mark.png) rather than derived from the dial, the same way
     Prospect's gold and Night's silver are their eagles' own metal:
     the accent is the eagle's struck mid-tone, the highlight its lit facet. */
  --accent: #ce6c3b;
  --gold: #fdca90;
  /* The deep ink under an accent fill — a button's own text colour. Kept
     near-black rather than re-derived, because a tinted foreground on a
     saturated fill is what turns a crisp button muddy. */
  --accent-foreground: #17111a;
}

/* This app's own layer, on top of the vendored nominate.ai language.
 *
 * Deliberately small. Everything structural — type scale, buttons, panels,
 * ladder, chips, tags, the starfield ground — comes from
 * `skin/site/tokens.css`, which is that site's own stylesheet. What is here is
 * only what a server-rendered dataset site needs and an Astro marketing page
 * did not: the page grid for panels, the door treatment on a linked panel,
 * and the state board.
 *
 * The idiom to keep when adding: 2px borders, hard offset shadows (no blur),
 * mono for labels and figures, orange reserved for the one thing that matters
 * in view. If a rule here starts inventing a second look, it belongs in the
 * vendored language instead — edit it in cbnominate and re-vendor.
 */

/* ── Hero ─────────────────────────────────────────────────────────────────
   The site's signature: a mono eyebrow with a trailing rule, a display
   headline whose second line turns accent, one measured lede. */
.hero-block {
  padding: 3.5rem 0 2.5rem;
  max-width: 54rem;
}
.hero-block .eyebrow {
  display: flex;
  align-items: center;
  gap: 0.9rem;
  margin-bottom: 1.1rem;
}
.hero-block .eyebrow::after {
  content: "";
  flex: 0 1 8rem;
  height: 2px;
  background: color-mix(in srgb, var(--line) 70%, transparent);
}
.hero-block h1 .accent {
  color: var(--accent);
}
.hero-block .lede {
  margin-top: 1.4rem;
  max-width: 40rem;
}

/* ── Panels as a grid ─────────────────────────────────────────────────────
   `.panel` is the site's card. It carries its own border, shadow and
   background; this only lays them out and pins equal heights so a row of
   them does not read as ragged.

   ⚠️ The GRID itself is `.ax-deck` now, dialled in brand.css. There used to be
   a second `.panel-grid` rule right here declaring `minmax(17rem, 1fr)`, and it
   was DEAD: brand.css declares the same class at the same specificity and loads
   after this file on both page paths (server/page.ts and public/index.html), so
   its 16rem always won. Two rules for one class where one can never apply is
   how a track width gets "fixed" in the copy nobody serves — deleted rather
   than converted. cbapex#22. */
.panel-grid .panel {
  margin-bottom: 0;
  display: flex;
  flex-direction: column;
  gap: 0.15rem;
}
/* Block, not inline: `.panel.is-door` is display:block so its children do not
   inherit the grid's flex column, and inline spans ran the eyebrow, the title
   and the lines together on one line. */
.p-eyebrow,
.p-title,
.p-line {
  display: block;
}
.p-eyebrow {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--accent);
}
.p-title {
  font-size: 1.9rem;
  font-weight: 900;
  letter-spacing: -0.02em;
  line-height: 1.05;
  font-variant-numeric: tabular-nums;
  margin: 0.15rem 0 0.35rem;
}
.panel.is-door .p-title {
  margin-top: 0.2rem;
}
.p-line {
  font-size: 0.9rem;
  color: var(--muted);
}
.p-line b {
  color: var(--foreground);
}

/* A panel that goes somewhere says so — the same rule Prospect learned: an
   iPad has no hover to discover a link with. */
.panel.is-door {
  display: block;
  text-decoration: none;
  color: inherit;
  position: relative;
  padding-right: 2.4rem;
  transition: border-color 0.15s, transform 0.15s;
}
.panel.is-door::after {
  content: "→";
  position: absolute;
  top: 50%;
  right: 1rem;
  transform: translateY(-50%);
  font-family: var(--font-mono);
  color: var(--muted);
  transition: color 0.15s;
}
.panel.is-door:hover {
  border-color: var(--gold);
  transform: translateX(3px);
}
.panel.is-door:hover::after {
  color: var(--gold);
}
.panel.is-door:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

/* ── The state board ──────────────────────────────────────────────────────
   The home page's "on the board" strip: every state we hold, as chips, each
   one a link a crawler follows. `.chip` is the site's; these are anchors
   rather than buttons, so the cursor and the padding are restated. */
.cta-row {
  margin-bottom: 2.25rem;
}

.board {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  align-items: center;
  margin: 0 0 3rem;
}
.board .board-label {
  font-family: var(--font-mono);
  font-size: 0.68rem;
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--accent);
  margin-right: 0.35rem;
}
.board .chip {
  text-decoration: none;
  cursor: pointer;
  color: var(--foreground);
}
.board .chip b {
  color: var(--accent);
  font-weight: 700;
  margin-right: 0.35rem;
}
.board .chip:hover {
  border-color: var(--gold);
  color: var(--gold);
}
.board .chip:hover b {
  color: var(--gold);
}

/* ── Sections ─────────────────────────────────────────────────────────── */
.strip {
  margin: 0 0 3rem;
  max-width: 46rem;
}
.strip h2 {
  font-size: 1.6rem;
  margin-bottom: 0.7rem;
}
.strip p {
  color: var(--muted);
  margin: 0 0 0.8rem;
}
.more {
  color: var(--gold);
  text-decoration: none;
  font-weight: 700;
}
.more:hover {
  text-decoration: underline;
}
main.wrap {
  padding-bottom: 2rem;
}

/* The rung's two labels stack. The site's ladder specimen sets a descriptor
   under the tier name; without an explicit block they run together
   ("SpecialSPECIAL OFFICES"). */
.rung .tier,
.rung .sub {
  display: block;
}
.rung .sub {
  margin-top: 0.15rem;
}

/* The mark. Sized to the header's cap height rather than a round number, so
   it sits ON the baseline of the wordmark instead of near it. */
.mark {
  display: inline-flex;
  align-items: center;
  gap: 0.55rem;
}
.mark-img {
  height: 1.7rem;
  width: auto;
  display: block;
}
.foot .mark-img {
  height: 1.4rem;
}

/* ── The nav must exist below 60rem ───────────────────────────────────────
 * The vendored shell hides `.navlinks` by default and reveals them as a
 * dropdown when a menu button adds `.open` — nominate.ai ships that button
 * and its script. This app does not: it has two links, and a hamburger to
 * hide two links is furniture.
 *
 * Without this override the nav was simply INVISIBLE on phone and tablet —
 * caught by the first page × viewport screenshot pass, which is exactly the
 * class of defect that pass exists to catch. It looked perfect on a laptop.
 */
.site-head .navlinks {
  display: flex;
  margin-left: auto;
  gap: 1.1rem;
  align-items: center;
}
.site-head .navlinks a {
  color: var(--muted);
  text-decoration: none;
  font-weight: 700;
  font-size: 0.88rem;
  /* A 44px touch target, per the same rule the rest of the fleet keeps. */
  min-height: 2.75rem;
  display: inline-flex;
  align-items: center;
}
.site-head .navlinks a:hover,
.site-head .navlinks a[aria-current] {
  color: var(--foreground);
}
@media (max-width: 26rem) {
  /* Narrowest phones: the wordmark and two links still fit if the links
     tighten. Tested at 320px. */
  .site-head .navlinks {
    gap: 0.75rem;
  }
  .site-head .navlinks a {
    font-size: 0.82rem;
  }
}

/* ── A person's card ──────────────────────────────────────────────────────
   The only page here about a human rather than a dataset, and it is read in
   one situation: on a phone, seconds after someone scanned a code across a
   table. So the face and the two tappable actions carry it, and the prose
   waits below. */
.repcard {
  border: 2px solid var(--line);
  border-radius: 0.5rem;
  box-shadow: var(--hard-card);
  background: linear-gradient(180deg, #fff7ec0e, #fff7ec06), var(--surface);
  padding: 1.75rem;
  margin: 2.5rem 0;
  max-width: 40rem;
}
.rep-head {
  display: flex;
  align-items: center;
  gap: 1.25rem;
  flex-wrap: wrap;
}
.rep-photo {
  width: 6rem;
  height: 6rem;
  border-radius: 50%;
  object-fit: cover;
  border: 2px solid var(--line);
  /* Grayscale, so a floor of photos taken in a dozen rooms reads as one set.
     The same treatment the card in Prospect applies. */
  filter: grayscale(1) contrast(1.05);
  display: grid;
  place-items: center;
  flex: 0 0 auto;
}
.rep-initials {
  font-family: var(--font-mono);
  font-weight: 800;
  font-size: 1.8rem;
  color: var(--accent);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
  filter: none;
}
.rep-head h1 {
  font-size: clamp(2rem, 6vw, 3rem);
  line-height: 1;
}
.repcard .lede {
  margin-top: 1.25rem;
}
.rep-actions {
  margin-top: 1.5rem;
}
@media (max-width: 30rem) {
  /* On the phone this page was built for, the actions go full width — a
     thumb should not have to aim. */
  .rep-actions {
    display: grid;
    gap: 0.6rem;
  }
  .rep-actions .btn {
    text-align: center;
  }
}

/* ── The optional door ─────────────────────────────────────────────────────
   Logging in is optional on this site — every page is public. The sign-in
   link is therefore quiet: present for the person who has an account, muted
   for the ninety-nine who do not.

   ⚠️ These selectors are scoped to `.navlinks`, WHICH THIS APP NO LONGER EMITS
   — page.ts renders the kit's `.shell-nav` instead. So the deliberate muting
   below has not applied since that change: the sign-in link renders at full nav
   weight, exactly as loud as Home and States, which is the opposite of the
   decision this comment describes. The rules are re-scoped to cover both, since
   a page somewhere may still carry the old markup. */
.navlinks .nav-signin,
.shell-nav .nav-signin {
  color: var(--muted);
  opacity: 0.85;
}
.navlinks .nav-signin:hover,
.shell-nav .nav-signin:hover {
  color: var(--gold);
  opacity: 1;
}

/* The wordmark is shell.css's .shell-mark now — the frame owns the voice. */

/* ── Density reaches the site's own surfaces ───────────────────────────────
   The strip's picker drives the shell tokens; these map them onto the site
   language's repeated surfaces, so Compact/Cozy/Comfortable is a real lever
   here and not furniture. Falls back to each surface's designed padding. */
.panel {
  padding: var(--shell-box-y, 1.15rem) var(--shell-box-x, 1.25rem);
}
.rung {
  padding-top: var(--shell-row-y, 0.7rem);
  padding-bottom: var(--shell-row-y, 0.7rem);
}
.board .chip {
  padding-top: var(--shell-cell-y, 0.45rem);
  padding-bottom: var(--shell-cell-y, 0.45rem);
}

/* ── The share card page (/e/<slug>/) ──────────────────────────────────────
   A pasted Election Night link needs a face, and the person who taps through
   from iMessage lands HERE first — signed out, on a phone, wanting two facts
   and a door. So: the facts as a definition list (label/value, not a table
   with one row), then the door, then the one sentence that explains why the
   numbers are not on this page.

   Deliberately few rules. This page borrows `.hero-block` and `.panel` from
   the site language it is served by; only the fact rows are its own. */
.sh-facts {
  display: grid;
  gap: 0.55rem;
  margin: 0 0 1.25rem;
}
.sh-row {
  display: grid;
  grid-template-columns: 9.5rem minmax(0, 1fr);
  gap: 0.4rem 1rem;
  align-items: baseline;
}
.sh-row dt {
  font-family: var(--ax-mono, ui-monospace, monospace);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}
.sh-row dd {
  margin: 0;
  font-size: 0.98rem;
  font-weight: 600;
}
.sh-go {
  margin: 0 0 0.9rem;
}
.sh-note {
  margin: 0;
  font-size: 0.84rem;
  color: var(--muted);
  max-width: 44ch;
}
/* On a phone the label stops taking a third of the width from the fact. */
@media (max-width: 34rem) {
  .sh-row {
    grid-template-columns: minmax(0, 1fr);
    gap: 0.1rem;
  }
}

/* ── Candidate pages (cbpublic#2) ────────────────────────────────────────────
   The rep card's frame, holding a candidate's own words. Two departures:
   the photo keeps its colour (grayscale is for a FLOOR of rep photos reading
   as one set; a candidate's page has one face and they chose it), and the
   paragraph they wrote gets paragraph spacing, which the rep card never
   needed because a rep's blurb is one line. */
.claimcard .rep-photo {
  filter: none;
}
.claimcard .claim-slogan {
  font-weight: 600;
}
.claimcard .claim-para {
  margin: 0.9rem 0 0;
  max-width: 36rem;
}
.claim-preview {
  margin-top: 1.5rem;
}
.claim-credit {
  margin: 1.5rem 0 2.5rem;
  font-size: 0.85rem;
}
