/* ---------------------------------------------------------
   Paris for You — quiet, image-led, warm.

   Light is the default on purpose: this reads like paper, and it
   suits the thing better than a dark interface. Dark is still
   available from the toggle in the header and is remembered.
   --------------------------------------------------------- */

:root {
  --bg:      #fdfaf5;   /* warm ivory, not white */
  --bg-2:    #f7f1e8;
  --card:    #fffdfa;
  --ink:     #241c18;   /* warm near-black, never pure grey */
  --soft:    #6b5c53;
  --faint:   #a1958a;
  --line:    #ece2d5;
  --accent:  #9d3f47;   /* wine */
  --blush:   #c98b8b;
  --gold:    #b38f5d;
  --img-bg:  #f0e7db;
  --radius:  12px;
  --serif:   "Iowan Old Style", "Palatino Linotype", Palatino, Georgia, serif;
  --sans:    -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
}

:root[data-theme="dark"] {
  --bg:      #17120f;
  --bg-2:    #1d1713;
  --card:    #201a15;
  --ink:     #f4ece1;
  --soft:    #b3a596;
  --faint:   #877a6d;
  --line:    #322820;
  --accent:  #e0918f;
  --blush:   #c98b8b;
  --gold:    #cdab74;
  --img-bg:  #271f19;
}

* { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

.wrap { width: 100%; max-width: 1040px; margin: 0 auto; padding: 0 1.25rem; }

/* ---------- head ---------- */

.head {
  padding: 3.25rem 0 1.9rem;
  background:
    radial-gradient(120% 80% at 12% -10%, color-mix(in srgb, var(--blush) 13%, transparent), transparent 62%),
    linear-gradient(180deg, var(--bg-2), var(--bg));
}

.head-top { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; }

/* ---------- lines that arrive a moment after the page does ----------

   Four lines of the header are written by JavaScript, and all four start
   empty: the dateline, the epigraph, the conditions line and the lede.
   Four empty elements collapse to nothing, so the header was several
   lines shorter than its real height until they were written, and then
   everything below — the wordmark, the location bar, the whole of main,
   the footer — moved down at once. One shift, 0.759 of layout shift,
   which is most of a failing grade on its own.

   Three of them never needed data at all and are written the moment this
   file's script runs rather than after the fetches (renderStatics() in
   js/app.js), which closes most of the gap. It does not close all of it:
   the stylesheet is what unblocks the first paint, and the scripts
   arrive just behind it, so there is a frame or two where the markup is
   on screen and the text is not.

   Hence `:empty`. The reservation exists for exactly the moment it is
   for and stops applying the instant there is something in the element,
   so it can never add height to the finished page — which is the usual
   failure of holding space open, and the reason not to do it with a
   plain min-height.

   How much to hold, measured rather than guessed, at 360–1280px:
     · the dateline is one line, always, at every width — exact
     · the lede on the opening view is one line from 412px up
     · the epigraph is two lines on nine days in fourteen, one on the
       other five, so two is the honest reservation and five days a year
       it gives back a line
   The conditions line waits on a forecast from a third party, and at
   phone widths it is two lines, not one. */
.date {
  margin: 0 0 .5rem;
  font-size: .7rem;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--faint);
}
.date:empty     { min-height: 1.4em; min-height: 1lh; }
.epigraph:empty { min-height: 3em;   min-height: 2lh; }
.lede:empty     { min-height: 1.5em; min-height: 1lh; }
.title {
  font-family: var(--serif);
  font-size: clamp(2.5rem, 9vw, 3.6rem);
  line-height: 1;
  margin: 0;
  font-weight: 400;
  letter-spacing: -.022em;
}
.title em { font-style: italic; color: var(--accent); }

/* a hairline of gold under the wordmark */
.rule {
  width: 3.2rem; height: 1px;
  background: linear-gradient(90deg, var(--gold), transparent);
  margin: 1rem 0 .9rem;
  border: 0;
}

.epigraph {
  font-family: var(--serif);
  font-style: italic;
  font-size: clamp(1rem, 2.6vw, 1.14rem);
  line-height: 1.5;
  color: var(--soft);
  margin: 0 0 1.1rem;
  max-width: 42ch;
}

/* The forecast is a third party and arrives after the first paint by
   design. At phone widths "overcast · favour museums, passages and
   workshops" is two lines, not one, so that is what gets held open. */
.meta-line {
  margin: .55rem 0 0;
  color: var(--soft);
  font-size: .9rem;
  min-height: 3em;
  min-height: 2lh;
}
@media (min-width: 640px) {
  .meta-line { min-height: 1.5em; min-height: 1lh; }
}

/* theme toggle — deliberately small */
.theme {
  flex: 0 0 auto;
  background: none;
  border: 1px solid var(--line);
  color: var(--faint);
  width: 2.1rem; height: 2.1rem;
  border-radius: 50%;
  font-size: .85rem; line-height: 1;
  cursor: pointer; padding: 0;
  transition: all .18s;
}
.theme:hover { border-color: var(--gold); color: var(--gold); transform: rotate(-12deg); }

.note {
  margin-top: 1rem;
  padding-left: .85rem;
  border-left: 2px solid var(--accent);
  font-size: .87rem;
  color: var(--soft);
}
.note b { color: var(--ink); font-weight: 600; }

/* ---------- tabs ---------- */

.tabs {
  position: sticky; top: 0; z-index: 20;
  background: color-mix(in srgb, var(--bg) 90%, transparent);
  backdrop-filter: saturate(180%) blur(14px);
  -webkit-backdrop-filter: saturate(180%) blur(14px);
  border-bottom: 1px solid var(--line);
}
/* Five destinations on the left; utilities kept small on the right, so the
   bar reads as one short list rather than twelve equal choices. */
.tabs-inner {
  display: flex; justify-content: space-between; align-items: center;
  gap: 1.5rem;
  overflow-x: auto; scrollbar-width: none;
}
.tabs-inner::-webkit-scrollbar { display: none; }
.tabs-main { display: flex; gap: 1.6rem; }
.tabs-util { display: flex; gap: 1rem; }

.tab {
  flex: 0 0 auto;
  background: none; border: none;
  padding: .95rem 0;
  font-family: var(--serif);
  font-size: 1.02rem;
  color: var(--faint);
  cursor: pointer;
  white-space: nowrap;
  border-bottom: 1.5px solid transparent;
  margin-bottom: -1px;
  transition: color .15s;
}
.tab:hover { color: var(--ink); }
.tab.on { color: var(--ink); border-bottom-color: var(--accent); }

.tab-sm {
  font-family: var(--sans);
  font-size: .78rem;
  letter-spacing: .06em;
  text-transform: uppercase;
}

/* ---------- bar ---------- */

