/* Portia_Air.
   The palette is the family site's, so arriving here from a card on the landing
   page does not feel like leaving — even though this page, unlike every other
   one there, is reachable without signing in to anything. */
:root {
  --bg: #0f1115;
  --panel: #141821;
  --card: #1a1d24;
  --card-hover: #222630;
  --accent: #4f9cf9;
  --text: #e6e8eb;
  --muted: #9aa0aa;
  --border: #2a2e38;
  --ok-fg: #7ee2a8;
  --err-fg: #ff9b9b;
  --warn: #ffcf7e;
}
* { box-sizing: border-box; margin: 0; padding: 0; }

/* The hidden attribute only works through the UA stylesheet's `[hidden] {
   display: none }`, which any author rule that sets `display` outranks. Panes
   are `display: flex`, so without this both forms render at once — while every
   script reading `.hidden` still reports them as hidden. Stated once here
   rather than per component, since the next element with a display rule would
   have exactly the same problem. */
[hidden] { display: none !important; }

body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
}

.topbar {
  display: flex; align-items: center; gap: 1rem;
  padding: 1rem 1.5rem;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
}
.topbar .brand { display: flex; align-items: center; gap: .55rem; }
.topbar .mark { color: var(--accent); font-size: 1.25rem; }
.topbar h1 { font-size: 1.1rem; font-weight: 700; letter-spacing: -0.01em; }
.topbar .spacer { flex: 1; }
.topbar .meta { color: var(--muted); font-size: .85rem; }

main {
  max-width: 27rem;
  margin: 0 auto;
  padding: 2.5rem 1.25rem 4rem;
}

.card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 14px;
  overflow: hidden;
}

/* ── Tabs ─────────────────────────────────────────────────────────────── */
.tabs { display: flex; border-bottom: 1px solid var(--border); }
.tab {
  flex: 1;
  background: none; border: 0; border-bottom: 2px solid transparent;
  color: var(--muted); font: inherit; font-size: .92rem;
  padding: .95rem .5rem; cursor: pointer;
}
.tab:hover { color: var(--text); background: var(--card-hover); }
.tab[aria-selected="true"] { color: var(--text); border-bottom-color: var(--accent); }
.tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ── Forms ────────────────────────────────────────────────────────────── */
.pane { display: flex; flex-direction: column; gap: 1rem; padding: 1.5rem; }
.pane label { display: flex; flex-direction: column; gap: .4rem; }
.pane label > span { font-size: .85rem; color: var(--muted); }
.pane small { font-size: .76rem; color: var(--muted); }

input, select {
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 9px;
  padding: .65rem .75rem; font: inherit; font-size: .95rem;
  width: 100%;
}
input:focus, select:focus { outline: 2px solid var(--accent); border-color: transparent; }
input:disabled, select:disabled { opacity: .55; }

button.primary {
  background: var(--accent); color: #06111f;
  border: 0; border-radius: 9px;
  padding: .75rem 1rem; font: inherit; font-weight: 650; font-size: .95rem;
  cursor: pointer;
}
button.primary:hover { filter: brightness(1.08); }
button.primary:disabled { opacity: .6; cursor: default; filter: none; }
button.primary:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }

button.ghost {
  background: none; color: var(--muted);
  border: 1px solid var(--border); border-radius: 8px;
  padding: .4rem .75rem; font: inherit; font-size: .85rem; cursor: pointer;
}
button.ghost:hover { color: var(--text); border-color: var(--accent); }

/* The message line is always in the layout, empty or not, so a failed sign-in
   does not shift the button out from under the cursor mid-click. */
.msg { min-height: 1.25rem; font-size: .85rem; color: var(--err-fg); }
.msg.ok { color: var(--ok-fg); }

/* ── Icons ────────────────────────────────────────────────────────────────
   Every entity icon occupies exactly the same 32×32 box whether it is one of
   the built-in SVGs or a file someone dropped into icons/. That is what makes
   them interchangeable: a replacement cannot change any layout, because the
   box does not depend on the art. `contain` letterboxes a non-square image
   rather than cropping or stretching it. */
.icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; flex: 0 0 32px;
  color: var(--muted);
}
.icon svg { width: 32px; height: 32px; display: block; }
.icon img { width: 32px; height: 32px; object-fit: contain; display: block; }

