/* The public viewer's styling, loaded by index.html after the vendored
   stylesheets so it can restyle them. Carries no city fact: colours and
   layout only. */
:root {
  --paper: #f4eddd;
  --paper-edge: #e7dcc4;
  --ink: #221b12;
  --ink-soft: #5c5142;
  --carmine: #b0403a;
  --carmine-dark: #8d2f2b;
  --olive: #8a7a3f;
  --hairline: rgba(34, 27, 18, .28);
  /* 1 while the panel floats over the map, 0 once it docks to the bottom of
     the screen. The slider clamp reads this to decide whether it has to keep
     the compare handle clear of the panel, so the breakpoint is stated once,
     in the media query below, and never duplicated in script. */
  --panel-overlays-map: 1;
}
* { box-sizing: border-box; margin: 0; }
html, body { height: 100%; overflow: hidden; }
body {
  font-family: "Newsreader", serif;
  color: var(--ink);
  background: #14110c;
}

#compare-wrap { position: fixed; inset: 0; }
#before, #after { position: absolute; inset: 0; }

/* ---------- the panel: an archival index card ---------- */
#panel {
  position: fixed;
  top: 18px; left: 18px;
  width: 320px;
  /* stops short of the bottom so the always-visible sources control below
     is never covered by a full-height panel */
  max-height: calc(100% - 88px);
  display: flex; flex-direction: column;
  background:
    radial-gradient(140% 90% at 12% 0%, rgba(176,64,58,.055), transparent 55%),
    linear-gradient(var(--paper), var(--paper));
  border: 1px solid var(--paper-edge);
  box-shadow: 0 1px 0 rgba(255,255,255,.5) inset, 0 18px 40px -18px rgba(0,0,0,.65);
  z-index: 30;
  animation: settle .7s cubic-bezier(.2,.9,.25,1) both;
}
@keyframes settle { from { opacity: 0; transform: translateY(10px); } }

#panel header {
  position: relative;
  padding: 20px 58px 14px 22px;
  border-bottom: 1px solid var(--hairline);
}
/* collapse / reopen: the panel is hidden with CSS, never unmounted, so the
   era selection, the opacity and the district list survive it */
/* ONE scroll region, at every width. The panel's fixed parts — the masthead,
   the era chips, the controls, the credit line — come to some 640px, and the
   district index is what a short window used to pay for them: it is the only
   elastic child, so it collapsed to its own padding and the reader saw no
   districts at all. The body scrolls instead, so what does not fit is below
   the fold rather than absent, and nothing can overflow the card. */
/* `scrollbar-gutter: stable`, or the era chips lose 15px and re-wrap the
   moment the list starts overflowing — which is most of the time */
#panel-body {
  flex: 1 1 auto; min-height: 0; display: flex; flex-direction: column;
  overflow-y: auto; overscroll-behavior: contain;
  scrollbar-width: thin; scrollbar-gutter: stable;
}
body.panel-collapsed #panel-body { display: none; }
body.panel-collapsed #panel { display: none; }
body.panel-collapsed #panel-reopen { display: flex; }
#panel-toggle, #panel-reopen {
  width: 44px; height: 44px;          /* the touch-target floor */
  align-items: center; justify-content: center;
  font: 600 20px/1 "Fraunces", serif; cursor: pointer;
}
#panel-toggle {
  position: absolute; top: 12px; right: 10px; display: flex;
  background: transparent; border: 1px solid transparent; color: var(--ink-soft);
}
#panel-toggle:hover { color: var(--carmine-dark); border-color: var(--hairline); }
#panel-reopen {
  position: fixed; top: 18px; left: 18px; z-index: 31; display: none;
  background: var(--paper); border: 1px solid var(--paper-edge); color: var(--ink);
  box-shadow: 0 10px 24px -12px rgba(0,0,0,.8);
}
/* One focus ring for every control on the page, stated once. `:focus-visible`
   so a mouse click never paints one, and a second colour for the controls
   that sit on the dark map, where carmine on near-black does not read. */
a:focus-visible, button:focus-visible, input:focus-visible, [tabindex]:focus-visible {
  outline: 2px solid var(--carmine); outline-offset: 2px;
}
#attrib-toggle:focus-visible, #attrib-text a:focus-visible { outline-color: #f0c9b2; }
/* clear of the thumb, which overhangs the 2px track it is drawn on — the
   small-screen block below grows both and has to restate this */
