/* zipcode.lol — design tokens and shell.
   Both themes are emitted as custom properties on [data-theme]; every component
   reads var(--token) so the theme toggle only has to swap the attribute. */

:root,
[data-theme="dark"] {
  --bg: #0A0B0C;
  --panel: #101213;
  --card: #16191A;
  --input: #191C1D;
  --hair: #1D2122;
  --bd: #23282A;
  --bd2: #262B2C;
  --bd3: #2A2F30;
  --btn: #1E2223;
  --btnbd: #2E3435;
  --sel: #3A4142;
  --tx: #E8EDEA;
  --tx2: #A9B2B0;
  --tx3: #8A9391;
  --tx4: #6B7472;
  --accent: #6EE7A0;
  --ink: #07130C;
  --soft: rgba(110, 231, 160, .09);
  --fill: rgba(255, 255, 255, .07);
  --shadow: rgba(0, 0, 0, .55);
  --glass: rgba(16, 18, 19, .92);
  --scrim: rgba(6, 7, 8, .72);
  --accbd: #26402F;
  --warn: #F2A05B;
  --bad: #E5675A;
  /* Ink that sits ON --bad. It has to flip with it: --bad is a light red on the
     dark theme and a dark red on the light one, so a fixed ink is legible in
     exactly one of them. The destructive button read at 3.5:1 in light mode and
     the error toast at 3.0:1 in dark before this existed. */
  --bad-ink: #1A0806;
  --stroke: rgba(255, 255, 255, .22);
  --polyFill: #8CE9B6;

  /* The veil over the landing page's hero map. It darkens the map in dark mode
     and lightens it in light mode; either way the headline has to stay readable
     over whatever the tiles happen to show. */
  /* The colours an advertiser can claim territory in.
     Eight slots, fixed order, never cycled — a bidder picks one and every area
     they lead wears it. Validated as a categorical palette against both surfaces
     (adjacent-pair CVD ΔE >= 8, normal-vision ΔE >= 15, chroma floor, lightness
     band). Do not add a ninth by inventing a hue: re-run the validator first.
     Slot names live in BRAND_COLORS in assets/js/core.js and must stay in step. */
  --brand-0: #3987e5;  /* blue    */
  --brand-1: #d95926;  /* orange  */
  --brand-2: #199e70;  /* aqua    */
  --brand-3: #c98500;  /* yellow  */
  --brand-4: #d55181;  /* magenta */
  --brand-5: #008300;  /* green   */
  --brand-6: #9085e9;  /* violet  */
  --brand-7: #e66767;  /* red     */

  --hero-veil: rgba(10, 11, 12, .93);
  --hero-veil-soft: rgba(10, 11, 12, .35);

  /* Pool ramp — one hue (brand green), seven steps, lightness monotone.
     Dark theme flips the anchor: the biggest pools are the brightest, because on a
     near-black map a dark fill reads as "nothing here", not as "a little". */
  --pool-0: #065b38;
  --pool-1: #007a47;
  --pool-2: #00975c;
  --pool-3: #38b277;
  --pool-4: #71ca99;
  --pool-5: #a5e0bd;
  --pool-6: #d8f6e3;
}

[data-theme="light"] {
  --bg: #EDEFEA;
  --panel: #FFFFFF;
  --card: #F7F8F4;
  --input: #F2F4EF;
  --hair: #E8EAE4;
  --bd: #E0E3DC;
  --bd2: #D9DCD4;
  --bd3: #CFD3CA;
  --btn: #F2F4EF;
  --btnbd: #D9DCD4;
  --sel: #C3C9BC;
  --tx: #151815;
  --tx2: #3E453F;
  --tx3: #5B635C;
  --tx4: #79817A;
  --accent: #0E9A60;
  --ink: #FFFFFF;
  --soft: rgba(14, 154, 96, .10);
  --fill: rgba(0, 0, 0, .07);
  --shadow: rgba(30, 45, 36, .16);
  --glass: rgba(255, 255, 255, .93);
  --scrim: rgba(28, 36, 31, .42);
  --accbd: #B7E2CC;
  --warn: #A85E17;
  --bad: #B93F2C;
  --bad-ink: #FFF3F1;
  --stroke: rgba(0, 0, 0, .3);
  --polyFill: #0E9A60;

  /* Light-mode steps of the same eight slots — chosen against the light surface,
     not an automatic lightening of the dark ones. */
  --brand-0: #2a78d6;
  --brand-1: #eb6834;
  --brand-2: #1baf7a;
  --brand-3: #eda100;
  --brand-4: #e87ba4;
  --brand-5: #008300;
  --brand-6: #4a3aa7;
  --brand-7: #e34948;

  --hero-veil: rgba(250, 251, 250, .92);
  --hero-veil-soft: rgba(250, 251, 250, .30);

  /* Same ramp, unflipped: on paper-white the lightest step is the emptiest ZIP. */
  --pool-0: #d8f6e3;
  --pool-1: #a5e0bd;
  --pool-2: #71ca99;
  --pool-3: #38b277;
  --pool-4: #00975c;
  --pool-5: #007a47;
  --pool-6: #065b38;
}

* { box-sizing: border-box; }

/* author display rules beat the UA's [hidden] rule — restore it */
[hidden] { display: none !important; }

html, body {
  margin: 0;
  padding: 0;
  background: var(--bg);
  color: var(--tx);
  font-family: 'Instrument Sans', system-ui, -apple-system, sans-serif;
  -webkit-font-smoothing: antialiased;
}

.mono { font-family: 'JetBrains Mono', ui-monospace, monospace; }

a { color: var(--accent); text-decoration: none; }
a:hover { opacity: .8; }
input, button, textarea, select { font-family: inherit; }
/* A button does not inherit colour — the UA hands it `buttontext`, which is
   near-black and unreadable on every dark surface in here. Inheriting instead
   means a themed <button> is right by default; anything that wants its own ink
   still sets it and still wins. */
button { cursor: pointer; color: inherit; }

::-webkit-scrollbar { width: 8px; height: 8px; }
::-webkit-scrollbar-thumb { background: var(--bd3); border-radius: 8px; }
::-webkit-scrollbar-track { background: transparent; }

@keyframes zbIn { from { opacity: 0; transform: translateY(8px); } to { opacity: 1; transform: translateY(0); } }
@keyframes zbPulse { 0%, 100% { opacity: .35; } 50% { opacity: .9; } }
@keyframes zbSpin { to { transform: rotate(360deg); } }

.zb-in { animation: zbIn .22s ease both; }
.pulse { animation: zbPulse 2s infinite; }

/* ---------- leaflet skin ---------- */

.leaflet-container {
  background: var(--bg);
  font-family: 'Instrument Sans', sans-serif;
  outline: none;
}
.leaflet-control-attribution {
  background: var(--glass) !important;
  color: var(--tx4) !important;
  font-size: 9px !important;
}
.leaflet-control-attribution a { color: var(--tx3) !important; }
/* --- zoom control ---
   Leaflet centres the + and − with line-height, which lands them at slightly
   different optical heights because the two glyphs have different vertical
   metrics. Grid centring puts both on the same axis whatever the font does, and
   an explicit square keeps the pair symmetrical at any size. */
.leaflet-bar { border: 0 !important; box-shadow: 0 4px 14px var(--shadow) !important; }
.leaflet-bar a {
  display: grid !important;
  place-items: center !important;
  width: 34px !important;
  height: 34px !important;
  line-height: 1 !important;
  font-size: 18px !important;
  font-weight: 600 !important;
  background: var(--glass) !important;
  color: var(--tx) !important;
  border: 1px solid var(--bd2) !important;
  backdrop-filter: blur(8px);
}
/* One shared edge between the two, not two stacked borders. */
.leaflet-bar a + a { border-top: 0 !important; }
.leaflet-bar a:first-child { border-radius: 10px 10px 0 0 !important; }
.leaflet-bar a:last-child  { border-radius: 0 0 10px 10px !important; }
.leaflet-bar a:hover { background: var(--btn) !important; color: var(--accent) !important; }
.leaflet-bar a.leaflet-disabled { opacity: .4 !important; }
.zb-lbl { background: none !important; border: 0 !important; }

/* ---------- map labels ----------
   These are rebuilt from scratch on every repaint — a few hundred of them in a
   wide viewport — so the markup each one carries is a hot path. Everything
   static lives here instead of being serialised into inline styles per chip;
   only the size varies, through the --sz custom property. Class names are
   terse for the same reason: this markup is generated, never hand-written. */

/* The disc sits ON the coordinate, with the caption hanging below it. Centring the
   whole stack instead (translate -50%) pushes the disc up by half the caption's
   height — about 13px for a chip with a pill — so the icon reads as offset from
   the place it marks. Only became visible once the tiers gained real outlines. */
.zb-chip, .zb-schip {
  position: relative;
  transform: translate(-50%, calc(var(--sz) / -2));
  display: flex;
  flex-direction: column;
  align-items: center;
  cursor: pointer;
}
.zb-chip { gap: 4px; }
.zb-schip { gap: 5px; }

/* the round brand disc */
.zb-chip .d, .zb-schip .d {
  position: relative;
  width: var(--sz);
  height: var(--sz);
  border-radius: 50%;
  background: #fff;
  color: #111;
  display: grid;
  place-items: center;
  font: 700 calc(var(--sz) * .44) 'Instrument Sans', system-ui, sans-serif;
  font-style: normal;
  overflow: hidden;
}
.zb-schip .d { font-size: calc(var(--sz) * .42); }
/* --ring is the leader's brand colour, set per chip in map-app.js, so the disc
   matches the fill it sits on. It falls back to the accent for anything drawn
   without a leader. */
.zb-chip .d { box-shadow: 0 4px 14px var(--shadow), 0 0 0 2px var(--ring, var(--accent)); }
.zb-schip .d { box-shadow: 0 6px 18px var(--shadow), 0 0 0 2.5px var(--ring, var(--accent)); }

.zb-chip .d img, .zb-schip .d img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  padding: calc(var(--sz) * .16);
  background: #fff;
  object-fit: contain;
}
.zb-schip .d img { padding: calc(var(--sz) * .15); }