.bar {
  display: flex; justify-content: space-between; align-items: baseline;
  gap: 1rem; flex-wrap: wrap;
  padding: 2rem 0 1.25rem;
}
/* The views are drawn into this. Empty, it is zero pixels tall, so the
   footer sat just under the tab bar and then dropped a full screen the
   moment the first view rendered — the single largest layout shift on
   the page.

   `:empty` rather than a bare `#view`, so the reservation exists only
   during the moment it is for. A permanent `min-height` in viewport
   units is a rule that keeps having opinions long after it is needed:
   it re-evaluates on every resize, including the synthetic full-height
   resize a page audit does to photograph the whole page, where 78vh of
   a five-thousand-pixel viewport is not a placeholder but a shove. */
#view:empty { min-height: 78vh; }

/* The wide photographs are wrapped in <picture> so a phone is not offered
   a desktop-sized rendition. Nothing about the layout should notice: every
   rule in this file targets the <img>, and `display: contents` takes the
   wrapper out of the box tree entirely, so the image stays a direct child
   of whatever frame it was in. */
picture { display: contents; }

.lede {
  margin: 0;
  color: var(--soft);
  font-family: var(--serif);
  font-style: italic;
  font-size: 1.02rem;
  line-height: 1.5;
  max-width: 52ch;
}
.bar-actions { display: flex; gap: .5rem; flex-shrink: 0; }

.ghost {
  background: none;
  border: 1px solid var(--line);
  color: var(--soft);
  padding: .42rem .95rem;
  border-radius: 999px;
  font-family: inherit; font-size: .84rem;
  cursor: pointer;
  transition: all .15s;
}
.ghost:hover { border-color: var(--soft); color: var(--ink); }
.ghost.accent { border-color: var(--accent); color: var(--accent); }
.ghost.accent:hover { background: var(--accent); color: var(--bg); }
.ghost[aria-expanded="true"] { background: var(--ink); border-color: var(--ink); color: var(--bg); }

/* ---------- filters ---------- */

.filters {
  padding: 1.15rem 0 1.4rem;
  border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line);
  margin-bottom: 1.75rem;
}
.frow { display: flex; align-items: baseline; gap: .9rem; margin-bottom: .7rem; }
.frow:last-child { margin-bottom: 0; }
.frow-end { justify-content: space-between; padding-top: .3rem; }
.flabel {
  flex: 0 0 3.2rem;
  font-size: .68rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--faint);
}
.chips { display: flex; flex-wrap: wrap; gap: .4rem; }
.chip {
  background: none;
  border: 1px solid var(--line);
  color: var(--soft);
  padding: .3rem .75rem;
  border-radius: 999px;
  font-family: inherit; font-size: .82rem;
  cursor: pointer;
  transition: all .14s;
}
.chip:hover { border-color: var(--soft); color: var(--ink); }
.chip.on { background: var(--ink); border-color: var(--ink); color: var(--bg); }

.link {
  background: none; border: none; padding: 0;
  color: var(--faint); font-family: inherit; font-size: .82rem;
  text-decoration: underline; cursor: pointer;
}
.link:hover { color: var(--accent); }
.count { font-size: .8rem; color: var(--faint); }

@media (max-width: 560px) {
  .frow { flex-direction: column; align-items: stretch; gap: .45rem; }
  .flabel { flex: none; }
}

/* ---------- grid + card ---------- */

.grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.75rem 1.4rem;
  padding-bottom: 1rem;
}
@media (min-width: 620px)  { .grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 940px)  { .grid { grid-template-columns: repeat(3, 1fr); } }

.card { display: flex; flex-direction: column; }
.card.done .shot { opacity: .45; }
.card.done .card-title { color: var(--soft); }

.shot {
  position: relative;
  aspect-ratio: 4 / 3;
  background: var(--img-bg);
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: .9rem;
  transition: opacity .2s;
}
/* a warm hairline over the photograph, so it sits on the paper rather than on top of it */
.shot::after {
  content: '';
  position: absolute; inset: 0;
  border-radius: var(--radius);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--ink) 9%, transparent);
  pointer-events: none;
}
.shot img {
  width: 100%; height: 100%;
  object-fit: cover;
  display: block;
  transition: transform .5s cubic-bezier(.2,.7,.3,1), opacity .22s;
  opacity: 0;
}
.shot img.loaded { opacity: 1; }
.card:hover .shot img { transform: scale(1.03); }

/* one small badge, top-left, only when it earns its place */
.badge {
  position: absolute; top: .6rem; left: .6rem;
  background: color-mix(in srgb, var(--card) 92%, transparent);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  color: var(--ink);
  font-size: .67rem; font-weight: 600;
  letter-spacing: .07em; text-transform: uppercase;
  padding: .22rem .55rem;
  border-radius: 5px;
}
.badge.hot { background: var(--accent); color: #fff; }

.kicker {
  font-size: .68rem; letter-spacing: .13em; text-transform: uppercase;
  color: var(--faint);
  margin: 0 0 .3rem;
}
.kicker b { color: var(--accent); font-weight: 600; }

/* things worth doing together get a quiet mark rather than a loud badge */
.duo { color: var(--blush); letter-spacing: 0; }
.card-title {
  font-family: var(--serif);
  font-size: 1.22rem;
  line-height: 1.25;
  margin: 0 0 .4rem;
  font-weight: 400;
  letter-spacing: -.005em;
  transition: color .18s;
}
.card:hover .card-title { color: var(--accent); }
.why {
  margin: 0;
  font-size: .88rem;
  color: var(--soft);
  line-height: 1.6;
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
  overflow: hidden;
}
.card.open .why { -webkit-line-clamp: unset; overflow: visible; }

.more {
  align-self: flex-start;
  background: none; border: none; padding: .45rem 0 0;
  color: var(--faint); font-family: inherit; font-size: .8rem;
  cursor: pointer; text-decoration: underline; text-underline-offset: 3px;
}
.more:hover { color: var(--accent); }

/* expanded detail */
.detail { display: none; padding-top: .8rem; }
.card.open .detail { display: block; }

.facts { margin: 0 0 .8rem; font-size: .82rem; color: var(--faint); }
.facts div { padding: .12rem 0; }

.prog { border-left: 2px solid var(--line); padding-left: .8rem; margin-bottom: .8rem; font-size: .82rem; color: var(--soft); }
.prog .now { color: var(--accent); font-weight: 600; }

.stops { list-style: none; margin: 0 0 .8rem; padding: 0; }
.stops li {
  font-size: .84rem; color: var(--soft);
  padding: .3rem 0 .3rem .9rem;
  border-left: 1px solid var(--line);
  position: relative;
}
.stops li::before {
  content: ''; position: absolute; left: -3.5px; top: .95rem;
  width: 6px; height: 6px; border-radius: 50%; background: var(--line);
}
.stops .w { color: var(--faint); font-size: .75rem; }

.near { font-size: .8rem; color: var(--faint); margin-bottom: .8rem; }
.near div { padding: .1rem 0; }

.links { display: flex; gap: 1rem; flex-wrap: wrap; margin-bottom: .75rem; }
.links a { font-size: .82rem; color: var(--accent); text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--accent) 35%, transparent); }
.links a:hover { border-bottom-color: var(--accent); }

