/* Corridor waypoint handles, edit mode only. Gold squares on the line,
   smaller hollow ones between them that become new points when dragged. */
.corr-handle {
  width: 14px; height: 14px;
  background: #D8A24A;
  border: 2px solid #fff;
  border-radius: 3px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.45);
  cursor: grab;
}
.corr-handle:active { cursor: grabbing; }
.corr-handle--mid {
  width: 10px; height: 10px;
  background: rgba(216,162,74,0.25);
  border: 2px dashed #D8A24A;
  border-radius: 50%;
}

/* =====================================================================
   FONTS — VENDORED, and the UI face changed for readability
   =====================================================================

   Two changes here, both for the television.

   1. NOTHING IS FETCHED FROM GOOGLE ANY MORE. A boardroom on whatever
      wifi the meeting happens to have, with fonts.googleapis.com slow or
      blocked, was one stalled request away from showing this whole site
      in Times New Roman. The latin subsets are about 90 KB and travel
      with the page.

   2. THE INTERFACE FACE IS NOW INTER, NOT JOST. Jost is a geometric
      sans — beautiful, and the wrong tool at four metres: its letters
      are near-circles, its x-height is small, and a single-storey "a"
      and an "o" stop being different characters at distance. Inter was
      drawn for screens, with a tall x-height and letterforms that stay
      distinct when they get small or far away. Everything a visitor
      actually READS is set in it.

   Cormorant Garamond stays, for the two display headings only — the
   page title and the card title. Those are 40 px and up, where a
   high-contrast serif is legible and carries the brand. It is not used
   for anything small.
   ===================================================================== */
@font-face { font-family: "Inter"; font-weight: 300; font-style: normal; font-display: swap; src: url("../vendor/fonts/inter-latin-300-normal.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-weight: 400; font-style: normal; font-display: swap; src: url("../vendor/fonts/inter-latin-400-normal.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-weight: 500; font-style: normal; font-display: swap; src: url("../vendor/fonts/inter-latin-500-normal.woff2") format("woff2"); }
@font-face { font-family: "Inter"; font-weight: 600; font-style: normal; font-display: swap; src: url("../vendor/fonts/inter-latin-600-normal.woff2") format("woff2"); }
@font-face { font-family: "Cormorant Garamond"; font-weight: 300; font-style: normal; font-display: swap; src: url("../vendor/fonts/cormorant-garamond-latin-300-normal.woff2") format("woff2"); }
@font-face { font-family: "Cormorant Garamond"; font-weight: 400; font-style: normal; font-display: swap; src: url("../vendor/fonts/cormorant-garamond-latin-400-normal.woff2") format("woff2"); }
@font-face { font-family: "Cormorant Garamond"; font-weight: 500; font-style: normal; font-display: swap; src: url("../vendor/fonts/cormorant-garamond-latin-500-normal.woff2") format("woff2"); }

/* =====================================================================
   ALIBAG PROJECT MAP — stylesheet
   =====================================================================
   Sections
     1  Tokens
     2  Reset and page
     3  The map surface (and taming Leaflet's own chrome)
     4  Header
     5  Project rail
     6  Pins
     7  Connectivity lines and landmark chips
     8  The project card
     9  Bottom bar and tools
    10  Editor strip
    11  Small screens and landscape phones
   ===================================================================== */

/* ---------------------------------------------------------------- 1 */
/* Palette carried over from the 360 tour so the two products read as
   one brand — see css/style.css:12.1 of the tour. The map inverts it:
   the tour is ink-on-paper, the map is paper-on-ink. `--gold` is the
   pin colour, and it is the one place the map is allowed to shout. */
:root {
  /* ---------------------------------------------------------------
     ONE DARK DESIGN.

     Deep slate panels on deep slate land, light text, one warm gold
     accent. Everything floating — card, rail, tools, gate, boot — is
     painted from the six --surface tokens and nothing else, so there is
     exactly one place to change the whole interface.
     --------------------------------------------------------------- */

  --ink:        #1E2428;   /* page ground. matches theme.ground so the
                              page never shows a seam behind the canvas */

  --surface:      rgba(26, 32, 36, 0.94);
  --surface-2:    rgba(255, 255, 255, 0.05);
  --surface-3:    rgba(255, 255, 255, 0.10);
  --on-surface:     #E9EEF0;
  --on-surface-dim: #96A2A8;
  --surface-line: rgba(255, 255, 255, 0.13);

  /* Text sitting directly on the map, with no panel behind it. This is
     the headline and the hint, and it is NOT the same as panel text —
     it has the land behind it, not a surface. */
  --on-map:     #EFF3F4;
  --on-map-dim: #A7B3B8;

  --gold:       #D9A441;
  --gold-dim:   rgba(217, 164, 65, 0.55);
  --gold-ghost: rgba(217, 164, 65, 0.13);

  --serif: "Cormorant Garamond", Georgia, serif;
  --sans:  "Inter", "Segoe UI Variable", "Segoe UI", system-ui, Helvetica, Arial, sans-serif;

  --lift: 0 16px 40px rgba(0, 0, 0, 0.45);

  --sl: env(safe-area-inset-left, 0px);
  --sr: env(safe-area-inset-right, 0px);
  --sb: env(safe-area-inset-bottom, 0px);
  --st: env(safe-area-inset-top, 0px);
}

/* If a pale preset is ever chosen in the theme panel, the WHOLE set
   flips — not half of it. A partial flip is what produced light panels
   with light text and a headline nobody could read. */
:root.map-light {
  --ink:        #EFE7D9;
  --surface:      rgba(252, 250, 245, 0.95);
  --surface-2:    rgba(58, 48, 34, 0.055);
  --surface-3:    rgba(58, 48, 34, 0.10);
  --on-surface:     #2C2720;
  --on-surface-dim: #6B6353;
  --surface-line: rgba(58, 48, 34, 0.15);
  --on-map:     #2B2620;
  --on-map-dim: #6A6250;
  --gold:       #8C6A2F;
  --gold-dim:   rgba(140, 106, 47, 0.55);
  --gold-ghost: rgba(140, 106, 47, 0.12);
  --lift: 0 14px 34px rgba(92, 74, 44, 0.16);
}

/* ---------------------------------------------------------------- 2 */
* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--ink);
  color: var(--on-surface);
  font-family: var(--sans);
  font-weight: 300;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;   /* stop the pull-to-refresh fighting a pan */
}

button { font: inherit; color: inherit; cursor: pointer; }

/* Every element below that carries a `display` of its own would otherwise
   OUT-SPECIFY the user-agent's [hidden]{display:none} and stay on screen.
   That is why an empty tourUrl still drew a dead "Explore in 360" button
   and why the editor strip was visible on load. One rule, stated once,
   rather than a per-element hidden class. `.card` is the deliberate
   exception directly below it — it needs to stay laid out to fade. */
.editbar[hidden],
.lcard__media[hidden],
.card__media[hidden],
.card__conn[hidden],
.card__loc[hidden],
.card__note[hidden],
.card__cta[hidden],
.card__facts[hidden],
.theme[hidden],
.tool[hidden] { display: none !important; }

/* ---------------------------------------------------------------- 3 */
/* The map fills the viewport and everything else floats. `fixed` rather
   than `absolute` so the address bar sliding away on a phone does not
   leave a strip of body colour under the tiles. */
#map {
  position: fixed;
  inset: 0;
  z-index: 0;
  background: var(--ink);
  outline: none;
}

.maplibregl-canvas-container canvas { outline: none; }

/* MapLibre's default controls are a white browser-chrome box. Rebuilt to
   match everything else, and moved up so they clear the bottom bar. */
.maplibregl-ctrl-bottom-right { bottom: calc(44px + var(--sb)); }
.maplibregl-ctrl-bottom-left  { bottom: calc(44px + var(--sb)); }

.maplibregl-ctrl-group {
  background: var(--surface) !important;
  border: 1px solid var(--surface-line);
  border-radius: 4px;
  box-shadow: var(--lift) !important;
  backdrop-filter: blur(8px);
  overflow: hidden;
}
.maplibregl-ctrl-group button + button { border-top: 1px solid var(--surface-line); }
.maplibregl-ctrl-group button { width: 32px; height: 32px; background: none; }
.maplibregl-ctrl-group button:hover { background: var(--surface-3) !important; }
.maplibregl-ctrl-group button:not(:disabled) .maplibregl-ctrl-icon {
  /* The control icons ship as dark SVGs meant for a light chrome. One
     filter flips the whole set rather than shipping replacements. */
  filter: invert(1) brightness(0.78);
}
.maplibregl-ctrl button:disabled .maplibregl-ctrl-icon { opacity: 0.25; filter: invert(1); }

/* Attribution is a licence condition of OpenStreetMap. Toned down, never
   removed. */
.maplibregl-ctrl-attrib {
  background: var(--surface) !important;
  color: var(--on-surface-dim) !important;
  font-size: 10px;
  backdrop-filter: blur(6px);
  border-radius: 3px;
}
.maplibregl-ctrl-attrib a { color: var(--on-surface) !important; }
.maplibregl-ctrl-attrib-button { filter: invert(1) brightness(0.8); }

/* The MapLibre wordmark is not a licence condition — the OSM credit
   above is. It is removed to keep the corner to one line. */
.maplibregl-ctrl-logo { display: none !important; }

/* MapLibre positions markers with its own transform on the wrapper, so a
   marker's own element must not carry one of its own or the two fight. */
.maplibregl-marker { will-change: transform; }

/* THE VIGNETTE IS GONE.

   It was three stacked gradients darkening the top, the bottom and the
   corners, designed for white text over a dark basemap. Over pale land
   it did not read as a vignette at all — it read as a huge grey-to-black
   wash lying across the map, which is exactly what it looked like.

   Text on the map is dark now and needs no help to be read. If a future
   palette ever needs it back, a single very soft top gradient is the
   most that should return.
   ------------------------------------------------------------------ */

/* ---------------------------------------------------------------- 4 */
.head {
  position: fixed;
  top: calc(18px + var(--st));
  left: calc(26px + var(--sl));
  z-index: 600;
  max-width: min(46ch, 40vw);
  pointer-events: none;         /* the header must never block a drag */
}
.head * { pointer-events: auto; }

.brand { display: flex; align-items: center; gap: 10px; margin-bottom: clamp(14px, 2vw, 30px); }
.brand__logo { width: clamp(120px, 8.4vw, 200px); display: block; }
.brand__text { line-height: 1.15; }
.brand__top  { font-size: clamp(9px, 0.62vw, 12px); letter-spacing: 0.30em; color: var(--on-map-dim); }
.brand__main { font-family: var(--serif); font-size: clamp(19px, 1.5vw, 30px); letter-spacing: 0.06em; color: var(--on-map); }
.brand__sub  { font-size: clamp(8px, 0.55vw, 11px); letter-spacing: 0.34em; color: var(--on-map-dim); }