input[type=range]:focus-visible { outline-offset: 7px; }
.kicker {
  font-size: 11px; letter-spacing: .24em; text-transform: uppercase;
  color: var(--carmine-dark); font-weight: 500;
}
h1 {
  font-family: "Fraunces", serif;
  font-weight: 900; font-size: 34px; line-height: 1.02;
  letter-spacing: -.01em; margin-top: 6px;
}
h1 .now { font-style: italic; font-weight: 400; }
.dek {
  margin-top: 8px; font-size: 14px; line-height: 1.45; color: var(--ink-soft);
}

/* era row */
/* `flex: 0 0 auto` on every region of the panel, the district list included:
   nothing squashes, the body scrolls */
#panel header, #eras, #controls, #colophon, #panel footer { flex: 0 0 auto; }
#eras {
  display: flex; flex-wrap: wrap; gap: 6px; padding: 12px 22px;
  border-bottom: 1px solid var(--hairline);
}
.era {
  font-family: "Fraunces", serif; font-size: 13px; font-weight: 600;
  padding: 5px 12px 6px; border: 1px solid var(--hairline);
  border-radius: 999px; background: transparent; color: var(--ink-soft);
  cursor: pointer; letter-spacing: .02em;
}
.era:hover:not(.active) { border-color: var(--carmine); color: var(--ink); }
.era.active {
  background: var(--carmine); border-color: var(--carmine-dark);
  color: #fdf6ea; box-shadow: 0 2px 6px -2px rgba(141,47,43,.6);
}
.era.soon { opacity: .45; }
.era.soon::after { content: " ·soon"; font-size: 10px; font-weight: 400; }

/* controls */
#controls { padding: 14px 22px; border-bottom: 1px solid var(--hairline); }
.ctl-label {
  display: flex; justify-content: space-between; align-items: baseline;
  font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-soft);
  margin-bottom: 8px;
}
.ctl-label output { font-family: "Fraunces", serif; font-size: 13px; color: var(--ink); letter-spacing: 0; }
input[type=range] {
  width: 100%; appearance: none; height: 2px;
  background: linear-gradient(to right, var(--carmine) var(--fill,100%), rgba(34,27,18,.18) var(--fill,100%));
}
input[type=range]::-webkit-slider-thumb {
  appearance: none; width: 15px; height: 15px; border-radius: 50%;
  background: var(--paper); border: 2px solid var(--carmine-dark);
  box-shadow: 0 1px 3px rgba(0,0,0,.35); cursor: ew-resize;
}
.hint { margin-top: 10px; font-size: 12.5px; font-style: italic; color: var(--ink-soft); }
.hint b { font-style: normal; font-weight: 500; color: var(--carmine-dark); }

/* address search */
#search-row { display: flex; gap: 6px; margin-top: 12px; }
#address {
  flex: 1; min-width: 0;
  font: 500 13.5px/1.2 "Newsreader", serif; color: var(--ink);
  padding: 6px 10px; background: rgba(255,255,255,.45);
  border: 1px solid var(--hairline);
}
#address:focus { border-color: var(--carmine); }
#search-go {
  font-family: "Fraunces", serif; font-weight: 600; font-size: 12.5px;
  padding: 6px 12px; cursor: pointer;
  background: var(--carmine); color: #fdf6ea; border: 1px solid var(--carmine-dark);
}
#search-go:hover { background: var(--carmine-dark); }
#search-note { margin-top: 6px; font-size: 12px; font-style: italic; color: var(--ink-soft); min-height: 15px; }

/* volume list */
#volumes { flex: 0 0 auto; padding: 10px 10px 10px 22px; }
.vol-head {
  font-size: 11px; letter-spacing: .18em; text-transform: uppercase;
  color: var(--ink-soft); margin: 6px 0 8px;
}
.vol-era {
  font-family: "Fraunces", serif; font-weight: 600; font-size: 12px;
  letter-spacing: .08em; color: var(--carmine-dark);
  margin: 12px 0 4px; padding-left: 8px;
}
/* a row of two controls — fly there, and switch that district off */
.vol { display: flex; align-items: stretch; border-left: 2px solid transparent; }
.vol:hover { border-left-color: var(--carmine); background: rgba(176,64,58,.06); }
.vol-go {
  display: flex; align-items: baseline; gap: 8px;
  flex: 1 1 auto; min-width: 0; text-align: left;
  padding: 5px 8px; border: 0; background: transparent;
  font: 500 14px/1.3 "Newsreader", serif; color: var(--ink);
  cursor: pointer;
}
.vol .no {
  font-family: "Fraunces", serif; font-weight: 600; font-size: 12px;
  color: var(--carmine-dark); min-width: 44px;
}
.vol .yr { margin-left: auto; font-size: 12px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }
/* switched off: the row STAYS, dimmed, so the way back is where the way out
   was — a district that vanished from the list could not be switched on again */
