/* The place map — the archive's one chart of the ground it was made on.
 *
 * Loaded by map.html and by browse.html (the locator in a /place/<slug>
 * gutter). One stylesheet for one component, rather than a block in base.css:
 * nothing BAKED into S3 draws this chart, so unlike the masonry and the
 * lightbox it needs no rebuild-shoot-html to get its styling, and base.css is
 * long enough already.
 *
 * It introduces NO colour. Everything here is base.css's tokens — the cream
 * ramp, the one gold, the hairline — because a chart that brought its own
 * palette would be the cartography talking over the photographs.
 */

.pm {
  --pm-ink: rgba(240, 234, 216, 0.38);   /* the outline */
  --pm-river: rgba(232, 168, 0, 0.30);   /* one river, named in the tool */
  --pm-thread: rgba(240, 234, 216, 0.42);
  --pm-dot: rgba(240, 234, 216, 0.68);
  --pm-dim: 0.34;                        /* what an unlit frame falls to */
  position: relative;
}

/* ── The plate ─────────────────────────────────────────────────────────────
 *
 * WIDE on purpose. Poland is very nearly square (1.05:1), so an outline sized
 * to the plate's height in a 16:10 box leaves a real margin down each side —
 * which is where the frames pushed out of the Warsaw cluster go, and what
 * turns the leader lines from stubs into the thing that carries the drawing.
 * A square plate would jam every displaced frame back over the map.
 */

.pm-plate {
  position: relative;
  width: 100%;
  /* Capped, and centred in whatever it is given. Unbounded, a 16:10 plate at a
     2560px window is 1600px of Poland — a wallpaper with thirteen photographs
     on it rather than a chart. */
  max-width: var(--pm-max, 1500px);
  margin-inline: auto;
  aspect-ratio: 16 / 10;
  /* And capped by HEIGHT as well, which is what makes a wide window spend its
     extra pixels on the right thing. The outline is fitted with `meet`, so a
     box wider than 16:10 does not enlarge Poland — it adds clear ground down
     both sides, which is exactly where the frames pushed out of the Warsaw
     cluster need to go. A taller plate would only push the heading off the
     screen above it. */
  max-height: min(72vh, 720px);
  /* The floor is the band's largest frame plus its clear space: below this the
     chart drops to points anyway (see `.pm--points`), so there is nothing to
     be gained by letting the plate get shorter than one photograph. */
  min-height: 240px;
}

.pm-ground,
.pm-threads {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: visible;
}

.pm-border {
  fill: none;
  stroke: var(--pm-ink);
  stroke-width: 1.15;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.pm-river {
  fill: none;
  stroke: var(--pm-river);
  stroke-width: 1;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
}

.pm-thread {
  stroke: var(--pm-thread);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
  transition: stroke 0.25s ease, opacity 0.25s ease;
}

.pm-dot {
  fill: var(--pm-dot);
  transition: fill 0.25s ease, opacity 0.25s ease;
}

/* ── The frames ────────────────────────────────────────────────────────────
 *
 * A place is its lead photograph at ITS OWN ASPECT. Nothing here crops: a
 * fixed square or 3:2 box would take a third off every portrait, and these are
 * thirteen pictures rather than thirteen avatars.
 */

.pm-frames {
  position: absolute;
  inset: 0;
}

.pm-frame {
  position: absolute;
  display: block;
  text-decoration: none;
  color: var(--color-text);
  background: var(--color-surface);
  /* The hairline is what makes a photograph read as a FRAME pinned to a chart
     rather than as a picture lying loose on it. */
  box-shadow: 0 0 0 1px rgba(240, 234, 216, 0.16),
              0 10px 26px -12px rgba(0, 0, 0, 0.85);
  transition: box-shadow 0.3s cubic-bezier(0.22, 1, 0.36, 1),
              opacity 0.3s ease,
              transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}

/* The clip is on the PHOTOGRAPH, not on the frame, so the name tag can hang
   below the frame's own edge instead of being cut off by it. */
.pm-shot {
  position: absolute;
  inset: 0;
  display: block;
  overflow: hidden;
}

.pm-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform 0.6s cubic-bezier(0.22, 1, 0.36, 1);
}

