/* Rare Earth GIS — rareearthgis.com
   A port of the brand kit's homepage design (`Rare Earth GIS Homepage.dc.html`)
   from its Design-Component format to plain HTML/CSS/JS. Layout, type scale,
   spacing, colour and interaction are the kit's; the inline `style=` /
   `style-hover=` / `style-focus=` attributes are lifted into real rules here so
   the page runs without the DC runtime or React.
   Content is NOT the kit's — see index.html and README.md. */

@import url("https://fonts.googleapis.com/css2?family=Archivo:ital,wght@0,400;0,500;0,600;0,700;0,800;1,700;1,800&family=Cinzel:wght@400;500;600;700&family=IBM+Plex+Mono:wght@400;500;600&display=swap");

/* ---------------------------------------------------------------- tokens
   The kit's homepage declares its own short aliases rather than importing
   brand/tokens.css. Kept verbatim so the design matches pixel for pixel. */
:root {
  --ink:   #090910;
  --ink800:#17171E;
  --ink700:#26262D;
  --ink500:#57575F;
  --ink300:#92939C;
  --bone:  #F5F3EE;
  --g300:  #FDE000;
  --g500:  #F1A900;   /* Gold 500 — primary on dark */
  --g600:  #D17E00;
  --g700:  #A75A00;
  --signal:#4FD7D1;   /* data layers only, never a CTA */
  --warn:  #D4614E;
  --line:  rgba(241,169,0,0.22);
  --lineN: #26262D;
  --hdr:   64px;
  --maph:  46svh;   /* pinned map band on stacked (narrow) layouts */
}

* { box-sizing: border-box; }

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

body {
  margin: 0;
  background: var(--ink);
  color: var(--bone);
  font-family: Archivo, "Helvetica Neue", Helvetica, sans-serif;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--g500); text-decoration: none; }
a:hover { color: var(--bone); }
::selection { background: var(--g500); color: var(--ink); }
:focus-visible { outline: 2px solid var(--g500); outline-offset: 2px; }
input { font-family: inherit; font-size: 16px; }  /* 16px: stops iOS auto-zoom */
img { max-width: 100%; }

.mono, .num, .eyebrow, .pill-tag, .src, .readout, .chip-beta,
.stat-l, .cov-k, .foot-m, .scroll-hint {
  font-family: "IBM Plex Mono", ui-monospace, monospace;
}

.skip {
  position: absolute; left: -9999px; top: 0; z-index: 100;
  background: var(--g500); color: var(--ink); padding: 14px 20px;
  font: 600 11px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.16em; text-transform: uppercase;
}
.skip:focus { left: 0; color: var(--ink); }

/* ---------------------------------------------------------------- header */

.hdr {
  position: fixed; inset: 0 0 auto 0; z-index: 80; height: var(--hdr);
  border-bottom: 1px solid var(--line);
  background: rgba(9, 9, 16, 0.88);
  backdrop-filter: blur(16px) saturate(1.4);
  -webkit-backdrop-filter: blur(16px) saturate(1.4);
}
.hdr-in {
  height: 100%; padding: 0 clamp(16px, 2.4vw, 30px);
  display: flex; align-items: center; gap: 22px;
}
.lockup { display: flex; align-items: center; gap: 12px; color: var(--bone); flex: none; }
.lockup img { align-self: center; height: 26px; width: auto; display: block; flex: none; }
.lockup span {
  font-family: Cinzel, Georgia, serif; font-weight: 600; font-size: 13px;
  letter-spacing: 0.17em; text-transform: uppercase; white-space: nowrap;
}
.lockup i { color: var(--g500); font-style: normal; }

.chip-beta {
  font: 500 9px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink300);
  border: 1px solid var(--lineN); padding: 6px 9px; flex: none;
}

.hdr-r { margin-left: auto; display: flex; align-items: center; gap: 18px; }
.readout {
  font: 400 9.5px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink300);
  white-space: nowrap;
}
@media (max-width: 720px) { .readout { display: none; } }

/* At phone widths the lockup + beta chip + CTA overrun the bar and clip the
   button. The mark and the CTA are the two that have to survive. */
@media (max-width: 560px) {
  .hdr-in { gap: 12px; }
  .chip-beta { display: none; }
  .lockup span { font-size: 11px; letter-spacing: 0.12em; }
  .hdr .btn { padding: 12px 14px; font-size: 9.5px; letter-spacing: 0.12em; }
}

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

