/* =========================================================================
   style.css - DataSense shared stylesheet
   =========================================================================
   Reusable, theme-driven CSS for the whole site (login, register, logout,
   change-password, and the main dashboard). Everything is built on CSS
   custom properties in :root, so a future site can reuse this file by
   copy/pasting it and only touching the variables block below.

   Sections in this file:
     1. Theme variables
     2. Base / reset
     3. Layout helpers (grid background, page shell)
     4. Topbar
     5. Dropdown menu
     6. Buttons
     7. Forms + auth card (login/register/change-password)
     8. Dashboard container / hello card
     9. Modal (fully generic - reusable on any future site as-is)
    10. Small utilities
    11. Field grid (index field list)
    12. Field map (fieldview site dots)
    13. Dropzone (admin_ui.php map upload)
    14. No-scroll page utility (reusable on any future full-screen page)
    15. Floating auto-hide topbar (reusable, opt-in via $topbarExtraClass)
    16. Full-screen map frame
    17. Site marker + hover label
    18. Map toolbar + orientation guard
    19. Site modal: compact info block, device accordion
    20. Code block (collapsible config/license viewer, vpn.php)
   ========================================================================= */


/* -------------------------------------------------------------------------
   1. THEME VARIABLES
   To reuse this stylesheet on another site, this is the only block you
   should need to edit - every rule below reads its colors from here.
   ------------------------------------------------------------------------- */
:root {
    /* Brand colors */
    /* --color-primary: #800020;          /* burgundy - buttons, links, accents */
    --color-primary: #ff0040;          /* burgundy - buttons, links, accents */
    --color-primary-hover: #9c0f31;
    --color-primary-contrast: #ffffff; /* text color placed on top of primary */

    /* Map site-dot size - adjustable at runtime via fieldview.php's
       Configurations modal, which sets this as an inline style on <html>
       and remembers it in localStorage. */
    --site-dot-size: 16px;

    /* Backgrounds */
    --color-bg: #2b2b2b;               /* dark gray - main page background */
    --color-bg-grid-line: #3d3d3d;     /* light-gray-on-dark grid accent lines */
    --color-surface: #363636;          /* cards, topbar, modal surfaces */
    --color-surface-raised: #404040;   /* dropdowns, hovered rows */
    --color-border: #4a4a4a;

    /* Text */
    --color-text: #f2f2f2;
    --color-text-muted: #b3b3b3;

    /* Feedback colors */
    --color-danger: #c0392b;
    --color-success: #2e7d32;
    --color-info: #2f7fd1;             /* default/no-data-yet site & device dots */
    --color-cyan: #17c9c9;             /* setpoint-matches-remote-value indicator */

    /* Sizing */
    --grid-size: 75px;                 /* the 75x75 accent grid */
    --radius: 8px;
    --topbar-height: 64px;
    --shadow-elevated: 0 10px 30px rgba(0, 0, 0, 0.45);

    --font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}


/* -------------------------------------------------------------------------
   2. BASE / RESET
   ------------------------------------------------------------------------- */
* {
    box-sizing: border-box;
}

html, body {
    height: 100%;
}

body {
    margin: 0;
    font-family: var(--font-family);
    color: var(--color-text);
    background-color: var(--color-bg);
}

a {
    color: var(--color-primary);
    text-decoration: none;
}

a:hover {
    text-decoration: underline;
}

h1, h2, h3, p {
    margin: 0 0 0.5em 0;
}


/* -------------------------------------------------------------------------
   3. LAYOUT HELPERS
   ------------------------------------------------------------------------- */

/* Full-page dark-gray background accented with a light-gray 75x75px grid.
   Apply to <body> or a full-height wrapper div. */
.bg-grid {
    background-color: var(--color-bg);
    background-image:
        linear-gradient(to right, var(--color-bg-grid-line) 1px, transparent 1px),
        linear-gradient(to bottom, var(--color-bg-grid-line) 1px, transparent 1px);
    background-size: var(--grid-size) var(--grid-size);
    min-height: 100vh;
}

