/* ==========================================================================
   PublicPortal mobile-first overrides (Phase 1).

   Every rule is scoped to BOTH `body.public-portal` AND the mobile media query, so:
     - IntranetPortal never matches it (its <body> has no .public-portal class),
     - desktop is unaffected by construction,
     - `screen` keeps A4 desktop printing (~760-790px) out of the mobile band.

   THE MOBILE QUERY IS TWO CLAUSES, not one:
       @media screen and (max-width: 767.98px),
              screen and (max-height: 500px) and (orientation: landscape)
   The second clause exists because a phone held sideways is WIDER than the phone band — iPhone 12 Pro
   landscape is 844x390 — so a width-only query switched this entire file off on the devices it was
   written for. Its desktop counterparts carry the negation (`and (min-height: 501px)`) so that a
   landscape phone matches the mobile side only. The same two clauses live in nexus-mobile.js
   (MOBILE_QUERY), map-fit-padding.js and nexus-sheet.css — change them together.

   Loaded once in the PublicPortal _Layout AFTER @RenderSection("Styles"), so it
   wins the cascade over per-page style sections. The `body.public-portal` prefix
   also raises specificity to (0,2,1), beating bare `.ol-popup` rules from vendor
   sheets and the GreenCadastreViaGeoserver inline <style> block.
   ========================================================================== */

/* Sheet-only chrome (grip / type row / result counter) is injected or shown by the InfoTool
   controller only on mobile. Hide it by default at ALL widths so it can NEVER leak onto desktop
   — e.g. after resizing iPhone -> desktop without a refresh, the injected DOM stays put. The
   mobile sheet block below re-shows these via the media query (so visibility is CSS-driven, not
   a persisted inline `display` from jQuery .show()). */
body.public-portal #map-info-tool-counter,
body.public-portal .info-tool-typerow {
    display: none;
}

/* S18 — the same guard for the grip. There used to be two more rules here, one per tool
   (`.infotool-grip`, `.searchtool-grip`); S20 moved both tools onto the SHARED grip class, so this one
   rule now covers every sheet. Scoped to .turbosidebar so the body-level dismiss shell's own grip is
   untouched; the snap block in nexus-sheet.css re-shows it inside the mobile media query at higher
   specificity. */
body.public-portal .turbosidebar .nexus-sheet__grip {
    display: none;
}

/* S30 — a tooltip must never be a click target, at ANY width.

   Measured kill chain on a phone in landscape (event targets recorded during a real tap on a tool icon):
       pointerdown -> I.fa ... touchend -> I.fa        the tap lands on the icon
       mouseover   -> I.fa                             tap-emulated hover fires
       mouseover   -> DIV.arrow                        the hover tooltip renders OVER the tap point
       mousedown/click -> DIV.arrow                    the compat click hits the TOOLTIP, not the anchor
   The sidebar's tab handler is `sub.onclick` on the anchor (ol3-sidebar.js), so the pane never opens —
   "tapping any tool does nothing". It fired in landscape because S28's `overflow: auto` makes the strip
   a scroll parent, which Popper v1 takes as the boundary: a ~100px tooltip clamped inside an 88px strip
   sits on the buttons themselves. But the CLASS of bug — hover tooltip swallowing the tap-click — exists
   on every touch screen at every width, so this rule is NOT media-gated. Bootstrap 4 ships no
   pointer-events rule on .tooltip (bootstrap.css:4301); Bootstrap 5 added exactly this one. */
body.public-portal .tooltip {
    pointer-events: none;
}

/* The layer switcher's two DRAG HANDLES - the reorder arrows and the opacity cursor - are the only
   places in the panel where a finger means "drag this", not "scroll the list". Without this the
   browser resolves the ambiguity its own way: it starts scrolling, the touchstart stops being
   cancelable, and the handler's preventDefault is refused with an "[Intervention] Ignored attempt to
   cancel a touchstart event" per attempt - i.e. the drag is simply lost to the scroll. Same fix the
   sheet's own grip already uses (`.nexus-sheet__grip`, nexus-sheet.css).

   NOT media-gated, for the same reason as the .tooltip rule above (S30): this is a touch-screen bug,
   not a narrow-screen one - a touch laptop at 1920px has it too. Scoped to `.nexus-layerswitcher` so
   it cannot be confused with `.ol-noscroll`, which the two handles also carry but which means
   `visibility: hidden` in the Field GIS sheet. */
body.public-portal .nexus-layerswitcher .layerup,
body.public-portal .nexus-layerswitcher .layerswitcher-opacity-cursor {
    touch-action: none;
}

