/* Shared app styles for the lookup (index.html) and constituency (pcon/) pages.
 *
 * These two pages had byte-identical <style> blocks — 120 lines, duplicated in
 * full — so every fix to the tiles, verdict badge, league modal or sauna list
 * had to be made twice, with nothing enforcing that it was. Extracted verbatim
 * on 14 Jul 2026; no rule was changed in the move.
 *
 * Colours and type come from tokens.css. Do not put hexes here — add a token.
 */

  *{box-sizing:border-box}
  body{margin:0;background:var(--bg);color:var(--ink);-webkit-font-smoothing:antialiased;
       font:16px/1.5 var(--font)}
  /* The app was built as a fixed 480px phone column, which stranded it in a sea
     of black on anything wider. The single linear layout is deliberate (the
     result reads top to bottom as one story), so responsiveness here is one
     fluid column that grows to a comfortable reading width — no reflow into
     grids. 640px keeps body copy around 66 characters a line; the block still
     collapses to the viewport on phones. Map height and the full-screen league
     modal get their own desktop tuning below. */
  .wrap{max-width:640px;margin:0 auto;padding:26px 18px 60px}
  h1{font-size:40px;font-weight:700;letter-spacing:-1px;line-height:1.05;margin:0 0 14px}
  .sub{font-size:15px;color:var(--muted);line-height:1.5;margin:0}
  .ast{color:var(--muted);text-decoration:none}

  .search{display:flex;gap:10px;margin:20px 0 0}
  .field{flex:1;display:flex;align-items:center;background:var(--card);
         border:1.5px solid var(--blue);border-radius:var(--r-lg);padding-right:6px;min-width:0}
  .field input{flex:1;min-width:0;background:none;border:0;color:var(--ink);font-size:16px;
               padding:13px 12px;outline:none;text-transform:uppercase}
  .field input::placeholder{color:var(--muted);text-transform:none}
  .geo{display:inline-flex;align-items:center;gap:5px;background:none;border:0;color:var(--blue);
       font-size:13px;cursor:pointer;white-space:nowrap;padding:6px 2px}
  .geo:disabled{color:var(--muted)} .geo svg{width:17px;height:17px;flex:none}
  .geo.iconly{padding:6px}
  #rand{padding:6px 4px} #rand svg{width:auto;height:30px}
  .go{background:var(--green);color:#fff;border:0;border-radius:var(--r-lg);font-size:16px;
      font-weight:600;padding:0 18px;cursor:pointer;white-space:nowrap}
  .go:active{transform:scale(.98)}
  .err{color:var(--red);font-size:14px;margin:0 2px}
  .err:not(:empty){margin:8px 2px 0}

  .loc{font-size:15px;color:var(--muted);margin:16px 0 18px}
  .loc b{color:var(--ink)}
  .la-lbl{font-size:15px;color:var(--muted);margin:0}
  .la-nm{font-size:38px;font-weight:600;letter-spacing:-.5px;line-height:1.1;margin:2px 0 8px}
  .rankline{font-size:15px;color:var(--muted);margin:0} .rankline b{color:var(--ink)}
  .ranklink{background:none;border:0;color:var(--blue);font-weight:600;font-size:15px;padding:0;
            cursor:pointer;text-decoration:underline;text-underline-offset:2px;font-family:inherit}
  .na{color:var(--muted);font-size:15px;margin:10px 0 0}

  .tiles{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin:16px 0 0}
  .tile{background:var(--card);border:1px solid var(--border);border-radius:var(--r-xl);min-height:172px;
        display:grid;grid-template-rows:auto 1fr auto;justify-items:center;align-items:center;
        text-align:center;padding:20px 12px}
  .tlab{font-size:14px;color:var(--muted)}
  .tnum{font-size:64px;font-weight:700;letter-spacing:-1px;line-height:1;align-self:start;padding-top:6px}
  .tnum.na{font-size:46px;color:var(--muted);letter-spacing:0}
  .tsub{font-size:22px;color:var(--muted)}
  .na-note{font-size:13px;color:var(--muted);line-height:1.5;margin:12px 2px 0}
  .tgap{font-size:14px;color:var(--muted);margin-top:10px}
  .tgap .g{font-weight:600} .pos .g,.g.pos{color:var(--green)} .neg .g,.g.neg{color:var(--red)}
  .badge{margin-top:9px;font-size:11px;font-weight:600;letter-spacing:.06em;
         padding:3px 10px;border-radius:var(--r-pill);border:1px solid}
  .badge.pos{color:var(--green);border-color:var(--green)}
  .badge.neg{color:var(--red);border-color:var(--red)}
  .x{text-transform:none}

  .surprise{background:var(--card);border:1px solid var(--border);border-radius:var(--r-xl);
            padding:15px 16px 12px;margin:12px 0 0}
  .sur-head{font-size:17px;font-weight:600;letter-spacing:-.3px;line-height:1.25;margin:0 0 7px}
  .sur-lab{font-size:12.5px;color:var(--muted);line-height:1.45;margin:0 0 9px}
  .sur-note{font-size:12.5px;color:var(--muted);line-height:1.45;margin:8px 0 0}
  .sur-note b{color:var(--ink)}
  .sur-badge{display:inline-block;font-size:11px;font-weight:600;letter-spacing:.06em;
             padding:3px 10px;border-radius:var(--r-pill);border:1px solid;margin-top:9px}

  /* .mapcard IS the Leaflet map container — Leaflet adds .leaflet-container to
     this same element — so the old `.mapcard .leaflet-container` (a *descendant*
     selector) never matched, and the map fell back to Leaflet's default #ddd.
     That pale ground is what flashed as the slider zoomed the tiles. Style the
     element directly with a dark ground that matches the tiles, so gaps and the
     idle state read as map. font:inherit puts map text (attribution) in Inter. */
  /* 4:3, not a fixed height, so the tile keeps its shape at every column width
     (was 230px, which read as a letterbox once the column grew). The
     ResizeObserver in app.js re-fits the map whenever this box changes. */
  .mapcard{aspect-ratio:4/3;border-radius:var(--r-xl);border:1px solid var(--border);margin:12px 0 0;
           overflow:hidden;position:relative;z-index:0;background:var(--map-ground);font:inherit}
  .mapcard .leaflet-control-attribution{background:rgba(0,0,0,.55);color:var(--muted);
           font-size:9px;padding:1px 5px}
  .mapcard .leaflet-control-attribution a{color:var(--muted)}

  /* Full-screen map modal. #mapbox wraps the map tile + slider + nearest-saunas
     list inline; adding .fullscreen (JS) turns it into a near-fullscreen sheet
     that stacks: map (fills the top), slider, then the scrolling list — the same
     Leaflet map and same #list, restacked. JS toggles only the class; button
     visibility is CSS below. */
  #mapbox{position:relative}
  .mapbtn{position:absolute;top:10px;right:10px;z-index:2;width:34px;height:34px;cursor:pointer;
          border-radius:var(--r-circle);background:var(--map-chrome-bg);border:1px solid var(--border);
          color:var(--ink);display:flex;align-items:center;justify-content:center;
          -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}
  .mapbtn svg{width:16px;height:16px}
  .mapbtn:active{transform:scale(.94)}
  /* Expand shows only once the map is up (map loses .hidden on first lookup) and
     only while inline — :has() and :not() keep this out of the JS. Close is the
     mirror: hidden by its .hidden class until .fullscreen out-specifies it. */
  #mapExpand{display:none}
  #mapbox:not(.fullscreen):has(#map:not(.hidden)) #mapExpand{display:flex}
  #mapbox.fullscreen #mapClose{display:flex;width:38px;height:38px;
           top:calc(14px + env(safe-area-inset-top));right:calc(14px + env(safe-area-inset-right))}
  #mapbox.fullscreen #mapClose svg{width:17px;height:17px}

  /* Full-bleed: the map is a view, not a floating sheet — it runs edge to edge
     (under the notch; tiles can safely paint there). The safe-area insets sit
     on the floating chrome instead: close/zoom/locate, scale bar, card,
     attribution each offset themselves clear of notch and home indicator. */
  #mapbox.fullscreen{position:fixed;z-index:200;overflow:hidden;background:var(--map-ground);
           inset:0}
  /* Fullscreen is an interactive map (pan + pinch-zoom, enabled in JS): the map
     fills the sheet and the inline slider/heading/list are hidden. The radius
     ring is hidden too — once you are exploring, the radius is no longer the
     point and the circle just gets in the way (it still frames the opening view,
     since the fit is computed from it). */
  #mapbox.fullscreen #map{position:absolute;inset:0;height:auto;aspect-ratio:auto;
           margin:0;border:0;border-radius:0}
  #mapbox.fullscreen .radius,#mapbox.fullscreen #s-head,#mapbox.fullscreen #list{display:none}
  #mapbox.fullscreen .leaflet-ring-pane{display:none}

  /* Scale bar: centred at the top, clear of the close/zoom stack on the right. */
  #mapScale{display:none}
  #mapbox.fullscreen #mapScale{display:flex;flex-direction:column;align-items:center;gap:3px;
           position:absolute;z-index:2;pointer-events:none;
           top:calc(16px + env(safe-area-inset-top));left:50%;transform:translateX(-50%)}
  #mapScale .mapscale-bar{height:2px;width:60px;background:rgba(255,255,255,.85);border-radius:1px;
           box-shadow:0 0 2px rgba(0,0,0,.6)}
  #mapScale .mapscale-label{font-size:12.5px;color:var(--ink);text-shadow:0 1px 3px rgba(0,0,0,.75)}

  /* Tapped sauna's card: slides up from the bottom, dismissed by tapping the map. */
  #mapCard{display:none}
  #mapbox.fullscreen #mapCard{display:block;position:absolute;z-index:2;pointer-events:auto;
           left:calc(12px + env(safe-area-inset-left));right:calc(12px + env(safe-area-inset-right));
           bottom:calc(12px + env(safe-area-inset-bottom));
           background:var(--card);border:1px solid var(--border);border-radius:var(--r-row);
           overflow:hidden;opacity:0;transform:translateY(130%);
           transition:transform .2s ease,opacity .2s ease}
  #mapbox.fullscreen #mapCard.shown{opacity:1;transform:translateY(0)}

  /* Right-hand control stack, fullscreen only: close (top), zoom buttons, locate.
     Zoom is a Leaflet control so it lands in Leaflet's own corner container —
     offset it down to clear the close button; locate is placed under it. */
  #mapLocate{display:none}
  #mapbox.fullscreen #mapLocate{display:flex;top:calc(140px + env(safe-area-inset-top));
           right:calc(14px + env(safe-area-inset-right))}
  #mapbox.fullscreen .leaflet-top.leaflet-right{top:calc(60px + env(safe-area-inset-top))}
  #mapbox.fullscreen .leaflet-control-zoom{border:0;box-shadow:none;
           margin:0 calc(14px + env(safe-area-inset-right)) 0 0}
  /* Attribution sits in Leaflet's bottom-left corner; lift it off the home
     indicator now the box is full-bleed. */
  #mapbox.fullscreen .leaflet-bottom{bottom:env(safe-area-inset-bottom)}
  #mapbox.fullscreen .leaflet-control-zoom a{width:34px;height:34px;line-height:32px;font-size:18px;
           background:var(--map-chrome-bg);border:1px solid var(--border);color:var(--ink);
           -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}
  #mapbox.fullscreen .leaflet-control-zoom a:hover{background:var(--map-chrome-bg-strong);color:var(--ink)}
  #mapbox.fullscreen .leaflet-control-zoom a:first-child{border-radius:var(--r-md) var(--r-md) 0 0}
  #mapbox.fullscreen .leaflet-control-zoom a:last-child{border-radius:0 0 var(--r-md) var(--r-md);border-top:0}

  /* Hover tooltips (sauna name + distance). Same floating-over-map chrome as
     the map buttons and the fullscreen zoom control: --map-chrome-bg behind a
     --border hairline, blurred. Arrowless — the default arrow is a solid
     triangle that cannot match a translucent box; app.js bindHover offsets the
     tooltip clear of the dot instead. Killed for every tooltip, labels too. */
  .leaflet-tooltip{background:var(--map-chrome-bg);border:1px solid var(--border);
           color:var(--ink);font-family:var(--font);font-size:12px;font-weight:600;
           border-radius:var(--r-md);padding:4px 9px;box-shadow:none;
           -webkit-backdrop-filter:blur(4px);backdrop-filter:blur(4px)}
  .leaflet-tooltip::before{display:none}

  /* Sauna name labels: permanent tooltips, virtualised in JS (app.js
     syncLabels) — bound only in fullscreen, only past LABEL_ZOOM, and only for
     markers inside the current view, so existence is the visibility switch and
     no CSS gating is needed. Bare text over the map: the chrome box above is
     stripped back off (a blur under a transparent box would still smear). */
  .sauna-label{background:none;border:0;box-shadow:none;padding:0;
           -webkit-backdrop-filter:none;backdrop-filter:none;
           color:var(--ink);font-size:11px;font-weight:600;white-space:nowrap;
           text-shadow:var(--map-label-shadow)}

  /* Your location pin: a divIcon (app.js renderMap), so unlike the Leaflet-
     drawn markers its colours resolve from the tokens. .pin--live is set only
     when the lookup came from a trusted geolocation fix (see the pages' geo
     handlers) — the slow first-iPhone "you are here" pulse; reduced-motion
     users keep the static pin. */
  .pin{position:relative;width:18px;height:18px;border-radius:var(--r-circle);
           background:var(--pin-fill);border:2px solid var(--blue)}
  @media (prefers-reduced-motion:no-preference){
    .pin--live::after{content:"";position:absolute;inset:-2px;border-radius:var(--r-circle);
             background:var(--blue);opacity:0;animation:pin-pulse 2.6s ease-out infinite}
  }
  @keyframes pin-pulse{
    0%{opacity:.45;transform:scale(.6)}
    70%,100%{opacity:0;transform:scale(2.4)}
  }

  .grouphead{font-size:17px;font-weight:600;margin:28px 0 12px}
  ul{list-style:none;margin:0;padding:0}
  li{background:var(--card);border:1px solid var(--border);border-radius:var(--r-row);margin-bottom:10px;overflow:hidden}
  .item{display:flex;justify-content:space-between;align-items:flex-start;gap:12px;
        padding:15px 18px;text-decoration:none;color:inherit}
  /* NB saunaRow() always emits <div class="item">, never <a class="item">, so a
     row is not a link and has no pressed state. Only the .s-link anchor inside
     it is interactive. (An `a.item:active` rule lived here for months and never
     matched anything.) */
  .s-nm{font-size:19px;font-weight:600}
  .bss{display:inline-block;font-size:10px;font-weight:600;letter-spacing:.05em;color:var(--bss);
       border:1px solid var(--bss);border-radius:var(--r-sm);padding:1px 5px;margin-left:8px;vertical-align:2px}
  /* "in <authority>" chip on the lookup page: marks the saunas inside the
     looked-up authority in the single radius list. Blue to tie to the map's
     area outline and the location pin. */
  .inla{display:inline-block;font-size:10px;font-weight:600;color:var(--blue);
        border:1px solid var(--blue);border-radius:var(--r-sm);padding:1px 5px;margin-left:8px;vertical-align:2px}
  .s-meta{font-size:14px;color:var(--muted);margin-top:3px}
  .s-link{font-size:13px;color:var(--blue);margin-top:7px;display:inline-flex;align-items:center;gap:3px}
  .s-r{font-size:14px;color:var(--muted);white-space:nowrap;padding-top:3px}
  .chev{color:var(--muted);font-weight:400}

  .radius{margin:0}
  .rhead{display:flex;justify-content:space-between;font-size:15px;color:var(--muted);margin-bottom:10px}
  .rhead b{color:var(--ink);font-weight:600}
  input[type=range]{width:100%;accent-color:var(--blue);height:22px}

  .about{margin-top:44px}
  .about hr{border:0;border-top:1px solid var(--border);margin:0 0 22px}
  .about-lbl{font-size:15px;color:var(--muted);margin:0 0 12px}
  .about h2{font-size:19px;font-weight:600;line-height:1.3;margin:0 0 16px}
  .about p{font-size:15px;color:var(--muted);line-height:1.6;margin:0 0 16px}
  .about b{color:var(--ink);font-weight:600}
  .about a{color:var(--blue);text-decoration:none}

  .modal{position:fixed;inset:0;background:var(--bg);z-index:100;display:flex;flex-direction:column;
         padding:20px 18px calc(20px + env(safe-area-inset-bottom))}
  .modal-head{display:flex;align-items:flex-start;justify-content:space-between;gap:12px;margin-bottom:14px}
  .modal-head h2{font-size:24px;font-weight:700;letter-spacing:-.5px;margin:0}
  .modal-sub{font-size:13px;color:var(--muted);margin:3px 0 0}
  .modal-close{background:var(--card);border:1px solid var(--border);color:var(--ink);width:36px;height:36px;
               border-radius:var(--r-circle);font-size:16px;line-height:1;cursor:pointer;flex:none;
               display:flex;align-items:center;justify-content:center}
  .modal-close svg{width:15px;height:15px}
  .modal-search{background:var(--card);border:1.5px solid var(--border);border-radius:var(--r-lg);color:var(--ink);
                font-size:16px;padding:12px 14px;margin-bottom:12px;outline:none;width:100%}
  .modal-search:focus{border-color:var(--blue)}
  .modal-search::placeholder{color:var(--muted)}
  .modal-list{overflow-y:auto;flex:1;-webkit-overflow-scrolling:touch;margin:0 -6px}
  .lrow{display:flex;align-items:flex-start;gap:10px;padding:12px 6px;border-bottom:1px solid var(--border)}
  .lrow.current{background:var(--blue-soft);border-radius:var(--r-md);border-bottom-color:transparent}
  .lrank{font-size:14px;color:var(--muted);width:32px;flex:none;padding-top:1px;font-variant-numeric:tabular-nums}
  .lname{flex:1;font-size:16px;font-weight:600;min-width:0}
  .lstats{font-size:13px;color:var(--muted);text-align:right;flex:none;white-space:nowrap;padding-top:1px}
  .lstats .g{font-weight:600}
  .lempty{color:var(--muted);font-size:15px;padding:16px 6px}

  /* Keyboard focus. :focus-visible only, so a mouse click never paints a ring
     but a Tab always does. Nothing here should be removed without a
     replacement — an invisible focus ring makes the page unusable by keyboard. */
  :focus-visible{outline:2px solid var(--blue);outline-offset:2px}
  /* A sauna row is a div, not a link — only the "Visit website" anchor inside
     it is focusable, so that is what takes the ring. */
  .s-link:focus-visible{outline-offset:3px;border-radius:var(--r-xs)}
  /* The postcode input sets outline:none (the blue field border is the resting
     state), so on keyboard focus the ring goes on the field, not the input. */
  .field:has(input:focus-visible){outline:2px solid var(--blue);outline-offset:2px}
  .modal-search:focus-visible{border-color:var(--blue)}

  /* Desktop tuning for the wider column. Kept to properties that don't touch
     `display`, so nothing here can out-rank .hidden (an #id display rule in a
     media query would beat the .hidden class and reveal #out before a lookup). */
  @media (min-width: 700px){
    .wrap{padding-top:38px}
    /* The league modal is position:fixed and ignores .wrap, so its rows would
       stretch the full viewport. Cap and centre the content to the same column. */
    .modal{padding-left:max(18px,calc((100% - 640px) / 2));
           padding-right:max(18px,calc((100% - 640px) / 2))}
  }

  /* Must be last: wins the cascade tie-break against any earlier same-
     specificity display rule (e.g. .modal{display:flex}) so elements that
     are both .modal and .hidden actually stay hidden until JS opens them. */
  .hidden{display:none}
