/* Plain CSS only. Tailwind component classes live in the inline
   <style type="text/tailwindcss"> block in index.html, because the Play CDN
   does not compile linked stylesheets. */

.maplibregl-ctrl-attrib,
.maplibregl-ctrl-bottom-right,
.maplibregl-ctrl-bottom-left { display: none !important; }
.maplibregl-canvas { outline: none; }

/* Compare handle: a full-height bar with a grip in the middle. The handle is
   the only thing over the map that takes pointer events; the "today" map
   underneath it is pointer-events: none so the base map keeps panning. */
#compare-handle { width: 0; cursor: ew-resize; touch-action: none; }
.compare-line {
  position: absolute; inset: 0 auto 0 -1px; width: 2px; background: #fff;
  box-shadow: 0 0 0 1px rgb(0 0 0 / 0.25);
}
.compare-grip {
  position: absolute; top: 88%; left: 50%; transform: translate(-50%, -50%);
  display: flex; align-items: center; justify-content: center;
  width: 40px; height: 40px; border-radius: 999px;
  background: #fff; color: #1c1917;
  box-shadow: 0 2px 8px rgb(0 0 0 / 0.35), 0 0 0 1px rgb(0 0 0 / 0.1);
  transition: transform 120ms;
}
#compare-handle.dragging .compare-grip { transform: translate(-50%, -50%) scale(1.08); }
/* A wider invisible hit area so the 2 px line is easy to grab. */
#compare-handle::before { content: ''; position: absolute; inset: 0 -14px; }
.compare-tag {
  /* 2 cm below the top edge: level with the map rather than tucked against the
     controls along the top of it */
  position: absolute; top: calc(0.75rem + 2cm); padding: 5px 14px; border-radius: 9px;
  font-size: 15px; font-weight: 700; letter-spacing: 0.01em; white-space: nowrap;
  /* light enough to read the imagery through: they caption the halves, they do
     not need to sit on a solid slab */
  color: #fff; background: rgb(28 25 23 / 0.55); backdrop-filter: blur(4px);
  box-shadow: 0 1px 4px rgb(0 0 0 / 0.3);
  pointer-events: none;
}
.compare-tag-left  { right: 12px; }
.compare-tag-right { left: 12px; }

/* Live location. Positioned from map.project() like the memory pins, for the
   same reason: a Marker would sit under the "today" half of the split. */
.locate-dot {
  position: absolute; left: 0; top: 0; width: 14px; height: 14px;
  margin: -7px 0 0 -7px; border-radius: 50%;
  background: #3b82f6;
  box-shadow: 0 0 0 2.5px #fff, 0 1px 5px rgb(0 0 0 / 0.5);
  will-change: transform;
}

/* Scale bar: the ruler line next to the distance label. */
.scale-bar {
  display: inline-block; height: 7px; width: 40px;
  border: 1px solid #57534e; border-top: 0;
  transition: width 120ms ease-out;
}

#results li[aria-selected="true"] { background: #f5f5f4; }

/* ── memories ────────────────────────────────────────────────────────
   Pins are HTML in #memories-layer, positioned by memories.js with a
   translate() from map.project(). Each element anchors itself with margins
   so the translate lands on the geographic point. */
#memories-layer > * {
  position: absolute; left: 0; top: 0; pointer-events: auto; will-change: transform;
  /* memories.js sets --x/--y from map.project(). The hover scale lives in the
     same transform on purpose: the separate `scale` property composes with
     `transform`, which multiplies the translate too and sends the pin flying
     away from the cursor.
     --zs is the zoom scale, set once on the layer and inherited: a pin sized
     for a street is a landmark when the whole island is on screen, and a
     hundred of them at full size is a wall of markers rather than a map. It
     multiplies with the hover scale rather than replacing it. */
  transform: translate(var(--x, 0px), var(--y, 0px)) scale(calc(var(--zs, 1) * var(--s, 1)));
}

