/* ==========================================================================
   Globe Pins 1.0 — dotted globe with anchored location cards
   --------------------------------------------------------------------------
   Plain CSS. No framework, no build step, no external request.

   The component enhances an ordinary address list. Until the script has run,
   that list is what the visitor sees and reads, so the page never depends on
   JavaScript to carry the information. Once enhanced, the same list becomes a
   set of pins floating over a rotating globe drawn on a <canvas>.

   PROPORTIONS
   The globe and the cards are two independent scales, because a map should
   not get denser when the text gets bigger:

     globe   everything derives from the globe radius R, itself a fraction
             --gp-remplissage of the shorter side of the frame
     cards   everything derives from --gp-corps, in em

   Ratios measured on the reference, at 1440 x 900 in DPR 2 (see README):

     globe diameter      0.6789 x frame height   (611 px of 900)
     camera half-angle   20.0 deg  ->  distance 2.924 R
     latitude rows       181
     fill dot diameter   0.0057 R    (3.48 px of 305.5 CSS radius)
     coast dot diameter  0.0070 R    (4.28 px)
     fill ink opacity    0.70 of the coast ink
     pin diameter        0.098 R
     halo reach          0.164 R
     card advance        0.6 em      (10.007 px of 16.68)
     card line-height    1.379 em    (23.00 px of 16.68)
     card padding        0.7255 em   (12.10 px of 16.68)
     card radius         0.5096 em   (8.50 px of 16.68)

   Order of this file:
     1. Design tokens
     2. Frame and canvas
     3. Fallback: the plain address list, before the script and without it
     4. Pins
     5. Cards
     6. Motion
     7. Variants
     8. Fallbacks: reduced motion, forced colours, no pointer

   Naming follows BEM: .globe-pins, .globe-pins__part, .globe-pins--variant.
   ========================================================================== */

/* ==========================================================================
   1. Design tokens
   ========================================================================== */

.globe-pins {
  /* ---- Globe -------------------------------------------------------- */

  /* Globe diameter, as a fraction of the shorter side of the frame. The
     reference draws a 611 px globe in a 900 px tall section. */
  --gp-remplissage: 0.6789;

  /* Camera distance, in globe radii. The reference sphere subtends 20.0 deg,
     so the camera sits at 1 / sin(20 deg) = 2.924 radii. Raise it towards 50
     to flatten the perspective, lower it towards 1.6 for a fish-eye. */
  --gp-recul: 2.924;

  /* Camera latitude. 0 looks at the equator, as the reference does. */
  --gp-inclinaison: 0deg;

  /* Longitude facing the camera when the globe first appears. */
  --gp-depart: 8deg;

  /* Latitude rows from pole to pole. Dots along a row keep the same arc
     spacing as the rows themselves, so the lattice stays square. */
  --gp-rangees: 181;

  /* Land south of this latitude is dropped. The reference leaves Antarctica
     out; set -90 to draw it. */
  --gp-latitude-min: -60;

  /* Dot diameters, as a fraction of the globe radius. They deliberately do
     NOT follow --gp-rangees: on the reference a dot keeps the same diameter
     whether the lattice has 181 rows or 227, so a denser globe reads as a
     fuller one. Divide these by (rangees / 181) if you would rather keep the
     same look at any density. */
  --gp-point: 0.0057;
  --gp-point-cote: 0.007;

  /* Coastline density, as a multiple of --gp-rangees. The outline is the same
     lattice, finer, kept only where land touches water. 1.5 with the coast dot
     above puts exactly as much ink on the picture as the reference does, to
     within 1 %, while keeping the outline visibly fatter than the fill —
     which is what the reference shows at 4x zoom. */
  --gp-cote-densite: 1.5;

  /* ---- Ink ------------------------------------------------------------ */

  --gp-fond: #171717;
  --gp-encre: #ffffff;
  --gp-encre-terres: 0.7; /* land fill, relative to the coastline ink */

  /* ---- Pins ----------------------------------------------------------- */

  --gp-epingle: 0.098; /* diameter, in globe radii */
  --gp-epingle-couleur: #ffffff;
  --gp-halo: 0.164; /* glow reach, in globe radii */
  --gp-halo-force: 0.55; /* 0 removes the glow entirely */

  /* ---- Cards ---------------------------------------------------------- */

  /* The reference sets a 10.007 px advance on a 0.6 em monospace, which is a
     16.68 px body. Three independent readings — the column comb, the box
     width and the box height — agree on it to within 0.2 px. */
  --gp-corps: 16.68px;
  --gp-police: ui-monospace, "SFMono-Regular", "SF Mono", Menlo, Consolas,
    "Liberation Mono", monospace;
  --gp-carte-fond: rgba(255, 255, 255, 0.24);
  --gp-carte-filet: rgba(255, 255, 255, 0.85);
  --gp-carte-texte: #ffffff;
  --gp-carte-flou: 8px;
  --gp-carte-rayon: 0.5096em;
  --gp-carte-marge: 0.7255em;
  --gp-carte-interligne: 1.379;
  /* Where the card sits relative to its pin. The reference drops the card on
     top of its own point; nudging it away keeps the pin visible. Set both to
     0 to get the reference placement back. */
  --gp-carte-x: 0.939em;
  --gp-carte-y: -0.939em;

  /* ---- Motion --------------------------------------------------------- */

  --gp-tour: 72s; /* one full revolution when idle */
  --gp-glisse: 620ms; /* travel time when a pin is brought to the front */
  --gp-fondu: 220ms; /* card fade */
  --gp-retard: 60ms; /* card fade delay, so travel reads before the card */

  position: relative;
  box-sizing: border-box;
  display: block;
  container-type: size;
  inline-size: 100%;
  min-block-size: 22em;
  color: var(--gp-carte-texte);
  background-color: var(--gp-fond);
  font-family: var(--gp-police);
  font-size: var(--gp-corps);
}

