:root {
  --bg: #c0c0c0;
  --ink: #0000ee;
  --ink-faded: #333333;
  --ink-faint: #c7c7c7;
  --bevel-face: #e9e9e9;
  --bevel-hi: #ffffff;
  --bevel-sh: #848484;
  /* Outline color/thickness for plain flat-bordered boxes (artist/city
     cards, table cells, the state map) — kept separate from the beveled
     3D controls (retro-btn/retro-field), which need distinct highlight/
     shadow color pairs and aren't simple single-color outlines. */
  --line-color: #000000;
  --line-width: 1px;
  /* State map section. --map-fill defaults to the same value as --map-bg
     on purpose — the shape blends invisibly into its own box by default
     (matching the old fixed fill:none look) while still being a real,
     independently adjustable color underneath. */
  --state-empty-color: #606060;
  --map-bg: #c0c0c0;
  --map-fill: #c0c0c0;
  /* Separate from --line-color since this specifically controls the
     country map's gridlines (both the full home map and the cropped
     region-search map), independent of every other box's outline color. */
  --country-grid-color: #000000;
  font-family: "Times New Roman", Times, serif;
}

* { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink-faded);
  line-height: 1.45;
  font-size: 1.05rem;
  font-weight: 400;
}

a { color: var(--ink); text-decoration: underline; }
a:visited { color: #551a8b; }

/* ---- retro (Windows/GeoCities-era) form chrome: beveled inset fields, raised buttons ---- */
.retro-field {
  background: #ffffff;
  border-style: solid;
  border-width: 2px;
  border-color: var(--bevel-sh) var(--bevel-hi) var(--bevel-hi) var(--bevel-sh);
  color: var(--ink);
  font-family: inherit;
}
.retro-field:focus { outline: 1px dotted var(--ink-faded); outline-offset: -3px; }

.retro-btn {
  background: var(--bevel-face);
  border-style: solid;
  border-width: 2px;
  border-color: var(--bevel-hi) var(--bevel-sh) var(--bevel-sh) var(--bevel-hi);
  color: var(--ink);
  font-family: inherit;
  cursor: pointer;
}
.retro-btn:active {
  border-color: var(--bevel-sh) var(--bevel-hi) var(--bevel-hi) var(--bevel-sh);
}

/* ---- admin hub (post-login #/add landing page) ---- */
.admin-btn-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 0.9rem;
  margin: 0 0 1.75rem;
}
/* Fixed min-height (rather than just letting the grid stretch to the
   tallest cell) so a short label like "+ Add an Artist" and a long one
   like "Relink Missing Spotify Links" render as the same-size button
   even when a group has only one entry and nothing to stretch against. */
.admin-btn {
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center;
  min-height: 4.5rem;
  padding: 0.75rem 1rem;
  font-size: 1rem;
  text-decoration: none;
}

/* persistent player bar — sticky, always the topmost element, survives
   navigation. Styled like a classic Windows app window: a blue titlebar
   over a beveled grey body. Holds a real, visible Spotify embed (its own
   native play/pause button) inside an inset frame — not a hidden
   custom-controlled one, see the note above playTrack() in app.js for why.
   Note: the iframe's own contents (Spotify's black/green UI) are a
   cross-origin embed and can't be restyled — only everything around it can. */
/* position:fixed (not sticky) so it stays pinned to the viewport at every
   scroll position on every page, including all the way at the bottom of a
   long one — sticky only holds within its own containing block, and with
   html/body set to height:100% that block is just one viewport tall, so a
   sticky bar stops tracking the moment you scroll past that point. Since
   fixed removes it from document flow, JS (see setPlayerBarVisible in
   app.js) adds matching padding-top to <body> while it's visible so it
   doesn't cover the header/content underneath. */
.player-bar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 100;
  background: var(--bevel-face);
  border-bottom: 2px solid var(--bevel-sh);
}
.player-bar[hidden] { display: none; }

.player-titlebar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: var(--ink);
  color: #ffffff;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 3px 4px 3px 8px;
}

.player-close {
  width: 1.1rem;
  height: 1.1rem;
  padding: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.85rem;
  line-height: 1;
  border-radius: 3px;
  background: var(--bevel-face);
  border-style: solid;
  border-width: 2px;
  border-color: var(--bevel-hi) var(--bevel-sh) var(--bevel-sh) var(--bevel-hi);
  color: var(--ink-faded);
  cursor: pointer;
}
.player-close:active {
  border-color: var(--bevel-sh) var(--bevel-hi) var(--bevel-hi) var(--bevel-sh);
}