/* ── One page ─────────────────────────────────────────────────────────────
   Four sections that used to be four tabs. Each carries its own title now that
   no tab label is naming it. */
.card + .card { margin-top: 1rem; }

/* Full width, stacked: fleet, then shop, then base. A two-column arrangement
   split the fleet down the middle of the screen, and the fleet is the one thing
   here that reads better wide — six lanes across the top rather than two lanes
   and a sideways scroll.

   min-width: 0 is still load-bearing. Two of these cards hold something wider
   than the viewport on purpose — the six lanes, and the base's row of panels —
   which scroll inside their own boxes. Without it those boxes refuse to shrink
   and the whole page scrolls sideways behind a topbar that does not. */
main > .card { min-width: 0; }

@media (min-width: 60rem) {
  main { max-width: 68rem; }
}

/* Before sign-in the auth card is the whole page, and it should not be as wide
   as the game is. */
main > .auth { max-width: 27rem; margin-left: auto; margin-right: auto; }

/* ── The overview bar ────────────────────────────────────────────────────
   Sticky under the topbar and never hidden: it holds everything the airport
   card used to, which is the point — the numbers that decide what you can do
   are the ones you need while doing it. Wraps to two lines on a phone rather
   than shrinking below legibility. */
.overview {
  position: sticky; top: 0; z-index: 15;
  display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .9rem;
  padding: .5rem 1.25rem;
  background: var(--panel); border-bottom: 1px solid var(--border);
  font-size: .82rem; color: var(--muted);
}
.overview .ov-name { display: inline-flex; align-items: center; gap: .45rem; color: var(--text); font-weight: 600; }
.overview .ov { display: inline-flex; align-items: center; gap: .3rem; white-space: nowrap; }
.overview .ov i { display: inline-flex; }
/* The icons are the 32px box like everywhere else; the bar is sized around
   them rather than scaling them, so a replacement image still fits. */
.overview .ov i .icon, .overview .status .icon { width: 32px; height: 32px; }
.overview b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }

/* A hairline under the figure: "480 / 1.000" is a reading, a bar is a glance. */
.overview .meter { position: relative; padding-bottom: 5px; }
.overview .mini {
  position: absolute; left: 32px; right: 0; bottom: 0; height: 3px;
  background: var(--bg); border-radius: 999px; overflow: hidden;
}
.overview .mini u { display: block; height: 100%; background: var(--accent); }
#ovFuel .mini u { background: var(--warn); }
#ovXP .mini u { background: var(--ok-fg); }

.overview .ov-tail { display: inline-flex; align-items: center; gap: .5rem; margin-left: auto; }
.overview .status.open { color: var(--ok-fg); }
.overview .status.closed { color: var(--err-fg); }
.overview .sync.stale { color: var(--warn); }

@media (max-width: 640px) {
  .overview { padding: .45rem .9rem; gap: .3rem .7rem; font-size: .76rem; }
  .overview .ov-tail { margin-left: 0; }
}

.icon-btn { font-size: 1rem; line-height: 1; padding: .35rem .6rem; }

/* ── Bars ─────────────────────────────────────────────────────────────────
   A progress bar, used by the contracts list and by the overview's meters.
   It outlived the airport card's resource rows, which is where it was born. */
.bar {
  display: block; height: 8px; border-radius: 999px;
  background: var(--bg); border: 1px solid var(--border); overflow: hidden;
}
.bar i {
  display: block; height: 100%; background: var(--accent);
  /* Values climb by a minute's generation at a time, so an instant jump reads
     as a glitch. Shorter than the poll interval, so it always settles first. */
  transition: width .4s ease;
}

/* ── Fleet ────────────────────────────────────────────────────────────── */
.fleet { padding: 1.25rem; }

.fleet-bar { display: flex; align-items: center; gap: .5rem; margin-bottom: 1rem; }
.fleet-bar .spacer { flex: 1; }
.fleet-bar .note { font-size: .78rem; color: var(--muted); text-align: right; }

button.sm { padding: .4rem .7rem; font-size: .82rem; }

/* Two bands of three lanes rather than one line of six: what you do to a plane
   on top, what happens to it while you wait underneath. Three across is nearly
   twice the width each, which is the difference between reading a row and
   truncating it — and on a desktop it removes the sideways scroll entirely. */