.globe-pins *,
.globe-pins *::before,
.globe-pins *::after {
  box-sizing: border-box;
}

/* ==========================================================================
   2. Frame and canvas
   ========================================================================== */

.globe-pins__scene {
  position: relative;
  display: block;
  inline-size: 100%;
  block-size: 100%;
  min-block-size: inherit;
  overflow: clip;
}

.globe-pins__toile {
  position: absolute;
  inset: 0;
  display: none; /* nothing to show until the script has drawn something */
  inline-size: 100%;
  block-size: 100%;
  touch-action: none;
}

.globe-pins[data-gp-pret] .globe-pins__toile {
  display: block;
  cursor: grab;
}

.globe-pins[data-gp-tire] .globe-pins__toile {
  cursor: grabbing;
}

/* ==========================================================================
   3. Fallback — the plain address list
   --------------------------------------------------------------------------
   This is the state before the script runs, without JavaScript, and under
   forced colours. It has to read as an ordinary list of offices.
   ========================================================================== */

.globe-pins__liste {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(15em, 100%), 1fr));
  gap: 1.5em;
  margin: 0;
  padding: 2.5em;
  list-style: none;
}

.globe-pins__lieu {
  margin: 0;
  padding: 0;
}

/* Before the script, and without it, the button leads nowhere: the addresses
   are all on screen already. So it is not rendered at all — no dead control
   in the tab order. */
.globe-pins__epingle {
  display: none;
  margin: 0;
  padding: 0;
  color: inherit;
  font: inherit;
  background: none;
  border: 0;
  cursor: pointer;
}

.globe-pins__carte {
  display: block;
  padding: var(--gp-carte-marge);
  line-height: var(--gp-carte-interligne);
  background-color: var(--gp-carte-fond);
  border: 1px solid var(--gp-carte-filet);
  border-radius: var(--gp-carte-rayon);
}

.globe-pins__pays {
  display: block;
  font-weight: 700;
}

.globe-pins__ligne {
  display: block;
  font-weight: 400;
}

/* ==========================================================================
   4. Pins — the enhanced state
   --------------------------------------------------------------------------
   Once the script runs, the list stops being a grid and each item is placed
   over the canvas at the projected position of its coordinates. The script
   only ever writes two custom properties per item, --gp-x and --gp-y, so a
   strict Content-Security-Policy is never in the way.
   ========================================================================== */

/* The list covers the whole scene to act as a reference frame for the pins.
   Without pointer-events: none it would intercept every pointerdown before the
   canvas, and the globe could no longer be grabbed: a transparent box is still
   a hover target. Only the buttons take the events back. */
.globe-pins[data-gp-pret] .globe-pins__liste {
  position: absolute;
  inset: 0;
  display: block;
  padding: 0;
  pointer-events: none;
}

.globe-pins[data-gp-pret] .globe-pins__lieu {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  translate: var(--gp-x, 0) var(--gp-y, 0);
  will-change: translate;
}

.globe-pins[data-gp-pret] .globe-pins__epingle {
  position: absolute;
  inset-block-start: 0;
  inset-inline-start: 0;
  display: block;
  inline-size: calc(var(--gp-rayon-epingle, 12px) * 2);
  block-size: calc(var(--gp-rayon-epingle, 12px) * 2);
  translate: -50% -50%;
  border-radius: 50%;
  pointer-events: auto;
}

/* Behind the globe: gone from the picture, and shrunk to a transparent pixel
   parked on the limb. It stays in the tab order on purpose — reaching it is
   how a keyboard visitor brings that place round to the front. A control set
   to display:none could not be focused at all. */
.globe-pins[data-gp-pret] .globe-pins__lieu[data-gp-cache] .globe-pins__carte {
  opacity: 0;
  visibility: hidden;
}

.globe-pins[data-gp-pret] .globe-pins__lieu[data-gp-cache] .globe-pins__epingle {
  inline-size: 1px;
  block-size: 1px;
  opacity: 0;
}

/* The disc itself is painted on the canvas: the button only carries the hit
   area, the focus ring and the accessible name. */
