
/* Manrope/DM Sans now load as a normal stylesheet in every page's own
   <head> (lib/layout.js getFontsPreload()) instead of via an @import here —
   this file only reaches the page through the portal's async JS injection,
   so importing the font from inside it meant the whole page (not just the
   portal) rendered in a fallback font until that entire fetch-then-inject
   chain finished, then visibly flashed to the real font. */
:root{--blue:#123d33!important;--blue-d:#0b2c25!important;--blue-l:#2f6b5c!important;--blue-dim:rgba(18,61,51,.08)!important;--blue-b:rgba(18,61,51,.2)!important;--green:#c8f36a!important;--green-d:#123d33!important;--green-dim:rgba(200,243,106,.15)!important;--green-b:rgba(96,135,39,.28)!important;--cream:#eef0e9!important;--cream-d:#e1e5dc!important;--bdr:#d9dfd8!important;--bdr-l:#e9ece8!important;--txt:#14231e!important;--r-sm:9px!important;--r-md:12px!important;--r-lg:18px!important;--r-xl:24px!important;--font-h:'Manrope',sans-serif!important;--font-b:'DM Sans',sans-serif!important;--shadow-sm:0 2px 8px rgba(11,31,25,.05)!important;--shadow-md:0 15px 45px rgba(11,31,25,.12)!important;--shadow-lg:0 30px 90px rgba(5,20,15,.25)!important}
*{scrollbar-width:thin;scrollbar-color:#aab8b2 transparent}html{-webkit-text-size-adjust:100%}body{background:#e8ece5!important;overflow-x:hidden}
/* Hero is its own solid-background section, never on top of map tiles.
   Everything about it (background/padding/layout/type) now lives in the
   shared /css/hero.css (.xrt-hero*), loaded by questionnaire-v2.html itself
   and already tuned to this exact dark production look — this only sets
   stacking context, nothing more. */
.hero{position:relative!important;z-index:1;left:auto;top:auto;pointer-events:auto}
/* Workspace: map and search panel side by side. 2026-10-11: both columns are
   now equal, fixed-height boxes (see the >=1024px block below) instead of
   one sizing off the other — the earlier sticky-map/free-scrolling-form
   design (kept the whole PAGE scrolling, which was the fix for an even
   older version that trapped page scroll inside the form's own scrollbox)
   let the form grow arbitrarily tall, which was exactly why the Search
   button could end up off-screen. This version still lets the page scroll
   normally past .wrap to .results below — only .form-scroll (the options
   list inside .form-card) scrolls internally, with overscroll-behavior so
   that inner scroll never chains into/traps the outer page scroll the way
   the old trapped-page version did. */
.wrap{max-width:none!important;margin:0!important;padding:0 32px!important;display:grid!important;grid-template-columns:minmax(0,1fr) minmax(380px,420px)!important;gap:6px!important;align-items:start!important;position:relative;height:auto!important;min-height:520px!important}
/* .map-card is a flex column — the control bar (.aoi-toolbar), then #map —
   at every breakpoint, so the bar docks directly above the map in normal
   flow (no gap, no floating over the tiles) and #map fills whatever height
   is left under it. See .aoi-toolbar below for the bar itself. Sizing
   (height/position) is breakpoint-specific — see the >=1024px and <1024px
   blocks below — so only the breakpoint-independent visual properties live
   here, to avoid two different height rules fighting each other. */
.map-card{border:0!important;border-radius:var(--r-lg,18px)!important;box-shadow:var(--shadow-md)!important;overflow:hidden!important;display:flex!important;flex-direction:column!important}
#map{flex:1 1 auto!important;height:auto!important;min-height:0!important;width:100%!important;filter:saturate(.88) contrast(1.03)}
/* Samples-ready arrow: pinned to the sticky map card so it's visible
   regardless of how far the visitor has scrolled through the form/results,
   shown once a search returns results (see renderResults() in
   questionnaire-v2.html). */
.samples-arrow{position:absolute!important;z-index:870;right:16px;bottom:78px;display:none;align-items:center;gap:7px;background:var(--green)!important;color:var(--txt)!important;border:0!important;border-radius:999px!important;padding:9px 16px 9px 14px!important;font:700 12.5px var(--font-h,'Manrope',sans-serif)!important;box-shadow:0 12px 30px rgba(0,0,0,.25);cursor:pointer;animation:xrt-samples-bounce 1.8s ease-in-out infinite}
.samples-arrow svg{width:15px;height:15px}
.samples-arrow:hover{filter:brightness(1.05)}
@keyframes xrt-samples-bounce{0%,100%{transform:translateY(0)}50%{transform:translateY(4px)}}
/* "Ready to search" arrow: pinned to the opposite corner of the same sticky
   map card, shown the moment an AOI exists (auto-placed or drawn) but no
   search has run yet — see showAoiReadyArrow()/setAoi() in
   questionnaire-v2.html. Distinct color from samples-arrow (blue = "do this
   next" vs green = "results are in") so the two are never mistaken for each
   other on the rare frame where both could be visible. */
.aoi-ready-arrow{position:absolute!important;z-index:870;left:16px;bottom:78px;display:none;align-items:center;gap:7px;background:var(--blue)!important;color:#fff!important;border:0!important;border-radius:999px!important;padding:9px 16px 9px 14px!important;font:700 12.5px var(--font-h,'Manrope',sans-serif)!important;box-shadow:0 12px 30px rgba(0,0,0,.25);cursor:pointer;animation:xrt-samples-bounce 1.8s ease-in-out infinite}
.aoi-ready-arrow svg{width:15px;height:15px}
.aoi-ready-arrow:hover{filter:brightness(1.1)}
/* Retired at every width (2026-10-11) — the mobile sticky search bar
   (.mobile-search-bar below) now does this nudge's job on mobile too, and
   desktop already had this hidden (see the removed min-width:1024px rule
   that used to live here). !important beats showAoiReadyArrow()'s inline
   style.display='flex' in questionnaire-v2.html, so the dead code path
   there stays harmless rather than needing to be ripped out too. */
.wrap:has(.form-scroll) #aoi-ready-arrow{display:none!important}
/* Brief highlight on the Search button itself when the arrow above is
   clicked — confirms to the visitor that this is the control the arrow
   meant, right as it scrolls into view. Self-removing (see the click
   handler), not a permanent state. */
@keyframes xrt-cta-pulse{0%,100%{box-shadow:0 0 0 0 var(--blue-b)}50%{box-shadow:0 0 0 10px var(--blue-b)}}
#btn-search.cta-pulse{animation:xrt-cta-pulse 1.1s ease-out 2}
/* Empty state shown in place of #results before the first search (or after
   a failed one) — see the placeholder markup right after .wrap and its
   show/hide toggles in the search handler / renderResults() in
   questionnaire-v2.html. */
.results-placeholder{max-width:640px;margin:48px auto!important;padding:40px 28px!important;text-align:center;border:1px dashed var(--bdr)!important;border-radius:var(--r-lg,18px)!important;background:#fff!important;color:#5b6b63}
.results-placeholder__icon{font-size:28px;margin-bottom:10px}
.results-placeholder__title{font:800 18px var(--font-h,'Manrope',sans-serif)!important;color:var(--txt)!important;margin:0 0 8px}
.results-placeholder__sub{font-size:13.5px;line-height:1.55;margin:0;color:#5b6b63}
.map-card:after{content:'';position:absolute;inset:0;pointer-events:none;background:linear-gradient(180deg,rgba(5,24,18,.16),transparent 22%);z-index:400}
/* AOI toolbar (location search + draw tools) is wrapped by JS into
   .aoi-toolbar — ONE horizontal row (search input, then Rectangle / Polygon
   / Clear / Satellite, in that existing order) docked directly above the
   map at every breakpoint: in normal flow, flush against #map with no gap,
   never floating over the tiles. It never wraps to a second row — when the
   row is wider than the map card it scrolls horizontally instead (see the
   overflow-x rules below); the scrollbar is hidden visually but the row
   stays swipeable/draggable. This is one unguarded rule (no media query)
   because the behaviour is identical at every width — only whether the
   overflow-x scroll actually engages changes, and that's automatic.
   The card look (background/border/blur/shadow/radius) now lives on
   .aoi-toolbar itself, ONE seamless bar, instead of being duplicated on
   .search-row and .map-tools separately — that duplication (two shadows,
   two border-radii, a gap between them) was both the "two separate pills"
   look and most of the extra bulk. .search-row gets a thin right border as
   the only seam between search and the buttons. Button height stays 44px
   (the existing accessible tap-target size — not shrunk, since the bulk
   was coming from the doubled-up container padding/shadows around it, not
   from the buttons themselves); with the outer padding trimmed to 3px the
   whole bar lands at ~50px tall. */
.aoi-toolbar{position:static!important;z-index:auto;display:flex!important;flex-direction:row!important;flex-wrap:nowrap!important;align-items:center;gap:0;width:100%;max-width:none;flex:0 0 auto;padding:3px;overflow-x:auto;-webkit-overflow-scrolling:touch;scrollbar-width:none;pointer-events:auto;border:1px solid rgba(255,255,255,.28)!important;background:rgba(8,30,24,.82)!important;backdrop-filter:blur(15px);border-radius:12px!important;box-shadow:0 12px 35px rgba(0,0,0,.2)}
.aoi-toolbar::-webkit-scrollbar{display:none}
.search-row{position:relative!important;z-index:1;padding:0!important;background:transparent!important;border:0!important;border-right:1px solid rgba(255,255,255,.2)!important;width:min(260px,60vw);flex:0 0 auto;display:flex;align-items:center}
.search-row input{min-height:44px!important;height:44px!important;border:0!important;border-radius:0!important;background:transparent!important;backdrop-filter:none;color:#fff!important;padding:0 14px!important;box-shadow:none;width:100%}
.search-row input::placeholder{color:rgba(255,255,255,.65)}
/* Position/z-index are JS-driven now (positionSuggestions() in v2, computed
   from #loc-search's real bounding rect) — v6 no longer hardcodes them. */
.map-tools{position:relative!important;z-index:1;display:flex!important;flex-wrap:nowrap!important;flex:0 0 auto;gap:6px;padding:0 0 0 5px!important;border:0!important;background:transparent!important;backdrop-filter:none;border-radius:0!important;box-shadow:none}
.tb-btn,.tb-layer{min-height:44px!important;height:44px!important;border:0!important;background:transparent!important;color:#fff!important;padding:0 13px!important;border-radius:8px!important;transition:background .15s;flex:0 0 auto}
.tb-btn:hover{background:rgba(255,255,255,.14)!important}
.tb-btn.active{background:var(--green)!important;color:var(--txt)!important}
.tb-btn.danger{color:#ffb5ad!important}
.tb-btn.danger:hover{background:#742f29!important;color:#fff!important}
.tb-layer{border-left:1px solid rgba(255,255,255,.2)!important;border-radius:0!important}
.tb-layer option{color:#111}
/* .map-foot (area/coords chip) moves to bottom-right, clear of both the
   control bar now docked above the map and the Leaflet/Esri attribution
   strip at the very bottom (36px clears the ~20px attribution row). It sat
   top-right before, which only worked while the toolbar was bottom-
   anchored or floating in a different corner — now that the bar always
   occupies the top of the map, top-right would sit directly under it. */
.map-foot{position:absolute!important;z-index:820;right:16px;bottom:36px;top:auto;left:auto;min-width:0;border:1px solid rgba(255,255,255,.25)!important;border-radius:12px!important;background:rgba(8,30,24,.78)!important;backdrop-filter:blur(15px);color:rgba(255,255,255,.65)!important;padding:10px 13px!important;max-width:min(320px,60vw)}
.map-foot b{color:#fff!important}
/* Leaflet's own zoom control sits top-left of #map — safe by construction
   now (the control bar docks above #map in normal flow, so it can never
   float over the tiles or the zoom control), just enlarge the hit area to
   the 44px minimum. */
.leaflet-control-zoom a{width:44px!important;height:44px!important;line-height:44px!important;font-size:18px!important}
/* Search panel: docked sidebar with its own scroll — the map pane never
   scrolls with it. */
.form-card{position:relative!important;z-index:1;top:auto!important;right:auto!important;width:auto!important;max-width:none!important;height:auto!important;max-height:none!important;display:flex!important;flex-direction:column;border:0!important;border-left:1px solid var(--bdr)!important;border-radius:0!important;background:#fff!important;backdrop-filter:none!important;box-shadow:none!important;container-type:inline-size;container-name:form-panel}
.form-header{position:relative!important;top:auto!important;flex:0 0 auto;z-index:2}
.form-body{flex:1 1 auto;overflow-y:visible!important;overflow:visible!important;-webkit-overflow-scrolling:touch}
@container form-panel (max-width: 360px){.row2{grid-template-columns:1fr!important}}
.form-header{background:#fff!important;border-bottom:1px solid var(--bdr)!important;padding:17px 20px!important}.form-header .fh-t{font-size:19px!important;color:var(--txt)!important;letter-spacing:-.03em}.form-header .fh-s{color:#697771!important}.form-body{padding:16px 20px 28px!important}.step-h{font-size:12.5px!important;margin:20px 0 9px!important}.step-h:first-child{margin-top:0!important}.step-num{background:var(--green)!important;color:var(--txt)!important;width:21px!important;height:21px!important}.pills{gap:5px!important}.pill{min-height:44px!important;display:inline-flex!important;align-items:center;font-size:11px!important;padding:7px 12px!important;border-radius:9px!important;background:#fff!important}.pill.active{background:var(--blue)!important;color:#fff!important}.lbl{font-size:9.5px!important;text-transform:uppercase;letter-spacing:.075em!important;margin:9px 0 5px!important}.inp,.sel{min-height:44px!important;padding:7px 10px!important;font-size:12.5px!important}.chk-classes{gap:12px!important}.chk-classes label{min-height:44px!important;padding:14px 16px!important;font-size:11.5px!important}.chk-classes small{font-size:9.5px!important}.branch-note{padding:9px 11px!important;font-size:11px!important;margin-top:9px!important}.estimate{background:#0d3027!important;margin:12px -20px 0!important;border-radius:0!important;padding:13px 20px!important;position:relative}.est-val{font-size:21px!important}.est-list{font-size:13px!important}.est-save{color:var(--txt)!important;background:var(--green)!important}.est-row{font-size:10.5px!important}.cta{min-height:44px!important;background:var(--green)!important;color:var(--txt)!important;border-radius:10px!important;padding:12px!important;font-size:13px!important;box-shadow:none!important;margin-top:10px!important}.cta.blue{background:var(--blue)!important;color:#fff!important}.cta:hover{filter:brightness(1.08)!important;transform:translateY(-1px)}.cta-sub{font-size:9.5px!important}.row2{gap:7px!important}
input[type=range]{min-height:44px!important;display:block}
.modal-x{min-width:44px!important;min-height:44px!important}
/* Errors must interrupt attention — visually distinct from the green
   informational "Live search" note (different bg/border + warning icon). */
.err-box{font-size:12px!important;font-weight:600;padding:10px 12px 10px 38px!important;position:relative;border-left:4px solid var(--err)!important;background:#fdeceb!important}
.err-box:before{content:'⚠';position:absolute;left:12px;top:9px;font-size:13px;line-height:1}
/* Capture mode: visual routes, synchronized to the original v2 select. */
#mode{position:absolute!important;width:1px!important;height:1px!important;opacity:0!important;pointer-events:none!important}
.v6-mode-picker{display:grid;grid-template-columns:1fr 1fr;gap:12px;margin:20px 0 10px}
.v6-mode{position:relative;text-align:left;border:1px solid var(--bdr);border-radius:11px;background:#fff;padding:14px 16px 14px 44px;min-height:51px;color:var(--txt);transition:.14s}
.v6-mode:hover{border-color:#8aa096;background:#f7faf7}
/* Was border-color + an inset box-shadow stacked on top of the base 1px
   border — two overlapping 1px lines at the same border-radius don't
   anti-alias cleanly at 1x pixel density, so the bottom of the curve
   rendered thin/washed out (worst on whichever card is selected, e.g.
   Emergency). A single 2px border reads as one crisp line instead. */
.v6-mode.active{border:2px solid var(--blue);background:#edf4f0}
.v6-mode .mi{position:absolute;left:14px;top:14px;width:18px;height:18px;border-radius:6px;background:#e9eeea;display:grid;place-items:center;font:800 10px Manrope}
.v6-mode.active .mi{background:var(--green)}
.v6-mode b{display:block;font:700 10.5px Manrope;line-height:1.2}
.v6-mode small{display:block;font-size:8.7px;line-height:1.25;color:#718078;margin-top:3px}
.v6-mode.more{grid-column:auto;min-height:51px;padding:14px 16px 14px 44px}
/* SAR / Hyperspectral / Not sure: same left-aligned icon+title+subtitle
   layout as the main routes — the old centered 8px padding gave the
   absolutely-positioned .mi icon box no reserved space, so it sat directly
   on top of the first 1-2 characters of the title/subtitle. Wraps to 2
   columns, then 1, instead of squeezing 3 across on narrow widths. */
.v6-mode-special{display:none;grid-column:1/-1;grid-template-columns:repeat(3,1fr);gap:12px;margin-top:2px}
.v6-mode-special.open{display:grid}
.v6-mode-special .v6-mode{padding:14px 16px 14px 44px;text-align:left;min-height:51px}
.v6-mode-special .v6-mode b{font-size:10.5px}
.v6-mode-special .v6-mode small{font-size:8.7px}
.v6-mode-caption{grid-column:1/-1;font-size:8.5px;color:#75827c;margin:-1px 1px 1px}
@media(max-width:620px){.v6-mode-special{grid-template-columns:repeat(2,1fr)}}
@media(max-width:400px){.v6-mode-special{grid-template-columns:1fr}}
/* 20px breathing room between the distinct field groups inside the scroll
   area (resolution, geometry, SAR/hyperspectral note, application/timeline,
   tasking date/cloud/nadir, estimate) — margin-collapsing with each group's
   own first-child .lbl (margin-top:9px) means the visible gap is exactly
   this value, not this plus 9px. */
#branch-archive,#branch-classic{margin-top:20px}
#cl-geom-wrap,#tasking-extra,#branch-classic>.row2{margin-top:20px}
.branch-note.classic{margin-top:20px!important}
.estimate{margin-top:20px!important}
.v6-exit{position:fixed;inset:0;z-index:6000;display:none;place-items:center;padding:18px;background:rgba(4,20,15,.68);backdrop-filter:blur(8px)}.v6-exit.open{display:grid}.v6-exit-card{width:min(470px,100%);background:#fff;border-radius:20px;box-shadow:var(--shadow-lg);overflow:hidden}.v6-exit-top{padding:23px 24px 16px;background:#0d3027;color:#fff;position:relative}.v6-exit-top .eyebrow{font:700 9px Manrope;letter-spacing:.1em;text-transform:uppercase;color:var(--green);margin-bottom:7px}.v6-exit-top h3{font:800 23px/1.12 Manrope;margin:0;letter-spacing:-.035em}.v6-exit-top p{font-size:11.5px;line-height:1.5;color:rgba(255,255,255,.7);margin:8px 34px 0 0}.v6-exit-x{position:absolute;right:14px;top:14px;width:31px;height:31px;border:0;border-radius:50%;background:rgba(255,255,255,.12);color:#fff;font-size:18px}.v6-exit-body{padding:18px 24px 22px}.v6-exit-fields{display:grid;grid-template-columns:1fr 1fr;gap:8px}.v6-exit-field{margin-top:10px}.v6-exit-field label{display:block;font:700 9px Manrope;letter-spacing:.07em;text-transform:uppercase;color:#718078;margin-bottom:5px}.v6-exit-field input,.v6-exit-field textarea{width:100%;border:1px solid var(--bdr);border-radius:9px;padding:9px 10px;font:12px DM Sans;outline:none}.v6-exit-field textarea{min-height:64px;resize:vertical}.v6-exit-actions{display:flex;gap:8px;margin-top:13px}.v6-exit-send{flex:1;border:0;border-radius:10px;background:var(--green);color:var(--txt);font:800 12px Manrope}.v6-exit-stay{border:1px solid var(--bdr);border-radius:10px;background:#fff;padding:11px 13px;font:700 11px Manrope}.v6-exit-error{display:none;color:#a3322d;background:#fff0ed;border-radius:8px;padding:8px 9px;font-size:10.5px;margin-top:9px}.v6-exit-success{display:none;text-align:center;padding:24px}.v6-exit-success i{display:grid;place-items:center;width:50px;height:50px;border-radius:50%;background:var(--green);font-style:normal;font-size:23px;margin:0 auto 10px}.v6-exit-success b{font:800 18px Manrope}.v6-exit-success p{font-size:11.5px;color:#718078}.v6-exit-privacy{font-size:9px;color:#89938e;margin-top:8px}@media(max-width:520px){.v6-exit-fields{grid-template-columns:1fr}.v6-exit-actions{flex-direction:column}.v6-exit-send,.v6-exit-stay{min-height:43px}}
/* Results become a dark recommendation workspace, visually separate from input. */
.results{display:none;max-width:none!important;margin:0!important;padding:72px 44px 90px!important;background:#0b201a!important;color:#fff!important}.results-head{max-width:1400px;margin:0 auto 19px!important;align-items:end!important}.results-head h2{font-size:clamp(29px,3vw,43px)!important;letter-spacing:-.05em;color:#fff!important}.results-head .chip{background:rgba(255,255,255,.08)!important;border-color:rgba(255,255,255,.16)!important;color:#d3e6df!important}.stat-tiles{max-width:1400px;margin:0 auto 13px!important;gap:8px!important}.tile{background:rgba(255,255,255,.07)!important;border-color:rgba(255,255,255,.12)!important;box-shadow:none!important}.tile .tv{color:#fff!important}.tile.good .tv{color:var(--green)!important}.tile .tl,.tile .tv small{color:rgba(255,255,255,.55)!important}.bundle-card{max-width:1400px;margin:0 auto 14px!important;background:#f7f8f4!important;border:0!important;box-shadow:none!important;color:var(--txt)!important}.bundle-top{background:#e7eee7!important;padding:16px 20px!important}.bundle-grid{grid-template-columns:minmax(0,1fr) 350px!important;padding:17px 20px 20px!important}.bundle-thumbs{display:grid!important;grid-template-columns:repeat(3,minmax(0,1fr));gap:8px!important}.mini{width:auto!important;border-radius:11px!important}.task-bridge{background:#fff5d9!important;border-color:#ead79c!important}.all-scenes{max-width:1400px;margin:9px auto 0!important}.all-scenes summary{color:var(--green)!important}.scene-card{box-shadow:none!important}.modal{border-radius:21px!important}.modal-head{background:#0d3027!important}.ctx-box{background:#f2f5f1!important;border-color:var(--bdr)!important}
/* ---- Equal fixed-height columns + form-card header/scroll/footer split
   (2026-10-11) — true desktop/tablet only (>=1024px). Below 1024px the
   layout stacks (map above, form below — see the max-width:1023px block)
   in normal document flow, so a fixed-height/internal-scroll treatment
   would be wrong there (and is explicitly left alone).
   Both columns get the SAME height (viewport minus header, 520px floor) so
   there's nothing for either to "catch up" to — no sticky needed. The real
   fix for the Search button potentially being off-screen: .form-card no
   longer grows with its content. Its content splits into .form-header
   (fixed), .form-scroll (the options list, flex:1, scrolls internally,
   overscroll-behavior:contain so that inner scroll can't chain into/trap
   the page scroll — the old squeezed-column bug this comment used to warn
   about came from an UNSCOPED min-height+overflow:hidden interacting with
   .form-card's container-type:inline-size on the mobile FLEX-stacked
   layout; this stays scoped to >=1024px's GRID layout, a different
   codepath, and was verified visually after this change), and .form-footer
   (fixed, holds whichever mode's primary button/note is active — see
   #footer-archive/#footer-classic in questionnaire-v2.html). */
@media(min-width:1024px){
  /* Every rule in this block is scoped with :has(.form-scroll) — that
     element only exists on the questionnaire-v2.html engine (the sticky-
     footer/scroll-area redesign built this session). A SEPARATE, older
     engine file (prototypes/questionnaire-explorer.html — an in-progress
     A/B test, currently wired to the South Africa location page only, see
     server.js's QUESTIONNAIRE_EXPLORER) shares this same stylesheet and
     the same .map-card/.form-card/.form-header/.form-body class names, but
     NOT this new structure (its .form-body is a plain padded block, no
     .form-scroll/.form-footer split) — these rules were applying to it
     unscoped, forcing a fixed height + overflow:hidden + flex column onto
     a card never built to expect that, clipping/squashing its content.
     The :has() guard makes this a no-op there, leaving its own original
     (pre-this-session) CSS in full effect — zero behavior change for the
     v2 engine itself, since every v2 instance does have .form-scroll. */
  .wrap:has(.form-scroll){padding:14px 32px!important}
  .wrap:has(.form-scroll) .map-card,.form-card:has(.form-scroll){height:calc(100vh - var(--xrt-header-h,64px) - 28px)!important;min-height:520px!important;max-height:none!important}
  .wrap:has(.form-scroll) .map-card{position:relative!important} /* containing block for .map-foot/.samples-arrow/#aoi-ready-arrow (position:absolute) — was silently broken by position:static */
  .form-card:has(.form-scroll){position:static!important;border:1px solid var(--bdr)!important;border-radius:var(--r-lg,18px)!important;box-shadow:var(--shadow-md)!important;overflow:hidden!important;display:flex!important;flex-direction:column!important}
  .form-card:has(.form-scroll) .form-header{flex:0 0 auto!important;padding:12px 20px!important}
  .form-card:has(.form-scroll) .form-body{flex:1 1 auto!important;display:flex!important;flex-direction:column!important;overflow:hidden!important;padding:0!important}
  .form-scroll{flex:1 1 auto!important;overflow-y:auto!important;padding:16px 22px!important;position:relative}
  .form-footer{flex:0 0 auto!important;border-top:1px solid var(--bdr)!important;background:var(--white)!important;padding:12px 22px 16px!important}
}
/* .xrt-whatsapp-fab repositioning deliberately does NOT live here — this
   whole stylesheet is injected by XRTPortalLoader.injectPortal() with every
   selector rewritten to "#xrt-portal-page-mount <selector>" (scoping it to
   the injected engine's own markup only), and the WhatsApp button is a
   sibling of the mount, not a descendant — a rule here can never match it.
   See the real fix in public/js/xrt-portal-mode-upgrade.js, which runs in
   the host page's actual global scope. */
/* ---- Tablet + mobile, <1024px: the "What do you need?" form panel reflows
   BELOW the map instead of squeezing into a narrow side column (which cut
   off/cramped its fields at tablet widths under the old 601-1023 side-by-
   side treatment). Map, then the request form directly below it in normal
   flow — no overlay, no internal scroll box. The control bar's own docked-
   above-the-map layout is already handled by the unguarded .aoi-toolbar
   rule above — nothing breakpoint-specific needed for it here. */
@media(max-width:1023px){
  .wrap{display:flex!important;flex-direction:column!important;align-items:stretch!important;height:auto!important;min-height:0!important;padding:0 24px!important}
  /* position:relative, not the base rule's sticky (that's desktop-only, so
     the map stays in view while the taller form scrolls beside it) — sticky
     leaking into this stacked layout is what made the floating search box
     and draw-tool toolbar (both absolutely positioned against .map-card)
     disappear on narrow screens: a sticky element establishes its own
     stacking/containing context, and combined with overflow:hidden on the
     same element that context wasn't resolving the toolbar's position
     correctly here. On a stacked layout the map is just supposed to scroll
     away normally once you pass it, same as any other block. */
  .map-card{position:relative!important;top:auto!important;width:100%!important;height:56vh!important;min-height:300px;flex:0 0 auto}
  .form-card{border-left:0!important;border-top:1px solid var(--bdr)!important;height:auto!important;max-height:none!important;flex:1 1 auto;overflow:visible!important}
  .form-body{overflow-y:visible!important;overflow:visible!important;max-height:none!important}
}
/* ---- Mobile-only spacing (<768px) for elements unrelated to the map
   control bar (that bar's layout is identical at every breakpoint — see
   the unguarded .aoi-toolbar rule above). ---- */
@media(max-width:767px){
  /* .wrap sits directly under .hero in normal flow (both this homepage and
     every location page graft the same engine markup right after their own
     hero — see excludeSelectors:['.hero'] in xrt-portal-loader.js callers)
     with margin:0 from the base rule above, so on a stacked mobile layout
     the dark hero and the map/toolbar (also a dark-on-dark surface) butted
     directly against each other with zero seam. */
  .wrap{padding:0 16px!important;margin-top:28px!important}
  .form-body{padding:14px 15px 28px!important}
  .estimate{margin-left:-15px!important;margin-right:-15px!important;padding-left:15px!important;padding-right:15px!important}
  .row2{grid-template-columns:1fr!important}
  .bundle-grid{grid-template-columns:1fr!important}
  .bundle-thumbs{grid-template-columns:1fr 1fr!important}
  .results{padding-left:10px!important;padding-right:10px!important}
  .stat-tiles{grid-template-columns:repeat(3,1fr)!important}
  .tile{padding:9px!important}
  .tile .tv{font-size:19px!important}
}
/* ---- Mobile/tablet-only "Search available imagery" bar (2026-10-11) ----
   Lives OUTSIDE .wrap/.form-card on purpose: .form-card has
   container-type:inline-size set above (line ~119), and layout containment
   makes an element the containing block for position:fixed descendants —
   same class of bug already documented for .aoi-toolbar's backdrop-filter
   elsewhere in this file. A fixed bar nested inside .form-card would pin to
   .form-card's own box, not the real viewport. #sticky-cta (the results
   page's buy bar) already lives outside .wrap for this exact reason — this
   mirrors it. Visibility is toggled by JS (an IntersectionObserver on
   .wrap in questionnaire-v2.html) via the .show class, not by this CSS. */
.mobile-search-bar{position:fixed!important;left:0;right:0;bottom:0;z-index:1600;padding:10px 16px calc(10px + env(safe-area-inset-bottom,0px));background:#fff!important;border-top:1px solid var(--bdr)!important;box-shadow:0 -8px 20px rgba(11,31,25,.18);display:none}
.mobile-search-bar.show{display:block}
.mobile-search-bar .cta{margin:0!important;min-height:44px!important}
@media(min-width:1024px){.mobile-search-bar{display:none!important}}