.eyebrow {
  margin: 0 0 10px;
  font-size: clamp(9px, 0.64vw, 12px);
  letter-spacing: 0.26em;
  text-transform: uppercase;
  /* --gold-on-map, NOT --gold. This line sits directly on a satellite
     photograph with no panel behind it, so it needs the palette's MAP
     accent, not its panel accent. On a pale scheme those two are
     opposites: cream reads over green fields and disappears on a cream
     card; the brown does the reverse. Falls back to --gold, so every
     dark scheme is unchanged. */
  color: var(--gold-on-map, var(--gold));
}
.title {
  margin: 0 0 12px;
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(30px, 3.4vw, 62px);
  line-height: 1.04;
  white-space: pre-line;        /* the \n in config becomes a real break */
  color: var(--on-map);
}
.desc {
  margin: 0;
  font-size: clamp(12px, 0.82vw, 16px);
  line-height: 1.6;
  color: var(--on-map-dim);
  max-width: 34ch;
}

/* ---------------------------------------------------------------- 4b */
/* THE LANDMARK CARD.

   Opens on the LEFT when a connectivity point is clicked. It is a
   different object from the project card on the right and it looks like
   one on purpose: a photograph, a name, two or three chips and a
   paragraph. No facts table, no coordinates, no buttons.

   The reason is editorial rather than technical. A project is the thing
   being sold and every number on its card is doing work. A landmark is
   why the project is worth something, and the moment it grows a
   coordinate block and an "Open in Maps" button it starts competing with
   the thing it is supposed to support.

   Same plate, border, blur and lift as the project card, so the two read
   as one family across the screen. */
.lcard {
  position: fixed;
  left: calc(26px + var(--sl));
  top: calc(50% + var(--st) / 2);
  transform: translateY(-50%);
  z-index: 700;
  width: min(340px, 32vw);
  max-height: min(76vh, 640px);
  overflow: auto;
  padding: 24px 24px 22px;
  background: var(--surface);
  border: 1px solid var(--surface-line);
  border-radius: 4px;
  box-shadow: var(--lift);
  backdrop-filter: blur(16px);

  /* Kept in flow rather than display:none, same as the project card, so
     it can fade instead of popping. */
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s ease, transform 0.4s ease, visibility 0.4s;
}
.lcard[hidden] { display: block; }
.lcard--on { opacity: 1; visibility: visible; }
.lcard:not(.lcard--on) { transform: translateY(-50%) translateX(-14px); }

.lcard__close {
  position: absolute; top: 8px; right: 10px;
  width: 26px; height: 26px; padding: 0;
  background: none; border: 0;
  font-size: 20px; line-height: 1;
  color: var(--on-surface-dim);
}
.lcard__close:hover { color: var(--on-surface); }

/* THE PICTURE, AND WHAT HAPPENS WHEN THERE IS NOT ONE.

   The plate is painted with a tint and the landmark's own glyph before
   any photograph loads, and it stays that way if none is set. That is
   the whole reason a missing image is not a hole: an empty box with a
   broken-image icon says the site is broken, and a tinted plate with a
   bridge mark on it says this is a bridge. js/map.js sets the background
   image only when the config actually has one. */
.lcard__media {
  height: 0; padding-top: 56.25%;      /* 16:9 */
  margin: -24px -24px 18px;
  position: relative;
  background: var(--surface-3);
  background-size: cover;
  background-position: center;
  border-radius: 3px 3px 0 0;
  border-bottom: 1px solid var(--surface-line);
}
.lcard__glyph {
  position: absolute; inset: 0;
  display: flex; align-items: center; justify-content: center;
  color: var(--gold); opacity: 0.55;
}
.lcard__glyph svg { width: 54px; height: 54px; }
/* Once a photograph is in, the glyph goes: it would sit on top of it. */
.lcard__media--photo .lcard__glyph { display: none; }

.lcard__eyebrow {
  margin: 0 0 6px;
  font-size: 9px; letter-spacing: 0.30em; color: var(--gold);
}
.lcard__title {
  margin: 0 0 12px;
  font-family: var(--serif); font-weight: 300;
  font-size: 30px; line-height: 1.06; letter-spacing: 0.01em;
  color: var(--on-surface);
}

/* Chips, not a table. Numbers and dates only: the things a presenter
   points at rather than reads out. */
.lcard__tags { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.lcard__tags:empty { display: none; }
.lcard__tag {
  padding: 3px 9px 2px;
  border-radius: 999px;
  border: 1px solid var(--surface-line);
  background: var(--surface-2);
  font-size: 9px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--on-surface-dim);
  white-space: nowrap;
}
.lcard__desc {
  margin: 0;
  font-size: 12.5px; line-height: 1.62;
  color: var(--on-surface-dim);
}

/* THE MENU STEPS ASIDE. They share the left column, and a landmark card
   over a list of projects is two things shouting at once. It fades
   rather than vanishing, so it is obvious where it went. */
body.lm-open .rail {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}

/* ---------------------------------------------------------------- 4c */
/* SURROUNDINGS, THE MANDWA CLOCK.

   The other developers on this coast, sorted by drive time to the jetty.
   Same panel language as the landmark card, and the same left column, but
   anchored to the BOTTOM rather than the middle: it belongs to a button
   in the bar and it should rise out of it.

   THE HEIGHT MATH, because it bites every time. The panel is inside
   `zoom: var(--ui)`, so every length written here renders at that many
   times its stated size. A ceiling expressed against the viewport must
   therefore be worked out in REAL pixels first and divided by --ui last,
   which is what the calc below does.

   THE HEADLINE IS THE OTHER END OF IT. The first version capped the
   panel against the viewport alone, and it grew to 791 pixels on a
   laptop: straight up through "Our Land, On The Map". --head-bottom is
   measured by syncHeadRoom() in js/map.js for exactly this reason and
   the project index already leans on it. 96px is the bar plus the gap
   above and below it. */
.surr {
  position: fixed;
  left: calc(26px + var(--sl));
  bottom: calc(86px + var(--sb));
  z-index: 700;
  width: min(336px, calc(100vw - 52px));
  max-height: max(200px, calc(
    (100vh - var(--head-bottom, 260px) - var(--sb) - 96px) / var(--ui, 1)));
  overflow: auto;
  padding: 18px 20px 14px;
  background: var(--surface);
  border: 1px solid var(--surface-line);
  border-radius: 4px;
  box-shadow: var(--lift);
  backdrop-filter: blur(16px);

  opacity: 0;
  visibility: hidden;
  transition: opacity 0.35s ease, transform 0.35s ease, visibility 0.35s;
}
.surr[hidden] { display: block; }
.surr--on { opacity: 1; visibility: visible; }
.surr:not(.surr--on) { transform: translateY(10px); }

.surr__close {
  position: absolute; top: 8px; right: 10px;
  width: 26px; height: 26px; padding: 0;
  background: none; border: 0;
  font-size: 20px; line-height: 1;
  color: var(--on-surface-dim);
}
.surr__close:hover { color: var(--on-surface); }

.surr__eyebrow {
  margin: 0 0 4px;
  font-size: 9.5px; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--gold);
}
.surr__title {
  margin: 0 0 6px;
  font-family: var(--serif);
  font-weight: 400; font-size: 19.5px; line-height: 1.15;
  color: var(--on-surface);
}
.surr__note {
  margin: 0 0 12px;
  font-size: 11.5px; line-height: 1.55;
  color: var(--on-surface-dim);
}

/* ---- One time band ----
   The time is the heading, which is the whole point of the slide: the
   band is what the market prices, the projects inside it are examples. */
.surr__band { margin-bottom: 11px; }
.surr__band:last-child { margin-bottom: 4px; }

.surr__time {
  margin: 0 0 7px;
  padding-bottom: 5px;
  border-bottom: 1px solid var(--surface-line);
  font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  color: var(--on-surface);
}

.surr__item { padding: 5px 0; }
.surr__item + .surr__item { border-top: 1px solid var(--surface-2); }

.surr__name {
  margin: 0;
  font-family: var(--serif);
  font-size: 14.5px; line-height: 1.25;
  font-variant-numeric: lining-nums;
  color: var(--on-surface);
}
/* Developer and village on one line, the way the deck has it. Dimmer
   than the name because the name is what somebody recognises. */
.surr__by {
  margin: 1px 0 0;
  font-size: 11px; line-height: 1.4;
  color: var(--on-surface-dim);
}
.surr__what {
  margin: 1px 0 0;
  font-size: 10.5px; line-height: 1.4;
  color: var(--on-surface-dim);
  opacity: 0.8;
}

/* WHOSE RESEARCH THIS IS, said once at the foot. These are other
   developers' projects and the acreages are the client's figures, not
   ours, so the panel says so rather than presenting them as measured. */
.surr__src {
  margin: 12px 0 0;
  padding-top: 9px;
  border-top: 1px solid var(--surface-line);
  font-size: 9.5px; line-height: 1.45;
  color: var(--on-surface-dim);
  opacity: 0.75;
}

/* Same bargain as the landmark card: they share the left column, so the
   index steps aside rather than being covered up. */
body.surr-open .rail {
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.3s ease;
}

/* ---------------------------------------------------------------- 5 */
/* THE PROJECT INDEX.

   REBUILT 9 SEPTEMBER 2026. The client's note was "it's not looking good",
   and she was right. Worth writing down what was wrong, because it was a
   category error rather than a spacing problem:

     ELEVEN ROUNDED PILLS IS A UI KIT, NOT A BRAND. A stack of fully
     rounded lozenges reads as an app: chips, tags, a contacts list. No
     estate agency at this level presents property that way. Their world
     is thin rules, wide margins, a serif that means something, and one
     restrained metal accent.

     IT WAS SMOTHERING THE MAP. Eleven opaque cream blobs covered a third
     of a satellite photograph the whole site exists to show. The panel
     was competing with the product instead of sitting quietly on it.

     NOTHING HAD RANK. Every row carried identical weight, so the three
     sites with a 360 tour, the ones a presentation actually opens, were
     buried among eight that look the same.

     EIGHT COPIES OF THE SAME PIN ICON carried no information at all.

   So this is an INDEX, the way the contents page of a property brochure
   is an index. One plate, hairline rules between entries, numbers down
   the left, names set in the same Cormorant as the card titles. The
   accent appears twice: on the entry you are looking at, and on the ones
   with a tour. Everywhere else the ink is quiet and the photograph shows.

   Everything it used to do it still does: filter, fold, hover-to-ring,
   arrow keys, and a count. It just stopped shouting. */