.page-content {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    min-height: calc(100vh - var(--topbar-height));
    padding: 2rem 1rem;
}


/* -------------------------------------------------------------------------
   4. TOPBAR
   Left slot: site title. Right slot: user badge + dropdown trigger.
   Markup shape:
     <header class="topbar">
       <div class="topbar__title">DataSense<span class="topbar__tm">TM</span></div>
       <div class="topbar__right"> ... </div>
     </header>
   ------------------------------------------------------------------------- */
.topbar {
    height: var(--topbar-height);
    background-color: var(--color-surface);
    border-bottom: 1px solid var(--color-border);
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 1.5rem;
    position: sticky;
    top: 0;
    z-index: 100;
}

.topbar__title {
    font-size: 1.25rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    color: var(--color-primary);
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* Precise Downhole Solutions' square mark (no wordmark) - links out to
   precisedhs.com. Sized to sit comfortably inside the fixed-height
   topbar next to the DataSense wordmark, not to dominate it. */
.topbar__logo {
    height: 22px;
    width: 22px;
    border-radius: 4px;
    display: block;
    flex-shrink: 0;
}

.topbar__tm {
    font-size: 0.6em;
    vertical-align: super;
    color: var(--color-text-muted);
    margin-left: 2px;
}

.topbar__right {
    display: flex;
    align-items: center;
    gap: 0.75rem;
}

/* The user's name, colored per their access level via an inline
   style="color: ..." set from level_to_color() in PHP. */
.user-badge {
    font-weight: 600;
    cursor: pointer;
    padding: 0.35rem 0.6rem;
    border-radius: var(--radius);
    transition: background-color 0.15s ease;
}

.user-badge:hover {
    background-color: var(--color-surface-raised);
    text-decoration: none;
}


/* -------------------------------------------------------------------------
   5. DROPDOWN MENU
   Markup shape:
     <div class="dropdown">
       <button class="dropdown__trigger"> ... </button>
       <div class="dropdown__menu"> <a>...</a> <a>...</a> </div>
     </div>
   Toggled by adding/removing the "is-open" class via JS.
   ------------------------------------------------------------------------- */
.dropdown {
    position: relative;
}

.dropdown__trigger {
    background: none;
    border: none;
    color: var(--color-text);
    font: inherit;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.35rem 0.6rem;
    border-radius: var(--radius);
}

.dropdown__trigger:hover {
    background-color: var(--color-surface-raised);
}

.dropdown__menu {
    position: absolute;
    right: 0;
    top: calc(100% + 6px);
    min-width: 190px;
    background-color: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-elevated);
    padding: 0.4rem;
    display: none;
    flex-direction: column;
}

.dropdown.is-open .dropdown__menu {
    display: flex;
}

.dropdown__menu a {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    padding: 0.55rem 0.7rem;
    border-radius: calc(var(--radius) - 3px);
    color: var(--color-text);
    text-decoration: none;
}

.dropdown__menu a:hover {
    background-color: var(--color-primary);
    color: var(--color-primary-contrast);
}


/* -------------------------------------------------------------------------
   6. BUTTONS
   ------------------------------------------------------------------------- */
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 0.5rem;
    font: inherit;
    font-weight: 600;
    padding: 0.65rem 1.1rem;
    border-radius: var(--radius);
    border: 1px solid transparent;
    cursor: pointer;
    text-decoration: none;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.btn:hover {
    text-decoration: none;
}

.btn-primary {
    background-color: var(--color-primary);
    color: var(--color-primary-contrast);
}

.btn-primary:hover {
    background-color: var(--color-primary-hover);
}

.btn-outline {
    background-color: transparent;
    border-color: var(--color-border);
    color: var(--color-text);
}

.btn-outline:hover {
    border-color: var(--color-primary);
    color: var(--color-primary);
}

.btn-block {
    width: 100%;
}

/* A row of small toggle-style buttons (e.g. the history graph's
   1H/24H/7D/1M range picker) - tighter padding than a normal .btn, and
   .is-active marks whichever one is currently selected. */
