/* Kawa — base styles. Scene logic and animation land in later tasks. */

:root {
  --bg: #0e0d0b;
  --fire: #ff5c1f;
  --amber: #ffb35c;
  --parchment: #efe6d8;
  --dim: #8a7a64;
  --dim-05: rgba(138, 122, 100, 0.05);
  --dim-15: rgba(138, 122, 100, 0.15);
  --dim-30: rgba(138, 122, 100, 0.3);
  --flash-white: #fff6e8;

  --font-serif: Georgia, 'Times New Roman', Times, serif;
  --font-mono: ui-monospace, 'SF Mono', Menlo, monospace;

  --z-load: 50;
  --z-flash: 40;
  --z-pos: 35; /* POSISYON readout — above the void, still under the flash */
  --z-rail: 34; /* constellation rail — under the readout/flash tier */
  --z-dissolve: 33; /* hop pixel veil — covers map + card, under the rail */
  --z-void: 30;
  --z-card: 25; /* place card — above the map, below the void */
  --z-ui: 20;
  --z-veil: 15; /* charcoal over the map for the return trip */
  --z-brush: 12; /* pixel brush — on the map, under everything else */
  --z-map: 10;
}

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

html,
body {
  margin: 0;
  padding: 0;
  width: 100%;
  height: 100%;
  height: 100dvh;
  background: var(--bg);
  color: var(--parchment);
  font-family: var(--font-serif);
  overflow-x: clip;
  /* the document itself never scrolls or rubber-bands — every scene is a
     fixed layer. The ledger fallback still scrolls normally; this only stops
     the chain/bounce at the edges (iOS pull-to-refresh, Android glow). */
  overscroll-behavior: none;
}

body {
  min-height: 100dvh;
}

a {
  color: inherit;
}

/* full-viewport fixed layer stack, top to bottom in paint order */

#load {
  position: fixed;
  inset: 0;
  z-index: var(--z-load);
  background: var(--bg);
  display: grid;
  place-items: center;
  touch-action: none; /* a touch here is a skip, never a scroll */
}

/* ambient mosaic backdrop — sparse dim cells behind the stage so scene 0 is
   never plain black (js/load.js builds and draws it) */
.load-backdrop {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* scene 0 — the word resolving over the backdrop (js/load.js owns it) */

.load-stage {
  display: flex;
  flex-direction: column;
  align-items: center;
  /* the upward bias lives in `translate`, NOT `transform`: anime.js owns
     transform for the exit scale, and overwriting a CSS transform mid-
     sequence is exactly the "word suddenly shifts down" bug. `translate`
     composes with transform, so the word holds one position start to end. */
  translate: 0 -5vh;
}

/* pixel logotype — a canvas at the void lattice's own cell pitch; the word
   is a hand-authored bitmap that resolves cell by cell (js/load.js) */
.load-title {
  margin-top: 28px;
  line-height: 0;
}

.load-title canvas {
  display: block;
  margin: 0 auto;
  max-width: 92vw; /* never overflow a narrow phone; cells stay authored at pitch */
  height: auto;
}

#flash {
  position: fixed;
  inset: 0;
  z-index: var(--z-flash);
  pointer-events: none;
  opacity: 0;
  /* --flash-x/--flash-y are set by journey.js to the clicked ember's screen
     position — the flash erupts from the click point, not viewport center */
  background: radial-gradient(
    circle at var(--flash-x, 50%) var(--flash-y, 50%),
    var(--flash-white) 0%,
    var(--amber) 35%,
    var(--fire) 70%,
    transparent 100%
  );
}

#void {
  position: fixed;
  inset: 0;
  z-index: var(--z-void);
  background: var(--bg);
}

/* touch drags on the lattice feed the heat wake (pointermove), never the
   page — without this iOS treats the drag as a scroll and pointermove stops */
#void canvas {
  touch-action: none;
}

#ui {
  position: fixed;
  inset: 0;
  z-index: var(--z-ui);
  pointer-events: none;
}

#ui > * {
  pointer-events: auto;
}

#map {
  position: fixed;
  inset: 0;
  z-index: var(--z-map);
  visibility: hidden;
  touch-action: none; /* the map is a non-interactive scene — no touch pans */
}

#veil {
  position: fixed;
  inset: 0;
  z-index: var(--z-veil);
  pointer-events: none;
  background: var(--bg);
  opacity: 0;
}

