/* ============================================================================
   Trash Finder — design system v2
   Quiet, trustworthy, modern travel utility. Japanese without cliché.
   Everything below is tokenised; components never hard-code a colour.
   ========================================================================== */

:root {
  color-scheme: light dark;

  /* --- neutral ramp: warm paper + sumi ink (BRAND.md, decision 4).
         The canvas is paper so that the map's white reads as the subject. --- */
  /* The ink ramp is a readability ramp, not a greyness ramp. This app is read
     outdoors, on a phone, by somebody holding a wrapper and in a hurry, so
     every step that carries TEXT has to clear WCAG AA (4.5:1) on the lightest
     ground it can land on -- --surface-3 #EFF1F4. The old --ink-4 #98A2AE was
     2.3:1 there: a design choice indoors and unreadable in sunlight, across
     about fifty rules. audit_contrast.py computes all of it.
     --ink-mute keeps the old faint tone for the few places where faintness is
     the point (a field placeholder, an unlit star) and nothing has to be read. */
  --ink:        #14181D;   /* text */
  --ink-1:      #14181D;   /* was used in 11 rules and never defined: they all
                              silently inherited their parent's colour */
  --ink-2:      #39424D;
  --ink-3:      #5A6573;   /* 5.4:1 on --surface-3 */
  --ink-4:      #636F7E;   /* 4.5:1 on --surface-3 -- the floor for text */
  --ink-mute:   #98A2AE;   /* decoration only. Never put words in this. */
  --navy:       #1B2430;   /* primary button, as on the board */
  --on-navy:    #FFFFFF;
  --canvas:     #F4F5F7;   /* cool light grey ground */
  --surface:    #FFFFFF;
  --surface-2:  #F8F9FB;
  --surface-3:  #EFF1F4;
  --line:       #E6E8EC;
  --line-2:     #D6DAE1;
  --on-ink:     #FFFFFF;
  /* An accent that sits ON --ink has to flip with it. --trust does not, so
     the toast's tick was bright green on near-black in light (fine) and
     bright green on near-white in dark: 1.95:1, invisible. Same trap as the
     `color: #fff` that was on the .photo-undo scrim. */
  --on-ink-accent: #46C078;
  --pine:       #0E3A2E;   /* editorial surfaces only */
  --pine-2:     #16513E;

  /* --- semantic --- */
  /* --- semantic only. Green never decorates: it means "you may use this". --- */
  /* Each semantic hue has two jobs and they need two values. The bright one
     fills pins, bars and icons, where 3:1 is the bar. The -800 one is for the
     same colour used as WORDS on its own tint -- "Public", "Ticketed area",
     "Yes, it was there" -- where the bar is 4.5:1 and the bright value misses
     it badly (--trust on --trust-50 was 2.9:1). Same hue, same meaning,
     legible. */
  --trust:      #2FA75B;
  --trust-700:  #218044;
  --trust-800:  #237D44;   /* trust as text on --trust-50 / -100 / white */
  --trust-100:  #E3F5EA;
  --trust-50:   #F1FAF4;
  --info:       #3B7BC8;   --info-100: #E6F0FB;   --info-700:   #336EB5;
  --violet:     #7A6BC4;   --violet-100: #EDEAF8; --violet-700: #6D5CBE;
  --amber:      #D08340;   --amber-100: #FBEFE3;  --amber-700:  #9E5F27;
  --danger:     #A8453C;   --danger-100: #F6E7E4; --danger-700: #A8453C;
  --geo:        #2F80ED;

  /* --- waste categories (colour-coded, per the concept board) --- */
  --w-general:   #2FA75B;  --w-general-bg:   #E3F5EA;   /* green  — general */
  --w-cup:       #6B7684;  --w-cup-bg:       #EDEFF3;   /* grey   — plastic cup */
  --w-pet:       #3B7BC8;  --w-pet-bg:       #E6F0FB;   /* blue   — PET bottle */
  --w-can:       #D4A017;  --w-can-bg:       #FBF3D9;   /* yellow — cans */
  --w-glass:     #7A6BC4;  --w-glass-bg:     #EDEAF8;   /* violet — glass */
  --w-food:      #9A6B3F;  --w-food-bg:      #F3EBE2;   /* brown  — food */
  --w-other:     #4E8FA8;  --w-other-bg:     #E6F1F5;   /* teal   — other */

  /* --- categories (board §6): green trash, indigo toilet, cyan water, amber smoking --- */
  --cat-trash:   #2FA75B;
  --cat-toilet:  #3B6FC8;
  --cat-water:   #2E9BB5;
  --cat-smoking: #C98A2E;
  /* The selected tab is white text on the category colour, and white on the
     bright green was 3.1:1 -- the primary navigation of the app, below AA.
     These are the same hues taken down until white clears 4.5:1. */
  --cat-trash-strong:   #218044;
  --cat-toilet-strong:  #2F5CA8;
  --cat-water-strong:   #247E93;
  --cat-smoking-strong: #96661F;
  --on-cat: #FFFFFF;
  /* JS overwrites these per category; the defaults matter for the first
     paint and for anything rendered before app.js has run. */
  --cat: var(--cat-trash);
  --cat-strong: var(--cat-trash-strong);
  /* Two waste glyphs sat under the 3:1 floor for non-text contrast. */
  --w-general-ico: #237D44;
  --w-can-ico:     #946C14;

  /* --- pins --- */
  --pin-verified:   #2FA75B;
  --pin-likely:     #8A94A2;
  --pin-unverified: #B9C1CB;
  --pin-customers:  #6155A6;
  --pin-restricted: #B2762C;

  /* --- elevation: tinted with the ink hue, never pure black --- */
  --e1: 0 1px 2px rgba(20,24,29,.05), 0 2px 8px rgba(20,24,29,.04);
  --e2: 0 2px 6px rgba(20,24,29,.06), 0 8px 24px rgba(20,24,29,.07);
  --e3: 0 4px 12px rgba(20,24,29,.08), 0 16px 40px rgba(20,24,29,.10);
  --e-sheet: 0 -2px 10px rgba(20,24,29,.05), 0 -18px 50px rgba(20,24,29,.10);

  /* --- shape --- */
  --r-xs: 8px; --r-sm: 12px; --r-md: 16px; --r-lg: 20px; --r-xl: 26px; --r-pill: 999px;

  /* --- motion --- */
  --spring: cubic-bezier(.32,.72,0,1);
  --ease:   cubic-bezier(.4,0,.2,1);

  --safe-t: env(safe-area-inset-top, 0px);
  --safe-b: env(safe-area-inset-bottom, 0px);
}

/* ---------------------------------------------------- the dark palette
   There were two of these: one in the media query for a system preference and
   one on [data-theme="dark"] for the toggle, with the values typed out twice.
   They had already drifted -- the toggle was still on the old --ink-3/--ink-4,
   so somebody who tapped the theme button got a measurably less readable app
   than somebody whose phone was already in dark mode. Nothing announced it.

   Pure CSS cannot share one declaration block between a media query and an
   attribute selector, so the VALUES live once, here, as --d-*, and the two
   selectors below only map them. The mapping is listed twice; the values
   cannot be. audit_css.mjs checks that the two lists stay identical. */
:root {
  --d-ink: #E9ECF1; --d-ink-1: #E9ECF1; --d-ink-2: #B6BEC9; --d-ink-3: #9AA6B4;
  --d-ink-4: #8D97A4; --d-ink-mute: #6D7784; --d-canvas: #0B0E12; --d-surface: #151A21;
  --d-surface-2: #1B212A; --d-surface-3: #232B35; --d-line: #252D37; --d-line-2: #323B47;
  --d-on-ink: #14181D; --d-on-ink-accent: #237D44; --d-navy: #E9ECF1; --d-on-navy: #14181D; --d-pine: #0E3A2E;
  --d-pine-2: #16513E; --d-cat-trash: #46C078; --d-cat-toilet: #6D9BE0;
  --d-cat-water: #5BBBD1; --d-cat-smoking: #DCA95A; --d-cat-trash-strong: #46C078;
  --d-cat-toilet-strong: #6D9BE0; --d-cat-water-strong: #5BBBD1;
  --d-cat-smoking-strong: #DCA95A; --d-on-cat: #14181D; --d-trust: #46C078;
  --d-trust-700: #6FD69B; --d-trust-800: #6FD69B; --d-trust-100: #15311F;
  --d-trust-50: #102419; --d-info: #7FA8CC; --d-info-100: #17222B; --d-info-700: #9CBFDC;
  --d-violet: #A296D6; --d-violet-100: #201D2B; --d-violet-700: #B6ACE1;
  --d-amber: #D08B60; --d-amber-100: #2A1E17; --d-amber-700: #DCA77F; --d-danger: #E2796E;
  --d-danger-100: #2C1715; --d-danger-700: #E2796E; --d-w-general: #47BE79;
  --d-w-general-bg: #16321F; --d-w-general-ico: #47BE79; --d-w-cup: #9AA4B2;
  --d-w-cup-bg: #212731; --d-w-pet: #6FA5DE; --d-w-pet-bg: #15212E; --d-w-can: #DCB43E;
  --d-w-can-bg: #2A2314; --d-w-can-ico: #DCB43E; --d-w-glass: #A296D6;
  --d-w-glass-bg: #201D2B; --d-w-food: #C49468; --d-w-food-bg: #271F17;
  --d-w-other: #7FA9B4; --d-w-other-bg: #172529; --d-pin-verified: #47BE79;
  --d-pin-likely: #6C7C83; --d-pin-unverified: #45545A; --d-e1: 0 1px 2px rgba(0,0,0,.4);
  --d-e2: 0 2px 6px rgba(0,0,0,.45), 0 8px 24px rgba(0,0,0,.35);
  --d-e3: 0 4px 12px rgba(0,0,0,.5), 0 16px 40px rgba(0,0,0,.45);
  --d-e-sheet: 0 -2px 10px rgba(0,0,0,.3), 0 -18px 50px rgba(0,0,0,.45);
}

