/* ============================================================ playdar
   Clean, fresh, confident. A white base with generous whitespace, one saturated
   accent doing all the work, and heavy geometric type. Restraint over decoration:
   separation comes from space and soft shadow, not from rules and fills.

   Tangerine is the only brand color and it appears in exactly three places -- the
   wordmark, primary buttons, and active states. Ink blue is reserved for inline
   emphasis and links and never becomes a button, so the two never compete.

   Semantic state colors are a separate system from the brand and stay that way.
   They also never rely on hue alone: each state carries a distinct SHAPE on the map
   and a distinct SURFACE treatment on chips (fill / dashed outline / solid outline /
   dotted outline), so all four survive greyscale and every color vision deficiency.
   Moving the neutral from warm to cool raised deuteranopia separation from 4.5 to
   16.0 dE, which is the one thing a rebrand must not undo. */

@font-face{
  font-family:"Outfit";
  src:url("fonts/outfit.woff2") format("woff2-variations"),
      url("fonts/outfit.woff2") format("woff2");
  font-weight:300 900; font-style:normal; font-display:swap;
}

:root{
  --paper:#FFFFFF; --card:#FFFFFF; --card-2:#F5F7FA;
  --ink:#101419; --ink-2:#454E5C; --ink-3:#646D7B;
  --line:#ECEFF4; --line-2:#DCE1E9;
  --brand:#FF7A29; --on-brand:#FFFFFF; --brand-text:#FF7A29;
  --brand-ink:#5C2600; --brand-deep:#8A3A05;
  --cluster-bg:#FF7A29; --cluster-fg:#FFFFFF;
  --cluster-ring:#FFFFFF; --cluster-outline:rgba(16,20,25,.16);
  --active:#1A3FA8; --on-active:#FFFFFF;
  --link:#1A3FA8;
  --yes:#0C4B34;   --yes-bg:#E8F8F2;
  --maybe:#956504; --maybe-bg:#FBF2DF;
  --no:#A31F4B;    --no-bg:#FBEAEF;
  --none:#636874;  --none-bg:#F0F2F4;
  --school:#3A0647;  --school-bg:#F7ECFA;
  --nycha:#1260F3;   --nycha-bg:#E7EFFE;
  --focus:#1A3FA8;
  --sh-1:0 1px 2px rgba(16,20,25,.03), 0 2px 8px rgba(16,20,25,.045);
  --sh-2:0 2px 6px rgba(16,20,25,.04), 0 12px 32px rgba(16,20,25,.075);
  --sh-3:0 -2px 10px rgba(16,20,25,.045), 0 -14px 44px rgba(16,20,25,.085);
  --sh-pin:0 1px 2px rgba(16,20,25,.32), 0 3px 8px rgba(16,20,25,.18);
  --tile-filter:none;
  --dock-h:74px;
  --r-card:24px; --r-pill:999px; --r-sheet:34px; --r-tile:20px;
  --ease:cubic-bezier(.22,.61,.24,1);
  --ease-sheet:cubic-bezier(.32,.72,0,1);
  --tile-filter:none;
  --sans:"Outfit",ui-sans-serif,system-ui,-apple-system,"Segoe UI",Roboto,sans-serif;
  --disp:"Outfit",ui-sans-serif,system-ui,-apple-system,sans-serif;
  --mono:ui-monospace,SFMono-Regular,"SF Mono",Menlo,Consolas,monospace;
}
@media (prefers-color-scheme:dark){
  :root{
    --paper:#141A24; --card:#1C2431; --card-2:#19212C;
    --ink:#EEF2F8; --ink-2:#B0BBCA; --ink-3:#8F9CAE;
    --line:#28313F; --line-2:#3A4757;
    --brand:#FF7A29; --on-brand:#FFFFFF; --brand-text:#FF7A29;
    --brand-ink:#5C2600; --brand-deep:#FFA766;
    --cluster-bg:#FF7A29; --cluster-fg:#FFFFFF;
    --cluster-ring:#FFFFFF; --cluster-outline:rgba(0,0,0,.55);
    --active:#3B6FD4; --on-active:#FFFFFF;
    --link:#9CBAFF;
    --yes:#2EB885;   --yes-bg:#16302A;
    --maybe:#F9D686; --maybe-bg:#2B2A1B;
    --no:#F68EAD;    --no-bg:#2E1F2A;
    --none:#C0C5CE;  --none-bg:#222A36;
    --school:#BC78DD; --school-bg:#2C1F33;
    --nycha:#66E3FF;  --nycha-bg:#12303A;
    --focus:#9CBAFF;
    --sh-1:0 1px 2px rgba(0,0,0,.4), 0 2px 8px rgba(0,0,0,.3);
    --sh-2:0 2px 6px rgba(0,0,0,.4), 0 12px 32px rgba(0,0,0,.45);
    --sh-3:0 -2px 10px rgba(0,0,0,.4), 0 -14px 44px rgba(0,0,0,.5);
    --sh-pin:0 1px 2px rgba(0,0,0,.55), 0 3px 8px rgba(0,0,0,.4);
    --tile-filter:invert(1) hue-rotate(180deg) brightness(.9) contrast(.92) saturate(.7);
  }
}
:root[data-theme="light"]{
  --paper:#FFFFFF; --card:#FFFFFF; --card-2:#F5F7FA;
  --ink:#101419; --ink-2:#454E5C; --ink-3:#646D7B;
  --line:#ECEFF4; --line-2:#DCE1E9;
  --brand:#FF7A29; --on-brand:#FFFFFF; --brand-text:#FF7A29;
  --brand-ink:#5C2600; --brand-deep:#8A3A05;
  --cluster-bg:#FF7A29; --cluster-fg:#FFFFFF;
  --cluster-ring:#FFFFFF; --cluster-outline:rgba(16,20,25,.16);
  --active:#1A3FA8; --on-active:#FFFFFF;
  --link:#1A3FA8;
  --yes:#0C4B34;   --yes-bg:#E8F8F2;
  --maybe:#956504; --maybe-bg:#FBF2DF;
  --no:#A31F4B;    --no-bg:#FBEAEF;
  --none:#636874;  --none-bg:#F0F2F4;
  --school:#3A0647;  --school-bg:#F7ECFA;
  --nycha:#1260F3;   --nycha-bg:#E7EFFE;
  --focus:#1A3FA8;
  --sh-1:0 1px 2px rgba(16,20,25,.03), 0 2px 8px rgba(16,20,25,.045);
  --sh-2:0 2px 6px rgba(16,20,25,.04), 0 12px 32px rgba(16,20,25,.075);
  --sh-3:0 -2px 10px rgba(16,20,25,.045), 0 -14px 44px rgba(16,20,25,.085);
  --sh-pin:0 1px 2px rgba(16,20,25,.32), 0 3px 8px rgba(16,20,25,.18);
  --tile-filter:none;
}
:root[data-theme="dark"]{
--paper:#141A24; --card:#1C2431; --card-2:#19212C;
  --ink:#EEF2F8; --ink-2:#B0BBCA; --ink-3:#8F9CAE;
  --line:#28313F; --line-2:#3A4757;
  --brand:#FF7A29; --on-brand:#FFFFFF; --brand-text:#FF7A29;
  --brand-ink:#5C2600; --brand-deep:#FFA766;
  --cluster-bg:#FF7A29; --cluster-fg:#FFFFFF;
  --cluster-ring:#FFFFFF; --cluster-outline:rgba(0,0,0,.55);
  --active:#3B6FD4; --on-active:#FFFFFF;
  --link:#9CBAFF;
  --yes:#2EB885;   --yes-bg:#16302A;
  --maybe:#F9D686; --maybe-bg:#2B2A1B;
  --no:#F68EAD;    --no-bg:#2E1F2A;
  --none:#C0C5CE;  --none-bg:#222A36;
  --school:#BC78DD; --school-bg:#2C1F33;
  --nycha:#66E3FF;  --nycha-bg:#12303A;
  --focus:#9CBAFF;
  --sh-1:0 1px 2px rgba(0,0,0,.4), 0 2px 8px rgba(0,0,0,.3);
  --sh-2:0 2px 6px rgba(0,0,0,.4), 0 12px 32px rgba(0,0,0,.45);
  --sh-3:0 -2px 10px rgba(0,0,0,.4), 0 -14px 44px rgba(0,0,0,.5);
  --sh-pin:0 1px 2px rgba(0,0,0,.55), 0 3px 8px rgba(0,0,0,.4);
  --tile-filter:invert(1) hue-rotate(180deg) brightness(.9) contrast(.92) saturate(.7);
}