/* POSISYON coordinate readout — fixed mono instrument, top-left. Lives as a
   direct child of <body> at --z-pos because #ui (z20) sits under the void
   (z30); the readout must be legible in BOTH worlds, and still be swallowed
   by the flash (z40) at burst peak. */

.kawa-pos {
  position: fixed;
  /* top-left is notch / dynamic-island territory in landscape — the safe-area
     insets are 0 everywhere else, so desktop is untouched */
  top: calc(1.25rem + env(safe-area-inset-top, 0px));
  left: calc(1.25rem + env(safe-area-inset-left, 0px));
  z-index: var(--z-pos);
  pointer-events: none;
  border: 1px solid var(--dim-15);
  background: rgba(14, 13, 11, 0.4);
  padding: 0.5rem 0.75rem;
  font-family: var(--font-mono);
}

.kawa-pos-label {
  display: block;
  font-size: 0.5625rem;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--dim);
  margin-bottom: 0.2rem;
}

.kawa-pos-value {
  display: block;
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  color: var(--parchment);
  font-variant-numeric: tabular-nums;
}

/* void ember labels — DOM layer inside #void, painted over the canvas,
   never intercepting pointer events (embers are hit via raycast) */

.kawa-labels {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  transition: opacity 0.2s ease;
}

.kawa-labels.is-diving {
  opacity: 0;
}

.kawa-label {
  position: absolute;
  left: 0;
  top: 0;
  will-change: transform;
  /* proximity rest state — the field stays quiet; coords surface only for
     the beacon nearest the pointer (is-near) or under it (is-hover) */
  opacity: 0.14;
  transition: opacity 0.25s ease;
}

.kawa-label.is-near,
.kawa-label.is-hover {
  opacity: 1;
}

.kawa-label-name {
  font-family: var(--font-serif);
  font-size: 1.125rem;
  color: var(--parchment);
  white-space: nowrap;
  margin: 0 0 0.1rem;
  opacity: 0;
  transform: translateY(5px);
  transition: opacity 0.16s ease-out, transform 0.16s ease-out;
}

.kawa-label-kind {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--amber);
  white-space: nowrap;
  margin: 0 0 0.35rem;
  opacity: 0;
  transform: translateY(5px);
  transition: opacity 0.16s ease-out 0.04s, transform 0.16s ease-out 0.04s;
}

.kawa-label.is-hover .kawa-label-name,
.kawa-label.is-hover .kawa-label-kind {
  opacity: 1;
  transform: none;
}

.kawa-label-coords {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  color: var(--dim);
  white-space: nowrap;
  margin: 0;
}

/* the crawl bar — chips in a row across the top, centered clear of the
   POSISYON block (left) and the rail (right). On phones it tucks under the
   readout instead. The container never takes the pointer. */
.kawa-crawlbar {
  position: fixed;
  z-index: var(--z-pos);
  top: calc(1.25rem + env(safe-area-inset-top, 0px));
  left: 50%;
  translate: -50% 0;
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.4rem;
  max-width: min(60vw, 640px);
  pointer-events: none;
}


@media (max-width: 767px) {
  /* an eyebrow + one slim row of city chips under the readout — stacked
     full-name chips were eating the void */
  .kawa-crawlbar {
    top: calc(4.9rem + env(safe-area-inset-top, 0px));
    left: 0;
    translate: 0 0;
    justify-content: flex-start;
    max-width: 100vw;
    padding: 0 calc(1.25rem + env(safe-area-inset-left, 0px));
  }

  .kawa-crawlbar .kawa-crawl {
    flex-shrink: 0;
    padding: 0.6rem 0.7rem;
    font-size: 0.625rem;
  }
}

/* the crawl chip — a proper square-cornered button, in both the void and
   on the landed map. Fades out for every transition; while a crawl runs,
   only its own chip stays, as the live progress marker. */
.kawa-crawl {
  display: inline-block;
  border: 1px solid var(--fire);
  border-radius: 0;
  background: rgba(14, 13, 11, 0.55);
  padding: 0.55rem 0.85rem;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--amber);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease, background 0.2s ease, color 0.2s ease;
}

.kawa-crawl.is-on {
  opacity: 1;
  pointer-events: auto;
}

/* chips that are off leave the flow entirely — otherwise they hold their
   flex slot invisibly and shove the live chip off-center (or into the
   rail on phones) */