/* the toggle wins in both directions; system preference is the default */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ink: var(--d-ink); --ink-1: var(--d-ink-1); --ink-2: var(--d-ink-2);
    --ink-3: var(--d-ink-3); --ink-4: var(--d-ink-4); --ink-mute: var(--d-ink-mute);
    --canvas: var(--d-canvas); --surface: var(--d-surface);
    --surface-2: var(--d-surface-2); --surface-3: var(--d-surface-3);
    --line: var(--d-line); --line-2: var(--d-line-2); --on-ink: var(--d-on-ink);
    --on-ink-accent: var(--d-on-ink-accent);
    --navy: var(--d-navy); --on-navy: var(--d-on-navy); --pine: var(--d-pine);
    --pine-2: var(--d-pine-2); --cat-trash: var(--d-cat-trash);
    --cat-toilet: var(--d-cat-toilet); --cat-water: var(--d-cat-water);
    --cat-smoking: var(--d-cat-smoking); --cat-trash-strong: var(--d-cat-trash-strong);
    --cat-toilet-strong: var(--d-cat-toilet-strong);
    --cat-water-strong: var(--d-cat-water-strong);
    --cat-smoking-strong: var(--d-cat-smoking-strong); --on-cat: var(--d-on-cat);
    --trust: var(--d-trust); --trust-700: var(--d-trust-700);
    --trust-800: var(--d-trust-800); --trust-100: var(--d-trust-100);
    --trust-50: var(--d-trust-50); --info: var(--d-info); --info-100: var(--d-info-100);
    --info-700: var(--d-info-700); --violet: var(--d-violet);
    --violet-100: var(--d-violet-100); --violet-700: var(--d-violet-700);
    --amber: var(--d-amber); --amber-100: var(--d-amber-100);
    --amber-700: var(--d-amber-700); --danger: var(--d-danger);
    --danger-100: var(--d-danger-100); --danger-700: var(--d-danger-700);
    --w-general: var(--d-w-general); --w-general-bg: var(--d-w-general-bg);
    --w-general-ico: var(--d-w-general-ico); --w-cup: var(--d-w-cup);
    --w-cup-bg: var(--d-w-cup-bg); --w-pet: var(--d-w-pet); --w-pet-bg: var(--d-w-pet-bg);
    --w-can: var(--d-w-can); --w-can-bg: var(--d-w-can-bg);
    --w-can-ico: var(--d-w-can-ico); --w-glass: var(--d-w-glass);
    --w-glass-bg: var(--d-w-glass-bg); --w-food: var(--d-w-food);
    --w-food-bg: var(--d-w-food-bg); --w-other: var(--d-w-other);
    --w-other-bg: var(--d-w-other-bg); --pin-verified: var(--d-pin-verified);
    --pin-likely: var(--d-pin-likely); --pin-unverified: var(--d-pin-unverified);
    --e1: var(--d-e1); --e2: var(--d-e2); --e3: var(--d-e3); --e-sheet: var(--d-e-sheet);
  }
}
:root[data-theme="dark"] {
  --ink: var(--d-ink); --ink-1: var(--d-ink-1); --ink-2: var(--d-ink-2);
  --ink-3: var(--d-ink-3); --ink-4: var(--d-ink-4); --ink-mute: var(--d-ink-mute);
  --canvas: var(--d-canvas); --surface: var(--d-surface); --surface-2: var(--d-surface-2);
  --surface-3: var(--d-surface-3); --line: var(--d-line); --line-2: var(--d-line-2);
  --on-ink: var(--d-on-ink); --on-ink-accent: var(--d-on-ink-accent);
  --navy: var(--d-navy); --on-navy: var(--d-on-navy);
  --pine: var(--d-pine); --pine-2: var(--d-pine-2); --cat-trash: var(--d-cat-trash);
  --cat-toilet: var(--d-cat-toilet); --cat-water: var(--d-cat-water);
  --cat-smoking: var(--d-cat-smoking); --cat-trash-strong: var(--d-cat-trash-strong);
  --cat-toilet-strong: var(--d-cat-toilet-strong);
  --cat-water-strong: var(--d-cat-water-strong);
  --cat-smoking-strong: var(--d-cat-smoking-strong); --on-cat: var(--d-on-cat);
  --trust: var(--d-trust); --trust-700: var(--d-trust-700);
  --trust-800: var(--d-trust-800); --trust-100: var(--d-trust-100);
  --trust-50: var(--d-trust-50); --info: var(--d-info); --info-100: var(--d-info-100);
  --info-700: var(--d-info-700); --violet: var(--d-violet);
  --violet-100: var(--d-violet-100); --violet-700: var(--d-violet-700);
  --amber: var(--d-amber); --amber-100: var(--d-amber-100);
  --amber-700: var(--d-amber-700); --danger: var(--d-danger);
  --danger-100: var(--d-danger-100); --danger-700: var(--d-danger-700);
  --w-general: var(--d-w-general); --w-general-bg: var(--d-w-general-bg);
  --w-general-ico: var(--d-w-general-ico); --w-cup: var(--d-w-cup);
  --w-cup-bg: var(--d-w-cup-bg); --w-pet: var(--d-w-pet); --w-pet-bg: var(--d-w-pet-bg);
  --w-can: var(--d-w-can); --w-can-bg: var(--d-w-can-bg); --w-can-ico: var(--d-w-can-ico);
  --w-glass: var(--d-w-glass); --w-glass-bg: var(--d-w-glass-bg);
  --w-food: var(--d-w-food); --w-food-bg: var(--d-w-food-bg); --w-other: var(--d-w-other);
  --w-other-bg: var(--d-w-other-bg); --pin-verified: var(--d-pin-verified);
  --pin-likely: var(--d-pin-likely); --pin-unverified: var(--d-pin-unverified);
  --e1: var(--d-e1); --e2: var(--d-e2); --e3: var(--d-e3); --e-sheet: var(--d-e-sheet);
}

/* ============================ base ============================ */
* { box-sizing: border-box; -webkit-tap-highlight-color: transparent; }

/* The browser's own [hidden] rule lives in the UA stylesheet, and ANY author
   rule beats it — so `.walkbar { display:flex }` silently defeated
   `walkbar.hidden = true` and left the walk bar pinned over the first-run card
   forever. Ten of the eighteen elements we toggle had the same defect. One
   declaration fixes the class of bug instead of the instances. */
[hidden] { display: none !important; }
html, body { height: 100%; margin: 0; }
body {
  font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI",
               "Noto Sans KR", "Noto Sans TC", "Noto Sans SC", sans-serif;
  font-size: 15px; line-height: 1.47; color: var(--ink); background: var(--canvas);
  font-feature-settings: "cv05" 1, "ss03" 1;
  -webkit-font-smoothing: antialiased;
  overscroll-behavior: none;
}
button, input { font: inherit; color: inherit; }
button { border: 0; background: none; cursor: pointer; padding: 0; }
:focus-visible { outline: 2px solid var(--geo); outline-offset: 3px; border-radius: 4px; }
.num { font-variant-numeric: tabular-nums; letter-spacing: -.01em; }
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
.i  { width: 20px; height: 20px; stroke-width: 1.8; flex: none; }
.i-sm { width: 16px; height: 16px; stroke-width: 2; flex: none; }
.i-lg { width: 24px; height: 24px; stroke-width: 1.7; flex: none; }
@media (prefers-reduced-motion: reduce) { * { transition: none !important; animation: none !important; } }

/* ============================ map ============================ */
#map { position: fixed; inset: 0; background: var(--canvas); }
.maplibregl-ctrl-attrib {
  font: 400 10px/14px Inter, sans-serif !important;
  background: color-mix(in srgb, var(--surface) 88%, transparent) !important;
  color: var(--ink-4) !important; border-radius: var(--r-xs) 0 0 0 !important;
}
.maplibregl-ctrl-attrib a { color: var(--ink-3) !important; }
.maplibregl-ctrl-bottom-right { z-index: 1; }

/* ============================ top chrome ============================ */
.top {
  position: fixed; top: calc(10px + var(--safe-t)); left: 0; right: 0; z-index: 20;
  pointer-events: none;
}
.top > * { pointer-events: auto; }

.searchbar {
  display: flex; align-items: center; gap: 8px; margin: 0 14px;
  height: 52px; padding: 0 6px 0 16px;
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  backdrop-filter: saturate(1.6) blur(18px);
  -webkit-backdrop-filter: saturate(1.6) blur(18px);
  border: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
  border-radius: var(--r-pill); box-shadow: var(--e2);
}
.searchbar input {
  flex: 1; min-width: 0; align-self: stretch; border: 0; outline: 0; background: none;
  font-size: 16px; font-weight: 450; color: var(--ink);   /* 16px: stops iOS zooming */
}
.searchbar input::placeholder { color: var(--ink-mute); font-weight: 450; }
.searchbar .lead { color: var(--ink-3); }
.icon-btn {
  width: 44px; height: 44px; border-radius: var(--r-pill);
  display: grid; place-items: center; color: var(--ink-2); flex: none;
  transition: background .16s var(--ease), color .16s var(--ease);
}
.icon-btn:hover { background: var(--surface-3); }
.icon-btn[data-on="true"] { background: var(--ink); color: var(--on-ink); }
.icon-btn .code { font-weight: 600; font-size: 12px; line-height: 1; letter-spacing: .04em; }
.icon-btn.wide { width: auto; min-width: 60px; height: 44px; padding: 0 12px; gap: 5px;
                 display: flex; align-items: center; justify-content: center; }
.icon-btn.wide .i-sm { color: var(--ink-4); }

/* Decision 9: categories are a switch, never parallel tabs, and never mixed
   on one map. Colour carries the category: green / indigo / cyan / amber. */
.cats { display: flex; gap: 7px; overflow-x: auto; padding: 10px 14px 0; scrollbar-width: none; }
.cats::-webkit-scrollbar { display: none; }
.cat {
  flex: none; display: flex; align-items: center; gap: 7px;
  height: 44px; padding: 0 16px; border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--surface) 92%, transparent);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
  color: var(--ink-2); font-size: 14px; font-weight: 600; letter-spacing: -.008em;
  box-shadow: var(--e1);
  transition: background .18s var(--ease), color .18s var(--ease), border-color .18s var(--ease);
}
.cat .i-sm { color: var(--cat, var(--trust)); }
.cat[aria-selected="true"] {
  background: var(--cat-strong, var(--cat, var(--trust))); color: var(--on-cat);
  border-color: var(--cat-strong, var(--cat, var(--trust)));
}
.cat[aria-selected="true"] .i-sm { color: var(--on-cat); }
.cat b { font-variant-numeric: tabular-nums; font-weight: 600; opacity: .6; font-size: 12px; }
.cat[aria-selected="true"] b { opacity: .8; }

.chips {
  display: flex; gap: 7px; overflow-x: auto; padding: 10px 14px 2px;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.chips::-webkit-scrollbar { display: none; }
.chip {
  flex: none; display: flex; align-items: center; gap: 6px;
  height: 44px; padding: 0 15px; border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--surface) 90%, transparent);
  backdrop-filter: blur(14px); -webkit-backdrop-filter: blur(14px);
  border: 1px solid color-mix(in srgb, var(--line) 70%, transparent);
  color: var(--ink-2); font-size: 13px; font-weight: 550; letter-spacing: -.005em;
  box-shadow: var(--e1);
  transition: background .18s var(--ease), color .18s var(--ease),
              border-color .18s var(--ease), transform .18s var(--spring);
}
.chip:active { transform: scale(.96); }
.chip[aria-pressed="true"] {
  background: var(--navy); color: var(--on-navy); border-color: var(--navy);
}
.chip .i-sm { opacity: .75; }

/* ============================ floating controls ============================ */
.controls {
  /* This said `bottom: calc(22px + var(--safe-b))` and the tab bar is 58px tall
     at z-index 35 with an opaque background, so the lower control -- "my
     location", the one thing this app is actually about -- had 36 of its 46
     pixels painted over and could not be tapped there.
     sheetState() in app.js already knew this and moved the stack to 73px, with
     the same arithmetic in a comment. But it only runs once the sheet changes
     state, and on a normal launch that happens AFTER `await ensurePrefs()` --
     a network round trip. So on a slow connection the buttons sat under the tab
     bar for as long as the data took, then jumped. That is the 「たまにバグが
     あります」: it was never a rare bug, it was the first second of every load.
     The starting value clears the bar too now. .askbar and .walkbar were
     already at 70px; this was the one that was missed. */
  position: fixed; right: 14px; bottom: calc(73px + var(--safe-b)); z-index: 20;
  display: flex; flex-direction: column; gap: 8px;
  transition: bottom .42s var(--spring), opacity .22s var(--ease);
}
.controls[data-hidden="true"] { opacity: 0; pointer-events: none; }
/* No tab bar at this width (see .tabbar below), so nothing to clear. */
@media (min-width: 920px) {
  .controls { bottom: calc(22px + var(--safe-b)); }
}
.ctrl {
  width: 46px; height: 46px; border-radius: var(--r-pill);
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  backdrop-filter: blur(18px); -webkit-backdrop-filter: blur(18px);
  border: 1px solid color-mix(in srgb, var(--line) 60%, transparent);
  box-shadow: var(--e2); color: var(--ink); display: grid; place-items: center;
  transition: transform .2s var(--spring), background .18s var(--ease), color .18s var(--ease);
}
.ctrl:active { transform: scale(.92); }
.ctrl[data-on="true"] { background: var(--ink); color: var(--on-ink); border-color: var(--ink); }

/* the permission ask, as a line under a live map -- never a wall (decision 15) */
.askbar {
  position: fixed; left: 12px; right: 12px; bottom: calc(70px + var(--safe-b));
  z-index: 33; display: flex; align-items: center; gap: 12px;
  padding: 13px 13px 13px 16px; border-radius: var(--r-lg);
  background: var(--surface); border: 1px solid var(--line); box-shadow: var(--e3);
}
.askbar-body { min-width: 0; flex: 1; }
.askbar-body b { display: block; font-size: 14.5px; font-weight: 640; letter-spacing: -.01em; }
.askbar-body span { display: block; font-size: 12px; color: var(--ink-3); margin-top: 2px; }
.ask-go {
  min-height: 44px; padding: 0 16px; border-radius: var(--r-pill); flex: none;
  background: var(--navy); color: var(--on-navy); font-size: 14px; font-weight: 620;
  display: flex; align-items: center; gap: 7px;
}
.ask-x { width: 44px; height: 44px; flex: none; color: var(--ink-4); display: grid; place-items: center; }
.note.toilet { margin-top: 10px; background: var(--info-100); border-color: color-mix(in srgb, var(--info) 24%, transparent); color: var(--info-700); }
.note.toilet .i-sm { color: var(--info-700); }
.check.shut, .check.shut .i-sm { color: var(--danger); }
.list-count em { font-style: normal; color: var(--trust); font-weight: 700; }

