/* ===========================================================================
   Application shell and screens
   ===========================================================================

   Everything here is placement: the header, the map screen's regions, the
   overlays over the canvas, the side panel, the list screens and the sign-in
   card. What things look like — buttons, fields, badges, tables, cards — comes
   from diia.css, which is loaded first and holds the design tokens this file
   spends. A colour, a radius or a type size written literally below is a bug;
   it belongs in the token block.

   Breakpoints per assumption A-19: 1920 / 1366 / 1024 / 360. The application is
   installed and used as a PWA on a phone (ADR-0012), so the narrow end of that
   range is a first-class target rather than a courtesy: below 640 px the
   navigation becomes a fixed bar of tabs across the bottom, the map screen keeps
   the full viewport with the list below it, and every inset the hardware imposes
   is paid back with env(safe-area-inset-*).
   ------------------------------------------------------------------------- */

:root {
    /* Height of the bottom navigation, defined once because three other things
       have to keep clear of it: the map screen's own height, the scrolling
       content of a list screen, and the htmx indicator. Zero above the phone
       breakpoint, where the navigation is in the header. */
    --nav-height: 0px;
}

/* ---------------------------------------------------------------------------
   Header
   --------------------------------------------------------------------------- */

.app-header {
    display: flex;
    align-items: center;
    gap: var(--sp-5);
    /* The top inset is the notch's, and it is zero in a browser window. An
       installed app paints under the status bar (viewport-fit=cover in the
       layout), so without this the brand sits behind the clock. */
    padding: calc(var(--sp-3) + env(safe-area-inset-top)) var(--sp-5) var(--sp-3);
    background: var(--c-white);
    border-bottom: 1px solid var(--c-border);
}

.brand { display: inline-flex; align-items: center; gap: var(--sp-3); color: var(--c-fg); text-decoration: none; }
.brand:hover { color: var(--c-fg); text-decoration: none; }

/* The mark is a glyph in a black tile — the Diia wordmark shape, carrying the
   application's own letter rather than the state brand's. */
.brand__mark {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.25rem;
    height: 2.25rem;
    border-radius: var(--radius-md);
    background: var(--c-black);
    color: var(--c-white);
    font-family: var(--font-head);
    font-size: var(--text-lg);
    font-weight: 700;
    line-height: 1;
}

.brand__name { display: block; font-family: var(--font-head); font-size: var(--text-base); font-weight: 700; line-height: 1.2; }
.brand__sub { display: block; font-size: var(--text-xs); color: var(--c-muted); }

.app-header nav { display: flex; align-items: center; gap: var(--sp-1); }

/* Navigation is a row of pills, and the current screen is the filled one. The
   state comes from the server (the layout compares the route), so it survives a
   boosted navigation without a script keeping it in step. */
.app-header nav a {
    padding: 0.4375rem 0.875rem;
    border-radius: var(--radius-pill);
    font-size: var(--text-md);
    font-weight: 500;
    color: var(--c-muted);
    text-decoration: none;
    transition: background var(--transition), color var(--transition);
}

.app-header nav a:hover { background: var(--c-surface); color: var(--c-fg); text-decoration: none; }
.app-header nav a.is-active { background: var(--c-black); color: var(--c-white); }

/* Each link carries a full label and a short one; the short one is for the phone
   tab bar, where "Обʼєкти моніторингу" does not fit. Exactly one is displayed,
   so a screen reader announces one name too. */
.nav__short { display: none; }

/* A destination that does not exist yet. Rendered rather than hidden because
   the operator was promised the section by the ToR and its absence is
   information; `aria-disabled` plus no href keeps it out of the tab order. */
.app-header nav .is-disabled { color: var(--c-border-strong); cursor: default; }
.app-header nav .is-disabled:hover { background: none; color: var(--c-border-strong); }

.app-user { margin-left: auto; display: flex; align-items: center; gap: var(--sp-3); }
.app-user form { margin: 0; }

/* Signed-in operator: initials, name, and the sign-out control beside them. */
.user-chip { display: flex; align-items: center; gap: var(--sp-2); }
.user-chip__name { font-size: var(--text-sm); font-weight: 500; }