/* ZIP chip: one pill, brand name + amount */
.zb-chip .p {
  display: flex;
  align-items: center;
  gap: 6px;
  background: var(--glass);
  border: 1px solid var(--bd2);
  border-radius: 7px;
  padding: 3px 7px;
  white-space: nowrap;
  font: 600 11px 'Instrument Sans', system-ui, sans-serif;
  color: var(--tx);
}
.zb-chip .p .v, .zb-schip .card .v {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-weight: 700;
  color: var(--accent);
}

/* state chip: two-line card under the disc */
.zb-schip .card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1px;
  background: var(--glass);
  border: 1px solid var(--bd2);
  border-radius: 8px;
  padding: 4px 9px;
  white-space: nowrap;
  box-shadow: 0 4px 14px var(--shadow);
}
.zb-schip .card .top {
  font: 700 9.5px 'JetBrains Mono', ui-monospace, monospace;
  color: var(--tx4);
  letter-spacing: .06em;
}
.zb-schip .card .lead {
  display: flex;
  align-items: center;
  gap: 6px;
  font: 600 12px 'Instrument Sans', system-ui, sans-serif;
  color: var(--tx);
}
.zb-chip.mine .p, .zb-schip.mine .card .lead { color: var(--accent); }

/* unclaimed marker, both tiers */
.zb-open {
  transform: translate(-50%, -50%);
  background: var(--glass);
  border: 1px dashed var(--bd3);
  border-radius: 8px;
  white-space: nowrap;
  cursor: pointer;
  color: var(--tx4);
}
.zb-open.z { padding: 4px 8px; font: 600 10.5px 'Instrument Sans', system-ui, sans-serif; }
.zb-open.s { padding: 5px 10px; font: 600 11px 'Instrument Sans', system-ui, sans-serif; }
.zb-open .m { font-family: 'JetBrains Mono', ui-monospace, monospace; }