/* ============================ add a spot ============================ */
.placer { position: fixed; inset: 0; z-index: 34; pointer-events: none; }
.crosshair {
  position: absolute; left: 50%; top: 50%; width: 60px; height: 60px;
  transform: translate(-50%, -50%);
}
.crosshair span {
  position: absolute; background: var(--navy); opacity: .8;
}
.crosshair span:first-child { left: 50%; top: 0; width: 2px; height: 60px; margin-left: -1px; }
.crosshair span:nth-child(2) { top: 50%; left: 0; height: 2px; width: 60px; margin-top: -1px; }
.crosshair b {
  position: absolute; left: 50%; top: 50%; width: 16px; height: 16px;
  margin: -8px 0 0 -8px; border-radius: 50%;
  border: 3px solid var(--navy); background: color-mix(in srgb, var(--surface) 70%, transparent);
}
.place-bar {
  position: absolute; left: 12px; right: 12px; bottom: calc(70px + var(--safe-b));
  background: var(--surface); border: 1px solid var(--line); border-radius: var(--r-lg);
  box-shadow: var(--e3); padding: 16px; pointer-events: auto;
}
.place-bar p { margin: 0 0 12px; font-size: 14px; color: var(--ink-2); }
.place-acts { display: flex; gap: 9px; align-items: center; }
.place-acts .ghost { flex: 0 0 auto; width: auto; padding: 0 18px; margin: 0; min-height: 44px;
}
.place-acts .cta { flex: 1; height: 48px; }

/* The photo, on the map. ChargeSPOT's idea: you are looking for a physical
   object, so show the object rather than a badge saying a picture exists. */
.pin-thumb {
  position: absolute; left: 50%; top: 2px; transform: translateX(-50%);
  width: 22px; height: 22px; border-radius: 50%;
  background: var(--surface-3) center/cover no-repeat;
  box-shadow: 0 0 0 2px var(--surface);
  pointer-events: none;
}
.pin-thumb.dot {
  /* whole pixels: a 2.4 px ring straddles a pixel boundary and softens */
  top: 50%; transform: translate(-50%, -50%);
  width: 18px; height: 18px; box-shadow: 0 0 0 2px var(--surface);
}
.pin[data-photo="true"] .pin-cam { display: none; }   /* the picture replaces the badge */

/* The counted hole, above the list. Reads as a task, not an error. */
.gaprow {
  display: flex; align-items: center; gap: 9px; width: 100%; text-align: left;
  margin: 10px 0 6px; padding: 11px 13px; border-radius: var(--r-sm);
  background: var(--surface-2); border: 1px dashed var(--line-2); min-height: 44px;
}
.gaprow[data-on="true"] {
  border-style: solid;
  border-color: color-mix(in srgb, var(--trust) 40%, transparent);
  background: color-mix(in srgb, var(--trust) 8%, var(--surface));
}
.gaprow .i-sm { color: var(--ink-4); flex: none; }
.gaprow[data-on="true"] .i-sm { color: var(--trust); }
.gaprow-t { flex: 1; min-width: 0; font-size: 13px; line-height: 1.45; color: var(--ink-2); }
.gaprow-cta { flex: none; font-size: 13px; font-weight: 640; color: var(--trust); }

/* A record whose coordinates are the station or the site, not the bin. Two
   importers wrote positionPrecision and nothing read it, so these looked as
   precise as a surveyed pin. */
.coarse, .units {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 8px 0 0; padding: 10px 12px; border-radius: var(--r-sm);
  background: var(--surface-2); border: 1px solid var(--line);
  font-size: 12px; line-height: 1.5; color: var(--ink-3);
}
.coarse .i-sm, .units .i-sm { flex: none; margin-top: 1px; color: var(--ink-4); }
.units { color: var(--ink-2); }

/* The walk is longer than it looks. A number that is wrong towards "closer
   than it is" is the worst kind on this map, because the reader walks. */
.detour {
  font-style: normal; font-size: 10px; font-weight: 700; letter-spacing: .04em;
  color: var(--pin-restricted); margin-left: 5px; white-space: nowrap;
}
.detourbox {
  display: flex; align-items: flex-start; gap: 8px;
  margin: 8px 0 0; padding: 10px 12px; border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--pin-restricted) 9%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--pin-restricted) 30%, transparent);
  font-size: 12.5px; line-height: 1.5; color: var(--ink-1);
}
.detourbox .i-sm { flex: none; margin-top: 1px; color: var(--pin-restricted); }

/* ============================ your own record ============================
   Sauna Ikitai's サ活 is a diary, not a review, and ten million of them exist
   because people keep diaries. This app already stored the diary in
   localStorage and never showed it back -- the menu said "12 points confirmed",
   which is a score, not a record you can look through. */
.mylog-box {
  display: block; padding: 15px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line);
}
.mylog-head { display: flex; align-items: center; gap: 8px; font-size: 14px; font-weight: 660; color: var(--ink-1); }
.mylog-head .i-sm { color: var(--trust); }
.mylog-n {
  margin-left: auto; font-size: 13px; font-weight: 700; color: var(--ink-2);
  background: var(--surface-3); padding: 2px 9px; border-radius: 999px;
}
.mylog-sub { margin: 8px 0 0; font-size: 12px; line-height: 1.5; color: var(--ink-3); }
.mylog-sub.dim { color: var(--ink-4); }
.mylog { list-style: none; margin: 10px 0 0; padding: 0; }
.mylog li + li { border-top: 1px solid var(--line); }
.mylog-row {
  display: flex; align-items: center; gap: 10px; width: 100%; text-align: left;
  padding: 11px 2px; background: none; min-height: 44px;
}
.mylog-row .i-sm.ok { color: var(--trust); }
.mylog-row .i-sm.no { color: var(--danger); }
.mylog-row .i-sm.dimmed { color: var(--ink-4); }
.mylog-body { flex: 1; min-width: 0; }
.mylog-name {
  display: block; font-size: 13.5px; font-weight: 560; color: var(--ink-1);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mylog-where {
  display: block; margin-top: 2px; font-size: 11.5px; color: var(--ink-3);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.mylog-at { flex: none; font-size: 11.5px; color: var(--ink-4); }
.mylog-empty {
  display: flex; align-items: flex-start; gap: 9px;
  font-size: 12.5px; line-height: 1.55; color: var(--ink-3);
}
.mylog-empty .i-sm { flex: none; margin-top: 1px; color: var(--ink-4); }

/* "312 people have used this. Last one three days ago." -- the pair Sauna
   Ikitai puts at the top of every facility, in place of a star rating. */
.usedline {
  display: flex; align-items: center; gap: 8px;
  margin: 8px 0 0; padding: 9px 12px; border-radius: var(--r-sm);
  background: var(--surface-2); border: 1px solid var(--line);
  font-size: 12.5px; color: var(--ink-2);
}
.usedline .i-sm { flex: none; color: var(--ink-4); }
.usedline[data-hot="true"] {
  background: color-mix(in srgb, var(--trust) 8%, var(--surface));
  border-color: color-mix(in srgb, var(--trust) 28%, transparent);
  color: var(--ink-1);
}
.usedline[data-hot="true"] .i-sm { color: var(--trust); }

/* ============================ where exactly ============================
   The most important line on the card, so it looks like it. Sits directly
   under the title, above the distance and the access rule, because "where is
   it" is the question and everything else is a follow-up. */
.where {
  display: flex; align-items: flex-start; gap: 9px; width: 100%; text-align: left;
  margin: 10px 0 4px; padding: 12px 13px; border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--trust) 7%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--trust) 26%, transparent);
}
.where .i-sm { color: var(--trust); flex: none; margin-top: 1px; }
.where-t { flex: 1; min-width: 0; font-size: 14.5px; line-height: 1.5; font-weight: 560; color: var(--ink-1); }
.where-tr, .note-trmark {
  flex: none; font-size: 10px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--ink-4);
  padding: 2px 6px; border-radius: 999px; background: var(--surface-3);
}
.note-orig { display: block; margin-top: 4px; font-size: 11.5px; color: var(--ink-4); }
/* The hole. A blank space gets skipped; a labelled hole with a button gets
   filled. Deliberately not styled as an error -- it is an invitation. */
.where-empty {
  background: var(--surface-2);
  border: 1px dashed var(--line-2);
  align-items: center;
}
.where-empty .i-sm { color: var(--ink-4); margin-top: 0; }
.where-empty .where-t { font-weight: 480; color: var(--ink-3); font-size: 13.5px; }
.where-cta { flex: none; font-size: 13px; font-weight: 640; color: var(--trust-800); }

/* Not for you, and why. Shown on household collection points and cemetery
   flower drops, which are real places and not answers to "where can I throw
   this away". */
.notvisitor {
  display: flex; align-items: flex-start; gap: 9px;
  margin: 10px 0 4px; padding: 12px 13px; border-radius: var(--r-sm);
  background: var(--danger-100);
  border: 1px solid color-mix(in srgb, var(--danger) 28%, transparent);
  font-size: 12.5px; line-height: 1.5; color: var(--danger);
}
.notvisitor .i-sm { flex: none; margin-top: 1px; }

/* ============================ stars ============================
   Three levels. The caption spells out what the level means, because a bare
   star count invites everyone to pick the top one. */
.stars { display: flex; align-items: center; gap: 4px; margin: 2px 0 6px; flex-wrap: wrap; }
.star {
  width: 44px; height: 44px; display: grid; place-items: center;
  color: var(--pin-restricted); border-radius: var(--r-sm);
  transition: transform .14s var(--spring);
}
.star:active { transform: scale(.9); }
.star svg { width: 26px; height: 26px; }
.star[data-on="false"] { color: var(--ink-mute); }
.star-cap { margin-left: 6px; font-size: 13px; font-weight: 560; color: var(--ink-2); }

/* required vs optional, said once and clearly */
.label.lead { font-size: 13.5px; font-weight: 680; color: var(--ink-1); margin-top: 4px; }
#new-where, #where-text {
  width: 100%; font: inherit; font-size: 15px; padding: 13px 14px;
  border-radius: var(--r-sm); border: 1px solid var(--line-2);
  background: var(--surface); color: var(--ink-1); margin: 6px 0 0;
}
#new-where:focus, #where-text:focus {
  outline: none; border-color: var(--trust);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--trust) 18%, transparent);
}
/* `.hint` alone is (0,1,0) and `.modal p` at the bottom of this file is
   (0,1,1), so inside a modal -- which is the only place a hint appears -- the
   container won: the hint rendered at 14.5px with `margin: 0 0 20px`, the same
   weight as body copy, when the whole point of a hint is to be quieter than the
   field it explains. Found by audit_dom.mjs on its first run; invisible in a
   screenshot because the field it sits under is collapsed by default. */
.hint, .modal .hint {
  margin: 7px 0 14px; font-size: 12px; line-height: 1.5; color: var(--ink-3);
}
.opt-mark, .opt-toggle {
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  width: 100%; margin: 14px 0 4px; font-size: 12px; font-weight: 620;
  letter-spacing: .04em; text-transform: uppercase; color: var(--ink-4);
}
.opt-toggle {
  min-height: 44px;
  padding: 12px 0; border-top: 1px solid var(--line); text-transform: none;
  letter-spacing: 0; font-size: 13.5px; color: var(--ink-2);
}
.opt-toggle[aria-expanded="true"] .i-sm { transform: rotate(180deg); }
.opt-toggle .i-sm { transition: transform .2s var(--ease); }