@media (max-width: 1024px) {
    .app-header { gap: var(--sp-4); padding: calc(var(--sp-3) + env(safe-area-inset-top)) var(--sp-4) var(--sp-3); }
    .brand__sub { display: none; }
    .app-header nav a { padding: 0.375rem 0.625rem; font-size: var(--text-sm); }
    .user-chip__name { display: none; }
}

/* --- Phone: the navigation moves to the bottom -----------------------------

   A row of destinations along the top of a phone screen is a row nobody can
   reach one-handed, and on the map screen it competes with the operating
   system's own status bar. The same anchors are laid out as tabs across the
   bottom instead — no second markup, no script, and the active tab is still the
   one the server marked.

   body.has-nav is set by the layout when an operator is signed in, which is
   exactly when the bar exists. The alternative, :has(), decides the same thing in
   the browser and silently drops the whole rule where it is unsupported — which
   would leave the content of every screen hidden behind the bar. */
@media (max-width: 640px) {
    :root { --nav-height: calc(3.5rem + env(safe-area-inset-bottom)); }

    .app-header { gap: var(--sp-3); padding: calc(var(--sp-2) + env(safe-area-inset-top)) var(--sp-3) var(--sp-2); }
    .brand__mark { width: 2rem; height: 2rem; font-size: var(--text-base); }
    .brand__name { font-size: var(--text-sm); }

    .app-header nav {
        position: fixed;
        left: 0;
        right: 0;
        bottom: 0;
        z-index: 50;
        gap: 0;
        justify-content: space-around;
        padding: var(--sp-1) var(--sp-1) calc(var(--sp-1) + env(safe-area-inset-bottom));
        background: var(--c-white);
        border-top: 1px solid var(--c-border);
    }

    .app-header nav a {
        flex: 1 1 0;
        min-width: 0;
        text-align: center;
        padding: var(--sp-2) var(--sp-1);
        border-radius: var(--radius-sm);
        font-size: var(--text-xs);
        /* A tab that has run out of room truncates rather than wrapping to a
           second line — a taller bar would move the whole map. */
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    .nav__long { display: none; }
    .nav__short { display: inline; }

    /* Scrolling content keeps clear of the bar; the map screen, which does not
       scroll, is shortened by the same amount instead (see below). */
    body.has-nav:not(.page-map) { padding-bottom: var(--nav-height); }
}

/* ---------------------------------------------------------------------------
   Map screen

   The map screen owns the viewport: the header takes what it needs and the map
   fills the remainder, with nothing scrolling. Deliberately no hard-coded header
   height — any such constant goes stale the moment the header gains a line.
   Scoped to body.page-map so the list screens keep document scrolling.
   --------------------------------------------------------------------------- */

body.page-map {
    height: 100%;
    overflow: hidden;
    display: flex;
    flex-direction: column;
    /* The bottom navigation is fixed and out of flow, so the screen has to give
       up its height rather than scroll under it. Zero except on a phone. */
    padding-bottom: var(--nav-height);
    /* Stops the browser's pull-to-refresh from firing on a downward pan of the
       map — which reloads the page mid-gesture and looks like a crash. */
    overscroll-behavior-y: contain;
}

body.page-map .app-header { flex: 0 0 auto; }
body.page-map #content { flex: 1 1 auto; min-height: 0; }

.map-layout { display: grid; grid-template-columns: 1fr auto; height: 100%; background: var(--c-surface); }
.map-wrapper { position: relative; min-width: 0; }
.map-canvas { width: 100%; height: 100%; }

.map-sidebar {
    width: 400px;
    border-left: 1px solid var(--c-border);
    background: var(--c-surface);
    overflow-y: auto;
    padding: var(--sp-4);
}

/* The panel is filled by htmx after load. :empty collapses the column so an
   unfilled panel does not reserve 400 px of map while the fragment is in flight —
   which is whitespace-sensitive, so the <aside> in Views/Home/Index.cshtml is
   written as an immediately closed tag pair with nothing between. */
.map-sidebar:empty { display: none; }

/* --- Overlays over the canvas ---------------------------------------------

   Three cards float over the map: statistics top-left, the route panel
   bottom-left, the legend bottom-right. They share one surface treatment, and
   only their placement differs. */

.map-card {
    position: absolute;
    z-index: 5;
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--radius-md);
    background: rgba(255, 255, 255, 0.94);
    backdrop-filter: blur(6px);
    box-shadow: var(--shadow-lg);
    font-size: var(--text-sm);
}