.btn-group {
    display: inline-flex;
    gap: 0.35rem;
}
.btn-group .btn {
    padding: 0.35rem 0.65rem;
    font-size: 0.8rem;
}
.btn-group .btn.is-active {
    background-color: var(--color-primary);
    border-color: var(--color-primary);
    color: var(--color-primary-contrast);
}


/* -------------------------------------------------------------------------
   7. FORMS + AUTH CARD (login / register / change-password)
   ------------------------------------------------------------------------- */
.auth-card {
    width: 100%;
    max-width: 380px;
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-elevated);
    padding: 2rem;
}

.auth-card__icon {
    font-size: 2.25rem;
    color: var(--color-primary);
    text-align: center;
    display: block;
    margin-bottom: 0.75rem;
}

.auth-card h1 {
    text-align: center;
    font-size: 1.35rem;
    margin-bottom: 1.5rem;
}

.form-group {
    margin-bottom: 1.1rem;
}

.form-group label {
    display: block;
    font-size: 0.85rem;
    color: var(--color-text-muted);
    margin-bottom: 0.35rem;
}

/* Puts two or more .form-group children side by side on one line (e.g.
   Title + Address in the site edit form) instead of stacked - each child
   just needs a flex-basis (inline "flex" style) to control its share of
   the row. */
.form-row {
    display: flex;
    gap: 1rem;
    align-items: flex-start;
}
.form-row > .form-group {
    flex: 1;
    min-width: 0; /* lets a long value shrink instead of overflowing the row */
}

/* Tighter vertical rhythm for a form packed into a modal that shouldn't
   scroll (e.g. the site edit modal) - same fields, less dead space. */
.form--compact .form-group {
    margin-bottom: 0.6rem;
}

.form-control {
    width: 100%;
    background-color: var(--color-bg);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    color: var(--color-text);
    padding: 0.6rem 0.75rem;
    font: inherit;
}

.form-control:focus {
    outline: none;
    border-color: var(--color-primary);
}

.auth-card__footer {
    text-align: center;
    margin-top: 1.25rem;
    font-size: 0.9rem;
    color: var(--color-text-muted);
}

.alert {
    border-radius: var(--radius);
    padding: 0.7rem 0.9rem;
    font-size: 0.9rem;
    margin-bottom: 1.1rem;
}

.alert-danger {
    background-color: rgba(192, 57, 43, 0.15);
    border: 1px solid var(--color-danger);
    color: #ff8a80;
}

.alert-success {
    background-color: rgba(46, 125, 50, 0.15);
    border: 1px solid var(--color-success);
    color: #81c784;
}


/* -------------------------------------------------------------------------
   8. DASHBOARD CONTAINER / HELLO CARD
   ------------------------------------------------------------------------- */
.hello-card {
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-top: 4px solid var(--color-primary);
    border-radius: var(--radius);
    box-shadow: var(--shadow-elevated);
    padding: 2.5rem 3rem;
    text-align: center;
    max-width: 520px;
}

.hello-card h1 {
    font-size: 1.5rem;
}

.hello-card p {
    color: var(--color-text-muted);
}


/* =========================================================================
   9. MODAL COMPONENT (generic + reusable)
   =========================================================================
   This block is self-contained and copy/paste friendly for any future
   site: it only depends on the theme variables in section 1.

   Markup shape:
     <div class="modal-overlay" id="my-modal">
       <div class="modal" role="dialog" aria-modal="true">
         <div class="modal__header">
           <h2 class="modal__title">Title</h2>
           <button class="modal__close" data-modal-close>&times;</button>
         </div>
         <div class="modal__body"> ... content ... </div>
       </div>
     </div>

   Behavior (see js in the pages that use it):
     - Add class "is-open" to .modal-overlay to show it.
     - Clicking the overlay itself, the [data-modal-close] button, or
       pressing Escape should remove "is-open" again.
     - .level-display is a specific helper for showing a big number
       (e.g. the user's access level) inside a modal body.
   ========================================================================= */
.modal-overlay {
    display: none;
    position: fixed;
    inset: 0;
    background-color: rgba(0, 0, 0, 0.6);
    align-items: center;
    justify-content: center;
    z-index: 1000;
    padding: 1rem;
}