.player-frame {
  margin: 8px;
  padding: 6px;
  border-radius: 3px;
  background: #ffffff;
  border-style: solid;
  border-width: 2px;
  border-color: var(--bevel-sh) var(--bevel-hi) var(--bevel-hi) var(--bevel-sh);
}

/* Spotify's iFrame API swaps #player-mount for its own iframe rather than
   nesting inside it, so style whatever lands there directly. */
#player-mount, #player-mount iframe, .player-frame iframe {
  display: block;
  width: 100%;
  height: 80px;
  border: none;
}

.site-header {
  padding: 1.75rem 1.25rem 1rem;
  text-align: center;
  border-bottom: 1px solid var(--ink-faint);
}

.header-top-row {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: 1rem;
}
#header-hitcounter {
  justify-self: start;
  text-align: left;
}
.header-top-spacer {
  justify-self: end;
}
/* Below this width there isn't room for the counter and the logo side by
   side without the logo overflowing the viewport (grid's auto-sized middle
   column doesn't shrink to fit leftover space) — fall back to stacking
   them, same as before the counter moved to the header row. */
@media (max-width: 480px) {
  .header-top-row {
    display: flex;
    flex-direction: column;
    align-items: center;
  }
  #header-hitcounter {
    text-align: center;
    margin-bottom: 0.75rem;
  }
  .header-top-spacer {
    display: none;
  }
}

/* Logo: a genuine 88x31 pixel-art button PNG (its own beveled border is
   baked into the image, so no extra CSS border here), scaled up with
   image-rendering:pixelated to keep the pixel edges crisp instead of blurry. */
.logo {
  display: inline-block;
  line-height: 0;
}
.logo-img {
  display: block;
  /* Fixed px width would be simplest, but .logo is an auto-sized
     inline-block, so a percentage width here has nothing to resolve
     against (circular) and silently collapses to the image's native
     88×31 — no visible upscale, no pixelation. min() with a vw unit
     sizes off the viewport instead, sidestepping that entirely. */
  width: min(264px, 70vw);
  height: auto;
  aspect-ratio: 88 / 31;
  image-rendering: pixelated;
  image-rendering: -moz-crisp-edges;
  image-rendering: crisp-edges;
}

.tagline {
  margin: 0.35rem 0 0;
  color: var(--ink-faded);
  font-size: 0.95rem;
}

.randomize-btn {
  margin-top: 0.75rem;
  font-size: 0.85rem;
  padding: 0.5rem 0.9rem;
}

.toolbar {
  display: flex;
  gap: 0.75rem;
  align-items: stretch;
  margin-bottom: 1.5rem;
}
.toolbar .search-box { margin-bottom: 0; flex: 1 1 auto; }
.toolbar .randomize-btn { margin-top: 0; flex: 0 0 auto; white-space: nowrap; }

main#app {
  max-width: 1000px;
  margin: 0 auto;
  padding: 2rem 1.25rem 4rem;
}

.site-footer {
  text-align: center;
  color: var(--ink-faded);
  font-size: 0.85rem;
  padding: 1.5rem 1.25rem 2.5rem;
  border-top: 1px solid var(--ink-faint);
}

/* ---- retro footer extras: hit counter + badge seals ---- */
.hit-counter-label {
  display: block;
  font-size: 0.65rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin-bottom: 0.25rem;
}
.hit-counter {
  display: inline-block;
  background: #000000;
  color: #33ff33;
  font-family: "Courier New", Courier, monospace;
  font-weight: 700;
  letter-spacing: 0.2em;
  padding: 0.3rem 0.5rem;
  font-size: 1rem;
  border-width: 2px;
  border-style: solid;
  border-color: var(--bevel-sh) var(--bevel-hi) var(--bevel-hi) var(--bevel-sh);
}
.retro-badges {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem;
  align-items: center;
  margin-top: 1rem;
}
.retro-badge {
  display: inline-block;
  background: #000080;
  color: #ffffff;
  box-sizing: border-box;
  min-width: 88px;
  min-height: 31px;
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  padding: 0.3rem 0.6rem;
  border: 2px solid #000000;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-align: center;
}