@media screen and (max-width: 767.98px),
       screen and (max-height: 500px) and (orientation: landscape) {

    /* S30 companion — on the touch band, hover tooltips are pure noise: there is no hover, so the only
       way one ever appears is tap-emulated hover, and then nothing dismisses it (the "tooltip parked in
       a corner" that POI's fix #1 deleted was this same mechanism). pointer-events above already stops
       them swallowing clicks at every width; here they stop painting at all. */
    body.public-portal .tooltip {
        display: none;
    }

    /* S29 companion — the vendor's `.turbosidebar-right { right: 10px }` lives behind a plain
       (min-width: 768px) in the GENERATED ol3-sidebar.css, which we do not edit. Portrait phones never
       met it, but short landscape is 768px+ wide, so the strip and the sheet both sat 10px off the edge
       (10px of the sheet clipped off-screen left, a 10px gap on the right). Zero it on the mobile band. */
    body.public-portal .turbosidebar-right {
        right: 0;
    }

    /* Map info popup: the desktop rule is min-width:360px (left:-50px), which
       overflows a 375px viewport. Drop the min-width and clamp to the screen. */
    body.public-portal .ol-popup {
        min-width: 0;
        max-width: calc(100vw - 32px);
        left: auto;
    }

    /* Touch targets ~44px: OpenLayers zoom buttons + turbosidebar close control. */
    body.public-portal .ol-zoom .ol-zoom-in,
    body.public-portal .ol-zoom .ol-zoom-out {
        width: 44px;
        height: 44px;
        font-size: 1.4rem;
        line-height: 40px;
    }

    body.public-portal .turbosidebar-close {
        min-width: 44px;
        min-height: 44px;
        font-size: 1.4rem;
    }

    /* iOS Safari auto-zooms on focus of any input whose computed font-size is
       < 16px. The Angle theme sets .form-control to 0.875rem (14px); bump to 16px
       on mobile so focusing a field (e.g. sidebar search) doesn't zoom the page. */
    body.public-portal .form-control,
    body.public-portal .custom-select {
        font-size: 16px;
    }

    /* S6 — a form control is a touch target too, and the theme leaves them around 34px. `min-height`
       rather than the plan's `height`, so a control that legitimately needs to be taller still can:
       a textarea must be able to grow, and `height` would have pinned it to one line. Excluded for the
       same reason, along with the file input, whose height comes from the filestyle button inside it. */
    body.public-portal .form-control:not(textarea):not(.form-control-file),
    body.public-portal .custom-select {
        min-height: 44px;
    }

    /* Sidebar polish on mobile (Phase 2, item 15 — safe subset, no behaviour change).
       Scroll is already handled by .turbosidebar-content { overflow-y:auto } in the
       generated sheet, so only two things are added here:

       (1) Sticky pane header — keep the title + close (X) pinned while a long pane scrolls. */
    body.public-portal .turbosidebar-header {
        position: sticky;
        top: 0;
        z-index: 3;
    }

    /* (2) 44px touch targets for the tool-icon strip (was 40px). The strip width doubles as the
       collapsed sidebar width and the content inset, so all three move together to stay aligned. */
    body.public-portal .turbosidebar.collapsed {
        width: 44px;
    }

    body.public-portal .turbosidebar-tabs,
    body.public-portal .turbosidebar-tabs > ul {
        width: 44px;
    }

    body.public-portal .turbosidebar-tabs > li,
    body.public-portal .turbosidebar-tabs > ul > li {
        height: 44px;
    }

    body.public-portal .turbosidebar-tabs > li > a,
    body.public-portal .turbosidebar-tabs > ul > li > a {
        line-height: 44px;
    }

    body.public-portal .turbosidebar-left .turbosidebar-content {
        left: 44px;
    }

    body.public-portal .turbosidebar-right .turbosidebar-content {
        right: 44px;
    }

    body.public-portal .turbosidebar-right .turbosidebar-header {
        padding-left: 44px;
    }

    /* ===================================================================
       InfoTool bottom-sheet (mobile PublicPortal only).

       The JS controller toggles `.infotool-sheet` + a `.snap-*` class on
       #map-sidebar ONLY while InfoTool is active on PublicPortal mobile.
       No other tool, and neither desktop nor IntranetPortal, ever gets the
       class — and even if it did, this whole block is behind body.public-portal
       + the 767.98px media query, so it can't apply off mobile-public.

       The CONTAINER itself is bottom-anchored to the snap height, so the map
       ABOVE the sheet is genuinely uncovered — map taps reach OpenLayers with
       no pointer-events routing at all. The tool strip is hidden while the sheet
       is open; the sheet's X closes InfoTool and brings the strip back.
       =================================================================== */

    /* Shell (container / snap heights / dragging / tabs-hidden / content) now comes from the shared
       .nexus-sheet--snap (nexus-sheet.css); the element carries it via NexusSheet.snap rootClasses.
       Only the InfoTool-specific grip + header + type-row + table + pager styling remain below. */

    /* The 23-line `.infotool-grip` block that stood here is deleted (S20): the tool now uses the shared
       `.nexus-sheet__grip`, styled once in nexus-sheet.css. It was byte-identical to SearchTool's copy
       apart from the class name. */

    /* Header and its close × now come from the SHARED .nexus-sheet--snap block in nexus-sheet.css (S4):
       44px, white, centred, `padding: 0 44px`, × at right 8px sized 44×44, sticky below the grip via
       --nexus-sheet-header-h. The per-tool copy that used to live here declared `padding: 0 16px` and a
       34px ×; it worked only because "Инфо" is a short word and 34px is under the 44px touch minimum.
       Only the genuinely tool-specific bit stays: the font-size of the glyph. */
    body.public-portal .turbosidebar.infotool-sheet .turbosidebar-header .turbosidebar-close {
        font-size: 20px;
    }

    /* Pull the sheet body up tight under the "Info" header so the type row sits higher. */
    body.public-portal .turbosidebar.infotool-sheet .pt-3 {
        padding-top: 0 !important;
    }

    /* Type row: result type BOLD on the LEFT, close X on the RIGHT, divider line below.
       Sticky under the header so the X stays reachable while the rows scroll. */
    body.public-portal .turbosidebar.infotool-sheet .info-tool-typerow {
        position: sticky;
        /* Was a hand-summed 62px = 22 grip + 40 vendor header. The header is now 44px and publishes its
           own height, so this stacks on it instead of guessing (S16). Getting this wrong parks the row
           INSIDE the header, which is opaque and has the higher z-index — the row and its × vanish. */
        top: calc(var(--nexus-sheet-grip-h, 22px) + var(--nexus-sheet-header-h, 44px));
        z-index: 2;
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 8px 4px 10px;
        background: #fff;
        border-bottom: 1px solid #e3e7ef;
    }
    body.public-portal .turbosidebar.infotool-sheet #map-info-tool-type {
        font-weight: 700;
        font-size: 15px;
        color: #1a2030;
    }

    /* S33 — before the first map tap the type row and the pager are empty shells: a 19px strip whose only
       visible part is its divider line, and a 26px blank row (the arrows are hidden, the counter has no
       text). At peek they were everything a citizen saw under "Инфо", and the "Кликнете на картата…" hint
       sat 55px below the sheet's bottom edge. Hide both while they have nothing to say; the first result
       fills #map-info-tool-type / #map-info-tool-counter and they come back. `:has()` is the pattern
       nexus-content.css already uses (`tr:not(:has(button))`); the id inside it also outranks the flex rule
       above, which is what makes the override win without `!important`. */
    body.public-portal .turbosidebar.infotool-sheet .info-tool-typerow:has(> #map-info-tool-type:empty),
    body.public-portal .turbosidebar.infotool-sheet .info-tool-nav:has(> #map-info-tool-counter:empty) {
        display: none;
    }
    /* The type row no longer carries the close × — it moved UP to the "Info" header row (first row).
       Kept in the DOM (baked once, reused on desktop) but hidden here on the mobile sheet. */
    body.public-portal .turbosidebar.infotool-sheet .info-tool-sheet-close {
        display: none;
    }

    /* The per-result <h3> is hidden by `.nexus-list--titleless` now (S23) — the type row already names the
       result, so the heading is redundant. Only the <br> spacers are still dropped here; `.nexus-content`
       drops direct-child <br> too, but this keeps the descendant ones the generators emit around the table. */
    body.public-portal .turbosidebar.infotool-sheet #map-sidebar-infotool-tab-container br {
        display: none;
    }

    /* Drop the result rows slightly; keep them full-width, edge to edge (no side inset). */
    body.public-portal .turbosidebar.infotool-sheet #map-sidebar-infotool-tab-container {
        padding-top: 12px !important;
    }

    /* Pager row layout. The arrow glyph + circle and the counter styling come from nexus-sheet.css, which
       names InfoTool's Font Awesome <em>s alongside .nexus-sheet__pager-btn — the markup stays portal-neutral
       so the IntranetPortal keeps its own arrows. Only the row layout and the counter's visibility (it's
       hidden by the top guard) are asserted here. */
    body.public-portal .turbosidebar.infotool-sheet .info-tool-nav {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 13px 6px;
    }
    body.public-portal .turbosidebar.infotool-sheet #map-info-tool-counter {
        display: inline-block;
    }

    /* The 19-declaration label/value list that stood here is deleted (S23). It was byte-identical — verified
       declaration by declaration — to the desktop copy below AND to `.nexus-list` in nexus-content.css, so
       all three were one rule written three times. The class is applied to this container at EVERY width by
       NexusMobile.applyContent() in the component, which is why the desktop copy goes too: the look was
       always meant to be viewport-independent, and two copies were how that was expressed. */

    /* ===================================================================
       LAYERS PANE — bottom sheet (S37) plus the 375px fixes that predate it.

       Why it was content-only until S37: each layer row carries a Bootstrap 4 dropdown holding that layer's
       primary actions (opacity slider, remove), positioned by Popper IN-DOM inside the sheet's overflow, so a
       floating menu was clipped - invisible at peek, cut off at half - and BS4 has no `container` option to
       re-parent it. What lifted that: on the phone band the menu is rendered INLINE, as a full-width block under
       its own row (`position: static`, the `.layers-sheet` rules below), so it scrolls with the list and nothing
       can clip it. Entry and exit come from the pane state (this pane has no tool object) - see nexus-sheet.js.

       These rules live here, never in wwwroot/css/nexus-layerswitcher.css: that sheet is loaded by 31
       IntranetPortal views, so editing it would reach all of them (mobile-guidelines.md §6).
       =================================================================== */

    /* The checkbox is the row's primary action and its real <input> is display:none — the tap target is
       the label, whose visual box is a 1.2em pseudo-element, i.e. ~15px. Give the LABEL a 44px target
       without moving that box: pad it out and re-centre both pseudo-elements vertically. */
    body.public-portal #map-sidebar-layers-pane .nexus-layerswitcher [type=checkbox] + label,
    body.public-portal #map-sidebar-layers-pane .nexus-layerswitcher [type=radio] + label {
        display: block;
        min-height: 44px;
        padding-top: 12px;
        padding-bottom: 12px;
    }

    body.public-portal #map-sidebar-layers-pane .nexus-layerswitcher [type=checkbox] + label:before,
    body.public-portal #map-sidebar-layers-pane .nexus-layerswitcher [type=radio] + label:before {
        top: 50%;
        transform: translateY(-50%);
    }

    /* The check mark is the vendor's "L" (right + bottom borders) ROTATED 45deg; re-centring it with a bare
       translateY threw that rotation away and drew an unrotated black corner. Keep both transforms, and keep the
       vendor's vertical offset of the mark against its box (mark centre .25em above the box centre, as in the
       1.4em desktop label) so the glyph reads as the same tick. */
    body.public-portal #map-sidebar-layers-pane .nexus-layerswitcher [type=checkbox]:checked + label:after {
        top: calc(50% - .25em);
        transform: translateY(-50%) rotate(45deg);
    }

    /* The radio dot is centred in its box by a .3em margin on the desktop label; centred at 50% that margin would
       push it .3em low, so only the horizontal part of it stays. */
    body.public-portal #map-sidebar-layers-pane .nexus-layerswitcher [type=radio]:checked + label:after {
        top: 50%;
        transform: translateY(-50%);
        margin-top: 0;
        margin-bottom: 0;
    }

    /* A long layer name must wrap inside the row instead of being clipped by the ancestor's
       overflow-x: hidden — the lane left for it is `calc(100% - 90px)`, i.e. ~245px at 375px. */
    body.public-portal #map-sidebar-layers-pane .nexus-layerswitcher .panel li > div.li-content {
        white-space: normal;
        word-break: break-word;
    }

    /* ---- S37: the sheet's own chrome, only while the sheet owns the host (`.layers-sheet`) ---- */

    /* The vendor row is a right-floated button lane beside an inline-block name lane of calc(100% - 90px). As one
       flex line the name takes what is left, the controls sit at the end and - the point of it - a wrapping child
       can drop to its own full-width line below the row. `display: contents` lifts the buttons wrapper and the
       BS4 .btn-group out of the way so the dropdown menu is that child. */
    body.public-portal .turbosidebar.layers-sheet .nexus-layerswitcher .panel li {
        display: flex;
        flex-wrap: wrap;
        align-items: center;
    }

    body.public-portal .turbosidebar.layers-sheet .nexus-layerswitcher .panel li > div.li-content {
        display: flex;
        align-items: center;
        order: 1;
        flex: 1 1 0;
        min-width: 0;
        width: auto;
    }

    body.public-portal .turbosidebar.layers-sheet .nexus-layerswitcher .panel li > div.li-content > label {
        flex: 1 1 auto;
        width: auto;
    }

    body.public-portal .turbosidebar.layers-sheet .nexus-layerswitcher-buttons,
    body.public-portal .turbosidebar.layers-sheet .nexus-layerswitcher-buttons > .btn-group {
        display: contents;
    }

    /* Touch targets: the dropdown toggle and a group's expand/collapse. The toggle also stops being a stretching
       .btn-group child (flex: 1 1 auto in BS4) now that it is a flex item of the row itself. */
    body.public-portal .turbosidebar.layers-sheet .nexus-layerswitcher-buttons .btn,
    body.public-portal .turbosidebar.layers-sheet .nexus-layerswitcher .expand-collapse {
        min-width: 44px;
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        justify-content: center;
    }

    body.public-portal .turbosidebar.layers-sheet .nexus-layerswitcher-buttons .btn {
        order: 2;
        flex: 0 0 auto;
        margin-right: 0;
    }

    /* A GROUP's children. The switcher nests them as a <ul> appended to the group's own <li>
       (nexus-layerswitcher.js, "Layer group"), so making that <li> a flex row above turns the whole
       sub-list into a flex ITEM of it - and an item with no `order` is order 0, i.e. BEFORE the
       `.li-content` (order 1) that carries the group's own name. Measured at 375px on the real
       switcher with Noise Monitoring's five categories: the five child rows rendered first and the
       group row itself was squeezed onto the same line at 9px wide, its checkbox stranded mid-list.

       Reachable anywhere a group is expanded - NoiseMonitoring is simply the first page that ships
       one open (`openInLayerSwitcher: true`), since the switcher renders no <ul> at all for a
       collapsed group.

       `padding-left` overrides the base `.panel ul` 20px because the phone band moved the goalposts:
       the expand/collapse control below is given a 44px touch target, which pushes the GROUP's own
       name 44px in, while its children were still indented only 20px - so the children ended up 10px
       to the LEFT of their parent and the nesting read backwards (desktop, same markup, indents them
       34px to the right). Matching the gutter to that same 44px puts them just inside the group name
       again, and spends no more of a 375px row than the control already costs. */
    body.public-portal .turbosidebar.layers-sheet .nexus-layerswitcher .panel li > ul {
        order: 4; /* last: after .li-content (1), the buttons (2) and the per-layer menu (3) */
        flex: 0 0 100%;
        width: 100%;
        padding-left: 44px;
    }

    /* The per-layer menu, INLINE: a full-width block under its row instead of a floating Popper layer. Popper still
       writes position/transform inline on every show, hence the two !importants. */
    body.public-portal .turbosidebar.layers-sheet .nexus-layerswitcher .dropdown-menu {
        position: static !important;
        transform: none !important;
        order: 3;
        flex: 0 0 100%;
        width: 100%;
        float: none;
        margin: 0 0 8px;
    }

    body.public-portal .turbosidebar.layers-sheet .nexus-layerswitcher .dropdown-menu .btn {
        min-height: 44px;
    }

    /* The opacity slider is an 8px line with a 26px touch cursor; give the row room to hit it. */
    body.public-portal .turbosidebar.layers-sheet .nexus-layerswitcher .layerswitcher-opacity-wrapper {
        padding-top: 14px;
        padding-bottom: 14px;
    }

    /* Bootstrap's .mt-3 on the container doubles the header's breathing room inside the sheet. */
    body.public-portal .turbosidebar.layers-sheet #sidebar-layers-container {
        margin-top: 6px;
    }

    /* ===================================================================
       PointOfInterestTool bottom-sheet — the ONLY tool-specific rules it needs.
       Everything else (container, snap heights, grip, header, close ×, content) comes from the shared
       .nexus-sheet--snap block and the opt-in .nexus-cards class.

       `peek` here is an ARMED state, not a parked one: it is entered when the user asks to place a point,
       and the only control that matters then is Cancel. So the header is hidden at that snap — the 44px
       Cancel button is the escape — which is what buys the arming row its room inside 96px.
       =================================================================== */
    body.public-portal .turbosidebar.poi-sheet.snap-peek .turbosidebar-header {
        display: none;
    }

    /* At peek the list is irrelevant; keep only the arming row on screen. */
    body.public-portal .turbosidebar.poi-sheet.snap-peek #map-sidebar-pointOfInterest-table-container,
    body.public-portal .turbosidebar.poi-sheet.snap-peek #map-sidebar-pointOfInterestTool-tab-container hr {
        display: none;
    }

    /* Arming row: the hint and Cancel side by side, both at a real touch size. */
    body.public-portal .turbosidebar.poi-sheet #addPointsBtn,
    body.public-portal .turbosidebar.poi-sheet #cancelPointsBtn {
        min-height: 44px;
        border-radius: 8px;
    }

    body.public-portal .turbosidebar.poi-sheet #clickOnMapText {
        padding-top: 6px;
        font-size: 13px;
        color: #6a7280;
    }

    /* ---- TWO trailing controls, where the shared card block assumes one ----
       Every other carded tool has a single per-row action, so `.nexus-cards` promotes `td:last-child` to
       an absolutely positioned 44px chevron and hides that button's icon. POI has two actions, and the
       generator now puts LOCATE last so the chevron is the one that navigates. Delete is the cell beside
       it — `:nth-last-child(2)` — which the shared block would otherwise render as muted subtitle TEXT,
       collapsing the button to a 16x6px sliver. These rules give it back a real target and park it next to
       the chevron. Nothing here changes the shared block; it only re-treats one cell POI alone has. */
    body.public-portal .turbosidebar.poi-sheet .nexus-cards table.table-bordered tr {
        /* 44 delete + 44 chevron + slack, so a long topic name never runs under either control. */
        padding-right: 92px;
    }

    body.public-portal .turbosidebar.poi-sheet .nexus-cards table.table-bordered td:nth-last-child(2) {
        position: absolute;
        right: 48px;
        top: 50%;
        transform: translateY(-50%);
        margin: 0;
        /* Undo the muted-subtitle treatment this cell inherits from the shared block. */
        font-size: inherit;
        color: inherit;
    }

    /* Only what DIFFERS from `.nexus-card-action` (nexus-content.css), which the delete button opts into at
       runtime. The 44px box, the round ghost treatment and the flex centring all come from there — this
       block used to repeat them and came out 10-of-12 declarations identical. */
    body.public-portal .turbosidebar.poi-sheet .nexus-cards table.table-bordered td:nth-last-child(2) .poi-delete-btn {
        /* Destructive, so it reads as destructive — but ghost, not a filled red block, because it sits on
           a card whose whole surface is tappable and must not compete with the content. */
        color: #d64545 !important;
        font-size: 15px;
    }

        /* The icon STAYS here. The shared block hides `td:last-child button i` because it swaps in the
           chevron glyph; delete is no longer the last cell, so its trash icon survives on its own. */
        body.public-portal .turbosidebar.poi-sheet .nexus-cards table.table-bordered td:nth-last-child(2) .poi-delete-btn:focus-visible {
            outline: 2px solid #d64545;
            outline-offset: -4px;
        }

    /* ===================================================================
       ExtractPlotElectronicIssuanceTool bottom-sheet — its own chrome only.

       The container, snap heights, grip, header, close and the stacked label/value list all come from the
       shared `.nexus-sheet--snap` block and the opt-in `.nexus-list` / `.nexus-form` classes. What is
       specific to this tool is the two-step shape: `peek` is the "tap a plot on the map" step, and the
       recap that keeps the eleven detail rows from burying the form.
       =================================================================== */

    /* At `peek` only the hint matters — the plot has not been picked yet, so the form below is for step
       two. Hiding it keeps 124px honest instead of showing a stub form. The plot table is not listed: it
       is empty until a plot is picked, and picking one lifts the sheet off `peek` anyway. */
    body.public-portal .turbosidebar.extractplot-sheet.snap-peek #map-sidebar-extractplotelectronicissuance-form {
        display: none;
    }

    /* "Add to shopping cart" is this form's whole purpose and stands alone, so it gets the full width and
       a real touch height. Kept OUT of `.nexus-form`: SearchTool's buttons sit inside `.input-group`
       attached to a text field, and blocking them there would break that flex row. */
    body.public-portal .turbosidebar.extractplot-sheet #map-sidebar-extractplotelectronicissuance-form .btn-primary {
        display: block;
        width: 100%;
        min-height: 44px;
        margin-top: 4px;
    }

    /* The <details> wrapper and its toggle stood here for one day (variant A of six) and were reverted at
       the owner's request: on mobile the whole table is to be visible, same as desktop. Plan section 5.2
       bug 4 is OPEN with its numbers recorded there — the table is 696-759px against the 294px `half`
       gives, so the form below it takes roughly three screens of scrolling to reach. Measured, not
       assumed: the pane DOES scroll (`.turbosidebar-content { overflow-y: auto }`), so nothing is
       unreachable; the cost is distance, not lost data.

       This one rule survives the revert because it belongs to bug 5, not to the recap: klasaNamena and
       kompKlasaNamena are unbounded strings, and ~120 characters would otherwise force a horizontal
       scrollbar inside a 375px sheet. */
    body.public-portal .turbosidebar.extractplot-sheet #mikroGradezniParceliTable td {
        word-break: break-word;
    }
    /* Empty state. Deliberately NOT a table row: `.nexus-cards` hides `tr:not(:has(button))` to drop the
       header rows the generators emit, and an empty-state row has no button either, so as a row this
       message rendered display:none and the pane looked blank. */
    body.public-portal .turbosidebar.poi-sheet .poi-empty {
        padding: 18px 8px;
        font-size: 13.5px;
        color: #6a7280;
        /* Explicit rather than leaning on the markup's `.text-center` utility: this message exists to be
           read when there is nothing else in the pane, so it should not depend on a Bootstrap class
           surviving in the cascade. */
        text-align: center;
    }

    /* ===================================================================
       SearchTool bottom-sheet (mobile PublicPortal only).

       Mirrors the InfoTool sheet SHELL (bottom-anchored container + grip +
       snap positions) but for the search form — no type row / pager / fit
       padding. Its own `.searchtool-sheet` class keeps InfoTool untouched.
       The JS controller adds `.searchtool-sheet` + a `.snap-*` class only
       while SearchTool is active on PublicPortal mobile; the whole block is
       behind body.public-portal + the 767.98px media query, so it cannot
       apply off mobile-public. Default snap is FULL (form gets space; it is
       form-first — you don't need the map while typing).
       =================================================================== */

    /* Shell (container / snap heights / dragging / tabs-hidden / content) now comes from the shared
       .nexus-sheet--snap (nexus-sheet.css); the element carries it via NexusSheet.snap rootClasses.
       Only the SearchTool-specific grip + header + form styling remain below. */

    /* The 23-line `.searchtool-grip` block that stood here is deleted (S20) — see the note in the InfoTool
       block above. Both tools now use the shared `.nexus-sheet__grip`. */

    /* Header: "Search" centered on a white bar with a divider; close X kept on the right. */
    /* Header and close × come from the shared .nexus-sheet--snap block (S4). The per-tool copy here
       declared `padding: 0 46px` and `right: 4px`, i.e. the same intent as InfoTool's `0 16px` / `right:
       8px` but with different numbers — exactly the fork a third tool would have had to choose between.
       Both are now one rule: `0 44px` with the × at `right: 8px`, 44×44. Nothing tool-specific remains. */

    /* ===================================================================
       Search FORM controls — normalise every category's fields in the sheet.

       The ~26 category partials were authored with THREE different form
       structures, which each broke differently on a narrow full-width sheet:
         (a) .input-group  (text + attached search button)  -> already fine;
         (b) .row.form-group with col-md-3 label + col-md-9 field: below 768px
             col-md-* carries no width, so the row's flex + negative margins
             left the field full-bleed / mis-aligned ("field too wide");
         (c) a bare <select> (e.g. Purpose: `<select class="">`) with no
             .form-control at all -> a tiny ~116px unstyled native control
             ("size error in the dropdown result").
       These rules unify (b) and (c) into clean, full-width 40px fields while
       leaving (a)'s input-group flex layout untouched. Everything is scoped to
       .searchtool-sheet + the mobile media query, so desktop / IntranetPortal
       keep the original two-column form layout.
       =================================================================== */

    /* (b) The three rules that stacked the col-md label over a full-width field are DELETED — they are
       `.nexus-form` in nexus-content.css now, applied to this container by the registry's `content:`.
       Byte-identical, so nothing about this form changes; the point is that ExtractPlot needed the same
       treatment and would otherwise have been a second copy (S17). Extraction WITH deletion, per the
       plan's section 4.3 — leaving both would mean the shared class won nothing. */

    /* (c) Every category <select> (styled or bare) -> a proper full-width field.
       :not(.select2-hidden-accessible) skips select2's hidden native mirror so we
       only ever style a genuinely visible <select>; margin-bottom restores spacing
       between stacked selects (e.g. _SearchBuildingPlots' two dropdowns, whose
       <br> spacers the results rule above hides). */
    body.public-portal .turbosidebar.searchtool-sheet #map-sidebar-searchtool-tab-container select:not(.select2-hidden-accessible) {
        display: block;
        width: 100%;
        height: 44px;
        margin: 0 0 12px;
        padding: 6px 12px;
        font-size: 16px;
        line-height: 1.5;
        color: #495057;
        background-color: #fff;
        border: 1px solid #ced4da;
        border-radius: 8px;
    }
    /* select2-enhanced dropdowns (if a category uses one) -> full width too. */
    body.public-portal .turbosidebar.searchtool-sheet #map-sidebar-searchtool-tab-container .select2,
    body.public-portal .turbosidebar.searchtool-sheet #map-sidebar-searchtool-tab-container .select2-container {
        width: 100% !important;
    }
    /* Lift EVERY open select2 option menu above the bottom sheet (z-index:2000). The
       category select2s (Purpose/Natura2000/Building Plots/Unicef) append their menu to
       <body> with a low z-index, so without this the list opens BEHIND the sheet. Not
       scoped to .searchtool-sheet because the menu is body-appended, outside it; still
       mobile + public only, so desktop / IntranetPortal keep the default stacking. */
    body.public-portal .select2-container--open,
    body.public-portal .select2-container--open .select2-dropdown {
        z-index: 3000 !important;
    }

    /* Consistent 44px touch height + rounded corners for all sheet fields. 44px, not the 40px this
       block was written with: S6 later put `min-height: 44px` on every mobile .form-control, which
       silently grew the input while the attached .input-group button kept this block's 40px — a 4px
       step between a text field and its own search button. One number for field, select AND attached
       button keeps the group a single pill. Width is deliberately NOT set here, so .input-group (a)
       keeps its input+button flex row intact. */
    body.public-portal .turbosidebar.searchtool-sheet #map-sidebar-searchtool-tab-container .form-control {
        height: 44px;
        border-radius: 8px;
    }
    body.public-portal .turbosidebar.searchtool-sheet #map-sidebar-searchtool-tab-container .input-group > .form-control {
        border-top-right-radius: 0;
        border-bottom-right-radius: 0;
    }
    body.public-portal .turbosidebar.searchtool-sheet #map-sidebar-searchtool-tab-container .input-group .btn,
    body.public-portal .turbosidebar.searchtool-sheet #map-sidebar-searchtool-tab-container .input-group-text {
        height: 44px;
    }
    /* The theme frames every input-group button with the same light border as the text field
       (app.css: `.input-group .btn { border-color: #dde6e9 }`). Around a FILLED button that 1px light
       frame reads as page background, so the fill looks inset by 1px on every side — "the search button
       looks smaller than the field". Transparent, not a colour: `.btn` has no background-clip, so the
       button's own background paints under the border and the fill reaches the full 44px box whatever
       colour the tenant's theme gives it. The box itself does not move. NOT applied to
       `.input-group-text` — that addon is an outline like the field, and its light border is correct. */
    body.public-portal .turbosidebar.searchtool-sheet #map-sidebar-searchtool-tab-container .input-group .btn {
        border-color: transparent;
    }
    /* Match the trailing element's outer corners to the 8px input so the pill isn't
       8px on the input side / 4px on the button side. */
    body.public-portal .turbosidebar.searchtool-sheet #map-sidebar-searchtool-tab-container .input-group-append:last-child > .btn,
    body.public-portal .turbosidebar.searchtool-sheet #map-sidebar-searchtool-tab-container .input-group-append:last-child > .input-group-text {
        border-top-right-radius: 8px;
        border-bottom-right-radius: 8px;
        border-top-left-radius: 0;
        border-bottom-left-radius: 0;
    }

    /* ===================================================================
       Search RESULTS as cards (Variant A) — mobile sheet only.

       Each category renders a `table.table-striped.table-bordered` of rows into
       #map-sidebar-searchtool-tab-container. Here that table is turned into modern
       cards purely with CSS (no JS): every <tr> becomes a card with a leading pin,
       the FIRST cell as the bold title, any MIDDLE cells as a muted subtitle, and
       the LAST cell (always the locate button) as a trailing "go" chevron. Because
       it targets cells by position, it works for every category despite their
       different column counts (1-4 data cells + the button).
       =================================================================== */

    /* Drop the column header + the <br> spacers; make the generated <h3> a small
       results label. The header is dropped two ways: real <thead> rows, AND the
       bare "<tr><td>label</td>...</tr>" headers that _SearchEvents/_SearchStreetLights
       emit (matched content-wise as "a row with no locate <button>", so a genuine
       result row — which always has the button — is never hidden). All table
       selectors are scoped to .table-bordered so the datetimepicker's
       .table-condensed calendar popup is never carded/hidden. */
    /* The 106-line card block that stood here is deleted (S23). It is `.nexus-cards` in nexus-content.css
       now, applied to this container by the sheet's own `content:` option — deliberately NOT by
       NexusMobile.applyContent, because unlike InfoTool's label/value list this treatment is MOBILE ONLY:
       desktop keeps the separate "minimal table" variant defined below. That difference is real, so the two
       tools apply their content classes through different mechanisms.

       TWO VALUES CHANGED with the move:
         - the locate button is 44x44, was 34x34 (the documented touch minimum per §4 of the guidelines;
           part of S6), and its offset follows arithmetically: right: 4px, was 8px, which keeps the chevron's
           centre where it was (8+34/2 = 25px from the edge, now 4+44/2 = 26px) while the target grows
         - the results caption margin is 4px 2px 10px, was 30px 2px 10px — reviewed and kept by the owner
       Everything else is declaration-for-declaration identical.

       ONE SELECTOR BUG this move introduced, now fixed: the caption rule was first written as
       `.nexus-cards > h3`, but this container does NOT hold the heading directly — each _Search*.cshtml
       partial brings its own inner `#map-sidebar-search<X>-table-container`, so the heading is three levels
       down. The rule never matched, and the caption fell back to the theme's default h3 (large, centred)
       on mobile while the >=768px rule below — a descendant selector — kept working. Every id-keyed rule
       replaced here was a descendant selector for that reason. See the combinator rule at the top of
       nexus-content.css. */

    /* ===================================================================
       EventCalendarTool bottom-sheet (S35) — its own chrome only. Shell, grip, header and the result cards
       come from the shared `.nexus-sheet--snap` block and the `.nexus-cards` class the registry applies on
       enter. What is specific here: the form's two buttons at a real touch height, the one-row filter bar the
       form collapses into on results (44px, range left, Edit right), and the results' Bootstrap `.container`,
       whose 15px gutters would double the sheet's own inset.
       =================================================================== */
    body.public-portal .turbosidebar.eventcalendar-sheet #eventcalendar-search-button,
    body.public-portal .turbosidebar.eventcalendar-sheet #eventcalendar-clear-button,
    body.public-portal .turbosidebar.eventcalendar-sheet #eventcalendar-filterbar-edit {
        min-height: 44px;
    }

    body.public-portal .turbosidebar.eventcalendar-sheet #eventcalendar-filterbar .col-12 {
        min-height: 44px;
    }

    /* The bar takes the form's place, but not its top gap (the form's first row carries `mt-3`), so the Edit
       button sat flush against the sheet header's bottom line. Same 16px as the form. */
    body.public-portal .turbosidebar.eventcalendar-sheet #eventcalendar-filterbar {
        margin-top: 16px;
    }

    body.public-portal .turbosidebar.eventcalendar-sheet #eventcalendar-filterbar-range {
        font-weight: 600;
        color: #1a2030;
    }

    body.public-portal .turbosidebar.eventcalendar-sheet #map-sidebar-eventcalendar-table-container .container {
        padding-left: 0;
        padding-right: 0;
    }

    /* The event popup (an ol.Overlay, kept — section 10 question 4) pages through stacked features with two
       `.fa-2x` glyphs of ~28px. Give them a 44px box on the touch band; the glyph itself is left as it is,
       so desktop is untouched and the shared chevron block in nexus-sheet.css is neither copied nor widened. */
    body.public-portal #eventcalendar-popup .infoToolPrevLeft,
    body.public-portal #eventcalendar-popup .infoToolNextRight {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        min-width: 44px;
        min-height: 44px;
    }

    /* ===================================================================
       LegendTool bottom-sheet (S36) — its own chrome only. Shell, grip, header and the stacked list come from the
       shared `.nexus-sheet--snap` block and the `.nexus-list` class the registry applies on enter (runtime only:
       the component is SHARED with 7 IntranetPortal views, so its markup is untouched). Specific here: the pane's
       Bootstrap `.pt-3` (16px) zeroed like InfoTool's, so peek is the 84 the registry declares and not 100; the
       layer-title row (odd rows of the one-column legend table) rendered as the same small caption `.nexus-list th`
       uses, sitting above its swatches instead of the desktop's grey band, which `.nexus-list tr` already strips;
       and the server-rendered legend graphics, which arrive at their own width, never wider than the sheet.
       =================================================================== */
    body.public-portal .turbosidebar.legendtool-sheet .pt-3 {
        padding-top: 0 !important;
    }

    body.public-portal .turbosidebar.legendtool-sheet #tblLayersLegend tr:nth-child(odd) td {
        padding: 11px 4px 0;
        font-size: 10.5px;
        text-transform: uppercase;
        letter-spacing: .05em;
        color: #6a7280;
        border-bottom: 0 !important;
    }

    body.public-portal .turbosidebar.legendtool-sheet #tblLayersLegend img.layer-legend-image {
        max-width: 100%;
        height: auto;
    }

    /* ===================================================================
       NoiseMonitoring (Areas/PublicPortal/Views/NoiseMonitoring/Index.cshtml).

       The map-click popup is routed through PPInfoPanel, so on this band its content renders in the
       shared `.nexus-sheet--dismiss` shell instead of the anchored OL popup - the shell itself needs
       nothing here. What follows is only what the sensor content needs inside it.
       =================================================================== */

    /* The shell's × is absolutely positioned top-right, which is exactly where the station header puts
       its status chip. Let the header wrap so the chip drops onto its own line under the name instead
       of sitting beneath the ×. */
    body.public-portal .nexus-sheet--dismiss .nm-hd {
        flex-wrap: wrap;
        padding-right: 44px;
    }

    /* ...and give the name a floor to wrap at, so the chip is what moves. Without it `.nm-grow` just
       shrinks (it is `flex: 1; min-width: 0`) and the chip never wraps: measured at 375px, a real
       station name came out over FOUR lines beside a chip that fit only because the name had given up
       everything. 180px is the flex BASE size, which is what the line-breaking step reads: 17px icon +
       9 + 180 + 9 + 65px - the NARROWER of the two chips ("Offline"; "Station active" is 95) - overflows
       the 269px a 375px sheet leaves, so BOTH chips drop to their own line rather than only the wide
       one. On a 414px phone both still fit beside the name and stay put. */
    body.public-portal .nexus-sheet--dismiss .nm-hd .nm-grow {
        flex: 1 1 180px;
    }

    body.public-portal .nexus-sheet--dismiss .nm-hd .nm-chip {
        margin-left: 26px; /* icon (17) + the header's 9px gap: line the chip up under the name */
    }

    /* A taller strip, and the bars grow into it: their height is calc() over the --nm-h ratio the view
       emits (noise-monitoring.css), so this one rule rescales all 24 of them. */
    body.public-portal .nm-strip {
        height: 86px;
    }

    body.public-portal .nm-strip .nm-bar {
        height: calc(13px + var(--nm-h, 0) * 70px);
        position: relative;
    }

    /* 24 hours across a 375px sheet is a ~12px column, and the quietest hour is a 13px stub - neither is
       a touch target, and widening the bar is not available when the whole point is one day at a glance.
       So the TAP AREA is the whole column instead of the drawn bar: 100px tall = the 86px strip (the
       element's bottom sits at the strip's bottom, so `height` counts straight up from there) plus the
       14px hour-label row under it. The ±1px reaches half of the 2px grid gap on each side, so the
       columns tile edge to edge without overlapping each other. */
    body.public-portal .nm-strip .nm-bar::after {
        content: "";
        position: absolute;
        inset: auto -1px -14px;
        height: 100px;
    }

    /* 7.5px is a label for a mouse pointer on a desktop popup. */
    body.public-portal .nm-xlab span {
        font-size: 9px;
    }

    body.public-portal .nm-pp .nm-foot {
        font-size: 11.5px;
    }

    /* ===================================================================
       S32 — "Продукти во нарачката" as CARDS (ShoppingCart/Index, Payments/ConfirmOrder,
       ShoppingCart/ShoppingCartOrders). Phone band only; desktop keeps the tables.

       Measured on the live tenant, 28.08.2026, at 390px: the cart table (10 columns) is 607px wide inside
       a 328px card body — horizontal scroll, headers broken over 3-4 lines, "Price" and "Action" off-screen;
       the orders table (13 columns) is DataTables-Responsive-collapsed to a single "Serial number" column
       with the rest behind a "+" control. Neither is a table a citizen can read on a phone.

       CSS-only, keyed on classes the three views now carry: `table.nexus-order-items` plus one class per
       cell ROLE — `.nexus-oi-serial / -title / -price / -action`, and on the orders page `-order / -type /
       -status / -cert`. Every other cell is a detail cell and prints its own column header as a small label
       from `data-label`. Roles are addressed by CLASS, not by position, because the three tables order
       their columns differently (municipality and plot number are swapped between the cart and the orders
       page), so a position-keyed shape would label the wrong cell on one of them.

       Not `.nexus-cards`: that shape is position-keyed for a 1-4 cell row with ONE trailing action, and it
       lives in nexus-content.css because it must render inside three shells. This one belongs to three
       plain pages, so it lives here with the other page-scoped mobile rules.

       Desktop is untouched by construction: everything here is inside the mobile query, and the only markup
       added for it (the "Delete" label inside the cart's trash button) is `d-none` at every other width.
       The orders page keeps DataTables search/paging/info; only its `responsive` option is switched off on
       the phone band (ShoppingCartOrders.cshtml, via NexusMobile.isPublicPageMobile()), because column
       collapsing and these cards are two answers to the same question.
       =================================================================== */
    body.public-portal .nexus-order-items {
        margin: 0 !important; /* the markup's `my-4` is !important (and DataTables' own table margin too); the cards carry their own spacing */
        width: 100%;
        border: 0; /* `.table-bordered` (orders page) frames the TABLE itself; Bootstrap's `.table-responsive > .table-bordered { border: 0 }` cannot reach it through the DataTables wrapper */
    }

    body.public-portal .nexus-order-items thead {
        display: none; /* each cell prints its own header label instead */
    }

    body.public-portal .nexus-order-items tbody,
    body.public-portal .nexus-order-items tfoot,
    body.public-portal .nexus-order-items tr,
    body.public-portal .nexus-order-items td {
        display: block;
        width: 100%;
        box-sizing: border-box; /* DataTables sets `content-box` on td — 50% + padding would then overflow the row */
        border: 0;
        padding: 0;
        text-align: left; /* ConfirmOrder and the orders page are `.text-center` tables */
        white-space: normal;
    }

    /* One card per row. Flex rather than grid: the header trio (serial / title / price) and the trailing
       action row are placed with `order`, and the detail cells between them wrap into two columns on their
       own, whatever their count. */
    body.public-portal .nexus-order-items tbody tr {
        display: flex;
        flex-wrap: wrap;
        align-items: flex-start;
        background-color: #fff; /* also kills the zebra and the orders page's whiteBack/greyBack shading */
        border: 1px solid #eee;
        border-radius: 4px;
        margin-bottom: 12px;
        padding-top: 6px;
    }

    body.public-portal .nexus-order-items tbody td {
        flex: 0 0 50%;
        width: auto; /* flex-basis owns the size; a `width: 100%` here becomes the basis of the `auto` cells (price) */
        max-width: 100%;
        padding: 6px 14px;
        font-size: 14px;
        line-height: 18px;
        color: #656565;
        word-break: break-word;
    }

    body.public-portal .nexus-order-items tbody td::before {
        content: attr(data-label);
        display: block;
        font-size: 12px;
        line-height: 14px;
        color: #6c757d; /* 4.69:1 on white; the theme's #909fa7 is 2.7:1 (WCAG AA needs 4.5:1 at this size) */
        margin-bottom: 1px;
    }

    /* ---- Header trio: serial number, product name, price ---- */
    body.public-portal .nexus-order-items tbody td.nexus-oi-serial {
        order: -3;
        flex-basis: 100%;
        padding-bottom: 0;
        font-size: 12px;
        line-height: 14px;
        color: #6c757d;
    }

    /* Carries the table id ON PURPOSE. The serial number is the views' own CSS counter, drawn by
       `table#permitIssuanceTable tbody tr:nth-child(n-1) td:first-child::before`, and only a selector that
       also bears the id outranks that. Same table, same id, still PublicPortal-scoped by the body class. */
    body.public-portal table#permitIssuanceTable.nexus-order-items tbody tr td.nexus-oi-serial::before {
        content: attr(data-label) " " counter(rowNumber);
        display: inline;
        margin: 0;
        min-width: 0;
    }

    body.public-portal .nexus-order-items tbody td.nexus-oi-title {
        order: -2;
        flex: 1 1 55%;
        padding-top: 2px;
        font-size: 15px;
        line-height: 20px;
        font-weight: 600;
        color: #4a4a4a;
    }

    body.public-portal .nexus-order-items tbody td.nexus-oi-price {
        order: -1;
        flex: 0 0 auto;
        margin-left: auto;
        padding-top: 2px;
        font-size: 16px;
        line-height: 20px;
        font-weight: 700;
        color: #4a4a4a;
        white-space: nowrap;
    }

    /* Cells that explain themselves print no label: the product name, the price, the two action cells,
       and DataTables' own empty-state cell. */
    body.public-portal .nexus-order-items tbody td.nexus-oi-title::before,
    body.public-portal .nexus-order-items tbody td.nexus-oi-price::before,
    body.public-portal .nexus-order-items tbody td.nexus-oi-action::before,
    body.public-portal .nexus-order-items tbody td.nexus-oi-cert::before,
    body.public-portal .nexus-order-items tbody td.dataTables_empty::before {
        display: none;
    }

    /* ---- Orders page: the order the item belongs to, above the item ---- */
    body.public-portal .nexus-order-items tbody td.nexus-oi-order {
        order: -6;
        flex: 1 1 50%;
        font-size: 18px;
        line-height: 22px;
        font-weight: 700;
        color: #4a4a4a;
    }

    body.public-portal .nexus-order-items tbody td.nexus-oi-type {
        order: -5;
        flex: 1 1 50%;
    }

    body.public-portal .nexus-order-items tbody td.nexus-oi-status {
        order: -4;
        flex-basis: 100%;
        font-weight: 600;
        border-bottom: 1px solid #eee;
        padding-bottom: 10px;
        margin-bottom: 4px;
    }

    /* Status colour by lookup id (extract_plot_orders_status), never by the tenant-editable name. Same
       reading as the IntranetPortal component: done = green, unpaid = red. */
    /* The theme's status hues, darkened to pass WCAG AA on white (>= 4.5:1 at 14px): the theme values are
       2.3-3.5:1 and these labels are the only status cue a low-vision citizen gets. */
    body.public-portal .nexus-order-items tbody td.nexus-oi-status--1 { color: #b45309; } /* paid, in progress (theme #ff902b) */
    body.public-portal .nexus-order-items tbody td.nexus-oi-status--2,
    body.public-portal .nexus-order-items tbody td.nexus-oi-status--4 { color: #1a7f37; } /* delivered / picked up (theme #27c24c) */
    body.public-portal .nexus-order-items tbody td.nexus-oi-status--3 { color: #0f6f8c; } /* ready at the counter (theme #1797be) */
    body.public-portal .nexus-order-items tbody td.nexus-oi-status--5 { color: #c0392b; } /* not paid (theme #f05050) */

    /* ---- Trailing action row: the cart's delete button, the orders page's certificate cell ---- */
    body.public-portal .nexus-order-items tbody td.nexus-oi-action,
    body.public-portal .nexus-order-items tbody td.nexus-oi-cert {
        order: 1;
        flex-basis: 100%;
        display: flex;
        justify-content: flex-end;
        align-items: center;
        gap: 6px;
        border-top: 1px solid #eee;
        margin-top: 4px;
        padding: 4px 6px;
        color: #6c757d; /* the "Pending" spinner + text, and the paper-order glyph, read as status */
    }

    /* ONE definition of "an action button in a card row", two colourings below. 44px tall (guidelines §4);
       inline-flex so the glyph and the label sit on one line. */
    body.public-portal .nexus-order-items td.nexus-oi-action .btn,
    body.public-portal .nexus-order-items td.nexus-oi-cert .btn {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        gap: 8px;
        margin: 0 !important; /* the cart button carries `mb-1`, which is !important */
        /* `!important` is load-bearing: `.btn-danger:not(:disabled):not(.disabled):active:focus` (bootstrap.css) scores
           (0,5,0) against this selector's (0,4,2), so while a focused Delete is pressed Bootstrap's 0.2rem red ring
           would win over a plain `none`. Same reasoning as the card-action rule in nexus-content.css. */
        box-shadow: none !important;
    }

    /* Delete: destructive, so it reads as destructive — ghost red with the trash glyph, not the theme's
       filled red block. On desktop the label is a `title` tooltip; on a phone there is no hover, so the
       label is shown. */
    body.public-portal .nexus-order-items td.nexus-oi-action .btn {
        padding: 0 12px;
        background-color: transparent;
        border-color: transparent;
        color: #c0392b; /* the theme's danger #f05050 is 3.5:1 on white; this is 5.4:1 */
    }

    body.public-portal .nexus-order-items .nexus-oi-action-label {
        display: inline !important; /* beats the utility's `d-none`, which is !important */
        font-family: "Source Sans Pro", sans-serif; /* the button itself is in the icon font (.fas) */
        font-weight: 400;
        font-size: 14px;
    }

    /* Download: the row's one real action, so it is the primary button. */
    body.public-portal .nexus-order-items td.nexus-oi-cert .btn {
        padding: 0 14px;
        background-color: #5d9cec;
        border-color: #5d9cec;
        color: #fff;
        font-weight: 600;
    }

    /* Killing the ring above removes the ONLY focus indication these buttons have: app.css ships
       `.btn { outline: none !important }`, so an outline here would be dead. Put a box-shadow ring back for
       keyboard users. `:focus-visible`, not `:focus`, so a tap does not paint it (same convention as
       `.nexus-cards` in nexus-content.css). `!important` because the base rule above needs it. */
    body.public-portal .nexus-order-items td.nexus-oi-action .btn:focus-visible,
    body.public-portal .nexus-order-items td.nexus-oi-cert .btn:focus-visible {
        box-shadow: 0 0 0 3px rgba(93, 156, 236, 0.55) !important;
    }

    /* DataTables pager (orders page): Bootstrap's `.page-link` is ~35px tall and ~34px wide per digit — under
       guidelines section 4. Search and length already get 44px from the S6 `.form-control` / `.custom-select`
       rules; this is the one control in the wrapper they do not reach. */
    body.public-portal .dataTables_wrapper .pagination .page-link {
        display: flex;
        align-items: center;
        justify-content: center;
        min-width: 44px;
        min-height: 44px;
    }

    /* ---- Totals (cart + confirm): label left, amount right; the last row is the grand total ---- */
    body.public-portal .nexus-order-items tfoot tr {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 4px 0;
    }

    body.public-portal .nexus-order-items tfoot td {
        width: auto;
    }

    body.public-portal .nexus-order-items tfoot td:first-child {
        flex: 1 1 auto;
        text-align: left !important; /* the markup's `.text-right` is !important */
    }

    body.public-portal .nexus-order-items tfoot tr:last-child {
        border-top: 1px solid #eee;
        margin-top: 4px;
        padding-top: 10px;
        font-size: 18px;
        line-height: 24px;
        font-weight: 700;
        color: #4a4a4a;
    }

    /* DataTables' "no data" cell spans the whole card. */
    body.public-portal .nexus-order-items tbody td.dataTables_empty {
        flex-basis: 100%;
        text-align: center;
        padding: 12px;
    }
}

/* ==========================================================================
   Search RESULTS — Variant C "minimal table"  (PublicPortal DESKTOP only).

   On desktop the SearchTool is a normal sidebar panel (not the mobile bottom-
   sheet), so results stay tabular. This restyles the Bootstrap
   `table-striped table-bordered table-hover` result table into a clean minimal
   table: an uppercase hairline header, no cell borders / zebra stripes, hairline
   row separators, a full-row hover, and a cyan "ghost" locate button (same cyan
   as the map highlight). CSS-only, targeting cells by position, so it works for
   every category. Scoped to body.public-portal + >=768px so the mobile sheet
   (Variant A cards) and IntranetPortal stay untouched; table.table-bordered keeps
   the datetimepicker's .table-condensed calendar popup out of scope. Row click is
   deliberately left button-only (unlike the mobile card) so KP text stays
   selectable/copyable with a mouse.
   ========================================================================== */
@media screen and (min-width: 768px) and (min-height: 501px) {

    /* results category heading -> small uppercase caption */
    body.public-portal #map-sidebar-searchtool-tab-container h3 {
        font-size: 11px;
        text-transform: uppercase;
        letter-spacing: .05em;
        font-weight: 600;
        color: #6a7280;
        text-align: left !important;
        margin: 14px 2px 8px;
    }

    /* strip the heavy bootstrap table chrome (all borders + zebra stripes) */
    body.public-portal #map-sidebar-searchtool-tab-container table.table-bordered,
    body.public-portal #map-sidebar-searchtool-tab-container table.table-bordered th,
    body.public-portal #map-sidebar-searchtool-tab-container table.table-bordered td {
        border: 0;
    }
    body.public-portal #map-sidebar-searchtool-tab-container table.table-bordered {
        width: 100%;
        margin-bottom: 8px;
        font-size: 13.5px;
    }
    body.public-portal #map-sidebar-searchtool-tab-container table.table-striped tbody tr:nth-of-type(odd) {
        background-color: transparent;
    }

    /* uppercase hairline header */
    body.public-portal #map-sidebar-searchtool-tab-container table.table-bordered thead th {
        padding: 6px 10px;
        font-size: 11px;
        font-weight: 600;
        text-transform: uppercase;
        letter-spacing: .05em;
        text-align: left;
        color: #6a7280;
        background: transparent;
        border-bottom: 1px solid #e6e9ef;
    }
    body.public-portal #map-sidebar-searchtool-tab-container table.table-bordered thead th:last-child {
        text-align: right;
    }

    /* body: hairline row separators + full-row hover */
    body.public-portal #map-sidebar-searchtool-tab-container table.table-bordered tbody td {
        padding: 10px;
        vertical-align: middle;
        border-bottom: 1px solid #eef1f5;
    }
    body.public-portal #map-sidebar-searchtool-tab-container table.table-bordered tbody tr {
        cursor: pointer;
        transition: background-color .12s;
    }
    body.public-portal #map-sidebar-searchtool-tab-container table.table-bordered tbody tr:hover {
        background-color: #eef3ff;
    }

    /* first cell emphasised, middle cells muted */
    body.public-portal #map-sidebar-searchtool-tab-container table.table-bordered tbody td:first-child {
        font-weight: 600;
        color: #161d2b;
    }
    body.public-portal #map-sidebar-searchtool-tab-container table.table-bordered tbody td:not(:first-child):not(:last-child) {
        color: #6a7280;
    }

    /* last cell = locate action -> right-aligned cyan ghost button */
    body.public-portal #map-sidebar-searchtool-tab-container table.table-bordered tbody td:last-child {
        width: 1%;
        white-space: nowrap;
        text-align: right;
    }
    body.public-portal #map-sidebar-searchtool-tab-container table.table-bordered tbody td:last-child .btn {
        width: 32px;
        height: 32px;
        min-width: 32px;
        padding: 0;
        display: inline-flex;
        align-items: center;
        justify-content: center;
        border: 1px solid transparent;
        border-radius: 8px;
        background: transparent;
        color: #0aa6c6;
        box-shadow: none;
    }
    body.public-portal #map-sidebar-searchtool-tab-container table.table-bordered tbody tr:hover td:last-child .btn {
        border-color: #0aa6c6;
        background: #e2f5fa;
    }
    body.public-portal #map-sidebar-searchtool-tab-container table.table-bordered tbody td:last-child .btn:focus-visible {
        outline: 2px solid #0aa6c6;
        outline-offset: 1px;
    }
}