.mem-pin {
  /* the margin anchors the tip of the pin on the point, not its centre, and the
     origin keeps it there through both scales -- from the centre, shrinking
     would walk the tip off the thing it is pointing at */
  transform-origin: 50% 100%;
  width: 30px; height: 41px; margin: -41px 0 0 -15px;
  background: none; border: 0; padding: 0;
  cursor: pointer; overflow: visible;
  /* drop-shadow follows the teardrop; box-shadow would draw a rectangle's */
  filter: drop-shadow(0 2px 3px rgb(0 0 0 / 0.5));
}
/* --c is the type colour, set per pin in memories.js, and the only thing that
   tells one kind of memory from another. paint-order puts the stroke under the
   fill so the white reads as an outline around the shape rather than a band
   eating into it. */
.mem-pin svg {
  width: 100%; height: 100%; display: block;
  fill: var(--c, #78350f); stroke: #fff; stroke-width: 2;
  paint-order: stroke fill; transition: stroke 120ms;
}
/* over the head of the pin, in viewBox proportions so it holds at any size */
.mem-pin img {
  position: absolute; left: 16.67%; top: 9.1%; width: 66.67%; height: 48.5%;
  border-radius: 50%; object-fit: cover; display: block;
}
.mem-pin:hover, .mem-pin.is-open { --s: 1.12; z-index: 1; }
.mem-pin:hover svg, .mem-pin.is-open svg { stroke: #f59e0b; }

.mem-cluster {
  min-width: 36px; height: 36px; padding: 0 8px; margin: -18px 0 0 -18px;   /* anchor: centre */
  border-radius: 999px; background: #1c1917; color: #fff;
  font: 600 13px/36px Inter, system-ui, sans-serif;
  box-shadow: 0 0 0 2.5px #fff, 0 2px 6px rgb(0 0 0 / 0.45);
  cursor: pointer;
}
.mem-cluster:hover { --s: 1.08; }

.mem-draft {
  width: 18px; height: 18px; margin: -9px 0 0 -9px;
  border-radius: 50%; background: #f59e0b; box-shadow: 0 0 0 3px #fff, 0 2px 6px rgb(0 0 0 / 0.5);
  pointer-events: none !important;
}
.mem-draft::before {
  content: ''; position: absolute; inset: -12px; border-radius: 50%;
  border: 2px solid #f59e0b; opacity: 0.7;
  animation: mem-pulse 1.4s ease-out infinite;
}
@keyframes mem-pulse { 0% { transform: scale(0.5); opacity: 0.8; } 100% { transform: scale(1.4); opacity: 0; } }

/* honeypot: present in the form, invisible to people */
.hp { position: absolute; left: -9999px; top: 0; width: 1px; height: 1px; overflow: hidden; }
/* the form's type picker */
.type-opt { display: none; }
.type-opt + span {
  display: flex; flex-direction: column; align-items: center; gap: 3px;
  padding: 8px 4px 6px; border-radius: 10px; border: 1px solid #d6d3d1;
  font-size: 12px; font-weight: 600; color: #57534e; cursor: pointer; text-align: center;
  transition: border-color 120ms, background 120ms;
}
.type-opt + span svg { width: 20px; height: 20px; color: var(--c); }
.type-opt + span:hover { background: #fafaf9; }
.type-opt:checked + span { border-color: #1c1917; background: #fafaf9; color: #1c1917; box-shadow: inset 0 0 0 1px #1c1917; }

/* ── mobile ──────────────────────────────────────────────────────────
   Collapsed, the Imagery panel keeps its full 17rem even when it shows
   nothing but a title, which on a 390 px screen sits on top of the search
   box. Shrink it to the icon so the top row fits. */
@media (max-width: 639px) {
  /* the attribution, once tapped open: wrap it instead of truncating, or the
     thing the tap was for is still cut off at the edge of the screen */
  #attribution-chip.is-open #attribution { overflow: visible; white-space: normal; text-overflow: clip; }

  /* The tags stay either side of the handle here as well: they label the two
     halves of the split, and a tag in a corner labels nothing. They only drop
     down the screen. The search is collapsed behind the magnifier here, so the
     top controls are a single row and the tags sit just under it: 0.75rem inset
     + 2.75rem button + the same 0.5rem the bar uses between its own controls.
     Opening the search adds a second row, so they drop back below it. */
  /* smaller and lighter here: at 15px on a dark slab they read as buttons you
     could press, when they only caption the two halves of the split */
  .compare-tag {
    top: calc(4rem + 0.5cm);
    font-size: 13px; padding: 3px 10px; border-radius: 7px;
  }
  body.search-open .compare-tag { top: calc(7.5rem + 0.5cm); }

  /* the grip lifts a centimetre clear of the map buttons and the status bar,
     which all crowd the bottom of the line on a phone */
  .compare-grip { top: calc(88% - 1cm); }

  /* Opened, the attribution lifts clear of the bar instead of widening it. In
     flow it wraps to a row of its own, and since the bar is anchored to the
     bottom the chips row -- the copyright button with it -- gets pushed up the
     screen, so the thing just tapped jumps out from under the thumb that
     tapped it. Fixed above the bar, nothing else moves. */
  #attribution-chip.is-open {
    position: fixed; left: 0.75rem; right: 0.75rem; bottom: 3.75rem;
    max-width: none;
  }
  #imagery-panel.is-collapsed { width: auto; }
  #imagery-panel.is-collapsed #panel-title { display: none; }
  #imagery-panel.is-collapsed #panel-toggle { gap: 0.25rem; }
}

/* Touch targets. iOS HIG and Material both want ~44px; the panel rows were
   32px and the map buttons 40px. Scoped to coarse pointers so the desktop
   layout is untouched. */
@media (pointer: coarse) {
  .seg { min-height: 44px; }
  [role="switch"] { min-height: 44px; }
  /* button.mapbtn, not .mapbtn: @apply h-10 lands in the Tailwind layer, which
     the CDN injects after this file, so a bare class selector loses the tie. */
  button.mapbtn { height: 44px; width: 44px; }
  #panel-toggle { min-height: 48px; }
  #search { padding-top: 6px; padding-bottom: 6px; }
  .type-opt + span { min-height: 44px; }
  /* Memory pins are the main thing to tap on the map. The margin anchors the
     pin's tail on the geographic point, so it has to grow with the pin:
     left = -width/2, top = -(height + 8px tail). */
  .mem-pin { width: 38px; height: 52px; margin: -52px 0 0 -19px; }
}

/* ── brand: "Cyprimento" + (i) ───────────────────────────────────────
   Hovering anywhere on the pill opens the card. The card is inside #brand
   and its padding-top bridges the gap, so the hover survives the move
   down into it. A click/tap on the (i) pins it via aria-expanded. */
#brand:hover #brand-pop,
#brand:has(:focus-visible) #brand-pop,
#brand:has(#brand-info[aria-expanded="true"]) #brand-pop { display: block; }
/* No hover on touch: only a tap opens the card. */
@media (hover: none) {
  #brand:hover #brand-pop { display: none; }
  #brand:has(#brand-info[aria-expanded="true"]) #brand-pop { display: block; }
}

/* ── measure ─────────────────────────────────────────────────────────
   Screen-space polyline redrawn from map.project() on every move, so it
   sits above both halves of the compare split like the memory pins. */
#measure-line {
  fill: none; stroke: #f59e0b; stroke-width: 3; stroke-linejoin: round; stroke-linecap: round;
  paint-order: stroke; filter: drop-shadow(0 0 1.5px rgb(0 0 0 / 0.7));
}
#measure-points circle { fill: #fff; stroke: #f59e0b; stroke-width: 3; }
.measure-tag {
  position: absolute; left: 0; top: 0; transform: translate(var(--x), var(--y)) translate(12px, -50%);
  padding: 3px 9px; border-radius: 7px; white-space: nowrap;
  font-size: 13px; font-weight: 700; font-variant-numeric: tabular-nums;
  color: #fff; background: rgb(28 25 23 / 0.85); box-shadow: 0 1px 4px rgb(0 0 0 / 0.3);
}

/* ── introduction ─────────────────────────────────────────────────────
   A first visit lands on the whole island before it lands on a street.
   Plain CSS, deliberately: the panel has to be right at the first paint,
   and Tailwind arrives here as a script (the Play CDN in development, a
   compiled sheet in production), so a class-built version would flash
   unstyled prose across the screen on a slow connection.

   display:none is the default and one attribute on <html> reveals it, so
   the markup can sit in the page for a returning visitor without ever
   being seen. */
#intro { display: none; }
html[data-intro] #intro {
  display: flex;
  position: fixed; inset: 0; z-index: 60;
  align-items: center; justify-content: center;
  padding: 2rem 1.5rem;
  overflow: hidden;
  background: #0b0a09;
  transition: opacity 650ms ease;
}
html[data-intro="out"] #intro { opacity: 0; pointer-events: none; }

/* The backdrop is the survey itself -- the island as it was photographed,
   cut out on its own no-data alpha (lib/tone.js gave it its contrast back).
   Dimmed hard: it is the ground the words stand on, not the subject. */
.intro-island {
  position: absolute; left: 50%; top: 50%;
  width: min(1500px, 122vw); height: auto;
  transform: translate(-50%, -50%);
  opacity: 0.2;
  filter: contrast(1.05);
  animation: intro-drift 30s cubic-bezier(0.2, 0, 0.2, 1) both;
  pointer-events: none;
}
@keyframes intro-drift {
  from { opacity: 0; transform: translate(-50%, -50%) scale(1.10); }
  25%  { opacity: 0.2; }
  to   { opacity: 0.2; transform: translate(-50%, -50%) scale(1); }
}
/* Weighted towards the bottom, where the smaller print sits. */
.intro-veil {
  position: absolute; inset: 0; pointer-events: none;
  background: radial-gradient(120% 80% at 50% 38%, rgb(11 10 9 / 0.35) 0%, rgb(11 10 9 / 0.82) 62%, #0b0a09 100%);
}

.intro-body {
  position: relative;
  width: min(51rem, 100%);
  max-height: 100%;
  /* hidden until the words have landed, then auto: every line animates in from
     14 px below where it belongs, so for the second or so that the stagger is
     running the content is taller than the box and a scrollbar flashes down the
     side of the panel. It is only ever needed on a short screen, and only after
     the text has settled. */
  overflow-y: hidden;
  text-align: center;
  color: #ede9e3;
}
.intro-h {
  margin-top: 0;
  font-size: clamp(1.75rem, 5.2vw, 3.05rem); font-weight: 700; letter-spacing: -0.02em;
  line-height: 1.1; color: #fff;
  text-wrap: balance;
}
.intro-body.is-settled { overflow-y: auto; }
/* The definition sits under the title and sets up everything below it, so it
   reads as the epigraph it is: quieter than the prose, and inset from it --
   but only just. Pulled in much further and the quoted clause breaks into a
   narrow column of its own while the paragraphs beside it run wide, which
   reads as a mistake rather than as emphasis. The measure only bites on a
   screen wide enough to have the room; below that the padding decides. */
.intro-def {
  /* clamped rather than fixed: the air under the title is what makes the
     definition read as an epigraph instead of a subtitle, but a phone pays for
     it in scroll, so it opens up only where there is room. */
  margin: clamp(1.8rem, 4.5vw, 2.9rem) auto 0; max-width: 46rem;
  font-size: clamp(0.85rem, 1.5vw, 1.05rem); line-height: 1.6; color: #a8a29e;
  text-wrap: balance;
}
.intro-def em { font-style: italic; color: #d6d3d1; }
.intro-body p.intro-p {
  margin-top: clamp(1.4rem, 3.2vw, 2rem);
  font-size: clamp(0.95rem, 1.7vw, 1.2rem); line-height: 1.6; color: #d6d3d1;
  /* balance, not pretty: pretty only leans against a short last line and still
     leaves one when the alternative scores worse, while balance evens every
     line in the block and there is nowhere for a stray word to sit. Browsers
     abandon it past about six lines -- a paragraph on a small phone -- which is
     what the non-breaking space before each closing word is for. */
  text-wrap: balance;
}
.intro-body p.intro-p b { font-weight: 600; color: #f5f5f4; }
.intro-body em { font-style: italic; }

.intro-actions { margin-top: clamp(2.2rem, 4.5vw, 2.9rem); }
#intro-go {
  display: inline-flex; align-items: center; gap: 0.6rem;
  cursor: pointer;              /* the only thing on this page that does anything */
  border-radius: 0.75rem; padding: clamp(0.72rem, 1.1vw, 0.85rem) clamp(1.4rem, 2vw, 1.65rem);
  background: #faf8f4; color: #1c1917;
  font-size: clamp(15px, 1.15vw, 16.5px); font-weight: 600;
  box-shadow: 0 6px 24px rgb(0 0 0 / 0.45);
  transition: transform 120ms ease, background-color 120ms ease;
}
#intro-go:hover { background: #fff; transform: translateY(-1px); }
#intro-go:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
/* Reads as a caption, because that is what it is: the one thing on this
   page a reader could otherwise take for evidence. */
.intro-note {
  margin: 2.25rem auto 0; max-width: 34rem;
  border-top: 1px solid rgb(255 255 255 / 0.1); padding-top: 1rem;
  font-size: 12px; line-height: 1.55; color: #78716c;
  text-wrap: balance;
}

/* WhatsApp, under the credits: the one place on this page anybody can answer
   from. Its green is the only colour on the screen, which is the point -- it
   has to be findable without competing with the button above it, so the
   surround stays an outline rather than a second filled pill. */
.intro-contact {
  display: inline-flex; align-items: center; gap: 0.55rem;
  margin-top: 1.15rem; padding: 0.5rem 1rem;
  border: 1px solid rgb(255 255 255 / 0.14); border-radius: 999px;
  font-size: 13px; line-height: 1;
  transition: background-color 120ms ease, border-color 120ms ease;
}
.intro-contact:hover { background: rgb(255 255 255 / 0.07); border-color: rgb(255 255 255 / 0.3); }
.intro-contact:focus-visible { outline: 2px solid #fff; outline-offset: 3px; }
.intro-contact svg { color: #25d366; flex: none; }
.intro-contact span { font-weight: 500; color: #d6d3d1; }

/* Each line arrives just behind the one above it. 90 ms apart: enough to
   read as a sequence, not enough to make anyone wait for the button. */
.intro-body > * { animation: intro-rise 800ms cubic-bezier(0.16, 0.9, 0.3, 1) both; }
.intro-body > :nth-child(1) { animation-delay: 120ms; }
.intro-body > :nth-child(2) { animation-delay: 320ms; }
.intro-body > :nth-child(3) { animation-delay: 540ms; }
.intro-body > :nth-child(4) { animation-delay: 740ms; }
.intro-body > :nth-child(5) { animation-delay: 960ms; }
.intro-body > :nth-child(6) { animation-delay: 1200ms; }
.intro-body > :nth-child(7) { animation-delay: 1340ms; }
@keyframes intro-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}

/* Someone who has asked for less movement still gets the page, at once and
   still. The drift and the stagger are decoration; the words are not. */
@media (prefers-reduced-motion: reduce) {
  .intro-island { animation: none; opacity: 0.2; transform: translate(-50%, -50%); }
  .intro-body > * { animation: none; }
  .intro-body { overflow-y: auto; }      /* nothing to settle: it is already still */
  html[data-intro] #intro { transition: none; }
}
