/* The tour globe, styled.
 *
 * ONE FILE FOR TWO TREES. public/ (the live site, styles.css) and public-next/
 * (the Phase 2 redesign, cutta.css) both load this after their own stylesheet,
 * so the globe looks identical in both while each tree keeps owning its own
 * page furniture. That is why every rule here is prefixed .globe-scope: it is
 * two classes deep, so it beats cutta.css's single-class .gpin/.gm-* rules on
 * specificity rather than on load order alone.
 *
 * IT DELIBERATELY DOES NOT SET .globe-wrap GEOMETRY. The live page is a
 * full-bleed fixed canvas; the staged page is a bounded block with a list and
 * a footer under it. Those are real layout differences and each tree keeps
 * them. Everything inside the canvas is shared.
 *
 * The palette is stated in absolute values rather than borrowed from either
 * stylesheet, because "the globe looks the same in both trees" is the point.
 * Bone on near-black, one red accent, and nothing else.
 */

/* THE PALETTE, AND THE CONTRAST ARITHMETIC BEHIND IT.
 *
 * Every value here that carries text was checked against the panel ground
 * (#09090b at 93%, so effectively #090909) for WCAG 2.2 AA, because all of this
 * type is small and none of it qualifies for the large-text 3:1 allowance:
 *
 *   --g-bone   #efe7d8 on panel   16.6:1
 *   --g-bone-2 #cfc5b4 on panel   12.4:1
 *   --g-dim    #8a8377 on panel    5.2:1   (was #7c766c at 4.27:1, which failed)
 *   --g-accent #ff2a2a on panel    5.3:1   as text
 *
 * The accent is the trap. White on #ff2a2a is 3.7:1 and fails; BLACK on the
 * same red is 5.7:1 and passes, which is why the one red button on this page
 * has black type on it rather than the white that looks more obvious.
 * --g-pin-past is deliberately NOT held to any of this: it colours a dot, not
 * type, and being dim is the whole point of it.
 */
.globe-scope {
  --g-bone: #efe7d8;
  --g-bone-2: #cfc5b4;
  --g-dim: #8a8377;
  --g-pin-past: #7c766c;
  --g-accent: #ff2a2a;
  --g-on-accent: #08080b;
  --g-line: rgba(239, 231, 216, 0.15);
  --g-panel: rgba(9, 9, 11, 0.93);
  --g-shadow: 0 24px 70px rgba(0, 0, 0, 0.72);

  /* HOW BIG A TAP IS, as a radius in CSS pixels. tour-globe.js reads this
     rather than hardcoding a number, so the touch target sits with the rest of
     the pin sizing instead of hiding in the script. 18px is a mouse; the
     coarse-pointer value below is 26px, which is a 52px target and clears the
     44px floor that the legal bar at the bottom of this page is already held
     to. Note that NEITHER value changes how big a pin looks: the dots stay
     12px, and only the area that answers a tap grows. */
  --g-tap-r: 18px;
}

@media (pointer: coarse) {
  .globe-scope { --g-tap-r: 26px; }
}

/* The warm ground behind the sphere. A CSS gradient rather than a rendered
   backdrop: it costs no GPU frame time and it is what keeps the scene from
   reading as a black void with an object in it. */
.globe-scope .globe-wrap::before {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  background:
    radial-gradient(60% 50% at 50% 46%, rgba(240, 228, 208, 0.09), rgba(240, 228, 208, 0) 70%),
    radial-gradient(120% 90% at 50% 120%, rgba(255, 42, 42, 0.07), rgba(0, 0, 0, 0) 60%);
}

/* THE LOAD-IN. tour-globe.js holds the canvas hidden until globe.gl reports the
   sphere is drawable AND this box has been measured, then adds .is-in and the
   compositor fades it up. Doing it here rather than in the scene is the point:
   a CSS transition is driven by the compositor, so a stalled render loop cannot
   compress it into a single frame the way globe.gl's own scale tween does.

   Two things this deliberately is not. It is not a scale from nothing, which is
   the pop it replaces. And it is not applied to .globe-wrap, so the warm
   gradient behind the sphere is already on screen and the globe fades onto a
   ground rather than out of a void.

   0.98 is small on purpose. It reads as arriving rather than as zooming, and it
   keeps the canvas within two percent of final size, so a tap that lands during
   the entrance still lands where the hit test believes it did. */