*{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0;background:var(--paper);color:var(--ink);
  font-family:var(--sans);font-size:16px;line-height:1.6;font-weight:440;
  -webkit-text-size-adjust:100%;overscroll-behavior:none;overflow:hidden;
  -webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;
  text-rendering:optimizeLegibility;
}
/* One easing and one duration everywhere, so nothing in the app moves in a way that
   feels borrowed from somewhere else. Presses settle rather than snap. */
button{font:inherit;color:inherit;background:none;border:0;cursor:pointer;
  transition:background-color .2s var(--ease), color .2s var(--ease),
             box-shadow .2s var(--ease), transform .16s var(--ease), opacity .2s var(--ease)}
button:active{transform:scale(.975)}
:focus-visible{outline:2.5px solid var(--focus);outline-offset:3px;border-radius:12px}
[hidden]{display:none!important}
.visually-hidden{
  position:absolute!important;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);white-space:nowrap;border:0;
}

/* ============================================================ shell */
#app{position:fixed;inset:0}
/* z-index:0 is load-bearing, not cosmetic. Without it #map has z-index:auto and
   never becomes a stacking context, so Leaflet's own panes (z-index 400) and control
   corners (700) escape into the parent context and paint straight over #listpane.
   Establishing the context traps them inside the map, where they belong. */
#map{position:absolute;inset:0;background:var(--card-2);z-index:0;isolation:isolate}
.leaflet-tile-pane{filter:var(--tile-filter)}
.leaflet-container{background:var(--card-2);font-family:var(--sans)}
.leaflet-container .leaflet-control-attribution{
  font-size:10px;background:var(--card);color:var(--ink-3);
  padding:3px 8px;border-radius:12px 0 0 0;
}
/* The two attribution links are 13px tall as Leaflet draws them. Growing the strip to
   44px would be visually absurd for a credit line, so the hit area is grown instead --
   an invisible 44px-tall box centred on each link, which is what a thumb actually
   needs. Layout is untouched. */
.leaflet-container .leaflet-control-attribution a{
  color:var(--link);position:relative;display:inline-block}
.leaflet-container .leaflet-control-attribution a::after{
  content:"";position:absolute;left:-9px;right:-9px;top:50%;
  height:44px;transform:translateY(-50%)}
.leaflet-bottom.leaflet-right{bottom:calc(var(--dock-h) + env(safe-area-inset-bottom,0px))}
.leaflet-top,.leaflet-bottom{z-index:700}

#topbar{
  position:absolute;top:0;left:0;right:0;z-index:900;
  display:flex;align-items:center;gap:12px;
  padding:calc(env(safe-area-inset-top,0px) + 14px) 18px 18px;
  background:linear-gradient(to bottom,var(--paper) 38%,
             color-mix(in srgb,var(--paper) 72%,transparent) 68%,transparent);
  pointer-events:none;
}
#topbar > *{pointer-events:auto}
.brand{display:flex;flex-direction:column;min-width:0;flex:1}
.brand b{
  font-family:var(--disp);font-size:29px;font-weight:800;letter-spacing:-.042em;
  color:var(--brand-text);text-transform:lowercase;
  line-height:1.05;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
.brand span{font-size:12.5px;color:var(--ink-3);font-weight:550;margin-top:1px;
  font-variant-numeric:tabular-nums;letter-spacing:.005em}
.iconbtn{
  width:48px;height:48px;border-radius:var(--r-pill);display:grid;place-items:center;
  background:var(--card);box-shadow:var(--sh-2);flex:none;
}
.iconbtn svg{width:22px;height:22px;stroke:var(--ink);fill:none;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}

/* ============================================================ map furniture */
#legend{
  position:absolute;left:18px;z-index:800;
  bottom:calc(var(--dock-h) + env(safe-area-inset-bottom,0px) + 22px);
  background:color-mix(in srgb,var(--card) 88%,transparent);
  -webkit-backdrop-filter:saturate(180%) blur(20px);
  backdrop-filter:saturate(180%) blur(20px);
  border-radius:var(--r-card);box-shadow:var(--sh-2);padding:16px 18px;max-width:72vw;
}
/* Grid, not flex: the six swatches are 14-19px across and different shapes, so a flex
   row leaves the labels on a ragged left edge. A fixed 20px gutter with the mark
   centred in it puts every label on the same axis, which is most of the difference
   between a key that looks designed and one that looks assembled. */
.lg{display:grid;grid-template-columns:20px 1fr;align-items:center;column-gap:13px;
  font-size:13px;color:var(--ink-2);line-height:1.35;font-weight:500;
  letter-spacing:-.005em}
.lg > i{justify-self:center;margin:0}
/* legend swatches mirror the pin shapes, so the key teaches shape as well as color */
.lg i{width:15px;height:15px;flex:none;display:block;border-radius:50%}
.lg i[data-shape="diamond"]{border-radius:4px;transform:rotate(45deg) scale(.84)}
.lg i[data-shape="square"]{border-radius:5px}
.lg i[data-shape="hollow"]{background:transparent!important;border:4px solid var(--none)}
/* Match the map marks exactly: hollow square for a school yard, triangle for NYCHA. */
.lg i[data-shape="school"]{width:13px;height:13px;border-radius:4px;
  background:var(--paper)!important;border:3.5px solid var(--school);margin:1px}
.lg i[data-shape="triangle"]{width:16px;height:14px;border-radius:0;
  background:var(--nycha)!important;clip-path:polygon(50% 0%,100% 100%,0% 100%)}
.lg i.dash{border:2.5px dashed var(--none);background:transparent!important;border-radius:50%}
#legendRows{display:flex;flex-direction:column;gap:11px;margin-top:15px;
  padding-top:15px;
  border-top:1px solid color-mix(in srgb,var(--line-2) 60%,transparent)}
#colorBy{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
#colorBy .cbl{
  font-size:10px;letter-spacing:.14em;text-transform:uppercase;font-weight:700;
  color:var(--brand-deep);width:100%;margin-bottom:7px;
}
#colorBy button{
  min-height:46px;min-width:48px;padding:0 19px;border-radius:var(--r-pill);
  background:var(--card-2);font-size:14.5px;font-weight:700;color:var(--ink-2);
  letter-spacing:-.01em;
}
/* Selected = the action colour, filled, with white text. Ink blue carries every
   primary and active state in the app; tangerine is the accent and never a fill
   behind text, because #FF7A29 is 2.60:1 against white and cannot legibly hold it. */
#colorBy button[aria-pressed="true"]{
  background:var(--active);color:var(--on-active);box-shadow:var(--sh-1)}

#toast{
  position:absolute;left:50%;transform:translateX(-50%);z-index:950;
  bottom:calc(var(--dock-h) + env(safe-area-inset-bottom,0px) + 20px);
  background:var(--ink);color:var(--paper);font-size:13px;font-weight:600;
  padding:11px 18px;border-radius:var(--r-pill);box-shadow:var(--sh-2);
  opacity:0;transition:opacity .25s;pointer-events:none;white-space:nowrap;max-width:88vw;
}
#toast.on{opacity:.96}
#mapEmpty{
  position:absolute;left:50%;top:46%;transform:translate(-50%,-50%);z-index:820;
  background:var(--card);border-radius:var(--r-card);
  box-shadow:var(--sh-2);padding:22px 24px;text-align:center;max-width:76vw;
}
#mapEmpty b{display:block;font-family:var(--disp);font-size:21px;font-weight:800;
  letter-spacing:-.025em;margin-bottom:6px}