.btn {
  font: 600 10.5px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.15em; text-transform: uppercase;
  color: var(--ink); background: var(--g500);
  padding: 12px 18px; min-height: 44px;
  display: inline-flex; align-items: center; white-space: nowrap;
  border: 0; cursor: pointer;
}
.btn:hover { color: var(--ink); background: var(--g600); }
.btn-lg { font-size: 11px; letter-spacing: 0.16em; padding: 17px 26px; }
.btn-ghost { color: var(--bone); background: transparent; border: 1px solid var(--line); }
.btn-ghost:hover { color: var(--bone); background: transparent; border-color: var(--g500); }
.btn[aria-disabled="true"] { opacity: 0.55; pointer-events: none; }

/* ---------------------------------------------------------------- layout
   row-reverse so the map is the visual right-hand column while the scrolling
   story stays first in source order for keyboard and screen-reader users. */

.split {
  display: flex; flex-wrap: wrap; flex-direction: row-reverse;
  align-items: flex-start; padding-top: var(--hdr);
}

/* The map is pinned and the story scrolls past it, so it must be opaque and
   above the story — otherwise the text renders straight through the map. */
.mapcol {
  flex: 1 1 100%; min-width: 300px;
  position: sticky; top: var(--hdr); height: var(--maph); align-self: flex-start;
  border-left: 1px solid var(--lineN); border-bottom: 1px solid var(--lineN);
  overflow: hidden; display: flex;
  background: var(--ink); z-index: 5;
}
.mapwrap { flex: 1 1 auto; width: 100%; height: 100%; min-width: 0; position: relative; }
.mapwrap product-map { display: block; width: 100%; height: 100%; }

.storycol { flex: 1 1 100%; min-width: 300px; max-width: 760px; }

/* Under 900px the map stacks above the story and stays pinned, so each step
   pads its content clear of the map band and centres in what is left visible.
   Above 900px the map is a side column and no offset is needed. */
@media (min-width: 900px) {
  .mapcol   { flex-basis: 58%; height: calc(100svh - var(--hdr)); }
  .storycol { flex-basis: 42%; }
}

/* ---------------------------------------------------------------- panel */

.panel {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 6;
  display: flex; flex-direction: column;
  background: rgba(9, 9, 16, 0.93);
  backdrop-filter: blur(10px); -webkit-backdrop-filter: blur(10px);
  border-top: 1px solid var(--line); overflow: hidden;
  max-height: 62%;
}
@media (max-width: 659px) { .panel { display: none; } }

.panel-tabs {
  display: flex; align-items: center; flex: none;
  border-bottom: 1px solid var(--lineN); padding-right: 14px;
}
.ptab {
  flex: 0 0 auto; min-width: 92px; min-height: 44px; cursor: pointer;
  background: transparent; border: 0; border-bottom: 2px solid transparent;
  padding: 12px 8px;
  font: 600 9px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink300);
}
.ptab.is-on { color: var(--g500); border-bottom-color: var(--g500); }

.pmode {
  margin-left: auto;
  font: 500 9px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink300);
}
.pmode.is-manual { color: var(--g500); }
.presume {
  margin-left: 12px; min-height: 44px; cursor: pointer;
  background: transparent; border: 0; padding: 0;
  font: 600 9px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--g500);
}

.panel-body { flex: 1; min-height: 0; overflow-y: auto; }

.pillars {
  padding: 10px 14px;
  display: grid; grid-template-columns: repeat(auto-fit, minmax(196px, 1fr));
  gap: 2px 20px; align-items: center;
}
.pillar {
  width: 100%; min-height: 44px; min-width: 0;
  display: flex; align-items: center; gap: 10px;
  padding: 0; background: transparent; border: 0; text-align: left;
  cursor: default; color: inherit;
}
.pillar.is-live { cursor: pointer; }

.sw {
  width: 34px; height: 18px; flex: none; padding: 2px;
  border: 1px solid var(--ink300); background: transparent;
  display: flex; justify-content: flex-start; align-items: center;
}
.sw i { width: 12px; height: 12px; display: block; background: var(--ink300); }
.pillar.is-on .sw { border-color: var(--g500); background: var(--g500); justify-content: flex-end; }
.pillar.is-on .sw i { background: var(--ink); }