.rail {
  position: fixed;
  left: calc(26px + var(--sl));
  /* ANCHORED UNDER THE HEADLINE, NOT TO THE FLOOR.

     It used to hang off the bottom of the screen, which was right when it
     was eleven rows tall and filled the column. With "All" gone it is six,
     and a short panel pinned to the floor left a large hole between the
     headline and itself and looked like something had failed to load.

     Anchored to the top it reads as one editorial column: brand, eyebrow,
     title, paragraph, index. It also stops the panel jumping when the
     filter changes: the top edge is fixed and the bottom moves, which is
     the direction a list is expected to grow.

     Divided by --ui because this element carries the TV zoom and every
     length inside it is multiplied by that factor; --head-bottom is a
     real screen measurement, so it has to be brought into the same
     space. See js/map.js -> syncHeadRoom(). */
  top: calc((var(--head-bottom, 260px) + 34px) / var(--ui, 1));
  z-index: 600;
  display: flex;
  flex-direction: column;
  width: min(272px, calc(100vw - 52px));
  /* See js/map.js -> syncHeadRoom(). --head-bottom is measured, because
     the header is inside this same zoom and its title is sized in vw. */
  max-height: max(220px, calc((100vh - var(--head-bottom, 260px)) / var(--ui, 1) - 112px));
}

/* ---- The head ----
   A rule under it rather than a gap. One hairline does more for this than
   any amount of space, and it is the first thing that says "printed"
   rather than "interface". */
.rail__head {
  display: flex; align-items: baseline;
  gap: 8px;
  padding: 0 2px 8px;
  border-bottom: 1px solid var(--on-map-dim);
  border-bottom-color: color-mix(in srgb, var(--on-map-dim) 35%, transparent);
  margin-bottom: 2px;
}
.rail__label {
  margin: 0; flex: 1 1 auto;
  font-size: 9px; letter-spacing: 0.32em; color: var(--on-map-dim);
}
.rail__count {
  font-size: 9px; letter-spacing: 0.14em;
  color: var(--on-map-dim); opacity: 0.7;
  font-variant-numeric: tabular-nums;
}
.rail__fold {
  flex: 0 0 auto;
  width: 20px; height: 20px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0;
  color: var(--on-map-dim);
  transition: color 0.2s, transform 0.25s;
  align-self: center;
}
.rail__fold svg { width: 14px; height: 14px; display: block; }
.rail__fold:hover { color: var(--on-map); }
.rail.is-folded .rail__fold { transform: rotate(180deg); }
.rail.is-folded .rail__body { display: none; }
.rail.is-folded { max-height: none; }
.rail.is-folded .rail__head { border-bottom: 0; margin-bottom: 0; padding-bottom: 0; }

.rail__body { display: flex; flex-direction: column; min-height: 0; }

/* ---- The filter ----
   Three words with hairlines between them, and the live one underlined in
   the accent. A segmented control in a rounded capsule was the single
   most "web app" object on the screen; this is how a magazine sets a
   contents filter, and it takes a third of the ink. */
.rail__filter {
  display: flex; align-items: center;
  gap: 10px;
  padding: 9px 2px 10px;
}
.rail__f {
  padding: 0 0 3px;
  background: none; border: 0;
  border-bottom: 1px solid transparent;
  font-size: 9px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--on-map-dim);
  transition: color 0.2s, border-color 0.2s;
  white-space: nowrap;
}
.rail__f:hover { color: var(--on-map); }
.rail__f--on {
  color: var(--gold-on-map, var(--gold));
  border-bottom-color: var(--gold-on-map, var(--gold));
}
.rail__fn {
  font-style: normal; font-size: 8px;
  margin-left: 0.3em; opacity: 0.6;
  font-variant-numeric: tabular-nums;
}
.rail__fsep {
  width: 1px; height: 9px;
  background: var(--on-map-dim); opacity: 0.3;
}

/* ---- The list ----
   ONE PLATE, not eleven. The rows live inside it and are separated by
   hairlines, which is the whole difference between a printed index and a
   pile of buttons. */
.rail__list {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  background: var(--surface);
  border: 1px solid var(--surface-line);
  border-radius: 3px;
  backdrop-filter: blur(12px);
  box-shadow: var(--lift);
  scrollbar-width: thin;
  scrollbar-color: var(--surface-3) transparent;
}
.rail__list::-webkit-scrollbar { width: 4px; }
.rail__list::-webkit-scrollbar-track { background: transparent; }
.rail__list::-webkit-scrollbar-thumb { background: var(--surface-3); border-radius: 2px; }

/* A SCROLLBAR IS NOT A SCROLL HINT ON A TELEVISION. Nobody standing in
   front of a four-metre screen reads a 4px thumb. When there is more
   below, the last few pixels fade out. js/map.js adds the class. */
.rail__list--more {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent 100%);
          mask-image: linear-gradient(to bottom, #000 calc(100% - 28px), transparent 100%);
}

/* ---- Section headings ----
   These do the job the status dot used to do, and better. A dot asks you
   to learn a key; a heading just says it. They appear only under "All",
   because under a filter the heading would be the same word three times.
   The per-row dot is gone entirely. */
.rail__group {
  margin: 0;
  padding: 12px 14px 6px;
  font-size: 8.5px; letter-spacing: 0.28em; text-transform: uppercase;
  color: var(--on-surface-dim); opacity: 0.75;
}
.rail__group + .rail__row { border-top: 0; }

/* ---- One entry ----
   Number, name, place. The active one takes an accent rule down its left
   edge, which is why the row carries padding-left rather than a border
   that appears and shifts the text sideways when selected. */
.rail__row {
  position: relative;
  display: flex; align-items: baseline;
  gap: 11px;
  width: 100%;
  /* Roomier than it was. With "All" gone the column holds six entries
     instead of eleven, so the space that used to be spent on rows is
     spent on air between them, which is what actually reads as
     uncluttered. */
  padding: 12px 14px;
  background: none;
  border: 0;
  border-top: 1px solid var(--surface-line);
  text-align: left;
  transition: background 0.22s, padding-left 0.22s;
}
.rail__row:first-child { border-top: 0; }
.rail__row::before {
  content: "";
  position: absolute; left: 0; top: -1px; bottom: 0;
  width: 2px;
  background: var(--gold);
  transform: scaleY(0);
  transform-origin: center;
  transition: transform 0.24s ease;
}
.rail__row:hover { background: var(--surface-2); }
.rail__row--on { background: var(--surface-2); padding-left: 18px; }
.rail__row--on::before { transform: scaleY(1); }

/* The number. Tabular so the column does not jitter between 01 and 11,
   and quiet enough that it reads as an index mark rather than as data. */
.rail__num {
  flex: 0 0 auto;
  min-width: 1.4em;
  font-size: 9px; letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
  color: var(--on-surface-dim);
  opacity: 0.6;
}
.rail__row--on .rail__num { color: var(--gold); opacity: 1; }

.rail__txt { flex: 1 1 auto; min-width: 0; }

/* THE NAME IS SET IN THE SERIF, and this is the change that does most of
   the work. Cormorant is already the voice of the page title and every
   card title; setting the index in it too makes the left column part of
   the same document instead of a control panel bolted to the side. */
.rail__name {
  display: block;
  font-family: var(--serif);
  font-weight: 400;
  font-size: 16.5px; line-height: 1.16;
  letter-spacing: 0.045em;
  /* Cormorant defaults to old-style figures, so "23 BY SAMIRA" came out
     with a two and a three shorter than the capitals beside them. Lining
     figures put them back on the cap height. */
  font-variant-numeric: lining-nums;
  color: var(--on-surface);
  opacity: 0.82;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  transition: opacity 0.2s;
}
.rail__row:hover .rail__name,
.rail__row--on  .rail__name { opacity: 1; }

.rail__meta {
  display: block; margin-top: 2px;
  font-size: 8.5px; letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--on-surface-dim);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* ---- The 360 mark ----
   The only other place the accent appears. Three of eleven have a tour,
   and those three are what a presentation opens, so this is the one piece
   of ranking the index needs. A boxed "360" badge was doing this before
   and looked like a notification count. */
.rail__360 {
  flex: 0 0 auto;
  align-self: center;
  font-family: var(--serif);
  font-size: 12px; letter-spacing: 0.04em;
  color: var(--gold);
  opacity: 0.85;
}

.rail__empty {
  padding: 16px 14px;
  font-size: 10px; letter-spacing: 0.12em;
  color: var(--on-surface-dim);
}

/* ---------------------------------------------------------------- 6 */
/* Pins are plain DOM inside a Leaflet divIcon, so they can be animated
   with CSS instead of being redrawn. */