.kawa-crawl:not(.is-on) {
  display: none;
}

.kawa-crawl:hover {
  background: var(--fire);
  color: var(--charcoal, #0e0d0b);
}

/* the return — one subtle line under the chip, only while grounded. The
   cards carry no back button; this is the single way home (plus Escape). */
.kawa-balik-nav {
  position: fixed;
  z-index: var(--z-pos);
  top: calc(5.1rem + env(safe-area-inset-top, 0px));
  left: calc(1.25rem + env(safe-area-inset-left, 0px));
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  border: none;
  background: none;
  padding: 0.5rem 0.85rem 0.5rem 0;
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  color: var(--dim);
  cursor: pointer;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.4s ease, color 0.2s ease;
}

/* the icon's ember cell — a 2x2 lattice fragment, dim until hovered */
.kawa-balik-px {
  display: grid;
  grid-template-columns: repeat(2, 6px);
  gap: 2px;
}

.kawa-balik-px i {
  width: 6px;
  height: 6px;
  background: var(--fire);
  opacity: 0.45;
  transition: opacity 0.2s ease;
}

.kawa-balik-px i:nth-child(2) { background: var(--amber); }
.kawa-balik-px i:nth-child(3) { background: var(--amber); opacity: 0.3; }

.kawa-balik-nav:hover .kawa-balik-px i {
  opacity: 1;
}

.kawa-balik-nav.is-on {
  opacity: 1;
  pointer-events: auto;
}

.kawa-balik-nav:hover {
  color: var(--amber);
}

@media (pointer: coarse) {
  .kawa-balik-nav {
    padding: 0.95rem 0.85rem 0.95rem 0;
  }
}

/* phones: back rides the top row, opposite POSISYON — the chips row below
   stays clear of it; the rail starts under both */
@media (max-width: 767px) {
  .kawa-balik-nav {
    top: calc(1.1rem + env(safe-area-inset-top, 0px));
    left: auto;
    right: calc(0.75rem + env(safe-area-inset-right, 0px));
    padding: 0.95rem 0 0.95rem 0.85rem;
  }

}

/* live crawl — the chip stops being a button and becomes the walk's marker:
   filled fire, progress count, no pointer. Distinct because selected. */
.kawa-crawl.is-live {
  background: var(--fire);
  border-color: var(--fire);
  color: #0e0d0b;
}

.kawa-crawl.is-live:hover {
  background: transparent;
  color: var(--fire);
}

/* fingers need the full 44px */
@media (pointer: coarse) {
  .kawa-crawl {
    padding: 0.95rem 0.9rem;
  }
}

/* square, right-angle CTA buttons — no rounded corners, ever */

.cta,
.ledger-cta,
.ledger-secondary {
  display: inline-block;
  border: 1px solid var(--fire);
  border-radius: 0;
  background: transparent;
  color: var(--amber);
  font-family: var(--font-mono);
  font-size: 0.875rem;
  letter-spacing: 0.02em;
  text-decoration: none;
  padding: 0.6em 1.2em;
  cursor: pointer;
}

.cta:hover,
.ledger-cta:hover {
  background: var(--fire);
  color: var(--bg);
}

.ledger-secondary {
  border-color: var(--dim);
  color: var(--parchment);
}

.ledger-secondary:hover {
  background: var(--dim-30);
}

/* semantic fallback ledger — legible without JS or three.js */

#ledger {
  position: relative;
  z-index: 1;
  max-width: 640px;
  margin: 0 auto;
  padding: 3rem 1.5rem 5rem;
  background: var(--bg);
  color: var(--parchment);
}

#ledger.is-hidden {
  display: none;
}

#ledger h1 {
  font-family: var(--font-serif);
  color: var(--fire);
  font-size: 2rem;
  margin: 0 0 0.5rem;
}

#ledger > p {
  color: var(--dim);
  font-family: var(--font-mono);
  font-size: 0.875rem;
  margin: 0 0 2rem;
}

.ledger-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--dim-15);
}

.ledger-item {
  border-bottom: 1px solid var(--dim-15);
  padding: 1.5rem 0;
}

.ledger-name {
  font-family: var(--font-serif);
  font-size: 1.25rem;
  color: var(--amber);
  margin: 0 0 0.25rem;
}

