/* PLAN.md §3. A light cartographic poster, deliberately the inverse of the dark
   reference site: white page, pale grey land, near-black text, hairline borders,
   no gradients, no glow, no dark mode. The claim colours are the only saturated
   thing here, which is what makes a claimed country read across the page. */

:root {
  --page: #ffffff;
  --surface: #fafafa;
  --line: #e4e4e7;
  --line-strong: #d4d4d8;
  --ink: #18181b;
  --ink-soft: #52525b;
  --ink-faint: #a1a1aa;
  --land: #ebebee;
  --land-edge: #ffffff;
  --focus: #2563eb;
  --radius: 10px;
  --gap: 1.5rem;
  --measure: 34rem;
}

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

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font: 400 16px/1.55 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  font-feature-settings: 'kern' 1;
}

a { color: inherit; }
a:focus-visible, button:focus-visible, input:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

h1, h2, h3 { line-height: 1.2; letter-spacing: -0.015em; margin: 0 0 0.5rem; font-weight: 600; }
h1 { font-size: clamp(1.6rem, 1.1rem + 1.8vw, 2.4rem); }
h2 { font-size: 1.05rem; letter-spacing: 0.02em; text-transform: uppercase; color: var(--ink-soft); }
p { margin: 0 0 0.75rem; }

.money { font-variant-numeric: tabular-nums; }
.fine { color: var(--ink-soft); font-size: 0.85rem; }
.flag { font-size: 1.1em; }
.flag.large { font-size: 2rem; line-height: 1; }
.empty { color: var(--ink-soft); }
.unclaimed { color: var(--ink-faint); }

/* -------------------------------------------------------------- chrome */

.site-header {
  display: flex; align-items: center; justify-content: space-between; gap: 1rem;
  max-width: 1180px; margin: 0 auto; padding: 1.1rem 1.25rem;
}
.brand { font-weight: 650; letter-spacing: -0.02em; text-decoration: none; }
.site-header nav { display: flex; gap: 1.1rem; font-size: 0.9rem; }
.site-header nav a { color: var(--ink-soft); text-decoration: none; }
.site-header nav a:hover { color: var(--ink); }

main { max-width: 1180px; margin: 0 auto; padding: 0 1.25rem 4rem; }

footer {
  max-width: 1180px; margin: 0 auto; padding: 1.5rem 1.25rem 3rem;
  border-top: 1px solid var(--line); color: var(--ink-faint); font-size: 0.85rem;
}

/* ------------------------------------------------- the map page's shell

   PLAN.md §3: the map is the page. The body is a column — bar, map, one line
   of footer — and the map takes whatever is left. Without JavaScript the
   document still scrolls normally; the script adds `.js-map` to <html>, which
   pins the column to the viewport because scrolling then zooms instead. */

body.map-page { display: flex; flex-direction: column; min-height: 100dvh; }
body.map-page > main { flex: 1 1 auto; display: flex; min-height: 0; max-width: none; margin: 0; padding: 0; }
/* `margin: 0 auto` is a cross-axis auto margin on a column flex item, which
   suppresses the stretch and shrinks the bar to its contents. Both bars want
   the full width here, so both drop it. */
body.map-page > footer { max-width: none; margin: 0; padding: 0.6rem 1.25rem; text-align: center; }
.js-map body.map-page { height: 100dvh; min-height: 0; overflow: hidden; }

.map-header {
  position: sticky; top: 0; z-index: 20;
  max-width: none; margin: 0; flex-wrap: wrap; align-items: center; gap: 0.6rem 1.5rem;
  padding: 0.55rem 1.25rem; background: var(--page); border-bottom: 1px solid var(--line);
}
.brand-block { display: flex; align-items: baseline; gap: 0.7rem; flex-wrap: wrap; min-width: 0; }
.map-header .brand { margin: 0; font-size: 1.05rem; letter-spacing: -0.02em; }
.map-header .brand a { text-decoration: none; }
.brand-tagline { margin: 0; color: var(--ink-soft); font-size: 0.85rem; }