.modal-overlay.is-open {
    display: flex;
}

.modal {
    width: 100%;
    max-width: 420px;
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    box-shadow: var(--shadow-elevated);
    overflow: hidden;
    max-height: 85vh;
    overflow-y: auto;
}

/* Optional modifier for a modal with more content (e.g. a device list with
   edit forms) than the default 420px comfortably fits. width:auto (not a
   fixed px/vw) so it shrink-wraps to whatever's actually inside - a
   collapsed device accordion stays narrow, but expanding one to reveal
   its wide datapoint table grows the modal to fit that table instead of
   squeezing it into a fixed 640px (the old behavior: max-width was also
   the effective width, since the base .modal's width:100% always filled
   it). Same shrink-to-fit technique as .modal--maximized below, just
   without that modifier's height/overflow handling. */
.modal--wide {
    width: auto;
    max-width: 95vw;
}

/* Optional modifier for a modal whose main content is an image that
   should dominate the screen (a site's diagram) - takes up most of the
   viewport but deliberately stops short of 100%, so the dimmed
   .modal-overlay backdrop (and whatever's behind it) stays a little
   visible around the edges rather than fully hiding it. */
.modal--maximized {
    /* width:auto (not a fixed vw) so the modal shrink-wraps to its
       content's actual rendered width (the image, via .map-container's
       own inline-block shrink-wrap) - .modal-overlay's flex centering
       sizes a flex item to its content by default, capped by max-width
       so it never exceeds the viewport. overflow:hidden (overriding the
       base .modal's overflow-y:auto) because the JS-computed image
       height (below) is sized to always fit without scrolling - if that
       math is ever off, this hides the overflow instead of showing a
       scrollbar, since a slightly-clipped image reads better than one
       with a scrollbar for something meant to fit as one screen. */
    width: auto;
    max-width: 95vw;
    max-height: 92vh;
    overflow: hidden;
}

.modal__header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 1rem 1.25rem;
    border-bottom: 1px solid var(--color-border);
}

.modal__title {
    font-size: 1.05rem;
    font-weight: 700;
}

.modal__close {
    background: none;
    border: none;
    color: var(--color-text-muted);
    font-size: 1.4rem;
    line-height: 1;
    cursor: pointer;
}

.modal__close:hover {
    color: var(--color-text);
}

.modal__body {
    padding: 1.75rem 1.25rem 2.25rem;
    text-align: center;
}

/* Large centered number used to display the user's access level. Color is
   set inline per-user via level_to_color() so it matches their badge. */
.level-display {
    font-size: 4.5rem;
    font-weight: 800;
    line-height: 1;
}

.level-display + p {
    margin-top: 0.75rem;
    color: var(--color-text-muted);
}


/* -------------------------------------------------------------------------
   10. UTILITIES
   ------------------------------------------------------------------------- */
.text-muted { color: var(--color-text-muted); }
.text-center { text-align: center; }
.mt-1 { margin-top: 0.5rem; }
.mt-2 { margin-top: 1rem; }

/* index.php's compact "Signed in as X" line - replaces what used to be a
   full .hello-card (large heading + access-level sentence), redundant
   with the topbar's own username badge and level modal. */
.index-greeting {
    font-size: 0.8rem;
    text-align: center;
    width: 100%;
    margin: 0;
}


/* -------------------------------------------------------------------------
   11. FIELD GRID (index field list)
   3-columns-wide, wraps to as many rows as needed, collapses to fewer
   columns on narrow viewports. Each card is a clickable <a> linking to
   fieldview.php?field=ID.
   Markup shape:
     <div class="field-grid">
       <a class="field-card" href="/fieldview.php?field=1">
         <h3 class="field-card__title">Name</h3>
         <p class="field-card__desc">Description</p>
       </a>
       ...
     </div>
   ------------------------------------------------------------------------- */
.field-grid {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 1.25rem;
    width: 100%;
    max-width: 900px;
}

@media (max-width: 720px) {
    .field-grid {
        grid-template-columns: repeat(2, 1fr);
    }
}

