/* shared-overlays.css - Cross-page overlay styles.
 *
 * These rules appear in BOTH the standard tour shell (src/tour/tour.css)
 * and the live tour shell (src/live/live-tour.css), so we hoist them
 * into a single file that every page links. Keeps the AIS vessel-label
 * pill (and its oncoming variant) in one place.
 *
 * MOVE 7 of the A7 restructure (2026-05-25): deduped from tour.css +
 * live-tour.css; removed from both source files. Loaded BEFORE the
 * page-specific stylesheet so later more-specific rules can still
 * override on a per-page basis.
 */

/* AIS vessel name label under each arrow. Apple-Maps-style pill: small
   navy text on a translucent white background with a soft blur, sitting
   ~22px under the arrow tip (positioned by Symbol.labelOrigin in JS).
   Google Maps wraps the label text in a <div> and applies the className,
   so this rule targets the wrapper. */
.lt-ais-label{
  background:rgba(255,255,255,0.85);
  padding:1px 6px;
  border-radius:6px;
  text-shadow:0 1px 1px rgba(255,255,255,0.8);
  -webkit-backdrop-filter:blur(4px);
  backdrop-filter:blur(4px);
  box-shadow:0 1px 2px rgba(9,28,53,0.12);
  white-space:nowrap;
  line-height:1.2;
}
.lt-ais-label--oncoming{
  /* Light-red tint pairs with the red halo on oncoming vessels. */
  background:rgba(255,232,228,0.92);
  box-shadow:0 1px 2px rgba(225,29,42,0.18);
}

/* "You are here" dot - the AdvancedMarker content div that
   src/shared/my-location.js renders on EVERY map surface (standard tour,
   vaarverkeer, vaarinfo). Google-Maps-default skin: solid blue disc, white
   stroke, soft halo.

   Lives here, not in tour.css: my-location.js is shared by three pages but
   the rule used to sit in src/tour/tour.css, which pages/vaarinfo.html does
   not load. Result was a 0x0 transparent div on that page - the captain saw
   only the 8%-opacity accuracy circle and had to zoom all the way in to find
   himself (2026-07-27). Keep this rule in a stylesheet every map page links. */
.gmaps-userdot{
  width:16px;height:16px;border-radius:50%;background:#1976D2;border:2.5px solid #fff;
  box-shadow:0 0 0 4px rgba(25,118,210,0.18);
}

/* ── Liquid Glass treatment for Google InfoWindow popups ─────────────────
   Applied here in shared-overlays.css (loaded by BOTH index.html standard
   tour + live.html live tour) so both surfaces render identical glass
   chrome. Content INSIDE the popup is still styled per-page:
     - .gmaps-popup (tour.js, standard tour)  -> stacked image on top
     - .lt-popup    (live-tour.js, live tour) -> side-by-side compact card
   The OUTER chrome (.gm-style-iw-c + .gm-style-iw-d + .gm-style-iw-chr
   + tail) is identical here.
   2026-05-27: close-X moved from chrome row to image overlay (save ~30 px
   vertical real-estate). 2026-05-28: padding resets re-applied after a
   Google v=weekly bump reintroduced them; glass body + tail added. */
/* Commit K (2026-07-06): the glass literals below are now var() aliases of
   the canonical --bb-glass-* tokens (styles/shared-prototype.css). Every
   page that loads this file also loads shared-prototype.css; the fallbacks
   carry the same bb values as a safety net. Visible deltas vs the old
   literals: radius 24 -> 22px, border alpha 0.22 -> 0.55, shadow tint
   black -> navy, inset highlight 0.6 -> 0.70. */
.gm-style .gm-style-iw-c{
  max-width:none!important;padding:0!important;
  background:var(--bb-glass-bg, rgba(255,255,255,0.66))!important;
  backdrop-filter:var(--bb-glass-blur, blur(22px) saturate(180%))!important;
  -webkit-backdrop-filter:var(--bb-glass-blur, blur(22px) saturate(180%))!important;
  border:1px solid var(--bb-glass-border, rgba(255,255,255,0.55))!important;
  border-radius:var(--bb-glass-radius, 22px)!important;
  box-shadow:var(--bb-glass-shadow, 0 14px 44px rgba(9,28,53,0.18),
             inset 0 1px 0 rgba(255,255,255,0.70))!important;
}