/* zipFull ranking card */
.zb-stack {
  transform: translate(-50%, -50%);
  width: 186px;
  background: var(--glass);
  border: 1px solid var(--bd3);
  border-radius: 10px;
  overflow: hidden;
  box-shadow: 0 10px 30px var(--shadow);
  cursor: pointer;
}
.zb-stack .h {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 6px 8px;
  border-bottom: 1px solid var(--bd);
}
.zb-stack .h .z { font: 700 11px 'JetBrains Mono', ui-monospace, monospace; color: var(--tx); }
.zb-stack .h .t { font: 700 10px 'JetBrains Mono', ui-monospace, monospace; color: var(--accent); }
.zb-stack .r { display: flex; align-items: center; gap: 6px; padding: 4px 8px; }
.zb-stack .r.top { background: var(--soft); }
.zb-stack .r .n { width: 9px; font: 700 9px 'JetBrains Mono', ui-monospace, monospace; color: var(--tx4); }
.zb-stack .r.top .n { color: var(--accent); }
.zb-stack .r .a {
  position: relative;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  background: #fff;
  color: #111;
  display: grid;
  place-items: center;
  font: 700 9px 'Instrument Sans', system-ui, sans-serif;
  overflow: hidden;
  flex: 0 0 auto;
}
.zb-stack .r .a img { position: absolute; inset: 0; width: 100%; height: 100%; padding: 3px; background: #fff; object-fit: contain; }
.zb-stack .r .nm {
  flex: 1;
  font: 600 10.5px 'Instrument Sans', system-ui, sans-serif;
  color: var(--tx);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 96px;
}
.zb-stack .r.mine .nm { color: var(--accent); }
.zb-stack .r .pc { font: 700 9.5px 'JetBrains Mono', ui-monospace, monospace; color: var(--tx3); }
.zb-stack .more {
  padding: 4px 8px;
  font: 600 9.5px 'Instrument Sans', system-ui, sans-serif;
  color: var(--tx4);
  border-top: 1px solid var(--bd);
}

/* ---------- shell ---------- */

.shell {
  display: flex;
  /* dvh, with vh as the fallback for browsers that lack it.
     On a phone `vh` is measured against the viewport with the address bar
     collapsed — the tallest it ever gets — so a 100vh shell is taller than what
     is actually on screen and anything pinned to its bottom edge sits below the
     fold. That is why the zoom control and the listing card needed scrolling to
     reach. dvh is the height that is visible right now. */
  height: 100vh;
  height: 100dvh;
  overflow: hidden;
}
.shell.narrow { flex-direction: column; }

.rail {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px;
  background: var(--panel);
  border-right: 1px solid var(--bd);
  border-bottom: 1px solid var(--bd);
  flex: 0 0 56px;
  flex-direction: column;
}
.shell.narrow .rail { flex-direction: row; flex: 0 0 auto; }

.rail button {
  width: 36px; height: 36px; flex: 0 0 auto;
  border-radius: 10px;
  display: grid; place-items: center;
  font-size: 13px; font-weight: 700;
  border: 1px solid var(--bd2);
  background: var(--card);
  color: var(--tx3);
}
.rail button.pin { border: 0; background: var(--accent); }
.rail button.on { background: var(--soft); border-color: var(--accbd); color: var(--accent); }

.panel {
  max-width: 100%;
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border-right: 1px solid var(--bd);
  border-bottom: 1px solid var(--bd);
  min-width: 0;
  min-height: 0;
  flex: 0 0 372px;
}
/* height is set by the phone sheet rule further down */

.panel-head {
  display: flex; align-items: center; gap: 10px;
  padding: 16px 16px 12px;
  border-bottom: 1px solid var(--hair);
}
.logo {
  width: 32px; height: 32px; border-radius: 9px;
  background: var(--accent);
  display: grid; place-items: center; flex: 0 0 auto;
}
.brandline { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
.brandline a { font-size: 15px; font-weight: 700; letter-spacing: -.2px; color: var(--tx); }
.brandline span {
  font-size: 10.5px; color: var(--tx4);
  letter-spacing: .06em; text-transform: uppercase;
}
.icon-btn {
  width: 30px; height: 30px; flex: 0 0 auto;
  border: 1px solid var(--bd2); background: var(--card); color: var(--tx2);
  border-radius: 9px; font-size: 13px; line-height: 1;
}

.controls {
  padding: 12px 16px 10px;
  display: flex; flex-direction: column; gap: 10px;
  border-bottom: 1px solid var(--hair);
}
.seg {
  display: flex; background: var(--input);
  border: 1px solid var(--bd2); border-radius: 9px;
  padding: 2px; gap: 2px; flex: 0 0 auto;
}
.seg button {
  border: 0; padding: 5px 9px; border-radius: 7px;
  font-size: 11px; font-weight: 600;
  background: transparent; color: var(--tx3);
}
.seg button.on { background: var(--accent); color: var(--ink); }

.search { position: relative; display: flex; align-items: center; flex: 1; min-width: 0; }
.search svg { position: absolute; left: 11px; }
.search input {
  width: 100%; background: var(--input);
  border: 1px solid var(--bd2); border-radius: 10px;
  padding: 9px 12px 9px 32px; color: var(--tx); font-size: 13px; outline: none;
}
.search input:focus { border-color: var(--sel); }

.results { display: flex; flex-direction: column; gap: 4px; max-height: 220px; overflow: auto; }
.results button {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  background: var(--card); border: 1px solid var(--bd); border-radius: 9px;
  padding: 8px 10px; color: var(--tx);
}
.kind {
  font-family: 'JetBrains Mono', monospace; font-size: 9px; color: var(--accent);
  border: 1px solid var(--accbd); padding: 2px 5px; border-radius: 5px; letter-spacing: .06em;
}
.grow-ellip { font-size: 12.5px; font-weight: 600; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.tabs { display: flex; gap: 6px; }
.tabs button {
  flex: 1; border: 1px solid var(--bd); background: transparent; color: var(--tx3);
  border-radius: 9px; padding: 7px 6px; font-size: 11.5px; font-weight: 600;
}
.tabs button.on { background: var(--btn); border-color: var(--sel); color: var(--tx); }

.scroll { flex: 1; overflow-y: auto; overflow-x: hidden; min-height: 0; }
.pad { padding: 14px 16px 18px; }

/* ---------- sidebar pieces ---------- */

.crumb {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  font-size: 10.5px; color: var(--tx4); margin-bottom: 10px;
}
.crumb button { background: none; border: 0; color: var(--tx4); padding: 0; font-size: 10.5px; }

.zip-title { font-family: 'JetBrains Mono', monospace; font-size: 26px; font-weight: 700; letter-spacing: -.5px; line-height: 1; }

/* `color` matters here because .card is worn by <button> as well as <div> — the
   top-bidder row in the area panel is one. A button does not inherit colour from
   its parent; the UA gives it `buttontext`, which is near-black, so a surface that
   sets a background and not an ink renders invisible text on the dark theme.
   Every surface declares both. */
.card { background: var(--card); border: 1px solid var(--bd); border-radius: 12px; padding: 12px; color: var(--tx); }
.stat-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
.stat {
  background: var(--card); border: 1px solid var(--bd); border-radius: 11px; padding: 10px;
  color: var(--tx);
}
.stat b { font-family: 'JetBrains Mono', monospace; font-size: 15px; font-weight: 700; display: block; }
.stat span { font-size: 9.5px; color: var(--tx4); text-transform: uppercase; letter-spacing: .06em; }

.cta {
  width: 100%; background: var(--accent); color: var(--ink);
  border: 0; border-radius: 11px; padding: 12px; font-size: 13.5px; font-weight: 700;
}
.cta.sm { padding: 9px 12px; font-size: 12.5px; border-radius: 10px; }
.cta:disabled { opacity: .5; cursor: not-allowed; }

.ghost {
  background: var(--btn); border: 1px solid var(--btnbd); color: var(--tx);
  border-radius: 10px; padding: 10px 12px; font-size: 12.5px; font-weight: 600;
}

.row {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  background: var(--card); border: 1px solid var(--bd); border-radius: 11px;
  padding: 9px 11px; color: var(--tx);
}
.row.me { background: var(--soft); border-color: var(--accbd); }

/* Rows are cards, so consecutive ones need air between them — without this a list
   of them reads as one block with internal lines. */
.row + .row { margin-top: 6px; }

/* A row that navigates somewhere. */
.row-tap { cursor: pointer; }
.row-tap:hover { border-color: var(--sel); background: var(--fill); }

/* Same for stacked cards and for a heading followed by its list. */
.card + .card { margin-top: 10px; }
.label-sm + .row, .label-sm + .card { margin-top: 2px; }
.rank { width: 16px; font-family: 'JetBrains Mono', monospace; font-size: 11px; font-weight: 700; color: var(--tx4); flex: 0 0 auto; }
.rank.first { color: var(--accent); }

.avatar {
  border-radius: 50%; background: #fff center/70% no-repeat;
  display: grid; place-items: center; color: #111; font-weight: 700;
  overflow: hidden; flex: 0 0 auto;
}
.avatar img { width: 100%; height: 100%; object-fit: contain; background: #fff; }

.badge { font-size: 9px; font-weight: 700; border-radius: 4px; padding: 1px 4px; background: var(--accent); color: var(--ink); }
.badge.grey { background: var(--bd2); color: var(--tx2); }

.bar { height: 3px; border-radius: 99px; background: var(--fill); overflow: hidden; margin-top: 5px; }
.bar i { display: block; height: 100%; background: var(--accent); }

.empty {
  border: 1px dashed var(--bd3); border-radius: 12px; padding: 22px 14px;
  text-align: center; font-size: 12.5px; color: var(--tx4); line-height: 1.5;
}

.label-sm { font-size: 11px; font-weight: 600; color: var(--tx3); text-transform: uppercase; letter-spacing: .06em; }

/* ---------- map pane ---------- */

.map-pane { flex: 1 1 auto; position: relative; min-width: 0; min-height: 0; }
#map { position: absolute; inset: 0; }

.overlay { position: absolute; z-index: 500; }
/* 70px clears the floating nav (46px tall at top:12px) */
.overlay.tl { top: 70px; left: 12px; max-width: min(300px, 60%); pointer-events: none; }
/* The corner is bounded by the pane, not by the viewport: on a phone the map is
   shorter than the screen by the height of the rail, so a dvh-based cap would let
   a tall card run up past the notices at the top. A percentage of the pane is the
   space that actually exists. 118px leaves the notices (top 64, ~40 tall) alone. */
.overlay.bl {
  bottom: 12px; left: 12px; max-width: calc(100% - 24px);
  max-height: calc(100% - 118px);
  display: flex; flex-direction: column; justify-content: flex-end;
}

.dot { width: 6px; height: 6px; border-radius: 50%; background: var(--accent); flex: 0 0 auto; }



.leader-strip {
  display: flex; align-items: center; gap: 10px;
  background: var(--glass); border: 1px solid var(--bd2); border-radius: 12px;
  padding: 8px 12px 8px 8px; backdrop-filter: blur(8px);
}

.toasts {
  position: absolute; right: 12px; bottom: 12px; z-index: 1600;
  display: flex; flex-direction: column; gap: 8px; align-items: flex-end;
}
.toast {
  background: var(--card); border: 1px solid var(--bd3); border-left: 3px solid var(--accent);
  border-radius: 11px; padding: 11px 13px; max-width: 330px;
  box-shadow: 0 14px 40px var(--shadow); animation: zbIn .2s ease both;
  display: flex; gap: 10px; align-items: flex-start;
}
.toast b { font-size: 12.5px; font-weight: 700; color: var(--accent); display: block; }
.toast p { margin: 3px 0 0; font-size: 11.5px; color: var(--tx2); line-height: 1.4; }
.toast.warn { border-left-color: var(--warn); } .toast.warn b { color: var(--warn); }
.toast.bad { border-left-color: var(--bad); } .toast.bad b { color: var(--bad); }
.toast button { background: none; border: 0; color: var(--tx4); font-size: 14px; line-height: 1; padding: 0; }

/* ---------- listing card ---------- */

.listing {
  width: min(340px, calc(100vw - 28px));
  background: var(--panel); border: 1px solid var(--bd3); border-radius: 14px;
  padding: 14px; box-shadow: 0 20px 50px var(--shadow); animation: zbIn .22s ease both;
  /* A listing with a long message and several stats is taller than a laptop in
     landscape, never mind a phone. It scrolls inside itself rather than growing
     off the bottom of the map; the corner above sets how much room there is. */
  min-height: 0;
  max-height: 100%;
  overflow-y: auto;
  overscroll-behavior: contain;
}
.chip-status {
  font-size: 9px; font-weight: 700; letter-spacing: .08em; border-radius: 6px;
  padding: 3px 6px; background: var(--soft); color: var(--accent);
}
.chip-status.plain { background: transparent; color: var(--tx4); }
.tiles { display: grid; grid-template-columns: repeat(3, 1fr); gap: 7px; }
.tile { background: var(--card); border: 1px solid var(--bd); border-radius: 9px; padding: 8px; }
.tile b { font-family: 'JetBrains Mono', monospace; font-size: 13px; font-weight: 700; display: block; }
.tile span { font-size: 8.5px; color: var(--tx4); text-transform: uppercase; letter-spacing: .05em; }

/* ---------- modal ---------- */

.scrim {
  position: fixed; inset: 0; z-index: 1400;
  background: var(--scrim); backdrop-filter: blur(4px);
  display: grid; place-items: center; padding: 20px;
}
.dialog {
  width: 100%; max-width: 430px; background: var(--panel);
  border: 1px solid var(--bd3); border-radius: 16px; padding: 18px;
  box-shadow: 0 30px 80px var(--shadow); max-height: 92vh; overflow: auto;
  animation: zbIn .22s ease both;
}
.field {
  display: flex; align-items: center; gap: 10px;
  background: var(--input); border: 1px solid var(--bd2); border-radius: 10px; padding: 8px 10px;
}
.field input, .field textarea {
  flex: 1; min-width: 0; background: none; border: 0; outline: none;
  color: var(--tx); font-size: 13px; resize: none;
}
.help { font-size: 10.5px; color: var(--tx4); margin-top: 5px; }

/* The brand-colour picker in the bid form.
   Eight swatches on one row that wraps to two on a narrow screen, each carrying
   its own name — the colour is never the only thing telling them apart, which is
   what the palette's light-mode contrast needs and what a colour-blind bidder
   needs to pick the one they meant. --sw is set per button from the theme's
   --brand-N, so the swatch and the map fill cannot drift apart. */
.swatch-row { display: grid; grid-template-columns: repeat(4, 1fr); gap: 6px; }
.swatch {
  position: relative; display: flex; flex-direction: column; align-items: center; gap: 4px;
  padding: 7px 2px 5px; border: 1px solid var(--bd3); border-radius: 10px;
  background: var(--input); cursor: pointer; min-height: 44px;
}
.swatch::before {
  content: ''; width: 20px; height: 20px; border-radius: 50%;
  background: var(--sw); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .18);
}
.swatch-name { font-size: 9.5px; font-weight: 600; color: var(--tx4); letter-spacing: .02em; }
.swatch:hover { border-color: var(--bd2); }
.swatch.on { border-color: var(--sw); background: var(--soft); }
.swatch.on .swatch-name { color: var(--tx2); }
/* The tick is the second channel: on a small screen the ring alone is easy to
   miss, and for a bidder who cannot separate two hues it is the only signal. */
.swatch.on::after {
  content: '✓'; position: absolute; top: 3px; right: 5px;
  font-size: 9px; font-weight: 700; color: var(--sw);
}
.swatch:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

@media (max-width: 380px) {
  .swatch-row { grid-template-columns: repeat(4, 1fr); }
  .swatch-name { font-size: 9px; }
}
.quick { display: flex; flex-wrap: wrap; gap: 6px; }
.quick button {
  background: var(--btn); border: 1px solid var(--btnbd); color: var(--tx2);
  border-radius: 8px; padding: 6px 9px; font-size: 11px; font-weight: 600;
}
/* Small action button inside a row — same shape as .quick's buttons, but it lives
   on its own rather than in a wrap-around group. */
.quick-btn {
  background: var(--btn); border: 1px solid var(--btnbd); color: var(--tx2);
  border-radius: 8px; padding: 6px 8px; font-size: 10.5px; font-weight: 600;
}
.quick-btn:hover { color: var(--tx); border-color: var(--sel); }

.proj { display: flex; flex-direction: column; gap: 8px; }
.proj div { display: flex; justify-content: space-between; align-items: baseline; gap: 10px; }
.proj span { font-size: 11.5px; color: var(--tx3); }
.proj b { font-family: 'JetBrains Mono', monospace; font-size: 12.5px; font-weight: 700; }
.err {
  background: var(--fill); border: 1px solid var(--bad); color: var(--bad);
  border-radius: 10px; padding: 9px 11px; font-size: 11.5px;
}

/* ---------- auth pages ---------- */

.auth-wrap { min-height: 100vh; display: grid; place-items: center; padding: 24px; }
.auth-card {
  width: 100%; max-width: 400px; background: var(--panel);
  border: 1px solid var(--bd3); border-radius: 16px; padding: 22px;
  box-shadow: 0 30px 80px var(--shadow);
}
.auth-card h1 { font-size: 22px; letter-spacing: -.6px; margin: 0 0 4px; }
.auth-card p.sub { font-size: 12.5px; color: var(--tx4); margin: 0 0 18px; line-height: 1.5; }
.auth-card label { display: block; font-size: 11px; font-weight: 600; color: var(--tx3); margin: 12px 0 5px; }
.auth-card input {
  width: 100%; background: var(--input); border: 1px solid var(--bd2); border-radius: 10px;
  padding: 10px 12px; color: var(--tx); font-size: 13px; outline: none;
}
.auth-card input:focus { border-color: var(--sel); }

/* ---------- browse view ---------- */

.browse-body { background: var(--bg); color: var(--tx); min-height: 100vh; }

.br-totals {
  display: flex; flex-wrap: wrap; gap: 2px;
  padding: 14px 20px; border-bottom: 1px solid var(--bd); background: var(--panel);
}
.br-total { display: flex; flex-direction: column; gap: 2px; padding: 0 26px 0 0; }
.br-total .k { font-size: 11px; color: var(--tx4); text-transform: uppercase; letter-spacing: .06em; }
.br-total .v { font: 700 18px 'JetBrains Mono', ui-monospace, monospace; color: var(--tx); }

.br-main { display: flex; align-items: flex-start; gap: 20px; padding: 20px; max-width: 1500px; margin: 0 auto; }

.br-filters {
  flex: 0 0 258px; position: sticky; top: 76px;
  background: var(--card); border: 1px solid var(--bd); border-radius: 12px; padding: 16px;
  display: flex; flex-direction: column; gap: 14px;
}
.br-filter-head { display: flex; align-items: center; justify-content: space-between; }
.br-filter-head h2 { font-size: 13px; text-transform: uppercase; letter-spacing: .07em; color: var(--tx4); margin: 0; }
.br-field { display: flex; flex-direction: column; gap: 6px; }
.br-field > span { font-size: 12px; font-weight: 600; color: var(--tx3); }
.br-field input, .br-field select {
  background: var(--input); border: 1px solid var(--bd2); border-radius: 8px;
  padding: 8px 10px; color: var(--tx); font-size: 13px; outline: none; width: 100%;
}
.br-field input:focus, .br-field select:focus { border-color: var(--sel); }
.br-range > div { display: flex; gap: 8px; }
.br-seg { display: flex; background: var(--input); border: 1px solid var(--bd2); border-radius: 8px; padding: 2px; }
.br-seg button {
  flex: 1; border: 0; background: none; color: var(--tx3);
  padding: 6px 4px; border-radius: 6px; font-size: 12px; font-weight: 600;
}
.br-seg button.on { background: var(--soft); color: var(--accent); }

.br-content { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 18px; }

.br-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 1080px) { .br-cards { grid-template-columns: 1fr; } .br-main { flex-direction: column; } .br-filters { position: static; width: 100%; flex: 1 1 auto; } }
.br-card { background: var(--card); border: 1px solid var(--bd); border-radius: 12px; padding: 14px 16px; }
.br-card h3 { margin: 0 0 10px; font-size: 12px; text-transform: uppercase; letter-spacing: .07em; color: var(--tx4); }
/* Rows carry a hover background but no border, so without a gap the two hover
   blocks meet and read as one shape. Both feed cards use this class, which is
   what keeps "Latest bids" and "Most valuable ZIPs" the same height beside each
   other — change the spacing here, never on one card. */
.br-feed { display: flex; flex-direction: column; gap: 4px; }
.br-row {
  display: flex; align-items: center; gap: 10px; padding: 8px 8px;
  border-radius: 8px; text-decoration: none; color: var(--tx);
}
.br-row:hover { background: var(--fill); }
.br-row.mine { background: var(--soft); }
.br-row-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.br-row-main strong { font-size: 13px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.br-sub { font-size: 11.5px; color: var(--tx4); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.br-amt { font: 700 12.5px 'JetBrains Mono', ui-monospace, monospace; color: var(--accent); }
.br-rank { width: 18px; font: 700 11px 'JetBrains Mono', ui-monospace, monospace; color: var(--tx4); text-align: center; }
.br-dot { width: 22px; height: 22px; border-radius: 50%; background: var(--fill); flex: 0 0 auto; }
.br-empty { color: var(--tx4); font-size: 13px; padding: 10px 4px; text-align: center; }

.br-listing { background: var(--card); border: 1px solid var(--bd); border-radius: 12px; overflow: hidden; }
.br-listing-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 13px 16px; border-bottom: 1px solid var(--bd);
}
.br-listing-head h3 { margin: 0; font-size: 14px; font-weight: 600; }
.br-pager { display: flex; align-items: center; gap: 8px; }
.br-page-label { font: 600 12px 'JetBrains Mono', ui-monospace, monospace; color: var(--tx4); }
.ghost.off { opacity: .35; pointer-events: none; }

.br-table-wrap { overflow-x: auto; }
.br-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.br-table th {
  text-align: left; padding: 9px 16px; font-size: 11px; font-weight: 600;
  text-transform: uppercase; letter-spacing: .06em; color: var(--tx4);
  border-bottom: 1px solid var(--bd); white-space: nowrap;
}
.br-table td { padding: 9px 16px; border-bottom: 1px solid var(--hair); white-space: nowrap; }
.br-table tr:last-child td { border-bottom: 0; }
.br-table tbody tr:hover { background: var(--fill); }
.br-table .num { text-align: right; }
.br-table .mono { font-family: 'JetBrains Mono', ui-monospace, monospace; }
.br-table a { color: var(--tx); text-decoration: none; font-weight: 600; }
.br-table a:hover { color: var(--accent); }
.br-state { color: var(--tx4); margin-left: 6px; font-size: 11.5px; }
.br-leader { font-weight: 600; }
.br-leader.mine { color: var(--accent); }
.br-open-tag {
  font-size: 11px; font-weight: 600; color: var(--accent);
  background: var(--soft); border-radius: 5px; padding: 2px 7px;
}

/* Sits at the foot of the results, not pinned to the viewport: it describes the
   result set, so it belongs where the result set ends. */
.br-bulk {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
  padding: 14px 16px; border-top: 1px solid var(--bd3);
  background: var(--soft);
}
.br-bulk-text { font-size: 13.5px; color: var(--tx2); }
.br-bulk-text strong { color: var(--tx); font-family: 'JetBrains Mono', ui-monospace, monospace; }
.br-bulk .cta { margin-left: auto; }

/* modal shared by the browse actions */
.br-scrim {
  position: fixed; inset: 0; z-index: 1400; display: grid; place-items: center;
  background: var(--scrim); padding: 20px;
}
.br-modal {
  width: 100%; max-width: 430px; background: var(--panel);
  border: 1px solid var(--bd3); border-radius: 14px; padding: 20px;
  display: flex; flex-direction: column; gap: 12px; max-height: 90vh; overflow: auto;
}
.br-modal h3 { margin: 0; font-size: 17px; }
.br-modal-sub { margin: 0; font-size: 13px; color: var(--tx3); }
.br-modal-field { display: flex; flex-direction: column; gap: 5px; }
.br-modal-field > span { font-size: 12px; font-weight: 600; color: var(--tx3); }
.br-modal-field input {
  background: var(--input); border: 1px solid var(--bd2); border-radius: 8px;
  padding: 9px 11px; color: var(--tx); font-size: 13.5px; outline: none;
}
.br-modal-field input:focus { border-color: var(--sel); }
.br-modal-field small { font-size: 11.5px; color: var(--tx4); }
.br-modal-actions { display: flex; gap: 10px; justify-content: flex-end; margin-top: 4px; }
.br-note { margin: 0; font: 600 13px 'JetBrains Mono', ui-monospace, monospace; color: var(--accent); }
.br-err { margin: 0; font-size: 13px; color: var(--warn); }

/* the map's filter panel reuses .field for its select */
select.field {
  background: var(--input); border: 1px solid var(--bd2); border-radius: 9px;
  color: var(--tx); font-size: 13px; outline: none;
}
select.field:focus { border-color: var(--sel); }
#filterBadge { cursor: pointer; }

/* ---------- universal navigation ---------- */

.zb-nav {
  display: flex; align-items: center; gap: 20px;
  height: 54px; padding: 0 18px;
  font-size: 13px;
}
.zb-nav-bar {
  background: var(--panel); border-bottom: 1px solid var(--bd);
  position: sticky; top: 0; z-index: 60;
}
/* On the landing hero the nav is part of the artwork: it sits in the flow at the
   top of the full-bleed section with nothing behind it, so the image runs
   uninterrupted to the edge of the screen. */
.zb-nav-hero {
  position: relative; z-index: 5;
  height: 74px; padding: 0 30px;
  background: none; border: 0;
  gap: 26px;
}
.zb-nav-hero .zb-nav-brand { font-size: 15px; letter-spacing: .04em; }
.zb-nav-hero .zb-nav-mark { width: 30px; height: 30px; border-radius: 9px; }
.zb-nav-hero .zb-nav-mark svg { width: 17px; height: 17px; }
/* Theme tokens, not literal white. This nav used to be written in white because
   the hero underneath it was always dark; once the landing page followed the
   theme, light mode put white links on a light hero and they vanished. --fill
   already flips from a white tint to a black one, so the hover and active states
   need no theme-specific rules of their own. */
.zb-nav-hero .zb-nav-links a { color: var(--tx2); }
.zb-nav-hero .zb-nav-links a:hover { background: var(--fill); color: var(--tx); }
.zb-nav-hero .zb-nav-links a.on { background: var(--fill); color: var(--tx); }
.zb-nav-hero .zb-nav-who { color: var(--tx4); }
.zb-nav-hero .zb-nav-theme {
  background: var(--fill); border-color: var(--bd2); color: var(--tx2);
}
.zb-nav-hero .zb-nav-theme:hover { color: var(--tx); }

@media (max-width: 640px) {
  .zb-nav-hero { height: 62px; padding: 0 16px; gap: 14px; }
}

/* the map has no room for a header, so the bar floats above it instead */
.zb-nav-overlay {
  position: absolute; top: 12px; left: 12px; right: 12px; z-index: 620;
  height: 46px; border-radius: 12px;
  background: var(--glass); border: 1px solid var(--bd2);
  backdrop-filter: blur(10px);
  box-shadow: 0 8px 24px var(--shadow);
  pointer-events: auto;
}

.zb-nav-brand {
  display: flex; align-items: center; gap: 9px;
  font-weight: 700; color: var(--tx); text-decoration: none; white-space: nowrap;
}
.zb-nav-mark {
  width: 24px; height: 24px; border-radius: 7px;
  background: var(--accent); color: var(--ink);
  display: grid; place-items: center; flex: 0 0 auto;
}
.zb-nav-mark svg { width: 14px; height: 14px; }

/* Links sit on the right on every page: the brand anchors the left, and the rest of
   the bar stays out of the way of whatever the page puts under it. */
.zb-nav-links { display: flex; gap: 3px; margin-left: auto; }
.zb-nav-links a {
  padding: 6px 12px; border-radius: 8px;
  font-weight: 600; color: var(--tx3); text-decoration: none;
}
.zb-nav-links a:hover { background: var(--btn); color: var(--tx); }
.zb-nav-links a.on { background: var(--soft); color: var(--accent); }

.zb-nav-right { display: flex; align-items: center; gap: 10px; }
.zb-nav-who {
  font: 500 11.5px 'JetBrains Mono', ui-monospace, monospace;
  color: var(--tx4); text-decoration: none; max-width: 220px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.zb-nav-who:hover { color: var(--tx3); }
.zb-nav-theme {
  border: 1px solid var(--btnbd); background: var(--btn); color: var(--tx3);
  width: 30px; height: 30px; border-radius: 8px; font-size: 13px;
}
.zb-nav-theme:hover { color: var(--tx); }

@media (max-width: 640px) {
  .zb-nav { gap: 12px; padding: 0 12px; }
  .zb-nav-brand span:last-child { display: none; }
  .zb-nav-who { display: none; }
}

#brSearch { width: 100%; }
.br-filter-note { margin: 0; font-size: 11.5px; color: var(--warn); text-align: center; }
.pulse-hint { box-shadow: 0 0 0 3px var(--soft); }
.br-table th.sortable { cursor: pointer; user-select: none; }
.br-table th.sortable:hover { color: var(--tx2); }
.br-table th.sorted { color: var(--accent); }
.br-table th .arrow { font-size: 10px; }
.crumb-link { cursor: pointer; }
.crumb-link:hover { color: var(--tx2); text-decoration: underline; }
.br-fetch { display: flex; align-items: center; gap: 10px; margin: -4px 0 2px; }
.br-fetch-note { font-size: 11.5px; color: var(--tx4); flex: 1; min-width: 0; }
.zb-nav-demo {
  font: 700 9.5px 'JetBrains Mono', ui-monospace, monospace;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--warn); border: 1px solid var(--warn); opacity: .8;
  border-radius: 5px; padding: 2px 6px;
}
/* A brand mark inside a row that opens the brand rather than the row's own target. */
.brand-tap { display: inline-flex; border-radius: 50%; cursor: pointer; flex: 0 0 auto; }
.brand-tap:hover { box-shadow: 0 0 0 2px var(--accent); }

/* ---------- pool ramp legend ---------- */

.legend {
  display: flex; align-items: center; gap: 7px;
  margin-top: 8px; padding: 6px 9px;
  background: var(--glass); border: 1px solid var(--bd2); border-radius: 9px;
  backdrop-filter: blur(8px);
}
.legend-cap {
  font: 600 9.5px 'JetBrains Mono', ui-monospace, monospace;
  color: var(--tx4); letter-spacing: .05em; text-transform: uppercase;
}
.legend-hi { color: var(--tx3); }
/* Set off from the ramp by a hairline: it labels the other channel, not the
   ramp's top end, and read as "$100k+ colour" when it sat flush against it. */
.legend-owner {
  padding-left: 7px; margin-left: 1px; border-left: 1px solid var(--bd2);
  color: var(--tx3); text-transform: none; letter-spacing: .02em;
}
.legend-ramp { display: flex; gap: 2px; }
.legend-ramp i {
  width: 16px; height: 9px; border-radius: 2px; display: block;
}
@media (max-width: 640px) { .legend { display: none; } }

/* ==========================================================================
   Back office (postmaster.php)

   Deliberately plainer than the public side: dense tables, no glass, no
   animation. This is a tool, and the operator is reading numbers, not being
   sold anything. It reuses the app's tokens so light/dark still work, but
   none of the map's chrome.
   ========================================================================== */

.pm-body { background: var(--bg); color: var(--tx); font-family: 'Instrument Sans', system-ui, sans-serif; margin: 0; }

/* --- shared small controls the panel needs (the public pages use .cta) --- */
.primary {
  background: var(--accent); color: var(--ink); border: 0; border-radius: 10px;
  padding: 10px 14px; font-size: 12.5px; font-weight: 700; cursor: pointer;
}
.primary:disabled, .ghost:disabled { opacity: .45; cursor: not-allowed; }
.ghost { cursor: pointer; }
.sm { padding: 6px 10px; font-size: 11.5px; border-radius: 8px; }
.block { display: block; width: 100%; }
.danger { color: var(--bad); }
button.danger.ghost:hover { border-color: var(--bad); }
.pm-body .danger:not(.ghost) { background: var(--bad); color: var(--bad-ink); border: 0; }

/* --- gate --- */
.pm-gate { min-height: 100vh; display: grid; place-items: center; padding: 24px; }
.pm-gate-card {
  width: 100%; max-width: 340px; background: var(--card); border: 1px solid var(--bd);
  border-radius: 16px; padding: 26px 24px; display: flex; flex-direction: column; gap: 12px;
}
.pm-gate-mark { width: 34px; height: 34px; color: var(--accent); }
.pm-gate-mark svg { width: 100%; height: 100%; }
.pm-gate-card h1 { font-size: 19px; margin: 2px 0 0; letter-spacing: -.3px; }
.pm-gate-sub { color: var(--tx4); font-size: 12px; margin: -6px 0 6px; }
.pm-gate-err {
  background: rgba(229, 103, 90, .12); border: 1px solid rgba(229, 103, 90, .3);
  color: var(--bad); border-radius: 9px; padding: 9px 11px; font-size: 12px; margin: 0;
}
.pm-gate-note { font-size: 11.5px; color: var(--tx4); line-height: 1.5; margin: 4px 0 0; }
.pm-gate-note code { font-family: 'JetBrains Mono', monospace; color: var(--tx2); }

/* --- chrome --- */
.pm-top {
  position: sticky; top: 0; z-index: 20; display: flex; align-items: center; gap: 16px;
  padding: 10px 18px; background: var(--panel); border-bottom: 1px solid var(--bd);
}
.pm-top-left, .pm-top-right { display: flex; align-items: center; gap: 8px; }
.pm-top-right { margin-left: auto; }
.pm-top-right a { text-decoration: none; }
.pm-badge {
  font-size: 10.5px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase;
  color: var(--accent); background: var(--soft); border: 1px solid var(--accbd);
  border-radius: 7px; padding: 4px 8px;
}
.pm-tabs { display: flex; gap: 2px; overflow-x: auto; }
.pm-tabs button {
  background: none; border: 0; border-radius: 8px; color: var(--tx3);
  padding: 7px 12px; font-size: 12.5px; font-weight: 600; cursor: pointer; white-space: nowrap;
}
.pm-tabs button:hover { color: var(--tx); background: var(--fill); }
.pm-tabs button.on { color: var(--tx); background: var(--btn); }

.pm-main { padding: 18px; max-width: 1500px; margin: 0 auto; }
.pm-panel { display: flex; flex-direction: column; gap: 14px; }

/* --- tiles & cards --- */
.pm-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 10px; }
.pm-tile {
  background: var(--card); border: 1px solid var(--bd); border-radius: 12px;
  padding: 13px 14px; display: flex; flex-direction: column; gap: 3px;
}
.pm-tile-label { font-size: 9.5px; text-transform: uppercase; letter-spacing: .07em; color: var(--tx4); }
.pm-tile-value { font-family: 'JetBrains Mono', monospace; font-size: 22px; font-weight: 700; letter-spacing: -.5px; }
.pm-tile-sub { font-size: 11px; color: var(--tx4); line-height: 1.4; }

.pm-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; align-items: start; }
@media (max-width: 980px) { .pm-cols { grid-template-columns: 1fr; } }

.pm-card { background: var(--card); border: 1px solid var(--bd); border-radius: 13px; padding: 15px; }
.pm-card h2 { font-size: 12px; text-transform: uppercase; letter-spacing: .07em; color: var(--tx3); margin: 0 0 12px; font-weight: 600; }
.pm-card > * + * { margin-top: 12px; }

.pm-note {
  background: rgba(242, 160, 91, .1); border: 1px solid rgba(242, 160, 91, .28);
  border-radius: 11px; padding: 11px 13px; font-size: 12px; color: var(--tx2); line-height: 1.5;
}
.pm-note strong { color: var(--warn); }
.pm-hint { font-size: 11.5px; color: var(--tx4); line-height: 1.5; margin: 0; }
.pm-sub { font-size: 12.5px; color: var(--tx2); margin: 0 0 4px; }
.pm-empty { color: var(--tx4); font-size: 12.5px; padding: 20px 0; text-align: center; margin: 0; }
.pm-loading { color: var(--tx4); font-size: 12.5px; padding: 28px 0; text-align: center; }

/* --- chart --- */
.pm-chart { display: flex; align-items: flex-end; gap: 3px; height: 150px; padding-top: 18px; }
.pm-bar { flex: 1; height: 100%; display: flex; flex-direction: column; justify-content: flex-end; position: relative; }
.pm-bar i { display: block; background: var(--pool-2); border-radius: 3px 3px 0 0; transition: background .12s; }
.pm-bar.peak i { background: var(--accent); }
.pm-bar:hover i { background: var(--accent); }
/* The only direct label is the peak — a number on every bar is noise. */
.pm-bar b {
  position: absolute; top: -16px; left: 50%; transform: translateX(-50%);
  font-family: 'JetBrains Mono', monospace; font-size: 10px; color: var(--tx3); white-space: nowrap;
}

/* --- tables --- */
.pm-list { min-height: 60px; }
.pm-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.pm-table th {
  text-align: left; font-size: 9.5px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--tx4); font-weight: 600; padding: 0 10px 8px; border-bottom: 1px solid var(--bd);
  white-space: nowrap;
}
.pm-table th.sortable { cursor: pointer; user-select: none; }
.pm-table th.sortable:hover, .pm-table th.on { color: var(--tx2); }
.pm-table td { padding: 9px 10px; border-bottom: 1px solid var(--hair); vertical-align: middle; }
.pm-table tbody tr:hover { background: var(--fill); }
.pm-table td.num { font-family: 'JetBrains Mono', monospace; text-align: right; white-space: nowrap; }
.pm-table td.mono, .pm-table td .mono { font-family: 'JetBrains Mono', monospace; font-size: 11.5px; }
.pm-table td.dim, .pm-table .dim { color: var(--tx4); }
.pm-table td.small, .pm-table .small { font-size: 10.5px; }
.pm-table td.right { text-align: right; white-space: nowrap; }
.pm-table td.wrap { white-space: normal; word-break: break-word; max-width: 380px; }
.pm-table code { font-family: 'JetBrains Mono', monospace; font-size: 11.5px; color: var(--tx2); }
.pm-table.tight td, .pm-table.tight th { padding: 6px 8px; }
.pm-table td .ghost + .ghost { margin-left: 4px; }