.rate { display: flex; gap: .3rem; margin-bottom: .6rem; }
.rate button {
  background: none; border: 1px solid var(--line);
  border-radius: 6px; width: 1.8rem; height: 1.8rem;
  font-size: .8rem; line-height: 1; cursor: pointer; padding: 0;
  transition: all .13s;
}
.rate button:hover { border-color: var(--soft); }
.rate button.on { background: var(--ink); border-color: var(--ink); }

.credit { font-size: .68rem; color: var(--faint); line-height: 1.45; }

/* ---------- weekend ---------- */

.plan { display: grid; gap: 1.4rem; grid-template-columns: 1fr; margin-bottom: 2.75rem; }
@media (min-width: 700px) { .plan { grid-template-columns: 1fr 1fr; } }

.day h3 {
  font-family: var(--serif); font-size: 1.3rem; font-weight: 400;
  margin: 0 0 .15rem;
}
.day .when { margin: 0 0 1rem; font-size: .78rem; color: var(--faint); }
.slot { display: flex; gap: 1rem; padding: .65rem 0; border-top: 1px solid var(--line); }
.slot .t {
  flex: 0 0 4.3rem;
  font-size: .67rem; letter-spacing: .12em; text-transform: uppercase;
  color: var(--faint); padding-top: .22rem;
}
.slot .s { font-size: .88rem; color: var(--soft); }
.slot .s b { color: var(--ink); font-weight: 600; display: block; }

.sub-head {
  font-size: .68rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--faint); margin: 0 0 1.1rem;
}

/* ---------- explore ---------- */

.hood { margin-bottom: 2.5rem; }
.hood-shot {
  aspect-ratio: 21 / 9; background: var(--img-bg);
  border-radius: var(--radius); overflow: hidden; margin-bottom: 1.1rem;
}
.hood-shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.hood h3 { font-family: var(--serif); font-size: 1.7rem; font-weight: 400; margin: 0 0 .2rem; }
.hood .sub { color: var(--faint); font-size: .85rem; margin: 0 0 1.4rem; }

.facts-grid { display: grid; gap: 1.05rem; grid-template-columns: 1fr; }
@media (min-width: 640px) { .facts-grid { grid-template-columns: 1fr 1fr; } }
.f dt {
  font-size: .66rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--faint); margin-bottom: .15rem;
}
.f dd { margin: 0; font-size: .89rem; color: var(--soft); }

.zone-grid { display: grid; gap: .35rem; grid-template-columns: 1fr; margin-top: 2rem; }
@media (min-width: 640px) { .zone-grid { grid-template-columns: 1fr 1fr; } }
.zone {
  display: flex; align-items: center; gap: .85rem;
  padding: .6rem 0; border-top: 1px solid var(--line);
  font-size: .88rem;
}
.zone .n { font-family: var(--serif); color: var(--accent); flex: 0 0 1.9rem; }
.zone .nm { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--soft); }
.zone button {
  background: none; border: 1px solid var(--line); border-radius: 999px;
  padding: .16rem .6rem; font-size: .72rem; font-family: inherit;
  color: var(--faint); cursor: pointer; white-space: nowrap;
}
.zone button:hover { border-color: var(--soft); color: var(--ink); }
.zone button.on { border-color: var(--accent); color: var(--accent); }

/* ---------- quests ---------- */

.quests { display: grid; gap: 2rem; grid-template-columns: 1fr; }
@media (min-width: 700px) { .quests { grid-template-columns: 1fr 1fr; } }
.quest h3 { font-family: var(--serif); font-size: 1.15rem; font-weight: 400; margin: 0; }
.quest .qtop { display: flex; justify-content: space-between; align-items: baseline; gap: .6rem; }
.quest .qn { font-size: .78rem; color: var(--faint); }
.quest p { font-size: .84rem; color: var(--faint); margin: .25rem 0 .8rem; }
.bar-track { height: 2px; background: var(--line); margin-bottom: .9rem; }
.bar-track span { display: block; height: 100%; background: var(--accent); transition: width .3s; }
.targets { display: flex; flex-wrap: wrap; gap: .35rem; }
.target {
  background: none; border: 1px solid var(--line); border-radius: 999px;
  padding: .2rem .6rem; font-size: .74rem; font-family: inherit;
  color: var(--soft); cursor: pointer; transition: all .13s;
}
.target:hover { border-color: var(--soft); }
.target.on { border-color: var(--accent); color: var(--accent); text-decoration: line-through; opacity: .65; }

/* ---------- saved ---------- */

.saved-group { margin-bottom: 1.8rem; }
.saved-group h3 {
  font-size: .68rem; letter-spacing: .14em; text-transform: uppercase;
  color: var(--faint); margin: 0 0 .6rem;
}
.saved-group ul { list-style: none; margin: 0; padding: 0; }
.saved-group li { padding: .4rem 0; border-top: 1px solid var(--line); font-size: .89rem; color: var(--soft); }

.empty { color: var(--faint); font-size: .9rem; font-style: italic; }

/* ---------- foot + toast ---------- */

.foot {
  margin-top: 4rem; padding: 2rem 0 3rem;
  border-top: 1px solid var(--line);
  color: var(--faint); font-size: .78rem;
}
.foot p { margin: 0 0 .5rem; max-width: 68ch; }

.toast {
  position: fixed; left: 50%; bottom: 1.5rem;
  transform: translateX(-50%) translateY(200%);
  background: var(--ink); color: var(--bg);
  padding: .6rem 1.1rem; border-radius: 999px;
  font-size: .85rem; z-index: 60;
  transition: transform .3s cubic-bezier(.2,.9,.3,1.2);
  box-shadow: 0 10px 34px rgba(0,0,0,.18);
}
.toast.show { transform: translateX(-50%) translateY(0); }