.vol.off .vol-go { opacity: .45; }
/* aligned to the top of the row, not its middle: a label that wraps to three
   lines would otherwise leave the switch floating beside the second one */
/* 26px square, which is the pointer-target floor — an 11px glyph on a
   `line-height: 1` box would otherwise be 18px tall */
.vol-eye {
  flex: 0 0 auto; width: 26px; min-height: 26px; align-self: flex-start;
  padding: 4px 0 0; border: 0; background: transparent; cursor: pointer;
  font-size: 11px; line-height: 1; color: var(--carmine-dark);
}
.vol-eye:hover { color: var(--ink); }
.vol.off .vol-eye { color: var(--ink-soft); }
.vol-all {
  display: block; width: 100%; text-align: left; cursor: pointer;
  margin: 2px 0 6px; padding: 6px 8px;
  background: rgba(176,64,58,.07); border: 1px solid var(--paper-edge);
  font: 600 11.5px/1 "Fraunces", serif; color: var(--carmine-dark);
}
.vol-all:hover { border-color: var(--carmine); }

#panel footer {
  padding: 12px 22px 16px; border-top: 1px solid var(--hairline);
  font-size: 11.5px; line-height: 1.5; color: var(--ink-soft);
}
#panel footer a { color: var(--carmine-dark); }

/* Colophon: the way out to the method and to the code, sat directly on the
   credit line so the two read as one block. It takes the hairline and the
   footer gives its own up — two rules 20px apart draw as a mistake. Each link
   is a 32px row rather than a line of text, because at 12.5px the glyph box
   alone is a 15px tap target. */
#colophon {
  display: flex; flex-wrap: wrap; gap: 0 16px;
  padding: 10px 22px 0; border-top: 1px solid var(--hairline);
  font-size: 12.5px;
}
#colophon a {
  display: inline-flex; align-items: center; min-height: 32px;
  color: var(--carmine-dark); font-weight: 600;
}
#colophon + footer { border-top: 0; padding-top: 4px; }

/* ---------- guided stories: built only when a city configures them ------ */
#story-entry { display: none; padding: 12px 22px 4px; flex: 0 0 auto; }
#story-entry.on { display: block; }
.story-open {
  display: block; width: 100%; text-align: left; cursor: pointer;
  padding: 10px 12px; margin-bottom: 6px; min-height: 44px;
  background: rgba(176,64,58,.07); border: 1px solid var(--paper-edge);
  border-left: 3px solid var(--carmine);
  font: 600 14px/1.3 "Fraunces", serif; color: var(--ink);
}
.story-open:hover { background: rgba(176,64,58,.13); }
.story-open .story-dek {
  margin-top: 3px; font: 400 12.5px/1.4 "Newsreader", serif; color: var(--ink-soft);
}
/* the prose scrolls with the panel body, not inside a second region of its own */
#story { display: none; flex: 0 0 auto; padding: 14px 22px 18px; }
#story.on { display: block; }
#story .stop-of {
  font-size: 11px; letter-spacing: .18em; text-transform: uppercase; color: var(--ink-soft);
}
#story h2 {
  font-family: "Fraunces", serif; font-weight: 900; font-size: 21px;
  line-height: 1.1; margin: 6px 0 8px;
}
#story .body { font-size: 14px; line-height: 1.55; }
#story .body p + p { margin-top: 9px; }
#story figure { margin: 12px 0; }
#story figure img { width: 100%; display: block; border: 1px solid var(--paper-edge); }
#story figcaption { margin-top: 4px; font-size: 11.5px; color: var(--ink-soft); }
#story .sources {
  margin-top: 12px; padding-top: 8px; border-top: 1px solid var(--hairline);
  font-size: 11.5px; line-height: 1.6; color: var(--ink-soft);
}
#story .sources a, #story .body a, #story figcaption a { color: var(--carmine-dark); }
/* pinned to the bottom of the scroll region: Back/Next must not sit below
   however much prose a caption carries */