.pm-brand { display: flex; align-items: center; gap: 8px; }
.pm-brand strong { display: block; font-weight: 600; }
.pm-brand span { display: block; font-size: 10.5px; }
.pm-pin { margin-left: 5px; font-size: 10px; }
/* The listing's map colour. Named as well as coloured — this table is scanned in
   a hurry, and a bare dot is unreadable to anyone who cannot separate two hues.
   .auto marks a colour derived from the domain rather than one the bidder chose. */
.pm-color { display: inline-flex; align-items: center; gap: 5px; margin-left: auto; font-size: 10.5px; color: var(--tx4); }
.pm-color i { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .2); }
.pm-color.auto { opacity: .62; font-style: italic; }
.pm-err-line { font-size: 10.5px; color: var(--bad); margin-top: 2px; }

.pm-pager {
  display: flex; align-items: center; justify-content: center; gap: 12px;
  padding: 14px 0 2px; font-size: 11.5px; color: var(--tx4);
}

/* --- chips --- */
.pm-chip {
  display: inline-block; font-size: 10px; font-weight: 600; letter-spacing: .04em;
  padding: 3px 7px; border-radius: 6px; background: var(--btn); border: 1px solid var(--btnbd); color: var(--tx3);
}
.pm-chip.active, .pm-chip.paid, .pm-chip.sent, .pm-chip.yes {
  background: var(--soft); border-color: var(--accbd); color: var(--accent);
}
.pm-chip.pending, .pm-chip.queued, .pm-chip.warn {
  background: rgba(242, 160, 91, .12); border-color: rgba(242, 160, 91, .3); color: var(--warn);
}
.pm-chip.released, .pm-chip.failed, .pm-chip.expired {
  background: rgba(229, 103, 90, .12); border-color: rgba(229, 103, 90, .3); color: var(--bad);
}
.pm-chips { display: flex; flex-wrap: wrap; gap: 5px; margin-bottom: 12px; }
.pm-chip.filter { cursor: pointer; }
.pm-chip.filter.on { background: var(--sel); color: var(--tx); border-color: var(--sel); }