#mapEmpty span{font-size:14px;color:var(--ink-2)}

/* pins: the 44px box is the tap target; the mark inside stays small.
   Shape carries state as reliably as colour does -- under deuteranopia yes/no sit
   9.7 dE apart, so the circle-versus-rounded-square difference is doing real work and
   must not be softened into the same silhouette.

   What is refined here is everything BUT the silhouette: a slightly larger mark, a
   two-layer shadow so pins sit on the map rather than being stamped onto it, a
   cleaner ring, and a settle on press. */
.bare{background:none;border:0}
.pinwrap{display:block;width:44px;height:44px;position:relative;cursor:pointer;
  -webkit-tap-highlight-color:transparent;
  transition:transform .18s var(--ease)}
.pinwrap:active{transform:scale(1.14)}
.pinwrap i{
  position:absolute;left:50%;top:50%;transform:translate(-50%,-50%);
  width:17px;height:17px;border-radius:50%;display:block;
  border:2.5px solid var(--card);box-shadow:var(--sh-pin);
  background:var(--none);
  transition:box-shadow .2s var(--ease);
}
.pinwrap[data-tone="ok"]  i{background:var(--yes)}
.pinwrap[data-tone="dis"] i{background:var(--maybe);border-radius:5px;
  transform:translate(-50%,-50%) rotate(45deg) scale(.9)}
.pinwrap[data-tone="bar"] i{background:var(--no);border-radius:6px}
.pinwrap[data-tone="non"] i{background:var(--card);border-color:var(--none);
  border-width:4.5px;box-shadow:var(--sh-1)}
.pinwrap.approx i{
  width:25px;height:25px;background:transparent!important;box-shadow:none;
  border:2.5px dashed var(--none);border-radius:50%;
  transform:translate(-50%,-50%);
}
.pinwrap.approx[data-tone="ok"]  i{border-color:var(--yes)}
.pinwrap.approx[data-tone="dis"] i{border-color:var(--maybe)}
.pinwrap.approx[data-tone="bar"] i{border-color:var(--no)}
.pinwrap:focus-visible i{outline:3px solid var(--focus);outline-offset:3px}

/* Schoolyards are a different KIND of record, not a fifth state, so they differ on
   three channels at once rather than on hue alone: a plum outside the four-state
   ramp, a hollow rounded-square silhouette, and a smaller mark. Smaller is also the
   honest hierarchy -- these carry no accessibility data, so they should recede
   behind the playgrounds that do. */
.pinwrap[data-tone="sy"] i{
  width:14px;height:14px;border-radius:5px;
  background:var(--paper);border:3.5px solid var(--school);
  box-shadow:var(--sh-1);
}

/* NYCHA play areas: a triangle. By the sixth class the hue wheel is genuinely full --
   the best remaining colour still lands 24 dE from its nearest neighbour under normal
   vision but only 0.007 apart in luminance, so greyscale separation has to come from
   silhouette, and a triangle is the one outline no other class can be mistaken for.
   Drawn with clip-path, so the usual white ring is replaced by a white drop-shadow to
   keep it legible over dark map tiles. */
.pinwrap[data-tone="ny"] i{
  width:19px;height:17px;border:0;border-radius:0;background:var(--nycha);
  clip-path:polygon(50% 4%,96% 96%,4% 96%);
  box-shadow:none;
  filter:drop-shadow(0 0 1.5px var(--card)) drop-shadow(0 0 1.5px var(--card))
         drop-shadow(0 1.5px 2px rgba(0,0,0,.35));
}

/* ---------------------------------------------------------------- CLUSTERS
   KNOWN WCAG EXCEPTION, DELIBERATE, DO NOT "FIX" BY DARKENING THE ORANGE.

   White numerals on #FF7A29 are 2.60:1 and fail WCAG AA 1.4.3, which requires 4.5:1
   (or 3:1 at large-text sizes; 13.5px bold does not qualify). This is the one place
   in the app that fails, and it is a decision rather than an oversight -- the accent
   is meant to stay bright, and darkening #FF7A29 far enough for white text is what
   previously turned it brown.

   Mitigations, none of which make it conformant: the numerals are heavy (800), the
   figure is short, the disc is 42-48px, and the same counts are reachable as text in
   List view. If this ever needs to conform, change the numeral colour rather than
   the fill -- #5C2600 on #FF7A29 is 4.66:1. See DATA_DICTIONARY.md.

   Dark mode adds an outer dark outline so the white ring does not blow out against
   the slate ground. */
.marker-cluster{background:transparent}
.marker-cluster div{
  background:var(--cluster-bg);color:var(--cluster-fg);
  font-family:var(--sans);font-weight:800;font-size:13.5px;text-align:center;
  border:2.5px solid var(--cluster-ring);border-radius:50%;
  box-shadow:0 0 0 1.5px var(--cluster-outline), var(--sh-2);
  width:42px;height:42px;margin-left:-1px;margin-top:-1px;line-height:37px;
  font-variant-numeric:tabular-nums;letter-spacing:-.02em;
  transition:transform .18s var(--ease);
}
.marker-cluster-large div{width:48px;height:48px;line-height:43px;margin-left:-4px;
  margin-top:-4px;border-width:3px;font-size:14.5px}
.marker-cluster span{line-height:inherit}
.marker-cluster:active div{transform:scale(1.08)}

/* ============================================================ dock */
#dock{
  position:absolute;left:0;right:0;bottom:0;z-index:900;
  height:calc(var(--dock-h) + env(safe-area-inset-bottom,0px));
  padding:0 14px env(safe-area-inset-bottom,0px);
  display:flex;align-items:center;gap:10px;
  background:color-mix(in srgb,var(--card) 92%,transparent);
  -webkit-backdrop-filter:saturate(180%) blur(22px);
  backdrop-filter:saturate(180%) blur(22px);
  box-shadow:var(--sh-3);
}
.seg{display:flex;background:var(--card-2);border-radius:var(--r-pill);padding:4px;
  gap:4px;flex:none}
.seg button{min-width:70px;height:46px;padding:0 16px;border-radius:var(--r-pill);
  font-size:15px;font-weight:700;color:var(--ink-2)}
.seg button[aria-selected="true"]{background:var(--active);color:var(--on-active);
  box-shadow:var(--sh-1);letter-spacing:-.012em}
.dockbtn{
  height:46px;border-radius:var(--r-pill);padding:0 18px;display:flex;align-items:center;
  gap:9px;background:var(--card-2);font-size:15px;font-weight:700;flex:none;
}
.dockbtn svg{width:19px;height:19px;stroke:currentColor;fill:none;stroke-width:2;
  stroke-linecap:round;stroke-linejoin:round}
/* "Filters are on" is signalled by the tangerine dot on the count, not by inverting
   the whole control -- a quieter, more confident way to say the same thing. */
.dockbtn.hot{background:var(--card-2);color:var(--ink)}
/* KNOWN WCAG EXCEPTION #3, DELIBERATE -- the same 2.60:1 as the cluster numerals and
   the filter-sheet badges, and here it is a consistency call rather than a fresh one.
   This badge and .gbadge are the same element in two places; having one white and one
   burnt-umber read as a bug. Chosen knowingly: matching them means the app has one
   count-badge treatment, and the conformant variant is the one that got dropped.
   See DATA_DICTIONARY.md. To conform, set both back to var(--brand-ink) (4.66:1). */