@media (max-width: 480px) {
    .field-grid {
        grid-template-columns: 1fr;
    }
}

.field-card {
    display: block;
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    border-top: 4px solid var(--color-primary);
    border-radius: var(--radius);
    padding: 1.25rem;
    color: var(--color-text);
    text-decoration: none;
    transition: transform 0.15s ease, box-shadow 0.15s ease;
    /* Lets .field-card__thumb bleed to the card's full width (its
       negative margins below cancel this padding) while still clipping
       to the card's own rounded corners, instead of the image's square
       corners poking out past them. */
    overflow: hidden;
}

.field-card:hover {
    text-decoration: none;
    transform: translateY(-2px);
    box-shadow: var(--shadow-elevated);
}

/* Bleeds out to the card's full width/top (negative margins cancel
   .field-card's 1.25rem padding) so it reads as a banner thumbnail, not
   a small inset image. Fixed height + object-fit:cover keeps every card
   in a grid the same height regardless of each map's own aspect ratio. */
.field-card__thumb {
    display: block;
    width: calc(100% + 2.5rem);
    height: 110px;
    margin: -1.25rem -1.25rem 0.75rem -1.25rem;
    object-fit: cover;
    border-bottom: 1px solid var(--color-border);
    background-color: var(--color-surface-raised);
}

.field-card__title {
    font-size: 1.05rem;
    font-weight: 700;
    margin: 0 0 0.4rem 0;
}

.field-card__desc {
    font-size: 0.85rem;
    color: var(--color-text-muted);
    margin: 0;
    display: -webkit-box;
    -webkit-line-clamp: 3;
    -webkit-box-orient: vertical;
    overflow: hidden;
}


/* -------------------------------------------------------------------------
   12. SITE MAP (a site's own diagram, inside its modal - see section 19)
   Tight-fit, not a fixed box: .map-container is display:inline-block, so
   it shrink-wraps to exactly whatever size the image itself renders at -
   there is no separate background/letterboxing visible around it, and
   (unlike an object-fit:contain approach) percentage-positioned markers
   always line up with the actual visible image, never drifting off it.
   The image is sized directly: height is viewport-relative and large
   (image dominates the modal), width is auto so the aspect ratio is
   preserved, capped by max-width so a very wide image still fits the
   modal (shrinking below the target height in that case). Center the
   container in its parent via text-align:center on that parent (e.g.
   .modal__body), since inline-block content follows text-align.
   Markup shape:
     <div class="map-container">
       <img class="map-image" src="..." alt="Site map">
       <span class="site-dot site-dot--green" style="left: 30%; top: 62%;" title="Site name"></span>
       ...
     </div>
   ------------------------------------------------------------------------- */
.map-container {
    position: relative;
    display: inline-block;
    max-width: 100%;
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    overflow: hidden;
    line-height: 0;
}

.map-image {
    display: block;
    height: auto;
    width: auto;
    max-width: 100%;
    /* max-height is set inline by JS (fieldview.php) once the modal is
       open and its header's real rendered height is known - a pure-CSS
       vh value can't account for that, and a wrong guess either leaves
       dead space or overflows into a scrollbar. */
    -webkit-user-drag: none;
    user-select: none;
}

.site-dot {
    position: absolute;
    width: var(--site-dot-size);
    height: var(--site-dot-size);
    margin-left: calc(var(--site-dot-size) / -2);
    margin-top: calc(var(--site-dot-size) / -2);
    border-radius: 50%;
    border: 2px solid rgba(0, 0, 0, 0.35);
    box-shadow: 0 0 0 3px rgba(0, 0, 0, 0.25);
    cursor: pointer;
}

.site-dot--blue {
    background-color: var(--color-info);
}

.site-dot--green {
    background-color: var(--color-success);
}

.site-dot--red {
    background-color: var(--color-danger);
    animation: site-dot-pulse 1.6s ease-in-out infinite;
}

/* A datapoint whose latest reading is status "fault" - a bad/malfunctioning
   sensor, distinct from a valid-but-alarming "alert" reading (still red)
   and distinct from a relay's real OFF state (also not this) - shown gray
   with "XXX" text (marker_value_text()) instead of a number or ON/OFF. */