/* "Add an artist" — a real raised, pressable 3D button (not another flat
   badge) so it reads as clearly clickable, deliberately distinct from both
   the black/green LED counter and the flat gold seal badge. */
.retro-badge-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--ink);
  color: #ffffff;
  font-size: 0.8rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  text-decoration: none;
  cursor: pointer;
  padding: 0.55rem 1.1rem;
  border-width: 3px;
  border-style: solid;
  border-color: #99b3ff #00008b #00008b #99b3ff;
}
.retro-badge-link:active {
  border-color: #00008b #99b3ff #99b3ff #00008b;
}
/* a:visited (element+pseudo-class) otherwise outranks the single-class
   .retro-badge-link rule above once the link has been visited, silently
   swapping the white label for the browser's default visited-purple. */
.retro-badge-link:visited {
  color: #ffffff;
}

/* "100% human-curated" — gold award-seal badge */
.retro-badge-seal {
  background: #ffcc00;
  color: #000000;
  border-color: #806600;
  box-shadow: inset 0 0 0 1px #000000;
}

/* breadcrumb */
.crumbs {
  font-size: 0.9rem;
  color: var(--ink-faded);
  margin-bottom: 1.5rem;
}
.crumbs a { color: var(--ink); }

/* search */
.search-box {
  width: 100%;
  padding: 0.55rem 0.75rem;
  font-size: 1rem;
  margin-bottom: 1.5rem;
  caret-color: var(--ink);
}
.search-box::placeholder { color: #767676; }

.search-results { margin-bottom: 2rem; }
.search-results-columns {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 1.5rem;
  align-items: start;
}
@media (max-width: 900px) {
  .search-results-columns { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 640px) {
  .search-results-columns { grid-template-columns: 1fr; }
}
.search-hit {
  display: block;
  padding: 0.6rem 0.9rem;
  border: 1px solid var(--ink);
  border-radius: 0;
  margin-bottom: 0.5rem;
  color: var(--ink);
  text-decoration: none;
}
.search-hit span { color: var(--ink-faded); font-size: 0.85rem; }

/* ---- region search hit: cropped copy of the home map for just that
   region's states, plus an alphabetical list ---- */
.region-hit { margin-bottom: 1.5rem; }
.region-map-grid {
  display: grid;
  gap: 1px;
  /* Cell size is fixed (set inline per region — see REGION_MAP_CELL_SIZE
     in app.js), not derived from a container width divided by column
     count, so every region's squares render at the same size regardless
     of how many columns that particular region spans. A region wide
     enough to overflow its column just scrolls horizontally rather than
     shrinking its squares to fit. width:max-content keeps the container
     itself shrunk to exactly that fixed grid size too — without it, a
     block-level grid defaults to filling the full available width,
     leaving blank space (showing the container's own line-color
     background) past the last real column. */
  width: max-content;
  max-width: 100%;
  overflow-x: auto;
  margin: 0 0 1rem;
  border: var(--line-width) solid var(--country-grid-color);
  /* Cells sit on top of this background with a 1px gap between them, so
     the gap itself reads as a grid line — no separate SVG overlay needed
     for this small cropped map, unlike the full home map. Same color
     variable as the home map's gridlines, for consistency. */
  background: var(--country-grid-color);
}
/* A non-member state inside the region's bounding box — filled with the
   same background used for a state's own map page (not the grid-line
   color), so it reads as "no data here" rather than a solid black hole. */
.region-map-gap { background-color: var(--map-bg); }
.region-state-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
  gap: 0.3rem;
}
.region-state-list a { color: var(--ink); }

/* ---- producer search hit: every song they're credited on ---- */
.producer-hit { margin-bottom: 1.5rem; }
.producer-credit-list { list-style: none; margin: 0; padding: 0; }
.producer-credit-list li { margin-bottom: 0.4rem; }
.producer-credit-link { color: var(--ink); }
.producer-credit-distance { color: var(--ink-faded); font-size: 0.85rem; margin-left: 0.4rem; }

/* ---- "Produced by" dropdown on an artist card ---- */
.producers-dropdown {
  margin: 0.75rem 0;
  border: var(--line-width) solid var(--line-color);
  padding: 0.6rem 0.9rem;
}
.producers-dropdown summary {
  cursor: pointer;
  font-weight: 700;
  text-transform: uppercase;
  font-size: 0.8rem;
  color: var(--ink-faded);
}
.producer-name-list { list-style: none; margin: 0.5rem 0 0; padding: 0; }
.producer-name-list li { margin-bottom: 0.3rem; }
/* A <button> styled to read as a link — it triggers searchForProducer()
   rather than navigating via href, since a producer's full song list only
   exists as a search result, not a page of its own. */
.producer-name-link {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--ink);
  text-decoration: underline;
  cursor: pointer;
}