/* ── The name ──────────────────────────────────────────────────────────────
 *
 * BESIDE the photograph, and only for the one place that is lit. Ten of the
 * archive's thirteen places sit inside one third of the country, so a caption
 * on every frame was five wrapped lines of "Lotnisko Leszno im. Adeli
 * Dankowskiej" spilling out of a 76-pixel box. Shrinking the type to fit made
 * it illegible; growing the frame to fit made the cluster insoluble; truncating
 * it turned thirteen places into thirteen prefixes.
 *
 * One tag at a time has the whole plate to sit on, so it needs none of those
 * compromises — and the resting chart is what this site says every surface
 * should be: photographs, with the interface out of the way until it is asked
 * for. The names are permanently legible in /map's index below, which is also
 * what a phone gets there in place of the frames.
 */

.pm-tag {
  position: absolute;
  top: calc(100% + 9px);
  left: 50%;
  transform: translateX(-50%) translateY(-3px);
  width: max-content;
  max-width: 210px;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.15em;
  padding: 0.4em 0.6em;
  text-align: center;
  background: rgba(12, 11, 8, 0.94);
  box-shadow: 0 0 0 1px rgba(232, 168, 0, 0.5),
              0 10px 26px -14px rgba(0, 0, 0, 0.9);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  z-index: 3;
  transition: opacity 0.22s ease, transform 0.22s cubic-bezier(0.22, 1, 0.36, 1),
              visibility 0s linear 0.22s;
}

/* Near the plate's bottom edge the tag flips above the frame — set in layout(),
   because only the solve knows where a frame ended up. */
.pm-frame--tagup .pm-tag {
  top: auto;
  bottom: calc(100% + 9px);
  transform: translateX(-50%) translateY(3px);
}

.pm-frame.on .pm-tag {
  opacity: 1;
  visibility: visible;
  transform: translateX(-50%);
  transition: opacity 0.22s ease, transform 0.22s cubic-bezier(0.22, 1, 0.36, 1),
              visibility 0s;
}

.pm-name {
  font-size: 0.72rem;
  line-height: 1.25;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--color-text);
}

.pm-n {
  /* 0.7rem is 11.2px — the floor below which small functional text stops being
     legible on a high-DPI phone, and the size the site's own `.page-label`
     already sits at. It was 0.6rem, which is 9.6. */
  font-size: 0.7rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  /* 0.62 cream on the tag's near-opaque ground clears 4.5:1. */
  color: rgba(240, 234, 216, 0.62);
}

/* ── Lighting ──────────────────────────────────────────────────────────────
 *
 * Gold is spent on ONE thing here: the place under the cursor, the keyboard, or
 * the locator's own subject. Everything else falls back, because a highlight
 * that only brightens one frame is invisible against twelve others — and the
 * dimming is also what lets a long leader be read across the middle of a plate
 * full of photographs.
 */

.pm--lit .pm-frame { opacity: var(--pm-dim); }
.pm--lit .pm-frame.on { opacity: 1; }

.pm-frame.on {
  box-shadow: 0 0 0 1px var(--color-accent),
              0 16px 34px -12px rgba(0, 0, 0, 0.9);
  transform: translateY(-3px);
  /* Above its neighbours: the frames overlap where the archive is dense, and a
     lit frame half-covered by a dark one is the opposite of a highlight. */
  z-index: 2;
}

.pm-frame.on .pm-shot img { transform: scale(1.045); }
.pm-frame.on .pm-name { color: var(--color-accent); }

.pm--lit .pm-thread { opacity: 0.25; }
.pm--lit .pm-dot { opacity: 0.25; }

.pm-thread.on {
  stroke: var(--color-accent);
  opacity: 1;
}

.pm-dot.on {
  fill: var(--color-accent);
  opacity: 1;
}

.pm-frame:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 3px;
}

/* ── Points ────────────────────────────────────────────────────────────────
 *
 * Under ~560px, and always in the /place/<slug> locator. Not a degraded
 * desktop layout: thirteen annotated photographic insets cannot be drawn at
 * 390px, and pinch-zoom is not an answer on a page that reflows. The chart
 * keeps the one job it can still do — saying where the places are — and on
 * /map the index below it becomes the control.
 */

.pm--points .pm-frames { display: none; }

.pm--points .pm-plate { aspect-ratio: 1 / 1; }

/* ── The index ─────────────────────────────────────────────────────────────
 *
 * /map's other half, and the chart's keyboard equivalent. Ordered by size like
 * the chart, so the tab order and the reading order are the same order.
 */