.card.surprise .shot { outline: 2px solid var(--accent); outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* =========================================================
   LAYOUT SYSTEMS

   One template for eighty things gives no rhythm and no signal.
   Each kind of content gets the shape that suits it:

     hero      one lead item, where the photograph is genuinely good
     list      names and distances — for food and shops, which have no
               photographs of their own and are read by scanning
     route     a numbered sequence, because the order is the content
     trip      wide editorial, for the few big aspirational things
     agenda    a plan laid out by day (.plan / .day, above)
     dossier   an article about one neighbourhood (.hood, above)
   ========================================================= */

/* ---------- hero ---------- */

.hero {
  position: relative;
  display: block;
  border-radius: var(--radius);
  overflow: hidden;
  margin-bottom: 2.25rem;
  background: var(--img-bg);
  text-decoration: none;
  color: inherit;
}
.hero-img { aspect-ratio: 21 / 9; }
.hero-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
@media (max-width: 640px) { .hero-img { aspect-ratio: 4 / 3; } }

.hero-body {
  position: absolute; inset: auto 0 0 0;
  padding: 2.4rem 1.4rem 1.3rem;
  /* deep enough that small type stays legible over a bright photograph */
  background: linear-gradient(to top,
    rgba(10,7,5,.94) 0%, rgba(10,7,5,.86) 38%, rgba(10,7,5,.46) 72%, transparent 100%);
  color: #fff;
  text-shadow: 0 1px 12px rgba(0,0,0,.5);
}
.hero-kicker {
  font-size: .68rem; letter-spacing: .16em; text-transform: uppercase;
  color: rgba(255,255,255,.88); margin: 0 0 .4rem;
}
.hero-kicker .duo { color: var(--blush); }
.hero-title {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(1.5rem, 4.2vw, 2.15rem);
  line-height: 1.12; margin: 0 0 .4rem; color: #fff;
}
.hero-why {
  margin: 0; font-size: .92rem; line-height: 1.55;
  color: rgba(255,255,255,.88); max-width: 56ch;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* ---------- list ---------- */

.list { margin: 0 0 2rem; }

.list-group { margin-bottom: 2rem; }
.list-group > h3 {
  font-size: .68rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--faint); margin: 0 0 .2rem; font-weight: 600;
}

.row {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: .1rem 1.25rem;
  padding: .8rem 0;
  border-top: 1px solid var(--line);
  cursor: pointer;
}
.row:hover .row-name { color: var(--accent); }
.row.done .row-name { color: var(--faint); }

/* Three weights of rule for three kinds of knowledge, so how much the
   site actually knows about a place is legible before you read a word:

     solid   somebody went and wrote it up
     dotted  researched, or a matter of record — but nobody went
     dashed  a name and a position, and no claim beyond that            */
.row.researched { border-top-style: dotted; }
.row.found      { border-top-style: dashed; }
.row.found .row-name { font-family: var(--sans); font-size: 1.02rem; }

.row-name {
  grid-column: 1;
  font-family: var(--serif); font-size: 1.12rem; line-height: 1.3;
  margin: 0; font-weight: 400;
  transition: color .16s;
}
.row-dist {
  grid-column: 2; grid-row: 1;
  font-size: .74rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--faint); white-space: nowrap; padding-top: .3rem;
}
.row-meta {
  grid-column: 1 / -1;
  font-size: .78rem; color: var(--faint);
}
.row-why {
  grid-column: 1 / -1;
  margin: .25rem 0 0; font-size: .86rem; line-height: 1.5; color: var(--soft);
  max-width: 74ch;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.row.open .row-why { -webkit-line-clamp: unset; overflow: visible; }
.row .detail { grid-column: 1 / -1; }
.row.open .detail { display: block; }

/* ---------- route ---------- */

.routes { display: grid; gap: 1.5rem; grid-template-columns: 1fr; }
@media (min-width: 800px) { .routes { grid-template-columns: 1fr 1fr; } }

/* A card left alone in the last row spans it. Sport has exactly one run
   with stops, so the two-column grid put a 488px card above three rows
   running the full 1000px — which reads as a layout fault rather than as
   the section's lead item. The same rule catches the third of three walks
   in Explore. */
.routes > .route:last-child:nth-child(odd) { grid-column: 1 / -1; }

.route {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.35rem 1.4rem;
  background: var(--card);
}
/* Stacked, the photograph is a band across the head of the card: wide and
   shallow, so it introduces the route rather than becoming it. The fade is
   the one the grid cards use — these load lazily, well below the fold. */
.route-shot {
  aspect-ratio: 21 / 9;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--img-bg);
  margin-bottom: 1.1rem;
}
.route-shot img {
  width: 100%; height: 100%;
  object-fit: cover; display: block;
  opacity: 0; transition: opacity .22s;
}
.route-shot img.loaded { opacity: 1; }

.route-head { margin-bottom: 1.1rem; }
.route h3 { font-family: var(--serif); font-size: 1.3rem; font-weight: 400; margin: 0 0 .25rem; }
.route-meta {
  font-size: .72rem; letter-spacing: .1em; text-transform: uppercase; color: var(--faint);
}
.route-why { font-size: .88rem; color: var(--soft); line-height: 1.6; margin: .6rem 0 0; }

.steps { list-style: none; margin: 0; padding: 0; counter-reset: step; }
.steps li {
  position: relative;
  counter-increment: step;
  padding: 0 0 1.05rem 2.1rem;
  font-size: .89rem; line-height: 1.5; color: var(--soft);
}
.steps li::before {
  content: counter(step);
  position: absolute; left: 0; top: -.1rem;
  width: 1.45rem; height: 1.45rem;
  border-radius: 50%;
  border: 1px solid var(--line);
  color: var(--accent);
  font-family: var(--serif); font-size: .8rem;
  display: grid; place-items: center;
  background: var(--card);
}
/* the thread between stops */
.steps li::after {
  content: '';
  position: absolute; left: .72rem; top: 1.45rem; bottom: .15rem;
  width: 1px; background: var(--line);
}
.steps li:last-child { padding-bottom: 0; }
.steps li:last-child::after { display: none; }
.steps .w { display: block; color: var(--faint); font-size: .75rem; margin-top: .1rem; }

/* Widened, the card stops stacking: the write-up takes one column and the
   stops the other. Ninety-character lines of prose and a numbered list
   trailing half a page of white space are what stacking looks like at
   1000px, and neither is the reason the card is the lead item. Cards that
   are still sharing the row keep the stacked layout. */
@media (min-width: 800px) {
  .route:last-child:nth-child(odd) {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 0 2.75rem;
    align-items: start;
  }
  .route:last-child:nth-child(odd) .route-head { margin-bottom: 0; }

  /* With the room to do it, the picture takes a column instead of a band —
     which is the anatomy of the rows underneath it, at the size the lead
     item deserves: photograph, then name, then the detail. A shallow strip
     over a card this wide would only be a rule with a view. */
  .route.has-shot:last-child:nth-child(odd) {
    grid-template-columns: 17rem minmax(0, 1fr) minmax(0, 1fr);
    gap: 0 2.25rem;
  }
  .route.has-shot:last-child:nth-child(odd) .route-shot {
    aspect-ratio: 4 / 3;
    margin-bottom: 0;
  }
}

/* ---------- trip (wide editorial) ---------- */

.trips { display: flex; flex-direction: column; gap: 2.25rem; }

.trip {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
  align-items: center;
}
@media (min-width: 680px) {
  .trip { grid-template-columns: 1.05fr 1fr; gap: 2rem; }
  .trip:nth-child(even) .trip-img { order: 2; }
}

.trip-img {
  aspect-ratio: 3 / 2;
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--img-bg);
  position: relative;
}
.trip-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.trip-time {
  position: absolute; left: .7rem; bottom: .7rem;
  background: color-mix(in srgb, var(--card) 92%, transparent);
  backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px);
  font-size: .7rem; letter-spacing: .1em; text-transform: uppercase;
  padding: .28rem .6rem; border-radius: 5px; color: var(--ink);
}
.trip h3 { font-family: var(--serif); font-size: clamp(1.4rem, 3vw, 1.75rem); font-weight: 400; margin: 0 0 .3rem; }
/* Long transit strings in uppercase are unreadable — keep them as written. */
.trip-meta { font-size: .82rem; color: var(--faint); margin: 0 0 .7rem; line-height: 1.5; }
.trip-why { font-size: .93rem; line-height: 1.65; color: var(--soft); margin: 0 0 .8rem; }