/* ── D2 Variant 1 - "Deep Navy Glass" InfoWindow popups in DARK theme ──────
   2026-06-08: when the APP is in dark theme ([data-theme="dark"] on <html>),
   the Google InfoWindow chrome flips from frosted-white to frosted DEEP NAVY
   glass with a cyan hairline + cyan accents, matching the legend / gear panel
   / dark map popups so the whole dark UI reads as one Liquid-Glass material.
   The MAP itself keeps its own theme - this only restyles the popup chrome.
   Mirrors pages/proto/bk-darkpopup-1.html. The popup BUILDERS already emit
   the .gmaps-popup / .lt-popup classes; this is purely the outer chrome skin,
   so no JS changes. The links / text inside flip via the per-page rules at the
   foot of this file + the popup content stylesheets; this block owns the
   chrome (body fill, border, tail, close-X).
   SCOPE: the stremming + opstap popups inject their OWN light-glass chrome
   (scoped via :has(.gmaps-popup--strem) / :has(.gmaps-popup--opstap)) and are
   deliberately LIGHT on every page in every theme (captain decision). The
   :not(:has(...)) guard below excludes them so this dark flip ONLY catches the
   generic route-highlight .gmaps-popup InfoWindow, never their self-styled
   bubbles - independent of stylesheet injection order. */
:root[data-theme="dark"] .gm-style .gm-style-iw-c:not(:has(.gmaps-popup--strem)):not(:has(.gmaps-popup--opstap)){
  /* Commit K: this rule sits under [data-theme="dark"], so the bb-glass
     tokens resolve to their DARK-theme values from shared-prototype.css
     (bg navy 0.74, blur 22px, cyan hairline 0.30, shadow 0.46). Deltas vs
     the old literals: blur 24 -> 22px, border 0.28 -> 0.30, shadow 0.50
     -> 0.46. Fallbacks carry the dark token values. */
  background:var(--bb-glass-bg, rgba(9,28,53,0.74))!important;
  backdrop-filter:var(--bb-glass-blur, blur(22px) saturate(180%))!important;
  -webkit-backdrop-filter:var(--bb-glass-blur, blur(22px) saturate(180%))!important;
  border:1px solid var(--bb-glass-border, rgba(105,254,255,0.30))!important;
  box-shadow:var(--bb-glass-shadow, 0 18px 48px rgba(0,0,0,0.46),
             inset 0 1px 0 rgba(255,255,255,0.08))!important;
}
.gm-style-iw-d{
  max-width:none!important;overflow-x:hidden!important;padding:0!important;
  /* Hide the scrollbar on the inner d-container (Google adds overflow:auto
     when content exceeds the popup height) - it was rendering as a white
     vertical bar on the right edge of the glass body, breaking the look. */
  scrollbar-width:none;
}
.gm-style-iw-d::-webkit-scrollbar{display:none}
/* Same scrollbar reset on the outer popup wrappers per page so neither
   the standard tour's .gmaps-popup nor the live tour's .lt-popup ever
   shows a white bar on the right edge. */
.gmaps-popup, .lt-popup{scrollbar-width:none}
.gmaps-popup::-webkit-scrollbar, .lt-popup::-webkit-scrollbar{display:none}

/* Style Google's tail/pointer (the small triangle pointing at the marker)
   so it matches the glass body. .gm-style-iw-tc is the tail container
   in modern InfoWindow chrome. */
.gm-style .gm-style-iw-tc::after{
  background:var(--bb-glass-bg, rgba(255,255,255,0.66))!important;
  box-shadow:none!important;
}
/* Dark-theme tail matches the navy-glass body fill. The strem/opstap tails
   are styled light by their own injected chrome (and excluded here via the
   sibling :has guard on the container above); their tail container does not
   carry the popup class, so we cannot :not(:has()) it directly - but their
   own tail rule is !important + injected later, so it wins the tie for those
   bubbles. This generic dark tail therefore only ends up applied to the
   route-highlight popup. */
:root[data-theme="dark"] .gm-style .gm-style-iw-tc::after{
  background:var(--bb-glass-bg, rgba(9,28,53,0.74))!important;
}
@supports not (backdrop-filter: blur(1px)){
  .gm-style .gm-style-iw-c{background:rgba(255,255,255,0.94)!important}
  .gm-style .gm-style-iw-tc::after{background:rgba(255,255,255,0.94)!important}
  :root[data-theme="dark"] .gm-style .gm-style-iw-c{background:rgba(9,28,53,0.96)!important}
  :root[data-theme="dark"] .gm-style .gm-style-iw-tc::after{background:rgba(9,28,53,0.96)!important}
}

/* Close-X chrome row repositioned to overlay the image as a small white
   circle (28 px) at top-right. Same rule for both popup types. */