.pm-index {
  list-style: none;
  margin: 2.5rem 0 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 20rem), 1fr));
  /* Columns are separated by WHITESPACE, rows by a hairline. Vertical rules
     were tried twice and both versions break on the same thing: thirteen
     places in a four-column grid leave three cells empty, so a coloured
     container showing through a 1px gap lights a block of nothing at the end
     of the list, and per-cell borders leave the closing rule running under one
     cell of a four-wide row. With space between the columns each one simply
     reads as its own list, and a list ending in a rule under its last item is
     what every list here does. */
  column-gap: clamp(1.5rem, 3vw, 3rem);
  border-top: 1px solid var(--color-border);
}

.pm-index li {
  border-bottom: 1px solid var(--color-border);
}

.pm-row {
  display: grid;
  grid-template-columns: 56px 1fr;
  grid-template-rows: auto auto;
  column-gap: 0.85rem;
  align-content: center;
  align-items: center;
  height: 100%;
  padding: 0.7rem 0.5rem;
  background: var(--color-bg);
  text-decoration: none;
  color: var(--color-text);
  transition: background 0.25s ease;
}

.pm-row-shot {
  grid-row: 1 / 3;
  display: block;
  width: 56px;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  background: var(--color-surface);
  box-shadow: 0 0 0 1px rgba(240, 234, 216, 0.12);
}

.pm-row-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.pm-row-name {
  font-size: 0.86rem;
  font-weight: 600;
  line-height: 1.25;
  align-self: end;
}

.pm-row-n {
  display: flex;
  gap: 0.55rem;
  font-size: 0.7rem;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: rgba(240, 234, 216, 0.55);
  align-self: start;
  margin-top: 0.2rem;
}

.pm-row-galleries::before {
  content: '·';
  margin-right: 0.55rem;
  color: rgba(240, 234, 216, 0.3);
}

.pm-row:hover,
.pm-row.on { background: var(--color-surface); }
.pm-row:hover .pm-row-name,
.pm-row.on .pm-row-name { color: var(--color-accent); }

.pm-row:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: -2px;
}

/* ── The one authored moment ───────────────────────────────────────────────
 *
 * The frames rise, biggest first, once, when the plate comes into view. The
 * stagger is the order the chart is built in, which is by size — the order the
 * eye takes it anyway. Not repeated on scroll, not on the index, not on the
 * outline: one moment, not an effect on every part.
 */

.pm-frame {
  opacity: 0;
  transform: translate3d(0, 12px, 0);
}

.pm--in .pm-frame {
  opacity: 1;
  transform: none;
  transition: opacity 0.75s cubic-bezier(0.22, 1, 0.36, 1) var(--pm-delay, 0ms),
              transform 0.75s cubic-bezier(0.22, 1, 0.36, 1) var(--pm-delay, 0ms),
              box-shadow 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}

/* Once the reveal is done, lighting takes the opacity back. Three classes to
   two: `.pm--in .pm-frame` above sets it as well, and on equal specificity the
   later rule wins — which is why the dimming silently did nothing until this
   pair was written to out-specify it rather than to follow it. */
.pm--in.pm--lit .pm-frame {
  opacity: var(--pm-dim);
  transition: opacity 0.3s ease,
              box-shadow 0.3s cubic-bezier(0.22, 1, 0.36, 1),
              transform 0.45s cubic-bezier(0.22, 1, 0.36, 1);
}

.pm--in.pm--lit .pm-frame.on { opacity: 1; }

.pm-threads {
  opacity: 0;
  transition: opacity 1s ease 0.15s;
}

.pm--in .pm-threads { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .pm-frame,
  .pm--in .pm-frame {
    opacity: 1;
    transform: none;
    transition: box-shadow 0.2s ease, opacity 0.2s ease;
  }
  .pm--lit .pm-frame { opacity: var(--pm-dim); }
  .pm--lit .pm-frame.on { opacity: 1; }
  .pm-frame.on { transform: none; }
  .pm-frame.on .pm-shot img { transform: none; }
  .pm-threads { opacity: 1; transition: none; }
  .pm-shot img { transition: none; }
}

/* ── The locator, in a /place/<slug> gutter ────────────────────────────────
 *
 * The same chart at a third of the size, holding its own subject lit: this
 * place against the twelve others, which is the browse layer's whole thesis —
 * a sideways move rather than a destination.
 */

.pm--anchored .pm-plate { aspect-ratio: 1 / 1; }