.ledger-kind {
  font-family: var(--font-mono);
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--dim);
  margin: 0 0 0.75rem;
}

.ledger-address,
.ledger-coords {
  font-family: var(--font-mono);
  font-size: 0.875rem;
  color: var(--parchment);
  margin: 0 0 0.25rem;
}

.ledger-coords {
  color: var(--dim);
}

.ledger-links {
  margin: 1rem 0 0;
  display: flex;
  gap: 0.75rem;
  flex-wrap: wrap;
}

/* place card — the landed place's dossier. Bottom sheet on mobile, left
   panel on desktop (the settle keeps the place right of the click point, so
   the panel never covers the grounded ember). Body-level fixed at --z-card:
   above the map, below the void, swallowed by the flash. */

.kawa-card {
  position: fixed;
  z-index: var(--z-card);
  display: none;
  left: 0;
  right: 0;
  bottom: 0;
  max-height: 45dvh;
  overflow-y: auto;
  background: rgba(14, 13, 11, 0.9);
  border-top: 1px solid var(--dim-15);
  /* bottom sheet: keep the Back row clear of the home indicator, and the
     text clear of curved corners in landscape (insets are 0 on desktop) */
  padding: 1.4rem
    calc(1.5rem + env(safe-area-inset-right, 0px))
    calc(1.25rem + env(safe-area-inset-bottom, 0px))
    calc(1.5rem + env(safe-area-inset-left, 0px));
  opacity: 0;
  touch-action: pan-y; /* the sheet may scroll itself; the page never moves */
  /* the landed map is draggable — the panel itself lets the pointer through;
     only its links and buttons take it (the card rhythm is sized to fit, so
     losing panel-scroll to the pass-through costs nothing) */
  pointer-events: none;
}

.kawa-card a,
.kawa-card button {
  pointer-events: auto;
}

/* no-maplibre fallback (body.kawa-mapless, set by main.js when the vendor
   script never loaded): dives are disabled and the card opens directly over
   the breathing lattice — so for this mode only it must sit above the void
   layer (and the readout), still under the flash. */
body.kawa-mapless .kawa-card {
  z-index: calc(var(--z-pos) + 1);
}

@media (min-width: 768px) {
  .kawa-card {
    left: calc(1.5rem + env(safe-area-inset-left, 0px));
    right: auto;
    bottom: auto;
    top: 50%;
    translate: 0 -50%; /* independent of the transform anime.js animates */
    width: min(380px, 42vw);
    max-height: 82dvh;
    border: 1px solid var(--dim-15);
    padding: 1.75rem 1.75rem 1.5rem;
  }
}

/* landscape phone (wide enough for the left panel, but very short): the
   45dvh sheet would bury the marker, so the panel mode is right — it just
   needs the mobile sheet's tighter rhythm and a hard height clamp so name,
   CTA and Back all stay reachable at ~390px of height. */
@media (min-width: 768px) and (max-height: 480px) {
  .kawa-card {
    max-height: calc(100dvh - 1.5rem - env(safe-area-inset-top, 0px));
    width: min(380px, 46vw);
    padding: 1.1rem 1.25rem calc(1rem + env(safe-area-inset-bottom, 0px));
  }

  .kawa-card-name {
    font-size: 1.25rem;
  }

  .kawa-card-meta {
    margin-bottom: 0.6rem;
  }

  .kawa-card-why {
    font-size: 0.875rem;
    line-height: 1.4;
    margin-bottom: 0.7rem;
  }

  .kawa-card-links {
    margin-top: 0.5rem;
  }

  .kawa-card-balik-row {
    margin-top: 0.7rem;
  }
}

/* the kind eyebrow — same voice as the void label's kind row, so the card
   and the field speak one language */
.kawa-card-kind {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--amber);
  margin: 0 0 0.3rem;
}

.kawa-card-name {
  font-family: var(--font-serif);
  font-size: 1.75rem;
  font-weight: normal;
  line-height: 1.15;
  color: var(--parchment);
  margin: 0 0 0.4rem;
}

.kawa-card-meta {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  color: var(--dim);
  margin: 0 0 1.1rem;
}

/* the place's photo — square corners (brand), a quiet warm grade so it sits
   inside Ink & Fire instead of shouting over it. Short on the mobile sheet
   (45dvh budget), taller in the desktop panel. */
