/* Blood & Money — committed dark, vampiric. Blood = need, gold = money. */
:root{
  color-scheme: dark;
  --bg:#0B0708; --bg-deep:#060405;
  --surface:#120D0F; --surface-hover:#1A1315; --surface-active:#241A1C;
  --text:#EDE4DC; --text-2:#A2938A; --text-muted:#8A7B72;   /* 4.7:1 on surface */
  --border:#251B1D; --border-strong:#3D2C30; --focus:#C8102E;

  --need:#C8102E; --need-bright:#EE3E56; --need-soft:#26090F;   /* 4.8:1 on need-soft */
  --paid:#B98F4B; --paid-bright:#D9AE68; --paid-soft:#20160B;
  --antid:#B48CFF;   /* the Rh-negative premium: violet, so it reads as neither economy's color */
  --platelet:#B48CFF; /* binary ring on volunteer sites; red heat still means demand */

  /* Basemap tokens — land sits ABOVE water so coastlines read as edges. */
  --map-land:#171013; --map-water:#050304; --map-park:#141014;
  --map-building:#2E1E22; --map-road:#402A2F; --map-coast:#4A3238;
  --map-label:#A2938A;

  --chev:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12' fill='none' stroke='black' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M2.5 4.5L6 8l3.5-3.5'/%3E%3C/svg%3E");
  --keyline-h:64px;
  --radius:10px; --radius-sm:6px;
  --shadow:0 1px 2px rgba(0,0,0,.6), 0 12px 32px rgba(0,0,0,.55);
  --t:150ms ease-out;

  /* Two faces only. Grenze Gotisch = every heading: genuinely gothic, and
     legible down to 17px because it is a modern gothic, not blackletter. */
  --display:"Grenze Gotisch",Georgia,serif;
  --font:"Inter",-apple-system,BlinkMacSystemFont,"Segoe UI",Helvetica,Arial,sans-serif;
}
*{box-sizing:border-box}
html,body{height:100%}
a{color:var(--platelet)}
a:hover{color:#C7A8E6}
body{margin:0;background:var(--bg-deep);color:var(--text);font-family:var(--font);
  font-size:14px;line-height:20px;-webkit-font-smoothing:antialiased}
svg{display:block}
::selection{background:var(--need);color:#fff}
.app{display:grid;grid-template-columns:376px 1fr;grid-template-rows:minmax(0,1fr) auto;
  height:100dvh}
.panel{grid-column:1;grid-row:1}
.map-wrap{grid-column:2;grid-row:1}

/* ---------- panel ---------- */
.panel{background:var(--surface);border-right:1px solid var(--border);
  display:flex;flex-direction:column;min-height:0;overflow-y:auto;overscroll-behavior:contain;
  position:relative;z-index:2;
  box-shadow:1px 0 0 rgba(200,16,46,.14), 24px 0 48px -24px rgba(0,0,0,.9)}
/* Title and tagline line up with the text inside the cards below: 24px section
   inset + the card's 1px border + 16px padding = 41px. */
.head{padding:24px 24px 24px 41px;border-bottom:1px solid var(--border);position:relative}
.brand{display:flex;align-items:baseline;gap:8px;margin:0 0 8px;flex-wrap:wrap}
.brand h1{font-family:var(--display);font-size:40px;line-height:44px;font-weight:700;
  margin:0;letter-spacing:.02em;color:var(--text);
  text-shadow:0 0 22px rgba(200,16,46,.28)}
.tagline{margin:0;font-size:13px;line-height:16px;color:var(--text-2)}

.layers{display:flex;gap:8px;padding:24px 24px 0}
.layer{flex:1;display:flex;flex-direction:column;gap:8px;padding:16px;cursor:pointer;
  border:1px solid var(--border);border-radius:var(--radius);background:var(--bg);
  color:var(--text-2);transition:background var(--t),border-color var(--t),color var(--t);
  text-align:left;font:inherit}
.layer:hover{background:var(--surface-hover);border-color:var(--border-strong)}
.layer:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.layer.need[aria-pressed="true"]{color:var(--need-bright);background:var(--need-soft);
  border-color:rgba(200,16,46,.5);box-shadow:inset 0 0 24px rgba(200,16,46,.16)}
.layer.paid[aria-pressed="true"]{color:var(--paid-bright);background:var(--paid-soft);
  border-color:rgba(185,143,75,.5);box-shadow:inset 0 0 24px rgba(185,143,75,.14)}
.layer .k{line-height:16px;display:flex;align-items:center;gap:4px;font-weight:600;font-size:13px;
  letter-spacing:.06em}
.layer .v{font-size:12px;line-height:16px;color:var(--text-muted)}
.layer[aria-pressed="true"] .v{color:inherit;opacity:.75}
.dot{width:8px;height:8px;border-radius:50%;background:currentColor;flex:none}

/* ---------- evidence badges ---------- */
/* One component, one placement: top-right of a card header. The colour encodes
   how much weight a claim carries, which is this project's whole argument:
   Medical = established clinical fact · Census = official statistics
   Reported = someone's own claim about themselves · Folklore = belief, not evidence */
.cardhead{display:flex;align-items:baseline;gap:8px;margin-bottom:8px}
.cardhead h3{margin:0;font-family:var(--display);font-size:22px;line-height:28px;
  font-weight:600;color:var(--text)}
.cardhead .jp{line-height:16px;font-size:12px;color:var(--text-muted)}
/* A label, not a control. No border, no fill, no pill: an outlined rounded box
   is exactly what a button looks like, which is why these read as CTAs. White
   text, letter-spaced, sitting quietly at the end of the card header. */
.badge{line-height:14px;margin-left:auto;flex:none;font-size:11px;letter-spacing:.02em;
  text-transform:none;font-style:italic;color:var(--text);border:0;background:none;padding:0;
  white-space:nowrap;align-self:center;opacity:.75}

/* Correlation cards: where each layer sits on a variable, as a position on the
   same 0-100 scale, so the comparison is visual rather than a table of numbers. */
.corr{margin:24px 24px 0;padding:16px;border:1px solid var(--border);border-radius:var(--radius);
  background:var(--bg)}
.corr .row{margin-bottom:24px}   /* a row's note sits 8px under its bar and 24px above the next row, so it reads as belonging to the bar */
.corr .row:last-of-type{margin-bottom:0}
/* Label and reading are one pair: tight lines inside (16px on 12px type), and a
   clear 16px step before the track so the pair reads as belonging together. */
.corr .lab{display:flex;justify-content:space-between;gap:8px;font-size:12px;line-height:16px;margin-bottom:16px}
.corr .lab b{font-weight:600;color:var(--text);line-height:16px}
.corr .lab span{color:var(--text-muted);font-size:11px;line-height:14px;font-variant-numeric:tabular-nums}
.corr .track{position:relative;height:8px;border-radius:999px;background:var(--surface-active)}
.corr .mid{position:absolute;left:50%;top:-4px;bottom:-4px;width:1px;background:var(--border-strong);opacity:.9}
.corr .pin{position:absolute;top:50%;width:8px;height:8px;border-radius:50%;transform:translate(-50%,-50%)}
.corr .pin.paid{background:var(--paid)}
.corr .pin.need{background:var(--need-bright)}
.corr .why{margin:8px 0 0;font-size:11px;line-height:14px;color:var(--text-muted)}
.corr .foot{margin:16px 0 0;padding-top:16px;border-top:1px solid var(--border);
  font-size:11px;line-height:16px;color:var(--text-muted)}

/* ---------- teaching touchpoints ---------- */
/* Always visible, never behind a click. Says the single most useful thing for
   whatever is currently on screen, and updates as layers and filters change. */
.btn{line-height:16px;display:inline-flex;align-items:center;gap:4px;height:40px;padding:0 16px;flex:none;
  border:1px solid var(--border-strong);border-radius:var(--radius-sm);background:var(--surface);
  color:var(--text);font:inherit;font-size:12px;font-weight:600;cursor:pointer;
  transition:background var(--t),border-color var(--t)}
.btn:hover{background:var(--surface-hover);border-color:var(--text-muted)}
.btn:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.btn:disabled{opacity:.55;cursor:default}

/* Keyline text starts at 41px like everything inside the cards, and the three
   rows under the toggles (toggles, keyline, explainer) breathe at 16px. */
.keyline{margin:16px 24px 0 41px;font-size:12px;line-height:16px;color:var(--text-2)}
.keyline b{color:var(--text);font-weight:600}
.keyline .num{font-variant-numeric:tabular-nums}

/* Sits directly under the layer toggles, because that is where the two-economies
   confusion actually happens. Closed by default; one line of cost. */
.explain{margin:16px 24px 0;padding:16px;border:1px solid var(--border);
  border-radius:var(--radius);background:var(--surface)}   /* same fill as the cards, so it reads as one of them */
.explain summary,.folklore summary{line-height:16px;display:flex;align-items:center;gap:8px;cursor:pointer;
  list-style:none;padding:0;margin:0;font-size:12px;color:var(--text-2)}
.explain summary::marker,.folklore summary::marker{content:''}
.explain summary::-webkit-details-marker,.folklore summary::-webkit-details-marker{display:none}
.explain summary svg,.folklore summary svg{color:var(--text-muted);flex:none;order:2;
  margin-left:auto;transition:transform var(--t)}
/* Belt and braces on the marker: Chrome keeps an inline-start inset on
   <summary> through several of these individually. */
.explain summary,.folklore summary{display:flex!important;padding-inline-start:0!important;
  text-indent:0;list-style-type:none;list-style-position:inside}
.explain details,.folklore details{padding:0;margin-inline:0}
.explain[open] summary svg,.folklore[open] summary svg,
.folklore details[open] summary svg{transform:rotate(180deg)}
.explain p{margin:8px 0 0;font-size:12px;line-height:16px;color:var(--text-muted)}
.explain em{color:var(--text-2);font-style:italic}

.findings-open{line-height:16px;text-decoration:underline;text-underline-offset:2px;white-space:nowrap;
  margin-left:8px;font-size:11px;color:var(--text-2)}
.findings{width:min(680px,calc(100vw - 48px));max-height:80dvh;overflow-y:auto;padding:24px;
  border:1px solid var(--border-strong);border-radius:var(--radius);background:var(--surface);
  color:var(--text);box-shadow:var(--shadow)}
.findings::backdrop{background:rgba(6,4,5,.72)}
.findings .fh{display:flex;align-items:baseline;justify-content:space-between;gap:16px;margin-bottom:16px}
.findings h2{margin:0;font-family:var(--display);font-size:28px;line-height:32px;font-weight:600}
.findings ol{margin:0;padding-left:24px}
.findings li{margin-bottom:16px;font-size:13px;line-height:18px;color:var(--text-2)}
.findings li b{color:var(--text);font-weight:600}
.findings .src{margin:16px 0 0;padding-top:16px;border-top:1px solid var(--border);
  font-size:11px;line-height:16px;color:var(--text-muted)}

/* ---------- blood type ---------- */
.types{padding:24px 24px 0}
.types.overlay-sec{padding:16px}
.types-head{display:flex;align-items:baseline;justify-content:space-between;margin-bottom:8px}
/* The section header sits outside a card but should read as if inside one: same
   1px border + 16px inset, so "Blood type" lines up with "Supply" and "Clear"
   with the card's badge. */
.types > .types-head{padding:0 16px;border:1px solid transparent}
.types-head h2{margin:0;font-family:var(--display);font-size:24px;line-height:28px;
  font-weight:600;letter-spacing:.01em}
.linkish{line-height:16px;background:none;border:0;min-height:40px;min-width:40px;margin:-8px -8px -8px 0;
  padding:8px;font:inherit;font-size:12px;color:var(--text-muted);cursor:pointer;
  border-radius:var(--radius-sm);transition:color var(--t),background var(--t)}
.linkish:hover{background:var(--surface-hover)}
.linkish:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.linkish:hover{color:var(--text-2)}
.chips{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}
/* Interactive: outlined, hoverable, pill-height. Non-interactive data chips below
   are filled, borderless and square-cornered so the two never read alike. */
.chip{height:40px;display:flex;align-items:center;justify-content:center;gap:4px;
  border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--bg);
  color:var(--text-2);font:inherit;font-weight:600;cursor:pointer;
  transition:background var(--t),border-color var(--t),color var(--t)}
.chip:hover{background:var(--surface-hover);border-color:var(--border-strong);color:var(--text)}
.chip:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.chip[aria-pressed="true"]{background:var(--need-soft);border-color:rgba(200,16,46,.55);
  color:var(--need-bright);box-shadow:inset 0 0 20px rgba(200,16,46,.16)}
.status{margin-top:16px;padding:16px;border:1px solid var(--border);border-radius:var(--radius);
  background:var(--bg)}
.status .lvl{line-height:18px;display:flex;align-items:center;gap:8px;font-weight:600;font-size:13px;
  margin-bottom:8px}
.status .lvl .dot{width:8px;height:8px}
.status .q{margin:0 0 8px;font-size:12px;line-height:16px;color:var(--text-2)}
.status .q a{color:var(--platelet)}
.status .src{margin:8px 0 0;font-size:11px;line-height:14px;color:var(--text-muted)}
.status .src a{color:var(--platelet)}
.lv-critical,.lv-standing{color:var(--need-bright)} .lv-urgent{color:#E8544A}
.lv-low{color:#E0864B}
.lv-moderate{color:var(--paid-bright)} .lv-healthy{color:#6FBF8F}
.lv-unknown{color:var(--text-muted)}

/* ---------- folklore ---------- */
/* Deliberately gold, never blood red — belief must not borrow the medical
   layer's color and read as data. */
/* One card treatment for every information block: folklore, census overlay and
   neighborhood data are visually identical containers. */
.folklore{margin:8px 24px 0;padding:16px;border:1px solid var(--border);
  border-radius:var(--radius);background:var(--bg)}
.folklore .title{font-family:var(--display);font-size:22px;line-height:28px;
  color:var(--text);margin:0 0 8px}
/* Non-interactive information is TEXT, not a chip. Only things you can press
   get a box: pressable chips are 40px tall, outlined and hoverable. */
.folklore .traits{display:block;margin:0 0 16px;font-size:12px;line-height:16px;color:var(--text-2)}
.folklore .traits b{color:var(--text);font-weight:600}
.folklore .traits .sh{color:var(--text-muted)}
.folklore .traits .sh::before{content:' · '}
.folklore p{margin:0 0 16px;font-size:12px;line-height:16px;color:var(--text-2)}
.folklore .empty{margin:0 0 16px;color:var(--text-muted)}
.folklore .pair{margin:0 0 16px;font-size:12px;line-height:16px;color:var(--text-2)}
.folklore .pair .lbl{color:var(--text-muted)}
.folklore .pair b{color:var(--text);font-weight:600}
.folklore .pair b.fr{color:var(--need-bright)}
.folklore .dist{display:flex;gap:16px;margin-bottom:16px}
.folklore .dist div{line-height:14px;flex:1;font-size:11px;color:var(--text-muted)}
.folklore .dist b{line-height:20px;display:block;font-size:14px;color:var(--paid-bright);font-weight:600}
.folklore ul{margin:8px 0 0;padding-left:16px}
.folklore li{font-size:12px;line-height:16px;color:var(--text-2);margin-bottom:8px}

/* real transfusion facts live with the medical card, never with folklore */
.compat{margin-top:8px;padding:16px;border:1px solid var(--border);border-radius:var(--radius);
  background:var(--bg)}
.compat .r{line-height:16px;display:flex;gap:8px;align-items:baseline;margin-bottom:8px;font-size:12px}
.compat .r:last-child{margin-bottom:0}
.compat .k{color:var(--text-muted);min-width:96px}
.compat .v{color:var(--text)}
.compat .uni{line-height:14px;margin:8px 0 0;font-size:11px;color:var(--need-bright)}
.folklore details{margin-top:16px;padding-top:16px;border-top:1px solid var(--border)}
.folklore summary{display:flex;align-items:center;gap:4px}
.folklore summary svg{color:var(--text-muted);transition:transform var(--t);flex:none}
.folklore details[open] summary svg{transform:rotate(180deg)}
.folklore details p{margin-top:8px}

.overlay-sec{margin:24px 24px 0;padding:16px;border:1px solid var(--border);
  border-radius:var(--radius);background:var(--bg)}
.overlay-sec .types-head{margin-bottom:8px}
.overlay-sec .chips{grid-template-columns:repeat(2,1fr)}
.overlay-chips{grid-template-columns:repeat(2,1fr)}
.overlay-chips .chip{line-height:16px;font-weight:500;font-size:12px}
/* Violet belongs to the census ramp on the map and nowhere else. An active
   overlay chip is outlined in the text color, never filled. */
.overlay-chips .chip[aria-pressed="true"]{background:transparent;border-color:var(--text);
  color:var(--text);box-shadow:none}
.ovnote{margin:8px 0 0;font-size:11px;line-height:14px;color:var(--text-muted)}
.ovnote .badge{margin:0 4px 0 0;display:inline-block;vertical-align:middle}
.ovnote a{color:var(--platelet)}

/* choropleth key — sequential violet, a third hue so it cannot be read as
   need (red) or paid (gold). Low steps sit under 3:1 on the map, so the
   key and the hover readout are the required relief, not decoration. */
.ramp{position:absolute;left:16px;top:16px;z-index:500;background:rgba(18,13,15,.92);
  backdrop-filter:blur(8px);border:1px solid var(--border-strong);border-radius:var(--radius);
  box-shadow:var(--shadow);padding:16px;min-width:200px}
.ramp h4{line-height:14px;margin:0 0 8px;font-size:11px;font-weight:600;color:var(--text-2)}
.ramp .bar{display:flex;height:8px;border-radius:999px;overflow:hidden;margin-bottom:4px}
.ramp .bar span{flex:1}
.ramp .ticks{line-height:14px;display:flex;justify-content:space-between;font-size:10px;color:var(--text-muted)}
.ramp .read{line-height:14px;margin-top:8px;padding-top:8px;border-top:1px solid var(--border);
  font-size:11px;color:var(--text);min-height:16px}
.ramp .read .muted{color:var(--text-muted)}

.search{padding:24px 24px 16px}
.field{position:relative;display:flex;align-items:center}
.field svg{position:absolute;left:16px;color:var(--text-muted);pointer-events:none}
.field input{width:100%;height:40px;padding:0 16px 0 40px;border-radius:var(--radius-sm);
  border:1px solid var(--border-strong);background:var(--bg-deep);color:var(--text);
  font:inherit;transition:border-color var(--t),box-shadow var(--t)}
.field input:focus{outline:none;border-color:var(--need);
  box-shadow:0 0 0 3px rgba(200,16,46,.16)}
.field input::placeholder{color:var(--text-muted)}

/* When the list is sorted by distance this is the thing being sorted on, so it
   should not be the smallest text on the row. */
.item .dist{float:right;margin-left:8px;font-family:var(--font);font-size:14px;
  line-height:20px;font-weight:400;color:var(--text-2);font-variant-numeric:tabular-nums}
.count{margin:0;padding:0;color:var(--text-muted);font-size:11px;line-height:14px;
  letter-spacing:0}
/* Each center is a card built exactly like the cards above it -- 1px border,
   16px inset, same fill -- so its text lands at 41px and its distance ends at
   334px without any per-row arithmetic. The layer shows as a tint washing in
   from the right edge, which is where the eye lands after reading the name. */
.list{padding:0 24px 24px;display:flex;flex-direction:column;gap:8px}
.list[hidden]{display:none}   /* an explicit display beats the hidden attribute otherwise */
.panel{scrollbar-width:thin;scrollbar-color:var(--border-strong) transparent}
.panel::-webkit-scrollbar{width:10px}
.panel::-webkit-scrollbar-thumb{background:var(--border-strong);border-radius:999px;
  border:3px solid var(--surface)}
.item{width:100%;text-align:left;display:block;position:relative;padding:16px;
  border:1px solid var(--border);border-radius:var(--radius);color:inherit;font:inherit;
  cursor:pointer;overflow:hidden;transition:border-color var(--t),background var(--t);
  background:linear-gradient(90deg,var(--surface) 52%,var(--paid-soft))}
.item.need{background:linear-gradient(90deg,var(--surface) 52%,var(--need-soft))}
.item.unknown{background:linear-gradient(90deg,var(--surface) 52%,var(--surface-active))}
.item:hover{border-color:var(--border-strong)}
.item:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
/* The selected card takes its layer's color on the border, the way a pressed
   layer toggle does. */
.item[aria-current="true"]{border-color:rgba(185,143,75,.55)}
.item.need[aria-current="true"]{border-color:rgba(200,16,46,.55)}
.item.unknown[aria-current="true"]{border-color:var(--text-muted)}
.item .bar{display:none}
.item > span:not(.bar){display:block}
.item .nm{display:block;font-family:var(--display);font-size:20px;line-height:24px;
  font-weight:500;letter-spacing:.01em;margin-bottom:4px;color:var(--text)}
.item .ad{display:block;color:var(--text-2);font-size:12px;line-height:16px}
.item .also{line-height:14px;display:inline-block;margin-top:8px;padding:4px 8px;border-radius:var(--radius-sm);
  font-size:11px;font-weight:500;border:0;background:var(--need-soft);color:var(--need-bright)}
.item .also + .also{margin-left:4px}
.item .also.platelet{color:var(--platelet);box-shadow:inset 0 0 0 1px rgba(180,140,255,.45)}
.item .pay.chainwide{background:var(--surface-active);color:var(--text-muted)}
.item .pay{line-height:14px;display:inline-block;margin-top:8px;padding:4px 8px;border-radius:var(--radius-sm);
  font-size:11px;font-weight:600;letter-spacing:.04em;border:0;
  background:var(--paid-soft);color:var(--paid-bright)}

/* ---------- map ---------- */
.map-wrap{position:relative;min-width:0;background:var(--bg-deep);
  overflow:hidden;isolation:isolate}   /* clips the grain overhang; own stacking context */
#map{position:absolute;inset:0;background:var(--bg-deep)}
.maplibregl-canvas{outline:none}
/* Everything floating over the map sits 16px off its two edges and wears the
   same surface: the zoom control, the overlay key and the legend. MapLibre's
   own default is 10px, which is both off the 8px grid and out of step with
   the panels. */
.maplibregl-ctrl{margin:16px!important}
/* Nav controls mirror the legend (floating surface) and the search icon
   (1.5 stroke, muted -> text on hover). Same radius, border and shadow. */
.maplibregl-ctrl-group{background:rgba(18,13,15,.92)!important;backdrop-filter:blur(8px);
  border:1px solid var(--border-strong)!important;border-radius:var(--radius)!important;
  box-shadow:var(--shadow)!important;overflow:hidden}
.maplibregl-ctrl-group button{width:40px!important;height:40px!important;
  background-color:transparent!important;transition:background-color var(--t)}
.maplibregl-ctrl-group button:hover{background-color:var(--surface-hover)!important}
.maplibregl-ctrl-group button:focus-visible{outline:2px solid var(--focus);outline-offset:-2px}
.maplibregl-ctrl-group button+button{border-top:1px solid var(--border)!important}
.maplibregl-ctrl-group button:disabled{opacity:.35}
.maplibregl-ctrl-group .maplibregl-ctrl-icon{background-size:16px 16px;background-position:center}
/* stroke #8A7B72 = --text-muted */
.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%238A7B72' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M8 3.2v9.6M3.2 8h9.6'/%3E%3C/svg%3E")!important}
.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%238A7B72' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M3.2 8h9.6'/%3E%3C/svg%3E")!important}
/* stroke #EDE4DC = --text */
.maplibregl-ctrl-zoom-in:hover .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23EDE4DC' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M8 3.2v9.6M3.2 8h9.6'/%3E%3C/svg%3E")!important}
.maplibregl-ctrl-zoom-out:hover .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23EDE4DC' stroke-width='1.5' stroke-linecap='round'%3E%3Cpath d='M3.2 8h9.6'/%3E%3C/svg%3E")!important}
/* Attribution is one more tile in the same family as the zoom control: a 40px
   square on the same surface, with the same border, radius and shadow, sitting
   16px off its two edges. Collapsed to the info icon until it is clicked. */
.maplibregl-ctrl-attrib.maplibregl-compact{position:relative;box-sizing:border-box;
  display:flex;align-items:center;height:40px;min-height:40px;margin:16px!important;
  padding:0!important;background:rgba(18,13,15,.92)!important;backdrop-filter:blur(8px);
  border:1px solid var(--border-strong)!important;border-radius:var(--radius)!important;
  box-shadow:var(--shadow)!important;overflow:hidden}
/* Collapsed: the button IS the tile, so it fills the padding box exactly and the
   icon centres on the tile rather than on a 40px square offset by the border. */
.maplibregl-ctrl-attrib.maplibregl-compact:not(.maplibregl-compact-show){width:40px}
.maplibregl-ctrl-attrib.maplibregl-compact:not(.maplibregl-compact-show) .maplibregl-ctrl-attrib-button{
  inset:0;width:auto!important;height:auto!important}
/* Expanded: the button becomes the right-hand segment, divided from the text the
   same way the zoom buttons are divided from each other. */
.maplibregl-ctrl-attrib.maplibregl-compact-show{padding:0 38px 0 16px!important}
/* The divider is an inset shadow, not a border: a border would shrink the
   button's padding box by 1px and background-position:center is measured from
   that box, which slid the glyph half a pixel every time it opened. */
.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button{
  right:0;top:0;bottom:0;width:38px!important;height:auto!important;
  border:0!important;box-shadow:inset 1px 0 0 var(--border)!important;border-radius:0!important}
.maplibregl-ctrl-attrib.maplibregl-compact .maplibregl-ctrl-attrib-inner{display:none}
.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-inner{display:block;
  font-size:11px;line-height:14px;white-space:nowrap}
.maplibregl-ctrl-attrib{color:var(--text-muted)!important}
.maplibregl-ctrl-attrib a{color:var(--platelet)!important}
.maplibregl-ctrl-attrib a:hover{color:#C7A8E6!important}
/* The glyph is drawn at the same 16px optical size as the + and the -: their
   strokes span the full box, so the circle needs a near-full radius to match. */
.maplibregl-ctrl-attrib-button{position:absolute!important;
  border:0;outline:none;appearance:none;cursor:pointer;
  background-color:transparent!important;background-repeat:no-repeat!important;
  background-size:16px 16px!important;background-position:center center!important;
  transition:background-color var(--t);
  background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%238A7B72' stroke-width='1.5' stroke-linecap='round'%3E%3Ccircle cx='8' cy='8' r='6.9'/%3E%3Cpath d='M8 7.4v3.8M8 4.9v.5'/%3E%3C/svg%3E")!important}
.maplibregl-ctrl-attrib-button:hover{background-color:var(--surface-hover)!important}
/* MapLibre tints the button while open and the browser paints its own focus
   ring; both were the mess on screen. Match the zoom buttons instead. */
.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button{
  background-color:transparent!important}
.maplibregl-ctrl-attrib.maplibregl-compact-show .maplibregl-ctrl-attrib-button:hover{
  background-color:var(--surface-hover)!important}
.maplibregl-ctrl-attrib-button:focus,.maplibregl-ctrl-attrib-button:active{outline:none!important;box-shadow:none!important}
.maplibregl-ctrl-attrib-button:focus-visible{outline:2px solid var(--focus)!important;outline-offset:-2px!important}

.maplibregl-popup-content{background:var(--surface);color:var(--text);padding:16px 16px 16px;
  border-radius:var(--radius);border:1px solid var(--border-strong);box-shadow:var(--shadow);
  font-size:13px;line-height:18px}
.maplibregl-popup-tip{border-top-color:var(--border-strong)!important;
  border-bottom-color:var(--border-strong)!important}
.maplibregl-popup{z-index:600}   /* above the legend (500) and the vignette (450): an open popup outranks everything */
.maplibregl-popup-close-button{color:var(--text-muted);font-size:22px;line-height:1;width:40px;height:40px;
  line-height:1;top:8px;right:8px;border-radius:var(--radius-sm)}
.maplibregl-popup-close-button:hover{background:transparent;color:var(--text)}
.pop h3{margin:0 0 8px;font-family:var(--display);font-size:22px;line-height:28px;
  font-weight:600;letter-spacing:.01em;padding-right:16px}
.pop .ad{color:var(--text-2);font-size:12px;line-height:16px;margin:0 0 16px}
.pop dl{margin:0;display:grid;grid-template-columns:auto 1fr;gap:8px 16px;align-items:baseline}
.pop dt{line-height:16px;color:var(--text-muted);font-size:11px;letter-spacing:.06em}
.pop dd{line-height:16px;margin:0;font-size:12px}
.pop .why{margin:8px 0 0;font-size:11px;line-height:14px;color:var(--text-muted)}
.pop .tractline{margin-top:8px;font-size:11px;line-height:14px;color:var(--text-2)}
.pop .tractline .hint{color:var(--text-muted)}
.pop .stamp{margin-top:16px;padding-top:16px;border-top:1px solid var(--border);
  color:var(--text-muted);font-size:11px;line-height:16px}
.pop a{color:var(--platelet)}

@media (prefers-reduced-motion:reduce){ .maplibregl-popup{transition:none} }

.legend{line-height:16px;position:absolute;left:16px;bottom:16px;z-index:500;background:rgba(18,13,15,.92);
  backdrop-filter:blur(8px);border:1px solid var(--border-strong);border-radius:var(--radius);
  box-shadow:var(--shadow);padding:16px;display:flex;flex-direction:column;gap:8px;
  font-size:12px}
.legend .row{display:flex;align-items:center;gap:8px;color:var(--text-2)}
.legend .dot.ring{background:transparent;border:1.5px solid var(--text-muted);box-sizing:border-box}
.legend .dot.platelet{background:var(--need-bright);border:1.5px solid var(--platelet);box-shadow:0 0 5px rgba(180,140,255,.45)}
.legend .dot.antid{background:var(--paid);border:1.5px solid var(--antid);box-sizing:border-box;
  box-shadow:0 0 6px 2px rgba(180,140,255,.55)}
.legend .row[hidden]{display:none}   /* display:flex on .row would otherwise defeat the hidden attribute */

/* Full-width footer beneath both the panel and the map. */
.note{grid-column:1 / -1;grid-row:2;padding:16px 41px;max-height:22dvh;overflow-y:auto;border-top:1px solid var(--border);
  background:var(--surface);color:var(--text-muted);font-size:11px;line-height:14px;
  max-width:none;position:relative;z-index:3}
.note a{color:var(--platelet)}

/* ---------- glow atmosphere ---------- */
/* Vignette + arterial wash over the map. Its own element, never intercepts clicks. */
.map-wrap .vignette{position:absolute;inset:0;pointer-events:none;z-index:450;
  background:
    radial-gradient(120% 90% at 50% 45%, transparent 42%, rgba(6,4,5,.55) 88%, rgba(6,4,5,.85) 100%),
    linear-gradient(160deg, rgba(200,16,46,.10), transparent 55%)}

/* ---------- responsive ---------- */
/* The count line carries the list's collapse control: text on the cards' own
   left edge, chevron hard right, matching the card's right edge. */
.countrow{display:flex;align-items:flex-start;justify-content:space-between;gap:8px;position:relative;
  /* 16px below the count text to match the 16px the search field leaves above
     it, whether the list underneath is open or collapsed away. */
  padding:0 41px 16px}
.countrow .count{padding-right:32px}
.countrow>.keytoggle{position:absolute;right:35px;top:-5px}
.keytoggle{display:flex;align-items:center;justify-content:center;gap:4px;flex:none;
  width:24px;height:24px;margin:-4px -6px -4px 0;padding:0;border:0;background:none;
  color:var(--text-muted);font:inherit;font-size:11px;line-height:14px;cursor:pointer;
  border-radius:var(--radius-sm);transition:color var(--t),background var(--t)}
.keytoggle:hover{color:var(--text)}
.keytoggle:focus-visible{outline:2px solid var(--focus);outline-offset:2px}
.keytoggle::after{content:'';width:12px;height:12px;flex:none;opacity:.9;transition:transform var(--t);
  background:currentColor;
  -webkit-mask:var(--chev) center/12px 12px no-repeat;mask:var(--chev) center/12px 12px no-repeat}
[aria-expanded="true"].keytoggle::after{transform:rotate(180deg)}   /* down when collapsed, up when open */
/* A key's header is a full-width row with its label, so the chevron still sits
   right. It reads exactly like a legend row (12px, regular, --text-2) and
   stands 40px tall, so every floating element on the map is at least as big as
   one zoom button in its smaller dimension. */
.keyhead{width:100%;justify-content:space-between;min-height:40px;height:auto;margin:0;
  font-size:12px;line-height:16px;font-weight:400;color:var(--text-2)}
.keyhead::after{opacity:.85}
.keyhead span{white-space:nowrap}
.legend .keyhead,.ramp .keyhead{display:none}   /* desktop: the keys are simply open */
.legend .keybody{display:flex;flex-direction:column;gap:8px}
[data-collapsed="true"] .keybody{display:none}

@media (max-width:900px){
  .app{grid-template-columns:1fr;grid-template-rows:40dvh minmax(0,1fr) auto}
  .panel{grid-column:1;grid-row:2}
  .map-wrap{grid-column:1;grid-row:1}
  .panel{border-right:0;border-bottom:1px solid var(--border);order:2;box-shadow:none}
  .map-wrap{order:1}
  .head{padding:16px}
  .brand h1{font-size:32px;line-height:40px}
  .layers,.search,.types{padding-left:16px;padding-right:16px}
  /* Every panel section shares one gutter here, the way they do on desktop.
     These kept their 24px desktop margins, so the neighborhood card and the
     explainer sat 8px narrower than the search field and the list cards. */
  .explain,.folklore,.corr,.overlay-sec{margin-left:16px;margin-right:16px}
  .keyline{margin-left:33px;margin-right:16px}
  .countrow{padding-left:33px;padding-right:33px}
  .list{padding:0 16px 16px}
  /* On a phone the page scrolls as one, so the footer lands at the very end
     instead of being pinned. The map still holds the top of the screen: it is
     fixed, and its grid row stays 40dvh to reserve the space it no longer
     occupies in flow. */
  html,body{height:auto}
  .app{height:auto;min-height:100dvh;grid-template-rows:40dvh auto auto}
  .map-wrap{position:fixed;top:0;left:0;right:0;height:40dvh;z-index:5}
  .panel{overflow:visible}
  .note{grid-row:3;padding:16px 33px;max-height:none;overflow:visible}
  /* Keys start collapsed here, so they sit on the same 16px inset as everything
     else instead of stretching across a map that is only 40dvh tall. Collapsed,
     each is a 40px tall chip: the same height as a zoom button and the info
     tile it shares the edge with. */
  .legend,.ramp{padding:0 16px;min-height:40px}
  .legend{bottom:16px}
  .ramp{top:16px;min-width:0;max-width:calc(100% - 88px)}
  .legend .keyhead,.ramp .keyhead{display:flex;min-height:38px}
  [data-collapsed="false"] .keybody{padding-bottom:16px}
  .ramp h4{margin-top:0}
}

/* The findings link belongs to the sentence, so it flows as part of it: no
   button box, no 40px hit area inflating the line it sits on. A text link in a
   paragraph is read, and clicked, as text. */
.note .nb{white-space:nowrap}
.note .findings-open{display:inline;min-height:0;min-width:0;margin:0;padding:0;
  vertical-align:baseline;font-size:inherit;line-height:inherit;white-space:normal;
  color:var(--text-2);border-radius:0}
.note .findings-open:hover{color:var(--text)}
.note .findings-open:focus-visible{outline:2px solid var(--focus);outline-offset:2px}



/* ---------- motion ---------- */
/* The keyline narrates the map, so it changes by fading rather than snapping. */
.keyline{transition:opacity 130ms ease-out}
/* The sentence rewrites at different lengths, so the block reserves the tallest
   of them: picking a type must not shift the chips or anything below them. */
.keyline{min-height:var(--keyline-h)}
.linkish.is-off{visibility:hidden;pointer-events:none}
.keyline.swapping{opacity:0}
/* The dots travel, so they need a transition for the reduced-motion path where
   GSAP never runs and the left value is simply set. */
.corr .pin{transition:left 500ms cubic-bezier(.22,1,.36,1)}
/* Cards a blood type reveals arrive rather than appear. */
@keyframes rise{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.status:not([hidden]),.compat-card:not([hidden]){animation:rise 260ms ease-out both}
.compat-card:not([hidden]){animation-delay:60ms}
/* The selected center's ring eases open instead of blinking on. */
@keyframes ringIn{from{opacity:0;transform:scale(.82)}to{opacity:1;transform:none}}
@media (prefers-reduced-motion:reduce){
  .keyline,.corr .pin{transition:none}
  .status:not([hidden]),.compat-card:not([hidden]){animation:none}
}

/* ---------- micro-interactions ---------- */
/* Every control gives a small, immediate answer to being touched. Nothing
   loops, nothing waits: the feedback is the press itself. */
.chip,.layer,.item,.btn,.linkish,.keytoggle{transition:background var(--t),border-color var(--t),
  color var(--t),transform 90ms ease-out,box-shadow var(--t)}
.chip:active,.layer:active,.btn:active,.linkish:active,.keytoggle:active{transform:scale(.97)}
.item:active{transform:scale(.995)}
/* A card lifts a little toward the cursor, so the list feels like objects. */
.item{will-change:transform}
.item:hover{transform:translateY(-1px);box-shadow:0 1px 2px rgba(0,0,0,.5),0 6px 16px rgba(0,0,0,.35)}
/* The track wipes open under its dots; the dots themselves never move. */
.corr .track{will-change:transform}
/* The selected center's ring opens rather than blinking on. */
.item[aria-current="true"]{box-shadow:inset 0 0 0 1px rgba(200,16,46,.18)}
@media (prefers-reduced-motion:reduce){
  .chip,.layer,.item,.btn,.linkish,.keytoggle{transition:color var(--t),background var(--t)}
  .chip:active,.layer:active,.btn:active,.linkish:active,.keytoggle:active,
  .item:active,.item:hover{transform:none}
}
