/* ==========================================================================
   Jubilee World — interactive chapter globe hero
   A dark, full-bleed banner that sits above the site's light content.
   ========================================================================== */

.globe {
  --globe-accent: #a97bff;
  --globe-accent-dim: rgba(169, 123, 255, 0.42);
  --globe-card: rgba(20, 14, 38, 0.72);

  /* Clears the floating .site-header so the sphere sits below the nav instead
     of behind it: 26px padding, a 300x59 logo, 26px padding = 111px, and 112
     leaves a hairline of sky rather than butting the two together. Mobile
     overrides this (see .globe--mobile), where the header is a different
     height and the canvas is a band rather than the rest of the hero. */
  --stage-top: 112px;

  position: relative;
  min-height: 100vh;
  min-height: 100svh;
  overflow: hidden;
  color: #fff;
  background:
    radial-gradient(120% 90% at 50% 45%, #241750 0%, #12082c 42%, #06030f 100%),
    #06030f;
  isolation: isolate;
}

/* Star field — two tiled dot layers, no image request. */
.globe::before {
  content: "";
  position: absolute;
  inset: -20%;
  z-index: 0;
  background-image:
    radial-gradient(1.4px 1.4px at 20% 30%, rgba(255,255,255,0.75) 50%, transparent 50%),
    radial-gradient(1px 1px at 70% 65%, rgba(255,255,255,0.55) 50%, transparent 50%),
    radial-gradient(1.6px 1.6px at 45% 80%, rgba(214,196,255,0.6) 50%, transparent 50%);
  background-size: 260px 260px, 170px 170px, 340px 340px;
  opacity: 0.7;
  animation: globe-drift 240s linear infinite;
}

@keyframes globe-drift {
  to { transform: translate3d(-260px, -170px, 0); }
}

.globe__canvas {
  position: absolute;
  inset: 0;
  z-index: 1;
  width: 100%;
  height: 100%;
  display: block;
}

/* The whole banner is draggable, so the grab affordance lives on the root.
   Selection is disabled here: the hero is a drag surface, and without this a
   spin gesture leaves the headline and card text highlighted. */
.globe--live {
  cursor: grab;
  touch-action: pan-y;
  user-select: none;
  -webkit-user-select: none;
}
.globe--live.is-dragging { cursor: grabbing; }
.globe--live.is-dragging .globe__pin-label { opacity: 0.35; }
.globe--live .chapter,
.globe--live .globe__pin { cursor: pointer; }

.globe--static .globe__canvas { display: none; }

/* Connector arcs sit above the globe but below pins and cards. */
.globe__links {
  position: absolute;
  inset: 0;
  z-index: 2;
  width: 100%;
  height: 100%;
  pointer-events: none;
  overflow: visible;
}

.globe__link {
  fill: none;
  stroke: var(--globe-accent-dim);
  stroke-width: 1.25;
  transition: stroke 0.3s ease, stroke-width 0.3s ease;
}
.globe__link.is-active {
  stroke: var(--globe-accent);
  stroke-width: 2;
}
.globe__link.is-behind { opacity: 0 !important; }

.globe--static .globe__links { display: none; }

/* ---------------------------------------------------------------- pins */

.globe__pins {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
}

/* The globe starts below the header, not behind it.
 *
 * These three layers were each inset:0 over the whole hero, which put the top
 * limb of the sphere — the brightest part of it, and the pins nearest the pole —
 * behind the nav links. Dropping the box's top edge to --stage-top solves that
 * at every viewport height, which tuning the camera distance does not: the
 * header is a fixed 111px while the hero is 100svh, so a distance that clears it
 * on a 900px-tall window is a few pixels short on a 700px one.
 *
 * All three move together and that is not optional — globe.js measures the
 * canvas and derives every pin coordinate from it, so a pin layer on a different
 * box slides the pins off the sphere. It is the same rule .globe--mobile follows
 * further down, for the same reason.
 *
 * The sphere ends up about 30px lower in the frame than it was, so the headline
 * anchored to the bottom of the hero overlaps a little more of it. That is the
 * trade this makes deliberately: the alternative is a smaller globe. */
.globe__canvas,
.globe__links,
.globe__pins {
  top: var(--stage-top);
  bottom: 0;
  height: auto;
}

/* The dot welded to the earth's surface at the exact coordinate. */
.globe__base {
  position: absolute;
  top: 0;
  left: 0;
  width: 7px;
  height: 7px;
  margin: -3.5px 0 0 -3.5px;
  border-radius: 50%;
  background: var(--globe-accent);
  box-shadow: 0 0 10px 2px rgba(169, 123, 255, 0.9);
  will-change: transform, opacity;
}
.globe__base.is-behind { opacity: 0 !important; }

/* The mast standing radially off the globe. */
.globe__stalk {
  stroke: rgba(169, 123, 255, 0.85);
  stroke-width: 1.8;   /* holds its own under the larger pin head */
  stroke-linecap: round;
  transition: stroke 0.3s ease, stroke-width 0.3s ease;
}
.globe__stalk.is-behind { opacity: 0 !important; }

/* The head at the tip of the mast. The margin is always half the size, so the
   dot stays centred on the point it marks. */
.globe__pin {
  position: absolute;
  top: 0;
  left: 0;
  width: 17px;
  height: 17px;
  margin: -8.5px 0 0 -8.5px;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: #fff;
  /* The innermost hairline is what keeps a white pin readable where it crosses
     cloud or desert on the daylit earth; against space it is invisible. */
  box-shadow:
    0 0 0 1.5px rgba(6, 3, 15, 0.55),
    0 0 0 3.5px rgba(169, 123, 255, 0.35),
    0 0 14px 3px rgba(169, 123, 255, 0.85);
  pointer-events: auto;
  cursor: pointer;
  transition: box-shadow 0.3s ease, background 0.3s ease;
  will-change: transform, opacity;
}

/* City name riding on the pin head, so the label and the point on the
   globe are unmistakably the same place. */
/* The chip carries the city's own treatment — 12px, 700, full white, the rail
   card's .chapter__city one size down — and .globe__pin-region below steps the
   country back from it. It was one flat weight at 0.92 white, which read as a
   single run of text where the card it belongs to has always had a bright name
   and a quiet country under it. */
.globe__pin-label {
  position: absolute;
  top: 50%;
  left: 24px;
  transform: translateY(-50%);
  padding: 4px 9px;
  border-radius: 4px;
  background: rgba(10, 5, 26, 0.72);
  font-family: var(--f-nav);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.5px;
  text-transform: uppercase;
  white-space: nowrap;
  color: #fff;
  transition: color 0.3s ease, background 0.3s ease;
}

/* Smaller, lighter, and one step back — .chapter__region's relationship to
   .chapter__city, in a chip instead of a stack.
 *
 * `opacity`, not a colour: the chip's own colour changes with state (#fff on
 * the HQ pin and on the active one, and the whole layer drops to 0.35 while the
 * globe is being dragged), and an explicit colour here would sit out every one
 * of those. An opacity keeps the country exactly one step behind the city in
 * all of them. */
.globe__pin-region {
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 1.3px;
  opacity: 0.62;
}
.globe__pin--label-left .globe__pin-label {
  left: auto;
  right: 24px;
}

.globe__pin--hq .globe__pin-label {
  background: rgba(75, 41, 123, 0.9);
  color: #fff;
}

.globe__pin.is-active .globe__pin-label {
  background: var(--c-purple);
  color: #fff;
}
.globe__pin.is-active ~ .globe__stalk,
.globe__stalk.is-active { stroke: var(--globe-accent); stroke-width: 2.2; }

.globe__pin::after {
  content: "";
  position: absolute;
  inset: -11px;
  border-radius: 50%;
  border: 1px solid rgba(169, 123, 255, 0.55);
  opacity: 0;
  transform: scale(0.6);
}

.globe__pin--hq::after,
.globe__pin.is-active::after {
  opacity: 1;
  animation: globe-ping 2.4s ease-out infinite;
}

@keyframes globe-ping {
  0%   { opacity: 0.9; transform: scale(0.6); }
  70%  { opacity: 0;   transform: scale(1.9); }
  100% { opacity: 0;   transform: scale(1.9); }
}

.globe__pin.is-active {
  background: var(--globe-accent);
  box-shadow:
    0 0 0 4px rgba(169, 123, 255, 0.45),
    0 0 22px 6px rgba(169, 123, 255, 0.95);
}
.globe__pin.is-behind { pointer-events: none; }

/* ---- Non-core chapters -------------------------------------------------
   Pin only, no card and no mast. They sit flush on the surface and keep
   their name hidden until pointed at, so twenty of them can share the globe
   without turning it into a wall of text. */
.globe__base--minor {
  width: 6px;
  height: 6px;
  margin: -3px 0 0 -3px;
  background: rgba(214, 196, 255, 0.9);
  box-shadow: 0 0 0 1px rgba(6, 3, 15, 0.45), 0 0 7px 1.5px rgba(169, 123, 255, 0.7);
}

/* Held at a higher opacity than the night globe needed: at 0.55 these
   disappeared over the bright parts of the daylit map. */
.globe__pin--minor {
  width: 12px;
  height: 12px;
  margin: -6px 0 0 -6px;
  background: rgba(255, 255, 255, 0.82);
  box-shadow:
    0 0 0 1px rgba(6, 3, 15, 0.5),
    0 0 0 2.5px rgba(169, 123, 255, 0.22),
    0 0 9px 2px rgba(169, 123, 255, 0.5);
}
.globe__pin--minor:hover,
.globe__pin--minor:focus-visible,
.globe__pin--minor.is-active {
  background: #fff;
  box-shadow: 0 0 0 3px rgba(169, 123, 255, 0.4), 0 0 18px 5px rgba(169, 123, 255, 0.9);
}

.globe__pin--minor .globe__pin-label {
  opacity: 0;
  transform: translateY(-50%) scale(0.94);
  transition: opacity 0.2s ease, transform 0.2s ease;
  pointer-events: none;
}
.globe__pin--minor:hover .globe__pin-label,
.globe__pin--minor:focus-visible .globe__pin-label,
.globe__pin--minor.is-active .globe__pin-label {
  opacity: 1;
  transform: translateY(-50%) scale(1);
}

/* While the camera dives toward a pin, quiet everything that isn't the
   destination so the arrival reads as a move, not a jumble. */
.globe.is-flying .globe__rail,
.globe.is-flying .globe__copy { opacity: 0; transition: opacity 0.5s var(--ease); }
.globe.is-flying .globe__link,
.globe.is-flying .globe__pin:not(.is-active) { opacity: 0 !important; }
.globe.is-flying { cursor: default; }

.globe--static .globe__pins { display: none; }

/* ------------------------------------------------------------- structure */

/* Two rows: the rails flank the globe in row 1, and the headline occupies a
   full-width band in row 2. The globe is lifted in the 3D scene to match, so
   no pin can ever land behind the copy. */
.globe__inner {
  position: relative;
  z-index: 4;
  display: grid;
  grid-template-columns: minmax(180px, 260px) 1fr minmax(180px, 260px);
  grid-template-rows: 1fr auto;
  align-items: center;
  gap: 24px;
  min-height: 100vh;
  min-height: 100svh;
  max-width: 1560px;
  margin-inline: auto;
  padding: 110px 32px 52px;
  pointer-events: none;   /* let drags reach the canvas */
}

.globe__rail {
  grid-row: 1;
  display: flex;
  flex-direction: column;
  gap: 26px;
  pointer-events: auto;
}
.globe__rail--left  { grid-column: 1; }
.globe__rail--right { grid-column: 3; align-items: flex-end; }

/* ------------------------------------------------------------------ copy */

.globe__copy {
  grid-column: 1 / -1;
  grid-row: 2;
  text-align: center;
  padding: 0 12px;
  pointer-events: auto;
}

.globe__title {
  font-family: var(--f-display);
  font-size: clamp(28px, 3.8vw, 54px);
  font-weight: 700;
  line-height: 1.06;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  text-shadow: 0 0 34px rgba(120, 80, 200, 0.75), 0 2px 20px rgba(0, 0, 0, 0.6);
  text-wrap: balance;
}

.globe__ref {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 16px;
  margin: 18px 0 14px;
  font-family: var(--f-nav);
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 4px;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.92);
}
.globe__ref::before,
.globe__ref::after {
  content: "";
  width: clamp(30px, 7vw, 96px);
  height: 1px;
  background: linear-gradient(90deg, transparent, var(--globe-accent-dim), transparent);
}