.gm-style-iw-chr{
  position:absolute!important;top:8px!important;right:8px!important;
  z-index:2;min-height:0!important;padding:0!important;
}
.gm-style-iw-chr .gm-ui-hover-effect{
  width:28px!important;height:28px!important;
  background:rgba(255,255,255,0.92)!important;border-radius:50%!important;
  box-shadow:0 1px 3px rgba(0,0,0,0.25)!important;
  display:flex!important;align-items:center!important;justify-content:center!important;
  opacity:1!important;
  /* Hover anim: subtle rotate + scale + crimson tint so it feels native.
     280ms cubic ease matches the lang-toggle slide elsewhere. Touch users
     get the active-state on tap-down for instant feedback. */
  transition:background 200ms ease, transform 280ms cubic-bezier(.2,.7,.3,1.4), box-shadow 200ms ease!important;
}
.gm-style-iw-chr .gm-ui-hover-effect:hover{
  background:#FFE8E4!important;
  transform:rotate(90deg) scale(1.08)!important;
  box-shadow:0 2px 6px rgba(255,80,60,0.28)!important;
}
.gm-style-iw-chr .gm-ui-hover-effect:active{
  transform:rotate(90deg) scale(0.94)!important;
  transition-duration:80ms!important;
}
.gm-style-iw-chr .gm-ui-hover-effect:focus-visible{
  outline:2px solid #FF503C!important;outline-offset:2px!important;
}
.gm-style-iw-chr .gm-ui-hover-effect span{
  width:14px!important;height:14px!important;margin:0!important;
  transition:background 200ms ease!important;
}
.gm-style-iw-chr .gm-ui-hover-effect:hover span{
  background:#8A1C0F!important;
}

/* ── Dark-theme (Deep Navy Glass) close-X ─────────────────────────────────
   The light disc + navy glyph above flips to a frosted NAVY disc with a light
   hairline + a near-white glyph, tinting red on hover. The glyph element is
   the inner <span> that Google paints as the X mask; we recolour it light,
   and to red on hover.
   Scoped under the same :not(:has(...)) container guard as the chrome so the
   self-styled strem / opstap close-X (light, injected later) is untouched. */
:root[data-theme="dark"] .gm-style-iw-c:not(:has(.gmaps-popup--strem)):not(:has(.gmaps-popup--opstap)) .gm-style-iw-chr .gm-ui-hover-effect{
  background:rgba(9,28,53,0.55)!important;
  border:1px solid rgba(255,255,255,0.16)!important;
  box-shadow:0 1px 3px rgba(0,0,0,0.4)!important;
  -webkit-backdrop-filter:blur(8px)!important;backdrop-filter:blur(8px)!important;
}
:root[data-theme="dark"] .gm-style-iw-c:not(:has(.gmaps-popup--strem)):not(:has(.gmaps-popup--opstap)) .gm-style-iw-chr .gm-ui-hover-effect span{
  background:rgba(243,246,249,0.78)!important;
}
:root[data-theme="dark"] .gm-style-iw-c:not(:has(.gmaps-popup--strem)):not(:has(.gmaps-popup--opstap)) .gm-style-iw-chr .gm-ui-hover-effect:hover{
  background:rgba(255,80,60,0.30)!important;
  box-shadow:0 2px 6px rgba(255,80,60,0.34)!important;
}
:root[data-theme="dark"] .gm-style-iw-c:not(:has(.gmaps-popup--strem)):not(:has(.gmaps-popup--opstap)) .gm-style-iw-chr .gm-ui-hover-effect:hover span{
  background:#FFFFFF!important;
}

/* ── Stremming (waterway-closure) InfoWindow ─────────────────────────────
   2026-05-31: the stremming popup is now the SAME light Liquid-Glass card as
   the route-highlights popup (captain: "visueel hetzelfde als bij route
   highlights"). Its complete style - light white-glass chrome + navy ink +
   brand-blue links - is shipped self-contained by live-stremmingen.js
   injectGlassChrome(), scoped via :has(.gmaps-popup--strem) so it renders
   identically on /vaarinfo (which does NOT load this file), /vaarverkeer and
   the live tour. Edit the stremming popup in live-stremmingen.js, not here.
   (Same pattern as the opstap popup below.) */

/* ── Opstap / doorvaartprofiel (ABC pickup) InfoWindow ───────────────────
   The opstap popup (src/live/live-opstap.js) reuses the SAME .gmaps-popup
   structure + dark navy Liquid Glass as the stremming popup above, so all
   three map popups read as one component (captain complaint, 2026-05-31:
   the opstap card was the last white-card popup).

   Its styles are NOT defined here. They live in a one-time injected
   <style id="opstap-glass-chrome"> shipped by live-opstap.js
   injectGlassChrome(), because /vaarinfo - one of the three pages that show
   this popup - does NOT load shared-overlays.css; shipping the styles with
   the JS makes the popup self-sufficient on every page. The outer Google
   chrome override is scoped via :has(.gmaps-popup--opstap) so it darkens
   ONLY the opstap bubble, never the LIGHT route popup that shares the map
   on the standard tour; the content classes are scoped on
   .gmaps-popup--opstap directly for the same reason. Edit them in
   live-opstap.js, not here. */