/* ==========================================================================
   InfoTool RESULTS — clean label/value list  (PublicPortal DESKTOP only).

   Brings the desktop InfoTool sidebar pane in line with the mobile bottom-sheet
   LOOK without turning it into a sheet: the pane stays a normal right-docked
   panel (as now), but its Bootstrap `table-striped table-bordered table-hover`
   info table is restyled into the same clean label/value list the sheet uses —
   a muted label on the LEFT, a bold value on the RIGHT, hairline DASHED row
   separators — a type ROW above the pager (result type bold, left) that the
   controller bakes into the pane and shares with the sheet, and a centered
   `‹  N / total  ›` pager whose bordered-circle chevron arrows override the Font
   Awesome icons (mirrors the sheet). Targets cells by position so it works for
   every category (building plot, block, scope, business entity, N2k…). Scoped to
   body.public-portal + >=768px so the mobile sheet (max-width 767.98px) and
   IntranetPortal (no .public-portal body class) stay untouched. Colours are the
   same as the sheet block above. Pairs with the JS: the baked type row, the
   counter/type population extended to PublicPortal, and infoSheetExit no longer
   removing the (now shared) type row.
   ========================================================================== */
@media screen and (min-width: 768px) and (min-height: 501px) {

    /* Zero Bootstrap's .pt-3 top padding so the type row sits flush under the pane header (no
       floating gap above its top divider line). Symmetry itself no longer depends on this — the
       type row owns both of its divider lines (see below) — but this keeps the top line snug. */
    body.public-portal #map-sidebar-infotool-pane .pt-3 {
        padding-top: 0 !important;
    }

    /* Type ROW above the pager (mirrors the sheet): result type BOLD on the left. The row
       is baked into the pane ONCE by the controller (for PublicPortal) and shared with the
       mobile sheet; shown here for the desktop panel (the top guard hides it by default). Its
       close X is kept for the sheet but HIDDEN on desktop — the pane header keeps its own X.
       Only shown once there's a result: the controller adds `.has-type` when a feature renders,
       so before a click (or on an empty identify) there's no stray empty strip.

       The row owns BOTH divider lines — border-top (the line under the "Инфо" header) and
       border-bottom (the line under the type) — with equal padding, so the type is EXACTLY
       equidistant from each line, independent of any Bootstrap .pt-3 padding above the row. */
    body.public-portal #map-sidebar-infotool-pane .info-tool-typerow.has-type {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 16px 4px 16px;
        border-top: 1px solid #e3e7ef;
        border-bottom: 1px solid #e3e7ef;
    }
    body.public-portal #map-sidebar-infotool-pane .info-tool-typerow #map-info-tool-type {
        font-weight: 700;
        /* Larger — only the type name (Опфат / Блок / Градежна парцела). */
        font-size: 18px;
        color: #1a2030;
    }
    body.public-portal #map-sidebar-infotool-pane .info-tool-typerow .info-tool-sheet-close {
        display: none;
    }

    /* The <h3> is hidden by `.nexus-list--titleless` now (S23). Only the structural <br> spacers stay here —
       `> br` matches DIRECT children only, never a <br> inside a value cell (UNICEF multi-line values), so
       those keep their line breaks. */
    body.public-portal #map-sidebar-infotool-tab-container > br {
        display: none;
    }

    /* Pager visibility + row layout. Arrow glyph, circle and counter styling come from nexus-sheet.css
       (which reaches InfoTool's portal-neutral <em>s behind body.public-portal). The row shows only when
       there's more than one result to page (the controller adds `.has-pager`); otherwise it is hidden. */
    body.public-portal #map-sidebar-infotool-pane .info-tool-nav {
        display: none;
    }
    body.public-portal #map-sidebar-infotool-pane .info-tool-nav.has-pager {
        display: flex;
        align-items: center;
        justify-content: space-between;
        padding: 12px 4px;
    }
    body.public-portal #map-sidebar-infotool-pane #map-info-tool-counter {
        display: inline-block;
    }

    /* The desktop copy of the same 19 declarations is deleted here too (S23). `.nexus-list` carries them
       and is applied at every width, so this block existing separately was only ever a way of saying
       "the same look on desktop". */

    /* Download buttons (General/Special conditions, Numerical indicators, N2k…) ->
       separated outline pills instead of a joined btn-group. */
    body.public-portal #map-sidebar-infotool-tab-container .btn-toolbar {
        display: flex;
        flex-wrap: wrap;
        gap: 8px;
        margin-top: 14px;
    }
    body.public-portal #map-sidebar-infotool-tab-container .btn-toolbar .btn {
        border-radius: 8px !important;
        flex: 1 1 auto;
    }
}