.dockbtn .n{
  font-size:12px;font-weight:800;background:var(--brand);color:#FFFFFF;
  border-radius:var(--r-pill);min-width:22px;height:22px;display:grid;place-items:center;
  padding:0 6px;font-variant-numeric:tabular-nums;
}
.dockbtn.hot .n{background:var(--brand);color:#FFFFFF;
  box-shadow:0 0 0 3px color-mix(in srgb,var(--brand) 24%,transparent)}
#spacer{flex:1}

/* ============================================================ list */
#listpane{
  position:absolute;left:0;right:0;top:0;z-index:30;
  bottom:calc(var(--dock-h) + env(safe-area-inset-bottom,0px));
  background:var(--paper);overflow-y:auto;-webkit-overflow-scrolling:touch;
  padding:calc(env(safe-area-inset-top,0px) + 78px) 18px 28px;
}
.listhead{font-size:11px;letter-spacing:.13em;text-transform:uppercase;font-weight:700;
  color:var(--ink-3);padding:2px 4px 18px}
.row{
  width:100%;text-align:left;background:var(--card);border:0;
  border-radius:var(--r-card);padding:21px 20px;margin-bottom:14px;display:block;
  box-shadow:var(--sh-1);
}
.row:active{transform:scale(.988)}
.row h3{font-family:var(--disp);margin:0 0 5px;font-size:21px;font-weight:750;
  letter-spacing:-.028em;line-height:1.22}
.row .sub{font-size:14px;color:var(--ink-2);line-height:1.45;margin-bottom:14px}
.row .meta{display:flex;align-items:center;gap:8px;font-size:12.5px;font-weight:550;
  color:var(--ink-3);margin-top:12px;font-variant-numeric:tabular-nums}
.chips{display:flex;flex-wrap:wrap;gap:8px}

/* ============================================================ state indicators

   The old encoding was a check, a bang, a cross and a dash -- accurate, and it read
   like validation output rather than an app someone uses one-handed on a sidewalk.

   The redundancy those glyphs provided is not optional: under deuteranopia the four
   semantic colors sit within a few dE of each other, so hue alone cannot carry state.
   So the redundancy moved into SURFACE rather than being removed:

     has it              solid tinted fill, no outline, heaviest weight
     uncertain           tinted fill + dashed outline
     does not have it    no fill, solid outline
     not listed          no fill, dotted outline, lightest weight

   Fill presence, outline style and weight are three channels that survive greyscale
   and every form of color blindness. Each also carries its own subject icon, so the
   chip says what it is as well as what state it is in. */
.chip{
  display:inline-flex;align-items:center;gap:8px;font-size:13px;line-height:1;
  padding:11px 15px;border-radius:var(--r-pill);border:2px solid transparent;
  letter-spacing:-.008em;
}
.chip svg{width:17px;height:17px;flex:none;stroke:currentColor;fill:none;
  stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.chip.ok {background:var(--yes-bg);   color:var(--yes);   font-weight:800}
.chip.dis{background:var(--maybe-bg); color:var(--maybe); font-weight:700;
          border-color:var(--maybe);border-style:dashed}
.chip.bar{background:transparent;     color:var(--no);    font-weight:700;
          border-color:var(--no)}
.chip.non{background:transparent;     color:var(--none);  font-weight:600;
          border-color:var(--none);border-style:dotted;border-width:2px}
/* Not states -- record types. Filled like .ok so they read as labels rather than
   verdicts, in the colours that mark each type everywhere else. */
.chip.sy {background:var(--school-bg); color:var(--school);font-weight:700}
.chip.ny {background:var(--nycha-bg);  color:var(--nycha); font-weight:700}
.chip .dot{display:none}

/* Detail rows use the same four treatments at card scale. */
.f{display:flex;align-items:center;gap:13px;padding:16px 17px;
  border-radius:var(--r-tile);
  background:var(--card);box-shadow:var(--sh-1);min-height:60px;
  border:2px solid transparent;margin-bottom:11px}
.f svg{width:25px;height:25px;flex:none;stroke:currentColor;fill:none;
  stroke-width:1.8;stroke-linecap:round;stroke-linejoin:round}
.f span{font-size:15px;font-weight:750;line-height:1.38;color:var(--ink);
  letter-spacing:-.01em}
.f.ok {background:var(--yes-bg);   color:var(--yes);   box-shadow:none}
.f.dis{background:var(--maybe-bg); color:var(--maybe); box-shadow:none;
       border-color:var(--maybe);border-style:dashed}
.f.bar{background:transparent;     color:var(--no);    box-shadow:none;
       border-color:var(--no)}
.f.non{background:transparent;     color:var(--none);  box-shadow:none;
       border-color:var(--none);border-style:dotted}
.f.bar span,.f.non span{color:var(--ink-2);font-weight:650}
.f.sy {background:var(--school-bg); color:var(--school); box-shadow:none}
.f.ny {background:var(--nycha-bg);  color:var(--nycha);  box-shadow:none}
.f .mark{display:none}
.f.sprink{margin:0 0 16px}

/* The single access line on a NYCHA page. Deliberately not a panel and not a colour
   from the state ramp -- it is a quiet aside with a phone number, sitting in the flow
   at body scale. A tinted box here would re-create the thing it replaced. */
.accessnote{
  margin:0 0 20px;padding:0 0 0 15px;
  border-left:2px solid var(--line-2);
  font-size:14px;line-height:1.6;color:var(--ink-3);
}
.accessnote a{color:var(--link);font-weight:700;white-space:nowrap}

/* ============================================================ sheets */
.scrim{position:fixed;inset:0;z-index:1000;background:rgba(12,16,22,.42);
  opacity:0;pointer-events:none;transition:opacity .3s var(--ease);
  -webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px)}
.scrim.on{opacity:1;pointer-events:auto}
.scrim.light{background:rgba(10,14,20,.10)}
/* The detail sheet is capped shorter than the others so a usable strip of map stays
   visible above it -- otherwise the footprint overlay it describes is always hidden.
   Filters and About have no map to reveal, so they keep the taller cap. */
#detail{max-height:62vh}
.sheet{
  position:fixed;left:0;right:0;bottom:0;z-index:1010;
  background:var(--paper);border-radius:var(--r-sheet) var(--r-sheet) 0 0;
  box-shadow:var(--sh-3);
  transform:translateY(101%);transition:transform .42s var(--ease-sheet);
  max-height:93vh;display:flex;flex-direction:column;
}
.sheet.on{transform:translateY(0)}
.grab{padding:13px 0 5px;display:grid;place-items:center;flex:none;touch-action:none;
  cursor:grab}
.grab i{width:40px;height:5px;border-radius:var(--r-pill);background:var(--line-2);
  display:block}
.sheethead{display:flex;align-items:flex-start;gap:14px;padding:8px 22px 18px;flex:none}
.sheethead h2{
  font-family:var(--disp);margin:0;font-size:33px;font-weight:800;letter-spacing:-.038em;
  line-height:1.1;flex:1;text-wrap:balance;
}
.sheetbody{overflow-y:auto;-webkit-overflow-scrolling:touch;padding:4px 22px 22px;
  padding-bottom:calc(env(safe-area-inset-bottom,0px) + 32px)}
.closebtn{width:44px;height:44px;border-radius:var(--r-pill);background:var(--card-2);
  display:grid;place-items:center;flex:none}
.closebtn svg{width:18px;height:18px;stroke:var(--ink-2);stroke-width:2.4;fill:none;
  stroke-linecap:round}

/* ============================================================ detail */
.crossst{font-size:16px;color:var(--ink-2);line-height:1.5;margin:0 0 6px;
  letter-spacing:-.008em}
.boroline{font-size:11px;color:var(--ink-3);letter-spacing:.14em;font-weight:700;
  text-transform:uppercase;margin-bottom:24px}
section.blk{margin:0 0 28px}
section.blk > h4{margin:0 0 14px;font-size:11px;font-weight:700;
  letter-spacing:.14em;text-transform:uppercase;color:var(--ink-3)}
