/* Bubbleberry — public journey map */

/* Self-hosted: no request ever leaves for a font CDN, which also keeps this
   clear of the GDPR problem with hotlinking Google Fonts from an EU site.
   Both faces are variable, one file per subset covering every weight. */
@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../vendor/fonts/fraunces-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Fraunces';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../vendor/fonts/fraunces-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: 'Work Sans';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('../vendor/fonts/worksans-latin-ext.woff2') format('woff2');
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
                 U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF,
                 U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: 'Work Sans';
  font-style: normal;
  font-weight: 400 600;
  font-display: swap;
  src: url('../vendor/fonts/worksans-latin.woff2') format('woff2');
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
                 U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122,
                 U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

:root {
  /* Panels are sage with black text — the map is the only bright thing. */
  --sage:      #a9c69b;
  --sage-deep: #93b384;
  /* Solid, not translucent: the panels are meant to read as this exact colour. */
  --panel:     #a9c69b;
  --panel-sc:  #a9c69b;
  --text:      #000000;
  --text-dim:  #3d4c36;
  --line:      rgba(0, 0, 0, .18);
  --drive:     #c8452a;
  --ferry:     #1b6ea8;
  --radius:    14px;
  --blur:      saturate(140%) blur(12px);
  --safe-b:    env(safe-area-inset-bottom, 0px);

  --font-display: 'Fraunces', Georgia, 'Times New Roman', serif;
  --font-body:    'Work Sans', ui-sans-serif, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
}


* { box-sizing: border-box; }

/* The sheet and lightbox set an explicit `display`, which would otherwise beat
   the [hidden] attribute and leave them covering the page and swallowing clicks. */
[hidden] { display: none !important; }

html, body { height: 100%; }
body {
  margin: 0;
  /* Shows through while map tiles are still arriving, so the gap reads as
     part of the design rather than as a broken page. */
  background: #8fa886;
  color: var(--text);
  font: 400 15px/1.6 var(--font-body);
  -webkit-font-smoothing: antialiased;
  overflow: hidden;
}

h1, h2, .hud-stats dd, .tl-name {
  font-family: var(--font-display);
  font-optical-sizing: auto;
}

.map { position: fixed; inset: 0; }

/* MapLibre's own chrome, toned down to sit under ours. */
.maplibregl-ctrl-bottom-right { margin-bottom: 92px; }
.maplibregl-ctrl-attrib { font-size: 10px; }
.maplibregl-ctrl-group { border-radius: 8px !important; }
@media (max-width: 760px) { .maplibregl-ctrl-bottom-right { display: none; } }

/* --------------------------------------------------------------- top HUD */
.hud {
  position: fixed; z-index: 10;
  background: var(--panel);
  -webkit-backdrop-filter: var(--blur); backdrop-filter: var(--blur);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 8px 30px rgba(0,0,0,.22);
}
.hud-top {
  top: 14px; left: 14px; right: 14px;
  display: flex; align-items: center; justify-content: space-between;
  gap: 1.2rem; padding: .75rem 1.15rem; flex-wrap: wrap;
}
.hud-brand h1 {
  margin: 0; font-size: 1.45rem; font-weight: 600; letter-spacing: -.015em;
  font-variation-settings: 'SOFT' 20, 'WONK' 1;
}
.hud-brand p {
  margin: 0; font-size: .8rem; color: var(--text-dim);
  letter-spacing: .02em;
}

.hud-stats { display: flex; gap: 1.5rem; margin: 0; flex-wrap: wrap; }
.hud-stats div { text-align: right; }
.hud-stats dt {
  font-size: .63rem; text-transform: uppercase; letter-spacing: .12em;
  color: var(--text-dim); font-weight: 500;
}
.hud-stats dd { margin: 0; font-size: 1.1rem; font-weight: 600; letter-spacing: -.02em; }

@media (max-width: 760px) {
  .hud-top { top: 8px; left: 8px; right: 8px; padding: .6rem .8rem; gap: .5rem; }
  .hud-brand h1 { font-size: 1.15rem; }
  .hud-stats { gap: .9rem; width: 100%; justify-content: space-between; }
  .hud-stats div { text-align: left; }
  .hud-stats dd { font-size: .95rem; }
}

