/* World Atlas map styling.
 *
 * Wrapped in @layer components, the studio.css treatment — see the
 * "cascade-layer trap" section of docs/DESIGN_SYSTEM.md. Tailwind's utilities
 * live in a later layer, so anything written as a utility in atlas markup
 * still wins over these rules rather than losing to a bare selector.
 *
 * Every colour here is a semantic token. That is not style discipline for its
 * own sake: the Leaflet SVG renderer emits real <path> elements, so `.dark`
 * flipping the token values repaints the whole map with no JavaScript, no
 * theme listener and no repaint hook. It only works because no hex ever passes
 * through Leaflet's style function.
 */
@layer components {
  /* The ceiling on an origin map, in ONE place because two rules need it and
     the second is derived from the first (see .atlas-map-panel.is-wide). At
     :root for the same reason --crop-* is: a value scoped to the panel is
     undefined to anything outside it, and that has already shipped once here
     as colourless legend swatches. */
  :root {
    --atlas-map-max-h: min(58rem, 88vh);
  }

  .atlas-map {
    /* Leaflet measures its container, so the height has to be real and
       present before the map initialises — a 0-height container yields a map
       that never draws and looks like a JS failure. */
    height: 22rem;
    width: 100%;
  }

  /* The world index stays a compact clickable picture. */
  @media (min-width: 1024px) {
    .atlas-map-panel .atlas-map { height: 18rem; }
  }

  /* THE INDEX MAP IS THE SHAPE OF THE BELT. See AtlasHelper#atlas_world_aspect:
     the belt is about four times wider than it is deep, and a box that ignored
     that padded the fit out with tens of degrees of latitude nobody grows
     coffee at. No max-width cap, unlike .is-wide — the index map wants the
     full content width; it is the aspect that was wrong, not the width. */
  .atlas-map-panel.is-belt .atlas-map {
    height: auto;
    aspect-ratio: var(--atlas-aspect, 4);
    min-height: 11rem;
    max-height: 30rem;
  }

  /* The origin map is a different object: every province of one country,
     coloured by species, pan and zoom. It takes the full content width and
     real height, because a country squeezed into a 20rem sidebar cannot be
     read, let alone explored. */
  /* THE BOX IS THE COUNTRY'S OWN SHAPE. A fixed height is wrong for a map:
     Vietnam is long and narrow, Indonesia is a wide band, and one box
     pillarboxes the first while letterboxing the second. --atlas-aspect comes
     from the country's bounding box (AtlasHelper#atlas_map_aspect), so the
     height follows from the column width and the country fills what it is
     given. The min/max are the guard rails for extreme shapes. */
  .atlas-map-panel.is-wide .atlas-map {
    height: auto;
    aspect-ratio: var(--atlas-aspect, 4 / 3);
    min-height: 18rem;
    /* ONE definition, read twice — here and by the .is-wide max-width cap
       below, which is `this x aspect`. They were two literals and could drift
       apart silently; a cap wider than the height allows is a box that never
       reaches its own column. The vh half is because this is an INTERACTIVE
       map: a country taller than the window cannot be taken in at a glance,
       and panning to see the rest of a static shape is not exploration. */
    max-height: var(--atlas-map-max-h);
  }

  /* The CARD hugs the map, rather than the map floating in a full-width card.
     Constraining the inner element alone left Vietnam as a narrow strip of map
     inside a wide empty box, which just moved the wasted space inside the
     border — tried it here at the smaller scale the shape-driven column
     produces, ~35px of dead border on each side, same flaw.
     The shape-driven column (AtlasHelper#atlas_layout_class) is a grid
     FRACTION sized to make room for a country, not measured to it: at two
     fifths of the row Vietnam gets a track far wider than its own 0.47 aspect
     implies at max-height. Past that point the aspect-ratio above loses to
     max-height and the box would render wider than Vietnam actually is — wide
     enough to pull an offshore label (Trường Sa) back inside a frame it is
     meant to sit outside of. Capping the CARD at height x aspect keeps it
     true to the shape regardless of how wide a track it was handed. It binds
     hardest on the narrowest countries and slackens as a country squares up;
     a broad one never comes near it. */
  .atlas-map-panel.is-wide {
    max-width: calc(var(--atlas-map-max-h) * var(--atlas-aspect, 1.333));
    margin-inline: auto;
  }

  /* THE SPLIT IS THE COUNTRY'S SHAPE. One rule, three outcomes, chosen in Ruby
     (AtlasHelper#atlas_layout_class) so nothing measures anything at runtime.
     Below lg everything stacks, map first.

     Both splits are fifths of one row, so the two buckets stay comparable
     rather than each being its own arbitrary ratio: a tall country hands the
     map 2 of 5 and a squarer one 3 of 5. Earlier these were a third and a
     half; the map was the thing readers came for and it was losing the row to
     cards that only ever restate what the map already shows. */
  @media (min-width: 1024px) {
    .atlas-layout.layout-tall     { grid-template-columns: minmax(0, 2fr) minmax(0, 3fr); }
    .atlas-layout.layout-balanced { grid-template-columns: minmax(0, 3fr) minmax(0, 2fr); }
  }

  /* COLUMN FLOW, not a two-column grid. A grid makes every row as tall as its
     tallest card, and these cards differ wildly — Lâm Đồng carries two nested
     districts and runs about twice the height of Tây Nguyên. The short card
     then held open a row it did not fill and left a ~330px hole above the
     next one, which reads as a bug rather than as a ragged edge.
     `align-items: stretch` is the other way out and is worse: it puts the same
     hole INSIDE the short card.
     Columns pack them instead. DOM order is untouched, so keyboard and
     screen-reader order still follow the regions' own `position`; only the
     visual flow becomes column-major, which is how any column of cards reads.

     Layout, all of it, lives on this class rather than on `grid gap-4`
     utilities in the markup. Tailwind's utilities sit in a LATER cascade layer
     than @layer components, so a `display: block` written here would lose to a
     `grid` class written there — the cascade-layer trap in
     docs/DESIGN_SYSTEM.md, which this feature has already fallen into twice. */
  .atlas-region-list > * { break-inside: avoid; }
  .atlas-region-list > * + * { margin-top: 1rem; }

  @media (min-width: 1280px) {
    .atlas-layout.layout-tall .atlas-region-list {
      columns: 2;
      column-gap: 1rem;
    }
    .atlas-layout.layout-tall .atlas-region-list > * {
      margin-top: 0;
      margin-bottom: 1rem;
    }
  }

  @media (min-width: 1024px) {
    /* A 2.2:1 country squeezed into even two thirds of a row is a strip of map
       nobody can read, so it stops sharing the row and the cards go beneath. */
    .atlas-layout.layout-broad { grid-template-columns: minmax(0, 1fr); }
    .atlas-layout.layout-broad > :last-child {
      display: grid;
      grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
      gap: 1rem;
    }
    /* That grid owns its own spacing; the stacked margin above would double it. */
    .atlas-layout.layout-broad .atlas-region-list > * { margin: 0; }
  }

  /* COLOUR AND ALPHA ARE SEPARATE, and that is not a style preference.
   *
   * The obvious spelling is `fill: var(--accent-tint-12)`, an 8-digit-hex
   * token. Chrome resolves it correctly on first paint and then does NOT
   * re-resolve it when `.dark` changes the token's value — the custom property
   * updates, `stroke` (a 6-digit token) repaints, and `fill` stays on the light
   * colour. The map ended up half-repainted in dark mode, which is exactly the
   * bug atlas_map_test.rb's theme case now pins.
   *
   * Splitting the tint into `fill: var(--accent)` plus a plain numeric
   * `fill-opacity` sidesteps it entirely, and reads better besides: hover moves
   * only the opacity, so the transition is one property rather than a crossfade
   * between two composited colours. */
  .atlas-country {
    fill: var(--accent);
    /* Transparent on an origin page, where the relief and the provinces are
       the content; the index has no relief, so it keeps its tint there — and
       this number is FOR the index, since .is-wide zeroes it one rule below.
       It was 0.12, which at world scale left the producing countries a wash
       of pale pink that the beige context read louder than. They are the
       subject of that map; they have to look like it. */
    fill-opacity: 0.5;
    stroke: var(--accent);
    stroke-width: 1;
    stroke-dasharray: 3 2;
    cursor: pointer;
    transition: fill-opacity 0.15s ease;
  }

  .atlas-country:hover,
  .atlas-country:focus-visible {
    fill-opacity: 0.78;
    outline: none;
  }

  .atlas-country:focus-visible {
    stroke: var(--ink);
    stroke-width: 2;
    stroke-dasharray: none;
  }

  /* An origin with no page: drawn for context, never clickable, never
     focusable. `is-dimmed` is the same treatment applied by the facet pass. */
  /* Context: the rest of the world, and it should read as ground rather than
     as figure. At full opacity the beige competed with the origins it exists
     to locate. */
  .atlas-country.is-inactive,
  .atlas-country.is-dimmed {
    fill: var(--surface-2);
    fill-opacity: 0.55;
    stroke: var(--border);
    stroke-opacity: 0.7;
    cursor: default;
  }

  .atlas-country.is-inactive:hover,
  .atlas-country.is-dimmed:hover {
    fill-opacity: 0.55;
  }

  /* AN ORIGIN, on the index map. Sized so that Rwanda is exactly as findable
     and exactly as clickable as Brazil — at belt scale a country's pixels
     measure its geography, not how much coffee comes out of it.
     Stroked in the page colour so a dot sitting on its own shaded country
     still reads as a separate mark rather than dissolving into it. */
  .atlas-origin-dot {
    fill: var(--accent);
    stroke: var(--bg);
    cursor: pointer;
    transition: r 0.12s ease;
  }

  .atlas-origin-dot:hover,
  .atlas-origin-dot:focus-visible {
    r: 7;
    outline: none;
  }

  .atlas-origin-dot:focus-visible {
    stroke: var(--ink);
  }

  /* Narrowed away by a facet. Matches what the countries do, so the map says
     one thing rather than two. */
  .atlas-origin-dot.is-dimmed {
    fill: var(--border);
    cursor: default;
  }

  /* A schematic growing area. Deliberately a soft, unpinned dot rather than a
     pin or a crosshair — the shape should not imply a surveyed coordinate,
     because it is not one. */
  /* A named feature that grows no coffee — drawn in ink, not in a crop
     colour, so it never reads as a growing area. */
  .atlas-place {
    fill: var(--ink-soft);
    stroke: var(--bg);
  }

  .atlas-region-dot {
    fill: var(--accent);
    stroke: var(--bg);
    stroke-width: 2;
  }


  /* ── Species colours ──────────────────────────────────────────────────────
   *
   * A categorical scale, and the ONLY place this design system carries more
   * than one hue. It is scoped to the map panel rather than added to the
   * global palette, because it is data encoding, not brand: outside a map
   * there is no such thing as "the robusta colour".
   *
   * Arabica takes the accent the rest of the site already uses; robusta an
   * indigo far enough away in hue AND lightness to survive greyscale printing
   * and the common forms of colour blindness; liberica and excelsa the earth
   * tones, which are the rarest crops and the least often drawn.
   */
  /* Declared at :root, not on the map panel. The legend's swatches sit OUTSIDE
     that panel, and scoping the scale to the map left them painting with an
     undefined variable — colourless chips beside correctly coloured shapes,
     which is worse than no legend. Named --crop-* rather than folded into the
     palette because they are data encoding, not brand. */
  :root {
    --crop-arabica:  #c25f42;
    --crop-robusta:  #4f6395;
    --crop-liberica: #8a6b4f;
    --crop-excelsa:  #5d7a55;
  }

  .dark {
    --crop-arabica:  #e8946f;
    --crop-robusta:  #8ea3d8;
    --crop-liberica: #d3b48c;
    --crop-excelsa:  #9dbd92;
  }

  /* The shaded-relief crop. Greyscale from Natural Earth, tinted to the paper
     rather than left grey: a neutral grey photograph under warm ink reads as a
     screenshot of a different map. Multiply blending lets the paper's own
     colour come through the light parts of the hillshade. */
  .atlas-relief {
    opacity: 0.72;
    mix-blend-mode: multiply;
    filter: sepia(0.35) saturate(0.7);
  }

  .dark .atlas-relief {
    /* Inverted, so ridges stay light against the charcoal instead of becoming
       black-on-black, and screened rather than multiplied for the same reason. */
    opacity: 0.4;
    mix-blend-mode: screen;
    filter: invert(1) sepia(0.3) saturate(0.6);
  }

  /* Everything outside the country, in the page's own colour, so the
     rectangular crop reads as a window rather than a pasted photograph. */
  .atlas-relief-mask {
    fill: var(--bg);
    fill-opacity: 1;
  }

  /* Every province of the country, drawn for context. NO FILL: the relief is
     underneath, and a filled province would hide the terrain the map now
     exists to show. Shaded ones override with a translucent fill. */
  .atlas-province {
    fill: var(--surface-2);
    fill-opacity: 0;
    /* Reads against the relief: --border is tuned for hairlines on flat paper
       and disappears over a hillshade. Solid rather than dashed, because an
       administrative boundary is a real line — the dashes elsewhere in this
       design system are a house style, not a cartographic one. */
    stroke: var(--ink-soft);
    stroke-opacity: 0.55;
    stroke-width: 0.75;
    stroke-dasharray: none;
    transition: stroke-opacity 0.15s ease;
  }

  .atlas-province:hover { stroke-opacity: 1; }

  /* Translucent, so the relief reads through the species colour — the whole
     point of putting terrain under a coffee map is that altitude and crop are
     the same story. */
  .atlas-province.is-arabica  { fill: var(--crop-arabica);  fill-opacity: 0.45; }
  .atlas-province.is-robusta  { fill: var(--crop-robusta);  fill-opacity: 0.45; }
  .atlas-province.is-liberica { fill: var(--crop-liberica); fill-opacity: 0.45; }
  .atlas-province.is-excelsa  { fill: var(--crop-excelsa);  fill-opacity: 0.45; }

  .atlas-province.is-arabica,
  .atlas-province.is-robusta,
  .atlas-province.is-liberica,
  .atlas-province.is-excelsa,
  .atlas-province.is-mixed {
    stroke: var(--ink-soft);
    stroke-width: 0.75;
    stroke-dasharray: none;
  }

  /* MORE THAN ONE SPECIES IN ONE PROVINCE — Lâm Đồng grows Arabica around
   * Đà Lạt and Robusta around Đức Trọng, and that is the normal case, not an
   * edge case. Stripes rather than a blended colour: a reader decodes stripes
   * as "both of these", and decodes an averaged colour as a third species
   * that does not exist. Painted with a repeating gradient so it needs no
   * SVG <pattern> node and no id to collide with.
   */
  /* An SVG fill cannot take a CSS gradient, so the stripes come from a real
     <pattern> the map controller injects into Leaflet's own <svg>. The
     pattern's two bands carry classes, which is what keeps them on the theme
     tokens and repainting in dark mode like everything else here. */
  .atlas-province.is-mixed {
    fill: url(#atlas-mixed-stripes);
    fill-opacity: 0.6;
  }

  .atlas-stripe-a { fill: var(--crop-arabica); }
  .atlas-stripe-b { fill: var(--crop-robusta); }

  /* The region a reader just asked to see. A ring rather than a colour change,
     so the species encoding underneath is not overwritten by the selection. */
  .atlas-province.is-focused {
    stroke: var(--ink);
    stroke-width: 2;
    stroke-dasharray: none;
  }

  .atlas-region-dot.is-focused {
    stroke: var(--ink);
    stroke-width: 3;
  }

  /* On a page with relief, the country outline is a border, not a fill. */
  .atlas-map-panel.is-wide .atlas-country { fill-opacity: 0; }

  /* WHERE A REGION SITS, against the other regions on the same page. The
     numeric range is printed above this; the bar exists so that "Nariño is the
     high one" is legible without reading four numbers and holding them in your
     head. Scaled to the regions rather than the country — see
     AtlasHelper#atlas_altitude_scale, which also explains why a country-scaled
     bar would overflow its own track. */
  .atlas-alt-track {
    position: relative;
    height: 0.25rem;
    border-radius: 999px;
    background: var(--surface-2);
    overflow: hidden;
  }

  .atlas-alt-band {
    position: absolute;
    top: 0;
    bottom: 0;
    border-radius: 999px;
    background: var(--accent);
    opacity: 0.55;
  }

  /* THE NEIGHBOURS. Context for where on earth the subject sits, and nothing
     more: a country alone on blank paper could be anywhere.
     Faint enough to never compete with the subject — this is the one place in
     the map where something is deliberately hard to read. Ink rather than
     accent, because accent is the colour that means "coffee is grown here"
     everywhere else on the page and a neighbour is making no such claim.
     The `.is-wide` selector above zeroes .atlas-country's fill on relief
     pages, so the fill-opacity here has to come after it to survive; the
     stroke is what actually carries the shape at this weight. */
  .atlas-map-panel.is-wide .atlas-country.is-neighbour {
    fill: var(--ink);
    fill-opacity: 0.04;
    stroke: var(--ink);
    stroke-opacity: 0.18;
    stroke-width: 0.75;
    stroke-dasharray: none;
    cursor: default;
    pointer-events: none;
  }

  /* A district: the same species encoding as a province, drawn harder. It is a
     finer and therefore truer claim, and it has to read on top of the province
     it sits inside. */
  .atlas-district {
    fill: var(--surface-2);
    fill-opacity: 0;
    stroke: var(--ink);
    stroke-width: 1.25;
    stroke-dasharray: none;
    pointer-events: none;
  }

  .atlas-district.is-arabica  { fill: var(--crop-arabica);  fill-opacity: 0.75; }
  .atlas-district.is-robusta  { fill: var(--crop-robusta);  fill-opacity: 0.75; }
  .atlas-district.is-liberica { fill: var(--crop-liberica); fill-opacity: 0.75; }
  .atlas-district.is-excelsa  { fill: var(--crop-excelsa);  fill-opacity: 0.75; }
  .atlas-district.is-mixed    { fill: url(#atlas-mixed-stripes); fill-opacity: 0.85; }

  .atlas-district.is-focused { stroke-width: 2.5; }

  .atlas-swatch {
    display: inline-block;
    width: 0.75rem;
    height: 0.75rem;
    border-radius: 2px;
    border: 1px solid var(--border);
  }

  .atlas-swatch.is-arabica  { background: var(--crop-arabica);  }
  .atlas-swatch.is-robusta  { background: var(--crop-robusta);  }
  .atlas-swatch.is-liberica { background: var(--crop-liberica); }
  .atlas-swatch.is-excelsa  { background: var(--crop-excelsa);  }
  .atlas-swatch.is-mixed {
    background: repeating-linear-gradient(45deg,
      var(--crop-arabica) 0 3px, var(--crop-robusta) 3px 6px);
  }

  /* The name is a button but must not look like one — it is the card's title.
     The affordance is the hover colour and the focus ring, not a border. */
  .atlas-region-name {
    background: none;
    border: none;
    padding: 0;
    font: inherit;
    color: inherit;
    cursor: pointer;
  }

  .atlas-region-name:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 3px;
    border-radius: 2px;
  }

}

/* ── Vendor overrides: DELIBERATELY UNLAYERED ────────────────────────────────
 *
 * These beat Leaflet's own sheet, and they can only do that from outside a
 * layer. `stylesheet_link_tag :app` loads every stylesheet in the directory
 * alphabetically, so leaflet.css lands AFTER atlas.css — and unlayered
 * declarations outrank every layered one regardless of source order (the
 * cascade-layer trap in docs/DESIGN_SYSTEM.md). Inside @layer components above,
 * `.leaflet-container { background: transparent }` loses to Leaflet's own
 * `#ddd` and the map renders as a grey slab on the paper.
 *
 * Keep this block small and strictly vendor-facing. Anything that is OUR
 * design belongs in the layered block above, where a Tailwind utility can
 * still override it. */
.atlas-map.leaflet-container {
  /* Two classes, so specificity settles this rather than source order.
     `stylesheet_link_tag :app` loads the directory alphabetically, so
     leaflet.css always lands after atlas.css and a plain `.leaflet-container`
     here loses to its `background: #ddd` — the map rendered as a grey slab on
     the paper. Scoped to OUR container, which Leaflet decorates with its own
     class on init, this wins wherever the sheets end up. */
  background: transparent;
  font: inherit;
  outline-offset: 2px;
}

/* UNLAYERED, with the other vendor overrides above — and for the same reason.
   Leaflet's own `.leaflet-tooltip` gives every tooltip a white box, a border
   and a shadow, and it is unlayered, so it beats anything inside
   @layer components no matter how specific. Layered, these rules lost and
   every province label rendered in a little white bubble on top of the
   terrain.

   Province names, on the provinces that grow coffee. Sits on the map with no
   box: a tooltip chrome per label would bury the terrain under bubbles. The
   halo is what keeps it legible over a hillshade, and it is drawn with four
   offset shadows rather than a stroke because paint-order is unreliable
   across browsers for text. */
.atlas-place-label.leaflet-tooltip {
  background: none;
  border: none;
  box-shadow: none;
  padding: 0;
  color: var(--ink-soft);
  font-size: 0.625rem;
  font-style: italic;
  white-space: nowrap;
  text-shadow:
     1px  1px 0 var(--bg),  -1px  1px 0 var(--bg),
     1px -1px 0 var(--bg),  -1px -1px 0 var(--bg);
}

.atlas-place-label.leaflet-tooltip::before { display: none; }

.atlas-district-label.leaflet-tooltip,
.atlas-province-label.leaflet-tooltip {
  background: none;
  border: none;
  box-shadow: none;
  padding: 0;
  color: var(--ink);
  font-family: var(--font-sans, inherit);
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  white-space: nowrap;
  text-shadow:
     1px  1px 0 var(--bg),  -1px  1px 0 var(--bg),
     1px -1px 0 var(--bg),  -1px -1px 0 var(--bg),
     0 2px 3px var(--bg);
}

.atlas-district-label.leaflet-tooltip::before,
.atlas-province-label.leaflet-tooltip::before { display: none; }

/* A district label is the finer of the two, so it is the smaller. */
.atlas-district-label.leaflet-tooltip {
  font-size: 0.5625rem;
  letter-spacing: 0.04em;
  text-transform: none;
}

.atlas-tooltip.leaflet-tooltip {
  background: var(--surface);
  color: var(--ink);
  border: 1px dashed var(--border);
  border-radius: 6px;
  box-shadow: none;
  font-size: 0.75rem;
  padding: 0.2rem 0.45rem;
}

.atlas-tooltip.leaflet-tooltip::before { display: none; }
