
/* vietnamese */ @font-face {font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 400; font-display: swap; src: url(/static/fonts/ibm-plex-mono-vietnamese-400.woff2) format('woff2'); unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;}
/* latin-ext */ @font-face {font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 400; font-display: swap; src: url(/static/fonts/ibm-plex-mono-latin-ext-400.woff2) format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
/* latin */ @font-face {font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 400; font-display: swap; src: url(/static/fonts/ibm-plex-mono-latin-400.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}
/* vietnamese */ @font-face {font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 500; font-display: swap; src: url(/static/fonts/ibm-plex-mono-vietnamese-500.woff2) format('woff2'); unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;}
/* latin-ext */ @font-face {font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 500; font-display: swap; src: url(/static/fonts/ibm-plex-mono-latin-ext-500.woff2) format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
/* latin */ @font-face {font-family: 'IBM Plex Mono'; font-style: normal; font-weight: 500; font-display: swap; src: url(/static/fonts/ibm-plex-mono-latin-500.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}
/* vietnamese */ @font-face {font-family: 'Nunito'; font-style: normal; font-weight: 400 900; font-display: swap; src: url(/static/fonts/nunito-vietnamese.woff2) format('woff2'); unicode-range: U+0102-0103, U+0110-0111, U+0128-0129, U+0168-0169, U+01A0-01A1, U+01AF-01B0, U+0300-0301, U+0303-0304, U+0308-0309, U+0323, U+0329, U+1EA0-1EF9, U+20AB;}
/* latin-ext */ @font-face {font-family: 'Nunito'; font-style: normal; font-weight: 400 900; font-display: swap; src: url(/static/fonts/nunito-latin-ext.woff2) format('woff2'); unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;}
/* latin */ @font-face {font-family: 'Nunito'; font-style: normal; font-weight: 400 900; font-display: swap; src: url(/static/fonts/nunito-latin.woff2) format('woff2'); unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;}

*,*::before,*::after{box-sizing:border-box}
body{margin:0;background:#FFFFFF;color:#1E293B;font-family:Nunito, system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;font-size:16px;
     line-height:1.5;-webkit-text-size-adjust:100%}
a{color:#2563EB} a:hover{color:#163A69}
.wrap{max-width:640px;margin:0 auto;padding:20px 20px 40px;display:flex;flex-direction:column;gap:14px}

/* The trail along the top. Small, quiet, and the first thing in the document so a
   keyboard or screen-reader user reaches it before the page's content. Scrolls
   sideways rather than wrapping into two ragged lines on a narrow phone. */
/* The map. Drawn from coordinates already on the cards -- no tiles, no third
   party, nothing loaded. See render/mapview.py for why that is not a shortcut. */
.map{width:100%;border-radius:12px;background:#E4EEFF;
  border:1px solid #CBDDFA;display:block;overflow:hidden}
/* SCOPED TO THE FALLBACK. As `.map svg` this also hit Leaflet's overlay SVG, which
   is absolutely positioned and sized by the library: `height:auto` collapsed it to
   zero and clipped every marker. The circles were there, correctly positioned, and
   invisible -- which looks exactly like "the map has no data". */
.mapfallback svg{width:100%;height:auto;display:block}
/* Only once Leaflet has taken over: before that the box is sized by the SVG inside
   it, so a fixed height would leave a grey slab if the script never arrives. */
.map.live{height:340px}
.map .leaflet-container{background:#E4EEFF;font-family:Nunito, system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif}
/* OSM's standard tiles are saturated -- greens, pinks, yellows -- and they competed
   with the page for attention. Greyscale and lifted toward white, the map reads as
   ground; the only colour left on it is our own pins. */
.map .leaflet-tile-pane{filter:grayscale(1) brightness(1.06) contrast(.88)}
/* The pin colour, as a variable so the script can read it rather than hard-coding
   the palette in two languages. */
/* The pin you are standing on is NAVIGATION, not a status. It was WARN_EDGE, which
   is the colour this product uses for "there is an open violation" -- so every
   building page highlighted itself in the warning colour and the map said something
   about the building that the records had not. Blue with a light-blue ring: the
   site's own colour, larger, never a status colour and never dark. */
:root{--pin:#2563EB;--pin-here:#2563EB;--pin-ring:#BFDBFE}
/* Zoomed out past the point where individual buildings mean anything, the map says
   so instead of showing an empty grey rectangle that looks like a loading failure. */
.map.zoomedout::after{content:"Zoom in to see indexed buildings";position:absolute;
  left:50%;top:12px;transform:translateX(-50%);font-size:11px;font-weight:600;
  color:#55637A;background:#FFFFFF;border:1px solid #CBDDFA;border-radius:12px;
  padding:4px 10px;pointer-events:none;z-index:500}
.map.live{position:relative}
/* The tooltip sits ON THE MAP, and the map is always light -- OSM standard tiles do
   not have a dark variant. So this must NOT follow the page's colour scheme: the
   dark-mode rule that used to pair with it rendered a near-black label on a pale
   street map, which was the one piece of text on the page nobody could read. Fixed
   values, no theme block. */
.map .leaflet-tooltip{font-family:Nunito, system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;font-size:12px;font-weight:600;
  color:#1E293B;background:#FFFFFF;border:1px solid rgba(16,32,64,.18);
  box-shadow:0 2px 8px rgba(16,32,64,.22);border-radius:7px;padding:3px 8px}
.map .leaflet-tooltip::before{border-top-color:#FFFFFF}
.map .leaflet-tooltip.here{font-weight:800;border-color:#1E293B;
  box-shadow:0 2px 10px rgba(16,32,64,.3)}
/* On a building page the map is a locator strip, not a browser. */
.map.locator.live{height:190px}

/* A BUILDING PAGE IS A MAP WITH A PAGE OVER IT, the way a listings app is: the map
   fills the screen behind, the page is a white sheet that rises from the bottom on a
   phone and docks on the right on a wide screen (the 900px block below), and the
   product name is a bar across the top. `:has` scopes all of it to building pages --
   the only ones with a locator -- without threading a flag through `document()`.

   z-index -1 and no z-index on the sheet: the map is a CHILD of `.wrap`, so a sheet
   with its own z-index made a stacking context that put the map ABOVE the sheet's
   text and the whole page answered clicks as the map. At -1 in the root context it
   paints under every block, and pointer-events lets clicks outside the sheet fall
   through the empty body to it. */
.map.locator.live{position:fixed;inset:0;height:auto;border:0;border-radius:0;z-index:-1;
  pointer-events:auto}
body:has(.map.locator){pointer-events:none}
.wrap:has(.map.locator){pointer-events:auto;position:relative;background:#FFFFFF;
  margin-top:44vh;border-radius:16px 16px 0 0;box-shadow:0 -6px 30px rgba(16,32,64,.16);
  max-width:none;min-height:56vh;box-sizing:border-box}
.wrap:has(.map.locator) .crumbs{position:fixed;top:0;left:0;right:0;height:52px;z-index:1000;
  padding:0 16px;background:#FFFFFF;border-bottom:1px solid #CBDDFA;box-sizing:border-box;
  pointer-events:auto}
/* Close the page and keep exploring the map; the building's own pin brings it back.
   Hidden until map.js runs, so a page without the script has no dead control. */
.dockx{position:absolute;top:10px;right:10px;width:44px;height:44px;border-radius:22px;
  border:0;background:#FFFFFF;color:#1E293B;display:flex;align-items:center;justify-content:center;
  cursor:pointer}
.dockx:hover{background:#E4EEFF}
.dockx[hidden]{display:none}
/* THE HOME PAGE IS THE MAP, with the search box floating over it -- the same idea as a
   building page, minus the sheet. Same fixed map, same click-through, same top bar. */
.map.homemap.live{position:fixed;inset:0;height:auto;border:0;border-radius:0;z-index:-1;
  pointer-events:auto}
body:has(.map.homemap){pointer-events:none}
.wrap:has(.map.homemap){max-width:640px;margin:0 auto;padding:68px 16px 16px;gap:0}
.wrap:has(.map.homemap) .crumbs{position:fixed;top:0;left:0;right:0;height:52px;z-index:1000;
  padding:0 16px;background:#FFFFFF;border-bottom:1px solid #CBDDFA;box-sizing:border-box;
  pointer-events:auto}
.homepanel{pointer-events:auto;background:#FFFFFF;border-radius:16px;padding:12px 14px 14px;
  display:flex;flex-direction:column;gap:8px;box-shadow:0 8px 30px rgba(16,32,64,.18)}
.homepanel .sub{margin:0;font-size:13px}
.map.homemap.zoomedout::after{top:auto;bottom:28px}
.map.homemap .leaflet-top.leaflet-left{top:auto;bottom:24px}
.wrap:has(.map.locator) .idblock{padding-top:34px}
.eyebrow.sheetcrumb{text-transform:none;letter-spacing:0;font-size:13px}
.sheetcrumb a{color:#2563EB;font-weight:600;text-decoration:none}
.sheetcrumb a:hover{text-decoration:underline}
.map.locator .leaflet-top{top:52px}
body.mapmode .wrap:has(.map.locator){background:none;box-shadow:none;pointer-events:none}
body.mapmode .wrap:has(.map.locator) > :not(.map):not(.crumbs){display:none}

/* A record's raw fields, under a disclosure. Label left, value right, so a column
   of them scans as a table without being one -- a <table> here would need headers
   that say nothing ("Field", "Value") and would not wrap on a phone. */
/* A record headline. Topics are short by construction, but a permit's job
   description is whatever DOB NOW was given, so the size steps down rather than
   letting one long string own the screen. */
/* The city's order, verbatim. HPD writes in capitals, so the type does the work
   the text cannot be made to do: smaller, looser tracking, and a rule down the side
   marking it as a quotation rather than our own words. */
.order{margin:0;font-size:13px;line-height:1.6;letter-spacing:.015em;color:#1E293B;
  border-left:2px solid #CBDDFA;padding-left:12px}

h1.rechead{margin:0;font-family:Nunito, system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;font-weight:800;letter-spacing:-0.015em;
  font-size:30px;line-height:1.15}
h1.rechead.mid{font-size:24px;line-height:1.2;letter-spacing:-0.01em}
h1.rechead.long{font-size:19px;line-height:1.3;letter-spacing:0;font-weight:700}
.card .src{margin:6px 0 0}

.kvs{margin:0;display:flex;flex-direction:column}
.kv{display:flex;gap:12px;justify-content:space-between;padding:7px 0;align-items:baseline}
.kv + .kv{border-top:1px solid #CBDDFA}
.kv dt{font-size:12px;color:#55637A;flex:0 0 auto}
.kv dd{margin:0;font-size:13px;font-weight:600;text-align:right;word-break:break-word;
  max-width:60%}
details.card > summary{cursor:pointer;list-style:none;display:flex;
  align-items:center;justify-content:space-between;gap:8px;min-height:32px}
details.card > summary::-webkit-details-marker{display:none}
details.card > summary::after{content:"\25BE";color:#55637A;font-size:11px}
details.card[open] > summary::after{content:"\25B4"}

.crumbs{display:flex;align-items:center;gap:6px;font-size:12px;color:#55637A;
  overflow-x:auto;white-space:nowrap;scrollbar-width:none;-webkit-overflow-scrolling:touch;
  padding-bottom:2px}
.crumbs::-webkit-scrollbar{display:none}
.crumbs a{color:#55637A;text-decoration:none;font-weight:600;
  min-height:32px;display:inline-flex;align-items:center}
/* The wordmark IS the logo -- there is no icon. Values taken from the CAD product's
   `.logo` rule (architectural-intelligence-ui/src/styles.css) so the two surfaces
   read as one company; change them together or not at all. */
.crumbs a.logo{font-size:17px;font-weight:900;letter-spacing:-0.01em;color:#2563EB;
  text-transform:lowercase}
.crumbs a.logo:hover{text-decoration:none;color:#163A69}
.crumbs a:hover,.crumbs a:focus-visible{color:#2563EB;text-decoration:underline}
.crumbs [aria-current="page"]{color:#1E293B;font-weight:600}
.crumbs .sep{color:#CBDDFA}

.topbar{display:flex;align-items:center;justify-content:space-between;gap:12px}
.url{font-family:'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;font-size:12px;color:#55637A;overflow:hidden;
      text-overflow:ellipsis;white-space:nowrap}
.pill{min-height:44px;padding:0 16px;border-radius:22px;border:1px solid #2563EB;
       background:#2563EB;color:#FFFFFF;font-family:Nunito, system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;font-size:14px;font-weight:600;
       cursor:pointer;display:inline-flex;align-items:center;text-decoration:none}
/* `display:inline-flex` above overrides the browser's own rule for [hidden], so a
   button shipped hidden would render anyway -- visible and doing nothing. */
.pill[hidden]{display:none}

.eyebrow{font-size:11px;font-weight:600;letter-spacing:.08em;color:#55637A;text-transform:uppercase}
h1{margin:0;font-family:Nunito, system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;font-size:30px;font-weight:800;
  line-height:1.15;letter-spacing:-0.015em}
.sub{font-size:13px;color:#55637A}
.idblock{display:flex;flex-direction:column;gap:4px}

.tabs{display:flex;gap:6px}
.tabs a{flex-grow:1;min-height:44px;border-radius:22px;border:1px solid #CBDDFA;background:#FFFFFF;
         color:#1E293B;font-size:13px;font-weight:600;text-decoration:none;
         display:flex;align-items:center;justify-content:center}
.tabs a[aria-current="page"]{background:#2563EB;color:#FFFFFF;border-color:#2563EB}

.headline{padding:14px 16px;border-radius:16px;background:#EFF6FF;border:1.5px solid #93C5FD;
           display:flex;flex-direction:column;gap:6px}
.headline.calm{background:#FFFFFF;border:1px solid #CBDDFA}
.headline .kicker{font-size:11px;font-weight:600;letter-spacing:.08em;color:#1D4ED8}
.headline .rgrid{margin-top:4px}
.headline .rcell:not([data-tip]){background:#FFFFFF}
.headline .also{margin-top:12px;padding-top:10px;border-top:1px solid #93C5FD}
.headline.calm .kicker{color:#0F6B5F}
.headline .say{font-family:Nunito, system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;font-size:17px;font-weight:700;line-height:1.3}
.src{font-family:'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;font-size:11px;color:#55637A}

.grid{display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:8px}
.tile{padding:10px 12px;border-radius:10px;background:#FFFFFF;border:1px solid #CBDDFA;
       display:flex;flex-direction:column;gap:3px;text-decoration:none;color:#1E293B;min-height:44px}
.tile.warn{border:1.5px solid #93C5FD;background:#EFF6FF}
.tile.na{border:1px dashed #8A8F8B}
/* Unresolved but not recent: stated, not shouted. A solid rule rather than
   the warn colour, so it reads as standing rather than urgent. */
.tile.old{border:1px solid #55637A}
.tile .sys{font-size:11px;font-weight:600;letter-spacing:.06em;color:#55637A;text-transform:uppercase}
.tile .q{font-size:13px;font-weight:600;line-height:1.25}
.tile .st{font-size:12px;font-weight:600;color:#0F6B5F}
.tile.warn .st{color:#1D4ED8}
.tile.na .st,.tile.old .st{color:#55637A}
.tile .d{font-size:11px;color:#55637A;display:-webkit-box;-webkit-line-clamp:2;
          -webkit-box-orient:vertical;overflow:hidden}

section{scroll-margin-top:24px}
/* The apartment-record invitation (record/pages/base.py `cta`). Its own block, never a link
   among the footer's housekeeping: it is an offer, and reads as one. */
.recordcta{display:flex;flex-wrap:wrap;align-items:center;justify-content:space-between;
            gap:8px 14px;margin-top:14px;padding:10px 14px;border-radius:10px;
            background:#EFF6FF;border:1px solid #CBDDFA;font-size:13px;color:#1E293B}
.recordcta p{margin:0;flex:1 1 220px}
.recordcta .go{display:flex;gap:12px;align-items:center;flex:0 0 auto}
.recordcta .ask{background:#2563EB;color:#fff;border-radius:999px;padding:6px 14px;
                 font-weight:600;text-decoration:none;white-space:nowrap}
.recordcta .ask:focus-visible,.recordcta .mine:focus-visible{outline:2px solid #1E293B;
                 outline-offset:2px}
.recordcta .mine{color:#2563EB;white-space:nowrap}
.card{padding:12px 14px;border-radius:10px;background:#FFFFFF;border:1px solid #CBDDFA;
       display:flex;flex-direction:column;gap:10px}
h2{font-size:11px;font-weight:600;letter-spacing:.08em;color:#55637A;text-transform:uppercase;
    margin:10px 0 0}
.row{display:flex;flex-direction:column;gap:3px}
.row + .row{border-top:1px solid #CBDDFA;padding-top:10px}
.row .label{font-size:15px;font-weight:600}
.row .body{font-size:13px;color:#55637A;margin:0}
.row{gap:2px}
details.defn summary{min-height:20px;font-size:11px}
/* The individual records behind a count: a date gutter and the thing itself, so a
   pattern is visible at a glance (four pest violations in one winter reads
   differently from four spread over a decade). */
/* A section announces its own state, so a reader can decide whether to read it
   before reading it. Same four words as the tile, same rule behind them. */
/* Jump bar: the systems that actually have something to say, as targets. It is
   also a summary -- what is on this page, and which parts are problems. */
.jumps{display:flex;flex-wrap:wrap;gap:6px}
.jumps a{font-size:12px;font-weight:600;padding:6px 10px;border-radius:16px;
          border:1px solid #CBDDFA;background:#FFFFFF;color:#1E293B;text-decoration:none;
          min-height:32px;display:inline-flex;align-items:center}
.jumps a.warn{border-color:#93C5FD;color:#1D4ED8}
.jumps a.old{border-color:#55637A}
.jumps a:hover{border-color:#1E293B}
.jumps a.on{background:#2563EB;color:#FFFFFF;border-color:#2563EB}
.jumps a.warn.on{background:#EFF6FF;border-color:#1D4ED8;color:#1D4ED8}
[hidden]{display:none !important}
/* Sources, grouped by agency: a column per agency on a wide screen, dates in a
   right-hand gutter so a stale one is visible by scanning rather than reading. */
.sourcecard{display:grid;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));gap:14px 22px}
.agency h3{margin:0 0 4px;font-size:11px;font-weight:600;letter-spacing:.06em;
            text-transform:uppercase;color:#55637A}
ul.srcs{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:3px}
ul.srcs li{display:flex;justify-content:space-between;gap:10px;align-items:baseline;
            font-size:12px;line-height:1.35}
.sname{flex:1;min-width:0}
.sdate{font-family:'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;font-size:11px;color:#55637A;white-space:nowrap}

/* Sources, grouped by agency: a column per agency on a wide screen, dates in a
   right-hand gutter so a stale one is visible by scanning rather than reading. */
.sourcecard{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:14px 22px}
.agency h3{margin:0 0 4px;font-size:11px;font-weight:600;letter-spacing:.06em;
            text-transform:uppercase;color:#55637A}
ul.srcs{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:3px}
ul.srcs li{display:flex;justify-content:space-between;gap:10px;align-items:baseline;
            font-size:12px;line-height:1.35}
.sname{flex:1;min-width:0}
.sdate{font-family:'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;font-size:11px;color:#55637A;white-space:nowrap}

.routed{margin-top:4px}
.routed > .label{font-size:11px;font-weight:600;letter-spacing:.06em;
                  text-transform:uppercase;color:#55637A}

.nothing{display:flex;flex-wrap:wrap;align-items:baseline;gap:8px}
.nothing + .nothing{border-top:1px solid #CBDDFA;padding-top:10px}

.sechead{display:flex;align-items:baseline;justify-content:space-between;gap:10px}
.sechead h2{margin:0}
.secstate{font-size:11px;font-weight:600;color:#0F6B5F;white-space:nowrap}
.secstate.warn{color:#1D4ED8}
.secstate.na,.secstate.old{color:#55637A}
.rowhead{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap}

/* Definitions explain what a count counts. True, useful once, and the longest
   text on the row -- so it folds away rather than competing with the finding. */
details.defn{font-size:12px;color:#55637A}
details.defn summary{cursor:pointer;min-height:24px;display:flex;align-items:center;
                      font-weight:500}
details.defn p{margin:4px 0 0}

/* Rows with nothing to report still appear -- "not required" and "no record" are
   different claims about a building and hiding either would misrepresent it --
   but one line each, not the full height of a finding. */
ul.quiet{list-style:none;margin:0;padding:0;display:flex;flex-direction:column;gap:4px}
ul.quiet li{display:flex;gap:8px;align-items:baseline;flex-wrap:wrap;font-size:12px;
             color:#55637A}
.qlabel{font-weight:600;color:#1E293B}
.qstate{font-size:11px;border:1px solid #CBDDFA;border-radius:8px;padding:1px 6px;white-space:nowrap}
.qwhy{flex-basis:100%;font-size:11px}

/* The compact rail: the same component as the Timeline tab, sized to sit inside a
   record row. Narrower gutter, smaller dot, no per-event source line (the row
   already names its source once). */
.tl.compact{margin:6px 0 0;max-width:none}
/* ' 2019 Jun' is 8 mono characters; at 50px it wrapped onto two lines and the
   rail looked broken. Sized to the longest value it can hold, and pinned. */
.tl.compact .when{width:58px;font-family:'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;font-size:11px;font-weight:400;
                   white-space:nowrap}
.tl.compact .dot{width:8px;height:8px;border-radius:4px}
.tl.compact .what{padding-bottom:8px}
.tl.compact .what .t{font-size:12px;font-weight:500}
.tl.compact .ev.open .what .t{font-weight:600}
.itag{font-size:11px;color:#55637A;font-weight:400;margin-left:6px}
/* The apartment a record names. A chip rather than more grey text: it is the only
   thing distinguishing four rows that otherwise read identically. */
.iunit{font-size:11px;font-weight:600;margin-left:6px;padding:1px 6px;border-radius:9px;
        border:1px solid #CBDDFA;color:#55637A;white-space:nowrap}
.pills{display:flex;flex-wrap:wrap;gap:8px}
.unitstrip{display:flex;flex-direction:column;gap:10px}
/* How often each problem is reported: year over year, months aligned. One thin row
   per year under one Jan–Dec header, so a problem that returns at the same time of
   year stacks in the same columns. No season is singled out. */
.recur{display:flex;flex-direction:column;gap:10px}
.rlist{list-style:none;margin:0 0 10px;padding:0;display:flex;flex-direction:column;gap:16px}
.rhead{display:flex;justify-content:space-between;gap:10px;font-size:14px;font-weight:600}
.rtimes{font-weight:500;color:#55637A}
.rcmp{font-size:13px;font-weight:600;color:#1D4ED8;margin:2px 0 6px}
.headline .rcmp{font-size:14px;margin:4px 0 8px}
.rhead a,.headline .say a{color:#1E293B;text-decoration:underline;text-decoration-color:#CBDDFA;
  text-underline-offset:3px}
.rhead a:hover,.headline .say a:hover{color:#2563EB;text-decoration-color:#2563EB}
.rgrid{display:grid;grid-template-columns:34px repeat(12,minmax(0,1fr));gap:2px;margin-top:2px}
.rhdr{margin-bottom:8px}
.rmh{font-size:11px;font-weight:700;text-align:center;color:#55637A}
.ryr{font-size:11px;font-weight:700;color:#55637A;line-height:14px}
.rcell{display:block;width:100%;height:14px;border:0;padding:0;border-radius:3px;background:#F6F8FC}
.rcell.later{background:none}
/* Filled months. Older ones recede; the past year is brightest. No tint on empty
   winter cells: it was nearly the colour of a filled month, and the header letters
   already say which columns are winter. */
.rcell[data-tip]{background:#BFDBFE;cursor:pointer}
.rcell[data-tip].many{background:#93C5FD}
.rcell[data-tip].now{background:#60A5FA}
.rcell[data-tip].now.many{background:#2563EB}
/* A filled month opens a small list of every complaint in it, each its own link. It
   stays open while the pointer moves into it: the padding is the bridge across the
   gap, and edge months open inward instead of off the card. */
.rc{position:relative}
.rc:hover .rcell,.rc:focus-within .rcell{outline:2px solid #2563EB;outline-offset:1px}
.rpop{display:none;position:absolute;bottom:100%;left:50%;transform:translateX(-50%);
  z-index:20;padding-bottom:6px;min-width:180px;max-width:240px;max-height:260px;overflow-y:auto}
.rc.edge-l .rpop{left:0;transform:none}
.rc.edge-r .rpop{left:auto;right:0;transform:none}
.rc:hover .rpop,.rc:focus-within .rpop{display:flex;flex-direction:column}
.rpop b,.rpop a{background:#FFFFFF;padding:3px 10px;font-size:13px}
.rpop b{display:block;font-weight:700;color:#1E293B;padding-top:8px;border:1px solid #CBDDFA;
  border-bottom:0;border-radius:8px 8px 0 0}
.rpop a{display:block;color:#2563EB;font-weight:600;text-decoration:none;
  border-left:1px solid #CBDDFA;border-right:1px solid #CBDDFA}
.rpop a:last-child{padding-bottom:8px;border-bottom:1px solid #CBDDFA;border-radius:0 0 8px 8px}
.rpop a:hover{text-decoration:underline}
.rpop{filter:drop-shadow(0 4px 10px rgba(16,32,64,.16))}
.rmore summary{cursor:pointer;font-size:13px;color:#2563EB;min-height:44px;display:flex;align-items:center}
.rmore .rlist{margin-top:8px}
/* The apartment box is the address box: same row, same icon button, same list
   under it. It had its own geometry and a text button reading "Open it", which put
   two controls doing the same job in two visual languages on one page. */
form.unitfind{max-width:360px}
.pill.ghost{background:transparent;color:#2563EB;min-height:36px;padding:0 12px;font-size:13px}
.pill.ghost:hover{background:#E4EEFF}
.railmore{font-size:11px;font-style:italic;color:#55637A;padding-left:62px}
.railopen > summary{cursor:pointer;list-style:none;font-size:13px;font-weight:600;color:#2563EB;
  padding-left:62px;min-height:44px;display:flex;align-items:center}
.railopen > summary::-webkit-details-marker{display:none}
.railopen[open] > summary{display:none}
/* Reading the full record happens on the agency's site, so this link is a
   real destination, not a disclosure toggle. */
.readmore{font-size:12px;font-weight:600;align-self:flex-start;
           min-height:28px;display:inline-flex;align-items:center}

.chip{align-self:flex-start;font-size:11px;font-weight:600;padding:3px 8px;border-radius:10px;
       border:1px solid #55637A;color:#55637A;white-space:nowrap}
.chip.warn{border-color:#1D4ED8;color:#1D4ED8}

.seeall{min-height:44px;display:flex;align-items:center;justify-content:center;border-radius:22px;
         border:1.5px solid #2563EB;color:#2563EB;font-weight:600;font-size:15px;text-decoration:none}
/* The timeline is a rail: a date gutter, a dot per event, and a line running
   between them. The dot carries the only state that matters at a glance -- still
   open or not -- and it is reinforced by weight and by the word "open" in the
   text, never by colour alone. */
.tl{display:flex;flex-direction:column;max-width:720px}
.ev{display:flex;gap:12px}
.ev .when{width:64px;flex-shrink:0;font-size:12px;font-weight:600;color:#55637A;
           padding-top:2px;white-space:nowrap}
.ev .rail{display:flex;flex-direction:column;align-items:center}
.ev .dot{width:12px;height:12px;border-radius:6px;background:#2563EB;flex-shrink:0}
.ev.open .dot{background:#2563EB;box-shadow:0 0 0 2px #BFDBFE}
/* Highlight only what is BOTH unresolved and from the last year. The row is
   tinted, not just the dot, so it is findable while scrolling a long rail. */
/* Recency is marked on the dot, not with a filled block. A tinted band behind one
   row in a rail competes with the rail itself and reads as a selection, not as a
   property of the record. A ring is the same signal at a tenth of the weight. */
.ev.open.recent .dot{box-shadow:0 0 0 3px #BFDBFE}
.ev.open.recent .when{color:#1D4ED8}
.ev .line{width:2px;flex-grow:1;background:#CBDDFA}
.ev:last-child .line{background:transparent}
.ev .what{display:flex;flex-direction:column;gap:2px;padding-bottom:14px}
a.ev.go{text-decoration:none;color:inherit;border-radius:8px;margin:0 -8px;padding:0 8px;
         position:relative}
.goout{position:absolute;right:6px;top:2px;font-size:11px;color:#55637A;opacity:0}
a.ev.go:hover .goout,a.ev.go:focus-visible .goout{opacity:1}
a.ev.go:hover,a.ev.go:focus-visible{background:#E4EEFF}
a.ev.go .what .t{text-decoration:underline;text-decoration-color:#CBDDFA;
                  text-underline-offset:3px}
a.ev.go:hover .what .t{text-decoration-color:#2563EB}
.ev .what .t{font-size:14px}
.ev.open .what .t{font-weight:600}
/* resets the uppercase/tracking the generic h2 rule applies: this is a display
   heading, not a section label */
.lede{font-family:Nunito, system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;font-size:22px;font-weight:800;letter-spacing:-0.01em;margin:0;
       text-transform:none;letter-spacing:normal;color:#1E293B}

.foot{font-size:11px;color:#55637A}
.foot.center{text-align:center}

/* The search row. The button used to be a square blue slab reading "Look up"
   pressed against a 10px-rounded field -- two different shapes fighting, and a verb
   nobody needed, since a magnifier next to an address box has never been ambiguous.
   Now: one rounded row, the button an icon square inside the same geometry. */
form.find{display:flex;gap:8px;align-items:stretch}
/* The suggestions. Below the field, not a page: an address box that makes you read
   a results page to choose between two boroughs is three steps where one will do.
   /search still exists and still works -- it is the scripting-off path and what a
   shared search URL resolves to. */
.sugg{list-style:none;margin:-6px 0 0;padding:4px;display:flex;flex-direction:column;
  gap:2px;background:#FFFFFF;border:1px solid #CBDDFA;border-radius:10px;
  box-shadow:0 6px 20px rgba(16,32,64,.10);max-height:300px;overflow-y:auto}
.sugg li{display:flex;flex-direction:column;gap:1px;padding:9px 10px;border-radius:8px;
  cursor:pointer;min-height:44px;justify-content:center}
.sugg li[aria-selected="true"]{background:#E4EEFF}
.sugg .ad{font-size:15px;font-weight:600;color:#1E293B}
.sugg .bo{font-size:12px;color:#55637A}
/* "No apartment matches — open its page anyway". Not an error: every apartment in a
   building has a page whether a city record ever named it or not, and most are named
   in none. A dashed edge reads as available; a grey one would read as refused. */
.sugg li.none{border:1px dashed #CBDDFA}
form.find button{flex:0 0 auto;width:52px;min-height:48px;border-radius:10px;
  border:1px solid #2563EB;background:#2563EB;color:#FFFFFF;cursor:pointer;
  display:inline-flex;align-items:center;justify-content:center;padding:0}
form.find button:hover{background:#163A69;border-color:#163A69}
form.find button:focus-visible{outline:2px solid #1E293B;outline-offset:2px}
/* The correction form: a stacked, ordinary form. Nothing clever -- it has to
   work on a phone, with autofill, and without JavaScript. */
form.stack{display:flex;flex-direction:column;gap:6px;max-width:520px}
form.stack label{font-size:13px;font-weight:600;margin-top:6px}
form.stack select,form.stack textarea,form.stack input{font:inherit;font-size:16px;
  padding:10px 12px;border:1px solid #CBDDFA;border-radius:10px;background:#FFFFFF;color:#1E293B}
form.stack textarea{resize:vertical;min-height:110px}
/* Full width rather than content width. The dispute form had this at flex-start,
   which is fine for one short "Send" on a desktop page and wrong for the primary
   action of a page someone is using one-handed in a basement. `form.stack` is
   already capped at 520px, so stretching does not run away on a wide screen. */
form.stack button{align-self:stretch;margin-top:10px;border:1px solid #2563EB;
  min-height:44px;border-radius:22px;background:#2563EB;color:#FFFFFF;
  font-family:Nunito, system-ui, -apple-system, 'Segoe UI', Helvetica, Arial, sans-serif;font-size:15px;font-weight:600;cursor:pointer}
form.stack button.quiet{background:transparent;color:#2563EB}
/* A label wrapping its own input: the text above, the field below. */
form.stack label{display:flex;flex-direction:column;gap:4px}
form.stack .hint{font-size:12px;color:#55637A;font-weight:400;line-height:1.45}
form.stack input,form.stack select{min-height:44px}
form.stack input[type=file]{padding:8px;font-size:13px;min-height:40px}
form.find input{flex:1;min-width:0;min-height:48px;padding:0 14px;font-size:16px;font-family:inherit;
                 border:1px solid #CBDDFA;border-radius:10px;background:#FFFFFF;color:#1E293B}
.results{list-style:none;padding:0;margin:0;display:flex;flex-direction:column;gap:8px}
.results a{display:flex;flex-direction:column;gap:2px;padding:12px 14px;border-radius:10px;
            background:#FFFFFF;border:1px solid #CBDDFA;text-decoration:none;color:#1E293B;min-height:44px}
.results .ad{font-weight:600;font-size:15px}
.results .bo{font-size:12px;color:#55637A}

/* The phone is the design's native size, so the column holds at 640px until there
   is real room. Past that, widen and let the grid take more columns rather than
   stretching two tiles across a desktop -- a 600px-wide tile is harder to scan,
   not easier. */
/* ── Four classes that were already in use and never defined ───────────────────
   `mono` (the URL line on every unit page), `disclaim`, `jump` and
   `visually-hidden`. The last one is the one that mattered: undefined, a skip
   link's text renders in the page instead of being available only to a screen
   reader. test_css.py checks balance and key rules, not that every class used in
   a renderer exists -- it does now. */
.mono{font-family:'IBM Plex Mono', ui-monospace, SFMono-Regular, Menlo, monospace;font-size:12px;color:#55637A;word-break:break-all}
.disclaim{font-size:11px;color:#55637A;line-height:1.45}
.jump{font-size:12px;font-weight:600}
.visually-hidden{position:absolute;width:1px;height:1px;margin:-1px;padding:0;
  overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}
.note{font-size:12px;color:#55637A;line-height:1.5;border-left:2px solid #CBDDFA;
  padding:2px 0 2px 10px;margin:0}

/* The camera: the only way a photo gets into an apartment record, so a record can say
   when each one arrived (record/pages/camera.py `_camera`). */
.shot{display:flex;flex-direction:column;gap:8px}
.shot video,.shot img[data-preview]{width:100%;max-height:52vh;border-radius:10px;
  background:#000;object-fit:cover}

@media (min-width: 900px){
  /* Wide screens: the page docks on the right instead of rising from the bottom. */
  .wrap:has(.map.locator){max-width:520px;margin:72px 16px 16px auto;border-radius:16px;
    padding:24px 28px 40px}
  .wrap:has(.map.locator) .grid{grid-template-columns:repeat(2,minmax(0,1fr))}
  .wrap{max-width:1040px;padding:32px 32px 56px;gap:18px}
  h1{font-size:38px}
  .headline .say{font-size:20px}
  .grid{grid-template-columns:repeat(auto-fit,minmax(230px,1fr))}
  .tile{padding:14px 16px;gap:4px}
  .tile .q{font-size:15px}
  .tile .d{font-size:12px;-webkit-line-clamp:3}
  .tabs{max-width:520px}
  /* Grid, not column-count. Multi-column flows content DOWN one column and back
     UP the next, so a reference list reads in the wrong order, and a tall section
     that cannot break gets stranded with its heading alone at the foot of a
     column while later sections fall out of view entirely. Grid places each
     section as a whole, left to right, and nothing is cut. */
  .recordcols{display:grid;grid-template-columns:repeat(auto-fit,minmax(320px,1fr));
               gap:18px;align-items:start}
  .recordcols > section{margin:0;display:flex;flex-direction:column;gap:6px}
  .recordcols > section > h2{margin:0}
  /* a tile links to #heat; without this the heading lands flush against the
     viewport edge and the reader cannot see they arrived somewhere */
  .recordcols > section{scroll-margin-top:24px}
  /* the catch-all blocks read better across the full width than as a
     narrow column of one-liners */
  .recordcols > section.wide{grid-column:1/-1}
}
@media (min-width: 1400px){
  .wrap{max-width:1240px}
  .grid{grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
}

/* LIGHT ONLY. There was a dark scheme here; the building page became a white sheet
   over a map, the dark rules kept turning its text near-white, and a reader on a
   dark-mode Chrome got white headings on a white panel. The product is white with
   blue, so the browser is told so rather than left to guess. */
:root{color-scheme:light}