/* ==========================================================================
   Navbar — collapses below the lg (992px) breakpoint into a hamburger menu.
   The collapse is left IN-FLOW (not an overlay) so it inherits the .topnavbar
   background/text colours automatically — no per-theme colour guessing. Trade-off:
   on map pages the opened menu grows the header; setMapHeight ran on load, so the
   map may need the menu closed to reframe (flagged for QA / optional JS follow-up).
   ========================================================================== */
@media screen and (max-width: 991.98px) {
    /* Angle only styles .navbar-toggler under .layout-h, so style the hamburger here. */
    body.public-portal .topnavbar .navbar-toggler {
        display: inline-block;
        border: 0;
        color: #fff;
        font-size: 1.5rem;
        padding: .25rem .75rem;
        margin-left: auto;
    }

    body.public-portal .topnavbar .navbar-collapse,
    body.public-portal .topnavbar .navbar-nav {
        width: 100%;
    }

    body.public-portal .topnavbar .navbar-nav .nav-link {
        padding: .6rem .25rem;
    }

    /* Dropdowns render inline (static) inside the collapsed menu instead of Angle's
       absolute top:54px positioning, so submenu items stay in flow and readable. */
    body.public-portal .topnavbar .dropdown-menu {
        position: static;
        float: none;
        width: 100%;
        margin: 0;
        border: 0;
        box-shadow: none;
    }
}