/* ---------- shared small bits ---------- */

.strip-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 1rem;
  margin: 2.4rem 0 1rem;
  padding-top: 1.4rem;
  border-top: 1px solid var(--line);
}
.strip-head h2 {
  font-family: var(--serif); font-weight: 400; font-size: 1.32rem; margin: 0;
}
.strip-head p { margin: 0; font-size: .8rem; color: var(--faint); }

.pick-label { color: var(--accent); font-weight: 600; letter-spacing: .06em; text-transform: uppercase; font-size: .72rem; }

/* the utility tabs collapse first when space runs out */
@media (max-width: 620px) {
  .tabs-inner { gap: 1rem; }
  .tabs-main { gap: .9rem; }
  .tab { font-size: .94rem; }
}

/* On a narrow screen the two utility tabs push the nav into a horizontal
   scroll, which hides them. Better to drop them out of the bar entirely
   and surface them in the footer, where nothing is competing for room. */
.foot-links { display: none; gap: 1.25rem; }
.tab-link {
  background: none; border: none; padding: 0;
  font-family: inherit; font-size: .82rem;
  color: var(--accent); cursor: pointer;
  text-decoration: underline; text-underline-offset: 3px;
}
.tab-link:hover { color: var(--ink); }

@media (max-width: 620px) {
  .tabs-util { display: none; }
  .tabs-inner { justify-content: flex-start; }
  .foot-links { display: flex; }
}

/* Placeholder tile for the handful of things with no freely licensed
   photograph. Same aspect ratio as a real one so the grid stays aligned,
   but visibly a tile rather than a picture — no pretending. */
.shot.ph {
  display: grid;
  place-items: center;
  background:
    radial-gradient(120% 100% at 30% 0%, color-mix(in srgb, var(--blush) 22%, transparent), transparent 70%),
    linear-gradient(150deg, var(--bg-2), var(--img-bg));
}
.ph-mark {
  font-size: 2.9rem;
  opacity: .45;
  filter: saturate(.7);
  line-height: 1;
}

/* With seven destinations the bar no longer fits a phone. Let it scroll, but
   make that legible: a soft fade on the right edge so it reads as "more this
   way" rather than as text clipped by accident. */
@media (max-width: 720px) {
  .tabs { position: sticky; }
  .tabs::after {
    content: '';
    position: absolute; top: 0; right: 0; bottom: 1px;
    width: 2.5rem;
    pointer-events: none;
    background: linear-gradient(to right, transparent, var(--bg) 82%);
  }
  .tabs-inner { padding-right: 2rem; }
}

/* =========================================================
   SECTION PERSONALITIES

   Same components, different temperature. Sport runs warm and
   energetic, Eat is appetite-warm, Explore and Away stay cool and
   editorial. Only the accent moves — the structure is shared, so
   this reads as one publication rather than six microsites.
   ========================================================= */

