/* ==========================================================================
   map.css — Conference map: panel, native-disclosure markers, popups, legend
   --------------------------------------------------------------------------
   Depends ONLY on the design tokens in css/variables.css. Works in BOTH dark
   (:root) and light (:root.light) themes.

   ZERO-JS CONTRACT. Every marker is a native <details> disclosure, so clicking
   the pin opens its popup with no script at all. js/map.js is optional polish
   (accordion fallback, Escape, outside press, measured data-flip); nothing here
   depends on a JS-applied state class.

   DOM contract (ALL of it authored in index.html):
     section.card#map > h2#map-heading + p.map-intro.muted
       + div.conference-map#conference-map
           > img.conference-map-art                  (decorative, first child)
           > details.map-marker[data-kind][data-pin][data-flip?][name]
                 inline style: left: X%; top: Y%     (zero-size anchor point)
               > summary.map-pin[aria-label]         (the visible dot)
               > div.map-popup
                   > h3.map-popup-title, span.map-kind-badge[data-kind],
                     p.map-popup-date, a.map-popup-venue (or the currently
                     unused p.map-popup-venue-plain when no verified URL),
                     p.map-popup-presenter (opt.), p.map-popup-note (opt.),
                     ul.map-popup-papers > li > a
       + ul.map-legend > li.map-legend-item[data-kind]
       + ul#conference-list          (PERMANENT linear text version; no JS ever
                                      hides it, the :not() guard just keeps the
                                      id from out-specifying .visually-hidden)

   Motion: transform/opacity only; BOTH `.no-motion` (optionally set by
   js/map.js) and `prefers-reduced-motion` disable the drop-in, the open pulse
   and the popup entrance (Req 3.4, 8.5).

   CONTRAST (Req 9.5): artwork, pin dots, pin glow and legend dots are
   DECORATIVE (non-text) uses of --brand / amber / teal, so the brand-as-text
   ratio does not apply. TEXT uses only --text, --muted and --link, each meeting
   AA on --panel/--card in both themes; each badge pairs a solid fill with its
   on-fill token (--on-brand, --map-award-on, --map-school-on).
   ========================================================================== */

/* --- 1. Kind palette, section intro, legend, text list ------------------- */

/* The palette lives on the SECTION (not the panel) so the pins, the badges and
   the legend below the panel all inherit the same tokens. Explicit rgba (no
   color-mix) for maximum browser support; `-tint` is the pin GLOW only, at
   roughly --ring's alpha so every kind glows equally hard. */
#map {
  --map-award: #f5a623;              /* warm amber, mirroring css/news.css */
  --map-award-on: #1e2030;
  --map-award-tint: rgba(245, 166, 35, 0.38);
  --map-school: #2dd4bf;             /* teal: distinct from --brand + amber */
  --map-school-on: #10241f;
  --map-school-tint: rgba(45, 212, 191, 0.38);
}

:root.light #map {
  --map-award: #c2410c;
  --map-award-on: #ffffff;
  --map-award-tint: rgba(194, 65, 12, 0.32);
  --map-school: #0f766e;
  --map-school-on: #ffffff;
  --map-school-tint: rgba(15, 118, 110, 0.32);
}

.map-intro {
  margin-top: calc(-1 * var(--space-1));
  font-size: 0.95rem;
  line-height: 1.55;
}

/* Legend: decodes the pin colours without opening a popup. Labels are --muted
   (AA on the card in both themes) — never --brand as small text. */
.map-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  margin: var(--space-2) 0 0;
  padding: 0;
  list-style: none;
}

.map-legend-item {
  --legend-dot: var(--brand);
  display: flex;
  align-items: center;
  gap: 6px;
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1.3;
}

.map-legend-item::before {
  content: '';
  flex: 0 0 auto;
  box-sizing: border-box;
  width: 11px;
  height: 11px;
  border: 2px solid var(--panel);    /* same ring treatment as the pins */
  border-radius: 50%;
  background-color: var(--legend-dot);
}

.map-legend-item[data-kind='award'] { --legend-dot: var(--map-award); }
.map-legend-item[data-kind='school'] { --legend-dot: var(--map-school); }

/* Compact, intentional text list of the same entries (Req 9.6). `:not()` keeps
   .visually-hidden's reset from being out-specified by the id. */