.globe__lede {
  max-width: 46ch;
  margin: 0 auto;
  font-size: clamp(15px, 1.3vw, 18px);
  line-height: 1.65;
  color: rgba(255, 255, 255, 0.82);
  text-wrap: balance;
}

/* ------------------------------------------------------------------ cards */

.chapter {
  --thumb: 118px;

  display: grid;
  grid-template-columns: var(--thumb) 1fr;
  align-items: center;
  gap: 14px;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  text-align: left;
  cursor: pointer;
  transition: transform 0.35s var(--ease);
}
.globe__rail--right .chapter {
  grid-template-columns: 1fr var(--thumb);
  text-align: right;
}

.chapter:hover,
.chapter:focus-visible { transform: translateY(-2px); }
.globe__rail--left  .chapter:hover { transform: translate(4px, -2px); }
.globe__rail--right .chapter:hover { transform: translate(-4px, -2px); }

/* 16/9: ten of the eleven chapter stills in public/assets/img/chapters are
   1280x720 frames of the 16/9 films the card opens, and a 16/10 box cropped
   11% off the sides of every one of them. st-louis.jpg is 480x360 and is now
   the one that crops — a source-asset shape rather than a box. The dialog these
   cards open is 16/9 too, so the poster and the film are finally the same
   shape. */