/* One quoted note, above the fold. Reads as a quote, taps like a link. */
.topnote {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  margin-top: 10px; padding: 10px 12px; border-radius: var(--r-sm);
  background: var(--surface-2); border: 1px solid var(--line);
  transition: background .16s var(--ease); min-height: 44px;
}
.topnote:hover { background: var(--surface-3); }
.topnote .i-sm { color: var(--ink-4); flex: none; }
.topnote-t {
  flex: 1; min-width: 0; font-size: 13px; line-height: 1.45; color: var(--ink-2);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.topnote-n {
  flex: none; font-size: 11px; font-weight: 700; color: var(--ink-4);
  padding: 2px 7px; border-radius: 999px; background: var(--surface-3);
}

/* ============================ your own point ============================
   Review state, said out loud. A contributor who cannot tell "on my phone"
   from "on the map" either assumes the wrong one and stops contributing, or
   reports a bug that is not one. */
.mine-panel {
  margin-top: 22px; padding: 15px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line);
}
.mine-panel[data-st="live"] { border-color: color-mix(in srgb, var(--trust) 34%, var(--line)); }
.mine-head {
  display: flex; align-items: center; gap: 8px; font-size: 13.5px; font-weight: 640;
  color: var(--ink-1);
}
.mine-panel[data-st="live"] .mine-head .i-sm { color: var(--trust); }
.mine-panel[data-st="review"] .mine-head .i-sm { color: var(--pin-restricted); }
.mine-panel[data-st="offline"] .mine-head .i-sm { color: var(--ink-3); }
.mine-panel p { margin: 8px 0 12px; font-size: 12.5px; line-height: 1.55; color: var(--ink-2); }
.mine-panel .cta-2 { width: 100%; height: 44px; }

/* ============================ standings ============================
   The counter is the point of return. It has to be legible at a glance and
   honest about what it can and cannot count -- there are no accounts here. */
.rank-row {
  display: block; padding: 15px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line);
}
/* The three numbers were `display: flex; gap: 18px` with the label inline and
   baseline-aligned, which rendered as one run of text: "12 登録 5 写真 38 確認".
   Three separate facts have to look like three. Number over label, a rule
   between them, equal thirds. */
.rank-nums {
  display: grid; grid-template-columns: repeat(3, 1fr);
  margin: 14px 0 2px; border-radius: var(--r-sm);
  background: var(--surface); border: 1px solid var(--line);
  overflow: hidden;
}
.rank-nums span {
  display: flex; flex-direction: column; align-items: center; gap: 2px;
  padding: 11px 6px; font-size: 11px; font-weight: 600; color: var(--ink-4);
  letter-spacing: .01em;
}
.rank-nums span + span { border-left: 1px solid var(--line); }
.rank-nums b { font-size: 24px; font-weight: 700; color: var(--ink-1); line-height: 1.1; }
/* The one number nobody can make alone leads. */
.rank-nums span.lead b { color: var(--trust-800); }
.rank-nums span.lead { color: var(--ink-3); }

/* Above the rank, because it matters more than the rank. A count of people,
   not a position. */
.rank-helped {
  display: flex; align-items: flex-start; gap: 8px; margin: -2px 0 10px;
  padding: 11px 12px; border-radius: var(--r-sm);
  background: var(--trust-50);
  border: 1px solid color-mix(in srgb, var(--trust) 24%, transparent);
  font-size: 14px; font-weight: 640; line-height: 1.45; color: var(--trust-800);
}
.rank-helped .i-sm { flex: none; margin-top: 2px; }

.rank-sub { margin: 8px 0 0; font-size: 12.5px; color: var(--ink-2); }

/* ============================ keeping the record ============================
   No account, and WebKit deletes a site's storage after seven days without an
   interaction. Saying so was already in the menu as one grey line; this is the
   same sentence with the two answers attached. */
.keep-row {
  display: block; margin-top: 10px; padding: 15px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line);
}
.keep-head {
  display: flex; align-items: center; gap: 8px; margin-bottom: 6px;
  font-size: 14px; font-weight: 660; color: var(--ink-1);
}
.keep-head .i-sm { color: var(--trust-800); flex: none; }
.keep-row p { margin: 0; font-size: 12.5px; line-height: 1.55; color: var(--ink-3); }
.keep-sub { margin: 6px 0 0 !important; font-size: 11.5px !important; }

/* The one action that stops the deletion happening, so it leads. */
/* `.keep-row .keep-cta`, not `.keep-cta`, and the extra class is load-bearing.
   This block sits inside #you-list, which carries .stack -- and `.stack button`
   is (0,1,1) to a bare `.keep-cta`'s (0,1,0), so the menu's row styling won
   outright: justify-content: space-between pushed the title away from its own
   subtitle, and align-items: center floated the icon down beside the second
   line. It looked like my flex-start had no effect; it was never applied. */
.keep-row .keep-cta {
  display: flex; align-items: flex-start; gap: 11px; width: 100%; text-align: left;
  justify-content: flex-start;
  min-height: 56px; margin: 12px 0 4px; padding: 11px 13px;
  border-radius: var(--r-md); background: var(--surface);
  border: 1px solid color-mix(in srgb, var(--trust) 34%, var(--line));
  color: var(--ink-1); font-size: 13.5px; transition: background .16s var(--ease);
}
.keep-row .keep-cta:active { background: var(--surface-3); }
.keep-row .keep-cta .i-sm { flex: none; color: var(--trust-800); margin-top: 2px; }
.keep-row .keep-cta b { display: block; font-weight: 660; }
/* Two more from the same family. `.stack button > span` sets
   `display: flex; flex-wrap: wrap` so the wrapper laid its own children out as
   flex items, and `.stack button > span > .sub` adds `margin-left: 26px` to
   clear the icon in a menu row -- where the sub sits beside the title, not
   under it. Here the icon is outside the wrapper, so that 26px was pure
   indent. Measured in the browser rather than guessed: the b was at x=77 and
   the sub at x=103. */
.keep-row .keep-cta > span { display: block; flex: 1 1 auto; min-width: 0; }
.keep-row .keep-cta > span > .sub {
  display: block; margin: 2px 0 0; flex: none;
  font-size: 11.5px; color: var(--ink-3);
}

/* The label sat beside the code on one row, so a code long enough to wrap left
   it centred against two lines with the button pushed off to the side. Label
   above, code and button on their own row, and the code is allowed to be as
   long as it needs -- it must never be shortened, because the whole point is
   that it round-trips exactly (code_test.mjs). */
.keep-code {
  display: grid; grid-template-columns: 1fr auto; align-items: center;
  gap: 4px 9px; margin-top: 14px; padding: 10px 12px 11px;
  border-radius: var(--r-sm);
  background: var(--surface); border: 1px dashed var(--line-2);
}
.keep-code-l {
  grid-column: 1 / -1;
  font-size: 10.5px; font-weight: 700; letter-spacing: .05em;
  text-transform: uppercase; color: var(--ink-4);
}
.keep-code code {
  min-width: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 13px; font-weight: 600; color: var(--ink-1);
  letter-spacing: .01em; line-height: 1.5; word-break: break-all; user-select: all;
}
.keep-restore { display: flex; gap: 8px; margin-top: 10px; }
.keep-restore input {
  flex: 1; min-width: 0; min-height: 44px; padding: 0 12px;
  border-radius: var(--r-sm); background: var(--surface);
  border: 1px solid var(--line-2); color: var(--ink);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13.5px;
}
.keep-restore input::placeholder { color: var(--ink-mute); }
.keep-files { display: flex; gap: 8px; margin-top: 12px; flex-wrap: wrap; }
.keep-files .tag, .keep-restore .tag, .keep-code .tag {
  display: inline-flex; align-items: center; gap: 6px;
  min-height: 44px; padding: 0 13px; border-radius: var(--r-sm);
  background: var(--surface); border: 1px solid var(--line-2);
  color: var(--ink-2); font-size: 12.5px; font-weight: 600; cursor: pointer;
}
.keep-files .tag:active, .keep-restore .tag:active { background: var(--surface-3); }

/* ============================ the ask ============================
   Placed under the counter on purpose: somebody who has just seen their own
   number is the one person most likely to bring a second contributor. */
.invite-row {
  display: block; padding: 15px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line);
}
.invite-head {
  display: flex; align-items: center; gap: 8px;
  font-size: 14px; font-weight: 660; color: var(--ink-1);
}
.invite-head .i-sm { color: var(--trust); }
.invite-row p { margin: 9px 0 13px; font-size: 12.5px; line-height: 1.6; color: var(--ink-2); }
.invite-acts { display: flex; gap: 9px; }
.invite-acts .cta-2 { flex: 1; height: 44px; margin-top: 0; font-size: 13.5px; }

/* Placing below the usable zoom: the crosshair is drawn muted so it reads as
   "not yet" rather than "broken", and the confirm button is out of reach. */
.placer[data-coarse="true"] .crosshair { opacity: .4; }
.place-bar .cta:disabled { opacity: .45; }

/* ============================ walk mode (board §4) ============================
   Decision 8: walking 220 m does not need turn-by-turn. A direction, a distance
   and a landmark are enough -- and they keep the person inside the product, so
   the verification loop can actually close. */