/* --- forms --- */
.pm-toolbar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.pm-toolbar .pm-input { max-width: 300px; }
.pm-toolbar .pm-hint { margin-left: auto; font-family: 'JetBrains Mono', monospace; font-size: 10.5px; }

.pm-input {
  background: var(--input); border: 1px solid var(--bd2); border-radius: 9px;
  color: var(--tx); padding: 9px 11px; font: inherit; font-size: 12.5px; width: 100%;
}
.pm-input:focus { outline: none; border-color: var(--sel); }
.pm-input.tall { font-family: 'JetBrains Mono', monospace; font-size: 11.5px; line-height: 1.6; resize: vertical; }
select.pm-input { cursor: pointer; }

.pm-field { display: flex; flex-direction: column; gap: 5px; }
.pm-field > span { font-size: 10.5px; text-transform: uppercase; letter-spacing: .06em; color: var(--tx4); font-weight: 600; }
.pm-field > em { font-style: normal; font-size: 11px; color: var(--tx4); line-height: 1.5; }
.pm-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 620px) { .pm-grid2 { grid-template-columns: 1fr; } }

.pm-check { display: flex; align-items: flex-start; gap: 8px; font-size: 12.5px; color: var(--tx2); cursor: pointer; line-height: 1.45; }
.pm-check input { margin: 1px 0 0; width: 15px; height: 15px; accent-color: var(--accent); flex: 0 0 auto; }