#conference-list:not(.visually-hidden) {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
  margin-top: var(--space-2);
  padding-left: var(--space-3);
  font-size: 0.92rem;
  line-height: 1.5;
  color: var(--text);
}

/* --- 2. Map panel — a responsive ~16/10 box. `position: relative` anchors the
   markers; `overflow: visible` lets a popup spill slightly past the edge, which
   the authored + measured data-flip values keep small. -------------------- */

.conference-map {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;
  min-height: 240px;                 /* floor if aspect-ratio is unsupported */
  margin-top: var(--space-3);
  overflow: visible;
  border: 1px solid var(--ring);
  border-radius: var(--radius-lg);
  background-color: var(--panel);
  background-image: linear-gradient(160deg, var(--panel), var(--card));
}

/* Panel artwork. A plain <img> in index.html (NOT a background layer and NOT a
   CSS mask): both of those hid the SVG silently. `z-index: 0` +
   `pointer-events: none` keep it under the markers (z-index 1+). */
.conference-map-art {
  position: absolute;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  /* The SVG's viewBox IS the projection window, so it must stretch to the panel
     (never letterbox) or the pin percentages would drift off the landmasses. */
  object-fit: fill;
  border-radius: inherit;
  pointer-events: none;
  user-select: none;
  opacity: 1;                        /* the SVG shapes carry their own alpha */
}

/* Light panel (#e6e9ef) is closer to #8aadf4, so it takes a small knock-back. */
:root.light .conference-map-art { opacity: 0.9; }

/* --- 3. Markers — <details class="map-marker">, a ZERO-SIZE anchor point at the
   projected coordinate. Zero size is deliberate: pin and popup are both
   absolutely positioned against this point, so the popup's own (changing) size
   can never shift the pin's centring translate. --------------------------- */

.map-marker {
  position: absolute;                /* left / top are inline, from index.html */
  z-index: 1;                        /* base layer above the artwork (0) */
  width: 0;
  height: 0;
}

/* An open marker rises above its neighbours — the enlarged dot, the glow and the
   popup would otherwise be clipped by nearby markers (Pisa / Lucca / Siena). */
.map-marker[open] { z-index: 3; }

/* The pin IS the <summary>: click, Enter and Space all open the disclosure
   natively (Req 9.7). It is a transparent 44x44 touch target (Req 10.4); the
   VISIBLE dot is a ::before centred inside it. Hover/focus growth scales the
   DOT, never the summary, so its centring translate is never overwritten. */
.map-pin {
  --pin-tint: var(--ring);           /* the pin glow; per-kind overrides below */
  position: absolute;
  top: 0;
  left: 0;
  display: block;                    /* drop the default `list-item` display   */
  width: 44px;
  height: 44px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  color: var(--brand);
  cursor: pointer;
  list-style: none;                  /* remove the disclosure triangle/marker  */
  -webkit-tap-highlight-color: transparent;
  animation: map-pin-drop 420ms var(--ease-out) both;
}

/* Legacy Safari/Chrome marker. */
.map-pin::-webkit-details-marker { display: none; }

/* Each kind sets its glow ONCE via `--pin-tint`, so the states below need no
   per-kind copies. Note the attribute now lives on the <details>. */
.map-marker[data-kind='award'] .map-pin { color: var(--map-award); --pin-tint: var(--map-award-tint); }
.map-marker[data-kind='school'] .map-pin { color: var(--map-school); --pin-tint: var(--map-school-tint); }

/* Visible dot: solid currentColor core, a --panel RING separating it from the
   artwork (so it contrasts in BOTH themes — that ring is what stops a #8aadf4
   brand pin vanishing into the #8aadf4 coastlines), then a tinted halo + glow.
   `box-sizing: border-box` keeps the dot 15px, so the border eats the core
   instead of shifting the centre. */
.map-pin::before {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  box-sizing: border-box;
  width: 15px;
  height: 15px;
  transform: translate(-50%, -50%);
  border: 2px solid var(--panel);
  border-radius: 50%;
  background-color: currentColor;
  box-shadow: 0 0 0 3px var(--pin-tint), 0 0 12px 2px var(--pin-tint);
  transition:
    transform var(--transition-fast) var(--ease-out),
    box-shadow var(--transition-fast) var(--ease-out);
}