.p-txt { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4px; }
.p-top { display: flex; align-items: baseline; gap: 7px; }
.p-lab {
  font: 600 9px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink300);
}
.pillar.is-active .p-lab, .pillar.is-active .p-w { color: var(--g500); }
.p-w {
  margin-left: auto;
  font: 400 9px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.1em; color: var(--ink300);
}
.p-tag {
  font: 500 9px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.12em; text-transform: uppercase; color: var(--ink300); flex: none;
}
.p-tag.is-warn { color: var(--warn); }
.p-name {
  display: block; font: 400 11px/1.3 Archivo, sans-serif;
  color: var(--ink300); white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.pillar.is-on .p-name { color: var(--bone); }

/* score view */
.score-grid {
  padding: 12px 14px;
  display: grid; grid-template-columns: auto minmax(0, 1fr);
  gap: 10px 24px; align-items: start;
}
.score-name { font-size: 14px; font-weight: 700; letter-spacing: -0.018em; }
.score-sub {
  margin-top: 4px;
  font: 400 9px/1.4 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink300);
}
.score-num { margin-top: 14px; display: flex; align-items: flex-end; gap: 10px; }
.score-val { font-weight: 800; font-size: 38px; line-height: 0.9; letter-spacing: -0.04em; }
.score-val span { color: var(--ink500); font-size: 0.62em; }
.score-lab {
  font: 500 9px/1.4 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--g500); padding-bottom: 4px;
}
.score-note {
  margin: 12px 0 0; max-width: 26ch;
  font: 400 9px/1.6 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.06em; color: var(--ink300);
}
.score-bars { min-width: 0; display: flex; flex-direction: column; gap: 9px; }
.bar-h {
  display: flex;
  font: 400 9px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink300);
}
.bar-h .w { margin-left: 5px; }
.bar-h .v { margin-left: auto; color: var(--g500); }
.bar-t { margin-top: 4px; height: 3px; background: var(--ink700); }
.bar-t i { display: block; height: 100%; background: var(--g500); }
.bar.is-off .bar-h .v { color: var(--warn); }
.bar.is-off .bar-t { background: repeating-linear-gradient(90deg, var(--ink700) 0 4px, transparent 4px 8px); }

/* ---------------------------------------------------------------- steps */

.step {
  min-height: 88svh;
  display: flex; flex-direction: column; justify-content: center;
  padding: clamp(40px, 6vh, 80px) clamp(22px, 3.6vw, 58px);
  border-top: 1px solid var(--lineN);
  scroll-margin-top: var(--hdr);
}
.step-hero { min-height: calc(100svh - var(--hdr)); padding-block: clamp(40px, 7vh, 90px); border-top: 0; }
.step[data-step="8"], .step[data-step="10"] { min-height: 92svh; }

.eyebrow {
  display: flex; align-items: center; gap: 11px; margin: 0 0 24px;
  font: 500 10.5px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.24em; text-transform: uppercase; color: var(--g500);
}
.dia { width: 7px; height: 7px; background: var(--g500); transform: rotate(45deg); display: block; flex: none; }

h1 {
  margin: 0; max-width: 17ch; font-weight: 800;
  font-size: clamp(34px, 3.9vw, 76px); line-height: 0.94;
  letter-spacing: -0.038em; text-wrap: balance;
}
h2 {
  margin: 20px 0 0; max-width: 22ch; font-weight: 800;
  font-size: clamp(26px, 3vw, 46px); line-height: 1;
  letter-spacing: -0.034em; text-wrap: balance;
}
.step[data-step="8"] h2, .step[data-step="10"] h2 {
  max-width: 20ch; font-size: clamp(28px, 3.5vw, 56px); line-height: 0.97; letter-spacing: -0.037em;
}
h1 em, h2 em { font-style: italic; color: var(--g500); }

.lede {
  margin: 28px 0 0; max-width: 46ch;
  font-size: clamp(15px, 1.05vw, 18px); line-height: 1.58;
  color: var(--ink300); text-wrap: pretty;
}
.body {
  margin: 22px 0 0; max-width: 48ch;
  font-size: clamp(14.5px, 1vw, 17px); line-height: 1.6;
  color: var(--ink300); text-wrap: pretty;
}
.body strong { color: var(--bone); font-weight: 600; }
.body em { font-style: italic; color: var(--g500); }

.src {
  margin: 24px 0 0; max-width: 48ch;
  font: 400 10.5px/1.7 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.08em; color: var(--ink300);
}
.src-warn { color: var(--warn); }

.cta-row { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 34px; }

.scroll-hint {
  margin-top: 52px; display: flex; align-items: center; gap: 12px;
  font: 400 9.5px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink300);
}
.scroll-hint .rule { width: 22px; height: 1px; background: var(--ink300); display: block; }