.blk h4 .sub{text-transform:none;letter-spacing:0;font-weight:500;color:var(--ink-3);
  font-size:13px;margin-left:8px}

.verdict{
  display:flex;gap:14px;align-items:flex-start;
  border-radius:var(--r-card);padding:20px;margin:0 0 18px;
  background:var(--card);box-shadow:var(--sh-1);
}
.verdict.ok {background:var(--yes-bg);box-shadow:none}
.verdict.dis{background:var(--maybe-bg);box-shadow:none}
.verdict.bar{background:var(--no-bg);box-shadow:none}
.verdict .ic{width:38px;height:38px;flex:none;display:grid;place-items:center;
  color:var(--ink-2)}
.verdict .ic svg{width:34px;height:34px;stroke:currentColor;fill:none;
  stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.verdict.ok .ic{color:var(--yes)}
.verdict.dis .ic{color:var(--maybe)}
.verdict.bar .ic{color:var(--no)}
.verdict h3{font-family:var(--disp);margin:0 0 6px;font-size:24px;font-weight:800;
  letter-spacing:-.034em;line-height:1.14}
.verdict p{margin:0;font-size:14.5px;line-height:1.55;color:var(--ink-2)}
.verdict.ok h3{color:var(--yes)}
.verdict.dis h3{color:var(--maybe)}
.verdict.bar h3{color:var(--no)}

.hours{display:flex;gap:13px;align-items:flex-start;border-radius:var(--r-card);
  padding:18px;margin:0 0 18px;background:var(--card);box-shadow:var(--sh-1)}
.hours svg{width:22px;height:22px;flex:none;margin-top:1px;stroke:var(--ink-2);
  fill:none;stroke-width:1.7;stroke-linecap:round;stroke-linejoin:round}
.hours .v{font-size:15px;font-weight:700;line-height:1.4;color:var(--ink)}
.hours .src{font-size:13px;color:var(--ink-3);margin-top:4px;line-height:1.45}

/* Single column. The labels have to name the accessibility meaning explicitly
   ("Transfer platform (wheelchair to equipment)"), which wraps to four cramped lines
   in a two-up grid. Clarity of the label wins over density. */
.feat{display:grid;grid-template-columns:1fr;gap:10px;margin:0 0 8px}
.feat.one{grid-template-columns:1fr}

.absent{margin-top:4px;font-size:13.5px;color:var(--ink-3);line-height:1.6;
  padding:15px 16px;border-radius:var(--r-tile);background:var(--card-2)}
.absent b{color:var(--ink-2);font-weight:700}
.taglist{display:flex;flex-wrap:wrap;gap:8px;margin-top:10px}
.tag{display:inline-flex;align-items:center;gap:8px;font-size:13px;font-weight:700;
  padding:11px 15px;border-radius:var(--r-pill);background:var(--yes-bg);color:var(--yes);
  letter-spacing:-.008em}
.tag svg{width:17px;height:17px;stroke:currentColor;fill:none;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}

.alert{background:var(--maybe-bg);border-radius:var(--r-card);padding:18px;margin:0 0 14px}
.alert .hl{font-family:var(--disp);margin:0 0 9px;font-size:19px;font-weight:750;
  letter-spacing:-.026em;color:var(--maybe)}
.alert .dline{border-left-color:var(--maybe)}
.dline{display:block;padding:11px 0 11px 14px;border-left:3px solid var(--maybe);
  font-size:14.5px;color:var(--ink);line-height:1.5}
.dline + .dline{border-top:1px dashed var(--line-2)}

.fact{border-radius:var(--r-card);padding:16px;background:var(--card);
  box-shadow:var(--sh-1);margin-bottom:12px}
.fact.lead{border-left:5px solid var(--line-2)}
.fact.lead.ok {border-left-color:var(--yes)}
.fact.lead.dis{border-left-color:var(--maybe);background:var(--maybe-bg);box-shadow:none}
.fact.lead.bar{border-left-color:var(--no)}
.fact.lead.non{border-left-color:var(--none)}
.fact .hl{font-family:var(--disp);font-size:18px;font-weight:750;line-height:1.3;
  margin:0 0 7px;letter-spacing:-.02em}
.fact p{margin:0;font-size:14.5px;color:var(--ink-2);line-height:1.55}
.fact p + p{margin-top:9px}
.fact a{color:var(--link);font-weight:700}
.note{font-size:13.5px;color:var(--ink-3);line-height:1.55;margin-top:12px;
  padding-top:12px;border-top:1px dashed var(--line)}
.prov{display:flex;flex-wrap:wrap;gap:7px;margin-top:12px;
  font-family:var(--mono);font-size:11px;color:var(--ink-3)}
.prov span{background:var(--card-2);border-radius:8px;padding:5px 8px;
  font-variant-numeric:tabular-nums}
.fpwrap{border-radius:var(--r-card);overflow:hidden;background:var(--card-2);
  margin-bottom:12px}
.fpwrap svg{display:block;width:100%;height:170px}
.fpcap{font-size:11.5px;color:var(--ink-3);padding:10px 14px}

details.why{margin-top:12px}
details.why summary{font-size:13.5px;color:var(--ink-2);cursor:pointer;font-weight:700;
  padding:8px 0;list-style:none;display:flex;align-items:center;gap:8px;min-height:44px}
details.why summary::-webkit-details-marker{display:none}
details.why summary::before{content:"\25B8";color:var(--ink-3);transition:transform .15s;
  display:inline-block}
details.why[open] summary::before{transform:rotate(90deg)}
details.why p{font-size:14px;color:var(--ink-2);line-height:1.55;margin:6px 0 0}

.prov-toggle{margin-top:14px;border-radius:var(--r-card);background:var(--card-2);
  overflow:hidden}
.prov-toggle summary{list-style:none;cursor:pointer;padding:20px 20px;min-height:60px;
  display:flex;align-items:center;gap:13px;font-size:15px;font-weight:700;
  letter-spacing:-.012em}
.prov-toggle summary::-webkit-details-marker{display:none}
.prov-toggle summary svg{width:20px;height:20px;stroke:var(--ink-3);fill:none;
  stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round;flex:none}
.prov-toggle summary .chev{margin-left:auto;transition:transform .24s var(--ease);
  color:var(--ink-3)}
.prov-toggle[open] summary .chev{transform:rotate(90deg)}
.prov-body{padding:0 20px 20px}
.prov-body h5{margin:20px 0 9px;font-size:10.5px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-3)}
.prov-body p{margin:0 0 11px;font-size:14px;line-height:1.6;color:var(--ink-2)}
.prov-body .kv{font-family:var(--mono);font-size:11.5px;color:var(--ink-2);
  line-height:1.65;background:var(--card);border-radius:12px;padding:11px 13px;
  margin-bottom:10px;word-break:break-word}
.prov-body .kv b{color:var(--ink);font-weight:700}

/* ============================================================ filters */
details.fgroup{border-radius:var(--r-card);margin-bottom:14px;background:var(--card);
  box-shadow:var(--sh-1);overflow:hidden}
details.fgroup summary{list-style:none;cursor:pointer;min-height:64px;padding:0 20px;
  display:flex;align-items:center;gap:12px;font-size:17.5px;font-weight:700}
details.fgroup summary::-webkit-details-marker{display:none}
details.fgroup summary .gt{flex:1;font-family:var(--disp);font-weight:650;
  letter-spacing:-.028em;color:var(--ink)}
/* KNOWN WCAG EXCEPTION #2, DELIBERATE -- see the cluster markers above and
   DATA_DICTIONARY.md. White numerals on #FF7A29 are 2.60:1 and fail 1.4.3. Accepted
   for the same reason: keeping the accent bright matters more here than the count,
   which is decoration on top of a section title that already names the filter, and
   which is restated in the dock and in the result count. If this ever has to conform,
   change the numeral to var(--brand-ink) (#5C2600, 4.66:1) -- do not darken --brand. */