/* Pulse ring — only while the disclosure is OPEN, so the resting map stays calm.
   Driven by [open], never by a JS-applied state class. */
.map-pin::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 50%;
  width: 15px;
  height: 15px;
  transform: translate(-50%, -50%);
  border: 2px solid currentColor;
  border-radius: 50%;
  opacity: 0;
  pointer-events: none;
}

.map-marker[open] .map-pin::after {
  animation: map-pin-pulse 1800ms var(--ease-out) infinite;
}

.map-marker[open] .map-pin::before,
.map-pin:focus-visible::before {
  transform: translate(-50%, -50%) scale(1.3);
  box-shadow: 0 0 0 5px var(--pin-tint), 0 0 16px 4px var(--pin-tint);
}

/* Hover growth is gated on a fine, hover-capable pointer so touch devices never
   get a stuck hover state (Req 10.5). */
@media (hover: hover) and (pointer: fine) {
  .map-pin:hover::before {
    transform: translate(-50%, -50%) scale(1.3);
    box-shadow: 0 0 0 5px var(--pin-tint), 0 0 16px 4px var(--pin-tint);
  }
}

.map-pin:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

@keyframes map-pin-drop {
  from { opacity: 0; transform: translate(-50%, calc(-50% - 14px)); }
  to { opacity: 1; transform: translate(-50%, -50%); }
}

@keyframes map-pin-pulse {
  from { opacity: 0.7; transform: translate(-50%, -50%) scale(1); }
  to { opacity: 0; transform: translate(-50%, -50%) scale(3); }
}

/* --- 4. Popup — the disclosure body, so <details> shows and hides it for free.
   Anchored to the marker's zero-size origin and nudged 14px clear of the dot.
   The `translate` PROPERTY carries the data-flip repositioning, leaving
   `transform` free for the fade+scale entrance. z-index stays below --z-header
   (50) so the sticky header wins. ---------------------------------------- */

.map-popup {
  position: absolute;
  top: 0;
  left: 0;
  z-index: 40;
  width: max-content;
  max-width: 300px;
  max-height: 240px;
  margin: 14px 0 0 14px;
  padding: var(--space-2);
  overflow-y: auto;                  /* long notes scroll inside the panel */
  border: 1px solid var(--ring);
  border-radius: var(--radius-md);
  background-color: var(--panel);
  background-image: linear-gradient(160deg, var(--panel), var(--card));
  box-shadow: 0 18px 40px -18px var(--ring);
  color: var(--text);
  scrollbar-width: thin;
  scrollbar-color: var(--brand) transparent;
  transform-origin: top left;
  animation: map-popup-in 220ms var(--ease-out) both;
}

.map-popup::-webkit-scrollbar { width: 6px; }
.map-popup::-webkit-scrollbar-track { background: transparent; }
.map-popup::-webkit-scrollbar-thumb { background: var(--brand); border-radius: var(--radius-pill); }

@keyframes map-popup-in {
  from { opacity: 0; transform: scale(0.94); }
  to { opacity: 1; transform: scale(1); }
}

/* Arrow pointing back at the pin. It hangs off the MARKER, not the popup: the
   popup is its own scroll container, which would clip a pseudo-element sitting
   outside its padding box. z-index 41 keeps it over the popup's border, and it
   only exists while the disclosure is open. */
.map-marker[open]::after {
  content: '';
  position: absolute;
  top: 8px;
  left: 28px;
  z-index: 41;                       /* over the popup's own border */
  width: 10px;
  height: 10px;
  transform: rotate(45deg);
  border-top: 1px solid var(--ring);
  border-left: 1px solid var(--ring);
  background-color: var(--panel);
}

/* --- data-flip: AUTHORED in index.html for the edge markers (Bari `up`, Athens
   `left-up`) so placement is right with zero JS; js/map.js may widen it after
   measuring, but never removes an authored value. ------------------------- */

.map-marker[data-flip='left'] .map-popup {
  translate: -100% 0;
  margin-left: -14px;
  transform-origin: top right;
}

.map-marker[data-flip='up'] .map-popup {
  translate: 0 -100%;
  margin-top: -14px;
  transform-origin: bottom left;
}