.num {
  font: 600 10px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.22em; color: var(--g500);
}
.pill-head { display: flex; align-items: baseline; gap: 14px; flex-wrap: wrap; }
.pill-tag {
  font: 500 9.5px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink300);
}

.chips { margin-top: 30px; display: flex; flex-wrap: wrap; gap: 8px; }
.chip {
  display: inline-flex; align-items: center; gap: 8px;
  font: 500 9.5px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink300);
  border: 1px solid var(--lineN); padding: 10px 12px;
}
.chip b { width: 7px; height: 7px; border-radius: 50%; display: block; flex: none; }
.chip u { text-decoration: none; color: var(--bone); }

.stat-row {
  margin-top: 30px; padding-top: 22px; border-top: 1px solid var(--lineN);
  display: flex; flex-wrap: wrap; gap: 26px;
}
.stat-n { font-weight: 800; font-size: 30px; letter-spacing: -0.035em; line-height: 1; }
.stat-n span { color: var(--g500); font-size: 0.45em; }
.stat-l {
  margin-top: 8px;
  font: 500 9px/1.4 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.16em; text-transform: uppercase; color: var(--ink300);
}

.cov { margin-top: 30px; display: flex; flex-direction: column; gap: 1px; }
.cov-row { outline: 1px solid var(--lineN); padding: 16px 16px 18px; }
.cov-k {
  font: 600 9px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.18em; text-transform: uppercase; color: var(--ink300);
}
.cov-row.is-on .cov-k { color: var(--g500); }
.cov-row.is-off .cov-k { color: var(--warn); }
.cov-row p { margin: 9px 0 0; font-size: 13.5px; line-height: 1.55; color: var(--ink300); }

/* ---------------------------------------------------------------- form */

#access-form { margin-top: 32px; max-width: 420px; display: flex; flex-direction: column; gap: 20px; }
#access-form label { display: block; }
#access-form label span {
  display: block;
  font: 500 9.5px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink300);
}
#access-form input {
  width: 100%; min-height: 48px; margin-top: 11px; padding: 13px 0;
  background: transparent; border: 0; border-bottom: 1px solid var(--line);
  color: var(--bone); outline: none;
}
#access-form input:focus { border-bottom-color: var(--g500); }
#access-form input[aria-invalid="true"] { border-bottom-color: var(--warn); }
#access-form button { align-self: flex-start; margin-top: 4px; }

.msg {
  margin: 0;
  font: 400 10.5px/1.6 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.08em; color: var(--ink300); min-height: 1em;
}
.msg[data-state="error"] { color: var(--warn); }

.sent { margin-top: 32px; max-width: 420px; border: 1px solid var(--g500); padding: 26px 24px; }
.sent-k {
  display: flex; align-items: center; gap: 10px;
  font: 600 10px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.2em; text-transform: uppercase; color: var(--g500);
}
.sent-k .dia { width: 8px; height: 8px; }
.sent p { margin: 15px 0 0; font-size: 15px; line-height: 1.6; color: var(--ink300); }

/* ---------------------------------------------------------------- footer */

.foot {
  margin-top: 44px; padding-top: 24px; border-top: 1px solid var(--lineN);
  display: flex; flex-wrap: wrap; gap: 14px 30px; align-items: center;
}
.foot-lock { display: flex; align-items: center; gap: 11px; }
.foot-lock img { align-self: center; height: 20px; width: auto; display: block; flex: none; }
.foot-lock span {
  font-family: Cinzel, Georgia, serif; font-weight: 600; font-size: 11px;
  letter-spacing: 0.18em; text-transform: uppercase;
}
.foot-m {
  font: 400 9px/1.6 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink300);
}
.foot-r { margin-left: auto; }
.foot-attr { flex-basis: 100%; letter-spacing: 0.1em; text-transform: none; }

/* ------------------------------------------------------- depth & texture

   #090910 with nothing on it reads as an absence rather than a material. Two
   decorative layers fix that without moving a single content colour:

     body::before   one low gold radial, fixed, weighted to the map side.
     .grain         fractal-noise tile at ~5%, so the ink gains a surface and
                    the large gradients band less.
     .mapcol::after a vignette, so the map has depth and the panel has
                    something to sit against.

   All three are inert to the pointer. The grain is an SVG data: URI because
   `img-src 'self' data:` in _headers already allows exactly that — moving it
   to a file would need no CSP change either, but the tile is 300 bytes and a
   request is not worth it. */