#story-nav {
  display: none; gap: 6px; align-items: center; flex: 0 0 auto;
  padding: 10px 22px; border-top: 1px solid var(--hairline);
  position: sticky; bottom: 0; background: var(--paper);
}
#story-nav.on { display: flex; }
#story-nav button {
  min-height: 44px; padding: 0 14px; cursor: pointer;
  font: 600 12.5px/1 "Fraunces", serif;
  background: transparent; color: var(--ink); border: 1px solid var(--hairline);
}
#story-nav button:hover:not(:disabled) { border-color: var(--carmine); }
#story-nav button:disabled { opacity: .35; cursor: default; }
/* two ids, so the hover rule above (one id, two classes) cannot put a border
   back on the way out of a story */
#story-nav #story-exit { margin-left: auto; border-color: transparent; color: var(--carmine-dark); }
/* reading a story, the panel is the story: the controls that belong to free
   browsing stand down so the prose gets the height */
body.story .hint, body.story #search-row, body.story #search-note,
body.story #copy-link, body.story #panel .dek { display: none; }
#copy-link {
  margin-top: 10px; min-height: 36px; padding: 6px 11px; cursor: pointer;
  font: 600 11.5px/1 "Fraunces", serif;
  background: transparent; color: var(--carmine-dark); border: 1px solid var(--hairline);
}
#copy-link:hover { border-color: var(--carmine); }

/* swipe divider restyle */
.maplibregl-compare { background: var(--paper); width: 2px; }
.maplibregl-compare .compare-swiper-vertical {
  background: var(--paper); border: 2px solid var(--carmine-dark);
  box-shadow: 0 2px 10px rgba(0,0,0,.5);
  /* left and margin both come from the vendored stylesheet and are sized for
     its 60px handle; restate both, or a 44px handle sits beside the divider
     instead of on it and the clamp's edge margin protects the wrong pixels */
  width: 44px; height: 44px; left: -22px; margin: -22px 0 0;
}

/* corner plates */
.plate {
  position: fixed; top: 18px; z-index: 25;
  font-family: "Fraunces", serif; font-weight: 900; font-size: 15px;
  letter-spacing: .06em; padding: 6px 14px 7px;
  background: rgba(20,17,12,.78); backdrop-filter: blur(3px);
  border: 1px solid rgba(244,237,221,.25);
  animation: settle .9s .25s cubic-bezier(.2,.9,.25,1) both;
}
#plate-era { right: calc(50% + 14px); color: #f0c9b2; }
#plate-now  { right: 18px; color: #cfd8cf; }

/* Sources: the same credit line the panel footer carries, mirrored into a
   control that lives outside the collapsible panel. Basemap attribution is
   a licence obligation, so it has to survive every collapse state and
   width — here it is always one tap away. */
#attrib {
  position: fixed; left: 18px; bottom: 18px; z-index: 40;
  display: flex; flex-direction: column-reverse; align-items: flex-start; gap: 6px;
  max-width: min(520px, calc(100% - 36px));
}
#attrib-toggle {
  min-height: 32px; padding: 7px 12px; cursor: pointer;
  background: rgba(20,17,12,.78); backdrop-filter: blur(3px);
  border: 1px solid rgba(244,237,221,.25); color: #efe6d4;
  font: 600 11px/1 "Fraunces", serif; letter-spacing: .16em; text-transform: uppercase;
}
#attrib-toggle:hover { background: rgba(20,17,12,.92); }
#attrib-text {
  padding: 9px 11px; overflow-wrap: anywhere; max-height: 38vh; overflow-y: auto;
  background: rgba(20,17,12,.9); backdrop-filter: blur(3px);
  border: 1px solid rgba(244,237,221,.25); color: #efe6d4;
  font-size: 11.5px; line-height: 1.55;
}
#attrib-text a { color: #f0c9b2; }

/* ---------- short windows: the masthead stands down ---------------------- */
/* Before the district index gets a pixel, the panel spends 256px on the
   masthead — on a 768px-tall laptop that is a third of everything it has. The
   title takes the size the phone layout gives it and the header tightens.
   The STANDFIRST stays: it is the only sentence on the page that says what the
   colours on a Sanborn sheet mean, and `.hint` repeats only its second half.
   Before the width block below, so a screen that is both narrow and short
   keeps the narrow paddings. */