.site-dot--gray {
    background-color: var(--color-text-muted);
}

/* Setpoint match indicator only (not part of the site/device status
   scheme above) - cyan when a datapoint's reported value matches its
   configured setpoint, red when it doesn't. */
.site-dot--cyan {
    background-color: var(--color-cyan);
}

@keyframes site-dot-pulse {
    0%, 100% { box-shadow: 0 0 0 3px rgba(192, 57, 43, 0.35); }
    50% { box-shadow: 0 0 0 7px rgba(192, 57, 43, 0.15); }
}


/* -------------------------------------------------------------------------
   13. DROPZONE (admin_ui.php map upload)
   A click-or-drag file target. Pair with a hidden <input type="file">
   inside it - clicking the zone forwards to the input, and dropping a
   file assigns it to the input's .files so a normal form submit handles
   the actual upload. See the inline <script> in admin_ui.php.
   ------------------------------------------------------------------------- */
.dropzone {
    border: 2px dashed var(--color-border);
    border-radius: var(--radius);
    padding: 1.75rem 1rem;
    text-align: center;
    color: var(--color-text-muted);
    cursor: pointer;
    transition: border-color 0.15s ease, background-color 0.15s ease;
}

.dropzone:hover,
.dropzone.is-dragover {
    border-color: var(--color-primary);
    background-color: var(--color-surface-raised);
}


/* -------------------------------------------------------------------------
   14. NO-SCROLL PAGE UTILITY (reusable on any future full-screen page)
   Add class="no-scroll-page" to <html> (not <body>) to disable both
   vertical and horizontal page scrolling entirely - for a kiosk-style
   full-viewport page like fieldview.php's map, where scrolling would
   fight with drag-to-reposition gestures on the content itself.
   ------------------------------------------------------------------------- */
html.no-scroll-page,
html.no-scroll-page body {
    overflow: hidden;
    height: 100%;
    margin: 0;
}


/* -------------------------------------------------------------------------
   15. FLOATING AUTO-HIDE TOPBAR (reusable, opt-in via $topbarExtraClass)
   Wrap includes/topbar.php in a .topbar-reveal-zone, and set
   $topbarExtraClass = 'topbar--floating' before including it. The zone is
   a fixed strip pinned to the exact height of the topbar - hovering
   anywhere in that band slides the topbar down into view; moving away
   slides it back up out of sight. Sized to match the topbar exactly so
   there's no dead zone or flicker between "hovering the trigger" and
   "hovering the now-visible topbar".
   Markup shape:
     <div class="topbar-reveal-zone">
       <?php $topbarExtraClass = 'topbar--floating'; include 'includes/topbar.php'; ?>
     </div>
   ------------------------------------------------------------------------- */
.topbar-reveal-zone {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: var(--topbar-height);
    z-index: 300;
}

.topbar--floating {
    position: static;
    transform: translateY(-100%);
    transition: transform 0.25s ease;
    box-shadow: var(--shadow-elevated);
}

.topbar-reveal-zone:hover .topbar--floating,
.topbar-reveal-zone:focus-within .topbar--floating {
    transform: translateY(0);
}


/* -------------------------------------------------------------------------
   16. FULL-SCREEN MAP FRAME
   .map-padding creates the inset "padding around it" look; .map-frame is
   sized and positioned by JS (fieldview.php) to exactly match the map
   image's rendered aspect ratio within that padded area, so percentage-
   based site marker positions line up with the actual visible image
   instead of any letterboxing. The rounded corners + border live on
   .map-frame so they hug the image tightly, not the blank padded area.
   ------------------------------------------------------------------------- */
.map-fullscreen-container {
    position: fixed;
    inset: 0;
    z-index: 1;
}

.map-padding {
    position: absolute;
    inset: 2rem;
}

.map-frame {
    position: absolute;
    border-radius: calc(var(--radius) * 2);
    overflow: hidden;
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    box-shadow: var(--shadow-elevated);
}

.map-frame .map-image {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
}