.pin {
  position: relative;
  width: 34px; height: 46px;
  cursor: pointer;
  transform-origin: 50% 100%;
  transition: transform 0.28s cubic-bezier(.2,.9,.3,1.4);
}
.pin:hover    { transform: scale(1.12); }
.pin--on      { transform: scale(1.16); }
.pin__svg     { width: 34px; height: 46px; display: block; filter: drop-shadow(0 6px 12px rgba(0,0,0,0.55)); }
.pin__body    { fill: var(--gold); }
.pin__hole    { fill: #ffffff; }

/* The halo pulses only while nothing is selected — once the visitor is
   reading a card, a throbbing pin is noise. */
.pin__halo {
  position: absolute; left: 50%; top: 13px;
  width: 34px; height: 34px; margin-left: -17px;
  border-radius: 50%;
  background: var(--gold);
  opacity: 0.30;
  animation: halo 2.6s ease-out infinite;
  pointer-events: none;
}
.pin--on .pin__halo, .pin--quiet .pin__halo { animation: none; opacity: 0; }
@keyframes halo {
  0%   { transform: scale(0.45); opacity: 0.42; }
  70%  { transform: scale(2.1);  opacity: 0;    }
  100% { transform: scale(2.1);  opacity: 0;    }
}

/* The name that rides beside the pin. Yellow, letterspaced, sitting on
   nothing — the same treatment as the reference. */
/* --tag-dy is written by placeTags() when two pins sit close enough for
   their names to collide. Nudging the LABEL rather than the pin keeps the
   pin on its real coordinate, which is the one thing that must not move. */
.pin__tag {
  position: absolute;
  left: 40px; top: 12px;
  white-space: nowrap;
  font-size: 11px;
  letter-spacing: 0.22em;
  color: var(--gold);
  text-shadow: 0 2px 10px rgba(0,0,0,0.85), 0 0 3px rgba(0,0,0,0.9);
  pointer-events: none;
  transform: translateY(var(--tag-dy, 0px));
  transition: transform 0.2s ease;
}
.pin--flip .pin__tag { left: auto; right: 40px; }

/* A hairline from the pin out to a nudged label, so a name that has been
   pushed clear still reads as belonging to its pin. */
.pin__leader {
  position: absolute; left: 30px; top: 20px;
  width: 12px; height: 1px;
  background: var(--gold-dim);
  transform-origin: 0 50%;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.2s;
}
.pin--nudged .pin__leader { opacity: 1; }
.pin--flip .pin__leader { left: auto; right: 30px; transform-origin: 100% 50%; }

/* The accuracy ring and the connectivity lines are no longer DOM — they
   are GL layers defined in js/map.js, because they have to lie ON the
   ground and follow the terrain when the map is tilted, which is the one
   thing that must NOT stay upright. Their colours live there. */

/* Motion is disabled wholesale for visitors who ask for that. */
@media (prefers-reduced-motion: reduce) {
  .pin, .pin__halo, .leaflet-fade-anim .leaflet-tile { animation: none !important; transition: none !important; }
}

/* ---------------------------------------------------------------- 7 */
.landmark {
  display: flex; align-items: center; gap: 6px;
  padding: 4px 10px 4px 7px;
  background: var(--surface);
  border: 1px solid var(--surface-line);
  border-radius: 999px;
  font-size: 10px;
  letter-spacing: 0.13em;
  color: var(--on-surface-dim);
  white-space: nowrap;
  backdrop-filter: blur(6px);
  transition: color 0.25s, border-color 0.25s;
  /* No translate here: the marker wrapper is anchored "center" and owns
     the positioning. A transform on this element would be composed on top
     of MapLibre's and throw the chip half a box off its point. */
}
.landmark__ic { width: 12px; height: 12px; color: var(--sage); flex: 0 0 auto; }
.landmark--on { color: var(--gold); border-color: var(--gold-dim); }
.landmark--on .landmark__ic { color: var(--gold); }

/* ---- THE FERRY ----
   A DOM marker, not a GL symbol, and the reason is in js/map.js next to
   startMotion(): a source that is rewritten every frame took this map
   from 61 frames a second to three. A marker only ever writes a CSS
   transform.

   The hull turns and the message does not, which is why they are two
   elements. Nothing here takes a pointer event: a boat crossing the
   middle of the harbour must never eat a click meant for the water or
   for a pin underneath it. */
.boat {
  display: flex; align-items: center; gap: 9px;
  pointer-events: none;
  /* No transform on this element. The marker wrapper is anchored
     "center" and owns the positioning; a transform here would compose on
     top of MapLibre's and throw the boat off its own position. */
}
.boat__hull {
  flex: 0 0 auto;
  width: 26px; height: 26px;
  display: block;
  transform-origin: 50% 50%;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.45));
}
.boat__hull svg { display: block; }

/* The message. Two lines, the service on top and the crossing time
   under it, because the time is the thing worth reading and it should
   not have to share a line. Dark plate in every scheme: it sits on open
   water, which is the darkest thing on the map, and a cream chip there
   disappears at a glance. */
.boat__msg {
  display: block;
  padding: 5px 9px;
  background: rgba(10, 14, 17, 0.84);
  border: 1px solid rgba(254, 249, 238, 0.28);
  border-radius: 3px;
  backdrop-filter: blur(6px);
  white-space: nowrap;
}
.boat__name {
  display: block;
  font-size: 9px; font-weight: 500;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: rgba(254, 249, 238, 0.72);
}
.boat__time {
  display: block;
  margin-top: 1px;
  font-family: var(--serif);
  font-style: normal;
  font-size: 13px; line-height: 1.15;
  font-variant-numeric: lining-nums;
  color: #FEF9EE;
}

/* ---- THE BRIDGE LIGHT ----
   The same trick as the boat, for the same measured reason: a light made
   out of `line-gradient` and repainted every frame ran the whole map at
   three frames a second. This is a DOM element that slides along the
   span, and the map never repaints for it.

   The bar's width and angle are set per frame in js/map.js, from the two
   ends of the span projected to the screen, so the light stays the same
   fraction of the bridge at every zoom. */
.spark { pointer-events: none; }
.spark__bar {
  position: absolute; top: 0; left: 0;
  display: block;
  height: 4px;
  border-radius: 3px;
  /* Bright in the middle, gone at both ends, so it reads as a light
     travelling rather than a bar sliding. */
  background: linear-gradient(90deg,
    rgba(255, 194, 77, 0) 0%,
    rgba(255, 194, 77, 0.85) 34%,
    rgba(255, 246, 223, 1) 50%,
    rgba(255, 194, 77, 0.85) 66%,
    rgba(255, 194, 77, 0) 100%);
  box-shadow: 0 0 14px 4px rgba(255, 194, 77, 0.38);
  /* The origin is the centre, because the marker is anchored there and
     the bar has to grow both ways from the point it is placed at. */
  transform-origin: 50% 50%;
  will-change: transform, width;
}

/* Someone who has asked their machine for less movement gets a ferry
   parked mid-harbour and a light standing still on the bridge, rather
   than neither: the route still reads, nothing travels. js/map.js makes
   the same call on its side. */
@media (prefers-reduced-motion: reduce) {
  .boat__hull { filter: none; }
  .spark__bar { box-shadow: none; }
}

/* Travelling dashes on the connectivity lines. The dash pattern is set
   in JS (config.connectivity.dash); only the offset animates here, which
   is the cheap half. */

/* Distance pill sitting at the midpoint of each line. */
.conn-tag {
  font-size: 9.5px;
  letter-spacing: 0.14em;
  color: var(--gold);
  background: var(--surface);
  border: 1px solid var(--gold-ghost);
  border-radius: 999px;
  padding: 2px 8px;
  white-space: nowrap;
  backdrop-filter: blur(6px);   /* see the note on .landmark: no transform */
}
.conn-tag--approx { color: var(--on-surface-dim); border-color: var(--line); }

/* The pin cursor changes only in editor mode, so an ordinary visitor is
   never invited to drag something that will not move. */
.pin--draggable { cursor: grab; }
.pin--draggable:active { cursor: grabbing; }

/* ---------------------------------------------------------------- 8 */
.card {
  position: fixed;
  right: calc(26px + var(--sr));
  top: calc(50% + var(--st) / 2);
  transform: translateY(-50%);
  z-index: 700;
  width: min(360px, 34vw);
  max-height: min(78vh, 720px);
  overflow: auto;
  padding: 26px 26px 24px;
  background: var(--surface);
  border: 1px solid var(--surface-line);
  border-radius: 4px;
  box-shadow: var(--lift);
  backdrop-filter: blur(16px);

  /* Same visibility mechanism as the tour's .level — kept in flow so the
     measurements inside it are never taken at 0x0. */
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s ease, transform 0.4s ease, visibility 0.4s;
}
.card[hidden] { display: block; }        /* [hidden] would kill the fade */
.card--on { opacity: 1; visibility: visible; transform: translateY(-50%) translateX(0); }
.card:not(.card--on) { transform: translateY(-50%) translateX(14px); }

.card__close {
  position: absolute; right: 12px; top: 8px;
  background: none; border: 0; font-size: 24px; line-height: 1;
  color: var(--on-surface-dim); padding: 4px 8px;
}
.card__close:hover { color: var(--gold); }

/* THE 360 DOCKS HERE.

   This is not an image any more, it is a reserved hole. When a project
   card opens, js/map.js measures this box and flies the corner 360 card
   into it, so the tour becomes the card's header without ever being
   moved in the DOM — moving an iframe between parents destroys and
   reloads it, and the whole design of this page is that the tour loads
   once and never again.

   16:9 of the card's own width, so it holds its shape at every UI
   scale, and pulled out to the card's edges so it reads as the card's
   top rather than as a picture sitting inside it. */
.card__media {
  height: 0; padding-top: 52%;
  margin: -26px -26px 20px;
  background: #0D1214;
  border-radius: 3px 3px 0 0;
}
/* The eyebrow row: village on the left, status chip on the right.
   `baseline` rather than `center` so the two sit on the same optical
   line despite the chip having padding and a border. */
.card__top {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; margin: 0 0 8px;
}
.card__eyebrow { margin: 0; font-size: 9px; letter-spacing: 0.30em; color: var(--gold); }

/* Ongoing is the filled chip, Completed the outlined one, matching the
   filled dot and hollow ring in the project menu. The two views have to
   agree or the pair of them teaches nothing. */
.card__status {
  flex: 0 0 auto;
  padding: 3px 9px 2px;
  border-radius: 999px;
  border: 1px solid var(--gold-dim);
  background: var(--gold-ghost);
  color: var(--gold);
  font-size: 8.5px; letter-spacing: 0.16em; text-transform: uppercase;
  white-space: nowrap;
}
.card__status--completed {
  background: none;
  color: var(--on-surface-dim);
  border-color: var(--surface-line);
}
.card__title   { margin: 0 0 10px; font-family: var(--serif); font-weight: 400; font-size: 30px; letter-spacing: 0.06em; color: var(--on-surface); }
.card__desc    { margin: 0 0 20px; font-size: 13px; line-height: 1.65; color: var(--on-surface-dim); }

.card__facts { display: grid; grid-template-columns: auto 1fr; gap: 9px 18px; margin-bottom: 20px; }
.card__fkey  { font-size: 10px; letter-spacing: 0.20em; color: var(--on-surface-dim); text-transform: uppercase; align-self: center; }
.card__fval  { font-size: 14px; color: var(--on-surface); }