/* The OTHER places keep most of their ink here, unlike on the big chart. There,
   dimming is what makes one frame readable in a wall of photographs; here the
   twelve unlit points ARE the offer — each is the next place, one press away —
   and at the 0.25 the general rule gives them they were barely on the page. */
.pm--anchored .pm-dot { opacity: 0.6; }
.pm--anchored .pm-thread { opacity: 0.5; }

.pm--anchored .pm-dot.on {
  opacity: 1;
  fill: var(--color-accent);
}

/* ── /map's own chrome ─────────────────────────────────────────────────────
 *
 * The page is the chart and the index; this is only what turns them into a
 * page. `.page-header` is per-page here — gallery.css, news.css and shoot.css
 * each carry their own — so /map carries one too, at /gallery's measurements,
 * because the archive's four ways of looking at itself should read as a set.
 *
 * The chart itself brings none of this: the three surfaces that draw it put it
 * in a host of their own shape, and everything above this line works wherever
 * it is mounted.
 */

/* The whole page aligns to the plate's own column, rather than the header
   hugging the left gutter while a capped chart floats in the middle of a 27-inch
   screen. The cap is the FIGURE's, not a measure imposed on the content: what
   the page is is one large drawing, and its header, index and footer are
   captions to it.

   `--measure` still does its own job inside — the description below the title
   keeps it, as it does on /gallery and /browse. */
.pm-page {
  padding-top: calc(var(--nav-height) + 4rem);
  padding-bottom: 6rem;
}

.pm-page > * {
  max-width: var(--pm-max, 1500px);
  margin-inline: auto;
}

.pm-page .page-header {
  margin-bottom: 2.5rem;
  padding: 0 var(--bleed-pad);
}

.pm-page .page-label {
  font-size: 0.7rem;
  letter-spacing: 0.3em;
  text-transform: uppercase;
  color: var(--color-accent);
  margin-bottom: 1rem;
}

.pm-page .page-title {
  font-family: var(--font-display);
  font-size: clamp(2rem, 4.5vw, 3.5rem);
  font-weight: 400;
  letter-spacing: 0.01em;
  line-height: 1.1;
}

.pm-page .page-desc {
  margin-top: 1rem;
  font-size: 0.9rem;
  line-height: 1.75;
  color: var(--color-muted);
  max-width: 560px;
  letter-spacing: 0.02em;
}

.pm-host {
  padding: 0 var(--bleed-pad);
}

.pm-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.8rem clamp(1.5rem, 4vw, 3rem);
  padding: 1.6rem var(--bleed-pad) 5rem;
}

/* The archive's size in the unit this page is about — deliberately the page's
   faintest ink, because it is a count nobody has to read. */
.pm-scale {
  margin: 0;
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgba(240, 234, 216, 0.38);
}

/* The reciprocal of the link that got you here, at the weight /gallery's and
   the browse hub's doors carry: it is a route to another half of the archive,
   and the set must read as the set it is. Body ink, not the scale line's —
   that alpha is right for a count and below AA for anything to press. */
.pm-door {
  margin: 0;
}

.pm-door-link {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.95rem;
  letter-spacing: 0.02em;
  color: var(--color-accent);
  text-decoration: none;
  padding-bottom: 0.3rem;
  border-bottom: 1px solid rgba(232, 168, 0, 0.42);
  transition: border-color 0.25s ease;
}

.pm-door-link:hover,
.pm-door-link:focus-visible { border-bottom-color: var(--color-accent); }

.pm-door-link:focus-visible {
  outline: 2px solid var(--color-accent);
  outline-offset: 5px;
}

.pm-door-arrow {
  flex: 0 0 auto;
  transition: transform 0.3s cubic-bezier(0.22, 1, 0.36, 1);
}

.pm-door-link:hover .pm-door-arrow,
.pm-door-link:focus-visible .pm-door-arrow { transform: translateX(4px); }

@media (prefers-reduced-motion: reduce) {
  .pm-door-arrow,
  .pm-door-link:hover .pm-door-arrow,
  .pm-door-link:focus-visible .pm-door-arrow { transition: none; transform: none; }
}

/* Nothing placed yet — an older shoots.js, or an archive whose locations carry
   no coordinates. A sentence, because this document's whole subject is the
   chart: an empty plate would read as a broken site rather than as an empty
   archive. */
.pm-empty {
  margin: 0;
  padding: 3rem 0 5rem;
  max-width: var(--measure);
  color: rgba(240, 234, 216, 0.62);
}