.map-marker[data-flip='left-up'] .map-popup {
  translate: -100% -100%;
  margin-top: -14px;
  margin-left: -14px;
  transform-origin: bottom right;
}

/* The arrow follows the flipped popup to the opposite corner. */
.map-marker[data-flip='left'][open]::after,
.map-marker[data-flip='left-up'][open]::after { right: 28px; left: auto; }

.map-marker[data-flip='up'][open]::after,
.map-marker[data-flip='left-up'][open]::after { top: auto; bottom: 8px; transform: rotate(225deg); }

/* --- 5. Popup contents --------------------------------------------------- */

.map-popup-title {
  margin: 0 0 var(--space-1);
  font-size: 1.05rem;
  line-height: 1.3;
  color: var(--text);
}

.map-kind-badge {
  display: inline-block;
  padding: 2px 10px;
  border-radius: var(--radius-pill);
  background-color: var(--brand);
  color: var(--on-brand);
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* Each fill is paired with its own on-fill token, both AA against the fill. */
.map-kind-badge[data-kind='award'] { background-color: var(--map-award); color: var(--map-award-on); }
.map-kind-badge[data-kind='school'] { background-color: var(--map-school); color: var(--map-school-on); }

.map-popup-date {
  margin-top: 6px;
  font-size: 0.72rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.map-popup-venue,
.map-popup-venue-plain {
  margin-top: 2px;
  font-weight: 600;
  line-height: 1.35;
}

.map-popup-venue { display: inline-block; color: var(--link); }

/* Venue with no verified URL: same typography, but --text — nothing is a link.
   CURRENTLY UNUSED: every marker now has a verified programme URL (Athens was
   the last plain one). Kept as the documented fallback for a future entry with
   no URL, so it is never reinvented as an empty-href anchor. */
.map-popup-venue-plain { color: var(--text); }

/* Subtle muted italic credit line for a co-author who delivered the talk. */
.map-popup-presenter {
  margin-top: 4px;
  color: var(--muted);
  font-size: 0.85rem;
  font-style: italic;
  line-height: 1.45;
}

.map-popup-note {
  margin-top: 6px;
  font-size: 0.88rem;
  line-height: 1.45;
  color: var(--text);
}

.map-popup-papers {
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: var(--space-1) 0 0;
  padding: 0;
  list-style: none;
}

.map-popup-papers li {
  position: relative;
  padding-left: var(--space-2);
  font-size: 0.85rem;
  line-height: 1.45;
}

.map-popup-papers li::before {
  content: '';
  position: absolute;
  top: 0.55em;
  left: 2px;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background-color: var(--brand);           /* decorative bullet */
}

.map-popup-papers a { color: var(--link); }

/* --- 6. Narrow viewports (<768px) — the panel is far too small to anchor a
   readable popup beside a pin, so the popup becomes a bottom sheet. It is the
   ONLY place we need the viewport as the containing block, hence the local
   `will-change: auto` (css/animations.css sets `will-change: transform` on every
   .reveal, which would otherwise anchor the sheet to the section). The open pin
   is raised above the sheet so tapping it again always closes it. ---------- */

@media (max-width: 767px) {
  #map.reveal { will-change: auto; }

  .map-popup,
  .map-marker[data-flip] .map-popup {
    position: fixed;
    inset: auto var(--space-2) var(--space-2);
    width: auto;
    max-width: none;
    max-height: 60vh;
    margin: 0;
    translate: none;
    transform-origin: bottom center;
  }

  /* No pin to point at once the popup is a sheet. */
  .map-marker[open]::after { display: none; }

  .map-marker[open] .map-pin { z-index: 45; }
}

/* --- 7. Reduced motion (Req 3.4, 8.5) — `.no-motion` (optionally set by
   js/map.js) and the media query both disable drop-in, pulse and entrance. -- */

.conference-map.no-motion .map-pin,
.conference-map.no-motion .map-marker[open] .map-pin::after,
.conference-map.no-motion .map-popup {
  animation: none;
}

.conference-map.no-motion .map-pin::before { transition: none; }

@media (prefers-reduced-motion: reduce) {
  .map-pin,
  .map-marker[open] .map-pin::after,
  .map-popup {
    animation: none;
  }

  .map-pin::before { transition: none; }
}