.globe-scope .globe-wrap canvas {
  display: block;
  touch-action: none;
  opacity: 0;
  transform: scale(0.98);
  transform-origin: 50% 50%;
  transition:
    opacity 620ms cubic-bezier(0.22, 0.61, 0.36, 1),
    transform 780ms cubic-bezier(0.22, 0.61, 0.36, 1);
}

.globe-scope .globe-wrap.is-in canvas { opacity: 1; transform: none; }

/* ---------------------------------------------------------------- pins --- */
/* Purely decorative and pointer-events:none on purpose. Clicks are handled by
   globe.gl's raycaster against invisible 3D points, which is what made taps
   land reliably on a phone; an HTML pin that could swallow the tap would undo
   that. Carried across from the original globe unchanged. */

.globe-scope .gpin { position: relative; width: 12px; height: 12px; pointer-events: none; }

.globe-scope .gpin-dot {
  position: absolute; inset: 0; border-radius: 50%;
  background: var(--g-bone);
  box-shadow: 0 0 10px 2px rgba(239, 231, 216, 0.5);
}

/* PLAYED: solid, small, unlit. It is a record, not an offer. */
.globe-scope .gpin.past { width: 8px; height: 8px; }
.globe-scope .gpin.past .gpin-dot {
  background: var(--g-pin-past);
  box-shadow: 0 0 6px 1px rgba(124, 118, 108, 0.45);
}

/* UPCOMING: lit, and breathing. */
.globe-scope .gpin.up .gpin-dot::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  border: 1.5px solid rgba(239, 231, 216, 0.7);
  animation: g-pulse 2.4s ease-out infinite;
}

/* THE NEXT ONE: the only red on the page. */
.globe-scope .gpin.next { width: 15px; height: 15px; }
.globe-scope .gpin.next .gpin-dot {
  background: var(--g-accent);
  box-shadow: 0 0 14px 3px rgba(255, 42, 42, 0.6);
}
.globe-scope .gpin.next .gpin-dot::after {
  border-color: rgba(255, 42, 42, 0.75);
  animation-duration: 1.9s;
}

@keyframes g-pulse {
  0%   { transform: scale(1);   opacity: 0.85; }
  100% { transform: scale(3.6); opacity: 0; }
}

.globe-scope .gpin-count {
  position: absolute; left: 9px; top: -10px;
  min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px;
  background: var(--g-bone); color: #08080b;
  font: 700 10px/16px inherit; text-align: center;
  box-shadow: 0 0 8px rgba(239, 231, 216, 0.45);
}
.globe-scope .gpin.next .gpin-count { background: var(--g-accent); color: var(--g-on-accent); }

.globe-scope .gpin-label {
  position: absolute; left: 20px; top: 50%; transform: translateY(-50%);
  white-space: nowrap; font: 600 10px/1 inherit;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--g-bone);
  text-shadow: 0 1px 8px #000, 0 0 4px #000;
  opacity: 0; transition: opacity 0.2s;
}
.globe-scope .gpin.next .gpin-label { opacity: 1; }

/* hover tooltip, rendered by globe.gl into its own layer */
.globe-scope .scene-tooltip { font-family: inherit !important; }
.globe-scope .gtip {
  background: var(--g-panel); border: 1px solid var(--g-line); border-radius: 9px;
  padding: 8px 13px; text-align: center; line-height: 1.4; box-shadow: var(--g-shadow);
  font-size: 11px; letter-spacing: 0.08em; text-transform: uppercase; color: var(--g-bone-2);
}
.globe-scope .gtip b {
  display: block; margin-bottom: 2px; font-size: 13px;
  letter-spacing: 0.02em; text-transform: none; color: var(--g-bone);
}

/* ----------------------------------------------------------------- HUD --- */

.globe-scope .ghud {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 20;
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 16px; padding: 18px clamp(14px, 4vw, 28px);
  pointer-events: none;
}
.globe-scope .ghud > * { pointer-events: auto; }

/* The next-show callout. A real button: it flies the globe to that pin and
   opens its card, so the callout and the globe can never disagree. */