/* ==========================================================================
   Navbar "Home" button — the way back to the start page's cards (owner, 29.09.2026).

   A white house icon between the logo and the hamburger, outside the collapse so it is one tap away
   without opening the menu. The navbar renders it only for a logged-in user who has cards to go back
   to, and never on the start page itself (_TopNavbar.cshtml).

   Variant 6, "icon only" (owner, 29.09.2026): the first version was a white pill with "Почетна",
   which the owner found too loud. Now it is drawn like the hamburger beside it - white, no background,
   no border - so the two read as one pair of navbar icons on every theme. The name "Почетна" (key
   "Start page") is its aria-label. The tap target stays 44x44.

   PHONE ONLY, so it is hidden at every width first and shown in the phone band below; desktop keeps
   its navbar exactly as it was.
   ========================================================================== */
body.public-portal .topnavbar .pp-navbar-start {
    display: none;
}

@media screen and (max-width: 767.98px),
       screen and (max-height: 500px) and (orientation: landscape) {

    body.public-portal .topnavbar .pp-navbar-start {
        display: inline-flex;
        flex-shrink: 0;
        align-items: center;
        justify-content: center;
        width: 44px;
        height: 44px;
        margin-left: auto;
        border-radius: 50%;
        color: #fff;
        font-size: 1.375rem;
        text-decoration: none;
    }

    body.public-portal .topnavbar .pp-navbar-start:hover,
    body.public-portal .topnavbar .pp-navbar-start:focus {
        color: #fff;
        text-decoration: none;
    }

    /* The only feedback a finger gets: a soft dark disc while it is pressed. */
    body.public-portal .topnavbar .pp-navbar-start:active {
        background: rgba(0, 0, 0, .12);
    }

    body.public-portal .topnavbar .pp-navbar-start:focus-visible {
        outline: 2px solid #fff;
        outline-offset: 0;
    }

    /* The hamburger's own `margin-left: auto` (the lg block above) would split the free space between
       the two; the house takes it all, and the hamburger sits right beside it. */
    body.public-portal .topnavbar .pp-navbar-start + .navbar-toggler {
        margin-left: 4px;
    }
}