.pm-save { display: flex; justify-content: flex-end; padding-top: 4px; }
.pm-testrow { display: flex; gap: 8px; align-items: center; }
.pm-testrow .pm-input { flex: 1; }
.pm-test-out:empty { display: none; }
.pm-ok { color: var(--accent); font-size: 12px; margin: 0; }
.pm-bad { color: var(--bad); font-size: 12px; margin: 0; line-height: 1.5; }
.pm-transcript {
  background: var(--input); border: 1px solid var(--bd); border-radius: 9px;
  padding: 10px; font-family: 'JetBrains Mono', monospace; font-size: 10.5px;
  color: var(--tx3); max-height: 200px; overflow: auto; margin: 8px 0 0; white-space: pre-wrap;
}

/* --- modal --- */
.pm-modal-back {
  position: fixed; inset: 0; z-index: 1400; background: var(--scrim);
  display: flex; align-items: center; justify-content: center; padding: 24px;
}
.pm-modal-back[hidden] { display: none; }
.pm-modal {
  width: 100%; max-width: 620px; max-height: 88vh; display: flex; flex-direction: column;
  background: var(--panel); border: 1px solid var(--bd3); border-radius: 15px;
  box-shadow: 0 22px 60px var(--shadow);
}
.pm-modal header { display: flex; align-items: center; gap: 12px; padding: 15px 17px; border-bottom: 1px solid var(--bd); }
.pm-modal header h3 { margin: 0; font-size: 14px; font-weight: 600; }
.pm-x { margin-left: auto; background: none; border: 0; color: var(--tx4); font-size: 14px; cursor: pointer; padding: 4px; }
.pm-x:hover { color: var(--tx); }
.pm-modal-body { padding: 17px; overflow: auto; display: flex; flex-direction: column; gap: 14px; }
.pm-modal footer { display: flex; justify-content: flex-end; gap: 8px; padding: 13px 17px; border-top: 1px solid var(--bd); }

.pm-warn {
  background: rgba(229, 103, 90, .09); border: 1px solid rgba(229, 103, 90, .26);
  border-radius: 10px; padding: 11px 13px;
}
.pm-warn p { margin: 0; font-size: 12.5px; color: var(--tx2); line-height: 1.55; }
.pm-warn p + p { margin-top: 6px; }
.pm-consent-read { font-size: 11.5px; color: var(--tx4); }
.pm-consent-read strong { color: var(--tx2); }

.pm-diff { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 620px) { .pm-diff { grid-template-columns: 1fr; } }
.pm-diff-col h4 { margin: 0 0 6px; font-size: 10px; text-transform: uppercase; letter-spacing: .07em; color: var(--tx4); }
.pm-diff-col pre {
  background: var(--input); border: 1px solid var(--bd); border-radius: 9px; padding: 10px;
  font-family: 'JetBrains Mono', monospace; font-size: 10.5px; color: var(--tx2);
  max-height: 340px; overflow: auto; margin: 0; white-space: pre-wrap; word-break: break-word;
}

/* --- toast --- */
.pm-toast {
  position: fixed; left: 50%; bottom: 22px; transform: translateX(-50%); z-index: 1600;
  background: var(--accent); color: var(--ink); border-radius: 10px;
  padding: 10px 16px; font-size: 12.5px; font-weight: 600; box-shadow: 0 10px 30px var(--shadow);
  max-width: 90vw;
}
.pm-toast.bad { background: var(--bad); color: var(--bad-ink); }
.pm-toast[hidden] { display: none; }

/* --- consent block in the bid form ---
   Sits between the projection and the button: the last thing read before
   committing, which is where an agreement belongs. */
.consent {
  margin-top: 12px; padding: 11px 12px;
  background: var(--card); border: 1px solid var(--bd); border-radius: 11px;
}
.consent-line { display: flex; align-items: flex-start; gap: 9px; font-size: 12px; color: var(--tx2); line-height: 1.5; cursor: pointer; }
.consent-line + .consent-line { margin-top: 9px; }
.consent-line input { margin: 1px 0 0; width: 15px; height: 15px; accent-color: var(--accent); flex: 0 0 auto; cursor: pointer; }
.linkish {
  background: none; border: 0; padding: 0; font: inherit; color: var(--accent);
  text-decoration: underline; text-underline-offset: 2px; cursor: pointer;
}
.terms-text {
  margin: 9px 0 2px; padding: 10px 11px; max-height: 190px; overflow: auto;
  background: var(--input); border: 1px solid var(--bd2); border-radius: 9px;
  font-size: 11.5px; line-height: 1.6; color: var(--tx3); white-space: pre-wrap;
}
.terms-text[hidden] { display: none; }

/* --- find-my-ZIP button, inside the search field --- */
.locate-btn {
  position: absolute; right: 5px; top: 50%; transform: translateY(-50%);
  width: 26px; height: 26px; display: grid; place-items: center;
  border: 0; border-radius: 7px; background: transparent; color: var(--tx4);
}
.locate-btn:hover { background: var(--fill); color: var(--tx2); }
.locate-btn.on { color: var(--accent); }
/* Spinning while the browser is getting a fix — that can take several seconds and
   silence reads as a dead button. */
