*, *::before, *::after { box-sizing: border-box; }

/* Kill the default mobile tap-flash on every interactive element. The UA
   default is a rectangular translucent overlay (grey on iOS, light-grey on
   Android) painted over the element's bounding box — it IGNORES the
   element's border-radius, so on rounded / circular / oval buttons it
   reads as a sharp-cornered square flash that "cuts into" the visible
   shape. Setting it to transparent everywhere unifies the feel: buttons
   that define a `:active` rule give shape-aware feedback (background or
   transform change, inside the radius); buttons that don't stay silent
   (native-app convention). Applied to the interactive element list only,
   not `*`, so non-clickable text isn't touched. */
a, button, summary, label, [role="button"], [tabindex] {
  -webkit-tap-highlight-color: transparent;
}

/* Status palette — every error, warning, and success surface in the app
   (status line, GPS button, invalid inputs) reads from
   these tokens. Keeps the three ad-hoc reds that used to ship (#b0321d,
   #c1432c, #8c2413) from drifting apart. Contrast on the paired bg meets
   WCAG AA for body text at 12 px+.

   color-scheme: only light is the authoritative opt-out from browser-
   forced dark modes (Samsung Internet, Yandex, MIUI, Kiwi, Chrome's
   "Force Dark Mode for Web Contents" flag, Android 10+ system dark).
   Without this the panel and form inputs flip to near-black on those
   engines, even though every background colour is explicit: the UA
   inverts or overlays colours at its own layer. `only` is stricter
   than plain `light` and blocks the auto-invert pass entirely. */
:root {
  color-scheme: only light;
  --err-text:   #a1361f;
  --err-bg:     #fff1ee;
  --err-border: #f0c4bb;
  --warn-text:   #8a5a00;
  --warn-bg:     #fff8e1;
  --warn-border: #ebd39a;
  --ok-text:   #0f4d2a;
  --ok-bg:     #e9f5ee;
  --ok-border: #bdd9c4;
}

html, body {
  height: 100%;
  margin: 0;
  /* Block the whole-document pull-to-refresh (Chrome Android) + rubber-
     band bounce (iOS Safari). Without this, a downward drag anywhere on
     the page — including on the Leaflet map — can temporarily lift the
     entire document by a few pixels, pushing the `position:fixed` bottom
     sheet (and its handle chevron) past the viewport bottom until the
     gesture releases. `none` stops both the gesture AND the visual
     glow/bounce; Leaflet handles its own map scroll so nothing is lost. */
  overscroll-behavior: none;
  overscroll-behavior-y: none;
}

/* Explicit background on the root element so browsers that paint the
   area outside `body` (e.g. during scroll over-fling on iOS, or when
   the layout temporarily fails to fill the viewport) don't reveal a
   system colour that flips dark under forced dark-mode overlays. */
html { background: #f5f7f3; }

body {
  font-family: "Inter", system-ui, -apple-system, "Segoe UI", sans-serif;
  color: #152514;
  background: #f5f7f3;
  -webkit-font-smoothing: antialiased;
}

.shell {
  display: flex;
  flex-direction: column;
  /* 100vh is iOS Safari's "largest" viewport (address bar hidden). When the
     real visible area shrinks — soft keyboard open, address bar expanded,
     gesture chrome visible — content measured against 100vh becomes taller
     than what the user can see, and any flex item that can shrink gets
     squeezed. 100dvh tracks the *current* visible area on iOS 16+ / Android
     Chrome 108+ and is the right anchor for mobile. 100vh stays as a
     fallback for older browsers. */
  min-height: 100vh;
  min-height: 100dvh;
}

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  /* Baseline padding for every regular browser mode. Safe-area insets
     are applied in a `@media (display-mode: standalone)` block further
     down (only installed PWAs actually need to respect the device's
     safe zones; regular browsers handle their own chrome). This keeps
     Hola / Yandex / UC / Samsung Internet style chromium derivatives
     from pumping their custom toolbar's height into our env() reads. */
  padding: 14px 20px;
  background: #0f4d2a;
  color: #fff;
  box-shadow: 0 1px 0 rgba(0,0,0,0.12);
  /* Sit above the bottom sheet AND every Leaflet control so a full-snap
     sheet on short viewports can never cover the brand row. Positioned
     ancestor is required for z-index to take effect on a static element. */
  position: relative;
  z-index: 1500;
  /* Pin the bar so a keyboard-resized viewport can't flex-shrink it down
     to a thin green sliver (user-reported on both iOS + Android). Without
     this, the default flex-shrink:1 lets the parent compress the topbar
     when .shell's min-height briefly exceeds the visible area. min-height
     guarantees the brand row stays readable even if padding/content shift. */
  flex-shrink: 0;
  min-height: 54px;
}