.chapter__media {
  position: relative;
  aspect-ratio: 16 / 9;
  border-radius: 12px;
  overflow: hidden;
  background: #1a1030;
  border: 1px solid rgba(169, 123, 255, 0.3);
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.5);
  transition: border-color 0.3s var(--ease), box-shadow 0.3s var(--ease);
}
.globe__rail--right .chapter__media { order: 2; }

.chapter__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0.92;
  transition: transform 0.6s var(--ease), opacity 0.3s var(--ease);
}

.chapter:hover .chapter__media,
.chapter.is-active .chapter__media {
  border-color: var(--globe-accent);
  box-shadow:
    0 0 0 1px var(--globe-accent),
    0 0 26px rgba(169, 123, 255, 0.55),
    0 8px 30px rgba(0, 0, 0, 0.55);
}
.chapter:hover .chapter__media img { transform: scale(1.06); opacity: 1; }

/* Play affordance */
.chapter__media::after {
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.92)
    url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'><path d='M8 5v14l11-7z' fill='%234B297B'/></svg>")
    center / 15px no-repeat;
  opacity: 0;
  transform: scale(0.75);
  transition: opacity 0.3s var(--ease), transform 0.3s var(--ease);
}
.chapter:hover .chapter__media::after,
.chapter:focus-visible .chapter__media::after {
  opacity: 1;
  transform: scale(1);
}