.pipeline { min-width: 0; display: flex; flex-direction: column; gap: 1.1rem; }

.band-title {
  display: flex; align-items: center; gap: .4rem;
  font-size: .72rem; color: var(--muted); font-weight: 500;
  text-transform: uppercase; letter-spacing: .05em; margin-bottom: .5rem;
}
.band-title .band-n {
  font-variant-numeric: tabular-nums; background: var(--bg);
  border: 1px solid var(--border); border-radius: 999px; padding: 0 .4rem;
}

/* Still scrollable, because three lanes at a legible width do not fit a narrow
   phone either — but three is a swipe, where six was a journey. */
.lanes {
  min-width: 0;
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(13rem, 1fr);
  gap: .6rem; overflow-x: auto; padding-bottom: .4rem;
  scroll-snap-type: x proximity;
}
.lane { scroll-snap-align: start; min-width: 0; }
.lane-head {
  display: flex; align-items: center; gap: .35rem;
  padding: .3rem .1rem .55rem; font-size: .75rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em;
  border-bottom: 1px solid var(--border); margin-bottom: .55rem;
}
.lane-head .lane-count { margin-left: auto; font-variant-numeric: tabular-nums; }

.entry {
  display: flex; align-items: center; gap: .5rem; width: 100%;
  background: var(--bg); border: 1px solid var(--border); border-radius: 10px;
  padding: .5rem; margin-bottom: .5rem;
  font: inherit; color: inherit; text-align: left;
}
.entry:disabled { cursor: default; }
.entry.actionable { cursor: pointer; }
.entry.actionable:hover { border-color: var(--accent); background: var(--card-hover); }
.entry-body { min-width: 0; flex: 1; }
.entry-name {
  font-size: .82rem; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.entry-sub { font-size: .7rem; color: var(--muted); font-variant-numeric: tabular-nums; }
.entry .count {
  font-size: .8rem; font-variant-numeric: tabular-nums;
  background: var(--card-hover); border-radius: 999px; padding: .05rem .45rem;
}

.fleet .empty { font-size: .82rem; color: var(--muted); margin-top: 1rem; line-height: 1.5; }

/* ── Dispatch, inline ─────────────────────────────────────────────────────
   Under the plane it belongs to, inside the lane. A dialog covered the pipeline
   you were dispatching into and turned two clicks into four. */
.entry.open { border-color: var(--accent); background: var(--card-hover); }

.dispatch {
  background: var(--bg); border: 1px solid var(--accent); border-radius: 10px;
  padding: .6rem; margin: -.25rem 0 .6rem; display: flex; flex-direction: column; gap: .5rem;
}

/* −5 −1 [n] +1 +5. A stepper rather than a number field: the answer is nearly
   always "one more" or "all of them", and typing is the slow way to say it. */
.amount { display: flex; align-items: center; gap: .25rem; flex-wrap: wrap; }
.step {
  background: var(--card); color: var(--text);
  border: 1px solid var(--border); border-radius: 7px;
  min-width: 2rem; padding: .25rem .35rem; font: inherit; font-size: .78rem;
  cursor: pointer; font-variant-numeric: tabular-nums;
}
.step:hover:not(:disabled) { border-color: var(--accent); }
.step:disabled { opacity: .35; cursor: default; }
.amount-n {
  min-width: 1.75rem; text-align: center;
  font-size: .95rem; font-weight: 650; font-variant-numeric: tabular-nums;
}
.amount .cost { flex: 1; text-align: right; font-size: .72rem; color: var(--muted); }

/* Country, then port — the order the terminal client asks in. */
.dispatch .field { display: flex; flex-direction: column; gap: .2rem; }
.dispatch .field > span {
  font-size: .68rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em;
}
.dispatch select { padding: .4rem .5rem; font-size: .84rem; }
.dispatch .send { width: 100%; }
.dispatch .msg { font-size: .78rem; }

/* Sell sits below a rule, quieter than Send. The two act on the same planes and
   do opposite things, and only one of them can be undone — so it is not a
   second button of equal weight next to the first. */
.sell-row {
  display: flex; flex-direction: column; gap: .3rem;
  padding-top: .5rem; border-top: 1px solid var(--border);
}
.sell-row .sell { width: 100%; }
.sell-row .entry-sub { font-size: .72rem; }

/* ── Visitors ─────────────────────────────────────────────────────────── */
.visitors { margin-top: 1.4rem; padding-top: 1.1rem; border-top: 1px solid var(--border); }
.visitors h3 { font-size: .78rem; color: var(--muted); text-transform: uppercase; letter-spacing: .04em; margin-bottom: .7rem; }
/* Visitors reuse .entry wholesale — same box, same icon, same countdown slot —
   so a visiting plane and one of your own read the same way. Only the width
   differs: a visitor is not in a lane, so it takes the row. */
.visitors .entry { width: 100%; }
.visitors .entry .clock { font-variant-numeric: tabular-nums; }

/* ── The shop's kind picker ───────────────────────────────────────────────
   The first question is what you are building, so it is the first thing on the
   card: six buttons, each with its icon and how many it holds. */
.kinds { display: flex; flex-wrap: wrap; gap: .45rem; margin-bottom: .9rem; }
.kind {
  display: inline-flex; align-items: center; gap: .35rem;
  background: var(--bg); color: var(--muted);
  border: 1px solid var(--border); border-radius: 999px;
  padding: .3rem .75rem .3rem .3rem; font: inherit; font-size: .82rem;
  cursor: pointer; text-transform: capitalize;
}
.kind:hover { color: var(--text); border-color: var(--accent); }
.kind.on { color: var(--text); border-color: var(--accent); background: var(--card-hover); }
.kind .kind-n {
  font-size: .72rem; font-variant-numeric: tabular-nums;
  background: var(--card-hover); border-radius: 999px; padding: 0 .35rem;
}
.kind.on .kind-n { background: var(--bg); }

/* Narrowing within a kind. Deliberately quieter than the kind chips above:
   picking a kind is the decision, these only trim what it produced, and two
   equally loud rows of pills would make the shop look like a settings screen. */
.facets { display: flex; flex-direction: column; gap: .35rem; margin-bottom: .9rem; }
.facet { display: flex; flex-wrap: wrap; align-items: center; gap: .3rem; }
.facet-label {
  min-width: 5rem; font-size: .74rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em;
}
.facet-v {
  background: transparent; color: var(--muted);
  border: 1px solid var(--border); border-radius: 999px;
  padding: .12rem .6rem; font: inherit; font-size: .78rem;
  cursor: pointer; text-transform: capitalize;
}
.facet-v:hover { color: var(--text); border-color: var(--accent); }
.facet-v.on { color: var(--text); border-color: var(--accent); background: var(--card-hover); }

@media (max-width: 30rem) {
  /* The label stops holding a column of its own once there is no room for one. */
  .facet-label { min-width: 0; width: 100%; }
}

.shop-filter {
  display: flex; align-items: center; gap: .6rem;
  margin-bottom: .7rem; font-size: .8rem; color: var(--muted);
}
.shop-filter .spacer { flex: 1; }
.shop-filter .count { font-variant-numeric: tabular-nums; }
.check {
  /* flex-direction comes from `dialog label`, which would otherwise stack the
     box above its own text, and `input { width: 100% }` would stretch it. Both
     are overridden here rather than loosened there, since every other label in
     a dialog does want a column. */
  display: inline-flex; flex-direction: row; align-items: center;
  gap: .4rem; cursor: pointer;
}
.check input { width: auto; flex: 0 0 auto; }

/* ── Shop, base and cargo ─────────────────────────────────────────────── */
.shop, .base, .cargo { padding: 1.25rem; }

/* The stockpile: one row per product, quantity right-aligned against the name
   so the figures line up in a column that can be read down. */
.cargo .entry { display: flex; align-items: baseline; gap: .5rem; padding: .35rem 0; }
.cargo .entry + .entry { border-top: 1px solid var(--border); }
.cargo .entry-name { flex: 1; min-width: 0; }
.cargo .entry .entry-sub { font-variant-numeric: tabular-nums; }
.cargo p.entry-sub { line-height: 1.5; margin: 0; }
/* A product at its cap: the next load of it is destroyed, so it reads as full
   at a glance rather than only in the sentence underneath. */
.cargo .entry.full .entry-sub { color: var(--warn); }
.shop h3, .base h3 {
  font-size: .75rem; color: var(--muted); text-transform: uppercase;
  letter-spacing: .04em; margin: 1.2rem 0 .6rem;
}
.shop h3:first-of-type, .base h3:first-of-type { margin-top: 0; }
.shop h4 {
  font-size: .7rem; color: var(--muted); text-transform: capitalize;
  margin: .9rem 0 .45rem; font-weight: 500;
}

.shop-item, .owned, .cap-row, .contract {
  display: flex; align-items: center; gap: .6rem;
  background: var(--bg); border: 1px solid var(--border); border-radius: 10px;
  padding: .55rem .6rem; margin-bottom: .5rem;
}
/* The buy button carries a price, so it is wider than a verb and must not be
   squeezed by a long aircraft name. */
.shop-item button, .owned button, .cap-row button { flex: 0 0 auto; }
/* The toggle sits under its list, full width, so it reads as the end of the
   list rather than as another row in it. */
.shop button.more { display: block; width: 100%; margin-top: .3rem; }

.facilities { display: flex; flex-wrap: wrap; gap: .4rem; }
.facility {
  display: inline-flex; align-items: center; gap: .3rem;
  background: var(--bg); border: 1px solid var(--border); border-radius: 999px;
  padding: .2rem .6rem .2rem .25rem; font-size: .76rem; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.facility.full { color: var(--warn); border-color: var(--warn); }

.contract .bar { margin: .35rem 0 .25rem; }
.contract .bar i { background: var(--ok-fg); }

.base .empty, .shop .empty { font-size: .82rem; color: var(--muted); }

/* ── The base as one row ──────────────────────────────────────────────────
   Capacity, runways, buildings and contracts side by side rather than stacked,
   so the whole state of the airport is one glance and one swipe. The panels
   have a floor width and the row scrolls; squeezing four panels onto a phone
   would make all four unreadable instead. */
.base-row {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(16rem, 1fr);
  gap: 1rem; overflow-x: auto; padding-bottom: .5rem;
  scroll-snap-type: x proximity;
}
.base-row .base-panel { scroll-snap-align: start; min-width: 0; }
.base-row .base-panel.wide { grid-column: span 1; }
.base-panel h3 { margin-top: 0; }

/* Income, grouped by resource. The total is the loud figure — it is the one the
   tick actually uses — and the contributing lines sit under it quietly, so the
   panel reads as "this much, from these" rather than as a table to add up. */
.income + .income { margin-top: .9rem; }
.income-head {
  display: flex; align-items: baseline; gap: .5rem;
  padding-bottom: .3rem; border-bottom: 1px solid var(--border);
}
.income-head .entry-name { flex: 1; text-transform: capitalize; }
.income-total { font-variant-numeric: tabular-nums; color: var(--accent); font-weight: 600; }
.income-row {
  display: flex; align-items: baseline; gap: .5rem;
  padding: .18rem 0; font-size: .82rem; color: var(--muted);
}
.income-row span:first-child { flex: 1; min-width: 0; }
.income-amount { font-variant-numeric: tabular-nums; }
/* The base rate is not something the player did, so it does not compete with
   the buildings that are. */
.income-row.base { opacity: .7; font-style: italic; }

@media (min-width: 60rem) {
  /* Buildings is the list that actually has rows in it, so it gets the room. */
  .base-row .base-panel.wide { grid-auto-columns: minmax(22rem, 1.5fr); }
}

/* ── Settings ─────────────────────────────────────────────────────────────
   Account facts and the way out. There is no password endpoint on this API, so
   there is nothing here that claims to change one. */
.account { display: grid; grid-template-columns: auto 1fr; gap: .4rem .9rem; font-size: .85rem; }
.account > div { display: contents; }
.account dt { color: var(--muted); }
.account dd { color: var(--text); }

/* ── Dispatch dialog ──────────────────────────────────────────────────── */
dialog {
  background: var(--card); color: var(--text);
  border: 1px solid var(--border); border-radius: 14px;
  padding: 0; width: min(22rem, calc(100vw - 2rem));
}
dialog::backdrop { background: rgba(0, 0, 0, .6); }
dialog form { display: flex; flex-direction: column; gap: 1rem; padding: 1.25rem; }
dialog h3 { font-size: 1rem; }
dialog label { display: flex; flex-direction: column; gap: .35rem; }
dialog label > span { font-size: .8rem; color: var(--muted); }
dialog small { font-size: .74rem; color: var(--muted); }
dialog menu { display: flex; justify-content: flex-end; gap: .5rem; padding: 0; }

/* ── Icon sheet ───────────────────────────────────────────────────────── */
.sheet {
  margin-top: 1.25rem; background: var(--card);
  border: 1px solid var(--border); border-radius: 12px; padding: .8rem 1rem;
  font-size: .82rem; color: var(--muted);
}
.sheet summary { cursor: pointer; }
.sheet-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(4.5rem, 1fr));
  gap: .8rem; margin-top: .9rem;
}
.sheet-grid figure { display: flex; flex-direction: column; align-items: center; gap: .3rem; }
.sheet-grid figcaption { font-size: .62rem; text-align: center; word-break: break-word; }