.map-card__title {
    display: flex;
    align-items: center;
    gap: var(--sp-2);
    margin-bottom: var(--sp-2);
    font-family: var(--font-head);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--c-muted);
}

/* Live indicator beside the statistics heading: the stream is a thing with a
   state, and a panel of numbers alone cannot say whether they are still
   arriving. */
.map-card__pulse {
    width: 0.5rem;
    height: 0.5rem;
    border-radius: 50%;
    background: var(--c-success);
    animation: pulse 2s ease-in-out infinite;
}

@keyframes pulse {
    0%, 100% { opacity: 1; }
    50% { opacity: 0.3; }
}

@media (prefers-reduced-motion: reduce) {
    .map-card__pulse { animation: none; }
}

/* --- Statistics panel ------------------------------------------------------

   Rewritten wholesale by StatsPanel on every tick, so the markup below is only
   the placeholder the server renders. Class names are the contract with
   ClientApp/map/stats.ts — renaming one here silently unstyles the panel. */

.map-stats { min-width: 244px; font-variant-numeric: tabular-nums; }
.map-stats .stat { display: flex; justify-content: space-between; gap: var(--sp-4); padding: 0.1875rem 0; }
.map-stats .stat span { color: var(--c-muted); }
.map-stats .stat b { color: var(--c-fg); font-weight: 600; }

/* Separates the zone counts from the ingest figures above them: the two answer
   different questions, and an unbroken column of six rows reads as one list. */
.map-stats .stat-group {
    margin-top: var(--sp-3);
    padding-top: var(--sp-2);
    border-top: 1px solid var(--c-border);
    font-size: var(--text-xs);
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--c-muted);
}

/* Zone rows are buttons that fly the map to their area, so they have to look
   pressable while still lining up with the read-only rows above them. */
.map-stats .zone-row {
    border: none;
    background: none;
    font: inherit;
    text-align: left;
    cursor: pointer;
    border-radius: var(--radius-sm);
    /* The negative margin cancels the padding, so the hover background extends
       past the text while the text itself stays flush with the rows above. */
    margin: 0 calc(var(--sp-2) * -1);
    padding: 0.1875rem var(--sp-2);
    width: calc(100% + var(--sp-4));
}

.map-stats .zone-row:hover { background: var(--c-surface); }
.map-stats .zone-swatch { width: 0.5625rem; height: 0.5625rem; margin-right: var(--sp-2); }

/* --- Legend ---------------------------------------------------------------

   States what the two icon colours mean. The fills are the ones
   ClientApp/map/icons.ts draws — the pair has to be changed together. */

.map-legend { right: 12px; bottom: 28px; }
.map-legend .legend-row { display: flex; align-items: center; gap: var(--sp-2); padding: 0.125rem 0; color: var(--c-muted); }

.legend-mark {
    width: 0.625rem;
    height: 0.625rem;
    border-radius: 2px;
    flex: 0 0 auto;
}