/* ---- manage regions admin page ---- */
.region-card {
  border: var(--line-width) solid var(--line-color);
  padding: 0.9rem 1.1rem;
  margin-bottom: 1.25rem;
}
.region-card-header {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
  margin-bottom: 0.5rem;
}
.region-card-header input[type="text"] { flex: 1 1 12rem; }
.region-color-label {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.8rem;
  color: var(--ink-faded);
  text-transform: uppercase;
}
.region-card-header input[type="color"] {
  width: 3rem;
  height: 2.2rem;
  padding: 2px;
  border: var(--line-width) solid var(--line-color);
  cursor: pointer;
}
.region-state-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(4.5rem, 1fr));
  gap: 0.4rem;
}
.region-state-checkbox {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  font-size: 0.9rem;
}

/* ---- state grid map ---- */
.map-scroll { margin-bottom: 0.75rem; }

.map-grid-container {
  position: relative;
  width: min(calc(12 * 3.4rem), 100%);
  aspect-ratio: 12 / 8;
  margin: 0 auto;
}

.grid-lines {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  /* Rendered after (on top of) .state-grid in the DOM — see renderHome —
     so the lines actually show over each tile's region color instead of
     being painted underneath and covered up. pointer-events:none lets
     clicks fall through to the tile links beneath regardless. */
  pointer-events: none;
}
.grid-lines line {
  stroke: var(--country-grid-color);
  stroke-width: var(--line-width);
  vector-effect: non-scaling-stroke;
}

.state-grid {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  grid-template-rows: repeat(8, 1fr);
}

.state-cell {
  position: relative;
  display: block;
  text-decoration: none;
}

.state-label {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  pointer-events: none;
  font-size: 0.95rem;
  font-weight: 700;
}

.is-documented .state-label { color: var(--ink); }
.is-empty .state-label { color: var(--state-empty-color); }

@media (max-width: 480px) {
  .state-label { font-size: 2.6vw; }
}

.hover-band {
  position: absolute;
  top: 0; left: 0; width: 100%; height: 100%;
  display: block;
  text-decoration: none;
}

.hover-tip {
  position: absolute;
  bottom: 100%;
  left: 50%;
  transform: translateX(-50%);
  margin-bottom: 3px;
  opacity: 0;
  pointer-events: none;
  white-space: nowrap;
  font-size: 0.8rem;
  padding: 2px 7px;
  background: var(--bg);
  border: 1px solid var(--ink);
  color: var(--ink);
  z-index: 30;
}
.hover-band:hover .hover-tip { opacity: 1; }

.map-key {
  font-size: 0.85rem;
  color: var(--ink-faded);
  margin: 0.5rem 0 2rem;
}
.map-key-note { opacity: 0.8; }
.key-swatch {
  display: inline-block;
  width: 0.75em;
  height: 0.75em;
  margin-right: 0.3em;
  vertical-align: middle;
}
.key-swatch.is-documented { background: var(--ink); }
/* Same variable as .is-empty .state-label — one color picker in the theme
   editor controls both, since they represent the same "undiscovered" idea. */
.key-swatch.is-empty { background: var(--state-empty-color); }

/* ---- state-level map ---- */
.state-map-wrap { margin-bottom: 2rem; }
.state-map-svg { display: block; margin: 0; width: 100%; max-width: 460px; height: auto; transform-origin: center center; }
.state-outline {
  fill: var(--map-fill);
  stroke: var(--line-color);
  stroke-width: var(--line-width);
  vector-effect: non-scaling-stroke;
}
.city-marker-hitarea {
  fill: transparent;
  stroke: none;
}
.city-marker text {
  fill: var(--ink);
  font-family: "Times New Roman", Times, serif;
}
.city-marker { cursor: pointer; }

/* Constrained to the same width as the svg it wraps (rather than the full
   content column), left-justified rather than centered. Zoom/pan is
   double-click and click-drag only (no wheel/scroll listener) so it never
   fights page scrolling on a touchpad or mobile. */