/* ==========================================================================
   Navbar logo on a phone: mirrors the menu button (owner, 07.10.2026).

   Under 768px the theme pads the navbar header with 2rem, so the logo sat 32px in from the edge; a
   phone held sideways gets the desktop header, a 90px column with the logo centred in it, 24px in.
   Now the logo answers the hamburger on the right:
   - its shield starts 12px from the left edge, as the hamburger's bars end 12px from the right. The
     logo image has a transparent margin of its own (10.5% of its width, ~4px at this size), so the
     header pads 8px;
   - it is 44px instead of 50px tall (at 50px in the 55px navbar it touched the top and the bottom),
     and sits 4px below the centre line: the shield's top is then level with the top of the menu
     button (~11px), its bottom ~3px above the navbar's edge (owner, "spušti go podole").
   Desktop keeps its header.
   ========================================================================== */
@media screen and (max-width: 767.98px),
       screen and (max-height: 500px) and (orientation: landscape) {

    body.public-portal .topnavbar .navbar-header {
        width: auto;
        padding-left: 8px;
        text-align: left;
        position: relative;
        top: 4px;
    }

    body.public-portal .topnavbar .navbar-header .navbar-brand {
        width: auto;
    }

    body.public-portal .topnavbar .navbar-header .brand-logo .img-fluid,
    body.public-portal .topnavbar .navbar-header .brand-logo-collapsed .img-fluid {
        max-height: 44px;
    }
}