.chapter__label { min-width: 0; }

.chapter__badge {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-bottom: 6px;
  padding: 4px 9px;
  border-radius: 4px;
  background: var(--c-purple);
  font-family: var(--f-nav);
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 1.4px;
  text-transform: uppercase;
  white-space: nowrap;
}

.chapter__city {
  font-family: var(--f-nav);
  font-size: 15px;
  font-weight: 700;
  letter-spacing: 1.2px;
  text-transform: uppercase;
  line-height: 1.24;
}

.chapter__region {
  font-family: var(--f-nav);
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 1.6px;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
  margin-top: 2px;
}

/* Headquarters card reads larger, as in the reference. */
.chapter--hq { --thumb: 168px; }
.chapter--hq .chapter__city { font-size: 18px; }
.chapter--hq .chapter__media { border-color: var(--globe-accent); }

/* Dim the rest while one chapter is focused. */
.globe--focused .chapter:not(.is-active) { opacity: 0.45; }

/* Pin has rotated to the far side of the globe. */
.globe--live .chapter.is-dormant { opacity: 0.4; }
.globe--live .chapter.is-dormant .chapter__media { border-color: rgba(169,123,255,0.16); }
.chapter:hover, .chapter:focus-visible { opacity: 1 !important; }

.chapter { transition: transform 0.35s var(--ease), opacity 0.45s var(--ease); }