details.fgroup summary .gbadge{
  font-size:12px;font-weight:800;background:var(--brand);color:#FFFFFF;
  border-radius:var(--r-pill);min-width:24px;height:24px;display:grid;place-items:center;
  padding:0 7px;font-variant-numeric:tabular-nums;
}
details.fgroup summary .gchev{display:grid;place-items:center;color:var(--ink-3);
  transition:transform .24s var(--ease)}
details.fgroup summary .gchev svg{width:20px;height:20px;stroke:currentColor;fill:none;
  stroke-width:2.2;stroke-linecap:round;stroke-linejoin:round}
details.fgroup[open] summary .gchev{transform:rotate(90deg)}
details.fgroup .gbody{padding:2px 20px 20px}
.fgroup h4{margin:0 0 10px;font-size:12px;font-weight:700;letter-spacing:.09em;
  text-transform:uppercase;color:var(--ink-3)}
.fgroup .hint{font-size:13px;color:var(--ink-3);margin:10px 0 0;line-height:1.5}
.fgroup .rarenote{margin:10px 0 0;font-size:13px;color:var(--maybe);line-height:1.5;
  display:flex;gap:8px;align-items:flex-start}
.fgroup .rarenote svg{width:16px;height:16px;flex:none;margin-top:2px;
  stroke:currentColor;fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.allof{font-size:13px;color:var(--ink-3);margin:16px 0 10px;line-height:1.55}
.opts{display:flex;flex-wrap:wrap;gap:10px}
.opt{
  min-height:48px;padding:12px 17px;border-radius:var(--r-pill);
  background:var(--card-2);font-size:14.5px;font-weight:700;color:var(--ink-2);
  display:inline-flex;align-items:center;gap:10px;white-space:nowrap;position:relative;
  letter-spacing:-.01em;
}
.opt .lab{white-space:nowrap}
.opt svg{width:19px;height:19px;flex:none;stroke:currentColor;fill:none;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}
.opt .sw{width:11px;height:11px;border-radius:50%;flex:none}
.opt[aria-pressed="true"]{background:var(--active);color:var(--on-active);
  box-shadow:var(--sh-1)}
.opt .cnt{font-size:12px;font-weight:800;background:var(--card);color:var(--ink-3);
  border-radius:var(--r-pill);padding:3px 9px;font-variant-numeric:tabular-nums}
.opt.rare .cnt{background:var(--maybe-bg);color:var(--maybe)}
.opt[aria-pressed="true"] .cnt,
.opt[aria-pressed="true"].rare .cnt{background:var(--on-active);color:var(--active)}
.opt.zero{opacity:.4}
.filterbar{display:flex;gap:12px;
  padding:16px 22px calc(env(safe-area-inset-bottom,0px) + 20px);
  flex:none;background:var(--paper);box-shadow:var(--sh-3)}
.btn{flex:1;min-height:58px;border-radius:var(--r-pill);font-size:16px;font-weight:800;
  display:grid;place-items:center;letter-spacing:-.016em;text-align:center;
  padding:0 14px;line-height:1.25}
.btn .sub{font-weight:500;font-size:13px;opacity:.85;margin-left:7px}
/* The action colour, not the accent. Ink blue holds white text at 9.05:1 in light and
   4.76:1 in dark, which no tangerine can do at any lightness that still reads orange. */
.btn.primary{background:var(--active);color:var(--on-active);box-shadow:var(--sh-2)}
.btn.primary:active{transform:scale(.985)}
.btn.primary[data-empty="true"]{background:var(--card-2);color:var(--ink-3);box-shadow:none}
/* Deliberately quiet. Clearing filters is a recovery action, not a destination. */
.btn.ghost{background:var(--card-2);color:var(--ink-2);font-weight:700}
.btn.ghost:disabled{opacity:.42;cursor:default}

.scarce{display:flex;gap:11px;align-items:flex-start;background:var(--maybe-bg);
  border-radius:var(--r-card);padding:15px;margin:0 0 14px}
.scarce svg{width:20px;height:20px;flex:none;margin-top:1px;stroke:var(--maybe);
  fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.scarce p{margin:0;font-size:14px;line-height:1.5;color:var(--ink)}
.scarce b{font-weight:800}

/* ============================================================ about */
.about h3{font-family:var(--disp);margin:36px 0 12px;font-size:23px;font-weight:800;
  letter-spacing:-.034em;color:var(--ink)}
.about h3:first-child{margin-top:0}
.about p{margin:0 0 14px;font-size:15.5px;line-height:1.7;color:var(--ink-2);
  letter-spacing:-.006em}
.about strong{color:var(--link);font-weight:700}
.about a{color:var(--link);font-weight:700}
.srctable{width:100%;border-collapse:collapse;margin:10px 0 16px;font-size:13.5px}
.srctable th,.srctable td{text-align:left;padding:12px 10px;
  border-bottom:1px solid var(--line);vertical-align:top}
.srctable th{font-size:11px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--ink-3);font-weight:700}
.srctable td.m{font-family:var(--mono);font-size:11.5px;color:var(--ink-2);white-space:nowrap}
.callout{background:var(--card-2);border-radius:var(--r-card);padding:20px;margin:0 0 20px}
.callout p{margin:0;font-size:14.5px;line-height:1.6;color:var(--ink)}
.callout p + p{margin-top:11px}

.empty{text-align:center;padding:64px 24px;color:var(--ink-3)}
.empty b{display:block;font-family:var(--disp);font-size:23px;font-weight:800;
  letter-spacing:-.032em;color:var(--ink);margin-bottom:9px}
#boot{position:absolute;inset:0;z-index:990;display:grid;place-items:center;
  background:var(--paper);gap:12px;text-align:center;padding:28px}
#boot p{margin:0;color:var(--ink-3);font-size:14.5px}
#boot b{display:block;font-family:var(--disp);font-size:29px;font-weight:800;
  letter-spacing:-.042em;color:var(--brand-text);margin-bottom:6px;
  text-transform:lowercase}

@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}

/* The dock has to hold four controls at 375px. Let the labels compress before the
   row does, rather than clipping "Filters" off the edge. */
@media (max-width:400px){
  #dock{gap:7px;padding:0 11px env(safe-area-inset-bottom,0px)}
  .seg button{min-width:58px;padding:0 11px;font-size:14px}
  .dockbtn{padding:0 13px;font-size:14px;gap:7px}
  .dockbtn svg{width:18px;height:18px}
}

/* list row: name and distance on one line, so the eye lands on both at once */
.rowtop{display:flex;align-items:baseline;gap:12px;margin-bottom:6px}
.rowtop h3{margin:0;flex:1;min-width:0}
.rowtop .dist{
  font-size:13px;font-weight:750;color:var(--ink-3);flex:none;
  font-variant-numeric:tabular-nums;white-space:nowrap;
}

/* ============================================================ location explainer */
.geolead{font-family:var(--disp);font-size:19px;font-weight:700;line-height:1.4;
  letter-spacing:-.022em;color:var(--ink);margin:0 0 14px}
.geonote{font-size:14.5px;line-height:1.65;color:var(--ink-2);margin:0}
#geo{max-height:auto}

/* ============================================================ feedback + footers */
.sheetfoot{margin:18px 0 0;text-align:center}
.reportlink{
  display:inline-flex;align-items:center;min-height:44px;padding:0 14px;
  font-size:13.5px;font-weight:650;color:var(--ink-3);text-decoration:underline;
  text-underline-offset:3px;text-decoration-color:var(--line-2);
}
.reportlink:hover{color:var(--ink-2)}
.listfoot{
  display:flex;align-items:center;justify-content:center;gap:4px;flex-wrap:wrap;
  padding:22px 0 6px;font-size:13px;color:var(--ink-3);
}
.listfoot a{
  display:inline-flex;align-items:center;min-height:44px;padding:0 10px;
  color:var(--ink-3);font-weight:650;text-decoration:underline;
  text-underline-offset:3px;text-decoration-color:var(--line-2);
}
.aboutfoot{
  margin:26px 0 0;padding-top:18px;border-top:1px solid var(--line);
  font-size:13px;text-align:center;
}
.aboutfoot a{display:inline-flex;align-items:center;min-height:44px;padding:0 12px;
  color:var(--ink-3);font-weight:650}