.card__conn { border-top: 1px solid var(--surface-line); padding-top: 16px; }
.card__connlabel { margin: 0 0 10px; font-size: 9px; letter-spacing: 0.28em; color: var(--on-surface-dim); }
.card__connlist { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.card__connlist li { display: flex; justify-content: space-between; gap: 14px; font-size: 12px; color: var(--on-surface-dim); }
.card__connlist b { font-weight: 400; color: var(--on-surface); }
.card__connlist span { color: var(--gold); white-space: nowrap; text-align: right; }
.card__connlist span.approx { color: var(--muted); }
/* The ferry's speed-boat note, a lighter second line under the time. */
.card__connsub { display: block; margin-top: 2px; font-size: 11px;
  color: var(--on-surface-dim); opacity: 0.85; }

/* The provenance line. Deliberately visible rather than tucked in a
   comment: a pin that is a guess should say so on the page it is on. */
.card__note {
  margin: 16px 0 0; padding: 9px 11px;
  border-left: 2px solid var(--gold-dim);
  background: rgba(245, 197, 24, 0.06);
  font-size: 10.5px; line-height: 1.55; color: var(--on-surface-dim);
}

.card__cta {
  display: block; margin-top: 20px; padding: 13px 18px;
  text-align: center; text-decoration: none;
  border: 1px solid var(--gold-dim); color: var(--gold);
  font-size: 11px; letter-spacing: 0.24em;
  transition: background 0.25s, color 0.25s;
}
.card__cta:hover { background: var(--gold); color: var(--ink); }


/* ---------------------------------------------------------------- 8b */
/* THE COMPARE SHEET.

   Same family as the cards: the palette's surface, a hairline border, the
   lift, and the serif for anything that is a name. Where it differs is
   width, because it is a two-column table and a chart.

   ONE THING IN HERE IGNORES THE PALETTE ON PURPOSE: the chart's plot
   ground. See js/config.js -> compare.plotGround for why. */
.cmp { position: fixed; inset: 0; z-index: 900; }
.cmp[hidden] { display: none; }
.cmp__scrim {
  position: absolute; inset: 0;
  background: rgba(6, 10, 12, 0.55);
  backdrop-filter: blur(3px);
  opacity: 0; transition: opacity 0.32s ease;
}
.cmp--on .cmp__scrim { opacity: 1; }

.cmp__sheet {
  position: absolute;
  left: 50%; top: 50%;
  transform: translate(-50%, -50%) scale(0.985);
  width: min(720px, calc(100vw - 64px));
  max-height: min(84vh, 780px);
  overflow: auto;
  padding: 26px 32px 26px;
  background: var(--surface);
  border: 1px solid var(--surface-line);
  border-radius: 4px;
  box-shadow: var(--lift);
  backdrop-filter: blur(18px);
  opacity: 0;
  transition: opacity 0.32s ease, transform 0.32s ease;
}
.cmp--on .cmp__sheet { opacity: 1; transform: translate(-50%, -50%) scale(1); }

.cmp__close {
  position: absolute; top: 12px; right: 14px;
  width: 28px; height: 28px; padding: 0;
  background: none; border: 0;
  font-size: 22px; line-height: 1;
  color: var(--on-surface-dim);
}
.cmp__close:hover { color: var(--on-surface); }

.cmp__eyebrow {
  margin: 0 0 16px;
  font-size: 9px; letter-spacing: 0.32em;
  color: var(--gold);
}

/* ---- THE GRID ----

   Rebuilt 9 September 2026 against the client's second sketch. The first
   version was a stack of ROWS with the parameter centred in each one,
   which is a different object: you read it across, one line at a time.
   She drew COLUMNS, and asked for the parameters down the middle, which
   means the eye should be able to run down one site's figures on its own
   and then down the other's.

   So it is one grid. The two pickers are its header row rather than a
   separate block floating above it, and a hairline runs down each side of
   the parameter column from the picker to the last figure. Those two
   vertical rules are what make it read as three columns instead of a list
   of rows with something in the middle.

   The parameter column is a FIXED width and the two site columns share
   what is left, so they are always exactly equal to each other. A
   comparison where one side is wider than the other quietly says one
   side matters more. */
.cmp__grid {
  display: grid;
  /* 152px, not 168. "ZIRAD HILLS BY SAMIRA" was being clipped inside its
     picker, and a native <select> gives no ellipsis and no wrap: it just
     cuts. The parameter column gives up the width because its longest
     label is "NAVI MUMBAI AIRPORT" at 8.5px, which needs far less. */
  grid-template-columns: 1fr 152px 1fr;
  align-items: stretch;
}

/* ---- The header row ---- */
.cmp__hd {
  padding: 0 12px 14px;
  border-bottom: 1px solid var(--surface-line);
  display: flex; flex-direction: column; justify-content: flex-end;
  min-width: 0;
}
.cmp__hd--m {
  border-left: 1px solid var(--surface-line);
  border-right: 1px solid var(--surface-line);
  align-items: center; justify-content: flex-end;
  padding-bottom: 20px;
}
.cmp__hdlabel {
  display: block; margin-bottom: 7px;
  font-size: 8.5px; letter-spacing: 0.24em; text-transform: uppercase;
  color: var(--on-surface-dim);
  text-align: center;
}
.cmp__vs {
  font-family: var(--serif); font-style: italic;
  font-size: 15px; color: var(--on-surface-dim);
}

/* A native <select>, restyled. Not a custom listbox: eleven options on a
   television, driven by whoever is holding the remote or the trackpad, is
   exactly the case where the browser's own control is more reliable than
   anything hand-rolled. */
.cmp__select {
  width: 100%;
  padding: 9px 26px 9px 10px;
  background: var(--surface-2);
  border: 1px solid var(--surface-line);
  border-radius: 3px;
  font-family: var(--serif);
  font-size: 14.5px; letter-spacing: 0.02em;
  font-variant-numeric: lining-nums;
  color: var(--on-surface);
  cursor: pointer;
  text-align: center;
  /* The native arrow is a different grey on every platform, so it is
     replaced with one drawn in the palette's own ink. */
  -webkit-appearance: none; appearance: none;
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: calc(100% - 13px) 55%, calc(100% - 8px) 55%;
  background-size: 5px 5px, 5px 5px;
  background-repeat: no-repeat;
}
.cmp__select:focus { outline: 1px solid var(--gold-dim); outline-offset: 1px; }

/* ---- The cells ----
   Every cell is a direct child of the grid, so the three columns line up
   by construction rather than by three separate elements agreeing on a
   width. The vertical rules live on the middle cell of each row and
   therefore never break. */
.cmp__c {
  padding: 10px 16px;
  border-bottom: 1px solid var(--surface-line);
  min-width: 0;
}
.cmp__c--val {
  text-align: center;
  font-size: 14.5px; letter-spacing: 0.02em;
  color: var(--on-surface);
  font-variant-numeric: tabular-nums;
}
/* A value nobody has supplied is a dash, not a blank and not a zero. */
.cmp__c--none { color: var(--on-surface-dim); opacity: 0.45; }
.cmp__c--key {
  border-left: 1px solid var(--surface-line);
  border-right: 1px solid var(--surface-line);
  text-align: center;
  font-size: 8.5px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--on-surface-dim);
  display: flex; align-items: center; justify-content: center;
}

/* A section heading runs across all three columns, so the rules stop and
   restart under it. That break is what tells you a new group of
   parameters has begun without adding another line of type. */
.cmp__grp {
  grid-column: 1 / -1;
  margin: 0;
  padding: 16px 16px 6px;
  border-bottom: 1px solid var(--surface-line);
  font-size: 8.5px; letter-spacing: 0.26em; text-transform: uppercase;
  color: var(--on-surface-dim); opacity: 0.8;
}

/* ---- The chart ---- */
.cmp__chart { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--surface-line); }
.cmp__chart[hidden] { display: none; }
.cmp__charttitle {
  margin: 0 0 3px;
  font-size: 9px; letter-spacing: 0.26em; text-transform: uppercase;
  color: var(--on-surface-dim);
}
.cmp__chartnote {
  margin: 0 0 14px;
  font-size: 10.5px; color: var(--on-surface-dim); opacity: 0.8;
}
.cmp__plot {
  background: #FCFCFB;          /* see compare.plotGround in js/config.js */
  border: 1px solid rgba(58, 42, 28, 0.14);
  border-radius: 3px;
  padding: 12px 14px 6px;
}
.cmp__plot svg { display: block; width: 100%; height: auto; }
.cmp__legend {
  display: flex; flex-wrap: wrap; gap: 18px;
  margin-top: 12px;
}
.cmp__lg { display: flex; align-items: center; gap: 7px; font-size: 11px; color: var(--on-surface-dim); }
/* The swatch carries the identity; the text stays in the panel's own ink,
   never in the series colour. */
.cmp__sw { width: 14px; height: 2px; border-radius: 1px; flex: 0 0 auto; }

.cmp__missing {
  margin: 18px 0 0;
  padding: 12px 14px;
  background: var(--surface-2);
  border-left: 2px solid var(--gold-dim);
  border-radius: 2px;
  font-size: 11px; line-height: 1.6;
  color: var(--on-surface-dim);
}
.cmp__missing[hidden] { display: none; }

/* An action rather than a filter, so it sits apart from the three that
   are and takes the accent. */
.rail__f--act {
  margin-left: auto;
  color: var(--gold-on-map, var(--gold));
  opacity: 0.9;
}
.rail__f--act:hover { opacity: 1; border-bottom-color: currentColor; }

@media (max-width: 720px) {
  .cmp__sheet { padding: 24px 18px; }
  .cmp__picks { grid-template-columns: 1fr; gap: 8px; }
  .cmp__vs { display: none; }
  /* Below this the three-column table stops working: the label column
     alone eats a third of a phone. Values stack under their own name. */
  .cmp__row { grid-template-columns: 1fr 1fr; }
  .cmp__key { grid-column: 1 / -1; min-width: 0; text-align: left; margin-bottom: 2px; }
  .cmp__val--a { text-align: left; }
}

/* ---------------------------------------------------------------- 9 */
.bar {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 600;
  display: flex; align-items: center; justify-content: space-between;
  gap: 16px;
  padding: 12px calc(26px + var(--sr)) calc(12px + var(--sb)) calc(26px + var(--sl));
  pointer-events: none;
}
.bar * { pointer-events: auto; }
.bar__hint { margin: 0; font-size: 12px; color: var(--on-map-dim); letter-spacing: 0.06em; }
.bar__tools { display: flex; gap: 8px; }

.tool {
  padding: 8px 15px;
  background: var(--surface);
  border: 1px solid var(--surface-line);
  border-radius: 999px;
  font-size: 10.5px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--on-surface-dim);
  backdrop-filter: blur(10px);
  transition: color 0.25s, border-color 0.25s, background 0.25s;
}
/* Layered, not swapped — same reason as .rail__btn--on above. */
.tool:hover {
  color: var(--on-surface);
  background: linear-gradient(var(--surface-3), var(--surface-3)), var(--surface);
}
.tool[aria-pressed="true"] { color: var(--gold); border-color: var(--gold-dim); }

/* ---- Location block on the card ---- */
.card__loc { border-top: 1px solid var(--surface-line); padding-top: 16px; margin-top: 16px; }

/* The coordinate is a button, not text, because the useful thing to do
   with a coordinate is take it somewhere else. */