.walkbar {
  position: fixed; left: 12px; right: 12px; z-index: 36;
  bottom: calc(70px + var(--safe-b));
  display: flex; align-items: center; gap: 12px;
  padding: 12px 12px 12px 14px; border-radius: var(--r-lg);
  background: var(--navy); color: var(--on-navy); box-shadow: var(--e3);
}
.walk-x {
  position: absolute; top: -14px; right: -8px; width: 44px; height: 44px;
  border-radius: 50%; color: var(--ink-3);
  display: grid; place-items: center;
}
.walk-x::before {
  content: ""; position: absolute; width: 30px; height: 30px; border-radius: 50%;
  background: var(--surface); box-shadow: var(--e2); z-index: -1;
}
.walk-arrow {
  width: 44px; height: 44px; flex: none; border-radius: 50%;
  background: color-mix(in srgb, var(--on-navy) 16%, transparent);
  display: grid; place-items: center; transition: transform .5s var(--spring);
}
.walk-body { min-width: 0; flex: 1; display: flex; flex-direction: column; gap: 1px; }
.walk-body b { font-size: 19px; font-weight: 660; letter-spacing: -.02em; }
.walk-body span {
  font-size: 12px; opacity: .82; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.walk-mark { font-weight: 600; opacity: .95 !important; }
.walk-acts { display: flex; align-items: center; gap: 8px; flex: none; }
.walk-go {
  min-height: 44px; padding: 0 16px; border-radius: var(--r-pill);
  background: var(--on-navy); color: var(--navy); font-size: 14px; font-weight: 640;
}
.walk-alt {
  width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center;
  background: color-mix(in srgb, var(--on-navy) 16%, transparent); color: var(--on-navy);
}

/* ============================ bottom tab bar ============================ */
.tabbar {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 35;
  display: flex; align-items: stretch;
  padding-bottom: var(--safe-b);
  background: color-mix(in srgb, var(--surface) 94%, transparent);
  backdrop-filter: saturate(1.5) blur(18px);
  -webkit-backdrop-filter: saturate(1.5) blur(18px);
  border-top: 1px solid var(--line);
}
.tab {
  flex: 1; min-height: 58px; display: flex; flex-direction: column;
  align-items: center; justify-content: center; gap: 4px;
  color: var(--ink-4); font-size: 10.5px; font-weight: 600; letter-spacing: .01em;
  transition: color .16s var(--ease);
}
.tab .i { width: 22px; height: 22px; stroke-width: 1.9; }
/* --trust on the tab bar's near-white ground is 3.09:1 at 10.5px -- the
   smallest text in the app, below AA, and audit_contrast.py was skipping the
   pair in silence because the bar's background is a color-mix against
   transparent. Same fix as every other place this colour carries words. */
.tab[data-on="true"] { color: var(--trust-800); }
@media (min-width: 920px) { .tabbar { display: none; } }

/* ============================ bottom sheet ============================
   Nothing in the sheet is selectable except the sentences somebody wrote. A
   drag to scroll was landing on the <h1> and selecting the title instead --
   the card then sat there highlighted with two selection handles on it and
   would not move. Notes stay selectable: a traveller may well want to copy
   "中央2改札入って右側トイレ前にある" into a translator. */
.sheet, .sheet * {
  -webkit-user-select: none; user-select: none; -webkit-touch-callout: none;
}
.sheet .notes, .sheet .notes *, .sheet .topnote-t, .sheet .note-body,
.sheet input, .sheet textarea {
  -webkit-user-select: text; user-select: text;
}
.sheet {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  height: 88vh; max-height: 88dvh;
  background: var(--surface);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
  box-shadow: var(--e-sheet);
  border-top: 1px solid color-mix(in srgb, var(--line) 80%, transparent);
  display: flex; flex-direction: column;
  transform: translateY(100%);
  transition: transform .46s var(--spring);
  touch-action: none;
}
/* The sheet is 88vh tall and slid DOWN to show less of itself. Whatever is
   translated past the bottom of the screen is still inside the scroll
   container, so scrolling to the end of .sheet-body left the last stretch of
   the card below the edge of the phone -- "下まで下がれない見れない". The card
   simply had no bottom in peek or half.
   --hidden is how far off-screen this state pushes the sheet; .sheet-body adds
   exactly that much bottom padding, so the end of the content can always reach
   the middle of the screen. */
.sheet[data-state="hidden"]   { --hidden: 88vh; }
.sheet[data-state="peek"]     { transform: translateY(calc(88vh - 172px)); --hidden: calc(88vh - 172px); }
.sheet[data-state="half"]     { transform: translateY(calc(88vh - 52vh)); --hidden: 36vh; }
.sheet[data-state="full"]     { transform: translateY(0); --hidden: 0px; }
.sheet.dragging { transition: none; }
.grab { padding: 9px 0 4px; display: grid; place-items: center; flex: none; cursor: grab; }
.grab span {
  width: 38px; height: 4px; border-radius: 2px; background: var(--line-2);
  transition: width .2s var(--ease);
}
.sheet[data-state="full"] .grab span { width: 26px; }
.sheet-body {
  overflow-y: auto; overscroll-behavior: contain; flex: 1;
  padding: 2px 18px calc(96px + var(--safe-b) + var(--hidden, 0px));
  touch-action: pan-y;
}
.sheet-body::-webkit-scrollbar { width: 0; }

/* ============================ photos ============================ */
/* A photo is evidence with a date on it, so the date is never optional. */
.shots {
  position: relative; display: grid; gap: 6px; margin: 4px 0 18px;
  grid-template-columns: repeat(3, 1fr);
}
.shot { margin: 0; position: relative; border-radius: var(--r-sm); overflow: hidden;
        background: var(--surface-3); aspect-ratio: 1; }
.shot.hero { grid-column: span 3; aspect-ratio: 16 / 10; border-radius: var(--r-md); }
.shot img { width: 100%; height: 100%; object-fit: cover; display: block; }
.shot figcaption {
  position: absolute; left: 8px; bottom: 8px;
  background: color-mix(in srgb, var(--ink) 74%, transparent); color: #fff;
  font: 600 10.5px/1 Inter, sans-serif; padding: 4px 7px; border-radius: 5px;
  backdrop-filter: blur(6px);
}
.shot:not(.hero) figcaption { display: none; }
.shot-empty {
  width: 100%; display: flex; align-items: center; gap: 11px; text-align: left;
  margin: 4px 0 18px; padding: 14px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px dashed var(--line-2);
  color: var(--ink-3); font-size: 13px; line-height: 1.45;
}
.shot-empty .i { color: var(--ink-4); flex: none; }
.shot-close {
  position: absolute; top: 10px; right: 10px; z-index: 2;
  width: 44px; height: 44px; border-radius: 50%;
  background: color-mix(in srgb, var(--ink) 55%, transparent); color: #fff;
  display: grid; place-items: center; backdrop-filter: blur(8px);
}
.shot-walk {
  position: absolute; top: 10px; left: 10px; z-index: 2;
  background: color-mix(in srgb, var(--surface) 92%, transparent); color: var(--ink);
  border-radius: var(--r-pill); padding: 6px 12px; backdrop-filter: blur(8px);
  font-size: 13px; font-weight: 640; box-shadow: var(--e1);
}

/* ============================ notes ============================ */
.cnt { color: var(--ink-4); font-weight: 600; }
.tagrow { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.tagpill {
  font-size: 12px; font-weight: 560; padding: 6px 11px; border-radius: var(--r-pill);
  background: var(--surface-3); color: var(--ink-2);
}
.tagpill b { color: var(--ink-4); font-weight: 700; margin-left: 3px; }
.notes { list-style: none; margin: 0; padding: 0; }
.notes li { padding: 11px 0; border-top: 1px solid var(--line); }
.notes li:first-child { border-top: 0; }
.note-text { display: block; font-size: 14px; color: var(--ink); }
.note-meta { display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
  font-size: 11.5px; color: var(--ink-4); margin-top: 5px; }
.note-lang {
  font-size: 9.5px; font-weight: 700; letter-spacing: .06em; padding: 2px 5px;
  border-radius: 4px; background: var(--surface-3); color: var(--ink-3);
}
.note-tr { color: var(--info); font-weight: 620; text-decoration: none; }
.note-tr:hover { text-decoration: underline; }
.note-tags { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.tagpill.xs { font-size: 10.5px; padding: 3px 8px; }
.note-sep {
  font-size: 10.5px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--ink-4); padding: 14px 0 6px; border-top: 1px solid var(--line);
}
.notes-none { margin: 0; font-size: 13.5px; color: var(--ink-4); }
.contrib-row { display: flex; gap: 9px; margin-top: 16px; }
.cta-2 {
  height: 48px; border-radius: var(--r-md); background: var(--surface-2);
  border: 1px solid var(--line); color: var(--ink-2);
  font-size: 14.5px; font-weight: 580;
  display: flex; align-items: center; justify-content: center; gap: 8px;
  transition: background .16s var(--ease), transform .16s var(--spring);
}
.cta-2:active { transform: scale(.98); }
.cta-2:hover { background: var(--surface-3); }
.contrib-row .cta-2 { flex: 1; margin-top: 0; }

/* ============================ contribution card ============================ */
.mini-q {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 12px 14px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line);
  font-size: 13.5px; color: var(--ink-2); margin-bottom: 12px;
}
.mini-btns { display: flex; gap: 6px; margin-left: auto; }
.mini {
  min-height: 44px; padding: 0 18px; border-radius: var(--r-pill);
  font-size: 14px; font-weight: 580;
  background: var(--surface); border: 1px solid var(--line-2); color: var(--ink-2);
}
.mini[data-on="true"] { background: var(--navy); color: var(--on-navy); border-color: var(--navy); }

.photo-drop {
  display: flex; flex-direction: column; gap: 8px; align-items: center; text-align: center;
  padding: 22px 18px; border-radius: var(--r-md);
  background: var(--trust-50); border: 1px dashed color-mix(in srgb, var(--trust) 40%, transparent);
  cursor: pointer; transition: background .16s var(--ease);
}
.photo-drop:hover { background: var(--trust-100); }
.photo-cta { display: flex; align-items: center; gap: 10px; color: var(--trust); }
.photo-cta b { font-size: 15.5px; font-weight: 640; }
.photo-rules { font-size: 11.5px; line-height: 1.5; color: var(--ink-3); max-width: 40ch; }
.photo-preview { position: relative; border-radius: var(--r-md); overflow: hidden; }
.photo-preview img { width: 100%; display: block; max-height: 240px; object-fit: cover; }
.photo-undo {
  position: absolute; right: 10px; bottom: 10px; display: flex; align-items: center; gap: 6px;
  /* `color: #fff` on a scrim made of --ink works in light and vanishes in dark,
     because --ink flips from #14181D to #E9ECF1: white on a light pill, 1.83:1.
     --on-ink flips with it. */
  background: color-mix(in srgb, var(--ink) 80%, transparent); color: var(--on-ink);
  padding: 8px 13px; border-radius: var(--r-pill); font-size: 12.5px; font-weight: 560;
  backdrop-filter: blur(6px); min-height: 44px;
}
.tagpick { display: flex; flex-wrap: wrap; gap: 7px; }
.tagbtn {
  min-height: 44px; padding: 0 15px; border-radius: var(--r-pill);
  font-size: 13.5px; font-weight: 550;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--ink-2);
  transition: background .15s var(--ease), color .15s var(--ease), border-color .15s var(--ease);
}
.tagbtn[aria-pressed="true"] { background: var(--navy); color: var(--on-navy); border-color: var(--navy); }
#note-text {
  width: 100%; margin-top: 14px; padding: 13px 14px; border-radius: var(--r-md);
  border: 1px solid var(--line); background: var(--surface-2); color: var(--ink);
  font-family: inherit; font-size: 16px; line-height: 1.5; font-weight: 400;
  resize: none; outline: 0;
}
#note-text:focus { border-color: var(--line-2); background: var(--surface); }

/* ============================ detail card ============================ */
.landmark {
  display: inline-flex; align-items: center; gap: 7px; margin: 0 0 10px;
  padding: 8px 13px 8px 11px; border-radius: var(--r-pill);
  background: var(--trust-100); color: var(--trust-800);
  font-size: 13.5px; font-weight: 620;
}
.landmark .i-sm { color: var(--trust); }
.detail-head { display: flex; align-items: flex-start; gap: 16px; padding-top: 6px; }
.why-maps {
  display: flex; align-items: flex-start; gap: 8px; margin: 10px 0 0;
  font-size: 12.5px; line-height: 1.5; color: var(--ink-3);
}
.why-maps .i-sm { color: var(--info); margin-top: 1px; flex: none; }
.report-row {
  width: 100%; display: flex; align-items: center; justify-content: center; gap: 8px;
  margin-top: 10px; padding: 13px; border-radius: var(--r-md);
  color: var(--ink-3); font-size: 13.5px; font-weight: 550;
  background: var(--surface-2); border: 1px solid var(--line);
}
.report-row:hover { background: var(--surface-3); }
.report-row .i-sm { color: var(--amber); }
.detail-title {
  font-size: 21px; line-height: 1.22; font-weight: 680; letter-spacing: -.021em;
  margin: 0; text-wrap: balance;
}
.detail-sub { margin-top: 6px; color: var(--ink-3); font-size: 13px; font-weight: 500; }
.walk {
  flex: none; text-align: right; display: flex; flex-direction: column; align-items: flex-end;
}
.walk b { font-size: 30px; line-height: 1; font-weight: 640; letter-spacing: -.035em; }
.walk span { font-size: 11px; font-weight: 600; letter-spacing: .07em;
             text-transform: uppercase; color: var(--ink-4); margin-top: 5px; }

.rule {
  margin-top: 16px; display: flex; align-items: flex-start; gap: 11px;
  padding: 13px 14px; border-radius: var(--r-md);
  background: var(--trust-50); border: 1px solid color-mix(in srgb, var(--trust) 18%, transparent);
}
.rule .i { color: var(--trust); margin-top: 1px; }
.rule-label {
  font-size: 11px; font-weight: 700; letter-spacing: .08em; text-transform: uppercase;
  color: var(--trust); display: block;
}
.rule-text { font-size: 14px; color: var(--ink-2); margin-top: 3px; display: block; }
.rule[data-kind="customers_only"] { background: var(--violet-100); border-color: color-mix(in srgb, var(--violet) 22%, transparent); }
.rule[data-kind="customers_only"] .i, .rule[data-kind="customers_only"] .rule-label { color: var(--violet); }
.rule[data-kind="beverage_only"] { background: var(--info-100); border-color: color-mix(in srgb, var(--info) 22%, transparent); }
.rule[data-kind="beverage_only"] .i, .rule[data-kind="beverage_only"] .rule-label { color: var(--info); }
.rule[data-kind="ticketed_area"] { background: var(--amber-100); border-color: color-mix(in srgb, var(--amber) 22%, transparent); }
.rule[data-kind="ticketed_area"] .i, .rule[data-kind="ticketed_area"] .rule-label { color: var(--amber); }
.rule[data-kind="fee"] { background: var(--amber-100); border-color: color-mix(in srgb, var(--amber) 22%, transparent); }
.rule[data-kind="fee"] .i, .rule[data-kind="fee"] .rule-label { color: var(--amber); }
.rule[data-kind="unknown"] { background: var(--surface-2); border-color: var(--line); }
.rule[data-kind="unknown"] .i, .rule[data-kind="unknown"] .rule-label { color: var(--ink-3); }