.kawa-card-photo {
  display: block;
  margin: 0 0 1.1rem;
  border: 1px solid var(--dim-15);
}

.kawa-card-photo img {
  display: block;
  width: 100%;
  height: 120px;
  object-fit: cover;
  filter: saturate(0.8) sepia(0.12) contrast(1.02);
}

@media (min-width: 768px) {
  .kawa-card-photo img {
    height: 170px;
  }
}

@media (min-width: 768px) and (max-height: 480px) {
  .kawa-card-photo img {
    height: 90px;
  }
}

.kawa-card-why {
  font-family: var(--font-serif);
  font-size: 1rem;
  line-height: 1.55;
  color: var(--parchment);
  margin: 0 0 1.2rem;
}

/* crawl notes — coffee + parking, the walk's practical layer. Mono ledger
   rows, quiet; only rendered mid-crawl. */
.kawa-card-notes {
  margin: -0.4rem 0 1.1rem;
  padding: 0.6rem 0.75rem;
  border: 1px solid var(--dim-15);
}

.kawa-card-note {
  display: flex;
  gap: 0.75rem;
  align-items: baseline;
  margin: 0 0 0.35rem;
}

.kawa-card-note:last-child {
  margin-bottom: 0;
}

.kawa-card-note-k {
  flex-shrink: 0;
  width: 5em;
  font-family: var(--font-mono);
  font-size: 0.5625rem;
  text-transform: uppercase;
  letter-spacing: 0.18em;
  color: var(--dim);
}

.kawa-card-note-v {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.03em;
  line-height: 1.5;
  color: var(--parchment);
}

.kawa-card-get {
  margin: 0 0 1.4rem;
}

.kawa-card-get-label {
  display: block;
  font-family: var(--font-mono);
  font-size: 0.5625rem;
  text-transform: uppercase;
  letter-spacing: 0.22em;
  color: var(--dim);
  margin-bottom: 0.3rem;
}

.kawa-card-get-text {
  font-family: var(--font-serif);
  font-size: 1rem;
  line-height: 1.5;
  color: var(--parchment);
  margin: 0;
}

.kawa-card-cta-row {
  margin: 0;
}

.kawa-card-cta {
  display: inline-block;
  border-radius: 0;
  border: 1px solid var(--fire);
  background: transparent;
  color: var(--fire);
  font-family: var(--font-mono);
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-decoration: none;
  padding: 0.65em 1.25em;
}

.kawa-card-cta:hover {
  background: var(--fire);
  color: var(--bg);
}

.kawa-card-links {
  margin: 0.9rem 0 0;
  display: flex;
  gap: 1rem;
  flex-wrap: wrap;
}

.kawa-card-link {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  color: var(--dim);
  text-decoration: none;
}

.kawa-card-link:hover {
  text-decoration: underline;
  color: var(--parchment);
}

/* Share sits in the same row as the links and reads as one of them — it is a
   button only because it hands over a URL instead of following one. */
.kawa-card-share {
  border: 0;
  padding: 0;
  background: none;
  cursor: pointer;
  font: inherit;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  color: var(--dim);
}

.kawa-card-balik-row {
  margin: 1.4rem 0 0;
  display: flex;
  align-items: baseline;
  gap: 1.25rem;
  flex-wrap: wrap;
}

/* mid-crawl: Next leads the card — a full fire bar right under the meta
   line, above the photo, above every fold. The walk's one obvious action. */
.kawa-card-next-row {
  margin: 0 0 1rem;
}

.kawa-card-susunod {
  display: block;
  width: 100%;
  min-height: 44px;
  border: 1px solid var(--fire);
  border-radius: 0;
  background: var(--fire);
  padding: 0.55em 1em;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  text-align: left;
  color: #0e0d0b;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.kawa-card-susunod:hover {
  background: transparent;
  color: var(--fire);
}

.kawa-card-balik {
  display: inline-block;
  border: none;
  background: none;
  padding: 0;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.06em;
  color: var(--amber);
  cursor: pointer;
}

.kawa-card-balik:hover {
  color: var(--parchment);
}

/* mobile sheet: tighter rhythm so the whole dossier, balik included, fits
   inside the 45dvh sheet without scrolling on a 375x667 phone. Placed after
   the base card rules — same specificity, later wins. */