/* ------------------------------------------------------------ mobile live

   Still the live 3D globe (WebGL + no reduced-motion still required — see
   globe.js), laid out in bands rather than in layers.

   The desktop hero is full-bleed: the canvas covers the whole section and the
   copy floats over it, with GLOBE_LIFT raising the sphere so no pin hides
   behind the headline. In portrait that arrangement has nowhere to go — the
   sphere fills ~68% of the viewport height, overlapping the site header at the
   top and sitting behind the copy at the bottom (which is what buried
   Colombia's pin under the verse). Tuning the 3D numbers cannot fix both ends
   at once: pulling the sphere down to clear the logo pushes it further into the
   copy.

   So on mobile the canvas stops being full-bleed and takes a band of its own —
   below the header, above everything else — and the copy and cards flow
   beneath it in normal document order. No overlap is possible by
   construction, and the hero is content-height instead of 100svh, so the
   section below is not permanently pinned to the fold.

   --stage-top clears the floating site header; --stage-h is the band. The
   canvas, the arc layer and the pin layer must share exactly this box:
   globe.js derives pin coordinates from the canvas's measured size, so a pin
   layer on a different box would slide the pins off the sphere. */
/* --stage-top clears the floating .site-header, which on a phone comes to
   roughly 87px (18px padding, a 46vw-wide logo at 300x59, the tagline, 18px
   padding); 96px leaves visible daylight between the logo and the sphere
   rather than butting them together.

   --stage-h is clamped, not a bare 54svh: a landscape phone is still under the
   860px breakpoint, and 54% of a 375px-tall viewport would leave a 202px band.
   The floor keeps it legible there and the ceiling stops it dominating a tall
   device.

   54svh rather than the 44svh this shipped with — a larger globe on the phone,
   by request and closer to docs/visuals/mobileglobe.jpg and modelglobe.jpg,
   where the earth fills the frame and crops against its edges. The floor and
   ceiling moved with it. On an 844px-tall phone the band goes 371px -> 456px,
   which takes the sphere 253px -> 311px on its own; globe.js's
   CAMERA_REST_Z_MOBILE then went 4.25 -> 3.85 and takes it to 344px. This is
   the knob that was raised first, and the reasoning for the order is on that
   constant — briefly, the sphere is a fixed fraction of this band's height, so
   a taller band scales the sphere and its margins together, where a closer
   camera grows the sphere inside a box that stays put and pushes the topmost
   pins toward the edge.

   What both of them cost is horizontal room for the pin labels, and on a 390px
   phone that is now the binding constraint rather than a detail. Labels radiate
   outward from their pin (.globe__pin-label, side chosen in globe.js's frame
   loop), so a bigger sphere pushes them into the overflow: hidden above: a name
   at the limb had ~15px of room at 44svh/4.25 and is ~42px short of fitting
   now, and over a full revolution about half the labels drawn are trimmed
   rather than a third. Fixing that means changing the labels, not the size —
   the 24px offset below, or flipping them inward over the sphere, where the
   translucent chip they sit on keeps them legible and nothing can clip them. */
.globe--mobile {
  --stage-top: 96px;
  --stage-h: clamp(240px, 54vh, 480px);

  min-height: 0;
}

/* svh excludes the mobile browser's collapsing UI, so the band does not jump
   as the address bar hides. Behind @supports because an unsupported unit
   inside a custom property parses fine and only fails where it is used —
   which would leave the canvas with no height at all. */
@supports (height: 1svh) {
  .globe--mobile { --stage-h: clamp(240px, 54svh, 480px); }
}

.globe--mobile .globe__canvas,
.globe--mobile .globe__links,
.globe--mobile .globe__pins {
  top: var(--stage-top);
  bottom: auto;
  height: var(--stage-h);
}

/* Flex column, not the desktop 3-column grid: the rails are flattened with
   display:contents below, so the cards become direct children here and
   globe.js's `order` decides their sequence. */
.globe--mobile .globe__inner {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-height: 0;
  padding: calc(var(--stage-top) + var(--stage-h) + 20px) 20px 36px;
}

.globe--mobile .globe__copy {
  order: -1;      /* copy first, cards after, whatever order the cards carry */
  padding: 0;
}

/* display:contents so both rails' cards land in the flex column above and can
   be ordered against each other — they arrive from two separate rail elements
   and DOM order would otherwise interleave them arbitrarily. */
.globe--mobile .globe__rail,
.globe--mobile .globe__rail--right { display: contents; }

/* display:none rather than opacity:0 — a hidden card must not hold space in
   the column. This also sidesteps .globe--focused .chapter:not(.is-active)
   above, which would otherwise reveal every off-stage card at 45% opacity the
   moment a chapter video opened. */