.map-header .stats {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.35rem 1.25rem;
  list-style: none; margin: 0 auto 0 0; padding: 0;
  color: var(--ink-soft); font-size: 0.85rem;
}
.map-header .stats strong { color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; }

.header-nav { display: flex; align-items: center; flex-wrap: wrap; gap: 0.25rem; font-size: 0.9rem; }
.header-nav .nav-link { color: var(--ink-soft); text-decoration: none; padding: 0.3rem 0.55rem; }
.header-nav .nav-link:hover { color: var(--ink); }

/* Disclosure dropdowns: they work by themselves, with no script at all. */
.header-panel { position: relative; }
.header-panel > summary {
  list-style: none; cursor: pointer; padding: 0.3rem 0.55rem; border-radius: 7px;
  color: var(--ink-soft); border: 1px solid transparent; white-space: nowrap;
}
.header-panel > summary::-webkit-details-marker { display: none; }
.header-panel > summary:hover { color: var(--ink); }
.header-panel[open] > summary { color: var(--ink); background: var(--surface); border-color: var(--line); }
.header-panel-body {
  position: absolute; top: calc(100% + 0.5rem); right: 0; z-index: 30;
  width: min(28rem, calc(100vw - 2rem)); max-height: min(70dvh, 32rem); overflow: auto;
  padding: 0.9rem 1.05rem; background: var(--page);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
}
.header-panel.wide .header-panel-body { width: min(44rem, calc(100vw - 2rem)); }
/* Narrow enough that a dropdown anchored to its own summary would hang off the
   left edge: pin it to the viewport instead, under the bar it belongs to. */
@media (max-width: 820px) {
  .header-panel-body, .header-panel.wide .header-panel-body {
    position: fixed; left: 0.75rem; right: 0.75rem; width: auto; max-height: 62dvh;
  }
}

/* Phones. The bar wraps to four rows here and was taking a quarter of a
   750px-tall viewport, which left the map a letterboxed strip in the middle of
   the page — and the map is the page (PLAN.md §3). Everything below buys that
   height back for the map. Nothing is dropped that is not still one tap away:
   the tagline is the sentence `/about` opens with. */
@media (max-width: 700px) {
  .map-header { padding: 0.45rem 0.85rem; gap: 0.4rem 0.9rem; }
  .brand-tagline { display: none; }
  .map-header .stats { gap: 0.2rem 0.9rem; font-size: 0.8rem; }
  .header-nav { gap: 0.05rem; font-size: 0.85rem; }
  .header-nav .nav-link, .header-panel > summary { padding: 0.25rem 0.4rem; }
  body.map-page > footer { padding: 0.45rem 0.85rem; font-size: 0.78rem; }
}

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

.map-stage { position: relative; flex: 1 1 auto; min-height: 0; min-width: 0; padding: 0.4rem 0.75rem; }
.map { display: block; width: 100%; height: 100%; touch-action: pan-x pan-y; }
/* Enhanced mode only. The script takes the touch gestures — one finger pans, two
   pinch — and it can only have them if the browser stops treating them as page
   scroll. That used to be the objection; it stopped being one when `.js-map`
   made the page not scroll at all (below). Without the script the map keeps its
   native behaviour and the document keeps scrolling, which is the guarantee. */
.js-map .map { touch-action: none; }
.map-stage.zoomable .map { cursor: grab; }
.map-stage.dragging .map { cursor: grabbing; }
@media (max-width: 700px) { .map-stage { min-height: 55dvh; } }

/* `:not([fill])` matters: a CSS `fill` beats the `fill="#…"` presentation
   attribute the server writes on a claimed country, which would leave the whole
   map grey. PLAN.md §3 wants the claims to be the only saturated thing here, so
   the stylesheet colours only the countries that have no claim colour of their own. */
.country { transition: fill 200ms ease; }
.country:not([fill]) { fill: var(--land); }
.preview { pointer-events: none; }
/* Countries are fills only. Every boundary is stroked once, by the meshed
   `.borders` path drawn over them: two neighbours stroking their shared edge put
   two coincident hairlines on it, and the doubled antialiasing made interior
   borders read heavier than coastlines. */