body::before {
  content: ""; position: fixed; inset: 0; z-index: 0; pointer-events: none;
  background:
    radial-gradient(58vw 58vw at 74% 10%, rgba(241, 169, 0, 0.10), transparent 62%),
    radial-gradient(44vw 44vw at 6% 86%, rgba(241, 169, 0, 0.045), transparent 66%);
}

/* Above the header (80) on purpose: grain over the chrome too, or the bar
   reads as a different material from the page. Below .skip (100). */
.grain {
  position: fixed; inset: 0; z-index: 90; pointer-events: none; opacity: 0.05;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.8' numOctaves='4' stitchTiles='stitch'/%3E%3CfeColorMatrix type='saturate' values='0'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)'/%3E%3C/svg%3E");
}

/* The story sits above the fixed glow but stays below the map (z 5), which
   the README's first invariant requires. */
.storycol { position: relative; z-index: 2; }

/* Under .panel (z 6), over .mapwrap (static). */
.mapcol::after {
  content: ""; position: absolute; inset: 0; z-index: 5; pointer-events: none;
  background: radial-gradient(128% 96% at 50% 42%, transparent 48%, rgba(9, 9, 16, 0.38) 100%);
}

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

   <product-map> renders in the light DOM, because MapLibre's stylesheet, its
   controls and its popups all live outside any shadow root. Everything below
   is either layout for the canvas or the app's chrome restated in this page's
   palette — MapLibre ships a light-theme control set that looks wrong on ink. */

product-map .pm-canvas { position: absolute; inset: 0; background: var(--ink); }
.pm-canvas.is-dead { background: var(--ink); }
.maplibregl-canvas { outline: none; }

/* Scroll belongs to the story until the visitor claims the map. */
.pm-hint {
  position: absolute; left: 50%; top: 18px; transform: translateX(-50%); z-index: 4;
  padding: 9px 14px; cursor: pointer;
  border: 1px solid var(--line); background: rgba(8, 9, 14, 0.82);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  color: var(--bone);
  font: 500 9.5px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.16em; text-transform: uppercase;
  transition: opacity 0.3s ease, border-color 0.2s ease;
}
.pm-hint:hover { border-color: var(--g500); }
.pm-hint.is-gone { opacity: 0; pointer-events: none; }

/* Deposit tooltip — the kit's .tip, restated for MapLibre's popup shell. */
.pm-pop .maplibregl-popup-content {
  padding: 8px 11px; border: 1px solid var(--line);
  background: rgba(8, 9, 14, 0.95);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  border-radius: 0; box-shadow: none;
  font: 500 10.5px/1.4 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--bone);
}
.pm-pop .maplibregl-popup-content b { display: block; color: var(--g500); letter-spacing: 0.11em; }
.pm-pop .maplibregl-popup-content u { display: block; text-decoration: none; color: var(--ink300); margin-top: 3px; }
.pm-pop .maplibregl-popup-tip { display: none; }

/* MapLibre's own chrome, in this page's palette. */
.maplibregl-ctrl-group {
  background: rgba(10, 12, 18, 0.72); border: 1px solid var(--lineN); border-radius: 0;
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); box-shadow: none;
}
.maplibregl-ctrl-group button + button { border-top: 1px solid var(--lineN); }
.maplibregl-ctrl-group button .maplibregl-ctrl-icon { filter: invert(1) opacity(0.72); }
.maplibregl-ctrl-group button:hover { background: rgba(241, 169, 0, 0.14); }
.maplibregl-ctrl-scale {
  background: transparent; border: 1px solid var(--lineN); border-top: 0; color: var(--ink300);
  font: 400 9px/1.4 "IBM Plex Mono", ui-monospace, monospace; letter-spacing: 0.12em;
}
/* Natural Earth is gone with the d3 map; Carto and OpenStreetMap now carry the
   licence requirement, and MapLibre renders that itself. It must stay legible. */
.maplibregl-ctrl-attrib {
  background: rgba(8, 9, 14, 0.72) !important;
  font: 400 9px/1.5 "IBM Plex Mono", ui-monospace, monospace; letter-spacing: 0.06em;
}
.maplibregl-ctrl-attrib a { color: var(--ink300); }
.maplibregl-ctrl-attrib a:hover { color: var(--g500); }
.maplibregl-ctrl-attrib-button { filter: invert(1) opacity(0.6); }

@media (max-width: 659px) { .pm-hint { font-size: 9px; padding: 8px 11px; } }

/* ---------------------------------------------------------- scroll depth */

.prog { position: absolute; left: 0; right: 0; top: 100%; height: 2px; pointer-events: none; }
.prog i {
  display: block; height: 100%; width: 100%;
  transform: scaleX(0); transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--g700), var(--g500) 55%, var(--g300));
  box-shadow: 0 0 14px rgba(241, 169, 0, 0.55);
}