/* The topbar is a wordmark, the airport name and one button now that the tabs
   are gone, which fits — but the name is also the first line of the airport
   card, so it is still the part that goes if the width gets tight. */
@media (max-width: 520px) {
  .topbar { padding: .75rem .9rem; gap: .6rem; }
  .topbar h1 { font-size: 1rem; }
  .topbar .meta { display: none; }
}

@media (max-width: 380px) {
  .stats { grid-template-columns: 1fr; }
}

/* ── Search boxes ─────────────────────────────────────────────────────────
   One shape wherever a list can be hunted through — the fleet, the catalogue,
   the buildings, the contracts. It is on screen only once the list is longer
   than SEARCH_FLOOR in filters.js, so it is never furniture over three rows;
   the terminal client can afford an unconditional `/` because a keystroke
   costs no space and a permanent input costs a row on every card. */
input.search {
  display: block; width: 100%; margin-bottom: .7rem;
  background: var(--bg); color: var(--text);
  border: 1px solid var(--border); border-radius: 8px;
  padding: .35rem .6rem; font: inherit; font-size: .82rem;
}
input.search::placeholder { color: var(--muted); }
input.search:focus-visible {
  outline: 2px solid var(--accent); outline-offset: -2px; border-color: var(--accent);
}

/* ── Queued dispatches ────────────────────────────────────────────────────
   Accepted by the server, not yet launched — holding for a runway or a bay.
   They hang under Waiting rather than sitting in it, the way `pipeline` in
   fleet.go puts them, because they have left the idle pool: a queued plane
   that read as still available would invite a dispatch that cannot happen.
   Dashed, because the row is a commitment that has not taken effect. */