.borders { fill: none; stroke: var(--land-edge); stroke-width: 0.4; pointer-events: none; }
.decorative-land { fill: #f2f2f4; }
/* Hover, focus and selection are stroke-only copies of the country path in the
   `.selection` layer above the mesh. A stroke on the country path itself sits
   under the mesh line, which splits it into a double line along coastlines, and
   under later-painted neighbours, which cover its outer half along shared
   borders — the same country then shows both kinds of edge at once. The server
   renders the selected copy; the script maintains the hover and focus ones, so
   without JavaScript the two `:not(.js-map)` rules below stroke the country
   path directly instead — imperfect, but hover and focus are transient. */
.outline { fill: none; pointer-events: none; }
.outline-hover { stroke: var(--ink); stroke-width: 0.8; }
.outline-focus { stroke: var(--focus); stroke-width: 1.2; }
.outline-selected { stroke: var(--ink); stroke-width: 1.2; }
.map a:focus-visible .country { outline: none; }
html:not(.js-map) .map a:hover .country { stroke: var(--ink); stroke-width: 0.8; }
html:not(.js-map) .map a:focus-visible .country { stroke: var(--focus); stroke-width: 1.2; }

.marks { pointer-events: none; }
.marks a { pointer-events: auto; cursor: pointer; }
.mark rect { stroke: #ffffff; stroke-width: 1; }
.mark-letter { font-weight: 650; }
/* The label sits on the claim colour or on pale land, depending on the country's
   shape, so it carries a white halo and reads on either. Its font-size and the
   halo's stroke-width are presentation attributes scaled per mark in views.js —
   a CSS value here would beat both on every mark at once. */
.mark-host {
  fill: var(--ink);
  paint-order: stroke; stroke: var(--page); stroke-linejoin: round;
}

/* -------------------------------------------------------------- panels */

.report, .powers { list-style: none; margin: 0; padding: 0; }
.report li, .powers li {
  display: grid; align-items: baseline; gap: 0 0.6rem; padding: 0.5rem 0;
  border-top: 1px solid var(--line); font-size: 0.9rem;
}
.report li { grid-template-columns: auto 1fr auto; }
.report li:first-child, .powers li:first-child { border-top: 0; }
.report-line { min-width: 0; }
.report-amount { font-variant-numeric: tabular-nums; font-weight: 600; }
.report-when { grid-column: 2 / -1; color: var(--ink-faint); font-size: 0.8rem; }
.powers li { grid-template-columns: 1fr auto auto; }
.power-name { display: flex; flex-direction: column; min-width: 0; }
.power-name .host { color: var(--ink-faint); font-size: 0.8rem; }
.power-count { color: var(--ink-soft); }
.power-total { font-variant-numeric: tabular-nums; font-weight: 600; min-width: 4.5rem; text-align: right; }

.top-holders {
  position: absolute; right: 1.25rem; bottom: 1rem; z-index: 9;
  width: min(25rem, 42vw); max-height: calc(100% - 2rem); overflow-y: auto;
  padding: 0.85rem 1rem; background: var(--page);
  border: 1px solid var(--line-strong); border-radius: var(--radius);
}
.top-holders h2 { margin: 0 0 0.25rem; font-size: 0.85rem; }
.holders { list-style: none; margin: 0; padding: 0; }
.holders li {
  display: grid; grid-template-columns: 1.25rem minmax(0, 1fr) auto auto;
  align-items: center; gap: 0.5rem; padding: 0.38rem 0;
  border-top: 1px solid var(--line); font-size: 0.8rem;
}
.holders li:first-child { border-top: 0; }
.holders img, .holder-monogram { width: 1.25rem; height: 1.25rem; border-radius: 5px; }
.holders img { object-fit: cover; }
.holder-monogram { display: grid; place-items: center; font-size: 0.7rem; font-weight: 650; }
.holder-address { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.holder-count { color: var(--ink-soft); white-space: nowrap; }
.holder-spent { min-width: 3.5rem; text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; }

.swatch {
  display: inline-block; width: 0.7rem; height: 0.7rem; border-radius: 3px;
  margin-right: 0.4rem; vertical-align: -1px;
}
.swatch.large { width: 1.6rem; height: 1.6rem; border-radius: 6px; margin: 0; }
/* A reinforcement carries no colour, because it changes nothing the map shows.
   Its row still needs the same indent as every other, so the swatch keeps its
   space and shows an outline instead of a fill. */
.swatch.empty { background: transparent; border: 1px dashed var(--line-strong); }

/* -------------------------------------------------------- country index */

.index { width: 100%; border-collapse: collapse; font-size: 0.9rem; }
.index th, .index td { text-align: left; padding: 0.45rem 0.5rem; border-top: 1px solid var(--line); }
.index thead th { border-top: 0; color: var(--ink-faint); font-weight: 500; font-size: 0.8rem; text-transform: uppercase; letter-spacing: 0.04em; }
.index tbody th { font-weight: 500; }
.index tbody th a { text-decoration: none; }
.index tbody th a:hover { text-decoration: underline; }
.index .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ------------------------------------------------------ the country panel

   An overlay beside the map, on the side away from the country it describes,
   so it never covers what was clicked. The server picks the side from the
   country's position on the map; the script re-picks it from the click.

   It is also the order form (PLAN.md §3), and the order inside it is the point:
   what the country is, then the fields, then the priced button. The terms sit
   under all of that in their own scrolling box, so they are present before
   payment without pushing the button off the panel. */

.detail {
  position: absolute; top: 1rem; z-index: 10;
  width: min(25rem, 42vw); max-height: calc(100% - 2rem);
  display: flex; flex-direction: column;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  padding: 1rem 1.15rem; background: var(--surface);
  /* The last resort, and only that. The terms below shrink first and scroll on
     their own, which is what an ordinary viewport uses; this catches the case
     where the header, the fields and the button alone are taller than the panel
     is allowed to be. Without it those children simply paint outside the border
     and over the footer, and with `.js-map` suppressing page scroll the button
     is then unreachable rather than merely ugly. */
  overflow-y: auto;
}
.detail.side-left { left: 1.25rem; }
.detail.side-right { right: 1.25rem; }
.detail:focus { outline: none; }
.detail:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }
.detail h2 {
  font-size: 1.15rem; text-transform: none; letter-spacing: -0.015em;
  color: var(--ink); padding-right: 3.5rem; margin: 0 0 0.6rem;
}
.detail-close {
  position: absolute; top: 0.7rem; right: 0.8rem; padding: 0.15rem 0.35rem;
  color: var(--ink-soft); font-size: 0.85rem; text-decoration: none;
}
.detail-close:hover { color: var(--ink); }

.held {
  display: flex; gap: 0.7rem; align-items: flex-start; font-size: 0.9rem;
  padding: 0.75rem; border: 1px solid var(--line-strong); border-radius: var(--radius);
}
.held-copy { min-width: 0; flex: 1; }
.held .tagline { margin: 0 0 0.15rem; color: var(--ink); }
.held .meta { color: var(--ink-faint); font-size: 0.8rem; margin: 0.15rem 0 0; }
.held p { margin: 0; }
.held-link { display: inline-block; font-size: 1.05rem; font-weight: 600; }
.held-og {
  display: block; max-width: 100%; width: 100%; max-height: 9rem; object-fit: cover;
  margin-top: 0.65rem; border-radius: 8px;
}
/* Scoped: the country index has its own quieter "Unclaimed" cell (`.unclaimed`
   near the top of this file) and must keep it. */
.detail .unclaimed { margin: 0; color: var(--ink-soft); font-size: 0.9rem; }

/* The form is the panel's body, and it never shrinks: the fields and the button
   are the panel's reason to exist. The terms below give up space first. */
.detail form { margin: 0.85rem 0 0; flex: 0 0 auto; }
.detail form label { margin-bottom: 0.5rem; font-size: 0.85rem; }
.detail form input[type='text'] { margin-top: 0.25rem; padding: 0.45rem 0.6rem; }
.detail .hint { font-size: 0.78rem; line-height: 1.35; }
/* Two deliberate rows of six keep the full spectrum scannable instead of
   compressing twelve unnamed marks into one dense strip. Only the panel gets
   this fixed grid; the general palette remains a wrapping flex row. */
.detail .palette {
  display: grid; grid-template-columns: repeat(6, 1.5rem);
  margin-bottom: 0.6rem; gap: 0.3rem;
}
.detail .palette legend { grid-column: 1 / -1; }
.detail .swatch.large { width: 1.5rem; height: 1.5rem; }
.detail .consent { margin: 0.6rem 0 0.75rem; padding: 0.6rem 0.75rem; font-size: 0.8rem; }
.detail .moves { gap: 0.35rem; }
.detail .move.take { padding: 0.7rem 1rem; font-size: 0.95rem; }
.detail .move.reinforce { padding: 0.45rem 0.95rem; }
.detail .errors { margin: 0.6rem 0 0; font-size: 0.85rem; }

/* Last, and the only part that scrolls in an ordinary viewport. Everything above
   it stays put, so the button never leaves the panel however long the disclosure
   is. One paragraph: it used to be a paragraph plus a list saying the same thing
   twice.

   Every property `.what-you-buy` sets is re-stated here, because the element
   carries both classes: this is the same paragraph, and on the panel it is a
   hairline-ruled footnote rather than the boxed callout the prose pages get.
   The descendant selector is what makes that win — the two class selectors
   otherwise tie and the later one, `.what-you-buy`, took it.

   `min-height` is the floor §2.2a asks for: the disclosure is shown before
   payment, so it may shrink to about two lines and never to nothing. Below that
   the panel itself scrolls (`.detail`, above). */
.detail .panel-terms {
  flex: 0 1 auto; min-height: 2.9em; overflow-y: auto;
  margin: 0.8rem 0 0; padding: 0.7rem 0 0;
  border: 0; border-top: 1px solid var(--line); border-radius: 0; background: none;
  color: var(--ink-soft); font-size: 0.78rem; line-height: 1.45;
}

@media (max-width: 780px) {
  .top-holders { display: none; }
  .detail {
    width: auto; left: 0.75rem; right: 0.75rem; top: auto; bottom: 0.75rem; max-height: 80%;
  }
}

/* ---------------------------------------------------------- move buttons

   PLAN.md §3. The take carries all the emphasis and its price; the
   reinforcement is quiet and names itself, so it cannot be hit by accident. */

.moves { margin: 0; padding: 0; display: grid; gap: 0.45rem; }
.move { display: inline-block; border-radius: 8px; text-decoration: none; }
.move.take {
  display: block; width: 100%; padding: 0.7rem 0.95rem; text-align: center;
  background: var(--ink); color: var(--page); font: inherit; font-weight: 550;
  border: 1px solid var(--ink); cursor: pointer;
}
.move.take:hover { background: #000; }
.move.take .money { color: var(--page); }
.move.reinforce {
  display: block; padding: 0.5rem 0.95rem; text-align: center;
  border: 1px solid var(--line-strong); background: transparent;
  color: var(--ink-soft); font-size: 0.875rem; font-weight: 400;
}
.move.reinforce:hover { color: var(--ink); border-color: var(--line-strong); background: var(--page); }

/* --------------------------------------------------------------- forms */

.form-shell { max-width: var(--measure); padding-top: 1rem; }
.back { display: inline-block; margin-bottom: 1rem; color: var(--ink-soft); font-size: 0.9rem; text-decoration: none; }
.back:hover { color: var(--ink); }

form label { display: block; margin-bottom: 0.9rem; font-size: 0.9rem; font-weight: 500; }
form input[type='text'], form input[type='number'] {
  display: block; width: 100%; margin-top: 0.35rem; padding: 0.6rem 0.7rem;
  font: inherit; color: var(--ink); background: var(--page);
  border: 1px solid var(--line-strong); border-radius: 8px;
}
.hint { display: block; font-weight: 400; color: var(--ink-soft); }
.url-error { color: #991b1b; margin-top: 0.25rem; }

.palette { border: 0; padding: 0; margin: 0 0 1.1rem; }
.palette legend { padding: 0; font-size: 0.9rem; font-weight: 500; margin-bottom: 0.45rem; }
.palette { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.palette legend { width: 100%; }
.swatch-option { margin: 0; cursor: pointer; }
.swatch-option .swatch.large { background: var(--swatch); display: block; border: 2px solid transparent; }
.swatch-option input { position: absolute; opacity: 0; width: 0; height: 0; }
.swatch-option input:checked + .swatch { border-color: var(--ink); box-shadow: 0 0 0 2px var(--page) inset; }
.swatch-option input:focus-visible + .swatch { outline: 2px solid var(--focus); outline-offset: 2px; }
.swatch-name {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}

.primary {
  display: inline-block; padding: 0.65rem 1.1rem; border: 0; border-radius: 8px;
  background: var(--ink); color: var(--page); font: inherit; font-weight: 550;
  text-decoration: none; cursor: pointer;
}
.primary:hover { background: #000; }

.errors {
  border: 1px solid #dc2626; border-radius: 8px; padding: 0.75rem 1rem; margin-bottom: 1rem;
  color: #991b1b; background: #fef2f2; font-size: 0.9rem;
}
.errors ul { margin: 0.4rem 0 0; padding-left: 1.1rem; }

/* --------------------------------------------------------------- prose */

.prose { max-width: var(--measure); padding-top: 1rem; }
.prose h2 { margin-top: 2rem; font-size: 0.95rem; }
.prose ul { padding-left: 1.1rem; color: var(--ink-soft); }
.prose li { margin-bottom: 0.5rem; }

/* ----------------------------------------------------------- directory */

/* The directory is a text list, not a board: it is what a payment leaves behind
   once the map has moved on, so it stays quiet next to the map's colour. */
.entries { list-style: none; padding: 0; margin: 1.5rem 0 0; }
.entry {
  display: flex; gap: 0.75rem; align-items: flex-start;
  padding: 0.85rem 0; border-top: 1px solid var(--line);
}
.entry .swatch { flex: 0 0 auto; margin-top: 0.25rem; }
.entry-body { min-width: 0; }
.entry-body strong { font-weight: 600; }
.entry-body .tagline { margin: 0.15rem 0 0.3rem; color: var(--ink-soft); }
.entry-body .meta { margin: 0; font-size: 0.85rem; color: var(--ink-faint); }
.entry-body .meta a { color: var(--ink-soft); }
.badge {
  margin-left: 0.5rem; padding: 0.05rem 0.4rem; border: 1px solid var(--line-strong);
  border-radius: 999px; font-size: 0.7rem; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--ink-soft); vertical-align: 0.1em;
}
/* "Reinforced" is a fact about the payment, not a claim on the country, so it
   reads quieter than the badge that says who holds a country now. */
.badge.quiet { border-style: dashed; color: var(--ink-faint); }
.more { display: inline-block; margin-top: 1.25rem; }

/* The withdrawal waiver (CRD Art. 16(m)). It is a required box the buyer ticks
   themselves, so it is given the room to be read rather than tucked under the
   button. The checkbox keeps its native appearance on purpose. */
.consent {
  display: flex; gap: 0.6rem; align-items: flex-start;
  margin: 1.25rem 0; padding: 0.75rem 0.9rem;
  border: 1px solid var(--line-strong); border-radius: var(--radius);
  background: var(--surface); font-size: 0.85rem; line-height: 1.45;
  color: var(--ink-soft); cursor: pointer;
}
.consent input { margin-top: 0.15rem; flex: none; }
.consent input:focus-visible { outline: 2px solid var(--focus); outline-offset: 2px; }

/* What a payment buys, and what can be taken away. It appears on every step that
   leads to money, so it is styled to be read rather than skipped. */
.what-you-buy {
  margin: 1.25rem 0; padding: 0.85rem 1rem; background: var(--surface);
  border: 1px solid var(--line); border-radius: var(--radius);
  font-size: 0.9rem; line-height: 1.5;
}