/* ── Generic .gmaps-popup CONTENT text colours (light + dark) ────────────
   2026-06-17: the route-highlight popup content text colours used to live
   ONLY in src/tour/tour.css. That file is loaded by index.html + vaarverkeer
   .html but NOT by live.html (which loads live-tour.css instead). The VESSEL
   popup (.gmaps-popup--vessel, built by live-boats-overlay.js) renders on ALL
   THREE pages, and the PLACE popup (.gmaps-popup--place, built by tour.js)
   renders on index.html. Those builders had their text colours hardcoded
   inline (navy #091C35 / brown #5C3A00 / blue #3366FF); the inline colours
   were stripped so the stylesheet can flip them in dark theme - but on
   live.html there was no .pp-* colour rule at all, so the vessel text fell
   back to black on the navy glass (unreadable). Mirror the tour.css content
   colours here so every page that shows a .gmaps-popup gets readable ink in
   BOTH themes. Loaded by index.html + vaarverkeer.html + live.html.

   Scope: bare .gmaps-popup only (the route / vessel / place bubbles). The
   strem + opstap bubbles inject their OWN content colours and stay LIGHT in
   every theme, so the :not(--strem):not(--opstap) guard excludes them. Values
   match tour.css exactly so on index.html / vaarverkeer.html (which load both
   files) there is no conflict regardless of link order. Colour Rule tokens:
   navy ink / Backbase-blue accent in light, near-white ink / cyan accent on
   the navy glass in dark. */