/* ============================================================ standalone doc page
   privacy.html shares the token system and nothing else, so it stays a single
   readable column with no app chrome. */
body.docpage{overflow:auto;background:var(--paper)}
.doc{max-width:660px;margin:0 auto;
  padding:calc(env(safe-area-inset-top,0px) + 26px) 22px
          calc(env(safe-area-inset-bottom,0px) + 48px)}
.docback{margin:0 0 26px;font-size:14px}
.docback a,.docfoot a{color:var(--link);font-weight:700}
.doc h1{font-family:var(--disp);font-size:38px;font-weight:800;letter-spacing:-.04em;
  line-height:1.06;margin:0 0 16px;color:var(--ink)}
.doc h2{font-family:var(--disp);font-size:22px;font-weight:750;letter-spacing:-.03em;
  line-height:1.2;margin:38px 0 12px;color:var(--ink)}
.doclead{font-size:17px;line-height:1.65;color:var(--ink-2);margin:0 0 8px;
  letter-spacing:-.008em}
.doc p{font-size:15.5px;line-height:1.7;color:var(--ink-2);margin:0 0 14px;
  letter-spacing:-.006em}
.doc strong{color:var(--ink);font-weight:750}
.doc a{color:var(--link);font-weight:650}
.doc ul{margin:0 0 16px;padding-left:22px}
.doc li{font-size:15.5px;line-height:1.7;color:var(--ink-2);margin-bottom:8px}
.docfoot{margin-top:44px;padding-top:20px;border-top:1px solid var(--line);
  font-size:13.5px;color:var(--ink-3)}

/* ============================================================ install prompt
   A bar, not a dialog: it sits above the dock, takes no focus when it appears, and
   leaves the map fully usable underneath. It lives inside #app, so when a sheet opens
   it inherits `inert` along with the rest of the background and cannot be tabbed into
   from behind a modal. */
#install{
  position:absolute;left:14px;right:14px;z-index:850;
  bottom:calc(var(--dock-h) + env(safe-area-inset-bottom,0px) + 14px);
  display:flex;align-items:center;gap:10px;
  padding:12px 12px 12px 16px;border-radius:var(--r-card);
  background:color-mix(in srgb,var(--card) 94%,transparent);
  -webkit-backdrop-filter:saturate(180%) blur(20px);
  backdrop-filter:saturate(180%) blur(20px);
  box-shadow:var(--sh-2);
  opacity:0;transform:translateY(10px);
  transition:opacity .32s var(--ease), transform .32s var(--ease);
}
#install.on{opacity:1;transform:translateY(0)}
.ib-body{display:flex;align-items:center;gap:12px;flex:1;min-width:0}
/* The glyph is decorative -- it is aria-hidden and the adjacent sentence carries the
   whole meaning -- so WCAG 1.4.11 does not apply to it. But tangerine on white is
   2.60:1 and reads faint rather than deliberate, so it sits on a tinted disc. That is
   a presentation fix, not a conformance one; the accent itself is unchanged. */
.ib-icon{flex:none;width:38px;height:38px;display:grid;place-items:center;
  border-radius:12px;background:color-mix(in srgb,var(--brand) 14%,transparent)}
.ib-icon svg{width:22px;height:22px;stroke:var(--brand);fill:none;stroke-width:2.1;
  stroke-linecap:round;stroke-linejoin:round}
.ib-text{margin:0;font-size:13.5px;line-height:1.4;color:var(--ink-2);
  letter-spacing:-.006em}
.ib-text b{color:var(--ink);font-weight:750}
/* The iOS share glyph, drawn inline so it sits on the text baseline mid-sentence. */
.shareglyph{width:17px;height:17px;vertical-align:-3px;margin:0 1px;
  stroke:var(--ink);fill:none;stroke-width:1.9;stroke-linecap:round;stroke-linejoin:round}
.ib-actions{display:flex;align-items:center;gap:4px;flex:none}
.ib-add{
  min-height:44px;padding:0 18px;border-radius:var(--r-pill);
  background:var(--active);color:var(--on-active);
  font-size:14.5px;font-weight:750;letter-spacing:-.012em;
}
.ib-close{width:44px;height:44px;border-radius:var(--r-pill);display:grid;
  place-items:center;flex:none}
.ib-close svg{width:17px;height:17px;stroke:var(--ink-3);fill:none;stroke-width:2.2;
  stroke-linecap:round}
.ib-close:hover svg{stroke:var(--ink-2)}
@media (max-width:400px){
  #install{left:10px;right:10px;padding-left:14px}
  .ib-text{font-size:13px}
  .ib-add{padding:0 14px}
}

/* While the install bar is up, the legend and toast move above it. The bar's height
   is measured and published as --install-h because the iOS copy wraps to two lines. */
body.installbar #legend,
body.installbar #toast{
  bottom:calc(var(--dock-h) + env(safe-area-inset-bottom,0px)
              + var(--install-h, 68px) + 26px);
}
#legend,#toast{transition:bottom .32s var(--ease)}

/* ============================================================ contribution
   Ergonomics first. A parent doing this is one-handed, watching a child, and has
   about thirty seconds. Every target here is well past 44px, the primary choices sit
   in the lower half of the screen where a thumb reaches, and nothing needs a second
   confirmation screen. */

/* ---- call to action on the detail page ---- */
.ctcta{margin-top:6px}
.ctprompt{
  display:flex;align-items:center;gap:13px;width:100%;text-align:left;
  min-height:60px;padding:15px 17px;margin:0 0 11px;
  border-radius:var(--r-tile);background:var(--brand-tint,var(--card-2));
  box-shadow:inset 0 0 0 2px var(--brand);
  font-size:15px;line-height:1.35;color:var(--ink);
}
.ctprompt b{font-weight:800}
.ctpin{flex:none;width:26px;height:26px;display:grid;place-items:center}
.ctpin svg{width:24px;height:24px;stroke:var(--brand);fill:none;stroke-width:1.9;
  stroke-linecap:round;stroke-linejoin:round}
.ctadd{
  display:flex;align-items:center;justify-content:center;gap:10px;width:100%;
  min-height:56px;border-radius:var(--r-pill);
  background:var(--active);color:var(--on-active);
  font-size:16px;font-weight:800;letter-spacing:-.014em;box-shadow:var(--sh-1);
}
.ctadd svg{width:20px;height:20px;stroke:currentColor;fill:none;stroke-width:2.4;
  stroke-linecap:round}
.ctnote{margin:11px 2px 0;font-size:13px;line-height:1.55;color:var(--ink-3)}