.globe--mobile .chapter { display: none; }
.globe--mobile .chapter.is-spotlight {
  display: grid;
  animation: chapter-arrive 0.4s var(--ease) both;
}

@keyframes chapter-arrive {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* Whichever rail a card came from, mobile shows it the same way — mirrored
   orientations stacked in one column would read as a mistake. */
.globe--mobile .globe__rail--right .chapter {
  grid-template-columns: var(--thumb) 1fr;
  text-align: left;
}
.globe--mobile .globe__rail--right .chapter__media { order: 0; }
.globe--mobile .globe__rail--right .chapter:hover { transform: none; }

/* No connector arcs: the cards sit below the globe's band now, not beside
   their pins, so an arc would point at nothing. */
.globe--mobile .globe__links,
.globe--mobile .globe__stalk { display: none; }

/* Smaller pins, and names turned inward over the sphere.
 *
 * Both follow from the globe being ~88% of a 390px-wide band now (see
 * CAMERA_REST_Z_MOBILE and --stage-h): at that size the desktop pin heads read
 * as oversized against the earth, and an outward label on a pin near the limb
 * spent more time clipped by the overflow above than fully on screen. globe.js
 * flips the side — `labelsInward` in the frame loop — and these rules bring the
 * offsets in to match the smaller heads.
 *
 * 11px core and 8px minor, against 17px and 12px. The glow rings come in with
 * them; the label offsets come in to 14px, which is the same air beside a 5.5px
 * radius that 24px left beside an 8.5px one. */
.globe--mobile .globe__pin {
  width: 11px;
  height: 11px;
  margin: -5.5px 0 0 -5.5px;
  box-shadow:
    0 0 0 1px rgba(6, 3, 15, 0.55),
    0 0 0 2.5px rgba(169, 123, 255, 0.35),
    0 0 10px 2px rgba(169, 123, 255, 0.85);
}

.globe--mobile .globe__pin--minor {
  width: 8px;
  height: 8px;
  margin: -4px 0 0 -4px;
  box-shadow:
    0 0 0 1px rgba(6, 3, 15, 0.5),
    0 0 0 2px rgba(169, 123, 255, 0.22),
    0 0 7px 2px rgba(169, 123, 255, 0.5);
}

/* The dot shrinks; the tap target must not. ::after is the pin's ping ring and
   is invisible at rest (opacity: 0) but still part of the button's hit box, so
   -9px around an 11px head keeps that box at 29px — above the 24px minimum a
   thumb needs, where the head on its own would be well under it. */
.globe--mobile .globe__pin::after { inset: -9px; }

.globe--mobile .globe__base {
  width: 5px;
  height: 5px;
  margin: -2.5px 0 0 -2.5px;
}

/* :not() rather than a plain `left`, because the base rule for a left-hand
   label sets `left: auto` at the same specificity and these rules come later:
   overriding it here would put every label back on the right. */
.globe--mobile .globe__pin:not(.globe__pin--label-left) .globe__pin-label { left: 14px; }
.globe--mobile .globe__pin--label-left .globe__pin-label { right: 14px; }

/* The chip sat against space before and sits on the daylit earth now, so it
   carries a little more of its own background — white-on-cloud is the case that
   needs it. Only core pins are affected in practice: a minor pin's label is
   opacity 0 until it is tapped. */
.globe--mobile .globe__pin-label { background: rgba(6, 3, 15, 0.82); }

/* The star field (.globe::before) already shows through the band — the WebGL
   canvas is transparent apart from the sphere itself (alpha: true, clear alpha
   0), so the stars sit immediately around the globe with nothing extra needed.
   What mobile drops is only its drift: 260px over 240s is ~1px/second, which no
   one perceives, but it keeps the compositor awake indefinitely on a hero that
   is now ~1000px tall. Static costs nothing visually and stops the battery
   drain. The reduced-motion rule already proved the static look is fine. */
.globe--mobile::before { animation: none; }

@media (prefers-reduced-motion: reduce) {
  .globe--mobile .chapter.is-spotlight { animation: none; }
}

/* --------------------------------------------------------- static fallback */

/* Both rails dissolve into one grid so all chapters flow together. */
.globe--static .globe__inner {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-content: start;
  padding: 104px 20px 52px;
  gap: 16px;
}

.globe--static .globe__copy {
  grid-column: 1 / -1;
  order: -2;                /* rails sit after the copy despite DOM order */
  margin-bottom: 12px;
}
.globe--static .chapter { order: 0; }
.globe--static .chapter--hq { order: -1; }

.globe--static .globe__rail { display: contents; }

.globe--static .chapter {
  --thumb: 100%;
  grid-template-columns: 1fr;
  width: 100%;
  text-align: center;
  gap: 9px;
}
.globe--static .globe__rail--right .chapter {
  grid-template-columns: 1fr;
  text-align: center;
}
.globe--static .globe__rail--right .chapter__media { order: 0; }
.globe--static .chapter--hq { --thumb: 100%; }

/* The headquarters card leads, spanning the full width.
 *
 * 21/9 is deliberate and stays: this is the static hero's banner, cropped
 * cinematically on purpose, and it is the one place a chapter still is treated
 * as a backdrop rather than as the poster for a film. Everything that actually
 * plays a video is the video's own shape; this frames a page. */
.globe--static .chapter--hq { grid-column: 1 / -1; }
.globe--static .chapter--hq .chapter__media { aspect-ratio: 21 / 9; }

/* --------------------------------------------------------------- dialog */

.video-dialog {
  width: min(1000px, 92vw);
  padding: 0;
  border: 0;
  border-radius: 14px;
  background: #12082c;
  color: #fff;
  overflow: hidden;
  box-shadow: 0 30px 90px rgba(0, 0, 0, 0.6);
}
.video-dialog::backdrop {
  background: rgba(6, 3, 15, 0.82);
  backdrop-filter: blur(4px);
}

.video-dialog__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 18px 20px 14px;
}