.globe-scope .gnext {
  display: block; text-align: left; max-width: min(62vw, 340px);
  min-height: 44px; padding: 10px 14px;
  background: var(--g-panel); border: 1px solid var(--g-line); border-left: 2px solid var(--g-accent);
  border-radius: 10px; color: var(--g-bone); font: inherit; cursor: pointer;
  backdrop-filter: blur(8px); box-shadow: var(--g-shadow);
  transition: border-color 0.2s, transform 0.2s;
}
.globe-scope .gnext:hover { transform: translateY(-1px); border-color: rgba(239, 231, 216, 0.4); }
.globe-scope .gnext:focus-visible { outline: 2px solid var(--g-accent); outline-offset: 3px; }
.globe-scope .gnext[hidden] { display: none; }
.globe-scope .gnext-k {
  display: block; font-size: 9px; letter-spacing: 0.26em; text-transform: uppercase;
  color: var(--g-accent); margin-bottom: 3px;
}
.globe-scope .gnext-d { display: block; font-size: 12px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--g-bone-2); }
.globe-scope .gnext-v {
  display: block; font-size: 15px; font-weight: 600; margin-top: 2px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* The counts. Driven by the data, so they cannot drift from the pins. */
.globe-scope .gstats {
  display: flex; gap: 18px; text-align: right; flex-wrap: wrap; justify-content: flex-end;
}
.globe-scope .gstat-n { display: block; font-size: 19px; font-weight: 600; color: var(--g-bone); line-height: 1.1; }
.globe-scope .gstat-k { display: block; font-size: 9px; letter-spacing: 0.2em; text-transform: uppercase; color: var(--g-dim); margin-top: 3px; }

.globe-scope .globe-hint {
  position: absolute; left: 0; right: 0; bottom: 86px; z-index: 10;
  text-align: center; font-size: 11px; letter-spacing: 0.22em; text-transform: uppercase;
  color: rgba(239, 231, 216, 0.62); pointer-events: none; margin: 0;
  transition: opacity 0.5s;
}
.globe-scope .globe-hint.gone { opacity: 0; }
.globe-scope .globe-hint a { color: inherit; pointer-events: auto; }

/* A legend only appears once there is something to explain. */
.globe-scope .glegend {
  position: absolute; top: 12px; right: clamp(14px, 4vw, 28px); z-index: 20;
  display: flex; gap: 14px; font-size: 9px; letter-spacing: 0.18em;
  text-transform: uppercase; color: var(--g-dim); pointer-events: none;
}
.globe-scope .glegend span { display: inline-flex; align-items: center; gap: 6px; }
.globe-scope .glegend i { width: 7px; height: 7px; border-radius: 50%; background: var(--g-pin-past); }
.globe-scope .glegend .up i { background: var(--g-bone); box-shadow: 0 0 8px rgba(239, 231, 216, 0.6); }
.globe-scope .glegend[hidden] { display: none; }

/* ---------------------------------------------------------------- card --- */

.globe-scope .globe-modal {
  position: absolute; z-index: 40; left: 50%; bottom: 96px;
  transform: translateX(-50%) translateY(12px);
  width: min(430px, 92vw); max-height: min(60vh, 520px); overflow-y: auto;
  -webkit-overflow-scrolling: touch;
  background: var(--g-panel); border: 1px solid var(--g-line); border-radius: 16px;
  padding: 22px 22px 18px; backdrop-filter: blur(12px); box-shadow: var(--g-shadow);
  opacity: 0; pointer-events: none; transition: opacity 0.25s, transform 0.25s;
  color: var(--g-bone);
}
.globe-scope .globe-modal.show { opacity: 1; pointer-events: auto; transform: translateX(-50%) translateY(0); }
.globe-scope .globe-modal[hidden] { display: none; }

.globe-scope .gm-close {
  position: absolute; top: 6px; right: 6px; width: 44px; height: 44px;
  background: none; border: 0; color: var(--g-dim); font-size: 24px; line-height: 1;
  cursor: pointer; border-radius: 10px;
}
.globe-scope .gm-close:hover { color: var(--g-bone); }
.globe-scope .gm-close:focus-visible { outline: 2px solid var(--g-accent); outline-offset: -4px; }

.globe-scope .gm-city { font-size: 10px; letter-spacing: 0.26em; text-transform: uppercase; color: var(--g-dim); margin: 0; }
.globe-scope .gm-venue { font-size: 22px; font-weight: 600; margin: 4px 0 14px; padding-right: 44px; color: var(--g-bone); }

.globe-scope .gm-grp {
  font-size: 9px; letter-spacing: 0.22em; text-transform: uppercase;
  color: var(--g-dim); margin: 14px 0 2px;
}
.globe-scope .gm-grp:first-child { margin-top: 0; }

.globe-scope .gm-show {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 12px 0; border-top: 1px solid var(--g-line);
}
.globe-scope .gm-show-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.globe-scope .gm-date { font-size: 11px; letter-spacing: 0.16em; text-transform: uppercase; color: var(--g-dim); }
.globe-scope .gm-title { font-size: 15px; font-weight: 500; color: var(--g-bone); }
.globe-scope .gm-sub { font-size: 12px; color: var(--g-bone-2); }

/* The ticket button. min-height 44px is the tap target, not decoration. */
.globe-scope .gm-cta {
  display: inline-flex; align-items: center; min-height: 44px; padding: 0 16px;
  border: 1px solid var(--g-bone); border-radius: 999px;
  background: var(--g-bone); color: #08080b; text-decoration: none;
  font-size: 11px; font-weight: 600; letter-spacing: 0.14em; text-transform: uppercase;
  white-space: nowrap;
}
.globe-scope .gm-cta:hover { background: #fff; border-color: #fff; }
.globe-scope .gm-cta:focus-visible { outline: 2px solid var(--g-accent); outline-offset: 3px; }
.globe-scope .gm-show.is-next .gm-cta { background: var(--g-accent); border-color: var(--g-accent); color: var(--g-on-accent); }

/* A played date, and a cancelled one, both end up here: a label, never a
   button. Selling a night that cannot be attended is the c.93A trap. */
.globe-scope .gm-tag {
  font-size: 10px; font-weight: 600; letter-spacing: 0.2em; text-transform: uppercase;
  color: var(--g-dim); border: 1px solid var(--g-line); border-radius: 999px;
  padding: 9px 13px; white-space: nowrap;
}
.globe-scope .gm-show-off .gm-title, .globe-scope .gm-show-off .gm-date { text-decoration: line-through; opacity: 0.65; }
.globe-scope .gm-show-past .gm-title { color: var(--g-bone-2); }

/* ---------------------------------------------------------- the list ----- */
/* The keyboard and screen-reader route to the same dates, and the fallback if
   WebGL is unavailable. A canvas is not a document. */

/* A ch measure rather than a rem one: the constraint that matters for a list
   of venue names is line length, and ch is the unit that actually tracks it. */
.globe-scope .glist ul { list-style: none; margin: 0; padding: 0; display: grid; gap: 10px; max-width: 52ch; }
.globe-scope .glist li { min-width: 0; }
.globe-scope .glist-btn {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  width: 100%; min-height: 44px; padding: 11px 14px; text-align: left;
  background: transparent; border: 1px solid var(--g-line); border-radius: 10px;
  color: var(--g-bone); font: inherit; cursor: pointer;
}
.globe-scope .glist-btn:hover { border-color: rgba(239, 231, 216, 0.42); }
.globe-scope .glist-btn:focus-visible { outline: 2px solid var(--g-accent); outline-offset: 2px; }
.globe-scope .glist-btn .d { font-size: 11px; letter-spacing: 0.14em; text-transform: uppercase; color: var(--g-dim); }
.globe-scope .glist-btn .v { font-size: 14px; font-weight: 500; }
.globe-scope .glist-btn.is-past { opacity: 0.78; }
.globe-scope .glist-btn.is-next { border-left: 2px solid var(--g-accent); }

/* --------------------------------------------------- reduced motion ------ */
/* An honest implementation of the preference: nothing here spins, pulses or
   flies. The globe is still fully draggable, zoomable and clickable, because
   the preference is about motion the visitor did not ask for, not about
   taking the interaction away. */

@media (prefers-reduced-motion: reduce) {
  /* No motion in. The canvas still starts hidden, so nothing is shown at a size
     it is about to change, but it arrives at final size with no travel of its
     own and no fade to sit through. */
  .globe-scope .globe-wrap canvas { transition: none; transform: none; }
  .globe-scope .globe-wrap.is-in canvas { transform: none; }
  .globe-scope .gpin-dot::after { animation: none !important; opacity: 0 !important; }
  .globe-scope .globe-modal { transition: none; }
  .globe-scope .gnext { transition: none; }
  .globe-scope .globe-hint { transition: none; }
}

@media (max-width: 760px) {
  .globe-scope .gm-venue { font-size: 20px; }
  .globe-scope .globe-hint { bottom: 78px; font-size: 10px; letter-spacing: 0.18em; }
  .globe-scope .ghud { padding: 14px; gap: 10px; align-items: flex-end; }
  .globe-scope .gstats { gap: 12px; }
  .globe-scope .gstat-n { font-size: 16px; }
  .globe-scope .glegend { display: none; }
  .globe-scope .globe-modal { bottom: 104px; padding: 18px 18px 14px; }
  .globe-scope .gm-show { gap: 10px; }
}

/* Very short landscape phones: the card would cover the whole scene. */
@media (max-height: 520px) {
  .globe-scope .globe-modal { max-height: 76vh; bottom: 12px; }
  .globe-scope .globe-hint { display: none; }
}


/* ------------------------------------------------------------- picker ---- */
/* Shown only when one tap could have meant more than one pin. It is not a
   tooltip and not the card: it is the step in between, where the globe admits
   it cannot tell which show was meant and hands the choice back.

   It sits at z-index 30, above the hint (10) and the HUD (20) so it is
   readable, and below the card (40) so that choosing a row and getting the
   card is a move forward rather than a fight between two panels. */

.globe-scope .gpick {
  position: absolute;
  z-index: 30;
  pointer-events: none;   /* only the menu inside it takes the pointer */
}

.globe-scope .gpick-legs {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: none;
}

/* Dashed, and thin: the line is there to say "this row is that dot", and the
   moment it competes with the arcs it has overstayed its welcome. */
.globe-scope .gpick-leg {
  stroke: rgba(239, 231, 216, 0.5);
  stroke-width: 1;
  stroke-dasharray: 3 4;
}

.globe-scope .gpick-ring {
  fill: none;
  stroke: rgba(239, 231, 216, 0.75);
  stroke-width: 1.5;
}

.globe-scope .gpick-menu {
  position: absolute;
  pointer-events: auto;
  min-width: 172px;
  max-width: 244px;
  padding: 8px 8px 6px;
  background: var(--g-panel);
  border: 1px solid var(--g-line);
  border-radius: 12px;
  box-shadow: var(--g-shadow);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
}

.globe-scope .gpick-title {
  margin: 2px 8px 6px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--g-dim);
}

/* 48px, not 44: this is the control the whole fix exists to make hittable, so
   it is not sized to scrape the floor. */
.globe-scope .gpick-row {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  min-height: 48px;
  padding: 8px 10px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  color: var(--g-bone);
  font: inherit;
  text-align: left;
  cursor: pointer;
}

.globe-scope .gpick-row + .gpick-row { margin-top: 2px; }

.globe-scope .gpick-row:hover { background: rgba(239, 231, 216, 0.09); }

.globe-scope .gpick-row:focus-visible {
  outline: none;
  background: rgba(239, 231, 216, 0.09);
  box-shadow: inset 0 0 0 2px var(--g-accent);
}

/* The swatch repeats the pin's own colour, so the row and the dot it points at
   are tied together by more than the line alone. */
.globe-scope .gpick-dot {
  flex: 0 0 auto;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  background: var(--g-bone);
}

.globe-scope .gpick-row.next .gpick-dot { background: var(--g-accent); }
.globe-scope .gpick-row.past .gpick-dot { background: var(--g-pin-past); }

.globe-scope .gpick-t { min-width: 0; }

.globe-scope .gpick-n {
  display: block;
  font-size: 13px;
  line-height: 1.25;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.globe-scope .gpick-s {
  display: block;
  font-size: 11px;
  line-height: 1.35;
  color: var(--g-bone-2);
}