/* ---- one tap ---- */
.ctonsitebadge{
  display:inline-block;margin:0 0 16px;padding:8px 14px;border-radius:var(--r-pill);
  background:var(--yes-bg);color:var(--yes);font-size:13px;font-weight:750;
}
.ctchoices{display:flex;flex-direction:column;gap:12px;padding-bottom:6px}
.ctbig{
  width:100%;min-height:76px;border-radius:var(--r-tile);
  font-family:var(--disp);font-size:21px;font-weight:750;letter-spacing:-.02em;
  border:2px solid transparent;
}
.ctbig.wide{min-height:84px;font-size:19px;line-height:1.3;padding:0 18px}
.ctbig.yes{background:var(--yes-bg);color:var(--yes)}
.ctbig.no{background:transparent;color:var(--no);border-color:var(--no)}
.ctbig.unsure{background:var(--card-2);color:var(--ink-2)}
.ctbig + .ctbig{margin-top:0}
#ctfConfirm .ctbig{margin-bottom:12px}
.ctdone{text-align:center;padding:8px 0 4px}
.ctdonemsg{
  font-family:var(--disp);font-size:22px;font-weight:750;letter-spacing:-.026em;
  line-height:1.3;color:var(--ink);margin:8px 0 20px;
}
.ctmore{
  width:100%;min-height:60px;border-radius:var(--r-pill);
  background:var(--active);color:var(--on-active);
  font-size:16px;font-weight:800;letter-spacing:-.014em;margin-bottom:10px;
}
.ctskip{
  width:100%;min-height:52px;border-radius:var(--r-pill);
  background:transparent;color:var(--ink-3);font-size:15px;font-weight:700;
}

/* ---- full pass grid ----
   Two columns at phone width with 64px rows: comfortably past the 44px minimum, and
   large enough to hit without looking. */
.ctcat{
  margin:6px 0 12px;font-size:11px;font-weight:700;letter-spacing:.14em;
  text-transform:uppercase;color:var(--ink-3);
}
.ctgrid{display:grid;grid-template-columns:1fr 1fr;gap:10px;margin-bottom:24px}
.ctchip{
  min-height:64px;padding:10px 12px;border-radius:var(--r-tile);
  background:var(--card-2);color:var(--ink-2);
  font-size:14.5px;font-weight:700;line-height:1.25;letter-spacing:-.01em;
  border:2px solid transparent;text-align:center;
}
.ctchip.on{
  background:var(--active);color:var(--on-active);box-shadow:var(--sh-1);
  font-weight:800;
}
/* Selection must survive greyscale and colour blindness, so it is not carried by the
   fill alone: a tick is prepended and the weight steps up. */
.ctchip.on::before{content:"\2713\00a0";font-weight:900}
.ctrestored{
  margin:0 0 14px;padding:11px 14px;border-radius:var(--r-tile);
  background:var(--card-2);color:var(--ink-2);font-size:13.5px;line-height:1.5;
}
.ctcount{
  flex:1;align-self:center;font-size:14px;font-weight:700;color:var(--ink-3);
  font-variant-numeric:tabular-nums;
}
#ctfBar .btn{flex:0 0 auto;min-width:132px}
.ctconfirmlead{
  font-family:var(--disp);font-size:22px;font-weight:750;letter-spacing:-.026em;
  margin:4px 0 10px;color:var(--ink);
}
.ctconfirmnote{font-size:14.5px;line-height:1.6;color:var(--ink-2);margin:0 0 20px}
.ctconfirmnote em{font-style:normal;font-weight:800;color:var(--ink)}

/* ---- contributed data on the detail page ----
   Deliberately unlike the city-data blocks: a bordered panel rather than the solid
   tiles used for Parks fields, so nobody mistakes a parent report for a city record. */
.ctsection{margin-top:26px}
.ctlist{
  border:2px dashed var(--line-2);border-radius:var(--r-card);overflow:hidden;
}
.ctrow{
  display:flex;align-items:center;justify-content:space-between;gap:12px;
  padding:14px 16px;font-size:15px;
}
.ctrow + .ctrow{border-top:1px solid var(--line)}
.ctlabel{font-weight:750;color:var(--ink)}
.ctverdict{font-size:13px;font-weight:700;text-align:right;flex:none}
.ctrow.ok  .ctverdict{color:var(--yes)}
.ctrow.non .ctverdict{color:var(--ink-3)}
.ctrow.dis .ctverdict{color:var(--maybe)}
.ctrow.dis{background:var(--maybe-bg)}
.ctonsite{
  display:inline-block;margin-left:7px;padding:2px 7px;border-radius:var(--r-pill);
  background:var(--yes-bg);color:var(--yes);font-size:11px;font-weight:800;
}

/* ============================================================ admin
   Utilitarian on purpose. It exists so one person can tell, fast, whether a report
   is plausible before it becomes public. */
.admin{max-width:820px}
.adminbox{max-width:380px;margin-top:8px}
.adminlabel{display:block;font-size:12px;font-weight:700;letter-spacing:.1em;
  text-transform:uppercase;color:var(--ink-3);margin:14px 0 6px}
.admininput{
  width:100%;min-height:52px;padding:0 16px;border-radius:var(--r-tile);
  border:2px solid var(--line-2);background:var(--card);color:var(--ink);
  font:inherit;font-size:16px;
}
.admininput:focus-visible{outline:2.5px solid var(--focus);outline-offset:2px}
.adminbox .btn{width:100%;margin-top:20px}
.adminerr{margin-top:14px;color:var(--no);font-size:14px;font-weight:700}
.adminbar{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-bottom:16px}
.admintabs{display:flex;gap:6px;background:var(--card-2);border-radius:var(--r-pill);
  padding:4px;flex:1}
.admintab{min-height:44px;padding:0 16px;border-radius:var(--r-pill);
  font-size:14.5px;font-weight:700;color:var(--ink-2)}
.admintab[aria-selected="true"]{background:var(--active);color:var(--on-active)}
.adminbar .btn{flex:0 0 auto;min-height:44px;min-width:120px}
.adminmeta{font-size:13px;color:var(--ink-3);margin:0 0 14px;
  letter-spacing:.06em;text-transform:uppercase;font-weight:700}
.acard{background:var(--card);border-radius:var(--r-card);padding:18px;
  margin-bottom:14px;box-shadow:var(--sh-1)}
.ahead{display:flex;justify-content:space-between;align-items:baseline;gap:12px;
  flex-wrap:wrap;margin-bottom:6px}
.ahead b{font-family:var(--disp);font-size:16px;font-weight:750;letter-spacing:-.02em}
.ahead time{font-size:12.5px;color:var(--ink-3);font-variant-numeric:tabular-nums}
.atag{display:inline-block;margin-left:8px;padding:3px 9px;border-radius:var(--r-pill);
  background:var(--card-2);color:var(--ink-3);font-size:11.5px;font-weight:800}
.atag.on{background:var(--yes-bg);color:var(--yes)}
.adevice{font-size:12.5px;color:var(--ink-3);margin:0 0 12px;line-height:1.45}
/* Codes stay monospaced because they get copied into SQL; the prose around them does
   not, so the line stops wrapping to three lines of typewriter text. */
.adevice code{font-family:var(--mono);font-size:11.5px;color:var(--ink-2)}
.achips{display:flex;flex-wrap:wrap;gap:7px;align-items:center;margin-bottom:9px}
.alabel{font-size:11px;font-weight:800;letter-spacing:.1em;text-transform:uppercase;
  color:var(--ink-3);min-width:52px}
.achip{padding:6px 11px;border-radius:var(--r-pill);font-size:13px;font-weight:700}
.achip.y{background:var(--yes-bg);color:var(--yes)}
.achip.n{background:transparent;color:var(--no);box-shadow:inset 0 0 0 2px var(--no)}
.achip.u{background:var(--card-2);color:var(--ink-3)}
.aactions{display:flex;gap:10px;margin-top:14px;flex-wrap:wrap}
.aactions .btn{flex:0 0 auto;min-width:130px;min-height:48px;font-size:15px}

/* Inline phone links inside prose (NYC Parks Accessibility, the NYCHA contact centre)
   sit mid-sentence at ~18px tall. Growing the line-height to 44px would wreck the
   paragraph, so the hit area is grown instead -- same technique as the map
   attribution links, layout untouched. */
.sheetbody a[href^="tel:"],
.doc a[href^="tel:"]{position:relative;display:inline-block}
.sheetbody a[href^="tel:"]::after,
.doc a[href^="tel:"]::after{
  content:"";position:absolute;left:-8px;right:-8px;top:50%;
  height:44px;transform:translateY(-50%);
}