.locate-btn.busy { color: var(--accent); animation: zbSpin 1s linear infinite; }
.locate-btn:disabled { cursor: default; }
@keyframes zbSpin { to { transform: translateY(-50%) rotate(360deg); } }
/* leave room so a long ZIP does not run under the button */
.search input { padding-right: 34px; }
.pm-statusline { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.pm-statusline .pm-hint { margin-left: auto; font-family: 'JetBrains Mono', monospace; font-size: 10.5px; }
.pm-input[readonly] { color: var(--tx3); cursor: text; }

/* ==========================================================================
   Content pages (route.php → inc/pages/*)

   Documents, not applications: a readable column, real headings, real tables.
   They share the app's tokens so light and dark still work, and nothing here
   depends on JavaScript having run.
   ========================================================================== */

.doc-body { background: var(--bg); color: var(--tx); font-family: 'Instrument Sans', system-ui, sans-serif; margin: 0; }
.doc { max-width: 1040px; margin: 0 auto; padding: 22px 20px 60px; }

.doc-crumbs { margin: 4px 0 22px; }
.doc-crumbs ol { display: flex; flex-wrap: wrap; gap: 6px; list-style: none; margin: 0; padding: 0; font-size: 12px; }
.doc-crumbs li + li::before { content: '/'; color: var(--tx4); margin-right: 6px; }
.doc-crumbs a { color: var(--tx3); text-decoration: none; }
.doc-crumbs a:hover { color: var(--accent); }
.doc-crumbs span { color: var(--tx4); }

.doc-head { margin-bottom: 20px; }
.doc-head h1 { font-size: clamp(26px, 4vw, 36px); line-height: 1.15; letter-spacing: -.6px; margin: 0 0 10px; }
.doc-head h1 .mono { font-family: 'JetBrains Mono', monospace; color: var(--accent); }
.doc-lede { font-size: 15px; line-height: 1.65; color: var(--tx2); margin: 0; max-width: 68ch; }
.doc-lede strong { color: var(--tx); }

.doc-stats { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px; margin: 22px 0; }
.doc-stat { background: var(--card); border: 1px solid var(--bd); border-radius: 12px; padding: 13px 14px; color: var(--tx); }
.doc-stat span { display: block; font-size: 9.5px; text-transform: uppercase; letter-spacing: .07em; color: var(--tx4); }
.doc-stat strong { display: block; font-family: 'JetBrains Mono', monospace; font-size: 21px; letter-spacing: -.5px; margin: 3px 0 2px; }
.doc-stat em { font-style: normal; font-size: 11px; color: var(--tx4); }

.doc-cta { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 26px 0; }
.doc-cta .cta { width: auto; padding: 12px 20px; text-decoration: none; display: inline-block; }
.doc-cta span { font-size: 12px; color: var(--tx4); }

.doc-sec { margin: 34px 0; }
.doc-sec h2 { font-size: 17px; letter-spacing: -.2px; margin: 0 0 14px; }
.doc-sec h2 .dim { font-weight: 400; font-size: 13px; }
.doc-sec p { font-size: 13.5px; line-height: 1.65; color: var(--tx2); max-width: 68ch; }
.doc-sec a { color: var(--accent); }

/* A wide table must scroll inside its own box, never push the page sideways. */
.doc-table { width: 100%; border-collapse: collapse; font-size: 13px; display: block; overflow-x: auto; white-space: nowrap; }
.doc-table th { text-align: left; font-size: 9.5px; text-transform: uppercase; letter-spacing: .07em;
  color: var(--tx4); font-weight: 600; padding: 0 12px 8px; border-bottom: 1px solid var(--bd); }
.doc-table td { padding: 9px 12px; border-bottom: 1px solid var(--hair); }
.doc-table tbody tr:hover { background: var(--fill); }
.doc-table td.num, .doc-table th.num { text-align: right; font-family: 'JetBrains Mono', monospace; }
.doc-table .dim, .doc-table td.dim { color: var(--tx4); }
.doc-table td strong { display: block; font-weight: 600; }
.doc-table td .dim { font-size: 11px; }
.doc-table a { color: var(--tx); text-decoration: none; border-bottom: 1px solid var(--bd3); }
.doc-table a:hover { color: var(--accent); border-color: var(--accent); }
.doc-table a.mono { font-family: 'JetBrains Mono', monospace; }

.doc-chips { display: flex; flex-wrap: wrap; gap: 6px; list-style: none; margin: 0; padding: 0; }
.doc-chips a {
  display: inline-block; background: var(--card); border: 1px solid var(--bd); border-radius: 8px;
  padding: 5px 10px; font-size: 12px; color: var(--tx2); text-decoration: none;
  font-family: 'JetBrains Mono', monospace;
}
.doc-chips a:hover { border-color: var(--accent); color: var(--accent); }

.doc-links { list-style: none; margin: 0; padding: 0; font-size: 13px; }
.doc-links li { padding: 6px 0; border-bottom: 1px solid var(--hair); display: flex; gap: 8px; align-items: baseline; }
.doc-links a { color: var(--tx); text-decoration: none; }
.doc-links a:hover { color: var(--accent); }
.doc-links .dim { color: var(--tx4); font-size: 11px; margin-left: auto; }
.doc-links.cols { columns: 3; column-gap: 26px; }
.doc-links.cols li { break-inside: avoid; }
@media (max-width: 860px) { .doc-links.cols { columns: 2; } }
@media (max-width: 560px) { .doc-links.cols { columns: 1; } }

.doc-pager { display: flex; align-items: center; justify-content: center; gap: 16px; margin-top: 20px; font-size: 13px; }
.doc-pager a { color: var(--accent); text-decoration: none; }
.doc-pager span { color: var(--tx4); }

.doc-foot { border-top: 1px solid var(--bd); padding: 22px 20px 40px; text-align: center; }
.doc-foot div { display: flex; gap: 18px; justify-content: center; flex-wrap: wrap; margin-bottom: 10px; }
.doc-foot a { color: var(--tx3); font-size: 12.5px; text-decoration: none; }
.doc-foot a:hover { color: var(--accent); }
.doc-foot p { font-size: 11.5px; color: var(--tx4); margin: 0; }
.pm-linkrow { display: flex; gap: 8px; flex-wrap: wrap; }
.pm-linkrow a { text-decoration: none; }
a.zip-title { color: var(--tx); text-decoration: none; display: inline-block; }
a.zip-title:hover { color: var(--accent); }

/* The one overlay notice left on the map: it says the view is filtered, which is
   the only thing the map cannot show you by looking at it. */
.map-notice {
  display: inline-flex; align-items: center; gap: 7px;
  background: var(--glass); border: 1px solid var(--accbd); border-radius: 10px;
  padding: 7px 11px; font-size: 11.5px; font-weight: 600; color: var(--accent);
  backdrop-filter: blur(8px); box-shadow: 0 4px 14px var(--shadow); cursor: pointer;
  max-width: 100%;
}
.map-notice i { font-style: normal; opacity: .75; }
/* Two notices can be up at once — a filter badge and a selection hint. */
.overlay.tl { display: flex; flex-direction: column; gap: 6px; align-items: flex-start; }
/* The container is pointer-events:none so it never blocks the map underneath;
   the notices themselves have to opt back in or they cannot be tapped. */
.map-notice { pointer-events: auto; }
.map-notice span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.map-notice .hint-cue {
  font-style: normal; font-size: 10.5px; font-weight: 600;
  color: var(--tx4); border-left: 1px solid var(--bd2);
  padding-left: 8px; white-space: nowrap; flex: 0 0 auto;
}

/* ==========================================================================
   Responsive

   Three widths, each chosen because something actually breaks there rather
   than because it is a round number:

     1080px  the browse page stops fitting a filter column beside the table
      940px  the map stops fitting a sidebar beside the map (JS also switches
             the shell to its stacked mode at this width — keep the two in step)
      640px  a phone: single column everywhere, and touch targets grow

   Rules that only shrink type or padding live at the widest breakpoint that
   needs them, so a tablet is not given phone treatment.
   ========================================================================== */

@media (max-width: 1080px) {
  .pm-main { padding: 14px; }
  .br-main { padding: 16px; gap: 14px; }
  .doc { padding: 18px 16px 48px; }
}

@media (max-width: 940px) {
  /* The map's own stacked mode is applied by JS at this width; these are the
     parts of it CSS owns. */
  .shell.narrow .map-pane { min-height: 320px; }

  /* --- what sits over the map, on a phone ---
     Four things want the edges and there is not room for them to be casual about
     it. Laid out so no two can meet, measured from the bottom:
       0-98px   the zoom control, on the right (two 44px squares plus its margin)
       0-~56px  the leader strip, on the left, stopping short of the control
       106px+   toasts, full width, clear of both
     The top-left notices clear the floating nav at 64px. */
  .overlay.tl { top: 64px; left: 8px; right: 8px; max-width: none; }
  .overlay.bl { bottom: 10px; left: 8px; right: 62px; max-width: none; }
  .leader-strip { width: fit-content; max-width: 100%; }
  /* The corner is 62px narrower than the screen here, so the card cannot keep
     its own 340px — it would run under the zoom control. */
  .listing { width: 100%; }
  .toasts { left: 8px; right: 8px; bottom: 106px; align-items: stretch; }
  .toast { max-width: none; }

  .pm-cols { grid-template-columns: 1fr; }
  .pm-tiles { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); }
}

@media (max-width: 640px) {
  /* --- map --- */
  .controls > div:first-child { flex-wrap: wrap; }
  .seg { width: 100%; }
  .seg button { flex: 1; }
  .search { width: 100%; }
  /* A 34px square is fine under a cursor and small under a thumb. The legend is
     hidden at this width, so the corner is the zoom control's alone. */
  .leaflet-bottom.leaflet-right { bottom: 10px; right: 10px; }
  .leaflet-control-zoom { margin: 0 !important; }
  .leaflet-bar a { width: 44px !important; height: 44px !important; font-size: 21px !important; }
  .leaflet-bar a:first-child { border-radius: 12px 12px 0 0 !important; }
  .leaflet-bar a:last-child  { border-radius: 0 0 12px 12px !important; }

  /* --- browse --- */
  .br-totals { padding: 12px 14px; gap: 0; }
  .br-total { padding: 0 18px 0 0; }
  .br-main { padding: 12px; }
  .br-listing-head { flex-wrap: wrap; gap: 8px; }
  /* Seven columns do not fit a phone, and a table that scrolls sideways hides
     four of them behind a gesture nobody makes. Each row becomes a card instead:
     the header is hidden and every cell prints its own label. Sorting still works
     — it lives in the filter panel, not in the header row. */
  .br-table-wrap { overflow-x: visible; }
  .br-table, .br-table tbody, .br-table tr, .br-table td { display: block; width: auto; }
  .br-table thead { display: none; }

  .br-table tr {
    background: var(--card);
    border: 1px solid var(--bd);
    border-radius: 12px;
    padding: 12px 14px;
    margin-bottom: 10px;
    position: relative;
  }
  .br-table tr.open { border-style: dashed; }

  .br-table td {
    border: 0;
    padding: 3px 0;
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 12px;
    text-align: right;
    font-size: 13.5px;
  }
  .br-table td::before {
    content: attr(data-label);
    flex: 0 0 auto;
    text-align: left;
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: .06em;
    color: var(--tx4);
    font-family: 'Instrument Sans', system-ui, sans-serif;
  }
  /* The ZIP is the card's heading, so it loses the label and gains the size. */
  .br-table td:first-child { padding-bottom: 8px; }
  .br-table td:first-child::before { content: none; }
  .br-table td:first-child a { font-size: 18px; font-weight: 700; }
  /* The action fills the width, the way a button on a card should. */
  .br-table td.br-act { padding-top: 10px; }
  .br-table td.br-act::before { content: none; }
  .br-table td.br-act button { width: 100%; min-height: 42px; font-size: 14px; }
  .br-bulk { flex-direction: column; align-items: stretch; gap: 10px; }
  .br-modal { max-width: 100%; margin: 0; border-radius: 14px 14px 0 0; }

  /* --- back office --- */
  .pm-top { flex-wrap: wrap; gap: 10px; padding: 10px 12px; }
  .pm-tabs { order: 3; width: 100%; }
  .pm-top-right { margin-left: auto; }
  .pm-main { padding: 12px; }
  .pm-table { display: block; overflow-x: auto; -webkit-overflow-scrolling: touch; white-space: nowrap; }
  .pm-toolbar { flex-direction: column; align-items: stretch; }
  .pm-toolbar .pm-input { max-width: none; }
  .pm-toolbar .pm-hint { margin-left: 0; }
  .pm-modal { max-width: 100%; max-height: 92vh; }
  .pm-chart { height: 120px; }
  .pm-statusline .pm-hint { margin-left: 0; width: 100%; }
  .pm-linkrow { flex-direction: column; }
  .pm-linkrow a { text-align: center; }

  /* --- content pages --- */
  .doc { padding: 14px 14px 40px; }
  .doc-head h1 { font-size: 24px; }
  .doc-stats { grid-template-columns: 1fr 1fr; gap: 8px; }
  .doc-cta { flex-direction: column; align-items: stretch; }
  .doc-cta .cta { text-align: center; }
  .doc-foot div { gap: 12px; }

  /* --- everywhere ---
     A 26px control is comfortable with a mouse and a miss with a thumb. These
     are the ones small enough to matter. */
  .icon-btn, .rail button, .pm-x, .locate-btn { min-width: 40px; min-height: 40px; }
  .locate-btn { width: 34px; height: 34px; }
  .quick-btn, .ghost.sm, .primary.sm { min-height: 36px; }
}