/* ---------------------------------------------------------- step focus

   One step is the subject at a time; the rest recede rather than disappear, so
   the page reads as one instrument being aimed somewhere instead of ten
   unrelated slides. Gated on `html:not(.no-js)` — with no script to set
   .is-active, every step would otherwise sit permanently dimmed. */

html:not(.no-js) .step {
  transition: opacity 0.55s cubic-bezier(0.22, 0.61, 0.36, 1),
              transform 0.55s cubic-bezier(0.22, 0.61, 0.36, 1),
              filter 0.55s ease;
}
html:not(.no-js) .step:not(.is-active) {
  opacity: 0.28; transform: translateY(12px); filter: saturate(0.55);
}
/* A step being typed into or tabbed through is never dimmed, whatever the
   scroll position currently says — this is what keeps the access form usable. */
html:not(.no-js) .step:focus-within { opacity: 1; transform: none; filter: none; }

/* Counting figures. Width is pinned in px by main.js before the count starts,
   measured off the real final string, so the heading never reflows mid-count. */
.tick { font-variant-numeric: tabular-nums; }

/* ---------------------------------------------------------- hero entrance */

@keyframes rise { from { opacity: 0; transform: translateY(18px); } to { opacity: 1; transform: none; } }

html:not(.no-js) .step-hero > * { animation: rise 0.9s cubic-bezier(0.22, 0.61, 0.36, 1) backwards; }
html:not(.no-js) .step-hero > .eyebrow     { animation-delay: 0.10s; }
html:not(.no-js) .step-hero > h1           { animation-delay: 0.22s; }
html:not(.no-js) .step-hero > .lede        { animation-delay: 0.38s; }
html:not(.no-js) .step-hero > .cta-row     { animation-delay: 0.52s; }
html:not(.no-js) .step-hero > .scroll-hint { animation-delay: 0.70s; }

@keyframes blink { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }
html:not(.no-js) .scroll-hint .rule { animation: blink 2.6s ease-in-out infinite 1.4s; }

/* At the hero the map is a frame, not an instrument. The panel is held back
   until step 2, where the story first refers to it. Its height is deliberately
   left in place: main.js feeds that height to the atlas as `insetb`, and
   collapsing it here would make the map re-fit the moment the panel returns. */
html:not(.no-js) .panel { transition: opacity 0.5s ease, transform 0.5s ease; }
html.at-hero .panel { opacity: 0; transform: translateY(14px); pointer-events: none; }

/* The hero is the one frame that has to earn the scroll, so it runs a scale
   above the rest of the page. Wide layouts only — stacked, it has the map band
   above it and no room to spare. */
@media (min-width: 900px) {
  .step-hero h1 { font-size: clamp(44px, 4.7vw, 88px); max-width: 15ch; }
  .step-hero .lede { font-size: clamp(16px, 1.15vw, 19px); }
}

/* ------------------------------------------------------- parcel record

   The product's ParcelInfoPanel, restated in this page's palette. Structure,
   section order and field set are the app's (web/src/components/Panels/
   ParcelInfoPanel.tsx); only the chrome is the kit's.

   It docks to the right of the map and pushes the Layers/Score panel's right
   edge in rather than covering it — the app puts its sidebar and its parcel
   panel on opposite sides for the same reason: a record you opened should not
   hide the controls that got you there. */

.pinfo {
  position: absolute; top: 0; right: 0; bottom: 0; z-index: 7;
  width: min(340px, 84%);
  display: flex; flex-direction: column;
  background: rgba(7, 7, 13, 0.96);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  border-left: 1px solid var(--line);
  transform: translateX(100%);
  transition: transform 0.34s cubic-bezier(0.22, 1, 0.36, 1);
}
.pinfo.is-open { transform: none; }
.pinfo[hidden] { display: none; }

.mapcol.has-pinfo .panel { right: min(340px, 84%); }

/* The hint and the cue centre on the map column, so an open record clips
   them against its left edge. Re-centre on the map that is still visible. */
.mapcol.has-pinfo .pm-hint,
.mapcol.has-pinfo .pm-cue { transform: translateX(calc(-50% - min(170px, 42%))); }