body[data-view="sport"]   { --accent: #b04a2f; --blush: #d8a07a; }
body[data-view="eat"]     { --accent: #a4472e; --blush: #d9a15f; }
body[data-view="nights"]  { --accent: #7d4a86; --blush: #b98fc4; }
body[data-view="explore"] { --accent: #4a6b5c; --blush: #9dbd93; }
body[data-view="away"]    { --accent: #3f5e7a; --blush: #93add0; }

:root[data-theme="dark"] body[data-view="sport"]   { --accent: #e79878; }
:root[data-theme="dark"] body[data-view="eat"]     { --accent: #e29a72; }
:root[data-theme="dark"] body[data-view="nights"]  { --accent: #c9a2d4; }
:root[data-theme="dark"] body[data-view="explore"] { --accent: #a3c79a; }
:root[data-theme="dark"] body[data-view="away"]    { --accent: #9dbbd8; }

/* ---------- play / watch mode switch ---------- */

.mode {
  display: grid; grid-template-columns: 1fr 1fr; gap: .6rem;
  margin-bottom: 2rem;
}
.mode-btn {
  text-align: left;
  background: none;
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1rem 1.1rem;
  font-family: var(--serif); font-size: 1.25rem;
  color: var(--faint);
  cursor: pointer;
  transition: all .18s;
}
.mode-btn em {
  display: block;
  font-family: var(--sans); font-style: normal;
  font-size: .76rem; letter-spacing: .04em;
  color: var(--faint); margin-top: .2rem;
}
.mode-btn .mode-emoji { font-size: 1.1rem; margin-right: .3rem; }
.mode-btn:hover { border-color: var(--soft); color: var(--ink); }
.mode-btn.on {
  border-color: var(--accent);
  color: var(--ink);
  background: color-mix(in srgb, var(--accent) 7%, transparent);
}
.mode-btn.on em { color: var(--accent); }

.sport-chips { margin-bottom: 1.4rem; }

/* ---------- sport of the week ---------- */

.sotw {
  border: 1px solid var(--accent);
  border-radius: var(--radius);
  padding: 1.2rem 1.3rem 1.35rem;
  margin-bottom: 1rem;
  background: color-mix(in srgb, var(--accent) 5%, transparent);
}
.sotw-kicker {
  margin: 0 0 .8rem;
  font-size: .68rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--accent); font-weight: 600;
}
.sotw-body { display: grid; grid-template-columns: 1fr; gap: 1.1rem; }
@media (min-width: 760px) { .sotw-body { grid-template-columns: 420px 1fr; } }
.sotw-img { aspect-ratio: 4/3; border-radius: 10px; overflow: hidden; background: var(--img-bg); }
.sotw-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sotw h3 { font-family: var(--serif); font-size: 1.5rem; font-weight: 400; margin: 0 0 .25rem; }
.sotw-meta { margin: 0 0 .7rem; font-size: .78rem; letter-spacing: .06em; text-transform: uppercase; color: var(--faint); }
.sotw-why { margin: 0 0 .9rem; font-size: .93rem; line-height: 1.65; color: var(--soft); }

/* ---------- the week plan ---------- */

.week-plan .slot .t { flex: 0 0 8.5rem; }
.week-plan .slot .s b { display: block; margin-bottom: .1rem; }
@media (max-width: 560px) { .week-plan .slot { flex-direction: column; gap: .1rem; } .week-plan .slot .t { flex: none; } }

/* ---------- pairings ---------- */

.pairs { display: flex; gap: .6rem; align-items: baseline; flex-wrap: wrap; margin: .7rem 0 .2rem; }
.pairs-label {
  font-size: .64rem; letter-spacing: .16em; text-transform: uppercase;
  color: var(--faint); flex: 0 0 auto;
}
.pairs-row { display: flex; gap: .4rem; flex-wrap: wrap; }
.pair {
  font-size: .78rem;
  padding: .2rem .6rem;
  border-radius: 999px;
  background: color-mix(in srgb, var(--accent) 9%, transparent);
  color: var(--soft);
}

/* ---------- fixtures (watch calendar) ---------- */

.fixtures { display: flex; flex-direction: column; }
.fixture {
  display: grid; grid-template-columns: 3.4rem 1fr; gap: 1.1rem;
  padding: 1.1rem 0;
  border-top: 1px solid var(--line);
}
.fx-date {
  text-align: center; padding-top: .2rem;
  border-right: 1px solid var(--line);
  padding-right: .6rem;
}
.fx-day { display: block; font-family: var(--serif); font-size: 1.6rem; line-height: 1; color: var(--accent); }
.fx-mon { display: block; font-size: .66rem; letter-spacing: .14em; text-transform: uppercase; color: var(--faint); margin-top: .2rem; }
.fx-body h3 { font-family: var(--serif); font-size: 1.2rem; font-weight: 400; margin: 0 0 .2rem; }
.fx-meta { margin: 0 0 .5rem; font-size: .76rem; letter-spacing: .05em; text-transform: uppercase; color: var(--faint); }
.fx-why { margin: 0 0 .6rem; font-size: .89rem; line-height: 1.6; color: var(--soft); }
.fx-spec {
  margin: 0 0 .5rem; font-size: .86rem; line-height: 1.6; color: var(--soft);
  border-left: 2px solid var(--accent); padding-left: .8rem;
}
.fx-spec b { color: var(--ink); font-weight: 600; }

/* ---------- eat: moods + missions ---------- */

.moods { margin-bottom: 1.8rem; }
.moods-label {
  display: block; margin-bottom: .6rem;
  font-family: var(--serif); font-style: italic; font-size: 1.05rem; color: var(--soft);
}
.chip.mood { font-size: .84rem; padding: .38rem .85rem; }

.missions { display: grid; grid-template-columns: 1fr; gap: 1.6rem; }
@media (min-width: 900px) { .missions { grid-template-columns: 1fr 1fr; } }

.mission {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--card);
  display: flex; flex-direction: column;
}
.mission.lead { border-color: var(--accent); margin-bottom: 1rem; }
.mission-img { aspect-ratio: 16/9; background: var(--img-bg); overflow: hidden; }
.mission.lead .mission-img { aspect-ratio: 21/9; }
.mission-img img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mission-body { padding: 1.2rem 1.3rem 1.3rem; }
.mission-kicker {
  margin: 0 0 .35rem;
  font-size: .66rem; letter-spacing: .15em; text-transform: uppercase; color: var(--accent); font-weight: 600;
}
/* Where the mission actually happens, when that is not where you are. */
.mission-where {
  margin: -.1rem 0 .5rem;
  font-size: .8rem; color: var(--muted);
}
.mission-title {
  font-family: var(--serif); font-weight: 400;
  font-size: clamp(1.35rem, 3.2vw, 1.9rem);
  margin: 0 0 .5rem; line-height: 1.15;
}
.mission.lead .mission-title { font-size: clamp(1.6rem, 4.5vw, 2.3rem); }
.mission-brief { margin: 0 0 .7rem; font-size: .95rem; line-height: 1.65; color: var(--soft); }
.mission-test {
  margin: 0 0 1rem; font-size: .86rem; line-height: 1.6; color: var(--soft);
  background: color-mix(in srgb, var(--accent) 6%, transparent);
  padding: .6rem .8rem; border-radius: 8px;
}
.mission-test b { color: var(--ink); font-weight: 600; }

.cands { list-style: none; margin: 0 0 .6rem; padding: 0; counter-reset: c; }
.cands li {
  display: grid; grid-template-columns: 1.7rem 1fr auto; gap: .1rem .7rem;
  align-items: baseline;
  padding: .6rem 0; border-top: 1px solid var(--line);
}
.cand-n {
  font-family: var(--serif); font-size: .95rem; color: var(--accent);
}
.sch-time {
  font-size: .72rem; letter-spacing: .06em; color: var(--accent); font-weight: 600;
}
.cand-body { font-size: .88rem; color: var(--soft); }
.cand-body b { display: block; color: var(--ink); font-weight: 600; font-size: .95rem; }
.cand-order { display: block; margin-top: .12rem; }
.cand-note { display: block; color: var(--faint); font-size: .82rem; margin-top: .1rem; }
.cand-walk { font-size: .72rem; color: var(--faint); white-space: nowrap; }

.mission-foot {
  display: flex; justify-content: space-between; align-items: center; gap: 1rem;
  flex-wrap: wrap; margin-top: .9rem; padding-top: .8rem; border-top: 1px solid var(--line);
}
.mission-why { font-size: .78rem; color: var(--faint); font-style: italic; flex: 1; min-width: 12rem; }

@media (max-width: 560px) {
  .mode { grid-template-columns: 1fr; }
  .cands li { grid-template-columns: 1.7rem 1fr; }
  .cand-walk { grid-column: 2; }
}

/* Emoji sit tight against following text in most system fonts. */
.e { margin-right: .38em; }

/* =========================================================
   QUESTS — progress you can see

   A checklist is not an achievement. Ring, count, completion
   state, and a map for the arrondissements, because Paris
   spirals outward from the 1st and watching that fill in beats
   watching a number go up.
   ========================================================= */

.quests.one { grid-template-columns: 1fr; }

.quest { position: relative; }
.qtop { display: flex; justify-content: space-between; align-items: flex-start; gap: 1.2rem; }
.qhead { flex: 1; min-width: 0; }
.qdesc { font-size: .84rem; color: var(--faint); margin: .25rem 0 .9rem; }

.qprog { position: relative; flex: 0 0 auto; width: 3.1rem; height: 3.1rem; display: grid; place-items: center; }
.ring { position: absolute; inset: 0; width: 100%; height: 100%; transform: rotate(-90deg); }
.ring-bg, .ring-fg { fill: none; stroke-width: 2.6; }
.ring-bg { stroke: var(--line); }
.ring-fg {
  stroke: var(--accent); stroke-linecap: round;
  transition: stroke-dashoffset .5s cubic-bezier(.3,.8,.3,1);
}
.qn { font-family: var(--serif); font-size: 1.05rem; color: var(--ink); line-height: 1; }
.qn-total { font-size: .68rem; color: var(--faint); }

.quest.complete { border-color: var(--accent); }
.quest.complete .ring-fg { stroke: var(--accent); }
.qdone {
  margin: 0 0 .8rem; font-size: .8rem; letter-spacing: .06em;
  color: var(--accent); font-weight: 600;
}

.target.on {
  border-color: var(--accent); color: var(--accent);
  background: color-mix(in srgb, var(--accent) 8%, transparent);
  text-decoration: none; opacity: 1;
}
.target.on::before { content: '✓ '; }

/* ---------- the arrondissement map ---------- */

.quest.is-map { grid-column: 1 / -1; }
.zone-map-wrap { margin-top: .4rem; }
.zone-map {
  width: 100%; max-width: 30rem; height: auto; display: block; margin: 0 auto;
  overflow: visible;
}
.seine {
  fill: none;
  stroke: color-mix(in srgb, var(--blue, #35566e) 45%, transparent);
  stroke-width: 2.4; stroke-linecap: round;
  opacity: .5;
}
.zone-dot { cursor: pointer; }
.zone-dot circle {
  fill: var(--card); stroke: var(--line); stroke-width: .9;
  transition: fill .25s, stroke .25s, transform .25s;
}
.zone-dot text {
  font-family: var(--sans); font-size: 4.4px; font-weight: 600;
  text-anchor: middle; fill: var(--faint);
  transition: fill .25s; pointer-events: none;
}
.zone-dot:hover circle { stroke: var(--accent); }
.zone-dot.on circle { fill: var(--accent); stroke: var(--accent); }
.zone-dot.on text { fill: var(--bg); }
.zone-dot.home circle { fill: var(--gold); stroke: var(--gold); }
.zone-dot.home text { fill: var(--bg); }
.zone-map-note {
  text-align: center; margin: .9rem 0 0;
  font-size: .76rem; color: var(--faint);
}

/* =========================================================
   ROW THUMBNAILS
   Cafés have no photograph of their own, but they do have a
   street — and at 64px a street reads as texture rather than
   as a claim about the shopfront.
   ========================================================= */

.row.has-thumb {
  grid-template-columns: 4rem 1fr auto;
  gap: .1rem .95rem;
  align-items: start;
}
.row-thumb {
  grid-row: 1 / span 3;
  width: 4rem; height: 4rem;
  border-radius: 8px; overflow: hidden;
  background: var(--img-bg);
  display: grid; place-items: center;
}
.row-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

/* A row with no photograph gets the same tile the cards use rather than a
   flat square — most of the list has no picture and never will, so the
   thing that stands in for one has to look composed rather than missing.
   The tint comes from --blush, which each view already shifts, so a
   bakery tile in Eat and a pitch in Sport are not the same colour. */
.row-thumb.ph {
  background:
    radial-gradient(120% 100% at 30% 0%, color-mix(in srgb, var(--blush) 20%, transparent), transparent 70%),
    linear-gradient(150deg, var(--bg-2), var(--img-bg));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--line) 70%, transparent);
}
.row-thumb.ph .e { font-size: 1.6rem; opacity: .55; margin: 0; filter: saturate(.75); }

/* ---------- markets get an awning, not a photograph ----------

   There is no openly licensed photograph of a Paris food market to be
   had. Measured, not assumed: Wikipedia page images matched none of a
   forty-name sample, Wikidata has no item for a municipal market, and
   Commons categories covered five of twenty-four — one of which is a
   picture of counterfeit perfume. The city photographs its own markets
   and those images are 300 KB each, cannot be thumbnailed, and are not
   ours to republish.

   So the tile stops pretending and becomes a drawing instead. Striped
   canvas is what a French market awning actually looks like, it costs no
   request and no bytes, and it makes a market row identifiable at a
   glance without claiming to be a photograph of anywhere in particular.
   The stripes take the accent the view is already using, so they sit in
   the palette rather than beside it. */
.row-thumb.ph[data-kind="market"],
.shot.ph[data-kind="market"] {
  --stripe: 5px;
  --canvas: color-mix(in srgb, var(--blush) 34%, transparent);
  background:
    repeating-linear-gradient(90deg,
      var(--canvas) 0 var(--stripe),
      transparent var(--stripe) calc(var(--stripe) * 2))
      top / 100% var(--awning, 100%) no-repeat,
    linear-gradient(170deg, var(--bg-2), var(--img-bg));
}

/* At card size the stripes have to stop being the whole tile. Run over
   300 pixels they read as wallpaper and swallow the mark; confined to a
   band across the top with the rest left quiet, the same two gradients
   read as a canopy over an empty stall, which is the thing being drawn.
   The row thumbnail is 64px and keeps the full stripe — a band that
   shallow would be four pixels of nothing. */
.shot.ph[data-kind="market"] {
  --stripe: 22px;
  --awning: 38%;
}
.row.has-thumb .row-name  { grid-column: 2; }
.row.has-thumb .row-dist  { grid-column: 3; grid-row: 1; }
.row.has-thumb .row-meta  { grid-column: 2 / -1; }
.row.has-thumb .row-why   { grid-column: 2 / -1; }
.row.has-thumb .detail    { grid-column: 1 / -1; }

@media (max-width: 560px) {
  .row.has-thumb { grid-template-columns: 3.2rem 1fr auto; }
  .row-thumb { width: 3.2rem; height: 3.2rem; }
}

/* Five subsections rather than two, so the buttons need to be narrower —
   but they stay prominent, because they are the navigation for this
   section rather than a footer to scroll past. */
.mode-wide { grid-template-columns: repeat(5, 1fr); gap: .5rem; }
.mode-wide .mode-btn { padding: .85rem .8rem; font-size: 1.05rem; }
.mode-wide .mode-btn em { font-size: .7rem; }
.mode-wide .mode-emoji { display: block; margin: 0 0 .15rem; font-size: 1.15rem; }

@media (max-width: 900px) { .mode-wide { grid-template-columns: repeat(3, 1fr); } }
@media (max-width: 560px) { .mode-wide { grid-template-columns: repeat(2, 1fr); } }

/* =========================================================
   LOCATION
   Where you are exploring from is the first thing the site
   needs to know, so it sits under the wordmark rather than
   in a settings screen.
   ========================================================= */

.locbar { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin: 0 0 1rem; }

.locbtn {
  display: flex; align-items: center; gap: .7rem;
  background: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: .45rem 1rem .45rem .8rem;
  cursor: pointer; font-family: inherit;
  transition: border-color .18s;
}
.locbtn:hover { border-color: var(--accent); }
.loc-pin { font-size: .95rem; }
.loc-text { text-align: left; line-height: 1.25; }
.loc-kicker {
  display: block; font-size: .6rem; letter-spacing: .16em;
  text-transform: uppercase; color: var(--faint);
}
.loc-name { display: block; font-family: var(--serif); font-size: 1rem; color: var(--ink); }
.loc-change {
  font-size: .72rem; letter-spacing: .08em; text-transform: uppercase;
  color: var(--accent); border-left: 1px solid var(--line); padding-left: .7rem;
}
.locreset {
  background: none; border: none; padding: 0;
  font-family: inherit; font-size: .78rem; color: var(--faint);
  text-decoration: underline; cursor: pointer;
}
.locreset:hover { color: var(--accent); }

.locpanel {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 1.1rem 1.15rem;
  margin: 0 0 1.2rem;
  background: var(--card);
}
.locrow { display: flex; gap: .5rem; margin-bottom: .7rem; }
.locrow input {
  flex: 1; min-width: 0;
  border: 1px solid var(--line); border-radius: 999px;
  padding: .5rem 1rem; font-family: inherit; font-size: .9rem;
  background: var(--bg); color: var(--ink);
}
.locrow input:focus { outline: none; border-color: var(--accent); }
.locactions { display: flex; gap: .4rem; flex-wrap: wrap; margin-bottom: .9rem; }
.locgroup { margin-bottom: .8rem; }
.loclabel {
  display: block; margin-bottom: .35rem;
  font-size: .64rem; letter-spacing: .16em; text-transform: uppercase; color: var(--faint);
}
.locerr {
  margin: 0 0 .7rem; font-size: .82rem; color: var(--accent);
  border-left: 2px solid var(--accent); padding-left: .7rem;
}
.locnote { margin: .3rem 0 0; font-size: .72rem; color: var(--faint); line-height: 1.5; }

/* ---------- around you ---------- */

.near-grid { display: grid; grid-template-columns: 1fr; gap: .85rem; }
@media (min-width: 620px) { .near-grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 940px) { .near-grid { grid-template-columns: repeat(3, 1fr); } }

.near-card {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: .9rem 1rem 1rem;
  background: var(--card);
}
.near-card.researched { border-style: dotted; }
.near-card.found      { border-style: dashed; }
.card.researched .shot.ph { border-style: dotted; }
.card.found .shot.ph      { border-style: dashed; }
.near-label {
  margin: 0 0 .3rem;
  font-size: .64rem; letter-spacing: .15em; text-transform: uppercase;
  color: var(--accent); font-weight: 600;
}
.near-name { font-family: var(--serif); font-size: 1.08rem; font-weight: 400; margin: 0 0 .2rem; line-height: 1.25; }
.near-meta { margin: 0 0 .4rem; font-size: .73rem; color: var(--faint); }
.near-why {
  margin: 0 0 .6rem; font-size: .85rem; line-height: 1.5; color: var(--soft);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.near-link { font-size: .78rem; color: var(--accent); text-decoration: none; border-bottom: 1px solid color-mix(in srgb, var(--accent) 35%, transparent); }

.debug {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: .72rem; line-height: 1.6;
  background: var(--bg-2); border: 1px solid var(--line);
  border-radius: 8px; padding: .8rem 1rem; margin: 0 0 1.2rem;
  white-space: pre; overflow-x: auto; color: var(--soft);
}

/* ---------- active city exploration ----------
   The Invader hunt, and whatever joins it. Built to read as a panel you
   act on rather than a card you look at: the score line, the nearest few
   with a tick beside each, then the missions. */

.citygame { margin: 0 0 2rem; }
.citygame h4 { font-size: 1.25rem; margin: 0 0 .35rem; letter-spacing: -.01em; }
.citygame-why { margin: 0 0 1rem; max-width: 62ch; color: var(--ink-2, #5b5651); }

/* The write-up stops at 62 characters and the panel below runs the full
   width, which left a column of nothing beside the opening paragraph and
   made the section read as heavier than it is. The drawing goes there. */
.citygame-head { display: flex; flex-direction: column; }
.citygame-tile {
  width: 5rem; order: -1; margin: 0 0 .8rem;
  aspect-ratio: 1;
  border-radius: var(--radius);
  display: grid; place-items: center;
  padding: .9rem;
  color: var(--accent);
  background:
    radial-gradient(120% 100% at 30% 0%, color-mix(in srgb, var(--blush) 22%, transparent), transparent 70%),
    linear-gradient(150deg, var(--bg-2), var(--img-bg));
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--line) 70%, transparent);
}
.citygame-tile svg { width: 100%; height: auto; display: block; }

@media (min-width: 760px) {
  .citygame-head {
    display: grid;
    grid-template-columns: minmax(0, 1fr) 10rem;
    gap: 0 2.5rem;
    align-items: start;
  }
  .citygame-tile { width: 10rem; order: 0; margin: .2rem 0 0; }
}

.inv-panel {
  border: 1px solid var(--rule, #e6e0d8);
  border-radius: 10px;
  padding: 1.1rem 1.2rem 1rem;
  background: var(--card, #fffdf9);
}

.inv-score { display: flex; gap: 2rem; margin: 0 0 1.1rem; flex-wrap: wrap; }
.inv-stat { display: flex; flex-direction: column; line-height: 1.1; }
.inv-stat b { font-size: 1.7rem; font-weight: 500; letter-spacing: -.02em; }
.inv-stat span { font-size: .78rem; text-transform: uppercase; letter-spacing: .06em;
                 color: var(--ink-3, #8a827a); margin-top: .2rem; }

.inv-sub { font-size: .78rem; text-transform: uppercase; letter-spacing: .06em;
           color: var(--ink-3, #8a827a); margin: 1.1rem 0 .5rem; }
.inv-sub:first-of-type { margin-top: 0; }

.inv-list { list-style: none; margin: 0; padding: 0; }
.inv-item {
  display: grid;
  grid-template-columns: auto auto 1fr auto auto;
  align-items: baseline;
  gap: .6rem;
  padding: .45rem 0;
  border-bottom: 1px solid var(--rule-soft, #f0ebe3);
  font-size: .92rem;
}
.inv-item:last-child { border-bottom: 0; }
.inv-item.done .inv-code, .inv-item.done .inv-where { opacity: .45; text-decoration: line-through; }

.inv-tick {
  border: 1px solid var(--rule, #e6e0d8); background: transparent; cursor: pointer;
  width: 1.55rem; height: 1.55rem; border-radius: 50%; line-height: 1;
  font-size: .85rem; color: var(--ink-2, #5b5651); padding: 0;
}
.inv-tick[aria-pressed="true"] { background: var(--accent, #8c2f39); border-color: var(--accent, #8c2f39); color: #fff; }

.inv-code { font-variant-numeric: tabular-nums; font-size: .82rem;
            color: var(--ink-3, #8a827a); white-space: nowrap; }
.inv-where { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.inv-dist { font-size: .82rem; color: var(--ink-3, #8a827a); white-space: nowrap; }
.inv-map { font-size: .82rem; white-space: nowrap; }

.inv-missions { display: grid; gap: .9rem; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }
.inv-mission {
  border: 1px solid var(--rule-soft, #f0ebe3); border-radius: 8px;
  padding: .8rem .9rem; background: var(--bg, #fbf8f3);
}
.inv-mission h5 { margin: 0 0 .3rem; font-size: 1rem; font-weight: 500; }
.inv-mission-line { margin: 0 0 .5rem; font-size: .9rem; }
.inv-mission-meta { margin: 0 0 .4rem; font-size: .82rem; color: var(--ink-3, #8a827a); }
.inv-mission-stops { margin: 0; font-size: .8rem; font-variant-numeric: tabular-nums;
                     color: var(--ink-3, #8a827a); word-break: break-word; }
.inv-pair { margin: .5rem 0 0; font-size: .84rem; }

.inv-caveat { margin: 1rem 0 0; font-size: .8rem; color: var(--ink-3, #8a827a); max-width: 68ch; }

@media (max-width: 560px) {
  .inv-item { grid-template-columns: auto 1fr auto; row-gap: .15rem; }
  .inv-code { grid-column: 2; }
  .inv-where { grid-column: 2; white-space: normal; }
  .inv-dist, .inv-map { grid-column: 3; }
  .inv-score { gap: 1.4rem; }
}