.entry.queued { border-style: dashed; }
.entry.queued .entry-name { color: var(--muted); }
.lane-head .lane-count.held { margin-left: .35rem; color: var(--warn); }

/* Runways and pads on the fleet bar. The Base draws these too, but "why is
   nothing taking off" is asked while looking at the pipeline, not three cards
   below it — internal/ui/fleet.go keeps its facilities line on this screen for
   the same reason. */
.fleet-bar .facilities { font-size: .74rem; }

/* A contract's deadline, loud in proportion to how near it is. The thresholds
   are contracts.go's: amber under two hours, red under thirty minutes. */
.expires { margin-top: .15rem; }
.expires.soon { color: var(--warn); }
.expires.urgent { color: var(--err-fg); }

/* Owned buildings carry a kind heading once there is more than one kind on
   screen. One heading over the whole list labels nothing, so it is not drawn. */
.group-label {
  margin: .7rem 0 .35rem; font-size: .68rem; color: var(--muted);
  text-transform: uppercase; letter-spacing: .05em;
}
.group-label:first-child { margin-top: 0; }

/* The generation rate, beside the meter it explains rather than inside a
   title= nobody on a touchscreen can reach. It is context, not a number you
   act on, so it is the first thing dropped when the bar gets tight. */
.overview .rate {
  font-size: .68rem; color: var(--muted); font-variant-numeric: tabular-nums;
}
.overview .ov-name .chip.country { font-weight: 400; color: var(--muted); }

@media (max-width: 60rem) {
  .overview .rate { display: none; }
}