/* At >=992px keep the pre-existing wrapping behaviour: navbar-expand-lg would set
   the row to nowrap, overflowing tenants with many nav items at 992-1366px. */
@media screen and (min-width: 992px) {
    body.public-portal .topnavbar .navbar-collapse {
        flex-wrap: wrap;
    }
}

/* ==========================================================================
   SHORT LANDSCAPE — the tool strip in two columns.

   The strip is `height: 100%` of the map box with `overflow: hidden` and no scroll (ol3-sidebar.css),
   so the moment its items stop fitting, the last ones are simply gone — no indicator, no scroll, no clue
   they exist. Measured on the deployed tenant, logged in, 10 items:

       device (landscape)          map box   strip   item   needs   reachable
       iPhone SE 2020  667x375      320px    320px   44px   440px     7 of 10
       iPhone 12 Pro   844x390      335px    315px   40px   400px     8 of 10
       Pixel 7         892x412      357px    337px   40px   400px     8 of 10
       iPhone 14 ProMax 932x430     375px    355px   40px   400px     9 of 10
       iPhone 12 Pro portrait       789px    789px   44px   440px    10 of 10

   The two tools that fell off the bottom every time were GoToCoordinatesTool and
   ExtractPlotElectronicIssuanceTool — last in the strip, and invisible on every phone in landscape.

   Two columns, not smaller icons: 44px is the documented touch minimum (mobile-guidelines.md section 4)
   and shrinking to fit would trade a visible defect for an unmeasurable one. 10 items in 2 columns is
   5 rows = 220px, which fits every box above with room to spare. The cost is 44px of width, which in
   landscape is the axis with 844px on it.

   `overflow: auto` on the container is the escape hatch, not the design: a tenant carrying more than
   14 tools would wrap into a third column, and this keeps it reachable instead of clipped.
   ========================================================================== */