.gmaps-popup:not(.gmaps-popup--strem):not(.gmaps-popup--opstap) strong{ color:var(--navy,#091C35); }
.gmaps-popup:not(.gmaps-popup--strem):not(.gmaps-popup--opstap) .pp-sub{ color:var(--accent,#3366FF); }
.gmaps-popup:not(.gmaps-popup--strem):not(.gmaps-popup--opstap) .pp-detail{ color:#4B5563; }
.gmaps-popup:not(.gmaps-popup--strem):not(.gmaps-popup--opstap) a.pp-link{ color:var(--blue,#3366FF); }

:root[data-theme="dark"] .gmaps-popup:not(.gmaps-popup--strem):not(.gmaps-popup--opstap) strong{ color:rgba(243,246,249,0.96); }
:root[data-theme="dark"] .gmaps-popup:not(.gmaps-popup--strem):not(.gmaps-popup--opstap) .pp-sub{ color:var(--bb-cyan,#69FEFF); }
:root[data-theme="dark"] .gmaps-popup:not(.gmaps-popup--strem):not(.gmaps-popup--opstap) .pp-detail{ color:rgba(243,246,249,0.70); }
:root[data-theme="dark"] .gmaps-popup:not(.gmaps-popup--strem):not(.gmaps-popup--opstap) a.pp-link{ color:var(--bb-cyan,#69FEFF); }

/* ── .gmaps-popup CONTENT layout (single owner) ──────────────────────────
   Commit K (2026-07-06): moved here from src/tour/tour.css (plus the
   mirrored subset that lived in src/fun-facts/fun-facts.css) so this file
   is the SINGLE owner of every .gmaps-popup content rule. The text COLOURS
   already live in the guarded blocks above; these are the layout /
   typography rules. The var() fallbacks (#091C35 navy / #3366FF blue)
   cover pages whose stylesheet does not define the legacy --navy / --blue
   aliases (e.g. live.html). The strem + opstap popups ship their own
   injected styles and simply inherit this shared layout skeleton, exactly
   as they did when it lived in tour.css. */
/* Popup: natural height, no scroll unless the content actually overflows
   the safety max-height (captain feedback 2026-05-29: flex + sticky made
   the popup always claim the full 52vh). */
.gmaps-popup{font-family:var(--font,'Libre Franklin',-apple-system,sans-serif);line-height:1.5;width:min(312px,calc(100vw - 90px));max-height:52vh;overflow-y:auto;position:relative}
.gmaps-popup{padding:0 0 12px!important}
/* ── Outer InfoWindow chrome height cap (standard tour only) ─────────────
   2026-06-17 (captain bug #1): a tall route-highlight popup could exceed
   the bottom Highlights sheet and clip behind it, because the inner
   .gmaps-popup cap (52vh above) caps the CONTENT but Google's OUTER chrome
   was uncapped. Cap the outer chrome so the popup ALWAYS fits the band.
   !important beats Google's own inline max-height. Inner 52vh < d 58vh <
   c 62vh so the caps cooperate.
   SCOPED to body.is-home (index.html + the route-detail-glass POC) so it
   never touches vaarverkeer / live / boat-info, which lack that class. */
body.is-home .gm-style-iw-c{max-height:62vh!important}
body.is-home .gm-style-iw-d{max-height:58vh!important;overflow-y:auto!important}
/* Image banner: 2:1 crop (2026-05-29 captain "iets hoger"), flush at the
   top edge-to-edge. */
.gmaps-popup img{width:100%;aspect-ratio:2/1;height:auto;object-fit:cover;object-position:center;display:block;margin-bottom:10px;border-radius:8px 8px 0 0}
/* Text content needs its own horizontal inset since .gm-style-iw-d's
   padding is 0; the image above stretches full-width. */
.gmaps-popup strong, .gmaps-popup .pp-sub, .gmaps-popup .pp-detail,
.gmaps-popup .pp-link, .gmaps-popup .pp-links{padding-left:12px;padding-right:12px}
/* text-wrap:balance keeps short title tails ("... suite #18") off their
   own line (captain feedback 2026-05-28). Colour comes from the guarded
   content-colour rules above. */
.gmaps-popup strong{display:block;text-wrap:balance;hyphens:none;word-break:normal;font-size:0.95rem}
.gmaps-popup .pp-sub{font-size:0.7rem;text-transform:uppercase;letter-spacing:0.06em;font-weight:600;margin:4px 0 8px}
.gmaps-popup .pp-detail{font-size:0.8125rem}
/* Stop-NUMBER badge in the popup header (captain 2026-06-15). Navy pin
   look with a cyan ring; .pp-head carries the 12px inset so the badge
   lines up with the title. */
.gmaps-popup .pp-head{display:flex;align-items:flex-start;gap:8px;padding-left:12px;padding-right:12px}
.gmaps-popup .pp-head strong{padding-left:0;padding-right:0;flex:1 1 auto;min-width:0}
.gmaps-popup .pp-num{flex:0 0 auto;display:inline-grid;place-items:center;width:26px;height:26px;margin-top:1px;border-radius:50%;background:var(--navy,#091C35);color:#fff;border:1.5px solid var(--bb-cyan,#69FEFF);font-size:0.8rem;font-weight:600;line-height:1}
.gmaps-popup a.pp-link{display:inline-block;margin-top:8px;font-size:0.7rem;text-transform:uppercase;letter-spacing:0.06em;font-weight:600}
/* On-demand narration player (captain 2026-07-15): a soft-tint "Luister/Listen"
   pill with a play/pause disc and a thin progress bar at its foot. Light-tint +
   navy text in light mode; navy-glass + cyan accent in dark (matches the popup
   source-icon skin above). */
/* Rij met de volumeknop naast de Luister-pill. De pill hield zijn eigen
   marges, dus de rij neemt die over en de pill wordt daarbinnen marge-loos -
   anders springt hij 12px naar rechts weg van de knop. */
.gmaps-popup .pp-audio-row{
  display:flex;align-items:center;gap:8px;
  margin:10px 12px 2px;
}
.gmaps-popup .pp-audio-row .pp-audio{ margin:0; width:auto; flex:1 1 auto; }
.gmaps-popup .pp-vol{
  flex:0 0 auto;display:inline-grid;place-items:center;
  width:38px;height:38px;padding:0;
  border:0;border-radius:12px;cursor:pointer;
  background:var(--bb-light-blue,#E5EBFF);color:var(--navy,#091C35);
  line-height:1;
}
.gmaps-popup .pp-vol[aria-expanded="true"]{background:rgba(51,102,255,0.22)}
.gmaps-popup .pp-vol:active{transform:scale(0.94)}
:root[data-theme="dark"] .gmaps-popup .pp-vol{
  background:rgba(255,255,255,0.06);color:rgba(243,246,249,0.92);
}
.gmaps-popup .pp-audio{
  display:flex;align-items:center;gap:10px;
  margin:10px 12px 2px;width:calc(100% - 24px);box-sizing:border-box;
  padding:9px 12px;border:none;border-radius:12px;cursor:pointer;
  background:var(--bb-light-blue,#E5EBFF);color:var(--navy,#091C35);
  font:600 0.82rem/1.2 inherit;text-align:left;position:relative;overflow:hidden;
}
.gmaps-popup .pp-audio .pp-audio-ico{
  flex:0 0 auto;display:inline-grid;place-items:center;
  width:26px;height:26px;border-radius:50%;
  background:var(--blue,#3366FF);color:#fff;font-size:0.66rem;line-height:1;
}
.gmaps-popup .pp-audio .pp-audio-ico::before{content:"\25B6";margin-left:1px}
.gmaps-popup .pp-audio.is-playing .pp-audio-ico::before{content:"\23F8";margin-left:0}
.gmaps-popup .pp-audio .pp-audio-txt{flex:1 1 auto}
.gmaps-popup .pp-audio .pp-audio-bar{position:absolute;left:0;right:0;bottom:0;height:3px;background:rgba(51,102,255,0.16)}
.gmaps-popup .pp-audio .pp-audio-fill{display:block;height:100%;width:0%;background:var(--blue,#3366FF);transition:width 0.15s linear}
:root[data-theme="dark"] .gmaps-popup .pp-audio{background:rgba(255,255,255,0.06);color:rgba(243,246,249,0.92)}
:root[data-theme="dark"] .gmaps-popup .pp-audio .pp-audio-ico{background:var(--bb-cyan,#69FEFF);color:var(--navy,#091C35)}
:root[data-theme="dark"] .gmaps-popup .pp-audio .pp-audio-bar{background:rgba(105,254,255,0.16)}
:root[data-theme="dark"] .gmaps-popup .pp-audio .pp-audio-fill{background:var(--bb-cyan,#69FEFF)}
/* Source-icon discs in DARK theme: navy-glass chip + cyan glyph (D2 V1).
   The LIGHT .pp-icon-link base rules stay in tour.css - they also skin the
   side-panel stop cards there (moreInfoLink()), outside any popup. */
:root[data-theme="dark"] .gmaps-popup:not(.gmaps-popup--strem):not(.gmaps-popup--opstap) .pp-icon-link{
  background:rgba(255,255,255,0.06);
  border:1px solid rgba(255,255,255,0.12);
  color:rgba(243,246,249,0.78);
}
:root[data-theme="dark"] .gmaps-popup:not(.gmaps-popup--strem):not(.gmaps-popup--opstap) .pp-icon-link:hover,
:root[data-theme="dark"] .gmaps-popup:not(.gmaps-popup--strem):not(.gmaps-popup--opstap) .pp-icon-link:focus-visible{
  background:rgba(105,254,255,0.16);
  color:var(--bb-cyan,#69FEFF);
}

/* ── Offline map placeholder (#266) ──────────────────────────────────────
   Google Maps JS tiles cannot be cached (Google ToS + heavy bloat), so when
   the device is offline we cover the blank/broken map canvas with a clean
   on-brand frosted card rather than an empty grey pane. Used on index.html
   (#mapOffline over #mapContainer) + live.html (#ltMapOffline over #ltMap);
   vaarinfo / vaarverkeer have their own .vi-/.vv-map-error cards. The class is
   BEM-namespaced (bb-map-offline*) so it never cascades into a generic shared
   selector (CSS Cascade Scoping Rule). Hidden via [hidden]; JS toggles it on
   the online/offline events + at boot. Colour Rule: light-blue tint card with
   navy ink in light mode, navy frosted glass in dark mode - no saturated fill,
   no left-accent ribbon. */
.bb-map-offline{
  position:absolute; inset:0; z-index:6;
  display:flex; align-items:center; justify-content:center;
  padding:24px;
  pointer-events:none; /* let map gestures through if it ever shows over a live map */
}
.bb-map-offline[hidden]{ display:none; }
.bb-map-offline-card{
  pointer-events:auto;
  max-width:320px;
  display:flex; flex-direction:column; align-items:center; gap:8px;
  text-align:center;
  padding:22px 24px;
  border-radius:16px;
  background:rgba(229,235,255,0.86);
  border:1px solid rgba(9,28,53,0.10);
  box-shadow:0 12px 36px rgba(9,28,53,0.16);
  -webkit-backdrop-filter:blur(18px) saturate(160%);
  backdrop-filter:blur(18px) saturate(160%);
  color:#091C35;
  font-family:'Libre Franklin', system-ui, sans-serif;
}
.bb-map-offline-card svg{
  stroke:#3366FF; fill:none; stroke-width:1.6;
  stroke-linecap:round; stroke-linejoin:round;
  opacity:0.85;
}
.bb-map-offline-card strong{
  font-size:15px; font-weight:600; letter-spacing:-0.01em;
}
.bb-map-offline-card span{
  font-size:12.5px; line-height:1.45; color:rgba(9,28,53,0.70);
}
html[data-theme="dark"] .bb-map-offline-card{
  background:rgba(14,34,64,0.82);
  border-color:rgba(105,254,255,0.18);
  color:#F3F6F9;
}
html[data-theme="dark"] .bb-map-offline-card svg{ stroke:#69FEFF; }
html[data-theme="dark"] .bb-map-offline-card span{ color:rgba(243,246,249,0.72); }

/* ════════════════════════════════════════════════════════════════════
   KAART-CHROME: losse knoppen boven een volledig-scherm kaart (P3,
   captain 2026-07-26, rapport docs/reports/topbar-merge-20260726 sectie 6).

   Dezelfde interactietaal als de live tour: geen balk boven de kaart,
   maar een verticale stack met 40px-knoppen rechtsboven (tandwiel eerst,
   dan de taal-knop) en - waar er geen bottom-nav is om op terug te
   vallen - een losse terug-chip linksboven. De live tour heeft zijn
   eigen kopie van dit recept in pages/live.html (body.lt-proto-scoped);
   deze klassen dragen een eigen prefix zodat de twee elkaar nooit raken.

   Klassen bewust NIET generiek (CSS Cascade Scoping Rule): .bb-mapfab*
   komt alleen voor op de kaartpagina's die hem opzettelijk gebruiken.
   ════════════════════════════════════════════════════════════════════ */
:root{
  --bb-mapfab-size: 40px;
  --bb-mapfab-gap: 10px;
  --bb-mapfab-bg: rgba(255, 255, 255, 0.92);
  --bb-mapfab-bg-active: #E5EBFF;
  --bb-mapfab-border: rgba(9, 28, 53, 0.10);
  --bb-mapfab-shadow: 0 4px 12px rgba(9, 28, 53, 0.22);
  /* Basis-offset: identiek aan --lt-top-base op de live tour, inclusief de
     app-shell-inset die pages/app.html per toestel doorgeeft. */
  --bb-mapfab-top: max(12px, var(--bb-safe-top), var(--bb-embed-safe-top, 0px));
}
/* De kaartpagina's dragen hun eigen chrome, dus de paginakop verdwijnt zodra
   deze module gemount is. In de app-shell verbergt embed() die kop al; deze
   regel dekt de STANDALONE-URL (/vaarinfo, /vaarverkeer) zodat er niet twee
   lagen chrome over elkaar staan (gemeten 2026-07-26: het tandwiel lag op de
   header). De bottom-tabs blijven de navigatie op mobiel; op desktop staat
   linksboven de terug-chip. */
html.bb-mapchrome .bb-header{ display: none; }

/* Waarom .bb-mapfab.bb-mapfab (dezelfde klasse twee keer): de live tour geeft
   zijn stack-knoppen OOK .lt-mini-btn / .lt-title-logo, en de terug-chip
   .bb-rd-back. Dat zijn enkele klassen uit stylesheets die NA deze geladen
   worden, dus bij gelijke specificiteit wonnen zij. De dubbele vorm tilt dit
   recept naar (0,2,0) - hoger dan die enkele klassen, lager dan een
   pagina-scope als `body.lt-proto .x` (0,2,1), zodat een pagina nog steeds
   bewust kan afwijken. */

.bb-mapfab-stack.bb-mapfab-stack{
  position: fixed;
  right: 12px;
  top: var(--bb-mapfab-top);
  z-index: 1090;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--bb-mapfab-gap);
  pointer-events: none;   /* container klikt door; kinderen zetten hem terug */
}
.bb-mapfab-stack.bb-mapfab-stack > *{ pointer-events: auto; }
.bb-mapfab.bb-mapfab{
  position: relative;
  width: var(--bb-mapfab-size); height: var(--bb-mapfab-size);
  margin: 0; padding: 0;
  border-radius: 50%;
  background: var(--bb-mapfab-bg);
  -webkit-backdrop-filter: blur(20px) saturate(160%);
  backdrop-filter: blur(20px) saturate(160%);
  border: 1px solid var(--bb-mapfab-border);
  box-shadow: var(--bb-mapfab-shadow);
  color: var(--bb-navy);
  display: inline-flex; align-items: center; justify-content: center;
  cursor: pointer;
  transition: background-color 160ms ease, border-color 160ms ease, transform 160ms ease;
}
/* 44px hit-area zonder de visuele 40px aan te tasten (HIG). */
.bb-mapfab.bb-mapfab::after{ content: ""; position: absolute; inset: -2px; border-radius: 50%; }
.bb-mapfab.bb-mapfab:hover{ background: #fff; }
.bb-mapfab.bb-mapfab:active{ transform: scale(0.92); }
.bb-mapfab.bb-mapfab:focus-visible{ outline: 2px solid var(--bb-cyan); outline-offset: 2px; }
.bb-mapfab.bb-mapfab[aria-expanded="true"]{ background: var(--bb-mapfab-bg-active); border-color: var(--bb-blue); }
.bb-mapfab.bb-mapfab svg{ width: 18px; height: 18px; display: block; stroke: var(--bb-navy); fill: none; }
/* De taalcode is een BADGE onder de wereldbol (captain 2026-07-26, variant
   G3): het icoon zegt "taal", de badge zegt "welke". Licht-blauwe tint met
   navy inkt en een witte rand - hetzelfde recept als de live-dot op het
   tandwiel, zodat de knoppen in de stack met elkaar rijmen. Hij steekt 5px
   onder de knop uit; met 10px stack-ruimte blijft er 5px lucht naar de knop
   eronder (gemeten). */
.bb-mapfab-code{
  position: absolute;
  left: 50%; bottom: -5px;
  transform: translateX(-50%);
  display: flex; align-items: center; justify-content: center;
  height: 15px; min-width: 15px; padding: 0 3.5px;
  box-sizing: border-box;
  border-radius: 999px;
  border: 2px solid #fff;
  background: var(--bb-light-blue, #E5EBFF);
  color: var(--bb-navy);
  font: 800 9px/1 'Libre Franklin', system-ui, sans-serif;
  letter-spacing: 0.03em;
  box-shadow: 0 1px 3px rgba(9, 28, 53, 0.25);
  pointer-events: none;
}
/* Groene live-dot linksboven op de knop; vervangt de "Live"-pil uit de kop. */
.bb-mapfab-dot{
  position: absolute;
  left: -2px; top: -2px;
  width: 10px; height: 10px;
  border-radius: 50%;
  border: 2px solid #fff;
  background: #0F7B4F;
  pointer-events: none;
}
.bb-mapfab-dot[hidden]{ display: none; }

/* Losse terug-chip linksboven: hetzelfde .bb-rd-back-glasrecept als de
   live tour, 6px groter zodat hij even zwaar weegt als de stack. */
.bb-mapback.bb-mapback{
  position: fixed;
  left: 12px;
  top: var(--bb-mapfab-top);
  z-index: 1090;
  width: var(--bb-mapfab-size); height: var(--bb-mapfab-size);
  display: inline-flex;
}
.bb-mapback.bb-mapback::after{ content: ""; position: absolute; inset: -2px; border-radius: 50%; }
.bb-mapback.bb-mapback svg{ width: 19px; height: 19px; }

/* Het paneel onder een knop: exact het live-tour-recept (300px navy-glas,
   radius 18, rechts uitgelijnd onder de knop, intern scrollend). */
.bb-mapfab-wrap{ position: relative; display: inline-flex; }
/* Een knop met een code-badge steekt 5px onder zijn eigen rand uit. Zonder
   compensatie werd de optische ruimte naar de volgende knop 6px terwijl de
   rest van de stack 10px aanhoudt (gemeten 2026-07-26); die 5px komen er dus
   onder bij, zodat het ritme klopt. */
.bb-mapfab-wrap:has(.bb-mapfab-code){ margin-bottom: 5px; }
.bb-mapfab-panel{
  position: absolute;
  /* Opent NAAST de stapel, niet eronder. De stapel is een kolom en het
     paneel hangt aan de bovenste knop, dus met `top:calc(100% + 6px)`
     viel het over alle knoppen eronder heen - op Vaarinfo verdween de
     NL-taalknop er volledig achter (audit 2026-07-28, gereproduceerd in
     de echte app). Links uitklappen houdt elke knop bereikbaar terwijl
     het paneel open staat. */
  top: 0;
  right: calc(100% + 8px); left: auto;
  z-index: 1200;
  /* Breedte houdt rekening met de stapel zelf (knop + tussenruimte +
     schermmarge), anders loopt het paneel op smalle toestellen links
     van het scherm af. */
  width: min(300px, calc(100vw - 80px));
  max-height: calc(100dvh - var(--bb-mapfab-top) - 48px - var(--bb-safe-bottom) - 24px);
  overflow-y: auto;
  overscroll-behavior: contain;
  background: rgba(9, 28, 53, 0.86);
  -webkit-backdrop-filter: blur(24px) saturate(160%);
  backdrop-filter: blur(24px) saturate(160%);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 18px;
  box-shadow: 0 12px 36px rgba(9, 28, 53, 0.34);
  color: #F3F6F9;
}
.bb-mapfab-panel[hidden]{ display: none; }
.bb-mapfab-panel::-webkit-scrollbar{ width: 0; height: 0; }
/* Kopje benoemt de context, zodat de knop dat niet hoeft te doen. */
.bb-mapfab-panel-head{
  display: flex; align-items: center; gap: 8px;
  padding: 11px 13px 9px;
  font-size: 13px; font-weight: 600;
}
.bb-mapfab-panel-head svg{
  width: 16px; height: 16px; flex: 0 0 auto;
  fill: none; stroke: var(--bb-cyan); stroke-width: 2;
  stroke-linecap: round; stroke-linejoin: round;
}
/* Taalkiezer-rijen (zelfde spec als de live tour, rapport 5d). */
.bb-mapfab-langlist{ padding: 2px 13px 10px; }
.bb-maplang-row{
  display: flex; align-items: center; gap: 10px;
  width: 100%; min-height: 44px;
  padding: 0; margin: 0;
  background: none; border: 0;
  color: rgba(243, 246, 249, 0.92);
  font-family: inherit; text-align: left; cursor: pointer;
}
.bb-maplang-row + .bb-maplang-row{ border-top: 1px solid rgba(255, 255, 255, 0.08); }
.bb-maplang-row:active{ opacity: 0.7; }
.bb-maplang-code{
  flex: 0 0 22px;
  font: 800 10.5px/1 'Libre Franklin', system-ui, sans-serif;
  letter-spacing: 0.04em;
  color: rgba(243, 246, 249, 0.55);
}
.bb-maplang-native{ flex: 1 1 auto; font-size: 13px; font-weight: 500; }
.bb-maplang-check{ flex: 0 0 auto; color: var(--bb-cyan); font-size: 13px; opacity: 0; }
.bb-maplang-row.is-on .bb-maplang-native{ font-weight: 700; }
.bb-maplang-row.is-on .bb-maplang-code{ color: rgba(243, 246, 249, 0.85); }
.bb-maplang-row.is-on .bb-maplang-check{ opacity: 1; }

@media (min-width: 960px){
  .bb-mapfab-stack.bb-mapfab-stack{ right: 24px; }
  .bb-mapback.bb-mapback{ left: 24px; }
}