.pinfo-head {
  flex: none; display: flex; align-items: flex-start; gap: 10px;
  padding: 14px 14px 12px; border-bottom: 1px solid var(--line);
}
.pinfo-id { min-width: 0; flex: 1; }
.pinfo-apn {
  margin: 0; color: var(--g500);
  font: 700 15px/1.25 "Newsreader", Georgia, serif;
  overflow-wrap: anywhere;
}
.pinfo-loc {
  margin: 3px 0 0; color: var(--ink300);
  font: 400 10.5px/1.4 "IBM Plex Mono", ui-monospace, monospace;
}
.pinfo-x {
  flex: none; width: 28px; height: 28px; cursor: pointer;
  display: flex; align-items: center; justify-content: center;
  background: transparent; border: 1px solid transparent; color: var(--ink300);
  transition: color 0.15s ease, border-color 0.15s ease;
}
.pinfo-x svg { width: 13px; height: 13px; }
.pinfo-x:hover { color: var(--bone); border-color: var(--lineN); }

.pinfo-body { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }

/* Qualified Opportunity Zone — the app leads with this as a pill because it is
   the one field on the record that changes the shape of a deal. */
.pinfo-qoz {
  margin: 0; padding: 10px 14px; border-bottom: 1px solid var(--lineN);
}
.pinfo-qoz span {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 5px 10px; border: 1px solid var(--line); background: rgba(241, 169, 0, 0.1);
  color: var(--g500);
  font: 600 9.5px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.14em; text-transform: uppercase;
}
.pinfo-qoz i { width: 5px; height: 5px; background: var(--g500); border-radius: 50%; }

.pinfo-sec { padding: 11px 14px; border-bottom: 1px solid var(--lineN); }
.pinfo-cap {
  margin: 0 0 7px; color: var(--ink500);
  font: 600 8.5px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.2em; text-transform: uppercase;
}
.pinfo-row {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding: 4px 0; border-bottom: 1px solid rgba(38, 38, 45, 0.55);
}
.pinfo-row:last-child { border-bottom: 0; }
.pinfo-k {
  flex: none; color: var(--ink500);
  font: 500 9px/1.3 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.12em; text-transform: uppercase;
}
.pinfo-v {
  min-width: 0; text-align: right; color: var(--bone);
  font: 400 11.5px/1.4 "Inter", system-ui, sans-serif;
  overflow-wrap: anywhere;
}
.pinfo-v.is-none { color: var(--ink500); }
.pinfo-v em { font-style: normal; color: var(--ink300); font-size: 9.5px; }

/* The score. This is the product's own composite for this parcel — the five
   pillar functions in scoring_engine.py, at the shipped weights. Build is
   drawn muted and labelled because it is not loaded in production, which is
   the same thing this page says at step 05 and on the Score tab. */
.pinfo-score { padding: 12px 14px; border-bottom: 1px solid var(--lineN); }
.pinfo-score-top {
  display: flex; align-items: flex-end; justify-content: space-between; gap: 12px;
  margin-bottom: 10px;
}
.pinfo-score-num {
  color: var(--g500); font: 700 34px/0.9 "Newsreader", Georgia, serif;
  font-variant-numeric: tabular-nums;
}
.pinfo-score-num span { font-size: 14px; color: var(--ink500); }
.pinfo-score-lab {
  text-align: right; color: var(--ink300);
  font: 500 8.5px/1.5 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.14em; text-transform: uppercase;
}
.pinfo-bar { margin-top: 6px; }
.pinfo-bar-h {
  display: flex; align-items: baseline; gap: 8px; margin-bottom: 3px;
  font: 500 9px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink300);
}
.pinfo-bar-h b { flex: 1; font-weight: 500; color: var(--bone); }
.pinfo-bar-h .w { color: var(--ink500); }
.pinfo-bar-h .n { color: var(--g500); font-variant-numeric: tabular-nums; }
.pinfo-bar-t { height: 3px; background: var(--ink800); overflow: hidden; }
.pinfo-bar-t i { display: block; height: 100%; background: var(--g500); }
.pinfo-bar.is-off .pinfo-bar-h b,
.pinfo-bar.is-off .pinfo-bar-h .n { color: var(--ink500); }
.pinfo-bar.is-off .pinfo-bar-t i { background: var(--ink700); }

.pinfo-foot {
  flex: none; padding: 11px 14px 13px; border-top: 1px solid var(--line);
}
.pinfo-prov {
  margin: 0 0 9px; color: var(--ink500);
  font: 400 9px/1.5 "IBM Plex Mono", ui-monospace, monospace;
}
.pinfo-cta {
  display: block; text-align: center; text-decoration: none;
  padding: 10px 12px; background: var(--g500); color: #100C00;
  font: 700 10px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.16em; text-transform: uppercase;
  transition: background 0.15s ease;
}
.pinfo-cta:hover { background: var(--g300); }