@media screen and (max-height: 500px) and (orientation: landscape) {

    body.public-portal .turbosidebar.collapsed {
        width: 88px;
    }

    body.public-portal .turbosidebar-tabs,
    body.public-portal .turbosidebar-tabs > ul {
        width: 88px;
    }

    /* Column-wrap: items fill top-to-bottom, then start a second column. `height: 100%` is what makes
       the wrap happen at all — the <ul> is position:absolute with auto height in the base sheet, so
       without it there is no axis to wrap against. */
    body.public-portal .turbosidebar-tabs > ul {
        display: flex;
        flex-flow: column wrap;
        align-content: flex-start;
        height: 100%;
    }

    body.public-portal .turbosidebar-tabs {
        overflow: auto;
        -webkit-overflow-scrolling: touch;
    }

    body.public-portal .turbosidebar-tabs > li,
    body.public-portal .turbosidebar-tabs > ul > li {
        width: 44px;
        flex: 0 0 44px;
    }

    /* The strip width doubles as the content inset, so both sides move with it. */
    body.public-portal .turbosidebar-right .turbosidebar-content {
        right: 88px;
    }

    body.public-portal .turbosidebar-left .turbosidebar-content {
        left: 88px;
    }

    body.public-portal .turbosidebar-right .turbosidebar-header {
        padding-left: 88px;
    }
}

/* ==========================================================================
   Print dialog ("Генерирај карта", ol-ext PrintDialog).

   ol-ext lays it out for a desktop: an 18em parameter column (13em under 25em wide) on the left and
   the page preview in what is left. On a 390px phone that is 182px of form beside a 178px preview:
   the two orientation tiles stack, and "Печати..." and "Откажи" fall under each other in the corner
   of the narrow column. On a phone the dialog takes the whole screen, the preview sits on top, the
   parameters scroll below it and the two buttons sit side by side at the bottom, full width, 44px.
   ol-ext scales the preview page to its container on open and on every resize
   (PrintDialog.setSize), so the preview follows the new box by itself.
   ========================================================================== */

@media screen and (max-width: 767.98px),
       screen and (max-height: 500px) and (orientation: landscape) {

    body.public-portal .ol-ext-print-dialog > form,
    body.public-portal .ol-ext-print-dialog.ol-visible > form {
        top: 0;
        left: 0;
        right: 0;
        bottom: 0;
        border: 0;
        box-shadow: none;
    }

    body.public-portal .ol-ext-print-dialog .ol-print-map {
        top: 0;
        left: 0;
        right: 0;
        bottom: auto;
        width: auto;
        height: 36%;
    }

    /* A column, so the buttons can sit at its foot when the parameters are short (margin-top: auto
       below); its children keep their size instead of shrinking into the scroll box. */
    body.public-portal .ol-ext-print-dialog .ol-print-param {
        top: 36%;
        left: 0;
        right: 0;
        width: auto;
        overflow-y: auto;
        padding: 1em 1em 0;
        display: flex;
        flex-direction: column;
    }

    body.public-portal .ol-ext-print-dialog .ol-print-param > * {
        flex-shrink: 0;
    }

    /* Close: top right over the preview (ol-ext puts it at left: 16.5em, made for the desktop column),
       a 44px target on a white disc so it reads over any map. */
    body.public-portal .ol-ext-print-dialog > form .ol-closebox {
        top: 8px;
        right: 8px;
        left: auto;
        width: 44px;
        height: 44px;
        border-radius: 50%;
        background-color: #fff;
        box-shadow: 0 1px 4px rgba(0, 0, 0, .3);
        color: #333;
        z-index: 2;
    }

    body.public-portal .ol-ext-print-dialog > form .ol-closebox:before,
    body.public-portal .ol-ext-print-dialog > form .ol-closebox:after {
        width: 18px;
        height: 2px;
    }

    /* The two buttons side by side, full width, kept in view at the bottom while the parameters
       scroll. The DOM order is Print, Cancel: row-reverse puts Cancel on the left, Print on the right. */
    body.public-portal .ol-ext-print-dialog .ol-ext-buttons {
        position: sticky;
        bottom: 0;
        display: flex;
        flex-direction: row-reverse;
        gap: 8px;
        margin: auto -1em 0;
        padding: 12px 1em calc(12px + env(safe-area-inset-bottom));
        background-color: #fff;
        border-top: 1px solid #ddd;
    }

    body.public-portal .ol-ext-print-dialog .ol-ext-buttons button {
        flex: 1 1 0;
        min-height: 44px;
        margin: 0;
        font-size: 1rem;
        border-radius: 4px;
    }
}

/* A phone held sideways is wide and short: the preview and the parameters go back side by side, half
   and half, keeping the full-screen form and the bottom buttons from the block above. */
@media screen and (max-height: 500px) and (orientation: landscape) {

    body.public-portal .ol-ext-print-dialog .ol-print-map {
        bottom: 0;
        left: auto;
        width: 50%;
        height: auto;
    }

    body.public-portal .ol-ext-print-dialog .ol-print-param {
        top: 0;
        right: auto;
        width: 50%;
    }
}

/* ==========================================================================
   School redistricting search (Address/Index, .nexus-school-search) on a phone.

   The form is two Bootstrap rows of col-sm-4 columns with no gap between them. Under 576px every input
   stacks right against the next one (Entrance directly on top of Apartment), and between 576 and 767px
   three inputs squeeze into one line (Docs/mobile-guidelines.md §6 names this page). On a phone: street
   and house number full width, entrance and apartment side by side (they are short numbers), both
   search buttons full width, and 44px fields at 16px so iOS does not zoom in on focus (§4).
   ========================================================================== */
@media screen and (max-width: 767.98px),
       screen and (max-height: 500px) and (orientation: landscape) {

    body.public-portal .nexus-school-search .card-header .row {
        row-gap: 12px;
    }

    /* ~ and not +: the <br> between the two rows is a sibling too (hidden below, as it would double the gap). */
    body.public-portal .nexus-school-search .card-header .row ~ .row {
        margin-top: 12px;
    }

    /* The <br> between the two rows would double that gap once the columns stack. */
    body.public-portal .nexus-school-search .card-header > br {
        display: none;
    }

    body.public-portal .nexus-school-search .card-header .row > [class*="col-"] {
        flex: 0 0 100%;
        max-width: 100%;
    }

    body.public-portal .nexus-school-search .card-header .row > .nexus-school-half {
        flex: 0 0 50%;
        max-width: 50%;
    }

    body.public-portal .nexus-school-search .form-control {
        min-height: 44px;
        font-size: 16px;
    }

    /* "Детали" under a result and "Назад кон пребарувањето" on the details page are btn-sm links, 31px. */
    body.public-portal .nexus-school-search a.btn {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        gap: 6px;
    }

    /* Full width, and all four corners rounded: "rounded-right" was made for a button beside the fields. */
    body.public-portal .nexus-school-search #btnSubmit,
    body.public-portal .nexus-school-search #btnSerachByMap {
        width: 100%;
        min-height: 44px;
        border-radius: .25rem;
    }
}

/* ==========================================================================
   Account page (Account/Index, .nexus-account): password, e-mail, phone and notifications on a phone.

   Every form carries Bootstrap's ml-5 (48px) inside two nested card bodies of 15px each, so on a 390px
   phone the fields started 95px from the edge and were about 205px wide: the e-mail address and the
   longer placeholders ("Потврдете ја тековната лозинка") were cut off. The four tabs wrapped into three
   or four ragged rows, "Промени е-пошта" broke at its hyphen in a half-width button, and the Yes/No
   radios were 21px tall. On a phone: no margin and slimmer card padding, the tabs as an even 2 x 2 grid,
   the buttons full width with the action on top, and a 44px row for each radio.
   ========================================================================== */
@media screen and (max-width: 767.98px),
       screen and (max-height: 500px) and (orientation: landscape) {

    /* ml-5 is !important in Bootstrap, so only !important takes it back. */
    body.public-portal .nexus-account .tab-pane form {
        margin-left: 0 !important;
    }

    body.public-portal .nexus-account > .card-body,
    body.public-portal .nexus-account .tab-pane > .card-body {
        padding-left: 10px;
        padding-right: 10px;
    }

    /* Whatever still does not fit (a long address on a narrow phone) ends in "..." instead of a hard cut. */
    body.public-portal .nexus-account .form-control {
        padding-left: 12px;
        padding-right: 12px;
        text-overflow: ellipsis;
    }

    /* "Потврдете ја тековната лозинка" needs 226px at 16px and a 360px phone has 217. Only the hint is
       smaller - typed text stays 16px, which is what keeps iOS from zooming in. */
    body.public-portal .nexus-account .form-control::placeholder {
        font-size: 15px;
    }

    body.public-portal .nexus-account .nav-tabs {
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 6px;
        margin-bottom: 10px;
        border-bottom: 0;
    }

    body.public-portal .nexus-account .nav-tabs > .nav-item > .nav-link {
        height: 100%;
        min-height: 44px;
        display: flex;
        align-items: center;
        justify-content: center;
        padding: 8px 10px;
        text-align: center;
        border-radius: 4px;
    }

    /* The desktop look - a white tab whose bottom border opens into the content - has nothing to open into
       in a grid, so the open tab is marked in the primary colour of the buttons. */
    body.public-portal .nexus-account .nav-tabs > .nav-item > .nav-link.active,
    body.public-portal .nexus-account .nav-tabs > .nav-item > .nav-link.active:hover,
    body.public-portal .nexus-account .nav-tabs > .nav-item > .nav-link.active:focus {
        color: #5d9cec;
        background-color: #fff;
        border-color: #5d9cec;
        box-shadow: inset 0 -3px 0 #5d9cec;
    }

    /* The content box had no top border of its own: the tab row drew that line. */
    body.public-portal .nexus-account .tab-content {
        border-top-width: 1px;
    }

    /* The two col-6 buttons sit in a flex-row-reverse row, so at full width the action comes first. */
    body.public-portal .nexus-account form .row > .btn {
        flex: 0 0 100%;
        max-width: 100%;
        min-height: 44px;
        display: flex;
        align-items: center;
        justify-content: center;
    }

    body.public-portal .nexus-account .form-check-inline {
        margin-right: 24px;
    }

    body.public-portal .nexus-account .form-check-input {
        width: 22px;
        height: 22px;
        margin-right: 8px;
    }

    /* The label is the tap target (for=): 44px tall, a little wider than the word. */
    body.public-portal .nexus-account .form-check-label {
        min-height: 44px;
        display: inline-flex;
        align-items: center;
        padding-right: 8px;
    }
}