@media (max-width: 767px) {
  .kawa-card {
    padding: 1.1rem
      calc(1.25rem + env(safe-area-inset-right, 0px))
      calc(1rem + env(safe-area-inset-bottom, 0px))
      calc(1.25rem + env(safe-area-inset-left, 0px));
    /* photos + the crawl's Next row outgrew the 45dvh sheet — the phone
       sheet scrolls itself again (the drag-through-panel trade stays
       desktop-only, where the panel never overflows) */
    pointer-events: auto;
  }

  .kawa-card-photo img {
    height: 96px;
  }

  .kawa-card-photo {
    margin-bottom: 0.8rem;
  }

  .kawa-card-name {
    font-size: 1.4rem;
  }

  .kawa-card-meta {
    margin-bottom: 0.7rem;
  }

  .kawa-card-why {
    font-size: 0.9375rem;
    line-height: 1.45;
    margin-bottom: 0.8rem;
  }

  .kawa-card-get {
    margin-bottom: 0.9rem;
  }

  .kawa-card-get-text {
    font-size: 0.9375rem;
    line-height: 1.45;
  }

  .kawa-card-links {
    margin-top: 0.6rem;
  }

  .kawa-card-balik-row {
    margin-top: 0.9rem;
  }

  /* the balik button is the only way home — give the tap a real target */
  .kawa-card-balik {
    padding: 0.5em 0;
  }
}

/* touch: the card's two controls get real 44px targets without changing
   their type scale — desktop (fine pointers) keeps its exact geometry */
@media (pointer: coarse) {
  .kawa-card-cta {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding-top: 0;
    padding-bottom: 0;
  }

  .kawa-card-balik {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    padding-top: 0;
    padding-bottom: 0;
    padding-right: 1.5em; /* widen the target rightward; text stays put */
  }

}

/* constellation rail — upper-right vertical stack of ember cells, one per
   place, on the landed map only. 14px visual cells inside 44px hit targets;
   the current place blinks the beacon cadence, the rest sit dim. The nav
   itself never blocks a drag — only the buttons take the pointer, and only
   while the rail is on. */

.kawa-rail {
  position: fixed;
  top: calc(1.25rem + env(safe-area-inset-top, 0px));
  right: calc(0.75rem + env(safe-area-inset-right, 0px));
  z-index: var(--z-rail);
  display: flex;
  flex-direction: column;
  /* 21 places outgrew a single 44px column — tighter cells, and when the
     column still overflows it wraps into a second one growing leftward:
     a small ember grid, same lattice grammar as everything else */
  flex-wrap: wrap-reverse;
  align-content: flex-start;
  max-height: calc(100dvh - 2.5rem - env(safe-area-inset-top, 0px) - env(safe-area-inset-bottom, 0px));
  gap: 2px 4px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.45s ease;
}

/* phones: the rail starts below the top row (POSISYON + back icon) and
   finishes above the card sheet, wrapping into tighter columns */
@media (max-width: 767px) {
  .kawa-rail {
    top: calc(4.9rem + env(safe-area-inset-top, 0px));
    max-height: calc(48dvh - 3.5rem);
  }
}

.kawa-rail.is-on {
  opacity: 1;
}

.kawa-rail-cell {
  position: relative;
  width: 34px;
  height: 30px;
  padding: 0;
  border: none;
  background: transparent;
  display: grid;
  place-items: center;
  cursor: pointer;
  pointer-events: none;
}

.kawa-rail.is-on .kawa-rail-cell {
  pointer-events: auto;
}

/* the visual cell — a square ember, dim until noticed; sized to whisper
   next to the real lattice, not shout over it */
.kawa-rail-px {
  width: 10px;
  height: 10px;
  background: #ff7f37;
  opacity: 0.5;
  transition: opacity 0.2s ease;
}

@media (hover: hover) {
  .kawa-rail-cell:hover .kawa-rail-px {
    opacity: 1;
  }
}

.kawa-rail-cell.is-armed .kawa-rail-px {
  opacity: 1;
}

/* current place — the beacon blink (same cadence as the grounded marker);
   yellow belongs to beacons/current only, and this is one */
.kawa-rail-cell.is-current .kawa-rail-px {
  opacity: 1;
  animation: kawa-beacon-blink 1.9s linear infinite;
}

/* mono name label sliding out to the left of the cell — hover (fine
   pointers) or tap-arm (coarse); never wraps, never takes the pointer */