/* ----------------------------------------------------------------- intro */
.intro {
  position: fixed; z-index: 14; left: 50%; top: 50%;
  transform: translate(-50%, -50%);
  width: min(520px, calc(100vw - 32px));
  background: var(--panel-sc);
  border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.6rem 1.7rem 1.3rem; text-align: center;
  box-shadow: 0 24px 70px rgba(0,0,0,.35);
  transition: opacity .5s, transform .5s;
}
.intro.gone { opacity: 0; transform: translate(-50%, -58%); pointer-events: none; }
.intro-body { font-size: 1.02rem; }
.intro-body p { margin: 0 0 .8em; }
.intro-body p:last-child { margin-bottom: 0; }
.intro-close { margin-top: .4rem; }

.load-error {
  position: fixed; z-index: 30; left: 50%; top: 50%; transform: translate(-50%, -50%);
  background: var(--panel-sc); border: 1px solid var(--line); border-radius: var(--radius);
  padding: 1.2rem 1.4rem; max-width: min(420px, calc(100vw - 32px)); text-align: center;
}

/* -------------------------------------------------------------- controls */
.ctl {
  background: var(--panel); color: var(--text);
  -webkit-backdrop-filter: var(--blur); backdrop-filter: var(--blur);
  border: 1px solid var(--line); border-radius: 99px;
  padding: .5rem 1.05rem; font: inherit; font-size: .85rem; font-weight: 500;
  cursor: pointer; box-shadow: 0 5px 20px rgba(0,0,0,.22);
  transition: background .15s, transform .15s;
}
.ctl:hover { background: var(--sage-deep); }
.ctl:active { transform: scale(.97); }
.ctl:disabled { opacity: .4; cursor: default; }
.ctl-sm { padding: .35rem .8rem; font-size: .8rem; }

.playbar {
  position: fixed; z-index: 12; left: 50%; bottom: calc(104px + var(--safe-b));
  transform: translateX(-50%);
  opacity: 0; pointer-events: none; transition: opacity .3s;
}
.playbar.on { opacity: 1; pointer-events: auto; }

.ctl-replay {
  position: fixed; z-index: 12; left: 50%; bottom: calc(104px + var(--safe-b));
  transform: translateX(-50%);
}