.card__coord {
  display: block; width: 100%; text-align: left;
  padding: 8px 11px; margin-bottom: 9px;
  background: var(--surface-2);
  border: 1px solid var(--surface-line); border-radius: 3px;
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
  font-size: 11.5px; letter-spacing: 0.03em; color: var(--on-surface);
  transition: border-color 0.2s, color 0.2s;
}
.card__coord:hover { border-color: var(--gold-dim); color: var(--gold); }
.card__coord::after { content: " ⧉"; opacity: 0.5; }
.card__coord--copied { color: var(--gold); border-color: var(--gold-dim); }
.card__coord--copied::after { content: " copied"; font-size: 9.5px; letter-spacing: 0.14em; }

.card__locbtns { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.card__lbtn {
  padding: 9px 10px; text-align: center; text-decoration: none;
  border: 1px solid var(--surface-line); border-radius: 3px;
  font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--on-surface-dim); transition: color 0.2s, border-color 0.2s, background 0.2s;
}
.card__lbtn:hover { color: var(--gold); border-color: var(--gold-dim); background: var(--gold-ghost); }

/* --------------------------------------------------------------- 10 */
/* THEME PANEL
   A design tool that happens to live in the product. It sits on the LEFT
   so it never covers the project card on the right — the two are open at
   the same time constantly while picking colours, and a panel that hides
   the thing you are colouring is useless. */
.theme {
  position: fixed;
  left: calc(26px + var(--sl));
  top: calc(18px + var(--st));
  bottom: calc(70px + var(--sb));
  z-index: 800;
  width: 292px;
  display: flex; flex-direction: column;
  padding: 0 0 14px;
  background: var(--surface);
  border: 1px solid var(--surface-line);
  border-radius: 5px;
  box-shadow: var(--lift);
  backdrop-filter: blur(18px);
  overflow: hidden auto;
}

.theme__head {
  position: sticky; top: 0; z-index: 2;
  display: flex; align-items: center; justify-content: space-between;
  padding: 13px 14px 11px;
  background: inherit;
  border-bottom: 1px solid var(--surface-line);
}
.theme__title { font-size: 10px; letter-spacing: 0.30em; color: var(--gold); }
.theme__close { background: none; border: 0; font-size: 20px; line-height: 1; color: var(--on-surface-dim); padding: 0 4px; }
.theme__close:hover { color: var(--gold); }

.theme__presets { display: flex; flex-wrap: wrap; gap: 6px; padding: 12px 14px 4px; }
.theme__preset {
  padding: 5px 11px;
  background: var(--surface-2);
  border: 1px solid var(--surface-line); border-radius: 999px;
  font-size: 10px; letter-spacing: 0.12em; color: var(--on-surface-dim);
  transition: color 0.2s, border-color 0.2s;
}
.theme__preset:hover { color: var(--gold); border-color: var(--gold-dim); }

.theme__section { padding: 12px 14px 0; }
.theme__label { margin: 0 0 9px; font-size: 9px; letter-spacing: 0.28em; color: var(--on-surface-dim); }

.theme__grid { display: grid; grid-template-columns: 1fr 1fr; gap: 7px 10px; }

.theme__row { display: flex; align-items: center; gap: 8px; min-width: 0; }
.theme__swatch {
  /* A native colour input, stripped of its chrome so it reads as a
     swatch. -webkit-color-swatch-wrapper is the only way to kill the
     inset padding Chrome adds. */
  appearance: none; -webkit-appearance: none;
  width: 22px; height: 22px; flex: 0 0 auto;
  padding: 0; border: 1px solid var(--surface-line); border-radius: 3px;
  background: none; cursor: pointer;
}
.theme__swatch::-webkit-color-swatch-wrapper { padding: 0; }
.theme__swatch::-webkit-color-swatch { border: 0; border-radius: 2px; }
.theme__swatch::-moz-color-swatch { border: 0; border-radius: 2px; }
.theme__name {
  font-size: 10.5px; color: var(--on-surface-dim); letter-spacing: 0.04em;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.theme__sliders { display: flex; flex-direction: column; gap: 11px; }
.theme__srow { display: grid; grid-template-columns: 1fr auto; gap: 3px 8px; }
.theme__sname { font-size: 10.5px; color: var(--on-surface-dim); }
.theme__sval  { font-size: 10.5px; color: var(--gold); font-variant-numeric: tabular-nums; }
.theme__range {
  grid-column: 1 / -1;
  appearance: none; -webkit-appearance: none;
  width: 100%; height: 3px; padding: 0;
  background: rgba(255,255,255,0.13); border-radius: 999px; cursor: pointer;
}
.theme__range::-webkit-slider-thumb {
  appearance: none; -webkit-appearance: none;
  width: 13px; height: 13px; border-radius: 50%;
  background: var(--gold); border: 0; cursor: grab;
}
.theme__range::-moz-range-thumb {
  width: 13px; height: 13px; border-radius: 50%;
  background: var(--gold); border: 0; cursor: grab;
}

.theme__foot { display: flex; gap: 8px; padding: 16px 14px 0; }
.theme__btn {
  flex: 1; padding: 9px 10px;
  background: var(--surface-2);
  border: 1px solid var(--surface-line); border-radius: 3px;
  font-size: 10px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--on-surface-dim);
  transition: color 0.2s, border-color 0.2s, background 0.2s;
}
.theme__btn:hover { color: var(--on-surface); }
.theme__btn--go { color: var(--gold); border-color: var(--gold-dim); }
.theme__btn--go:hover { background: var(--gold); color: var(--ink); }

.theme__hint { margin: 10px 14px 0; font-size: 9.5px; line-height: 1.5; color: var(--on-surface-dim); }
.theme__hint code { color: var(--on-surface); }

/* --------------------------------------------------------------- 10b */
.editbar {
  position: fixed; left: 50%; top: calc(14px + var(--st));
  transform: translateX(-50%);
  z-index: 900;
  display: flex; align-items: center; gap: 9px;
  padding: 8px 16px;
  background: rgba(245, 197, 24, 0.12);
  border: 1px solid var(--gold-dim);
  border-radius: 999px;
  font-size: 11px; letter-spacing: 0.10em; color: var(--gold);
  backdrop-filter: blur(10px);
}
.editbar__sel {
  background: rgba(0,0,0,0.28);
  border: 1px solid var(--gold-dim);
  border-radius: 3px;
  color: var(--gold);
  font: inherit; font-size: 11px;
  padding: 2px 5px; margin: 0 2px;
}
.editbar__sel option { background: #21282B; color: #EEF1F5; }
.editbar b { font-weight: 500; }

.editbar__dot { width: 7px; height: 7px; border-radius: 50%; background: var(--gold); animation: halo 1.4s ease-out infinite; }

/* --------------------------------------------------------------- 11 */
/* Fluid clamps do most of the work. These are the two cases clamps
   cannot solve: not enough WIDTH for a side card, and not enough HEIGHT
   on a landscape phone — the height case is the one that bit the tour
   (handbook 12.3), so it is handled explicitly here rather than left to
   a width query that will not fire. */
@media (max-width: 900px) {
  .head { max-width: none; right: calc(26px + var(--sr)); }
  .desc { display: none; }

  /* The card becomes a sheet rising from the bottom. Position is reset
     in full so nothing from the desktop transform survives. */
  .card {
    left: 0; right: 0; top: auto; bottom: 0;
    width: auto; max-height: 62vh;
    border-radius: 14px 14px 0 0;
    transform: translateY(100%);
    padding-bottom: calc(24px + var(--sb));
  }
  .card--on { transform: translateY(0); }
  .card:not(.card--on) { transform: translateY(100%); }
  .card__media { margin-top: -26px; }

  .rail { top: calc((var(--head-bottom, 240px) + 22px) / var(--ui, 1)); }
  .rail__btn { padding: 7px 13px 7px 10px; }
  /* The card is a bottom sheet below 900px, so the panel gets less
     room, not more. */
  .rail { max-height: max(180px, calc((100vh - var(--head-bottom, 240px)) / var(--ui, 1) - 120px)); }
  .bar__hint { display: none; }
}

/* Below this width the eyebrow wraps to two lines and eats a third of the
   map. It is the least load-bearing line on the page, so it goes. */
@media (max-width: 620px) {
  .eyebrow { display: none; }
  .card { max-height: 56vh; }
}

/* The theme panel on a phone. It is a design tool, so when it is open it
   simply takes the screen — a 292px column beside a 390px viewport would
   leave neither the panel nor the map usable. The tools row also wraps
   here, because four buttons do not fit on one line. */
@media (max-width: 700px) {
  .theme {
    left: 0; right: 0; top: auto; bottom: 0;
    width: auto; max-height: 74vh;
    border-radius: 14px 14px 0 0;
    padding-bottom: calc(14px + var(--sb));
  }
  .theme__grid { grid-template-columns: 1fr 1fr; }
  .bar { flex-wrap: wrap; }
  .bar__tools { flex-wrap: wrap; justify-content: flex-end; }
}

/* Under ~360px the two-column swatch grid clips its labels. */
@media (max-width: 380px) {
  .theme__grid { grid-template-columns: 1fr; }
}

@media (max-height: 520px) {
  /* Landscape phone. The title and the rail cannot both have the left
     column, so the rail wins — it is the control, the title is decoration. */
  .title { font-size: 24px; }
  .eyebrow { display: none; }
  .brand { margin-bottom: 8px; }
  .rail { top: calc((var(--head-bottom, 200px) + 14px) / var(--ui, 1)); }
  .rail__sub { display: none; }
  .card { max-height: 84vh; }
}

@media (max-width: 480px) {
  .head { top: calc(12px + var(--st)); left: calc(16px + var(--sl)); }
  .rail { left: calc(16px + var(--sl)); }
  .title { font-size: 26px; }
  .pin__tag { font-size: 10px; letter-spacing: 0.16em; }
}

/* =====================================================================
   12  LOADING, FOCUS, AND THE TOUR GATE
   ===================================================================== */

/* ---- Boot screen ----
   A map is not "loaded" when the page is; it is loaded when the style
   has parsed and the first tiles have arrived. Before that the visitor
   sees a coloured rectangle with two pins floating on nothing, which
   looks broken rather than slow. This holds the door shut until there is
   something worth showing, and — importantly — fades rather than cuts,
   so a fast connection does not produce a flash. */
.boot {
  position: fixed; inset: 0; z-index: 9500;
  display: flex; align-items: center; justify-content: center;
  background: var(--ink);
  transition: opacity 0.55s ease, visibility 0.55s;
}
.boot--gone { opacity: 0; visibility: hidden; pointer-events: none; }

.boot__inner { text-align: center; width: min(280px, 70vw); }
.boot__mark  { margin-bottom: 26px; }
.boot__mark img { width: 190px; max-width: 100%; }
.boot__mark .brand__text { color: var(--on-map); }

.boot__bar {
  height: 2px; border-radius: 2px;
  background: var(--surface-3);
  overflow: hidden;
}
.boot__bar span {
  display: block; height: 100%; width: 0%;
  background: var(--gold);
  transition: width 0.45s ease;
}
.boot__msg {
  margin: 14px 0 0;
  font-size: 10px; letter-spacing: 0.28em; text-transform: uppercase;
  color: var(--on-map-dim);
}

/* ---- Focus ----
   With a project open, the headline is competing with the card for the
   same attention. It steps back rather than disappearing, so the page
   still reads as the same page. */
/* With a project open the headline steps BACK. It does not go away.
   0.22 was effectively invisible — the note that it "is hidden" was
   right, and a masthead that vanishes makes the page look broken rather
   than focused. The sub-line, which is an instruction the visitor has
   already followed, is the part that can go. */
/* With a project open, only the two lines that have done their job go:
   the sub-line is an instruction the visitor has already followed, and
   the bottom hint says the same thing again. THE MASTHEAD STAYS AT FULL
   STRENGTH. Fading it was my idea and it was wrong — a title at 22%
   does not read as "focused", it reads as broken, which is exactly what
   it looked like. */
body.is-focused .desc      { opacity: 0; pointer-events: none; }
body.is-focused .bar__hint { opacity: 0; }
.desc, .bar__hint { transition: opacity 0.45s ease; }

/* ---- The tour gate ---- */
.gate {
  position: fixed; inset: 0; z-index: 9000;
  display: flex; align-items: center; justify-content: center;
  opacity: 0; visibility: hidden;
  transition: opacity 0.35s ease, visibility 0.35s;
}
.gate[hidden] { display: flex; }          /* [hidden] would kill the fade */
.gate--on { opacity: 1; visibility: visible; }

.gate__scrim {
  position: absolute; inset: 0;
  background: rgba(58, 46, 28, 0.48);
  backdrop-filter: blur(5px);
}

.gate__panel {
  position: relative;
  width: min(460px, calc(100vw - 40px));
  padding: 40px 40px 34px;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--surface-line);
  border-radius: 5px;
  box-shadow: var(--lift);
  transform: translateY(14px) scale(0.985);
  transition: transform 0.35s cubic-bezier(.2,.9,.3,1.05);
}
.gate--on .gate__panel { transform: none; }