/* Coarse pointers get bigger targets whatever the screen width — a tablet is
   not a small desktop. */
@media (pointer: coarse) {
  /* Safari zooms the page whenever a field under 16px takes focus, and does not
     zoom back out. On the map that reads as the layout breaking; in the filter
     panel it hides the thing you were typing into. 16px everywhere is the only
     reliable way to stop it, and it costs nothing on a touch screen. */
  input, select, textarea,
  .search input, .br-field input, .br-field select, .pm-input, .field input, .field textarea {
    font-size: 16px;
  }
  button, .cta, .row-tap, .doc-chips a { min-height: 38px; }
  .pm-table td .ghost { min-height: 34px; }
}

/* Someone who asked their system for less motion means it. The map's own
   animations are already off; this covers the rest. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .01ms !important;
    scroll-behavior: auto !important;
  }
}

/* --- the map sidebar on a phone ---
   Below 940px the panel is not a column beside the map, it is a sheet over it.
   Half the height of a phone is not enough for either one, so the map keeps the
   screen and the panel takes all of it when asked. */
@media (max-width: 940px) {
  .shell.narrow .panel {
    position: fixed;
    inset: 0;
    z-index: 1200;
    flex: 1 1 auto;
    max-width: none;
    border-right: 0;
    /* Under the notch and above the home indicator on a phone. */
    padding-bottom: env(safe-area-inset-bottom);
  }
  .shell.narrow .panel .panel-head { padding-top: max(16px, env(safe-area-inset-top)); }

  /* The rail is the way back in: a short bar the map is not fighting for room. */
  .shell.narrow .rail {
    border-bottom: 1px solid var(--bd);
    padding: 8px 10px;
    padding-top: max(8px, env(safe-area-inset-top));
  }
  .shell.narrow .rail button { width: 40px; height: 40px; }

  /* The map gets whatever the rail does not. */
  .shell.narrow .map-pane { flex: 1 1 auto; min-height: 0; }

  /* Closing it is one tap, and the target is thumb-sized. */
  .shell.narrow .panel [data-act="collapse"] {
    min-width: 40px; min-height: 40px; font-size: 15px;
  }
}

/* While the sheet is up, the page behind it must not scroll — on iOS a scroll
   that starts on an overlay otherwise drags the document underneath. */
body.panel-open { overflow: hidden; }

/* --- stacking order ---
   Written down because the mobile panel broke it: the panel was 1200 and the bid
   dialog 1000, so opening the bid form from inside the panel put the form behind
   the thing that opened it.

        5-60  page chrome (the nav on a document page)
         500  overlays pinned over the map
         620  the floating nav on the map
        1200  the sidebar sheet on a phone
        1400  modals — they cover the sheet that opened them
        1600  toasts — always visible, including over a modal
   ========================================================================== */

/* ==========================================================================
   Dialogs on a phone

   The bid form is the one screen where getting this wrong costs money: it is
   where someone types an amount and a link. Below 640px it stops being a box in
   the middle of the screen and becomes a sheet rising from the bottom, which is
   where a thumb is and where the keyboard pushes from.
   ========================================================================== */

@media (max-width: 640px) {
  .scrim { padding: 0; place-items: end stretch; }

  .dialog {
    max-width: none;
    /* dvh, not vh: on iOS the address bar collapses as you scroll and vh is
       measured against the tallest state, so a 92vh sheet is taller than the
       screen and its last field sits under the fold. */
    max-height: 92dvh;
    border-radius: 18px 18px 0 0;
    border-bottom: 0;
    padding: 16px 16px calc(16px + env(safe-area-inset-bottom));
    animation: zbSheet .24s ease both;
  }

  /* A grab bar, so it reads as something that can be dismissed downward even
     though the scrim is what closes it. */
  .dialog::before {
    content: '';
    display: block;
    width: 38px; height: 4px;
    margin: -4px auto 12px;
    border-radius: 4px;
    background: var(--bd3);
  }

  /* iOS zooms the whole page when a field under 16px takes focus. On a map that
     reads as the layout breaking. Nothing else here needs to change size. */
  .field input, .field textarea, .dialog input, .dialog select { font-size: 16px; }

  .cta { padding: 14px; font-size: 15px; }
  .quick { gap: 8px; }
  .quick button, .quick-btn { padding: 9px 12px; font-size: 12.5px; min-height: 38px; }

  /* Label and figure on their own lines rather than squeezed onto one. */
  .proj div { flex-wrap: wrap; gap: 2px 10px; }
  .proj span { flex: 1 1 60%; }

  .terms-text { max-height: 34vh; }
  .consent-line { font-size: 13px; }
  .consent-line input { width: 18px; height: 18px; }

  /* Same treatment for the browse page's dialogs. */
  .br-scrim { padding: 0; place-items: end stretch; }
  .br-modal {
    max-width: none; max-height: 92dvh; overflow: auto;
    border-radius: 18px 18px 0 0;
    padding-bottom: calc(16px + env(safe-area-inset-bottom));
  }
  .br-modal input, .br-modal select, .br-modal textarea { font-size: 16px; }
  .br-modal-actions { position: sticky; bottom: 0; background: var(--panel); padding-top: 10px; }
  .br-modal-actions button { min-height: 44px; }
}

@keyframes zbSheet {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: translateY(0); }
}
/* The address appears with the notification box it belongs to. */
.consent-email { margin: 9px 0 2px 24px; }
.consent-email[hidden] { display: none; }
.consent-email input {
  width: 100%; box-sizing: border-box;
  background: var(--input); border: 1px solid var(--bd2); border-radius: 9px;
  color: var(--tx); padding: 9px 11px; font: inherit; font-size: 13px;
}
.consent-email input:focus { outline: none; border-color: var(--sel); }


/* ==========================================================================
   The update page (update.php)

   Same plainness as the back office: this is a tool an operator reads once
   every few months, so nothing here animates or decorates.
   ========================================================================== */

.up-wrap { max-width: 640px; margin: 0 auto; padding: 40px 20px 60px; }
.up-h { font-size: 22px; font-weight: 700; margin: 0 0 4px; color: var(--tx); }
.up-sub { font-size: 12.5px; color: var(--tx3); margin: 0 0 22px; }

.up-card {
  background: var(--card); border: 1px solid var(--bd); border-left-width: 3px;
  border-radius: 10px; padding: 13px 15px; margin-bottom: 14px; color: var(--tx2);
}
.up-card strong { display: block; color: var(--tx); font-size: 13.5px; margin-bottom: 4px; }
.up-card p { margin: 0; font-size: 12.5px; line-height: 1.55; }
.up-card.ok { border-left-color: var(--accent); }
.up-card.warn { border-left-color: var(--warn); }
.up-card.bad { border-left-color: var(--bad); }

.up-list { margin: 10px 0 0; padding: 0; list-style: none; }
.up-list li { padding: 5px 0; border-top: 1px solid var(--hair); font-size: 12.5px; }
.up-list code { font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 11.5px; color: var(--tx); }
.up-tag { font-size: 10.5px; color: var(--tx4); margin-left: 7px; }

.up-log { margin: 0 0 14px; padding: 0; list-style: none; }
.up-log li {
  font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 11.5px;
  padding: 4px 10px; border-left: 2px solid var(--bd2); color: var(--tx3);
}
.up-log li.ok { border-left-color: var(--accent); color: var(--tx2); }
.up-log li.warn { border-left-color: var(--warn); }
.up-log li.skip { opacity: .7; }

.up-err {
  margin: 9px 0 0; padding: 10px; border-radius: 8px;
  background: var(--input); border: 1px solid var(--bd);
  font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 11.5px;
  color: var(--bad); white-space: pre-wrap; word-break: break-word;
}

.up-form { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 18px; }
.up-form input { flex: 1 1 220px; }
.up-actions { display: flex; gap: 14px; margin-top: 20px; font-size: 12.5px; }

@media (max-width: 480px) {
  .up-wrap { padding: 26px 14px 48px; }
  .up-form button { width: 100%; }
}


/* The payment return page (paid.php). One sentence and a way onward. */
.paid-wrap { max-width: 560px; margin: 0 auto; padding: 64px 20px 80px; text-align: center; }
.paid-h { font-size: 26px; font-weight: 700; margin: 0 0 10px; color: var(--tx); letter-spacing: -.01em; }
.paid-sub { font-size: 14px; line-height: 1.6; color: var(--tx2); margin: 0 0 26px; }
.paid-actions { display: flex; gap: 12px; justify-content: center; flex-wrap: wrap; }
@media (max-width: 480px) {
  .paid-wrap { padding: 44px 16px 60px; }
  .paid-h { font-size: 21px; }
  .paid-actions a { width: 100%; text-align: center; }
}