.globe-pins[data-gp-pret] .globe-pins__epingle:focus-visible {
  outline: 2px solid var(--gp-epingle-couleur);
  outline-offset: 4px;
}

.globe-pins__nom {
  position: absolute;
  inline-size: 1px;
  block-size: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* ==========================================================================
   5. Cards — the enhanced state
   ========================================================================== */

.globe-pins[data-gp-pret] .globe-pins__carte {
  position: absolute;
  inset-block-end: calc(var(--gp-carte-y) * -1);
  inset-inline-start: var(--gp-carte-x);
  inline-size: max-content;
  max-inline-size: 16em;
  background-color: var(--gp-carte-fond);
  -webkit-backdrop-filter: blur(var(--gp-carte-flou));
  backdrop-filter: blur(var(--gp-carte-flou));
  opacity: 1;
  transition:
    opacity var(--gp-fondu) ease var(--gp-retard),
    visibility 0s linear 0s;
  pointer-events: none;
}

/* A card near the edge of the frame would be clipped. These two modifiers flip
   it across its pin instead of moving the pin. The list item has no width and
   no height, so anchoring the card by its far edge is enough. */
.globe-pins[data-gp-pret] .globe-pins__lieu--gauche .globe-pins__carte {
  inset-inline-start: auto;
  inset-inline-end: var(--gp-carte-x);
}

.globe-pins[data-gp-pret] .globe-pins__lieu--bas .globe-pins__carte {
  inset-block-end: auto;
  inset-block-start: calc(var(--gp-carte-y) * -1);
}

/* Cards on demand: hidden until their pin is hovered, focused or pressed. */
.globe-pins[data-gp-pret][data-gp-cartes="au-focus"] .globe-pins__carte {
  opacity: 0;
  visibility: hidden;
  transition:
    opacity var(--gp-fondu) ease,
    visibility 0s linear var(--gp-fondu);
}

.globe-pins[data-gp-pret][data-gp-cartes="au-focus"] .globe-pins__lieu:not([data-gp-cache]):is(:hover, :focus-within) .globe-pins__carte,
.globe-pins[data-gp-pret][data-gp-cartes="au-focus"] .globe-pins__lieu[data-gp-actif]:not([data-gp-cache]) .globe-pins__carte {
  opacity: 1;
  visibility: visible;
  transition:
    opacity var(--gp-fondu) ease var(--gp-retard),
    visibility 0s linear 0s;
}

/* ==========================================================================
   6. Motion
   ========================================================================== */

.globe-pins[data-gp-pret] .globe-pins__lieu {
  transition: translate 0s;
}

/* ==========================================================================
   7. Variants
   ========================================================================== */

.globe-pins--cyan {
  --gp-fond: #000000;
  --gp-encre: #42cae9;
  --gp-epingle-couleur: #9ceeff;
  --gp-halo-force: 1;
  --gp-carte-fond: rgba(20, 60, 72, 0.55);
  --gp-carte-filet: rgba(156, 238, 255, 0.5);
  --gp-carte-texte: #d6f6ff;
}

.globe-pins--clair {
  --gp-fond: #fafdff;
  --gp-encre: #668cb3;
  --gp-rangees: 227;
  --gp-epingle-couleur: #1d4e78;
  --gp-halo-force: 0.25;
  --gp-carte-fond: rgba(255, 255, 255, 0.72);
  --gp-carte-filet: rgba(29, 78, 120, 0.22);
  --gp-carte-texte: #12283c;
}

/* Half the type, half the globe: for a sidebar or a card. */
.globe-pins--compact {
  --gp-corps: 13px;
  --gp-rangees: 128;
  --gp-remplissage: 0.82;
  min-block-size: 15em;
}

/* ==========================================================================
   8. Fallbacks
   ========================================================================== */

/* No motion: the globe stops turning and the card travel is instant. A delay
   is motion spread over time, so the delays go to zero as well. */
@media (prefers-reduced-motion: reduce) {
  .globe-pins {
    --gp-glisse: 0ms;
    --gp-fondu: 0ms;
    --gp-retard: 0ms;
  }
}

/* Forced colours drop the canvas to nothing, so the component goes back to
   being the address list it started as. */
@media (forced-colors: active) {
  .globe-pins[data-gp-pret] .globe-pins__toile {
    display: none;
  }

  .globe-pins[data-gp-pret] .globe-pins__liste {
    position: static;
    display: grid;
    padding: 2.5em;
  }

  .globe-pins[data-gp-pret] .globe-pins__lieu,
  .globe-pins[data-gp-pret] .globe-pins__epingle,
  .globe-pins[data-gp-pret] .globe-pins__carte {
    position: static;
    inline-size: auto;
    block-size: auto;
    max-inline-size: none;
    translate: none;
    opacity: 1;
    visibility: visible;
    pointer-events: auto;
  }

  .globe-pins__nom {
    position: static;
    inline-size: auto;
    block-size: auto;
    margin: 0;
    overflow: visible;
    clip-path: none;
  }

  .globe-pins__carte {
    border-color: CanvasText;
  }
}