/* -------------------------------------------------------------------------
   17. SITE MARKER + HOVER LABEL
   Replaces the plain .site-dot positioning from section 12 with a
   wrapper: .site-marker is the absolutely-positioned, draggable unit
   (left/top set as a percentage of .map-frame); .site-dot inside it goes
   back to being an ordinary inline element; .site-dot-label is the
   title that fades in on hover (or while actively being dragged).
   ------------------------------------------------------------------------- */
.site-marker {
    position: absolute;
    transform: translate(-50%, -50%);
    cursor: pointer;
    touch-action: none;
    -webkit-user-drag: none;
    user-select: none;
}

.site-marker .site-dot {
    /* .site-dot is a <span> (inline by default). Its base rule's
       width/height only take effect on a block-level or inline-block
       box - inline elements silently ignore explicit sizing, which
       squashes the circle into a sliver. display:inline-block restores
       it as a clean 16x16 circle now that position is static instead of
       the base rule's absolute (which auto-computes to block on its
       own). */
    display: inline-block;
    position: static;
    margin: 0;
}

.site-marker.is-draggable {
    cursor: grab;
}

.site-marker.is-dragging {
    cursor: grabbing;
    z-index: 50;
}

.site-dot-label {
    position: absolute;
    top: 50%;
    left: 100%;
    margin-left: 10px;
    transform: translateY(-50%);
    padding: 0.25rem 0.6rem;
    background-color: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    font-size: 0.8rem;
    white-space: nowrap;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.15s ease;
}

.site-marker:hover .site-dot-label,
.site-marker.is-dragging .site-dot-label {
    opacity: 1;
}

/* Datapoint/sensor markers on a site's own map (as opposed to site
   markers on the field map) show their label - the latest value and
   units - always, positioned under the dot rather than beside it. */
.site-marker--pinned .site-dot-label {
    opacity: 1;
    top: 100%;
    left: 50%;
    margin-left: 0;
    margin-top: 6px;
    transform: translateX(-50%);
}


/* -------------------------------------------------------------------------
   18. MAP TOOLBAR + ORIENTATION GUARD
   .map-toolbar holds the always-visible escape hatches (return to index,
   lock toggle) that stay on screen even while the floating topbar is
   hidden. .orientation-guard is a full-screen takeover shown only in
   CSS portrait orientation, telling the visitor to rotate their device.
   ------------------------------------------------------------------------- */