/* confidence meter */
.trustrow {
  margin-top: 26px; padding: 13px 14px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line);
}
.trustrow-top { display: flex; align-items: center; gap: 8px; }
.trustrow[data-c="verified"] .trustrow-top .i-sm,
.trustrow[data-c="official"] .trustrow-top .i-sm { color: var(--trust); }
.trustrow-top .i-sm { color: var(--ink-3); }
.trustrow-name { font-size: 13px; font-weight: 620; letter-spacing: -.005em; }
.meter { display: flex; gap: 3px; margin: 10px 0 9px; }
.meter i {
  height: 4px; flex: 1; border-radius: 2px; background: var(--line-2);
  transition: background .3s var(--ease);
}
.meter i.on { background: var(--trust); }
.meter[data-level="1"] i.on { background: var(--ink-4); }
.meter[data-level="2"] i.on { background: var(--info); }
.rate {
  display: flex; align-items: center; gap: 10px; margin: 4px 0 12px;
  padding: 11px 13px; border-radius: var(--r-md);
  background: var(--surface); border: 1px solid var(--line);
}
.rate b { font-size: 21px; font-weight: 680; letter-spacing: -.03em; color: var(--ink-3); }
.rate[data-good="true"] b { color: var(--trust); }
.rate span { font-size: 12.5px; color: var(--ink-3); line-height: 1.4; }
.progress-row {
  padding: 14px; border-radius: var(--r-md); background: var(--surface-2);
  border: 1px solid var(--line); margin: 6px 0;
}
.progress-row span { display: flex; align-items: center; gap: 10px; font-size: 14.5px; font-weight: 600; }
.progress-row .sub { display: block; font-size: 12px; color: var(--ink-4); font-weight: 500; margin-top: 2px; }
.progress-row .i-sm { color: var(--trust); }
.facts { display: flex; flex-direction: column; gap: 5px; }
.checks { display: flex; flex-direction: column; gap: 9px; margin-top: 14px; }
.check { display: flex; align-items: center; gap: 9px; font-size: 14px; color: var(--ink-2); }
.check .i-sm { color: var(--trust); flex: none; }
.check.dim, .check.dim .i-sm { color: var(--ink-4); }
.accessline { display: flex; align-items: center; gap: 8px; margin-top: 14px; flex-wrap: wrap; }
.apill {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 6px 12px; border-radius: 8px; font-size: 12px; font-weight: 700;
  letter-spacing: .05em; text-transform: uppercase;
  background: var(--trust-100); color: var(--trust-800);
}
.apill[data-a="customers_only"] { background: var(--violet-100); color: var(--violet-700); }
.apill[data-a="beverage_only"] { background: var(--info-100); color: var(--info-700); }
.apill[data-a="ticketed_area"] { background: var(--amber-100); color: var(--amber-700); }
.apill[data-a="fee"] { background: var(--amber-100); color: var(--amber-700); }
.apill[data-a="unknown"] { background: var(--surface-3); color: var(--ink-3); }
.awhy {
  width: 44px; height: 44px; border-radius: 50%; display: grid; place-items: center;
  color: var(--ink-4); background: var(--surface-2); border: 1px solid var(--line);
}
.fact { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--ink-2); }
.fact .i-sm { color: var(--ink-4); }
.fact.dim, .fact.dim .i-sm { color: var(--ink-4); }

.label {
  font-size: 11px; font-weight: 700; letter-spacing: .085em; text-transform: uppercase;
  color: var(--ink-4); margin: 24px 0 10px;
}

/* layer 2 — what you must NOT put here */
.rejects { display: flex; flex-wrap: wrap; gap: 7px; align-items: center; }
.reject {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 6px 11px 6px 8px; border-radius: var(--r-pill);
  background: var(--danger-100); color: var(--danger);
  font-size: 12.5px; font-weight: 560;
}
.reject .i-sm { width: 14px; height: 14px; }
.reject-note {
  flex-basis: 100%; font-size: 12px; color: var(--ink-4); margin-top: 4px; line-height: 1.45;
}

/* layer 2 — the record */
.facts-table {
  margin: 0; display: grid; grid-template-columns: auto 1fr; gap: 0;
  border: 1px solid var(--line); border-radius: var(--r-md); overflow: hidden;
}
.facts-table dt {
  padding: 11px 14px; background: var(--surface-2); font-size: 12.5px; font-weight: 560;
  color: var(--ink-3); white-space: nowrap; border-bottom: 1px solid var(--line);
}
.facts-table dd {
  margin: 0; padding: 11px 14px; font-size: 13.5px; font-weight: 500;
  border-bottom: 1px solid var(--line); overflow-wrap: anywhere;
}
.facts-table dd.dim { color: var(--ink-4); }
.facts-table dt:nth-last-of-type(1), .facts-table dd:nth-last-of-type(1) { border-bottom: 0; }

/* layer 3 — when it is not there */
.fallback {
  margin-top: 26px; padding: 16px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line);
}
.fallback-head {
  display: flex; align-items: center; gap: 8px; font-size: 12.5px; font-weight: 660;
  color: var(--ink-2); margin-bottom: 12px;
}
.fallback-head .i-sm { color: var(--ink-3); }
.alt {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 12px 13px; border-radius: var(--r-sm); background: var(--surface);
  border: 1px solid var(--line); transition: transform .16s var(--spring);
}
.alt:active { transform: scale(.985); }
.alt-body { min-width: 0; flex: 1; }
.alt-label {
  display: block; font-size: 10.5px; font-weight: 700; letter-spacing: .07em;
  text-transform: uppercase; color: var(--trust);
}
.alt-name {
  display: block; font-size: 14px; font-weight: 560; margin-top: 3px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.alt-time { font-size: 14px; font-weight: 640; color: var(--ink-2); }
.fallback-none { margin: 0; font-size: 13.5px; color: var(--ink-3); }
.fallback-tip {
  margin: 12px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--ink-3);
}

/* why this rating */
.trustrow-why {
  margin-left: auto; font-size: 11px; font-weight: 640; color: var(--ink-4);
  letter-spacing: .02em;
}
.evlabel { font-size: 10.5px; font-weight: 700; letter-spacing: .07em; text-transform: uppercase;
  color: var(--ink-4); margin: 12px 0 7px; }
.evrow { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.evchip { font-size: 11.5px; font-weight: 560; padding: 5px 10px; border-radius: 7px;
  background: var(--trust-100); color: var(--trust-800); }
.evchip[data-neg="1"] { background: var(--surface-3); color: var(--ink-3); }
.why.doubt { color: var(--amber); }
.cluster-note { display: inline-flex; align-items: center; gap: 7px; margin-bottom: 8px;
  padding: 5px 11px; border-radius: var(--r-pill); background: var(--surface-3);
  color: var(--ink-2); font-size: 12px; font-weight: 620; }
.cluster-note .i-sm { color: var(--ink-4); }
.why { margin: 0 0 10px; font-size: 12.5px; line-height: 1.5; color: var(--ink-3); }
.items { display: flex; gap: 7px; flex-wrap: wrap; }
.item {
  display: flex; flex-direction: column; align-items: center; gap: 8px;
  width: 76px; padding: 13px 6px 11px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line);
  font-size: 11.5px; font-weight: 560; color: var(--ink-3); text-align: center;
  line-height: 1.3;
}
.item .badge-ico {
  width: 38px; height: 38px; border-radius: var(--r-sm); display: grid; place-items: center;
  background: var(--w-general-bg); color: var(--w-general-ico);
}
.item[data-w="drink_cup"] .badge-ico { background: var(--w-cup-bg); color: var(--w-cup); }
.item[data-w="pet_bottle"] .badge-ico { background: var(--w-pet-bg); color: var(--w-pet); }
.item[data-w="can"] .badge-ico { background: var(--w-can-bg); color: var(--w-can-ico); }
.item[data-w="glass"] .badge-ico { background: var(--w-glass-bg); color: var(--w-glass); }
.item[data-w="food"] .badge-ico { background: var(--w-food-bg); color: var(--w-food); }
.item[data-w="other"] .badge-ico { background: var(--w-other-bg); color: var(--w-other); }

/* stays reachable however far the record scrolls — two taps to navigation */
.actions {
  /* NOT sticky, and this is the third attempt at this element.
     It was `position: sticky; bottom: calc(70px + safe-b)` so the button would
     stay reachable. Inside .sheet-body that anchor sits above the element's own
     place in the document, so sticky pulled it UPWARDS -- it is the fifth
     section of the card and it was being drawn over the first and the second:
     on top of the photos, and on top of the "nobody has written where this is"
     box. Two CSS attempts at the backdrop (widening, then a solid fill with the
     fade moved to a ::before) made the ghosting cleaner and did nothing about
     the overlap, because the overlap was never the backdrop -- it was the
     positioning.
     In normal flow it cannot overlap anything. The cost is that the button is
     where the card says it is, so on a half-open sheet you drag the sheet up
     to reach it. That is the correct trade: a button that covers the content is
     worse than a button you scroll to. */
  margin-top: 26px; display: flex; gap: 9px;
}
.cta {
  flex: 1; height: 54px; border-radius: var(--r-md); background: var(--navy); color: var(--on-navy);
  font-size: 16px; font-weight: 620; letter-spacing: -.01em;
  display: flex; align-items: center; justify-content: center; gap: 9px;
  box-shadow: var(--e2); transition: transform .18s var(--spring), opacity .18s var(--ease);
}
.cta:active { transform: scale(.975); }
.cta-icon {
  width: 54px; height: 54px; flex: none; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line); color: var(--ink-2);
  display: grid; place-items: center;
  transition: transform .18s var(--spring), background .18s var(--ease);
}
.cta-icon:active { transform: scale(.94); }
.source {
  margin-top: 18px; padding-top: 14px; border-top: 1px solid var(--line);
  font-size: 11px; color: var(--ink-4); display: flex; align-items: center; gap: 6px;
  flex-wrap: wrap;
}
.source a { color: var(--ink-3); text-underline-offset: 2px; }

/* ============================ list ============================ */
.list-head {
  display: flex; align-items: baseline; justify-content: space-between;
  margin: 10px 0 2px;
}
.list-title { font-size: 17px; font-weight: 660; letter-spacing: -.018em; }
.list-count { font-size: 12px; font-weight: 550; color: var(--ink-4); }
.row {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 13px 10px 13px 8px; border-radius: var(--r-sm);
  transition: background .14s var(--ease);
}
.row:hover { background: var(--surface-2); }
.row + .row { box-shadow: inset 0 1px 0 var(--line); }
.rowdot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.rowdot[data-c="unverified"] { box-shadow: inset 0 0 0 1.5px var(--pin-unverified); background: transparent !important; }
.rowbody { min-width: 0; flex: 1; }
.rowtop { display: flex; align-items: center; gap: 6px; margin-bottom: 5px; }
.rowname {
  font-size: 14.5px; font-weight: 570; letter-spacing: -.008em; display: block;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.apill.sm { padding: 3px 8px; font-size: 10.5px; letter-spacing: .04em; border-radius: 6px; }
/* the position relative to the station, stated first -- ecbo's pattern */
.ptag {
  display: inline-flex; align-items: center; padding: 6px 11px; border-radius: 8px;
  background: var(--navy); color: var(--on-navy);
  font-size: 11.5px; font-weight: 700; letter-spacing: .03em;
}
.ptag.sm { padding: 3px 8px; font-size: 10.5px; border-radius: 6px; }
.rowfrom { display: block; font-size: 11.5px; color: var(--ink-4); margin-top: 3px; }
.rowmeta { font-size: 12px; color: var(--ink-4); margin-top: 2px; display: flex; gap: 6px; align-items: center; }
.tag {
  font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  padding: 2px 6px; border-radius: 5px; background: var(--surface-3); color: var(--ink-3);
}
.tag[data-c="verified"], .tag[data-c="official"] { background: var(--trust-100); color: var(--trust-800); }
.tag[data-c="likely"] { background: var(--info-100); color: var(--info-700); }
.rowtime { font-size: 14px; font-weight: 620; color: var(--ink-2); white-space: nowrap; flex: none; }

.outlinks {
  display: flex; flex-wrap: wrap; gap: 8px; margin: 22px 0 4px;
  padding-top: 18px; border-top: 1px solid var(--line);
}
.outlinks a {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 9px 14px; border-radius: var(--r-pill);
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--ink-2); text-decoration: none; font-size: 13px; font-weight: 560;
}
.outlinks a:hover { background: var(--surface-3); }
.outlinks .i-sm { color: var(--ink-4); }