@media (max-height: 820px) {
  h1 { font-size: 24px; }
  #panel header { padding: 14px 58px 10px 22px; }
}

/* ---------- small screens: the panel docks, the map keeps the width ----- */
@media (max-width: 820px) {
  :root { --panel-overlays-map: 0; }

  /* A bottom sheet. The sheet scrolls; the page still never does. */
  #panel {
    top: auto; bottom: 0; left: 0; right: 0; width: auto;
    /* Short of mid-height, so the compare handle — pinned at 50% — clears
       the sheet on any real screen. The floor wins below about 284px of
       viewport height and the handle's lower edge then goes behind the
       sheet; that is the better trade, because without a floor the sheet
       resolves to zero height on a very short window and its own header is
       the only way to reopen it here. */
    max-height: max(120px, calc(50% - 60px));
    border-left: 0; border-right: 0; border-bottom: 0;
  }
  body.panel-collapsed #panel { display: flex; }
  body.panel-collapsed #panel-reopen { display: none; }
  body.panel-collapsed #panel .dek { display: none; }

  #panel header { padding: 12px 58px 10px 16px; }
  #panel-toggle { top: 6px; }
  h1 { font-size: 24px; }
  .dek { font-size: 13px; }

  /* Every chip on screen. A single scrolling row is more compact but slices
     its last chip at the screen edge with nothing to say there is more, so
     the row wraps and the chips shrink far enough that seven eras take two
     lines even at 320px. The touch target stays 44px; a city with many more
     eras grows the block, and the sheet scrolls. */
  #eras { padding: 8px 16px; }
  .era { flex: 0 0 auto; display: inline-flex; align-items: center;
         min-height: 44px; padding: 0 11px; font-size: 12.5px; }

  #controls { padding: 12px 16px; }
  #address, #search-go { min-height: 44px; }
  input[type=range] { height: 4px; }
  input[type=range]::-webkit-slider-thumb { width: 22px; height: 22px; }
  /* a 22px thumb on a 4px track overhangs by 9, so the desktop 7px offset
     would draw the focus ring straight through it */
  input[type=range]:focus-visible { outline-offset: 11px; }
  #volumes { padding: 8px 8px 8px 16px; }
  .vol-go { padding: 9px 8px; }
  /* the touch-target floor, which the desktop row does not need */
  .vol-eye { width: 44px; min-height: 44px; padding: 0; align-self: center; font-size: 13px; }
  /* the touch-target floor again: the links stack rather than sit side by
     side, so two 44px rows cost nothing the panel was using */
  #colophon { padding: 4px 16px 0; gap: 0 14px; }
  #colophon a { min-height: 44px; }
  #panel footer { padding: 10px 16px 14px; }

  /* plates share the top edge without colliding or overrunning it */
  .plate { top: 10px; font-size: 12px; padding: 5px 10px 6px;
           max-width: calc(50% - 14px);
           overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  #plate-era { left: 8px; right: auto; }
  #plate-now { right: 8px; }

  /* the sheet owns the bottom edge, so sources move under the plates */
  #attrib { top: 48px; bottom: auto; left: 8px;
            flex-direction: column; max-width: calc(100% - 16px); }
  #attrib-text { max-height: 30vh; }

  /* a phone sheet has room for the story or for the chrome, not both; the
     header strip still names the era and the chips still switch the decade */
  /* NOT while collapsed: the toggle lives in that header, and the reopen
     control is hidden at this width, so hiding it too would strand a reader
     in a story with no way back to the panel */
  body.story:not(.panel-collapsed) #panel header,
  body.story:not(.panel-collapsed) #controls { display: none; }
  #story { padding: 12px 16px 14px; }
  #story-entry { padding: 10px 16px 2px; }
  #story-nav { padding: 8px 16px; box-shadow: 0 -6px 12px -10px rgba(0,0,0,.6); }
}

#loading {
  position: fixed; inset: 0; z-index: 50; display: grid; place-items: center;
  background: #14110c; color: #f4eddd; font-family: "Fraunces", serif;
  transition: opacity .6s; font-size: 18px; letter-spacing: .08em;
}
#loading.done { opacity: 0; pointer-events: none; }
/* The overlay when there is no atlas to reveal: it says why, and lists the
   cities published here so the visitor is not left reading a dead end. */
#loading.choose { grid-auto-flow: row; align-content: center; gap: .8em; text-align: center; }
#loading.choose a { color: #d9a441; }