.gate__close {
  position: absolute; right: 14px; top: 10px;
  background: none; border: 0; font-size: 24px; line-height: 1;
  color: var(--on-surface-dim); padding: 4px 8px;
}
.gate__close:hover { color: var(--gold); }

.gate__eyebrow { margin: 0 0 12px; font-size: 9px; letter-spacing: 0.34em; color: var(--gold); }
.gate__title {
  margin: 0 0 14px;
  font-family: var(--serif); font-weight: 300;
  font-size: clamp(30px, 3.2vw, 44px); letter-spacing: 0.05em;
  color: var(--on-surface);
}
.gate__desc { margin: 0 0 8px; font-size: 13.5px; line-height: 1.65; color: var(--on-surface-dim); }
.gate__meta { margin: 0 0 26px; font-size: 10.5px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--on-surface-dim); }

.gate__go {
  display: block; padding: 15px 20px;
  background: var(--gold); color: var(--ink);
  border: 1px solid var(--gold);
  text-decoration: none;
  font-size: 11.5px; letter-spacing: 0.26em; text-transform: uppercase;
  transition: filter 0.2s;
}
.gate__go:hover { filter: brightness(1.08); }

.gate__back {
  margin-top: 14px; background: none; border: 0;
  font-size: 10.5px; letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--on-surface-dim);
}
.gate__back:hover { color: var(--on-surface); }

@media (max-width: 620px) {
  .gate__panel { padding: 32px 24px 26px; }
}

/* The landmark chips are GL symbols in normal use; these are the
   temporary HTML stand-ins the pin editor puts up so they can be
   dragged. Deliberately obvious — you should be able to see at a glance
   that you are in an editing mode. */
.landmark--draggable {
  cursor: grab;
  padding: 5px 11px;
  background: var(--surface);
  border: 1px dashed var(--gold);
  border-radius: 999px;
  font-size: 11px; letter-spacing: 0.12em;
  color: var(--gold);
  white-space: nowrap;
  box-shadow: var(--lift);
}
.landmark--draggable:active { cursor: grabbing; }

/* =====================================================================
   16. THE 360, IN THE CORNER  (and the panel it opens into)
   =====================================================================

   The mirror of what the retired present.html did. There the tour filled
   the screen and the map was a corner card; here the MAP takes priority
   — it is the thing the client wanted first — and the tour is the small
   one, opening into a sheet down the left with the project card still
   readable on the right.

   ONE ELEMENT, TWO SIZES. `.tourwrap` holds one iframe. `--mini` is the
   corner card, `--panel` is the sheet, and everything in between is a
   transition on that one box. The frame is never rebuilt, so opening the
   tour is not a page load and there is no second progress bar in front
   of a client.
   ===================================================================== */

.tourwrap {
  position: fixed;
  z-index: 650;
  overflow: hidden;
  background: var(--ink);
  border: 1px solid var(--surface-line);
  box-shadow: var(--lift);
  transition:
    left 0.6s cubic-bezier(0.22, 0.9, 0.24, 1),
    right 0.6s cubic-bezier(0.22, 0.9, 0.24, 1),
    top 0.6s cubic-bezier(0.22, 0.9, 0.24, 1),
    bottom 0.6s cubic-bezier(0.22, 0.9, 0.24, 1),
    width 0.6s cubic-bezier(0.22, 0.9, 0.24, 1),
    height 0.6s cubic-bezier(0.22, 0.9, 0.24, 1),
    border-radius 0.6s cubic-bezier(0.22, 0.9, 0.24, 1),
    border-color 0.35s ease;
}

/* Corner card, bottom RIGHT. It started bottom-left, under the project
   rail; the client asked for it on the same side as everything else you
   read. That corner had to be cleared first — see the note on the
   MapLibre controls in js/map.js, and `body.card-open` further down. */
.tourwrap--mini {
  right: calc(26px + var(--sr));
  bottom: calc(78px + var(--sb));
  left: auto; top: auto;
  width: clamp(200px, 20vw, 400px);
  height: var(--tour-h);
  border-radius: 6px;
  cursor: pointer;
}
.tourwrap--mini:hover { border-color: var(--gold-dim); }

/* THE PANEL OPENS ON THE RIGHT.

   It used to open on the left and stop short of the project card. It
   does not have to any more: opening the tour hides that card, and
   picking a project shrinks the tour back to its corner, so the two are
   never on screen together and the panel can have the whole right-hand
   side. What stays visible down the left is the brand, the title and the
   map itself — enough that the tour reads as a panel over the map rather
   than as a different page. */
.tourwrap--panel {
  z-index: 720;             /* over the card, which fades but stays laid out */
  left: max(34%, 420px);
  top: calc(20px + var(--st));
  /* Stops above the bottom bar rather than over it: the bar is where
     Connectivity, 2D view and the basemap toggle live, and clipping the
     left end of that row is worse than a 60-pixel shorter tour. */
  bottom: calc(72px + var(--sb));
  right: calc(20px + var(--sr));
  width: auto; height: auto;
  border-radius: 10px;
  cursor: default;
}
@media (max-width: 1100px) {
  .tourwrap--panel { left: calc(20px + var(--sl)); }
}

.tourwrap__frame { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; display: block; }

/* ---- The cover ----
   A live 360 in a 300-pixel box is a moving smudge, and it costs a WebGL
   context and a stream of texture uploads for something nobody can read.
   So the corner shows a still of the first vantage point with a play
   mark on it, and the frame behind only starts when it is opened. */
.tourwrap__cover {
  position: absolute; inset: 0; z-index: 2;
  background-size: cover; background-position: 50% 50%;
  display: flex; align-items: flex-end;
  transition: opacity 0.45s ease;
}
.tourwrap__cover::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(to top, rgba(8, 12, 14, 0.88) 0%, rgba(8, 12, 14, 0.15) 55%, rgba(8, 12, 14, 0.3) 100%);
}
.tourwrap--panel .tourwrap__cover { opacity: 0; pointer-events: none; }

/* A LABEL, NOT A PLAY TRIANGLE.

   The triangle is the universal sign for "video", and this is not a
   video — it is a place you walk into. Across a boardroom a 46px glyph
   is also barely a shape, whereas two words are still two words. */
.tourwrap__cta {
  position: absolute; left: 50%; top: 46%; transform: translate(-50%, -50%);
  z-index: 2;
  display: flex; align-items: center; gap: 0.5em;
  padding: 0.75em 1.4em;
  background: rgba(12, 16, 18, 0.62);
  border: 1px solid rgba(255, 255, 255, 0.34);
  border-radius: 999px;
  backdrop-filter: blur(6px); -webkit-backdrop-filter: blur(6px);
  font-size: 12px; font-weight: 500; letter-spacing: 0.22em; text-transform: uppercase;
  line-height: 1; color: var(--on-map); white-space: nowrap;
  transition: border-color 0.25s, background 0.25s, color 0.25s;
}
.tourwrap__cta i { font-style: normal; font-size: 1.35em; line-height: 0; margin-top: -0.08em; }
.tourwrap--mini:hover .tourwrap__cta {
  border-color: var(--gold); background: rgba(217, 164, 65, 0.28); color: #fff;
}

.tourwrap__tag {
  position: relative; z-index: 1;
  display: flex; flex-direction: column; gap: 3px;
  padding: 12px 14px;
}
.tourwrap__eyebrow { font-size: 9px; letter-spacing: 0.3em; color: var(--gold); }
.tourwrap__name { font-family: var(--serif); font-size: 18px; letter-spacing: 0.07em; color: var(--on-map); line-height: 1; }

/* BOTTOM-RIGHT, and it took three tries to find a corner that was free.
   The tour puts its brand top-left, its destination toggles top-centre,
   its MENU top-right, the compass below that, the point rail down the
   left and the facts panel bottom-left. Bottom-right is the one corner
   it does not use. A labelled pill rather than a bare cross, because
   this is read from across a room. */
.tourwrap__close {
  position: absolute; right: 18px; bottom: 18px; z-index: 3;
  display: none; align-items: center; gap: 0.6em;
  padding: 0.7em 1.2em;
  background: var(--surface); border: 1px solid var(--surface-line); border-radius: 999px;
  font-size: 12px; letter-spacing: 0.24em; text-transform: uppercase;
  line-height: 1; color: var(--on-surface);
  backdrop-filter: blur(10px);
  cursor: pointer;
  zoom: var(--ui, 1);
  transition: border-color 0.25s, color 0.25s;
}
.tourwrap--panel .tourwrap__close { display: flex; }
.tourwrap__close:hover { border-color: var(--gold-dim); color: var(--gold); }
.tourwrap__close span { font-size: 15px; line-height: 0.8; }