/* ============================ states ============================ */
.empty { padding: 26px 4px 10px; }
.empty-mark {
  width: 44px; height: 44px; border-radius: var(--r-sm); display: grid; place-items: center;
  background: var(--surface-2); border: 1px solid var(--line); color: var(--ink-3); margin-bottom: 16px;
}
.empty h2 { font-size: 19px; font-weight: 660; letter-spacing: -.02em; margin: 0 0 8px; }
.empty p { color: var(--ink-3); margin: 0; font-size: 14px; }
.note {
  margin-top: 16px; padding: 12px 14px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line);
  color: var(--ink-3); font-size: 13px; display: flex; gap: 9px; align-items: flex-start;
}
.note .i-sm { color: var(--ink-4); margin-top: 1px; }

.skeleton { padding: 14px 0; }
.sk {
  height: 14px; border-radius: 5px; margin-bottom: 10px;
  background: linear-gradient(90deg, var(--surface-3) 25%, var(--surface-2) 37%, var(--surface-3) 63%);
  background-size: 400% 100%; animation: shimmer 1.4s ease infinite;
}
@keyframes shimmer { 0% { background-position: 100% 0 } 100% { background-position: 0 0 } }

/* ============================ markers ============================ */
.pin {
  /* never override .maplibregl-marker's positioning: it is what keeps the
     marker out of normal flow. Only the size is ours to set.
     No transition on transform here -- see the note above. */
  position: absolute; left: 0; top: 0;
  width: 32px; height: 31px;        /* must match the svg box exactly */
  cursor: pointer;
}
/* A 24px dot is a fair target with a mouse and a miss on a pavement, one-handed,
   while holding something. WCAG 2.5.5 and Apple both ask 44px, so the hit area
   is grown to 44 and the drawing is left exactly where it was -- the svg box
   must keep matching the viewBox or the pin blurs, and nothing here may write a
   transform. An invisible ::before does both. */
.pin::before, .pin-dot::before {
  content: ""; position: absolute; left: 50%; top: 50%;
  width: 44px; height: 44px; margin: -22px 0 0 -22px;
  border-radius: 50%;
}
.pin[data-selected="true"] { z-index: 6; }
.pin svg { transition: transform .22s var(--spring); transform-origin: 50% 100%; }
.pin[data-selected="true"] svg { transform: scale(1.2); }
.pin svg { display: block; filter: drop-shadow(0 3px 6px rgba(20,24,29,.3)); }
/* An unverified point. The ring and the glyph are drawn in the svg (see
   dotSvg) so the category is readable at a glance; the border/background that
   used to live here would have sat under it as a second, offset ring. */
.pin-dot {
  /* The comment on .pin above says "No transition on transform here" and it
     means it: maplibre positions every marker by writing transform:
     translate(...) on this element on each frame. Transitioning transform makes
     the pin chase the map instead of sticking to it -- every dot wobbles on
     pan and zoom -- and a :hover transform replaces the translate outright,
     which on a touch screen sticks after a tap and throws the pin across the
     map. Both were introduced here with the category glyph and both are gone.
     Animate the svg INSIDE the marker, never the marker. */
  position: absolute; left: 0; top: 0;
  width: 24px; height: 24px;        /* must equal the svg viewBox, or it blurs */
  border-radius: 50%; cursor: pointer;
  filter: drop-shadow(0 1px 3px rgba(20,24,29,.3));
  transition: filter .18s var(--ease);
}
.pin-dot svg { display: block; transition: transform .18s var(--spring); }
.pin-dot:hover svg { transform: scale(1.14); }
.pin-dot[data-photo="true"] { filter: drop-shadow(0 0 0 2px var(--trust)) drop-shadow(0 1px 3px rgba(20,24,29,.3)); }
/* a pin that carries a traveller's photo says so on the map */
.pin-cam {
  position: absolute; top: -3px; right: -5px; width: 17px; height: 17px;
  border-radius: 50%; background: var(--surface); color: var(--ink-2);
  display: grid; place-items: center; box-shadow: 0 1px 3px rgba(20,24,29,.35);
}
.pin-cam svg { width: 10px; height: 10px; }
.rowshot {
  width: 46px; height: 46px; flex: none; border-radius: 10px;
  background: var(--surface-3) center/cover no-repeat;
}
.sheet-body.walking { padding-bottom: calc(170px + var(--safe-b)); }

.pill {
  position: absolute; top: 42px; left: 50%; transform: translateX(-50%);
  background: var(--navy); color: var(--on-navy); border-radius: var(--r-pill);
  padding: 3px 9px; white-space: nowrap;
  font: 620 11px/1.4 Inter, sans-serif; letter-spacing: -.01em;
  box-shadow: var(--e2);
}
.me { width: 20px; height: 20px; display: grid; place-items: center; flex: none; }
.me::before {
  content: ""; position: absolute; width: 42px; height: 42px; border-radius: 50%;
  background: color-mix(in srgb, var(--geo) 18%, transparent);
  animation: halo 2.8s var(--ease) infinite;
}
.me::after {
  /* whole pixels. maplibre already places this marker at a fractional offset;
     a half-pixel border on top of that is a guaranteed soft edge. */
  content: ""; width: 16px; height: 16px; border-radius: 50%; background: var(--geo);
  border: 3px solid var(--surface); box-shadow: 0 1px 5px rgba(11,17,20,.35); z-index: 1;
}
@keyframes halo { 0%,100% { transform: scale(.82); opacity: .85 } 50% { transform: scale(1.05); opacity: .5 } }
.cluster {
  /* same rule: shrink-wrap, or translate(-50%,-50%) uses the container width */
  position: absolute; left: 0; top: 0;
  display: inline-flex; width: max-content; flex-direction: column;
  align-items: center; gap: 4px; cursor: pointer;
}
.cluster b { transition: transform .18s var(--spring); }
.cluster:hover { z-index: 5; }
.cluster:hover b { transform: scale(1.1); }
.cluster b {
  min-width: 34px; height: 34px; padding: 0 8px; border-radius: var(--r-pill);
  background: var(--surface); border: 2px solid var(--line-2); box-shadow: var(--e2);
  display: grid; place-items: center;
  font: 620 12px/1 Inter, sans-serif; color: var(--ink-2); font-variant-numeric: tabular-nums;
}
.cluster[data-v="1"] b { border-color: var(--cat-strong, var(--cat-trash-strong)); color: var(--cat-strong, var(--cat-trash-strong)); background: var(--surface); }
.cluster i {
  font: 600 10px/1.2 Inter, sans-serif; font-style: normal; color: var(--ink-2);
  background: color-mix(in srgb, var(--surface) 86%, transparent);
  padding: 2px 6px; border-radius: 5px; white-space: nowrap;
  opacity: 0; transition: opacity .2s var(--ease);
}
.cluster[data-named="1"] i { opacity: 1; }

/* country overview list */
.stat-strip { display: flex; gap: 9px; margin: 12px 0 6px; }
.stat {
  flex: 1; padding: 13px 12px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line);
}
.stat b { display: block; font-size: 22px; font-weight: 650; letter-spacing: -.03em;
          font-variant-numeric: tabular-nums; }
.stat span { display: block; font-size: 11px; font-weight: 600; letter-spacing: .06em;
             text-transform: uppercase; color: var(--ink-4); margin-top: 4px; }
.stat[data-tone="trust"] b { color: var(--cat-active, var(--trust)); }
.pref-row {
  display: flex; align-items: center; gap: 12px; width: 100%; text-align: left;
  padding: 12px 10px 12px 6px; border-radius: var(--r-sm);
}
.pref-row:hover { background: var(--surface-2); }
.pref-row + .pref-row { box-shadow: inset 0 1px 0 var(--line); }
.pref-name { font-size: 14.5px; font-weight: 570; flex: 1; }
.pref-name span { color: var(--ink-4); font-weight: 500; margin-left: 6px; font-size: 12px; }
.pref-num { font-size: 13px; font-weight: 620; font-variant-numeric: tabular-nums; color: var(--ink-3); }
.pref-num em { font-style: normal; color: var(--cat-active, var(--trust)); }
.pref-num[data-zero="1"] em { color: var(--ink-4); }

/* ============================ modals ============================ */
.scrim {
  position: fixed; inset: 0; z-index: 40; opacity: 0; pointer-events: none;
  background: color-mix(in srgb, var(--ink) 34%, transparent);
  backdrop-filter: blur(2px); transition: opacity .26s var(--ease);
}
.scrim[data-open="true"] { opacity: 1; pointer-events: auto; }
.modal {
  position: fixed; z-index: 50; left: 50%; bottom: 0; transform: translate(-50%, 100%);
  width: min(480px, 100%); background: var(--surface);
  border-radius: var(--r-xl) var(--r-xl) 0 0; box-shadow: var(--e3);
  padding: 26px 20px calc(24px + var(--safe-b));
  transition: transform .42s var(--spring); max-height: 92vh; overflow-y: auto;
}
.modal[data-open="true"] { transform: translate(-50%, 0); }

/* The thanks sheet. Centred, short, and over in one screen. */
.thanks-mark {
  display: grid; place-items: center;
  width: 56px; height: 56px; margin: 0 auto 14px;
  border-radius: 999px;
  background: var(--trust-50); color: var(--trust-800);
}
#m-thanks h2, #m-thanks p { text-align: center; }
#m-thanks p { margin-bottom: 14px; }
.thanks-count {
  text-align: center; margin: 0 0 18px;
  font-size: 13px; font-weight: 640; color: var(--ink-3);
}

/* What the phone worked out, above the question it answers. Tinted rather than
   bordered: it is an observation, not a field. */
.place {
  display: flex; align-items: center; gap: 8px;
  margin: 0 0 14px; padding: 10px 12px;
  border-radius: var(--r-md);
  background: var(--trust-50); color: var(--trust-800);
  font-size: 13.5px; font-weight: 560;
}
.place .i-sm { flex: 0 0 auto; color: var(--trust-800); }
.place > span { flex: 1 1 auto; min-width: 0; }
.place-no {
  flex: 0 0 auto; min-height: 44px; padding: 0 12px;
  border: 0; background: none; cursor: pointer;
  font-size: 13px; font-weight: 640;
  color: var(--trust-800); text-decoration: underline;
}

/* Six of the seven sheets had no close control at all -- they relied on tapping
   the scrim, or on Escape, which a phone does not have. Measured on a 390x844
   viewport, the You and Menu sheets are 776px tall, so the scrim left to tap is
   a 68px strip at the very top of the screen, under the notch. That is not a
   close affordance. Every sheet now carries a real button, sticky so that
   scrolling a long sheet cannot carry it off screen. */
.mbar {
  position: sticky; top: 0; z-index: 3;
  margin: -26px -20px 8px; padding: 12px 10px 6px;
  display: flex; align-items: center; justify-content: flex-end;
  background: var(--surface);
  border-radius: var(--r-xl) var(--r-xl) 0 0;
}
.mbar::before {
  content: ""; position: absolute; left: 50%; top: 9px; transform: translateX(-50%);
  width: 38px; height: 4px; border-radius: 2px;
  background: color-mix(in srgb, var(--ink) 15%, transparent);
}
.mclose {
  /* A flex item shrinks by default, and this one must not: 44px is the floor,
     not a preference. */
  flex: 0 0 auto;
  width: 44px; height: 44px; min-height: 44px; padding: 0;
  display: grid; place-items: center;
  border: 0; border-radius: 999px; background: none;
  color: var(--ink-3); cursor: pointer;
}
.mclose:active { background: color-mix(in srgb, var(--ink) 8%, transparent); }
.modal h2 { font-size: 21px; font-weight: 680; letter-spacing: -.022em; margin: 0 0 8px; }
.modal p { color: var(--ink-3); margin: 0 0 20px; font-size: 14.5px; }
.modal-eyebrow {
  font-size: 11px; font-weight: 700; letter-spacing: .085em; text-transform: uppercase;
  color: var(--ink-4); margin-bottom: 8px;
}
.choice { display: flex; gap: 10px; }
.choice button {
  flex: 1; min-height: 92px; border-radius: var(--r-md); font-size: 14.5px; font-weight: 600;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; padding: 14px 10px; text-align: center; border: 1px solid transparent;
  transition: transform .18s var(--spring);
}
.choice button:active { transform: scale(.97); }
.yes { background: var(--trust-50); color: var(--trust-800); border-color: color-mix(in srgb, var(--trust) 22%, transparent) !important; }
.no  { background: var(--danger-100); color: var(--danger); border-color: color-mix(in srgb, var(--danger) 22%, transparent) !important; }
/* The escape hatch. Reads as an option, not as an answer. */
.unsure {
  display: block; width: 100%; min-height: 44px; margin-top: 10px;
  border-radius: var(--r-md); background: var(--surface-2);
  border: 1px solid var(--line); color: var(--ink-3);
  font-size: 13.5px; font-weight: 560;
  transition: background .16s var(--ease);
}
.unsure:active { background: var(--surface-3); }