/* The cue that the map is now clickable. Both this and .pm-hint want the top
   centre, and until the visitor claims the map both are on screen — so the cue
   drops below the hint for exactly as long as the hint is still there. */
.pm-hint:not(.is-gone) ~ .pm-cue { top: 58px; }
.pm-cue {
  position: absolute; left: 50%; top: 18px; transform: translateX(-50%); z-index: 4;
  padding: 8px 13px; pointer-events: none;
  border: 1px solid var(--line); background: rgba(8, 9, 14, 0.86);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  color: var(--g500);
  font: 600 9px/1 "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: 0.16em; text-transform: uppercase;
  transition: opacity 0.3s ease;
}
.pm-cue[hidden] { display: none; }
.pm-cue.is-gone { opacity: 0; }

/* Narrow: the record takes the whole pinned map band, the way the app's panel
   takes the bottom 70dvh of the screen. The map is still there behind it and
   the close button is the way back. */
@media (max-width: 899px) {
  .pinfo { inset: 0; width: auto; border-left: 0; }
  .mapcol.has-pinfo .panel { right: 0; }
}

/* ------------------------------------------------------- stacked layout
   Must come after the .step rules: the base `.step { padding: ... }`
   shorthand has equal specificity and would otherwise reset padding-top. */
@media (max-width: 899px) {
  /* Content starts just below the pinned map rather than centring in a box
     whose top half the map covers — centring there hides the heading. */
  .step, .step-hero {
    min-height: calc(100svh - var(--hdr));
    justify-content: flex-start;
    padding-top: calc(var(--maph) + 28px);
  }

  /* The hero is the ONE step the pinned map never covers, and it must not pay
     the clearance the others do.

     `.mapcol` is sticky, not fixed, so at scroll 0 it still occupies its own
     height in normal flow directly above the story column. Every later step
     scrolls up underneath it and needs `--maph` of padding to keep its heading
     out from behind the map. The hero starts below the map band already, so
     that same padding is counted a second time and pushed the entire hero —
     eyebrow, headline, lede, both buttons — off the bottom of every phone
     screen (eyebrow at y=706 on a 667px iPhone SE). The first words of the
     page were unreachable without scrolling.

     Must stay AFTER the rule above: equal specificity, so order decides. */
  .step-hero {
    padding-top: clamp(18px, 3vh, 30px);
    /* Share the first screen with the map band instead of demanding a full
       one of its own, which would only add dead space under the buttons. */
    min-height: calc(100svh - var(--hdr) - var(--maph));
  }

  /* Tighter vertical rhythm in the hero only. The desktop spacing is built for
     a full column height; on a phone the hero has roughly 350px between the
     map band and the fold, and the request-access button has to land inside
     it. Everything below the hero keeps the original rhythm. */
  .step-hero .eyebrow   { margin-bottom: 18px; }
  .step-hero .lede      { margin-top: 22px; }
  .step-hero .cta-row   { margin-top: 26px; }
  .step-hero .scroll-hint { margin-top: 28px; }

  .scroll-hint { margin-top: 32px; }
}

/* A short phone gives the hero the least room of anything on the page: the map
   band is a percentage of the viewport, so it shrinks with the screen while the
   copy does not. Give the words a little of it back, and let the two hero
   buttons share one row instead of stacking into 104px. */
@media (max-width: 899px) and (max-height: 740px) {
  :root { --maph: 40svh; }
}
@media (max-width: 430px) {
  .step-hero .btn-lg { padding: 15px 17px; letter-spacing: 0.11em; }
}

/* ---------------------------------------------------------------- motion */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  * { animation-duration: 0.01ms !important; transition-duration: 0.01ms !important; }
  /* Keep the focus cue, drop its travel, and lift the floor so a dimmed step
     is still comfortably readable when nothing is animating it back. */
  html:not(.no-js) .step:not(.is-active) { opacity: 0.55; transform: none; filter: none; }
  html:not(.no-js) .scroll-hint .rule { animation: none; opacity: 1; }
}

/* Decoration must not survive into a forced palette, and a dimmed step in
   forced colours is just an unreadable step. */
@media (forced-colors: active) {
  .grain, .prog, .mapcol::after, body::before { display: none; }
  html:not(.no-js) .step:not(.is-active) { opacity: 1; transform: none; filter: none; }
  html.at-hero .panel { opacity: 1; transform: none; pointer-events: auto; }
}