/* ----------------------------------------------------------------- pins */
.pin-wrap { width: 22px; height: 22px; }
.pin {
  display: block; position: relative; width: 22px; height: 22px;
  background: none; border: 0; padding: 0; cursor: pointer;
  opacity: 0; transform: scale(.6);
  transition: opacity .35s, transform .35s cubic-bezier(.2,1.3,.4,1);
}
.pin.pin-in { opacity: 1; transform: none; }
.pin-dot {
  position: absolute; inset: 0; border-radius: 99px;
  background: var(--drive); border: 2.5px solid var(--sage);
  box-shadow: 0 2px 8px rgba(0,0,0,.45);
  display: grid; place-items: center;
  transition: transform .18s, background .18s;
}
.pin-num { font-size: 10px; font-weight: 700; color: #fff; line-height: 1; }
.pin:hover .pin-dot { transform: scale(1.22); }
.pin.pin-active { z-index: 2; }
.pin.pin-active .pin-dot { background: #000; transform: scale(1.3); }
.pin.pin-active .pin-num { color: var(--sage); }
/* A pill, not bare text — the basemap has its own city labels to compete with. */
.pin-label {
  position: absolute; top: 100%; left: 50%; margin-top: 6px;
  transform: translateX(-50%);
  padding: 2px 8px; border-radius: 99px;
  background: var(--sage); color: #000;
  border: 1px solid var(--line);
  font-size: 11px; font-weight: 500; white-space: nowrap; line-height: 1.45;
  opacity: 0; transition: opacity .2s; pointer-events: none;
}
.pin:hover { z-index: 3; }
.pin:hover .pin-label, .pin.pin-active .pin-label, .pin.pin-in .pin-label { opacity: 1; }
.pin.pin-active .pin-label { background: #000; color: var(--sage); border-color: #000; }
@media (max-width: 760px) { .pin-label { display: none; } }

.head { pointer-events: none; }
.head-icon {
  display: grid; place-items: center; width: 34px; height: 34px;
  border-radius: 99px; background: var(--sage); font-size: 18px;
  box-shadow: 0 3px 14px rgba(0,0,0,.45), 0 0 0 3px rgba(200,69,42,.4);
}

/* -------------------------------------------------------------- timeline */
.timeline {
  position: fixed; z-index: 11; left: 14px; right: 14px;
  bottom: calc(14px + var(--safe-b));
  display: flex; align-items: stretch; gap: .3rem;
  background: var(--panel);
  -webkit-backdrop-filter: var(--blur); backdrop-filter: var(--blur);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 8px 30px rgba(0,0,0,.22);
  padding: .35rem;
}
.tl-arrow {
  flex: none; width: 30px; border: 0; border-radius: 10px;
  background: transparent; color: var(--text-dim);
  font-size: 1.3rem; line-height: 1; cursor: pointer;
}
.tl-arrow:hover { background: rgba(0,0,0,.08); color: var(--text); }

.tl-track {
  list-style: none; display: flex; gap: .2rem; margin: 0; padding: 0;
  overflow-x: auto; scroll-behavior: smooth; scrollbar-width: none; flex: 1;
}
.tl-track::-webkit-scrollbar { display: none; }
.tl-track li { flex: none; }
.tl-track button {
  display: grid; grid-template-rows: auto auto auto; justify-items: center; gap: 1px;
  background: none; border: 0; cursor: pointer; color: var(--text-dim);
  padding: .35rem .7rem; border-radius: 10px; font: inherit; min-width: 74px;
  transition: background .15s, color .15s;
}
.tl-track button:hover { background: rgba(0,0,0,.08); color: var(--text); }
.tl-track button.on { background: #000; color: var(--sage); }
.tl-dot {
  width: 7px; height: 7px; border-radius: 99px;
  background: currentColor; opacity: .5; margin-bottom: 2px;
}
.tl-track button.on .tl-dot { background: var(--drive); opacity: 1; }
.tl-name {
  font-size: .84rem; font-weight: 500; white-space: nowrap;
  max-width: 110px; overflow: hidden; text-overflow: ellipsis;
}
.tl-date { font-size: .68rem; opacity: .75; white-space: nowrap; font-family: var(--font-body); }

/* ----------------------------------------------------------------- sheet */
.sheet {
  position: fixed; z-index: 20;
  background: var(--panel-sc);
  border: 1px solid var(--line);
  box-shadow: 0 18px 60px rgba(0,0,0,.4);
  display: flex; flex-direction: column;
  transition: transform .3s cubic-bezier(.2,.8,.3,1), opacity .25s;
}
/* Desktop: a card down the left, centred against the map's own height rather
   than pinned into the corner. The map keeps the whole right-hand side. */
@media (min-width: 761px) {
  .sheet {
    /* left matches the HUD and timeline gutter so the three line up. */
    top: 50%; left: 14px;
    width: clamp(380px, 30vw, 470px);
    max-height: min(680px, calc(100vh - 200px));
    border-radius: var(--radius);
    transform: translate(-26px, -50%); opacity: 0;
  }
  .sheet.open { transform: translate(0, -50%); opacity: 1; }
}
/* Mobile: a bottom sheet. */
@media (max-width: 760px) {
  .sheet {
    left: 0; right: 0; bottom: 0;
    max-height: 82vh;
    border-radius: var(--radius) var(--radius) 0 0;
    border-bottom: 0;
    transform: translateY(100%);
    padding-bottom: var(--safe-b);
  }
  .sheet.open { transform: none; }
}

.sheet-close {
  position: absolute; z-index: 3; top: 10px; right: 10px;
  width: 30px; height: 30px; border-radius: 99px; border: 0;
  background: rgba(0,0,0,.72); color: var(--sage); font-size: 1.2rem; line-height: 1;
  cursor: pointer;
}
.sheet-close:hover { background: #000; }

.sheet-scroll { overflow-y: auto; overscroll-behavior: contain; }
/* Fade the last few pixels while there is more below, so a long entry doesn't
   just stop mid-sentence at a hard edge. Removed once you reach the end. */
.sheet.has-more .sheet-scroll {
  -webkit-mask-image: linear-gradient(to bottom, #000 calc(100% - 34px), transparent);
          mask-image: linear-gradient(to bottom, #000 calc(100% - 34px), transparent);
}
/* Right padding keeps the title clear of the close button. */
.sheet-head { padding: .95rem 3rem .8rem 1.15rem; }
.sheet-pad  { padding: .95rem 1.15rem 1.2rem; }
.sheet h2 {
  margin: 0 0 .15rem; font-size: 1.5rem; font-weight: 600; letter-spacing: -.02em;
  font-variation-settings: 'SOFT' 20, 'WONK' 1;
}

.stop-meta { margin: 0; font-size: .84rem; color: var(--text-dim); }

/* Fixed tile heights rather than an aspect ratio: at this card width a 4/3
   hero is over 350px tall, which buries the story under the photographs. */
.gallery { display: grid; grid-template-columns: 1fr 1fr; gap: 2px; }
.gallery-one { grid-template-columns: 1fr; }
.gallery .shot {
  border: 0; padding: 0; background: var(--sage-deep);
  cursor: zoom-in; display: block; height: 140px;
}
.gallery .shot:first-child:nth-last-child(odd) { grid-column: 1 / -1; height: 190px; }
.gallery-one .shot { height: 215px; }
.gallery img {
  width: 100%; height: 100%; object-fit: cover; display: block;
  transition: opacity .2s;
}
.gallery .shot:hover img { opacity: .85; }

.weather {
  display: flex; gap: .55rem; flex-wrap: wrap; align-items: center;
  padding: .5rem .7rem; margin: 0 0 .7rem;
  background: rgba(0,0,0,.07); border-radius: 10px;
  font-size: .82rem; color: var(--text-dim);
}
.weather .w-main { color: var(--text); font-weight: 600; }
.w-note { margin: -.3rem 0 .8rem; font-size: .85rem; color: var(--text-dim); font-style: italic; }

/* The story sits on lined paper, full-bleed across the card.
 *
 * The whole effect depends on one thing: the rule spacing and the line-height
 * have to be the same fixed pixel value, and every vertical gap has to be a
 * multiple of it. Otherwise the text drifts off the lines a paragraph or two
 * in. Hence px rather than a unitless line-height, and a paragraph gap of
 * exactly one rule — which reads as a blank line, like a real notebook.
 */
.story {
  --rule: 26px;
  --paper: #f2f7fc;
  --ink-line: #cfe0f0;

  font-size: .94rem;
  line-height: var(--rule);
  margin: .3rem -1.15rem 0;
  padding: var(--rule) 1.15rem;
  background-color: var(--paper);
  background-image: repeating-linear-gradient(
    to bottom,
    transparent 0,
    transparent calc(var(--rule) - 1px),
    var(--ink-line) calc(var(--rule) - 1px),
    var(--ink-line) var(--rule)
  );
  box-shadow: inset 0 1px 0 rgba(0,0,0,.06), inset 0 -1px 0 rgba(0,0,0,.06);
}
.story p { margin: 0 0 var(--rule); }
.story p:last-child { margin-bottom: 0; }
.story a { color: var(--drive); }

.odo {
  margin: 1rem 0 0; padding-top: .7rem; border-top: 1px solid var(--line);
  font-size: .77rem; color: var(--text-dim);
}

.sheet-nav {
  display: flex; justify-content: space-between; gap: .5rem;
  padding: .6rem .8rem; border-top: 1px solid var(--line); flex: none;
}

/* -------------------------------------------------------------- lightbox */
/* Stays dark: photographs should be judged against neutral, not sage. */
/*
 * The photo is centred in a single full-viewport cell and the arrows float
 * over it. They used to be grid columns beside it, but the image is sized
 * against the viewport, not against the leftover track: below ~1000px the
 * image plus both arrows was wider than the screen, the grid overflowed to
 * the right, and the photo sat visibly left of centre with the "next" arrow
 * pushed off the edge. Out of flow, the arrows can no longer move the photo.
 */
.lightbox {
  position: fixed; inset: 0; z-index: 50;
  background: rgba(10,14,10,.95);
  display: grid; place-items: center;
  opacity: 0; transition: opacity .2s;
}
.lightbox.open { opacity: 1; }
.lb-figure { margin: 0; max-width: 100%; max-height: 100vh; padding: 2rem 0; text-align: center; }
.lb-figure img {
  /* Leave room for the arrows so they sit beside the photo, not on it. */
  max-width: min(1400px, calc(100vw - 11rem)); max-height: 78vh; object-fit: contain;
  border-radius: 4px; box-shadow: 0 18px 70px rgba(0,0,0,.6);
}
.lb-figure figcaption { margin-top: .9rem; font-size: .85rem; color: var(--sage); }
.lb-close {
  position: absolute; top: 14px; right: 16px; z-index: 2;
  width: 40px; height: 40px; border-radius: 99px; border: 0;
  background: var(--sage); color: #000; font-size: 1.5rem; cursor: pointer;
}
.lb-close:hover { background: #fff; }
.lb-arrow {
  position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  border: 0; background: none; color: var(--sage); opacity: .7;
  font-size: 2.6rem; line-height: 1; padding: 1rem; cursor: pointer;
}
.lb-prev { left: .4rem; }
.lb-next { right: .4rem; }
.lb-arrow:hover { opacity: 1; }
@media (max-width: 760px) {
  /* Too narrow to keep the photo clear of the arrows — let them overlap. */
  .lb-arrow { padding: .5rem; font-size: 2.2rem; }
  .lb-prev { left: 0; } .lb-next { right: 0; }
  .lb-figure img { max-width: 92vw; }
}

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}