/* With the tour open the rail and the bottom bar would sit on top of the
   sheet. They fade rather than vanish, so the screen does not lurch. */
/* THE CARD AND THE TOUR ARE NEVER BOTH OPEN.

   The panel now owns the right-hand side, which is where the card lives.
   Rather than shuffle the card somewhere it does not belong, the two
   simply take turns: opening the tour fades the card out, and picking a
   project (a pin, or a row in the rail) shrinks the tour back to its
   corner and brings the card back. js/map.js drives the second half of
   that; this is the first. */
body.tour-open .rail,
body.tour-open .card,
body.tour-open .bar__hint { opacity: 0; pointer-events: none; transition: opacity 0.35s ease; }

/* The rail has the bottom-left corner to itself again, now that the 360
   card has moved across. */
:root { --tour-h: clamp(124px, 12.5vw, 250px); }

/* DOCKED — the third state, and the answer to "don't let it disappear".

   The corner card and the project card both want the bottom-right, and
   the project card is too tall on a television to stack anything under
   it. Hiding the 360 was the first answer and the wrong one: the tour is
   the thing being sold, and it vanished at the exact moment somebody
   showed interest in a project.

   So instead of hiding, it MOVES. js/map.js measures `.card__media` and
   sets these four numbers, and the same box that was sitting in the
   corner slides up and becomes the card's header image. Nothing is
   re-parented, so the iframe inside it never reloads — which is the only
   reason this is possible at all.

   No `zoom` here: the geometry comes from getBoundingClientRect, which
   already reports real viewport pixels, and zooming it again would
   double-apply the UI scale. */
.tourwrap--docked {
  /* ABOVE THE CARD. The card is z-index 700 and this box is 650, so
     docked into the card's own header hole it was drawn behind it — a
     dark empty rectangle where the 360 should be. It only needs to win
     while it is sitting inside the card. */
  z-index: 750;
  border-radius: 3px 3px 0 0;
  cursor: pointer;
  box-shadow: none;
  border-color: transparent;
}
.tourwrap--docked:hover { border-color: var(--gold-dim); }


/* =====================================================================
   17. TV SIZING
   =====================================================================

   This map is shown on a boardroom television, read from across a table,
   and every size in this stylesheet was chosen for a laptop at arm's
   length. `--ui` is the one number that fixes that: `ui.scale` in
   js/config.js writes it, and everything the visitor reads is scaled by
   it in one place.

   WHY `zoom` AND NOT A FONT SIZE. These panels are laid out in pixels —
   paddings, gaps, icon sizes, the card's width. Scaling only the type
   would give big words in small boxes. `zoom` scales the whole box and
   its insets together, which is exactly what "make it bigger" means
   here, and it costs one line per panel instead of touching ninety
   values. Chrome, Edge and Safari have supported it for years; Firefox
   since 126. In a browser that ignores it, everything is simply the
   laptop size it was before — no layout breaks either way.

   `.bar` is NOT in the list: it spans the full width, so zooming it
   would make it wider than the screen. Its two children are zoomed
   instead, which gets the same result.

   The map's own labels are not CSS at all — they are drawn by the GPU —
   so they follow `ui.mapScale`, applied in js/map.js. */
/* .lcard belongs here too: it was left out on the first pass and was the
   one panel that stayed laptop-sized on a four-metre screen. */
.head, .rail, .card, .lcard, .surr, .editbar, .theme, .tourwrap--mini {
  zoom: var(--ui, 1);
}
/* ...but never the docked state: its box comes from
   getBoundingClientRect, which is already in real viewport pixels. */
.tourwrap--docked { zoom: 1; }
.bar__tools, .bar__hint { zoom: var(--ui, 1); }

/* =====================================================================
   18. THE COLOUR MENU
   =====================================================================
   Client-facing, not a developer tool: the palettes were sent to be
   looked at, so they are on the map rather than in a config file. Top
   right, above the map's own zoom buttons, which are pushed down to
   make room.
   ===================================================================== */

.pal { position: fixed; z-index: 760; right: calc(26px + var(--sr)); top: calc(20px + var(--st)); zoom: var(--ui, 1); }

.pal__btn {
  display: flex; align-items: center; gap: 0.65em;
  padding: 0.62em 1.05em;
  background: var(--surface); border: 1px solid var(--surface-line); border-radius: 999px;
  font-size: 11px; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--on-surface-dim);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  transition: color 0.22s, border-color 0.22s;
}
.pal__btn:hover, .pal__btn[aria-expanded="true"] { color: var(--on-surface); border-color: var(--gold-dim); }
.pal__dot { width: 0.85em; height: 0.85em; border-radius: 50%; background: var(--gold); flex: none;
            box-shadow: 0 0 0 1px rgba(255,255,255,0.22); }

.pal__list {
  margin-top: 8px;
  padding: 6px;
  background: var(--surface); border: 1px solid var(--surface-line); border-radius: 8px;
  box-shadow: var(--lift);
  backdrop-filter: blur(12px); -webkit-backdrop-filter: blur(12px);
  min-width: 190px;
}
.pal__item {
  display: flex; align-items: center; gap: 0.7em; width: 100%;
  padding: 0.55em 0.7em;
  background: none; border: 0; border-radius: 5px;
  font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase;
  color: var(--on-surface-dim); text-align: left;
  transition: background 0.18s, color 0.18s;
}
.pal__item:hover { background: var(--surface-2); color: var(--on-surface); }
.pal__item.on { color: var(--on-surface); background: var(--gold-ghost); }

/* Three swatches per row — the accent and both project colours. It is
   the only honest preview: a single dot cannot tell you that a scheme's
   two projects are hard to tell apart. */
.pal__sw { display: flex; flex: none; }
.pal__sw i { display: block; width: 11px; height: 16px; }
.pal__sw i:first-child { border-radius: 3px 0 0 3px; }
.pal__sw i:last-child  { border-radius: 0 3px 3px 0; }

/* The map's own controls move down so the menu can have the corner. */
.maplibregl-ctrl-top-right { top: calc(58px + var(--st)); }

/* =====================================================================
   TWO-COLOUR CONNECTIVITY, client's note of 19 September 2026.
   Blue = routes you can drive today; yellow = upcoming infrastructure
   (Virar Alibaug corridor, Revas Karanja bridge). These rules carry
   that pairing into the top legend and the card's two route groups,
   so the panel and the map speak the same colour language.
   ===================================================================== */
.connlegend {
  position: fixed;
  top: 16px; left: 50%;
  transform: translateX(-50%);
  z-index: 30;
  display: inline-flex; align-items: center; gap: 18px;
  padding: 8px 16px;
  border-radius: 999px;
  background: rgba(15, 20, 24, 0.68);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  border: 1px solid rgba(255,255,255,0.14);
  pointer-events: none;
}
.connlegend[hidden] { display: none; }
.connlegend__item {
  font-family: "Jost", sans-serif;
  font-size: 10px; font-weight: 600; letter-spacing: 1.8px;
  color: rgba(255,255,255,0.85);
  display: inline-flex; align-items: center; gap: 8px;
  white-space: nowrap;
}
.connlegend__line {
  display: inline-block;
  width: 26px; height: 0;
  border-top: 3px solid #2E9BFF;
  border-radius: 2px;
}
.connlegend__line--future {
  border-top-style: dotted;
  border-top-width: 4px;
  border-top-color: #FFD84D;
}
@media (max-width: 700px) {
  .connlegend { gap: 10px; padding: 6px 12px; top: 10px; }
  .connlegend__item { font-size: 9px; letter-spacing: 1.2px; }
}

/* The card's two route groups. The group titles carry a short swatch of
   their own colour; upcoming rows tint their figures the same yellow
   (darkened for the pale card) so the pairing survives at a glance. */
.card__conngroup {
  list-style: none;
  margin: 10px 0 4px;
  font-size: 10px; font-weight: 700; letter-spacing: 1.8px;
  color: #7A736A;
  display: flex; align-items: center; gap: 8px;
}
.card__conngroup::before {
  content: "";
  display: inline-block;
  width: 20px; height: 0;
  border-top: 3px solid #2E9BFF;
  border-radius: 2px;
}
.card__conngroup--future::before {
  border-top-style: dotted;
  border-top-width: 4px;
  border-top-color: #E3B41D;
}
.card__connrow-future span { color: #A8830F; }

/* =====================================================================
   FSI & ZONING sheet, 19 September 2026. Rides the Compare sheet's
   frame (.cmp) so it inherits the scrim, plate and close button; these
   rules only lay out its own content.
   ===================================================================== */
.fsi__body {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 28px;
  margin-top: 18px;
}
@media (max-width: 760px) { .fsi__body { grid-template-columns: 1fr; } }
.fsi__h {
  font-family: "Cormorant Garamond", serif;
  font-size: 22px; font-weight: 600;
  color: #2E2A24;
  margin: 0 0 8px;
}
.fsi__p { font-size: 13.5px; line-height: 1.55; color: #4A443C; margin: 0 0 12px; }
.fsi__ex {
  font-size: 12.5px; line-height: 1.55; color: #5A5348;
  background: rgba(0,0,0,0.045);
  border-radius: 8px;
  padding: 10px 12px;
  margin: 0;
}
.fsi__ex span {
  display: block;
  font-size: 10px; font-weight: 700; letter-spacing: 1.6px;
  color: #8A8274;
  margin-bottom: 4px;
  text-transform: uppercase;
}
.fsi__zones { margin: 0; display: grid; grid-template-columns: 44px 1fr; row-gap: 12px; }
.fsi__zones dt {
  font-weight: 700; font-size: 14px; color: #2E2A24;
  border-left: 3px solid #B8A97F; padding-left: 8px;
}
.fsi__zones dd { margin: 0; font-size: 13px; line-height: 1.5; color: #4A443C; }

/* ---- WATERMARK ------------------------------------------------------
   Small "Made by Cinosky" credit, bottom-left. Added 20 September 2026
   at Pratik's request. pointer-events:none so it never intercepts a map
   click; low opacity so it reads as a watermark, not a label. */
body::after {
  content: "Made by Cinosky";
  position: fixed;
  left: 12px;
  bottom: 9px;
  z-index: 6;
  font: 600 10px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.5);
  text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7);
  pointer-events: none;
}