.state-map-viewport {
  overflow: hidden;
  touch-action: none;
  cursor: grab;
  width: min(460px, 94%);
  margin: 0;
  background: var(--map-bg);
}
.state-map-viewport.is-panning { cursor: grabbing; }

.map-zoom-controls {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  margin: 0 0 0.5rem;
}
.map-zoom-controls .retro-btn {
  min-width: 2.1rem;
  padding: 0.25rem 0.6rem;
}
.map-zoom-hint {
  margin: 0;
  font-size: 0.8rem;
}

/* city / rapper lists */
.city-list { display: grid; gap: 0.75rem; }
.city-card {
  display: block;
  padding: 0.85rem 1.1rem;
  border: 1px solid var(--ink);
  border-radius: 0;
  color: var(--ink);
  text-decoration: none;
}
.city-card h3 { margin: 0; font-weight: 700; font-size: 1.1rem; text-transform: uppercase; }

.rapper-list { display: grid; gap: 1.25rem; }
.city-block {
  border: var(--line-width) solid var(--line-color);
  border-radius: 0;
  padding: 0.9rem 1.1rem;
}
.city-heading {
  display: block;
  color: var(--ink);
  text-decoration: none;
  font-size: 1.2rem;
  font-weight: 700;
  text-transform: uppercase;
  margin-bottom: 0.5rem;
}
.rapper-names { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.3rem; }
.rapper-names li::before { content: "- "; color: var(--ink-faded); }
.rapper-names a { color: var(--ink); text-decoration: none; }

/* page detail */
h1.page-title {
  color: var(--ink-faded);
  margin: 0 0 1.25rem;
  font-size: 1.9rem;
  font-weight: 700;
  text-transform: uppercase;
}
.page-subtitle {
  color: var(--ink-faded);
  margin: 0 0 1.5rem;
  font-size: 1rem;
}

.artist-list { display: grid; gap: 1rem; }
.artist-card {
  position: relative;
  border: var(--line-width) solid var(--line-color);
  border-radius: 0;
  padding: 0.9rem 1.1rem;
}
/* Points at an artist card jumped to from a search result, floating just
   outside its left edge. Offset (-1rem) is kept a little short of the
   page's own left padding (1.25rem on main#app) so it never clips off
   the edge of the viewport on narrow/mobile widths. */
.entry-pointer {
  position: absolute;
  left: -0.9rem;
  top: 50%;
  transform: translateY(-50%);
  font-size: 1.3rem;
  line-height: 1;
  color: #cc0000;
  animation: entry-pointer-bounce 0.8s ease-in-out infinite;
  pointer-events: none;
}
@keyframes entry-pointer-bounce {
  0%, 100% { transform: translateY(-50%) translateX(0); }
  50% { transform: translateY(-50%) translateX(-4px); }
}
@media (prefers-reduced-motion: reduce) {
  .entry-pointer { animation: none; }
}
.artist-card h3 {
  margin: 0 0 0.4rem;
  font-weight: 700;
  font-size: 1.2rem;
  text-transform: uppercase;
  color: var(--ink-faded);
}
.new-tag {
  display: inline-block;
  margin-left: 0.5rem;
  padding: 0.05rem 0.4rem;
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  vertical-align: middle;
  background: #cc0000;
  color: #ffffff;
  border: 1px solid #000000;
  animation: retro-blink 1s steps(1, start) infinite;
}
@keyframes retro-blink {
  50% { opacity: 0; }
}
@media (prefers-reduced-motion: reduce) {
  .new-tag { animation: none; }
}

/* Birth year pill, shown below the artist name (see birthYearTagHtml in
   app.js) — one fixed color per decade, so every artist born in the same
   decade reads the same at a glance across the whole site. */