.kawa-rail-name {
  position: absolute;
  right: calc(100% - 4px);
  top: 50%;
  translate: 0 -50%;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.06em;
  color: var(--parchment);
  background: rgba(14, 13, 11, 0.75);
  padding: 0.25em 0.6em;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  transform: translateX(6px);
  transition: opacity 0.18s ease-out, transform 0.18s ease-out;
}

@media (hover: hover) {
  .kawa-rail-cell:hover .kawa-rail-name {
    opacity: 1;
    transform: none;
  }
}

.kawa-rail-cell.is-armed .kawa-rail-name {
  opacity: 1;
  transform: none;
}

/* hop pixel veil + map pixel brush — full-viewport canvases, never
   interactive. The dissolve covers the world-swap; the brush is transparent
   until a pointer wakes cells on the landed map. */

.kawa-dissolve {
  position: fixed;
  inset: 0;
  z-index: var(--z-dissolve);
  pointer-events: none;
}

.kawa-brush {
  position: fixed;
  inset: 0;
  z-index: var(--z-brush);
  pointer-events: none;
}

/* grounded ember — the destination marker on the landed map. Circles are
   allowed here: it's an ember, not a UI control. pointer-events: none so it
   never steals clicks from the map or the card. */

.kawa-ember-marker {
  position: relative;
  /* glow halo scales with the pixel it carries */
  width: calc(var(--kawa-pitch, 24px) * 1.8);
  height: calc(var(--kawa-pitch, 24px) * 1.8);
  pointer-events: none;
}

.kawa-ember-glow {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: radial-gradient(
    circle,
    rgba(255, 92, 31, 0.5) 0%,
    rgba(255, 92, 31, 0.16) 45%,
    transparent 70%
  );
  animation: kawa-ember-pulse 2.4s ease-in-out infinite;
}

.kawa-ember-core {
  position: absolute;
  left: 50%;
  top: 50%;
  /* the marker IS one lattice pixel — same size as the brush overlay's
     cells (--kawa-pitch, set by main.js from the void's shared pitch, minus
     the lattice's 2px cell inset). The pixel you clicked is standing in the
     city at the exact scale of the pixels your finger wakes around it. */
  width: calc(var(--kawa-pitch, 24px) - 2px);
  height: calc(var(--kawa-pitch, 24px) - 2px);
  transform: translate(-50%, -50%);
  border-radius: 0;
  background: #ff7f37;
  box-shadow:
    0 0 6px 2px rgba(255, 92, 31, 0.9),
    0 0 18px 6px rgba(255, 92, 31, 0.35);
  animation: kawa-beacon-blink 1.9s linear infinite;
}

@keyframes kawa-ember-pulse {
  0%, 100% { transform: scale(0.72); opacity: 0.65; }
  50% { transform: scale(1.2); opacity: 1; }
}

/* the place-cell beacon cadence from the void: rest ~82% of the cycle,
   fast attack, brief yellow hold, fast release */
@keyframes kawa-beacon-blink {
  0%, 81% {
    background: #ff7f37;
    box-shadow:
      0 0 6px 2px rgba(255, 92, 31, 0.9),
      0 0 18px 6px rgba(255, 92, 31, 0.35);
  }
  84%, 93% {
    background: #ffd166;
    box-shadow:
      0 0 8px 3px rgba(255, 209, 102, 0.95),
      0 0 22px 8px rgba(255, 92, 31, 0.45);
  }
  96%, 100% {
    background: #ff7f37;
    box-shadow:
      0 0 6px 2px rgba(255, 92, 31, 0.9),
      0 0 18px 6px rgba(255, 92, 31, 0.35);
  }
}

@media (prefers-reduced-motion: reduce) {
  /* CSS side of the reduced seam (the JS side lives in main.js: static
     lattice frame, crossfade trips, opacity-only card): every CSS animation
     holds still */
  .kawa-ember-glow {
    animation: none;
    transform: scale(1);
    opacity: 0.8;
  }
  .kawa-ember-core {
    animation: none;
    background: #ff7f37; /* steady ember, no beacon blink */
  }
  .kawa-card-px span {
    animation: none;
    opacity: 0.18; /* static cells at base alpha, no breath */
  }
  .kawa-rail-cell.is-current .kawa-rail-px {
    animation: none; /* current reads by full opacity alone, no blink */
  }
}