.map-toolbar {
    position: fixed;
    bottom: 1rem;
    right: 1rem;
    z-index: 250;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.map-toolbar .btn-icon {
    width: 42px;
    height: 42px;
    border-radius: 50%;
    display: flex;
    align-items: center;
    justify-content: center;
    background-color: var(--color-surface);
    border: 1px solid var(--color-border);
    color: var(--color-text);
    cursor: pointer;
    text-decoration: none;
    font-size: 1.1rem;
    box-shadow: var(--shadow-elevated);
    transition: border-color 0.15s ease, color 0.15s ease;
}

.map-toolbar .btn-icon:hover {
    border-color: var(--color-primary);
    color: var(--color-primary);
    text-decoration: none;
}

.map-toolbar .btn-icon.is-locked {
    color: var(--color-danger);
    border-color: var(--color-danger);
}

.map-toolbar .btn-icon.is-unlocked {
    color: var(--color-success);
    border-color: var(--color-success);
}

.orientation-guard {
    display: none;
    position: fixed;
    inset: 0;
    z-index: 500;
    background-color: var(--color-bg);
    flex-direction: column;
    align-items: center;
    justify-content: center;
    text-align: center;
    padding: 2rem;
}

.orientation-guard i {
    font-size: 3rem;
    color: var(--color-primary);
    margin-bottom: 1rem;
}

@media (orientation: portrait) {
    .orientation-guard {
        display: flex;
    }
    .map-fullscreen-container,
    .map-toolbar {
        display: none;
    }
}


/* -------------------------------------------------------------------------
   19. SITE MODAL: structured header, device accordion
   Markup shape (site view modal only):
     <div class="modal__header site-modal-header">
       <div class="site-modal-header__text">
         <h2 class="modal__title">Title</h2>
         <div class="site-modal-header__meta">
           <span class="site-modal-header__location"><i class="fa-solid fa-location-dot"></i> Address</span>
           <span class="site-modal-header__description">Description</span>
         </div>
       </div>
       <div class="site-modal-header__actions"> ...edit/delete/close icons... </div>
     </div>
   .modal__header's own border-bottom (section 9) is the horizontal line
   separating this from the map underneath - no separate <hr> needed.
   ------------------------------------------------------------------------- */
.site-modal-header {
    align-items: stretch;
    padding-top: 0.6rem;
    padding-bottom: 0.6rem;
}

.site-modal-header .modal__title {
    margin: 0;
}

.site-modal-header__text {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
    min-width: 0;
}

.site-modal-header__meta {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.35rem 0.9rem;
    margin-top: 0.15rem;
    font-size: 0.85rem;
}

.site-modal-header__location {
    color: var(--color-text-muted);
    white-space: nowrap;
}

.site-modal-header__description {
    color: var(--color-text);
}

.site-modal-header__actions {
    display: flex;
    align-items: flex-start;
    gap: 0.5rem;
    flex-shrink: 0;
}

/* Native <details>/<summary> accordion for the device list inside a
   site's edit modal - no JS needed for expand/collapse. */
.device-accordion {
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    margin-bottom: 0.6rem;
    overflow: hidden;
}

.device-accordion summary {
    padding: 0.6rem 0.9rem;
    cursor: pointer;
    background-color: var(--color-surface-raised);
    list-style: none;
}

.device-accordion summary::-webkit-details-marker {
    display: none;
}

.device-accordion summary::before {
    content: '\25B8';
    display: inline-block;
    width: 1em;
    color: var(--color-text-muted);
    transition: transform 0.15s ease;
}

.device-accordion[open] summary::before {
    transform: rotate(90deg);
}

.device-accordion .hello-card {
    border-radius: 0;
    border-left: none;
    border-right: none;
    border-bottom: none;
    margin-top: 0 !important;
}


/* -------------------------------------------------------------------------
   20. CODE BLOCK (collapsible config/license viewer, vpn.php)
   Markup shape:
     <div class="code-block">
       <div class="code-block__actions"> ...buttons... </div>
       <details>
         <summary>Show configuration</summary>
         <pre><code> ... </code></pre>
       </details>
     </div>
   The actions bar is a sibling of <details>, not a child of it - native
   <details> hides every child except <summary> when collapsed, so
   buttons that must stay visible collapsed or expanded (a copy button,
   a download link) can't live inside it. .code-block is the positioning
   context both are pinned to instead.
   ------------------------------------------------------------------------- */
.code-block {
    position: relative;
    margin-top: 0.5rem;
}

.code-block__actions {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    display: flex;
    gap: 0.4rem;
    z-index: 1;
}

.code-block__btn {
    padding: 0.35rem 0.55rem;
    line-height: 1;
}

.code-block summary {
    padding: 0.6rem 0.9rem;
    padding-right: 5rem; /* keep the summary's own text clear of the pinned buttons */
    cursor: pointer;
    background-color: var(--color-surface-raised);
    border: 1px solid var(--color-border);
    border-radius: var(--radius);
    list-style: none;
    color: var(--color-text-muted);
    font-size: 0.85rem;
}

.code-block summary::-webkit-details-marker {
    display: none;
}

.code-block details[open] summary {
    border-bottom-left-radius: 0;
    border-bottom-right-radius: 0;
}

.code-block pre {
    margin: 0;
    padding: 0.9rem;
    background-color: var(--color-bg);
    border: 1px solid var(--color-border);
    border-top: none;
    border-radius: 0 0 var(--radius) var(--radius);
    overflow-x: auto;
    max-height: 50vh;
    overflow-y: auto;
}

.code-block code {
    font-family: ui-monospace, SFMono-Regular, Consolas, monospace;
    font-size: 0.8rem;
    white-space: pre;
    color: var(--color-text);
}