.birth-year-tag {
  display: inline-block;
  margin: 0.3rem 0 0.5rem;
  padding: 0.05rem 0.45rem;
  font-size: 0.7rem;
  font-weight: 700;
  color: #ffffff;
  border: 1px solid #000000;
}
.decade-1900, .decade-1910, .decade-1920, .decade-1930, .decade-1940 { background: #5c3a21; }
.decade-1950 { background: #8b5a2b; }
.decade-1960 { background: #cc6600; }
.decade-1970 { background: #ccaa00; color: #000000; }
.decade-1980 { background: #008000; }
.decade-1990 { background: #0055cc; }
.decade-2000 { background: #a300a3; }
.decade-2010, .decade-2020 { background: #cc0066; }

.artist-card p {
  margin: 0 0 0.6rem;
  color: var(--ink-faded);
  font-size: 0.95rem;
}
.manage-controls {
  font-size: 0.85rem;
  margin: 0 0 0.5rem !important;
}
.manage-controls a { color: var(--ink); }

.track-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.3rem; }
.track-row { display: block; }
.track-static { color: var(--ink-faded); }
.track-toggle {
  font-family: inherit;
  font-size: 1rem;
  background: none;
  border: none;
  color: var(--ink);
  cursor: pointer;
  padding: 0.15rem 0;
  text-align: left;
  text-decoration: underline;
}

.section-heading {
  font-size: 0.95rem;
  font-weight: 700;
  text-transform: uppercase;
  color: var(--ink-faded);
  margin: 0 0 0.75rem;
}

/* ---- add-artist form ---- */
.gate-wrap { max-width: 420px; margin: 2rem auto; text-align: center; }
.gate-wrap input {
  width: 100%;
  padding: 0.55rem 0.75rem;
  font-size: 1rem;
  margin-bottom: 0.75rem;
}
.gate-wrap button, .form-actions button {
  font-size: 1rem;
  padding: 0.55rem 1.1rem;
}
.gate-error { color: var(--ink-faded); margin-top: 0.5rem; }

.add-form { display: grid; gap: 1rem; max-width: 560px; }
.add-form label {
  display: block;
  font-size: 0.85rem;
  text-transform: uppercase;
  color: var(--ink-faded);
  margin-bottom: 0.3rem;
}
.add-form input, .add-form select, .add-form textarea {
  width: 100%;
  padding: 0.5rem 0.65rem;
  font-size: 1rem;
}
.track-input-row { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.4rem; }
.track-input-row input { flex: 1 1 10rem; }
.track-input-row button {
  flex: 0 0 auto;
  padding: 0 0.6rem;
}
.add-track-btn {
  padding: 0.4rem 0.8rem;
  align-self: start;
}
.form-actions { display: flex; gap: 0.75rem; align-items: center; }
.form-status { color: var(--ink-faded); font-size: 0.9rem; }
.form-status.is-error { color: var(--ink); }
.snippet-box {
  background: transparent;
  border: var(--line-width) solid var(--line-color);
  color: var(--ink-faded);
  padding: 0.75rem;
  font-family: "Courier New", monospace;
  font-size: 0.8rem;
  white-space: pre-wrap;
  word-break: break-word;
  max-height: 240px;
  overflow-y: auto;
}

/* ---- bulk upload preview table ---- */
.bulk-table {
  width: 100%;
  border-collapse: collapse;
  margin: 1rem 0;
  font-size: 0.9rem;
}
.bulk-table th, .bulk-table td {
  border: var(--line-width) solid var(--line-color);
  padding: 0.4rem 0.6rem;
  text-align: left;
}
.bulk-table th { color: var(--ink-faded); font-weight: 700; }
.bulk-table td { color: var(--ink-faded); }
.bulk-row-error td:last-child { color: var(--ink); }

/* ---- theme editor ---- */
.theme-form {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
  gap: 1rem;
  margin: 1rem 0;
}
.theme-field label {
  display: block;
  font-size: 0.85rem;
  text-transform: uppercase;
  color: var(--ink-faded);
  margin-bottom: 0.3rem;
}
.theme-field input[type="color"] {
  width: 100%;
  height: 2.4rem;
  padding: 2px;
  border: var(--line-width) solid var(--line-color);
  background: #ffffff;
  cursor: pointer;
}
.theme-field input[type="number"] {
  width: 100%;
  padding: 0.5rem 0.65rem;
  font-size: 1rem;
}
.theme-advanced {
  margin: 1.25rem 0;
  border: var(--line-width) solid var(--line-color);
  padding: 0.75rem 1rem;
}
.theme-advanced summary {
  cursor: pointer;
  font-weight: 700;
  text-transform: uppercase;
  font-size: 0.85rem;
  color: var(--ink-faded);
}
.theme-advanced .theme-form { margin-bottom: 0; }
.theme-css-input {
  width: 100%;
  font-family: "Courier New", monospace;
  font-size: 0.85rem;
  padding: 0.6rem;
  margin-top: 0.5rem;
}