.legend-mark--moving { background: #2f9e44; }
.legend-mark--stopped { background: #f0a500; }
.legend-mark--track { background: #2f9e44; height: 0.1875rem; width: 1rem; border-radius: var(--radius-pill); }

/* --- Route panel ----------------------------------------------------------

   Bottom-left rather than under the statistics panel: the track is fitted into
   the viewport when it loads, and a panel in the top-left corner would sit over
   the part of it the map has just centred. Contents are rendered by
   ClientApp/map/track.ts — the class names below are that module's contract. */

.map-stats-card { top: 12px; left: 12px; }
.map-track { left: 12px; bottom: 28px; max-width: 330px; }
.map-track[hidden] { display: none; }

.map-track-head { display: flex; align-items: center; justify-content: space-between; gap: var(--sp-4); }
.map-track-head b { font-family: var(--font-head); }

.map-track-close {
    border: none;
    background: none;
    color: var(--c-muted);
    font-size: var(--text-lg);
    line-height: 1;
    cursor: pointer;
    padding: 0 0.125rem;
    border-radius: var(--radius-sm);
}

.map-track-close:hover { color: var(--c-fg); }
.map-track-note { color: var(--c-muted); margin-top: var(--sp-1); font-size: var(--text-xs); font-variant-numeric: tabular-nums; }
.map-track-error { color: var(--c-error); }
.map-track-windows { display: flex; gap: var(--sp-1); margin-top: var(--sp-3); }

/* The window selector is the design system's chip, sized to the panel. */
.map-track-window {
    flex: 1 1 auto;
    padding: 0.25rem var(--sp-2);
    border: 1px solid var(--c-border-strong);
    border-radius: var(--radius-pill);
    background: var(--c-white);
    color: var(--c-muted);
    font: inherit;
    font-size: var(--text-xs);
    cursor: pointer;
    transition: background var(--transition), border-color var(--transition), color var(--transition);
}

.map-track-window:hover { border-color: var(--c-black); color: var(--c-fg); }
.map-track-window.is-active { background: var(--c-black); border-color: var(--c-black); color: var(--c-white); }

/* --- Vessel popup ---------------------------------------------------------- */

.maplibregl-popup-content {
    padding: var(--sp-3) var(--sp-4);
    border-radius: var(--radius-md);
    box-shadow: var(--shadow-lg);
    font-family: var(--font-sans);
}

.vessel-popup { font-size: var(--text-sm); line-height: 1.5; min-width: 12rem; }
.vessel-popup b { font-family: var(--font-head); }

/* How long ago the position above this line was reported. A vessel that has gone
   silent stays on the map at its last fix, and without the age nothing on screen
   would say so. */
.vessel-popup-age { color: var(--c-muted); font-size: var(--text-xs); }

/* Opens the vessel's stored track. Full width of the popup so it reads as an
   action on the vessel above it rather than as a caption. */
.vessel-popup-track {
    display: block;
    width: 100%;
    margin-top: var(--sp-3);
    padding: 0.4375rem var(--sp-3);
    border: none;
    border-radius: var(--radius-pill);
    background: var(--c-black);
    color: var(--c-white);
    font: inherit;
    font-size: var(--text-sm);
    font-weight: 600;
    cursor: pointer;
    transition: background var(--transition);
}

.vessel-popup-track:hover { background: #2c2c2c; }

/* Pressed while this vessel's route is on the map — the same button closes it
   again. Green matches the route itself, so the link between the button and the
   line on the map is not something the operator has to remember. */
.vessel-popup-track.is-active { background: #2f9e44; }
.vessel-popup-track.is-active:hover { background: #27833a; }

.maplibregl-ctrl-attrib { font-size: var(--text-xs); }
.maplibregl-ctrl-group { border-radius: var(--radius-sm) !important; box-shadow: var(--shadow-sm) !important; }

@media (max-width: 1024px) {
    .map-layout { grid-template-columns: 1fr; grid-template-rows: 60vh 1fr; }

    /* A grid item's automatic minimum is its content, so a list longer than its
       track pushes the track open and takes the height from the map above it —
       the rows then divide nothing like the ratio they name. The panel scrolls;
       it does not need the room. */
    .map-sidebar { width: auto; min-height: 0; border-left: none; border-top: 1px solid var(--c-border); }
    .map-stats { min-width: 0; font-size: var(--text-xs); }
    .map-track { max-width: calc(100% - 24px); }
    /* Two cards along one edge would overlap on a narrow screen, and the legend
       is the one the operator can do without. */
    .map-legend { display: none; }
}

/* --- Phone -----------------------------------------------------------------

   The map keeps the larger share of the screen and the list becomes a panel
   under it, scrolling on its own. Both rows are fractions rather than one fixed
   height: the viewport is already short, and a map measured in vh ignores the
   header and the bottom bar that are eating into it. */
@media (max-width: 640px) {
    .map-layout { grid-template-rows: 3fr 2fr; }
    .map-sidebar { padding: var(--sp-3); }

    /* The statistics card is the one overlay that stays, and on a phone it has to
       earn the map it covers: two thirds of the width, and three of its six rows.
       ClientApp/map/stats.ts writes them in a fixed order — rate, total, on the
       map, changed this tick, redraw time, uptime — and the last three are
       throughput diagnostics rather than anything an inspector acts on. The zone
       counts below them are a separate element and stay. */
    .map-stats-card {
        top: 8px;
        left: 8px;
        right: auto;
        max-width: 68%;
        /* However many zones the catalogue grows to, the card keeps most of the
           map visible and scrolls its own list instead. */
        max-height: 55%;
        overflow-y: auto;
        padding: var(--sp-2) var(--sp-3);
    }

    .map-stats .stat { gap: var(--sp-3); }
    .map-stats .stat:not(.zone-row):nth-child(n+4) { display: none; }
    .map-track { left: 8px; right: 8px; bottom: 12px; max-width: none; }

}

/* On a touch screen this small, MapLibre's zoom buttons duplicate a pinch and
   cost a corner of the map. A narrow desktop window keeps them — there the
   gesture is a scroll wheel that not every mouse has. */
@media (max-width: 640px) and (pointer: coarse) {
    .maplibregl-ctrl-group { display: none; }
}

/* ---------------------------------------------------------------------------
   Side panel

   The map's panel and the full list screen render the same fragments. The panel
   is 400 px wide and the full page is not, so the differences live here rather
   than in a second set of views.
   --------------------------------------------------------------------------- */

.panel-head { display: flex; align-items: baseline; justify-content: space-between; gap: var(--sp-3); margin-bottom: var(--sp-3); }
.panel-head h2 { font-size: var(--text-base); }
.panel-link { font-size: var(--text-sm); }

/* Cards stacked down the panel: summary, then the list. */
.panel-stack { display: flex; flex-direction: column; gap: var(--sp-3); }

/* The tracker column is the first thing to go when the table is squeezed into
   the panel — the vessel's name and its state are what the panel is for, and the
   identifier is a click away on the full screen. */
.map-sidebar .col-device { display: none; }
.map-sidebar .data-table th,
.map-sidebar .data-table td { padding: 0.5rem var(--sp-2); }

.map-sidebar .card { padding: var(--sp-4); border-radius: var(--radius-md); }
.map-sidebar .pager { padding-top: var(--sp-2); }

/* Rows in the side panel are clickable — a click centres the map on the vessel
   and opens its popup (ClientApp/map/index.ts). The cursor lives only here, where
   the map is: the same rows on the standalone /Vessels screen do nothing, and the
   affordance there would be a lie. The answer to the click is drawn on the map,
   not in this list — the island may not render outside #map (htmx-conventions §1),
   so a selected-row state would have to be server-rendered through hx-trigger. */
.map-sidebar .data-table tbody tr[id^="vessel-"] { cursor: pointer; }

/* Sign-in invitation shown to an anonymous visitor in place of the list. The map
   is a legitimate anonymous landing page; the panel is the part that needs an
   operator, and saying so beats a column that is simply missing. */
.panel-signin { text-align: center; }
.panel-signin p { margin: 0 0 var(--sp-4); font-size: var(--text-sm); color: var(--c-muted); }

/* ---------------------------------------------------------------------------
   List screens
   --------------------------------------------------------------------------- */

.page { padding: var(--sp-6) var(--sp-5); max-width: 1180px; margin: 0 auto; }
.page-head { display: flex; align-items: flex-end; justify-content: space-between; gap: var(--sp-4); margin-bottom: var(--sp-5); }
.page-head p { margin: var(--sp-1) 0 0; font-size: var(--text-sm); color: var(--c-muted); max-width: 46rem; }
/* The summary keeps its width while the heading beside it takes the rest. */
.page-head__tiles { flex: 0 0 auto; min-width: 16rem; }

@media (max-width: 1024px) {
    .page-head { flex-direction: column; align-items: stretch; }
    .page-head__tiles { min-width: 0; }
}

.filters { display: flex; align-items: center; gap: var(--sp-2); margin-bottom: var(--sp-4); }
.filters .search { max-width: 24rem; }
.map-sidebar .filters .search { max-width: none; }

.table-panel { background: var(--c-white); border: 1px solid var(--c-border); border-radius: var(--radius-lg); padding: var(--sp-4); }
.map-sidebar .table-panel { border-radius: var(--radius-md); }
.panel-note { color: var(--c-muted); font-size: var(--text-xs); padding: var(--sp-3) var(--sp-1) 0; max-width: 48rem; }

@media (max-width: 640px) {
    .page { padding: var(--sp-4) var(--sp-3); }
    .page-head { margin-bottom: var(--sp-4); }
    .filters .search { max-width: none; }
    .table-panel { padding: var(--sp-3); }

    /* Four columns do not fit on a 360 px screen. The tracker identifier goes
       first, exactly as it does in the map's side panel — it is the column an
       operator reads off a box, not one they scan a list by. What is left still
       overflows on the narrowest phones, so the table scrolls sideways inside its
       own panel rather than pushing the page's whole layout wider. */
    .col-device { display: none; }
    .table-panel { overflow-x: auto; }
    .data-table { min-width: 20rem; }
}

/* ---------------------------------------------------------------------------
   Sign-in
   --------------------------------------------------------------------------- */

/* The sign-in screen has one thing on it, so it is centred in what is left of
   the viewport rather than hung from the top — and by the same flex column the
   map screen uses, so neither has to know the header's height. */
body.page-auth { background: var(--c-surface); min-height: 100vh; display: flex; flex-direction: column; }
body.page-auth .app-header { flex: 0 0 auto; }
body.page-auth #content { flex: 1 1 auto; display: grid; place-items: center; padding: var(--sp-4); }

.auth-card {
    width: 100%;
    max-width: 24rem;
    margin: 0;
    padding: var(--sp-6);
    background: var(--c-white);
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-sm);
}

.auth-card h1 { font-size: var(--text-xl); margin-bottom: var(--sp-2); }
.auth-card > p { margin: 0 0 var(--sp-5); font-size: var(--text-sm); color: var(--c-muted); }
.auth-card label { display: block; margin-bottom: var(--sp-1); font-size: var(--text-sm); font-weight: 500; color: var(--c-muted); }

.auth-card input {
    width: 100%;
    padding: 0.625rem 0.875rem;
    margin-bottom: var(--sp-4);
    border: 1px solid var(--c-border-strong);
    border-radius: var(--radius-sm);
    font: inherit;
    transition: border-color var(--transition), box-shadow var(--transition);
}

.auth-card input:focus { outline: none; border-color: var(--c-accent); box-shadow: var(--focus-ring); }
/* The validation span sits between the input and the next label; when it holds a
   message it must not be pushed away from the field it belongs to. */
.auth-card .field-error { margin: calc(var(--sp-4) * -1) 0 var(--sp-4); }
.auth-card .alert-error { margin-bottom: var(--sp-4); }

/* ---------------------------------------------------------------------------
   htmx request indicator

   Shown only while a request is in flight. Any request longer than 300 ms needs
   one, otherwise the interface looks frozen (docs/htmx-conventions.md, §7).
   --------------------------------------------------------------------------- */

.htmx-indicator {
    position: fixed;
    /* Above the bottom navigation where there is one, which is why the bar's
       height is a variable rather than a number in the phone media query. */
    bottom: calc(var(--nav-height) + var(--sp-4));
    right: var(--sp-4);
    z-index: 100;
    padding: var(--sp-2) var(--sp-4);
    border-radius: var(--radius-pill);
    background: var(--c-black);
    color: var(--c-white);
    font-size: var(--text-sm);
    box-shadow: var(--shadow-lg);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--transition);
}

.htmx-request .htmx-indicator,
.htmx-request.htmx-indicator { opacity: 1; }

/* ---------------------------------------------------------------------------
   Touch input

   Keyed on the pointer rather than on the viewport width: a tablet in landscape
   is wide and still has no mouse, and a narrow desktop window has a cursor that
   hits a 24 px target perfectly well. Both queries below are inert on a machine
   with a mouse.
   --------------------------------------------------------------------------- */

@media (pointer: coarse) {
    /* A finger's contact patch is about 9 mm, which is roughly 44 px — the floor
       both platform guidelines name and the one the compact controls in this
       interface fall under on their own. */
    .btn--sm { padding: 0.5rem 1rem; }
    .btn--icon { width: 2.75rem; height: 2.75rem; }
    .chip, .toggle { padding: 0.5rem 1rem; }
    .pager button { width: 2.75rem; height: 2.75rem; }
    .data-table th, .data-table td { padding: 0.875rem 0.75rem; }
    .map-stats .zone-row { padding: 0.4375rem var(--sp-2); }
    .map-track-window { padding: 0.5rem var(--sp-2); }
    .map-track-close { padding: var(--sp-2); }

    /* Safari zooms the whole page in when a field with type smaller than 16 px
       takes focus, and it does not zoom back out afterwards — the operator is
       left on a magnified screen with the map off the side of it. */
    .input, .select, .textarea, .search, .auth-card input { font-size: 1rem; }

    /* The platform's own blue flash on tap is a rectangle around the element's
       box, which on a pill-shaped button is visibly the wrong shape. */
    a, button, .chip, .toggle { -webkit-tap-highlight-color: rgba(0, 0, 0, 0.06); }
}

/* On a phone the row keeps its touch height and gives back the horizontal room,
   which is what decides whether a vessel's name fits on one line. */
@media (pointer: coarse) and (max-width: 640px) {
    .data-table th, .data-table td { padding: 0.8125rem 0.4375rem; }
}

/* Hover is a state a finger cannot express: on a touch screen the browser
   applies :hover on tap and then leaves it applied until something else is
   tapped, so a row stays highlighted long after the operator has moved on. */
@media (hover: none) {
    .data-table tbody tr:hover td { background: none; }
    .map-stats .zone-row:hover { background: none; }
}

/* ---------------------------------------------------------------------------
   Component gallery (/Design, Development only)
   --------------------------------------------------------------------------- */

.kit { padding: var(--sp-6) var(--sp-5); max-width: 1180px; margin: 0 auto; }
.kit__intro { max-width: 46rem; margin: var(--sp-2) 0 var(--sp-6); color: var(--c-muted); }
.kit section { margin-bottom: var(--sp-6); }
.kit section > h2 { margin-bottom: var(--sp-1); }
.kit__note { margin: 0 0 var(--sp-4); font-size: var(--text-sm); color: var(--c-muted); max-width: 46rem; }
/* Headings carry no margin of their own (diia.css), which is right on a screen
   where each has a section around it and wrong in a specimen where they are
   stacked with nothing between. */
.kit__type > * + * { margin-top: var(--sp-3); }
.kit__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); }
.kit__row + .kit__row { margin-top: var(--sp-4); }
/* Three placeholder lines of decreasing width — what a loading list looks like. */
.kit__lines .skeleton { height: 1rem; margin-bottom: var(--sp-2); }
.kit__lines .skeleton:nth-child(2) { width: 70%; }
.kit__lines .skeleton:nth-child(3) { width: 45%; margin-bottom: 0; }
.kit__grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); gap: var(--sp-4); }

/* Colour tokens shown as what they are: a filled tile with the variable name
   under it, so the gallery stays true when a token's value changes. */
.kit__swatches { display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: var(--sp-3); }
.kit__swatch { border: 1px solid var(--c-border); border-radius: var(--radius-md); overflow: hidden; }
.kit__swatch i { display: block; height: 3.5rem; }
.kit__swatch span { display: block; padding: var(--sp-2); font-family: var(--font-mono); font-size: var(--text-xs); color: var(--c-muted); }

/* ---------------------------------------------------------------------------
   Connections screen ("Підключення") — device on-boarding and the dead-letter
   tail. Placement only; components come from diia.css.
   --------------------------------------------------------------------------- */

.connect-steps { margin: 0; padding-left: 1.25rem; max-width: 46rem; }
.connect-steps li { margin-bottom: var(--sp-2); }
.connect-steps li:last-child { margin-bottom: 0; }

.dlq-total { margin: 0 0 var(--sp-3); font-size: var(--text-sm); color: var(--c-muted); }
.dlq-total b { font-variant-numeric: tabular-nums; }