.video-dialog__title {
  font-family: var(--f-display);
  font-size: 19px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.6px;
}

.video-dialog__meta {
  margin-top: 3px;
  font-family: var(--f-nav);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1.8px;
  text-transform: uppercase;
  color: var(--globe-accent);
}

.video-dialog__close {
  flex: none;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  color: #fff;
  background: rgba(255, 255, 255, 0.1);
  font-size: 20px;
  line-height: 1;
  transition: background 0.2s var(--ease);
}
.video-dialog__close:hover { background: rgba(255, 255, 255, 0.22); }

.video-dialog__frame {
  aspect-ratio: 16 / 9;
  background: #000;
}
.video-dialog__frame iframe {
  width: 100%;
  height: 100%;
  border: 0;
  display: block;
}

.video-dialog__fallback {
  padding: 26px 22px 30px;
  font-size: 15px;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.75);
  text-align: center;
}
.video-dialog__fallback a { color: var(--globe-accent); text-decoration: underline; }

/* "Visit Jubilee <country>" — core chapters only. */
.video-dialog__foot {
  display: flex;
  justify-content: center;
  padding: 20px 22px 24px;
}
.video-dialog__visit[hidden] { display: none; }

/* A minor chapter is reached by flying into its pin, so the panel grows out
   of the point on the globe the visitor dived into. */
@keyframes video-arrive {
  from {
    opacity: 0;
    transform: translate(
        calc(var(--origin-x, 50vw) - 50vw),
        calc(var(--origin-y, 50vh) - 50vh)
      ) scale(0.16);
  }
  to { opacity: 1; transform: none; }
}

.video-dialog--arrived[open] {
  animation: video-arrive 0.62s var(--ease) both;
}

@media (prefers-reduced-motion: reduce) {
  .video-dialog--arrived[open] { animation: none; }
}

/* ------------------------------------------------------------ responsive */

@media (max-width: 1180px) {
  .globe__inner { grid-template-columns: minmax(150px, 210px) 1fr minmax(150px, 210px); padding-inline: 20px; }
  .chapter { --thumb: 92px; }
  .chapter--hq { --thumb: 120px; }
  .chapter__city { font-size: 13px; }
}

@media (max-width: 860px) {
  .globe { min-height: auto; }
  .globe__inner { min-height: auto; }
  .globe__title { font-size: clamp(26px, 7vw, 38px); }
}

@media (prefers-reduced-motion: reduce) {
  .globe::before { animation: none; }
  .globe__pin::after { animation: none; }
}