.brand {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.brand-logo {
  flex-shrink: 0;
  display: block;
  border-radius: 8px;
}

.brand-text { min-width: 0; }

.brand-name {
  font-weight: 700;
  font-size: 18px;
  letter-spacing: 0.2px;
}

.brand-caption {
  font-size: 12px;
  opacity: 0.85;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.top-actions {
  display: flex;
  align-items: center;
  gap: 14px;
}

.back-link {
  display: inline-grid;
  place-items: center;
  width: 32px;
  height: 32px;
  color: #d9f0e1;
  text-decoration: none;
  border-radius: 8px;
  transition: background-color 0.14s ease, color 0.14s ease;
  -webkit-tap-highlight-color: transparent;
}

.back-link:hover {
  background-color: rgba(255, 255, 255, 0.12);
  color: #fff;
}

.back-link:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.back-link svg { display: block; }

.lang-switch {
  display: flex;
  gap: 4px;
  background: rgba(255,255,255,0.12);
  padding: 3px;
  border-radius: 8px;
}

.lang-btn {
  color: #fff;
  text-decoration: none;
  font-size: 12px;
  font-weight: 600;
  padding: 5px 10px;
  border-radius: 6px;
  letter-spacing: 0.4px;
}

.lang-btn.is-active {
  background: #fff;
  color: #0f4d2a;
}

.layout {
  /* Panel grows with the viewport between a tight floor and a ceiling that
     still leaves the map enough room for its floating cards. 28vw tracks
     ~400 px on a 1440 px screen (comfortable for the Date-row + Datasets
     chips) and hits the 440 px ceiling beyond ~1580 px, so the map never
     gets squeezed under the legend/stats overlays on ultrawide monitors.
     Below ~1357 px it floors at 380 px — still 20 px wider than the
     previous fixed value so the chip grid stops reflowing onto two rows. */
  --panel-width: clamp(380px, 28vw, 440px);
  flex: 1;
  position: relative;
  display: grid;
  grid-template-columns: var(--panel-width) 1fr;
  gap: 0;
  min-height: 0;
  transition: grid-template-columns 280ms cubic-bezier(0.4, 0, 0.2, 1);
}

.layout.is-panel-collapsed {
  grid-template-columns: 0 1fr;
}

.panel { min-width: 0; }

.panel--controls {
  padding: 18px 18px 24px;
  background: #fff;
  border-right: 1px solid #dde4d8;
  overflow-y: auto;
  overflow-x: hidden;
  max-height: calc(100vh - 56px);
  transition: padding 280ms cubic-bezier(0.4, 0, 0.2, 1),
              border-right-color 280ms cubic-bezier(0.4, 0, 0.2, 1);
}

.layout.is-panel-collapsed .panel--controls {
  padding-left: 0;
  padding-right: 0;
  border-right-color: transparent;
  pointer-events: none;
}

/* Panel toggle — half hugs the panel right edge when open; fully visible at
   the left of the map area when collapsed. Vertically centred on the panel
   so it (a) doesn't collide with the Leaflet top-left basemap switcher and
   (b) lands near natural eye-level on any monitor size (same pattern as
   Figma/VS Code sidebar collapse buttons). 38 px circle exceeds Apple/
   Material touch-target minimums — comfortable on phones if we ever
   promote it there. */
.panel-toggle {
  position: absolute;
  top: 50%;
  /* Hugs the panel's right edge — 19 px is half the 38 px toggle, so it
     straddles the border regardless of the clamped panel width. */
  left: calc(var(--panel-width) - 19px);
  z-index: 600;
  width: 38px;
  height: 38px;
  padding: 0;
  border: 1.5px solid #fff;
  border-radius: 50%;
  background: #0f4d2a;
  color: #fff;
  cursor: pointer;
  display: grid;
  place-items: center;
  box-shadow: 0 3px 10px rgba(0, 0, 0, 0.22);
  /* translateY pulls the centre exactly to the vertical midpoint while
     `top: 50%` aligns the toggle's top to 50 % of .layout — the standard
     centring pair. Kept outside the transition list so collapse/expand
     snaps without a visible vertical drift. */
  transform: translateY(-50%);
  transition: left 280ms cubic-bezier(0.4, 0, 0.2, 1),
              background-color 0.16s ease,
              box-shadow 0.16s ease;
  -webkit-tap-highlight-color: transparent;
}

.panel-toggle:hover {
  background: #15683a;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.28);
}
.panel-toggle:active { background: #0a3a1f; }
.panel-toggle:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
  box-shadow: 0 0 0 4px rgba(15, 77, 42, 0.45);
}

.panel-toggle svg {
  display: block;
  width: 16px;
  height: 16px;
  transition: transform 280ms cubic-bezier(0.4, 0, 0.2, 1);
}

/* When collapsed: button sits flush against viewport left edge, fully visible */
.layout.is-panel-collapsed .panel-toggle { left: 0; }
.layout.is-panel-collapsed .panel-toggle svg { transform: rotate(180deg); }

/* Leaflet zoom + draw controls — top-right vertical column, brand-tuned.
   Visual blocks are the LEAF toolbars (zoom, rectangle/polygon, edit/delete)
   — NOT the .leaflet-draw wrapper, which holds two sub-toolbars and would
   otherwise glue them together. Wrapper is reset to a transparent shell so
   every visible group gets its own pill with identical 8px spacing. */
.leaflet-top.leaflet-right { padding-top: 12px; padding-right: 12px; }

.leaflet-top.leaflet-right .leaflet-control,
.leaflet-top.leaflet-right .leaflet-draw,
.leaflet-top.leaflet-right .leaflet-draw-section {
  margin: 0 !important;
  background: transparent !important;
  box-shadow: none !important;
  border: none !important;
  padding: 0 !important;
}

.leaflet-top.leaflet-right .leaflet-control-zoom,
.leaflet-top.leaflet-right .leaflet-draw-toolbar {
  width: 34px;
  margin-top: 0 !important;       /* override Leaflet's 12px on the second draw toolbar */
  margin-bottom: 8px !important;  /* uniform 8px gap below every group */
  border: 1px solid #c4d0bf !important;  /* consistent frame on zoom + draw + edit */
  border-radius: 10px !important;
  overflow: hidden;
  background-color: #fff !important;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15) !important;
}

/* Last group has no trailing gap */
.leaflet-top.leaflet-right .leaflet-draw-section:last-child .leaflet-draw-toolbar {
  margin-bottom: 0 !important;
}

/* Action subtoolbar (Apply / Cancel that opens after clicking edit / delete)
   — slide it far enough LEFT that it never overlaps the icon column. Default
   Leaflet offset is right:26px which clips the right edge of our 34px buttons. */
.leaflet-right .leaflet-draw-actions,
.leaflet-top.leaflet-right .leaflet-draw-actions {
  right: 44px !important;   /* 34px button + 1px border + ~9px breathing */
  left: auto !important;
}

.leaflet-control-zoom a,
.leaflet-draw-toolbar a {
  width: 34px !important;
  height: 34px !important;
  line-height: 34px !important;
  background-color: #fff !important;
  color: #2f4431 !important;
  border-bottom: 1px solid #eef2ea !important;
  font-size: 18px !important;
  font-weight: 500 !important;
  transition: background-color 0.14s ease, color 0.14s ease;
  -webkit-tap-highlight-color: transparent;
}

/* Lock the Leaflet.Draw icon spritesheet to its logical 300×30 cell grid.
   Without this, some mobile browsers (Safari iOS, older Chromium) render
   the 2x retina sprite (spritesheet-2x.png is 600×60) at its natural size
   when .leaflet-retina is active, which bleeds two adjacent icons into
   each 34px button — a user-reported "double icon" on rectangle, polygon,
   edit and delete. Explicitly pinning background-size + background-repeat
   makes both 1x and 2x sprites render identically. */
.leaflet-draw-toolbar a,
.leaflet-retina .leaflet-draw-toolbar a {
  background-size: 300px 30px !important;
  background-repeat: no-repeat !important;
}

.leaflet-control-zoom a:last-child,
.leaflet-draw-toolbar a:last-child { border-bottom: none !important; }

.leaflet-control-zoom a:hover,
.leaflet-draw-toolbar a:hover {
  background-color: #f0f6ed !important;
  color: #0f4d2a !important;
}

.leaflet-control-zoom a.leaflet-disabled,
.leaflet-draw-toolbar a.leaflet-disabled {
  background-color: #f5f7f3 !important;
  color: #b7c5b4 !important;
  cursor: not-allowed;
}

/* Action subtoolbar (Apply / Cancel after edit/delete) */
.leaflet-draw-actions a {
  background-color: #fff !important;
  color: #2f4431 !important;
  font-weight: 600 !important;
  border-color: #c4d0bf !important;
}
.leaflet-draw-actions a:hover {
  background-color: #f0f6ed !important;
  color: #0f4d2a !important;
}

/* ---- Basemap switcher (top-left) ---------------------------------------
   Two-button pill: OSM streets ⇄ Esri satellite. Visually mirrors the
   zoom/draw toolbars on the right edge — same 34 px cell, same frame,
   same shadow — so the map chrome reads as one coherent control surface.
   Active state fills the cell with brand green; inactive hover surfaces
   the hint without committing. focus-visible ring for keyboard users. */
.leaflet-top.leaflet-left { padding-top: 12px; padding-left: 12px; }

.leaflet-top.leaflet-left .basemap-switch {
  margin: 0 !important;
  display: flex;
  border: 1px solid #c4d0bf;
  border-radius: 10px;
  overflow: hidden;
  background-color: #fff;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
}

.basemap-btn {
  width: 34px;
  height: 34px;
  padding: 0;
  background: #fff;
  color: #2f4431;
  border: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  transition: background-color 0.14s ease, color 0.14s ease;
  -webkit-tap-highlight-color: transparent;
  border-right: 1px solid #eef2ea;
}

.basemap-btn:last-child { border-right: none; }

.basemap-btn svg {
  display: block;
  flex: none;
}

.basemap-btn:hover:not([aria-pressed="true"]) {
  background: #f0f6ed;
  color: #0f4d2a;
}

.basemap-btn[aria-pressed="true"] {
  background: #0f4d2a;
  color: #fff;
}

.basemap-btn:focus-visible {
  outline: 2px solid #0f4d2a;
  outline-offset: -2px;
  z-index: 1;
}

/* Mobile: keep the same 34 px cells as desktop — two finger-friendly bumps
   side-by-side made the pill dominate the map chrome. 34 px is still above
   the practical tap-target threshold for an isolated icon button. */

/* ---- OSM attribution ---------------------------------------------------
   Legally required per ODbL + OSMF Attribution Guidelines. Must stay
   readable — never use opacity < ~0.7 or colour below #888 on #fff, and
   never shrink below ~10px. Hover reveals the full strength for anyone
   actually trying to read or click through. */
.leaflet-control-attribution {
  background: rgba(255, 255, 255, 0.72) !important;
  padding: 1px 7px !important;
  border-radius: 4px 0 0 0 !important;
  font-size: 10px !important;
  line-height: 1.5 !important;
  color: #6b7363 !important;
  transition: background-color 0.16s ease, color 0.16s ease;
}

.leaflet-control-attribution a {
  color: #4a5a49 !important;
  text-decoration: none;
}

.leaflet-control-attribution:hover {
  background: rgba(255, 255, 255, 0.95) !important;
  color: #2f4431 !important;
}

.leaflet-control-attribution:hover a {
  color: #0f4d2a !important;
  text-decoration: underline;
}

/* Hide every Leaflet / Leaflet.Draw tooltip surface.
   - `.leaflet-draw-tooltip`: the floating hint that follows the cursor
     while a drawing tool is active ("Click to start drawing shape",
     "Click to continue…", etc.) — noise, not guidance.
   - `.leaflet-tooltip`: the generic Leaflet tooltip used by layers.
   Native browser `title` tooltips are stripped in JS (see
   repurposeControlTooltips), so no other hover hint sneaks back in. */
.leaflet-draw-tooltip,
.leaflet-draw-tooltip-single,
.leaflet-tooltip {
  display: none !important;
}

.control-section {
  padding: 12px;
  margin-bottom: 12px;
  border: 1px solid #d7e8f2;
  border-radius: 10px;
  background: linear-gradient(180deg, #f7fbff 0%, #ffffff 100%);
  box-shadow: 0 8px 22px rgba(42, 88, 117, 0.07);
}

.control-section--date {
  background: linear-gradient(180deg, #f8fcff 0%, #ffffff 100%);
}

.control-section--layer {
  background: linear-gradient(180deg, #f7fbff 0%, #fbfdf9 100%);
}

.control-actions {
  margin-top: 8px;
}

.panel-title {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.8px;
  text-transform: uppercase;
  color: #4a6048;
  margin: 18px 0 8px;
}

.panel-title:first-of-type { margin-top: 0; }

.control-section .panel-title {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 8px;
  color: #31536a;
}

.control-section .panel-title::before {
  content: "";
  width: 7px;
  height: 7px;
  flex: 0 0 auto;
  border-radius: 999px;
  background: #0f6d8f;
  box-shadow: 0 0 0 3px rgba(15, 109, 143, 0.11);
}

/* Hints are a whisper under the heading — dimmer and smaller than the
   title so the heading dominates the vertical rhythm. Default top margin
   leaves breathing room when the hint follows a form row (e.g. the date
   inputs); the `.panel-title + .hint` rule below pulls it closer only in
   the heading→hint→inputs case so the heading and its hint read as one unit. */
.hint {
  font-size: 11px;
  font-weight: 400;
  line-height: 1.35;
  color: #7a877a;
  margin: 8px 0 10px;
}

.panel-title + .hint {
  margin-top: -2px;
}

.control-section .hint:last-child,
.control-section .bbox-readout:last-child,
.control-section .resolution-hint:last-child {
  margin-bottom: 0;
}

.search-box {
  display: flex;
  gap: 6px;
  margin-bottom: 8px;
}

.search-box input {
  flex: 1;
  /* Without min-width: 0 a flex item's min-width defaults to its intrinsic
     content width — for an input that's the placeholder ("City / district
     / address..." / "İl / ilçe / adres..."). On narrow phones (iPhone SE,
     320-375 px) that intrinsic width alone can exceed the available row
     width; the flex container then OVERFLOWS, and since .panel--controls
     has overflow-x: hidden, the last sibling ("Ara" button) is clipped off
     the right edge. Explicit 0 min-width lets the input shrink to fit. */
  min-width: 0;
  font: inherit;
  font-size: 13px;
  padding: 8px 10px;
  border: 1px solid #c4d0bf;
  border-radius: 8px;
  background: #fafcf8;
}

.search-box input:focus {
  outline: 2px solid #2d8a4f;
  outline-offset: -2px;
  border-color: transparent;
}

/* Scoped to the search submit only. Previously this selector ALSO styled
   the GPS icon button next to it — with dark-green fill, text-align inherit,
   and horizontal padding that squeezed the SVG to one edge of its square.
   :not(.gps-btn) keeps the GPS button on its own dedicated rules below. */
.search-box button:not(.gps-btn) {
  /* flex-shrink:0 pins the Ara/Search label at its natural width —
     otherwise under pressure from a narrow viewport the button would
     truncate the text or its padding would collapse before the input
     gave up any width. */
  flex-shrink: 0;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 8px 14px;
  border: none;
  border-radius: 8px;
  background: #0f4d2a;
  color: #fff;
  cursor: pointer;
}

.search-box button:not(.gps-btn):hover { background: #15683a; }

.geocode-results {
  border: 1px solid #dde4d8;
  border-radius: 8px;
  background: #fff;
  margin-bottom: 10px;
  max-height: 180px;
  overflow-y: auto;
}

.geocode-result {
  padding: 7px 10px;
  font-size: 12px;
  cursor: pointer;
  border-bottom: 1px solid #eef2ea;
}

.geocode-result:last-child { border-bottom: none; }
.geocode-result:hover { background: #f0f6ed; }

.bbox-readout {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  font-size: 12px;
  padding: 8px 10px;
  background: #f5f7f3;
  border-radius: 8px;
  margin-bottom: 6px;
  color: #2f4431;
}

.bbox-label { color: #4a6048; font-weight: 600; }
.bbox-value { font-variant-numeric: tabular-nums; }

.clear-btn {
  margin-left: auto;
  font: inherit;
  font-size: 11px;
  padding: 3px 8px;
  border: 1px solid #c4d0bf;
  border-radius: 6px;
  background: #fff;
  cursor: pointer;
  color: #4a6048;
}

.clear-btn:hover { background: #f0f6ed; }

.date-row {
  display: grid;
  /* auto-fit + minmax is the only layout guaranteed never to overlap on
     *any* mobile engine. Fixed "1fr 1fr" trusted every browser to honour
     the grid's 8 px gap and to keep <input type="date"> rendered within
     its declared width; some iOS Safari + Chrome Android builds ignore
     this because the input's shadow DOM (calendar indicator, locale
     placeholder) enforces its own UA-level minimum width and bleeds
     into the neighbouring cell. With auto-fit, whenever the container
     can't fit two 130-px columns (≈ tiny phones under 280 px content
     width) the second column drops to a new row instead of overlapping.
     130 px minimum still comfortably shows "dd.mm.yyyy" + calendar icon
     at 14 px font. */
  grid-template-columns: repeat(auto-fit, minmax(130px, 1fr));
  gap: 8px;
}

.date-row label,
.cloud-row label {
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-size: 11px;
  font-weight: 600;
  color: #4a6048;
  /* Safety net: clip any native-rendered overflow inside the label wrap
     (calendar indicator shadow parts on older WebKit) so visual bleed
     can't escape into the adjacent grid cell even if the input itself
     misreports its width. */
  min-width: 0;
  overflow: hidden;
}

.cloud-row {
  margin-top: 8px;
}

.date-row input,
.cloud-row input {
  font: inherit;
  font-size: 13px;
  padding: 7px 8px;
  border: 1px solid #c4d0bf;
  border-radius: 8px;
  background: #fafcf8;
  /* Explicit text colour — some forced-dark implementations invert the
     value colour without touching the background, producing white-on-
     white unreadable dates. Also keeps the native date-picker popup
     rendering in light mode on Chrome/Edge where it inherits the
     element's color-scheme. */
  color: #1f2f22;
  /* width + max-width + min-width triple lock: the input always matches
     the grid cell, never wider (overflow from native glyph), never
     narrower (intrinsic content floor), never bleeds into neighbours. */
  width: 100%;
  max-width: 100%;
  min-width: 0;
  box-sizing: border-box;
}

.cloud-row input {
  max-width: 150px;
}

/* Input + select + textarea + button: native-rendered parts (date picker
   popups, scroll arrows, option lists) follow the element's color-scheme
   on Chromium/WebKit, not just the :root declaration. Scoping it here
   keeps the wheel pickers and calendar popups light-themed even when
   the user's OS is in dark mode but we're overriding to light. */
input, select, textarea, button { color-scheme: only light; }

.layer-chips {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px;
  margin-bottom: 12px;
}

.layer-chip {
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  padding: 10px 8px;
  text-align: left;
  border: 1px solid #c4d0bf;
  border-radius: 8px;
  background: #fafcf8;
  color: #2f4431;
  cursor: pointer;
  transition: all 0.12s;
}

.layer-chip:hover { background: #f0f6ed; }

.layer-chip.is-active {
  background: #0f4d2a;
  color: #fff;
  border-color: #0f4d2a;
}

.resolution-hint {
  font-size: 11px;
  color: #6a7e68;
  margin: 4px 0 12px;
  min-height: 14px;
  line-height: 1.45;
  font-variant-numeric: tabular-nums;
}

.resolution-hint strong {
  color: #2f4431;
  font-weight: 600;
}

.resolution-hint-note {
  color: #7a8d77;
  font-size: 10.5px;
  margin-top: 2px;
}

.primary-btn {
  width: 100%;
  font: inherit;
  font-size: 14px;
  font-weight: 700;
  padding: 12px;
  border: none;
  border-radius: 10px;
  background: #0f4d2a;
  color: #fff;
  cursor: pointer;
  letter-spacing: 0.3px;
  margin-top: 6px;
}

.primary-btn:hover:not(:disabled) { background: #15683a; }

.primary-btn:disabled {
  background: #b7c5b4;
  cursor: not-allowed;
}

.secondary-btn {
  width: 100%;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  padding: 9px 12px;
  border: 1px solid #c4d0bf;
  border-radius: 8px;
  background: #fafcf8;
  color: #2f4431;
  cursor: pointer;
  margin-top: 10px;
  letter-spacing: 0.2px;
}

.secondary-btn:hover { background: #f0f6ed; border-color: #a8b8a3; }
.secondary-btn:active { background: #e2ecdc; }

.status-line {
  font-size: 12px;
  color: #4a6048;
  min-height: 16px;
  margin: 10px 0 0;
}

.status-line.is-error   { color: var(--err-text); }
.status-line.is-warning { color: var(--warn-text); }
.status-line.is-success { color: var(--ok-text); }

.scene-info {
  margin-top: 14px;
  padding: 12px 14px;
  background: #f5f7f3;
  border: 1px solid #e3e9de;
  border-radius: 10px;
  font-size: 12px;
  color: #2f4431;
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.scene-info-title {
  display: block;
  color: #0f4d2a;
  font-size: 11px;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  font-weight: 700;
}

.scene-info-header {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.scene-info-row {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 2px 0;
}

.scene-info-row > span:first-child {
  color: #5e6e5c;
}

.scene-info-row > span:last-child {
  font-variant-numeric: tabular-nums;
  font-weight: 500;
  color: #1f2f22;
}

.scene-info-note {
  margin: 0;
  padding: 6px 10px;
  background: #ffffff;
  border-left: 2px solid #0f4d2a;
  border-radius: 4px;
  font-size: 11.5px;
  line-height: 1.4;
  color: #4a5a49;
}

.scene-info-list {
  margin: 0;
  border-top: 1px dashed #c8d2c0;
  padding-top: 8px;
}

.scene-info-list > summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 8px;
  padding: 2px 0;
  font-size: 11.5px;
  user-select: none;
  outline: none;
  border-radius: 4px;
}

.scene-info-list > summary::-webkit-details-marker {
  display: none;
}

.scene-info-list > summary:hover .scene-info-list-toggle,
.scene-info-list > summary:focus-visible .scene-info-list-toggle {
  color: #0a3a1f;
  text-decoration: underline;
}

.scene-info-list-count {
  color: #5e6e5c;
}

.scene-info-list-toggle {
  color: #0f4d2a;
  font-weight: 600;
  font-size: 11px;
}

.scene-info-list[open] .scene-info-list-items {
  margin-top: 6px;
}

.scene-info-list-items {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 176px;
  overflow-y: auto;
  border-radius: 6px;
  background: #ffffff;
  border: 1px solid #e3e9de;
}

.scene-info-list-items::-webkit-scrollbar {
  width: 6px;
}

.scene-info-list-items::-webkit-scrollbar-thumb {
  background: #c8d2c0;
  border-radius: 3px;
}

.scene-info-list-item {
  display: grid;
  grid-template-columns: 1fr auto auto;
  align-items: center;
  gap: 10px;
  padding: 6px 10px;
  font-size: 11.5px;
  border-bottom: 1px solid #eef2eb;
}

.scene-info-list-item:last-child {
  border-bottom: none;
}

.scene-info-list-item.is-chosen {
  background: #e6f0df;
}

.scene-info-list-item.is-skipped .scene-info-list-date,
.scene-info-list-item.is-skipped .scene-info-list-meta {
  color: #8b978a;
}

.scene-info-list-date {
  font-weight: 500;
  color: #1f2f22;
  font-variant-numeric: tabular-nums;
}

.scene-info-list-meta {
  color: #5e6e5c;
  font-variant-numeric: tabular-nums;
  font-size: 11px;
  min-width: 3ch;
  text-align: right;
}

.scene-info-list-tag {
  font-size: 10px;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  font-weight: 700;
  padding: 2px 7px;
  border-radius: 999px;
  white-space: nowrap;
}

.scene-info-list-tag.is-chosen {
  background: #0f4d2a;
  color: #ffffff;
}

.scene-info-list-tag.is-used {
  background: #dbe7d0;
  color: #0f4d2a;
}

.scene-info-list-tag.is-skipped {
  background: #edf0e9;
  color: #8b978a;
}

/* Clickable rows — the whole row behaves like a button so the larger touch
   target helps on mobile. Keyboard users get a crisp focus ring. */
.scene-info-list-item.is-clickable {
  cursor: pointer;
  transition: background-color 0.12s ease, transform 0.12s ease;
  -webkit-tap-highlight-color: transparent;
}

.scene-info-list-item.is-clickable:hover {
  background: #f0f6ed;
}

.scene-info-list-item.is-clickable.is-chosen:hover {
  /* Chosen row hover is a no-op action (re-renders the same scene), so we
     don't lift the background — it would hint at an action that isn't there. */
  background: #e6f0df;
}

.scene-info-list-item.is-clickable:active {
  transform: translateY(0.5px);
}

.scene-info-list-item.is-clickable:focus-visible {
  outline: 2px solid #0f4d2a;
  outline-offset: -2px;
  background: #f0f6ed;
}

.scene-info-list-hint {
  margin: 6px 2px 0;
  font-size: 10.5px;
  color: #8b978a;
  line-height: 1.4;
}

/* Manual-pick strategy note gets a subtle amber accent + inline reset link */
.scene-info-note.is-manual {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 8px;
  border-left-color: #c48a17;
  background: #fdf6e3;
  color: #5a4711;
}

.scene-info-reset {
  appearance: none;
  background: none;
  border: none;
  padding: 2px 4px;
  margin: 0;
  color: #0f4d2a;
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  cursor: pointer;
  border-radius: 4px;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.scene-info-reset:hover {
  color: #0a3a1f;
  background: #e6f0df;
}

.scene-info-reset:focus-visible {
  outline: 2px solid #0f4d2a;
  outline-offset: 1px;
}

.timeline-panel {
  margin-top: 12px;
  padding: 12px 14px;
  background: #ffffff;
  border: 1px solid #dfe7d9;
  border-radius: 8px;
  color: #263a29;
}

.timeline-panel[hidden] {
  display: none;
}

.timeline-head,
.timeline-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

.timeline-title {
  color: #0f4d2a;
  font-size: 11px;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  font-weight: 700;
}

.timeline-meta {
  color: #6d7a69;
  font-size: 11px;
  font-variant-numeric: tabular-nums;
}

.timeline-label {
  margin-top: 8px;
  color: #5e6e5c;
  font-size: 11.5px;
}

.timeline-label strong {
  color: #1f2f22;
  font-size: 13px;
  font-variant-numeric: tabular-nums;
}

.scene-slider {
  width: 100%;
  margin: 10px 0 0;
  accent-color: #0f4d2a;
  cursor: pointer;
}

.scene-slider:disabled {
  cursor: default;
  opacity: 0.55;
}

.map-timeline {
  position: absolute;
  z-index: 650;
  width: min(360px, calc(100% - 24px));
  padding: 9px 12px 10px;
  border: 1px solid rgba(17, 17, 17, 0.2);
  border-radius: 8px;
  background: rgba(255, 255, 255, 0.96);
  box-shadow: 0 10px 32px rgba(0, 0, 0, 0.22);
  color: #111111;
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  pointer-events: auto;
  box-sizing: border-box;
}

.map-timeline[hidden] {
  display: none;
}

.map-timeline-track {
  position: relative;
  padding-top: 21px;
}

.map-timeline-date {
  position: absolute;
  top: 0;
  left: 50%;
  transform: translateX(-50%);
  max-width: 100%;
  padding: 2px 8px;
  border-radius: 999px;
  background: #111111;
  color: #ffffff;
  font-size: 11px;
  font-weight: 700;
  line-height: 1.35;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.map-scene-slider {
  display: block;
  width: 100%;
  margin: 0;
  accent-color: #111111;
  cursor: pointer;
}

.map-scene-slider:disabled {
  cursor: default;
  opacity: 0.55;
}

.result-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 10px;
}

.result-actions .secondary-btn {
  margin-top: 0;
  flex: 1 1 142px;
}

.ai-analysis {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 1700;
  display: grid;
  justify-items: end;
  gap: 10px;
  pointer-events: none;
  transition: opacity 0.16s ease, transform 0.16s ease;
}

.ai-analysis-toggle,
.ai-analysis-panel {
  pointer-events: auto;
}

.ai-analysis-toggle {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  min-height: 46px;
  padding: 7px 15px 7px 7px;
  border: 1px solid rgba(20, 84, 52, 0.28);
  border-radius: 999px;
  background: linear-gradient(135deg, #0f4d2a 0%, #145c3d 58%, #1f7048 100%);
  color: #ffffff;
  box-shadow: 0 12px 32px rgba(11, 42, 24, 0.26), inset 0 1px 0 rgba(255,255,255,0.16);
  font: inherit;
  font-size: 13px;
  font-weight: 800;
  cursor: pointer;
  transition: transform 0.16s ease, box-shadow 0.16s ease, border-color 0.16s ease;
}

.ai-analysis-toggle:hover {
  border-color: rgba(246, 204, 92, 0.75);
  box-shadow: 0 14px 38px rgba(11, 42, 24, 0.32), inset 0 1px 0 rgba(255,255,255,0.2);
  transform: translateY(-1px);
}

.ai-analysis-toggle:active {
  transform: translateY(1px) scale(0.99);
}

.ai-analysis-toggle:focus-visible,
.ai-analysis-close:focus-visible,
.ai-analysis-refresh:focus-visible {
  outline: 2px solid #ffffff;
  outline-offset: 2px;
}

.ai-analysis-toggle-mark,
.ai-analysis-avatar {
  display: inline-grid;
  place-items: center;
  flex: 0 0 auto;
  width: 32px;
  height: 32px;
  border-radius: 999px;
  background: #f9fff4;
  color: #0f4d2a;
  box-shadow: inset 0 0 0 1px rgba(15, 77, 42, 0.12), 0 0 0 3px rgba(255,255,255,0.08);
}

.ai-analysis-toggle-text {
  max-width: 122px;
  line-height: 1.05;
  white-space: nowrap;
}

.ai-analysis-icon {
  width: 22px;
  height: 22px;
  display: block;
  overflow: visible;
}

.ai-analysis-icon-orbit,
.ai-analysis-icon-link,
.ai-analysis-icon-stem,
.ai-analysis-icon-vein {
  fill: none;
  stroke-linecap: round;
  stroke-linejoin: round;
}

.ai-analysis-icon-orbit {
  stroke: rgba(28, 120, 78, 0.26);
  stroke-width: 3.2;
}

.ai-analysis-icon-link {
  stroke: #1f7048;
  stroke-width: 3.2;
}

.ai-analysis-icon-stem {
  stroke: #0f4d2a;
  stroke-width: 3.2;
}

.ai-analysis-icon-vein {
  stroke: #ffffff;
  stroke-width: 2.6;
}

.ai-analysis-icon-leaf {
  fill: #8fcf6a;
  stroke: #0f4d2a;
  stroke-width: 2.6;
  stroke-linejoin: round;
}

.ai-analysis-icon-node {
  fill: #f2c84b;
  stroke: #0f4d2a;
  stroke-width: 2.4;
}

.ai-analysis-toggle:hover .ai-analysis-icon-orbit {
  stroke: rgba(242, 200, 75, 0.78);
}

.ai-analysis-avatar {
  width: 34px;
  height: 34px;
  background: #eef8e8;
  box-shadow: inset 0 0 0 1px #c9ddbf;
}

.ai-analysis-avatar .ai-analysis-icon {
  width: 23px;
  height: 23px;
}

.ai-analysis-panel {
  width: min(390px, calc(100vw - 36px));
  max-height: min(560px, calc(100dvh - 96px));
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border: 1px solid rgba(205, 216, 198, 0.95);
  border-radius: 10px;
  background: #ffffff;
  color: #18251c;
  box-shadow: 0 24px 70px rgba(12, 23, 15, 0.26);
}

.ai-analysis-panel[hidden] {
  display: none;
}

.ai-analysis-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 12px 13px;
  border-bottom: 1px solid #dce6d6;
  background: #f7faf5;
}

.ai-analysis-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
}

.ai-analysis-title {
  margin: 0;
  color: #17251b;
  font-size: 15px;
  line-height: 1.2;
}

.ai-analysis-status {
  margin: 2px 0 0;
  color: #667462;
  font-size: 11.5px;
  line-height: 1.3;
}

.ai-analysis-close {
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  border: 1px solid #cbd8c4;
  border-radius: 8px;
  background: #ffffff;
  color: #2f4431;
  font-size: 21px;
  line-height: 1;
  cursor: pointer;
}

.ai-analysis-body {
  min-height: 120px;
  overflow: auto;
  padding: 14px 15px;
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: rgba(15, 77, 42, 0.25) transparent;
}

.ai-analysis-body p {
  margin: 0 0 10px;
  color: #253528;
  font-size: 13px;
  line-height: 1.55;
}

.ai-analysis-body p:last-child {
  margin-bottom: 0;
}

.ai-analysis-body[data-state="empty"] p,
.ai-analysis-body[data-state="ready"] p:first-child {
  color: #4f5f4d;
}

.ai-analysis-body[data-state="error"] p {
  color: var(--err-text);
}

.ai-analysis-loading {
  width: 34px;
  height: 34px;
  margin: 34px auto;
  border: 3px solid #dce8d6;
  border-top-color: #0f4d2a;
  border-radius: 999px;
  animation: ai-analysis-spin 0.8s linear infinite;
}

@keyframes ai-analysis-spin {
  to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .ai-analysis-toggle,
  .ai-analysis-loading {
    transition: none;
    animation: none;
  }
}

.ai-analysis-footer {
  display: flex;
  justify-content: flex-end;
  padding: 10px 13px 12px;
  border-top: 1px solid #edf2e9;
  background: #fbfdf9;
}

.ai-analysis-refresh {
  min-height: 34px;
  padding: 7px 12px;
  border: 1px solid #cbd8c4;
  border-radius: 8px;
  background: #f7faf4;
  color: #0f4d2a;
  font: inherit;
  font-size: 12px;
  font-weight: 800;
  cursor: pointer;
}

.ai-analysis-refresh:hover:not(:disabled) {
  background: #edf5e8;
}

.ai-analysis-refresh:disabled {
  cursor: default;
  opacity: 0.55;
}

.analysis-modal {
  position: fixed;
  inset: 0;
  z-index: 3100;
  display: grid;
  place-items: center;
  padding: 18px;
}

.analysis-modal[hidden] {
  display: none;
}

.analysis-backdrop {
  position: absolute;
  inset: 0;
  border: 0;
  background: rgba(15, 30, 20, 0.42);
  backdrop-filter: blur(7px);
  -webkit-backdrop-filter: blur(7px);
}

.analysis-card {
  position: relative;
  z-index: 1;
  width: min(940px, calc(100vw - 36px));
  max-height: min(780px, calc(100dvh - 36px));
  overflow: hidden;
  display: flex;
  flex-direction: column;
  background: #f7faf5;
  border: 1px solid rgba(205, 216, 198, 0.95);
  border-radius: 8px;
  box-shadow: 0 24px 80px rgba(12, 23, 15, 0.28);
}

.analysis-modal.is-loading .analysis-card {
  width: min(460px, calc(100vw - 36px));
}

.analysis-header {
  display: flex;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 18px 13px;
  border-bottom: 1px solid #dce6d6;
  background: #ffffff;
}

.analysis-title {
  margin: 0;
  color: #18251c;
  font-size: 18px;
  line-height: 1.2;
}

.analysis-subtitle {
  margin: 5px 0 0;
  color: #5f6c5c;
  font-size: 12.5px;
  line-height: 1.45;
  font-variant-numeric: tabular-nums;
}

.analysis-subtitle:empty {
  display: none;
}

.analysis-close {
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
  border: 1px solid #cbd8c4;
  border-radius: 8px;
  background: #f7faf4;
  color: #2f4431;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
}

.analysis-close:hover {
  background: #edf5e8;
}

.analysis-close:focus-visible,
#analysis-csv-btn:focus-visible {
  outline: 2px solid #0f4d2a;
  outline-offset: 2px;
}

.analysis-status {
  min-height: 18px;
  padding: 10px 20px 0;
  color: #455444;
  font-size: 13px;
}

.analysis-status:empty {
  display: none;
}

.analysis-status.is-error {
  color: var(--err-text);
}

.analysis-content {
  overflow: auto;
  padding: 16px 18px 18px;
  overscroll-behavior: contain;
}

.analysis-content[hidden] {
  display: none;
}

.analysis-summary {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 10px;
  margin-bottom: 12px;
}

.analysis-summary-card {
  min-width: 0;
  padding: 10px 11px;
  border: 1px solid #dce5d7;
  border-left: 4px solid #9ca89a;
  border-radius: 8px;
  background: #ffffff;
}

.analysis-summary-card.is-normal {
  border-left-color: #177245;
}

.analysis-summary-card.is-watch {
  border-left-color: #c28a09;
}

.analysis-summary-card.is-stress {
  border-left-color: #c24124;
}

.analysis-summary-card.is-neutral {
  border-left-color: #6f7d6b;
}

.analysis-summary-label {
  display: block;
  color: #657462;
  font-size: 11px;
  line-height: 1.3;
}

.analysis-summary-value {
  display: block;
  margin-top: 4px;
  color: #18251c;
  font-size: 17px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.analysis-chart-wrap {
  border: 1px solid #dce5d7;
  border-radius: 8px;
  background: #ffffff;
  overflow: hidden;
}

.analysis-chart {
  display: block;
  width: 100%;
  height: auto;
  min-height: 400px;
}

.analysis-table-wrap {
  margin-top: 12px;
  max-height: 260px;
  overflow: auto;
  border: 1px solid #dce5d7;
  border-radius: 8px;
  background: #ffffff;
}

.analysis-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 11.5px;
}

.analysis-table th,
.analysis-table td {
  padding: 8px 9px;
  border-bottom: 1px solid #edf2e9;
  text-align: left;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.analysis-table th {
  position: sticky;
  top: 0;
  z-index: 1;
  background: #f4f7f1;
  color: #43533f;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.35px;
}

.analysis-table tr:last-child td {
  border-bottom: 0;
}

.analysis-signal {
  display: inline-flex;
  align-items: center;
  min-height: 22px;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
}

.analysis-metric-pill {
  display: inline-flex;
  justify-content: center;
  min-width: 44px;
  padding: 2px 7px;
  border-radius: 999px;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.analysis-metric-pill.is-normal {
  background: #e8f3e4;
  color: #16603a;
}

.analysis-metric-pill.is-watch {
  background: #fff4c7;
  color: #6d5100;
}

.analysis-metric-pill.is-stress {
  background: #fde8e2;
  color: #8a2c18;
}

.analysis-metric-pill.is-neutral {
  background: #eef2ea;
  color: #52604f;
}

.analysis-signal.is-normal {
  background: #e7f2df;
  color: #0f4d2a;
}

.analysis-signal.is-watch {
  background: #fff2cc;
  color: #6a4a00;
}

.analysis-signal.is-stress {
  background: #fde4dc;
  color: #8a2c18;
}

.analysis-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 14px;
}

.analysis-actions .secondary-btn {
  margin-top: 0;
}

body.analysis-modal-open {
  overflow: hidden;
}

.panel--map {
  position: relative;
  min-height: 400px;
}

#map {
  position: absolute;
  inset: 0;
  /* Belt-and-suspenders: Leaflet already sets `touch-action: none` on
     .leaflet-container, but some mobile chromium derivatives still
     propagate the first downward drag to the document's overscroll
     behaviour before Leaflet's handler claims it. `contain` locks the
     scroll chain at the map boundary so pull-to-refresh never fires,
     even on that first pre-claim frame. */
  overscroll-behavior: contain;
}

.legend {
  position: absolute;
  bottom: 18px;
  left: 18px;
  z-index: 400;
  background: rgba(255,255,255,0.96);
  padding: 10px 12px;
  border-radius: 10px;
  box-shadow: 0 2px 12px rgba(0,0,0,0.12);
  font-size: 11px;
  min-width: 180px;
}

.legend-title {
  font-weight: 700;
  color: #0f4d2a;
  margin-bottom: 6px;
}

.legend-gradient {
  height: 10px;
  border-radius: 4px;
  margin-bottom: 4px;
}

.legend-ticks {
  display: flex;
  justify-content: space-between;
  color: #4a6048;
  font-variant-numeric: tabular-nums;
}

/* ---- Sheet handle (drag grip) — mobile only -------------------------- */
/* Desktop: hidden. On mobile the bottom-sheet uses this pill as its primary
   drag target. Sticky so it stays reachable after the user has scrolled the
   sheet's contents. */
.sheet-handle { display: none; }

/* ---- Inline GPS button inside the search-box ------------------------- */
/* Visible on all breakpoints, invaluable on mobile where drawing a precise
   rectangle with a finger is painful. One tap → center + auto-select a
   500m × 500m square at the user's location. */
.gps-btn {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  padding: 0;
  border: 1px solid #c4d0bf;
  border-radius: 8px;
  background: #fafcf8;
  color: #2f4431;
  cursor: pointer;
  /* inline-flex + align/justify is robust across Safari/Chrome/Firefox
     and immune to inherited text-align from parents. grid+place-items has
     an older-Safari quirk on buttons where the SVG anchors to one edge. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 0;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
  -webkit-tap-highlight-color: transparent;
}

.gps-btn svg {
  display: block;
  flex: none;
}

.gps-btn:hover { background: #f0f6ed; border-color: #0f4d2a; color: #0f4d2a; }
.gps-btn:active { background: #e2ecdc; }
.gps-btn:focus-visible { outline: 2px solid #0f4d2a; outline-offset: 2px; }
/* Busy: the whole SVG spins around its concentric center dot — reads as
   "locating…" better than fading the glyph and works at any size. */
.gps-btn[data-state="busy"] { cursor: progress; color: #0f4d2a; }
.gps-btn[data-state="busy"] svg { animation: gps-spin 1s linear infinite; transform-origin: 50% 50%; }
.gps-btn[data-state="error"] {
  border-color: var(--err-border);
  color: var(--err-text);
  background: var(--err-bg);
}

@keyframes gps-spin {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}


/* ---- Mobile layout: bottom sheet ------------------------------------- */
/*
   Activates on narrow OR short viewports — so landscape phones (iPhone 14
   is 844×390-ish in landscape) get the sheet instead of a cramped 360px
   sidebar eating half the horizontal space. Desktops with ≥500px height
   stay on the desktop layout unless they're also narrow.

   Three snap heights driven by JS (`data-snap` = peek | half | full).
   Transition disabled mid-drag so the finger follows the sheet 1:1.

   Why a sheet, not a left drawer: better one-hand thumb reach, no conflict
   with iOS's edge-swipe back gesture, and when the on-screen keyboard
   appears the sheet promotes itself to "full" so the focused input never
   ends up hidden.
*/
@media (max-width: 760px), (max-height: 500px) {
  /* flex column (not grid/block) because .panel--map needs a resolvable
     height, not `height: 100%` against an auto parent. `.shell` is
     flex-column with `.layout` set to flex:1, which gives layout an
     explicit pixel height; flex:1 on .panel--map then gives IT an explicit
     height too, so #map (position:absolute inset:0 inside) finally has
     something to anchor against. Without this, mapOffsetH = 0. */
  .layout,
  .layout.is-panel-collapsed {
    display: flex;
    flex-direction: column;
    position: relative;
    overflow: hidden;
    transition: none;
  }

  .panel--map {
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
  }

  /* The desktop chevron has no place on mobile — the bottom-sheet handle
     replaces it. */
  .panel-toggle { display: none; }

  /* The side-panel becomes a fixed bottom sheet whose height is driven by
     a CSS variable set from JS. `overscroll-behavior: contain` stops the
     over-fling at the top from pulling the page behind. */
  /* z-index 1200 sits the sheet ABOVE every Leaflet control (zoom,
     draw, edit/delete, attribution — all ~1000). Topbar still wins at
     1500 so short viewports never lose the brand row. */
  .panel--controls,
  .layout.is-panel-collapsed .panel--controls {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    top: auto;
    height: var(--sheet-h, 56px);
    /* --topbar-h comes from JS (measureTopbar). CSS + JS share the cap
       so the snap math and the visual max-height agree to the pixel.
       100dvh tracks the *visible* viewport (keyboard open, address bar
       expanded) so the sheet never over-shoots and covers the topbar. */
    max-height: calc(100vh - var(--topbar-h, 54px) - 8px);
    max-height: calc(100dvh - var(--topbar-h, 54px) - 8px);
    border: none;
    border-top: 1px solid #dde4d8;
    border-radius: 18px 18px 0 0;
    background: #fff;
    box-shadow: 0 -6px 24px rgba(10, 30, 20, 0.14),
                0 -1px 0 rgba(255, 255, 255, 0.9) inset;
    z-index: 1200;
    overflow-y: auto;
    overflow-x: hidden;
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
    padding: 0 18px 18px;  /* safe-area override in the standalone block */
    pointer-events: auto;
    transition: height 340ms cubic-bezier(0.32, 0.72, 0, 1);
    /* Tame the native scrollbar so it doesn't look like it's leaking out of
       the 18 px rounded top corners on persistent-scrollbar engines
       (Samsung Internet, Kiwi, MIUI, desktop emulators). iOS/Android Chrome
       already use thin overlay scrollbars; this just aligns the rest. */
    scrollbar-width: thin;
    scrollbar-color: rgba(15, 77, 42, 0.28) transparent;
  }

  .panel--controls::-webkit-scrollbar { width: 5px; }
  .panel--controls::-webkit-scrollbar-track { background: transparent; }
  .panel--controls::-webkit-scrollbar-thumb {
    background: rgba(15, 77, 42, 0.28);
    border-radius: 3px;
  }
  .panel--controls::-webkit-scrollbar-thumb:hover {
    background: rgba(15, 77, 42, 0.45);
  }

  /* While the user is mid-drag we want instantaneous feedback, not easing. */
  .panel--controls[data-dragging="true"] {
    transition: none;
  }

  /* Sticky handle at the top of the sheet. Binary open/closed model:
     the chevron rotates 180° between states (points up when closed —
     "open me"; points down when open — "collapse me"). 44px tall hit
     area. `touch-action: none` is REQUIRED — otherwise iOS steals
     vertical pan and drag-to-toggle dies. */
  .sheet-handle {
    display: flex;
    align-items: center;
    justify-content: center;
    position: sticky;
    top: 0;
    left: -18px;
    right: -18px;
    width: calc(100% + 36px);
    margin: 0 -18px 4px;
    padding: 10px 0 8px;
    border: none;
    background: #fff;
    color: #4a6048;
    cursor: pointer;
    touch-action: none;
    -webkit-tap-highlight-color: transparent;
    z-index: 3;
    border-radius: 18px 18px 0 0;
    transition: color 0.15s ease;
  }
  .sheet-handle:hover,
  .sheet-handle:focus-visible { color: #0f4d2a; outline: none; }
  .sheet-handle:active { cursor: grabbing; }

  .sheet-handle-chevron {
    display: block;
    transition: transform 320ms cubic-bezier(0.32, 0.72, 0, 1);
  }
  /* Closed state → chevron up ("tap to open"). Open state → chevron
     down ("tap to close"). Direction tells user where the card will go. */
  .panel--controls[data-snap="open"] .sheet-handle-chevron {
    transform: rotate(180deg);
  }

  /* When closed, hide every form element so nothing peeks above the
     handle row. visibility:hidden preserves the scroll container's
     sizing without rendering anything. Handle stays visible via explicit
     exclusion. Result: closed state = ONLY the chevron-bearing strip. */
  .panel--controls[data-snap="closed"] > *:not(.sheet-handle) {
    visibility: hidden;
  }

  /* Run lives in the normal document flow at the END of the form (after
     layer chips). User scrolls to reach it — same as desktop. Two
     earlier attempts failed here:
       1. sticky bottom:0 never engages because Run's natural position at
          half-snap is below the viewport, so sticky doesn't pull it up.
       2. position:absolute bottom:14 pinned Run to the sheet bottom —
          but then it floated OVER the layer chips below "NDVI", making
          the last four chips (NDMI/NDRE/LST/SAR) untappable on mobile.
     Flow positioning avoids both traps: content is always reachable by
     scrolling, and at peek the sheet is only 56px tall so Run is
     naturally offscreen without any opacity tricks. */
  .panel--controls #run-btn {
    margin-top: 16px;
  }

  /* Leaflet controls inherit the desktop default (34×34 pills, 300×30
     sprite) as-is. Every previous attempt to enlarge them on mobile
     introduced either sprite bleed, missing icons, or size mismatches
     between zoom and draw/edit pills. The desktop layout is already
     battle-tested and looks right; keeping it unchanged on mobile is
     the simplest correct answer. Safe-area overrides are applied only
     when a PWA is installed (see the `display-mode: standalone` block). */
  .leaflet-top.leaflet-right {
    padding-top: 12px;
    padding-right: 12px;
  }

  /* Legend tracks the sheet's height so it never hides under the sheet. */
  .legend {
    left: 12px;
    right: auto;
    bottom: calc(var(--sheet-h, 140px) + 12px);
    max-width: calc(100vw - 24px);
    transition: bottom 340ms cubic-bezier(0.32, 0.72, 0, 1);
  }

  /* Slight mobile squeeze on the layer chip grid so all six layer labels
     (NDVI / NDMI / NDRE / True-color / LST / SAR) fit without awkward
     wrapping. */
  .layer-chips { gap: 8px; }
  .layer-chip { padding: 12px 8px; }

  /* Tap targets inside the sheet go up to a finger-friendly size. */
  .date-row input,
  .cloud-row input { padding: 10px; font-size: 14px; }
  .cloud-row input { max-width: 100%; }
  .search-box input { padding: 11px 12px; font-size: 14px; }
  .gps-btn { width: 42px; height: 42px; }

  .timeline-panel {
    padding: 11px 12px;
  }

  .scene-slider {
    min-height: 32px;
  }

  .ai-analysis {
    right: 12px;
    bottom: calc(var(--sheet-h, 56px) + 12px);
    max-width: calc(100vw - 24px);
    transition: bottom 340ms cubic-bezier(0.32, 0.72, 0, 1),
                opacity 0.16s ease,
                transform 0.16s ease;
  }

  .ai-analysis-toggle {
    min-height: 44px;
    padding-right: 12px;
  }

  .ai-analysis-panel {
    width: min(360px, calc(100vw - 24px));
    max-height: min(440px, calc(100dvh - var(--topbar-h, 54px) - var(--sheet-h, 56px) - 28px));
  }

  html[data-sheet-snap="open"] .ai-analysis {
    opacity: 0;
    pointer-events: none;
    transform: translateY(8px);
  }

  .analysis-modal {
    align-items: end;
    padding: 0;
  }

  .analysis-card {
    width: 100%;
    max-height: calc(100dvh - 22px);
    border-radius: 14px 14px 0 0;
  }

  .analysis-modal.is-loading .analysis-card {
    width: min(420px, 100%);
  }

  .analysis-header {
    padding: 16px 16px 12px;
  }

  .analysis-content {
    padding: 12px 16px 18px;
  }

  .analysis-summary {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .analysis-chart {
    min-height: 360px;
  }

  .analysis-table th,
  .analysis-table td {
    padding: 8px;
  }
}


/* ---- Topbar profile button -------------------------------------------- */

.profile-btn {
  position: relative;
  display: inline-grid;
  place-items: center;
  width: 32px;
  height: 32px;
  padding: 0;
  background: transparent;
  color: #d9f0e1;
  border: none;
  border-radius: 8px;
  cursor: pointer;
  transition: background-color 0.14s ease, color 0.14s ease;
  -webkit-tap-highlight-color: transparent;
}

.profile-btn:hover {
  background-color: rgba(255, 255, 255, 0.12);
  color: #fff;
}

.profile-btn:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

.profile-btn svg { display: block; }

/* ---- Service worker update banner ----------------------------------------
   Shown when a newer SW has finished installing while the user's current
   tab is still running the previous build in memory. A tap on "Yenile"
   forces a full reload so the stale JS in memory is replaced. Fixed at
   the bottom so it never obscures the topbar or main controls; z-index
   above the bottom sheet (1200) but below the modal (3000). */
.update-banner {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: 16px;  /* safe-area override in the standalone block */
  z-index: 1800;
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 14px 10px 16px;
  background: #0f4d2a;
  color: #fff;
  border-radius: 999px;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.28);
  font-size: 13px;
  font-weight: 500;
  max-width: calc(100vw - 24px);
  animation: update-banner-in 0.22s ease-out;
}

@keyframes update-banner-in {
  from { opacity: 0; transform: translate(-50%, 12px); }
  to   { opacity: 1; transform: translate(-50%, 0); }
}

.update-banner-btn {
  appearance: none;
  border: none;
  background: #fff;
  color: #0f4d2a;
  font: inherit;
  font-weight: 700;
  padding: 6px 14px;
  border-radius: 999px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color 0.14s ease;
}

.update-banner-btn:hover { background: #e9f5ee; }
.update-banner-btn:active { background: #dbe9d4; }
.update-banner-btn:focus-visible {
  outline: 2px solid #fff;
  outline-offset: 2px;
}

/* ---- PWA standalone safe-area insets ----------------------------------
   env(safe-area-inset-*) is ONLY honored when the app is running in
   installed-PWA standalone/fullscreen mode. Regular browsers (Chrome,
   Safari, Firefox, Edge) do NOT need our layout to respect device
   insets — their own chrome already covers the device's unsafe zones.

   Quirky chromium derivatives (Hola, Yandex, UC, some Samsung Internet
   builds) report non-zero env() values in regular browser mode because
   they treat their own custom toolbar as "unsafe". Without this scoping
   the topbar balloons to 80+ px, Leaflet controls slide down, the
   legend shifts inward, and the sheet grows a huge bottom pad.

   By putting every env() consumer behind display-mode:standalone we
   keep the PWA experience pixel-correct on iOS home-screen installs
   and Android TWA/installed-PWAs, while every browser tab renders
   with the pristine fixed baselines above. */
@media (display-mode: standalone), (display-mode: fullscreen), (display-mode: minimal-ui) {
  .topbar {
    padding: max(14px, env(safe-area-inset-top, 0px))
             max(20px, env(safe-area-inset-right, 0px))
             14px
             max(20px, env(safe-area-inset-left, 0px));
  }

  .update-banner {
    bottom: max(16px, env(safe-area-inset-bottom, 0px));
  }
}

@media (display-mode: standalone) and (max-width: 760px),
       (display-mode: fullscreen) and (max-width: 760px),
       (display-mode: minimal-ui) and (max-width: 760px) {
  .leaflet-top.leaflet-right {
    padding-top: max(12px, env(safe-area-inset-top, 0px));
    padding-right: max(12px, env(safe-area-inset-right, 0px));
  }

  .leaflet-top.leaflet-left {
    padding-top: max(12px, env(safe-area-inset-top, 0px));
    padding-left: max(12px, env(safe-area-inset-left, 0px));
  }

  .panel--controls,
  .layout.is-panel-collapsed .panel--controls {
    padding-bottom: max(18px, env(safe-area-inset-bottom, 0px));
  }

  .legend {
    left: max(12px, env(safe-area-inset-left, 0px));
  }

  .ai-analysis {
    right: max(12px, env(safe-area-inset-right, 0px));
  }
}