/* A trip, not a streak -- see currentTrip() in app.js. */
.tripline {
  display: flex; align-items: center; gap: 8px; min-height: 44px;
  padding: 10px 14px; border-radius: var(--r-md);
  background: var(--trust-50); border: 1px solid color-mix(in srgb, var(--trust) 20%, transparent);
  color: var(--trust-800); font-size: 13.5px; font-weight: 600;
}
.tripline .i-sm { flex: none; }

/* A rule we could not trace to the operator says so, on the card. */
.rule-unverified {
  display: inline-flex; align-items: center; gap: 5px; margin: 4px 0 2px;
  font-size: 11.5px; font-weight: 640; color: var(--amber-700);
}
.rule-unverified .i-sm { flex: none; }

/* Phrase cards. The Japanese is the payload -- it is what a stranger reads off
   the screen at arm's length -- so it is the biggest thing here and it never
   shrinks below 22px. The reader's own language sits under it so they know
   what they are holding up, and the romaji is there to be attempted aloud. */
.says { display: flex; flex-direction: column; gap: 10px; }
.say {
  padding: 16px 16px 14px; border-radius: var(--r-md);
  background: var(--surface); border: 1px solid var(--line-2);
  text-align: left;
}
.say-ja {
  display: block; font-size: 22px; line-height: 1.45; font-weight: 640;
  color: var(--ink); letter-spacing: -.01em;
}
.say-ro {
  display: block; margin-top: 7px; font-size: 12.5px; font-weight: 560;
  color: var(--ink-4); font-style: italic;
}
.say-me {
  display: block; margin-top: 9px; padding-top: 9px;
  border-top: 1px solid var(--line); font-size: 13.5px; line-height: 1.45;
  color: var(--ink-3);
}
/* A Japanese reader does not need a translation of Japanese. */
:root[lang="ja"] .say-ro, :root[lang="ja"] .say-me { display: none; }

/* In the empty state this is not a consolation prize: with 4,443 bins in the
   whole country, "ask somebody" is frequently the correct answer. */
.saybtn {
  display: flex; align-items: center; justify-content: center; gap: 9px;
  width: 100%; min-height: 48px; margin-top: 14px;
  border-radius: var(--r-md); background: var(--navy); color: var(--on-navy);
  font-size: 14.5px; font-weight: 640;
  transition: transform .18s var(--spring);
}
.saybtn:active { transform: scale(.98); }
.saybtn .i-sm { flex: none; }

/* "4 of the last 5 people could use it." The line that says whether the bin
   works, as opposed to whether it exists. */
.workedline {
  display: flex; align-items: center; gap: 7px; margin-top: 5px;
  font-size: 12.5px; font-weight: 600; color: var(--ink-3);
}
.workedline .i-sm { flex: none; color: var(--ink-4); }
.workedline[data-ok="true"] .i-sm { color: var(--trust-800); }
.workedline[data-ok="false"] { color: var(--amber-700); }
.workedline[data-ok="false"] .i-sm { color: var(--amber-700); }
.stack { display: flex; flex-direction: column; gap: 4px; }
.stack button {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 15px 14px; border-radius: var(--r-md); font-size: 15px; font-weight: 560;
  transition: background .16s var(--ease);
}
.stack button:hover { background: var(--surface-2); }
.stack button[aria-current="true"] { background: var(--surface-2); }
.stack .i { color: var(--trust); }
/* This is a flex ROW, so the label (a bare text node = an anonymous flex item)
   competed for width with the .sub beside it and collapsed to one character per
   line -- "この端末の写真" rendered vertically. Let the row wrap and give .sub a
   line of its own, which is what it was always meant to have. */
.stack button > span, .stack a > span { flex: 1; min-width: 0; text-align: left;
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.stack button > span > .sub, .stack a > span > .sub { flex: 1 0 100%; margin-left: 26px; }
.stack a {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 15px 14px; border-radius: var(--r-md); font-size: 15px; font-weight: 560;
  text-decoration: none; color: inherit; transition: background .16s var(--ease);
}
.stack a:hover { background: var(--surface-2); }
.stack a .i { color: var(--ink-4); }
.stack a .i-sm { color: var(--ink-3); }
#storage-row { width: 100%; align-items: flex-start; }
/* the explanation under the label wraps to three lines; keep the Clear button
   pinned to the first line instead of floating over the middle of the text */
#storage-clear { margin-top: 2px; flex: none; }
#storage-clear { cursor: pointer; }
#storage-clear:hover { background: var(--danger-100); color: var(--danger); }
.stack .sub { font-size: 12px; color: var(--ink-4); font-weight: 500; display: block; margin-top: 2px; }
.modal .cta { width: 100%; margin-top: 6px; }
.ghost {
  width: 100%; height: 48px; border-radius: var(--r-md); margin-top: 9px;
  color: var(--ink-3); font-size: 15px; font-weight: 560;
  display: flex; align-items: center; justify-content: center; gap: 8px;
}
.ghost:hover { background: var(--surface-2); }

.toast {
  /* 28px put this on top of the tab labels: the toast is ~48px tall and the bar
     is 58px, and at z-index 60 it paints over it. A message that hides the
     navigation reads as a glitch. Above the bar instead. */
  position: fixed; left: 50%; bottom: calc(72px + var(--safe-b));
  transform: translate(-50%, 16px); z-index: 60;
  background: var(--ink); color: var(--on-ink); padding: 13px 18px;
  border-radius: var(--r-md); font-size: 14px; font-weight: 560;
  box-shadow: var(--e3); opacity: 0; pointer-events: none; max-width: 90vw;
  transition: opacity .22s var(--ease), transform .38s var(--spring);
  display: flex; align-items: center; gap: 9px;
}
.toast[data-open="true"] { opacity: 1; transform: translate(-50%, 0); }
@media (min-width: 920px) {
  .toast { bottom: calc(28px + var(--safe-b)); }
}
.toast .i-sm { color: var(--on-ink-accent); }

/* ============================ desktop ============================ */
@media (min-width: 920px) {
  .sheet-body { padding-bottom: calc(40px + var(--hidden, 0px)); }
  .actions { bottom: 4px; }
  .sheet {
    top: 0; bottom: 0; left: 0; right: auto; width: 412px; height: 100%; max-height: 100%;
    border-radius: 0; border-top: 0; border-right: 1px solid var(--line);
    box-shadow: var(--e3); transform: translateX(-100%);
  }
  .sheet[data-state="peek"], .sheet[data-state="half"], .sheet[data-state="full"] { transform: translateX(0); }
  .sheet-body { padding: 14px 26px calc(40px + var(--hidden, 0px)); }
  .grab { display: none; }
  .top { left: 412px; max-width: 560px; transition: left .4s var(--spring); }
  .top.wide { left: 0; }
  .controls { bottom: 28px; right: 22px; }
  .controls[data-hidden="true"] { opacity: 1; pointer-events: auto; }
  .modal {
    border-radius: var(--r-lg); bottom: auto; top: 50%;
    transform: translate(-50%, calc(-50% + 16px)); opacity: 0;
  }
  .modal[data-open="true"] { transform: translate(-50%, -50%); opacity: 1; }
}

/* On a phone the ask bar's three children cannot share one row: the button and
   the dismiss X are flex:none, so the text was crushed into a 92px column and
   read as if the button were sitting on top of it. Give the text its own row
   and let the button fill the next one. */
@media (max-width: 430px) {
  .askbar { flex-wrap: wrap; row-gap: 10px; }
  .askbar-body { flex: 1 0 100%; }
  .ask-go { flex: 1 1 auto; justify-content: center; }
}

/* A bottom sheet taller than the screen scrolls, but with no affordance that it
   does. In the verify flow the content ran to 953px inside a 747px box, so the
   "Done" button sat 130px below the fold and the step looked unfinishable.
   Pin the primary action to the bottom of the sheet: the content above still
   scrolls, but the way out is always on screen. */
#contrib-done {
  position: sticky;
  bottom: 0;
  margin-bottom: 0;
  box-shadow: 0 -16px 20px -8px var(--surface), 0 8px 0 8px var(--surface);
}

/* Says once what the minute figures on each row are measured from. */
.list-note {
  font-size: 12px; color: var(--ink-4); margin: -4px 0 10px;
  display: flex; align-items: center; gap: 6px;
}

/* First-party AREA rules. Named .arule, not .rule: .rule already belongs to the
   access-rule card in the spot sheet, and reusing it made this inherit
   display:flex and stack the title one character per line. */
/* First-party rules. Where there is no pin, the rule IS the answer, so it gets
   the weight of a card rather than a footnote. */
.arule {
  margin-top: 12px; padding: 14px 16px; border-radius: var(--r-md);
  background: var(--surface-2); border: 1px solid var(--line); text-align: left;
}
/* Hardcoded #FDF4E7 here put near-white text on a near-white card in dark mode
   -- contrast 0.04, i.e. invisible -- and this is the most important card of the
   three. --amber-100 already flips with the theme, like every other surface. */
.arule[data-tone="warn"] {
  background: var(--amber-100);
  border-color: color-mix(in srgb, var(--amber) 30%, transparent);
}
.arule[data-tone="good"] { background: var(--trust-50); border-color: color-mix(in srgb, var(--trust) 26%, transparent); }
.arule b { display: block; font-size: 14.5px; font-weight: 640; letter-spacing: -.01em; }
.arule p { margin: 6px 0 0; font-size: 13px; line-height: 1.62; color: var(--ink-3); }
/* the source link is small text, but it is still something a thumb has to hit:
   pad it out to the 44px target the rest of the app keeps to */
.arule a {
  display: inline-flex; align-items: center; margin-top: 4px;
  min-height: 44px; padding: 0 10px 0 0; font-size: 12px; color: var(--ink-4);
}

/* Where exactly, in words. An operator's announcement gives a station, not a
   pin; this is the line that actually walks someone there. */
.whereis {
  display: flex; gap: 9px; align-items: flex-start;
  margin: 10px 0 2px; padding: 11px 13px; border-radius: var(--r-md);
  background: var(--trust-50);
  border: 1px solid color-mix(in srgb, var(--trust) 24%, transparent);
  font-size: 14px; font-weight: 560; color: var(--ink);
}
.whereis .i-sm { color: var(--trust); margin-top: 2px; flex: none; }

/* The "no bin here" fallback: a drinks machine's recycling box. Styled as a
   real answer rather than an apology, but labelled with its limit on every row
   so nobody carries a burger wrapper to one. */
.fb-head { margin: 16px 0 8px; font-size: 12px; font-weight: 680;
           letter-spacing: .02em; color: var(--ink-3); text-align: left; }
.fb-row {
  display: flex; align-items: center; gap: 10px; width: 100%;
  min-height: 52px; padding: 10px 14px; margin-bottom: 8px;
  border-radius: var(--r-md); background: var(--surface);
  border: 1px solid var(--line); text-align: left;
}
.fb-kind { font-size: 14.5px; font-weight: 600; flex: none; }
.fb-rule { font-size: 12px; color: var(--ink-4); flex: 1; min-width: 0; }
.fb-d { font-size: 14px; font-weight: 640; flex: none; }
