/* ============================================================
   OBSTACLE REGISTRY — Design Tokens & Component Styles
   Aviation-themed: deep navy/slate surfaces, cyan-teal accent
   ============================================================ */

/* --- Fonts --- */
@import url('https://api.fontshare.com/v2/css?f[]=general-sans@400,500,600,700&display=swap');

:root {
  --font-body: 'General Sans', 'Helvetica Neue', sans-serif;
  --font-display: 'General Sans', 'Helvetica Neue', sans-serif;
  --font-mono: 'JetBrains Mono', 'Fira Code', monospace;
}

/* --- Type Scale --- */
:root {
  --text-xs:   clamp(0.75rem,  0.7rem  + 0.25vw, 0.875rem);
  --text-sm:   clamp(0.875rem, 0.8rem  + 0.35vw, 1rem);
  --text-base: clamp(1rem,     0.95rem + 0.25vw, 1.125rem);
  --text-lg:   clamp(1.125rem, 1rem    + 0.75vw, 1.5rem);
  --text-xl:   clamp(1.5rem,   1.2rem  + 1.25vw, 2.25rem);
}

/* --- Spacing (4px base) --- */
:root {
  --space-1:  0.25rem;
  --space-2:  0.5rem;
  --space-3:  0.75rem;
  --space-4:  1rem;
  --space-5:  1.25rem;
  --space-6:  1.5rem;
  --space-8:  2rem;
  --space-10: 2.5rem;
  --space-12: 3rem;
  --space-16: 4rem;
  --space-20: 5rem;
  --space-24: 6rem;
}

/* --- Color: Aviation Dark Theme (default) --- */
:root, [data-theme="dark"] {
  --color-bg:             #0f1117;
  --color-surface:        #161922;
  --color-surface-2:      #1c202c;
  --color-surface-offset: #1a1e2a;
  --color-surface-3:      #232838;
  --color-divider:        #2a2f42;
  --color-border:         #333a50;

  /* Three text tiers, and every one of them has to be READABLE.

     Measured 21.08.2026, worst case across bg / surface / surface-2 /
     surface-offset / surface-3 (WCAG AA wants 4.5:1 for ordinary text):

       before          dark          light
       text            10.50         13.51
       muted            4.73          4.53   — only just over the line
       faint            2.24          2.05   — fails everywhere, 33 text uses

     There was no room for a third tier: between "passes AA" and "visibly
     fainter than muted" the gap was already closed. So the whole scale moved
     up a rung — the old muted became the new faint, and muted was given a
     brighter value above it:

       after           dark          light
       text            10.50         13.51
       muted            7.00          7.01
       faint            4.61          4.59

     What this costs, and it is worth saying: the tiers now sit CLOSER
     together. Contrast of one tier against the next fell from 2.22/2.11 to
     1.50/1.52 in the dark theme. Three legible tiers do not fit as far apart
     as three tiers where one of them could be unreadable. */
  --color-text:           #d6dae6;
  --color-text-muted:     #aeb3c2;
  --color-text-faint:     #8890a8;
  --color-text-inverse:   #0f1117;

  --color-primary:        #22d3ee;
  --color-primary-hover:  #06b6d4;
  --color-primary-active: #0891b2;
  --color-primary-dim:    rgba(34, 211, 238, 0.12);
  --color-primary-glow:   rgba(34, 211, 238, 0.25);

  --color-error:          #f87171;
  --color-error-dim:      rgba(248, 113, 113, 0.12);
  --color-warning:        #fbbf24;
  --color-warning-dim:    rgba(251, 191, 36, 0.12);
  --color-success:        #34d399;
  --color-success-dim:    rgba(52, 211, 153, 0.12);
  --color-info:           #60a5fa;
  --color-info-dim:       rgba(96, 165, 250, 0.12);

  --color-orange:         #fb923c;
  --color-purple:         #a78bfa;

  --shadow-sm: 0 1px 3px rgba(0,0,0,0.4);
  --shadow-md: 0 4px 12px rgba(0,0,0,0.5);
  --shadow-lg: 0 12px 32px rgba(0,0,0,0.6);

  /* A POPOVER IS NOT A CARD.
     A card sits in the page and one step of surface is enough to read it. A
     popover COVERS content, and MEASURED 07.09.2026 the row menu's fill was
     1.08:1 against the table rows underneath it in this theme and 1.06:1 in
     the light one — the same colour, to the eye. What separates a popover is
     its edge, not its fill, so it gets its own shadow rather than the one
     cards use. */
}

/* --- Light Mode --- */
[data-theme="light"] {
  --color-bg:             #f0f2f5;
  --color-surface:        #ffffff;
  --color-surface-2:      #f7f8fa;
  --color-surface-offset: #ebedf2;
  --color-surface-3:      #e4e7ed;
  --color-divider:        #d1d5de;
  --color-border:         #bcc1ce;

  /* Same move as the dark theme above — see the note there for the numbers. */
  --color-text:           #1a1d2b;
  --color-text-muted:     #454b5d;
  --color-text-faint:     #5d6683;
  --color-text-inverse:   #ffffff;

  --color-primary:        #0891b2;
  --color-primary-hover:  #0e7490;
  --color-primary-active: #155e75;
  --color-primary-dim:    rgba(8, 145, 178, 0.08);
  --color-primary-glow:   rgba(8, 145, 178, 0.18);

  --color-error:          #dc2626;
  --color-error-dim:      rgba(220, 38, 38, 0.08);
  --color-warning:        #d97706;
  --color-warning-dim:    rgba(217, 119, 6, 0.08);
  --color-success:        #059669;
  --color-success-dim:    rgba(5, 150, 105, 0.08);
  --color-info:           #2563eb;
  --color-info-dim:       rgba(37, 99, 235, 0.08);

  --color-orange:         #ea580c;
  --color-purple:         #7c3aed;

  --shadow-sm: 0 1px 3px rgba(0,0,0,0.08);
  --shadow-md: 0 4px 12px rgba(0,0,0,0.1);
  --shadow-lg: 0 12px 32px rgba(0,0,0,0.14);
  /* See the note in the dark theme. Heavier here than --shadow-lg, because a
     white panel over white rows has nothing else to be seen by. */
}

/* --- Radius & Transitions --- */
:root {
  --radius-sm: 0.375rem;
  --radius-md: 0.5rem;
  --radius-lg: 0.75rem;
  --radius-xl: 1rem;
  --radius-full: 9999px;
  --transition-interactive: 180ms cubic-bezier(0.16, 1, 0.3, 1);
  --transition-slow: 320ms cubic-bezier(0.16, 1, 0.3, 1);
}

/* ============================================================
   LAYOUT
   ============================================================ */

.app-layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  grid-template-rows: 56px 1fr;
  grid-template-areas:
    "sidebar topbar"
    "sidebar main";
  height: 100dvh;
  overflow: hidden;
}

/* --- Sidebar --- */
.sidebar {
  grid-area: sidebar;
  background: var(--color-surface);
  border-right: 1px solid var(--color-divider);
  display: flex;
  flex-direction: column;
  padding: var(--space-4) 0;
  overflow-y: auto;
  z-index: 20;
}

.sidebar-logo {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-5);
  margin-bottom: var(--space-4);
  text-decoration: none;
  color: var(--color-text);
}

.sidebar-logo svg {
  flex-shrink: 0;
}

.sidebar-logo-text {
  font-size: var(--text-sm);
  font-weight: 700;
  letter-spacing: -0.02em;
  line-height: 1.2;
}

.sidebar-logo-sub {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  font-weight: 400;
}

.sidebar-section {
  padding: var(--space-2) var(--space-3);
}

.sidebar-section-label {
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-text-faint);
  padding: var(--space-1) var(--space-3);
  margin-bottom: var(--space-1);
}

.sidebar-link {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--color-text-muted);
  text-decoration: none;
  cursor: pointer;
  border: none;
  background: none;
  width: 100%;
  text-align: left;
}

.sidebar-link:hover {
  background: var(--color-primary-dim);
  color: var(--color-text);
}

.sidebar-link.active {
  background: var(--color-primary-dim);
  color: var(--color-primary);
}

.sidebar-link i {
  width: 20px;
  height: 20px;
  flex-shrink: 0;
}

.sidebar-spacer {
  flex: 1;
}

/* --- Top Bar --- */
.topbar {
  grid-area: topbar;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-divider);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0 var(--space-6);
  z-index: 15;
}

.topbar-left {
  display: flex;
  align-items: center;
  gap: var(--space-4);
}

.topbar-title {
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--color-text);
}

.topbar-right {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.topbar-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  font-weight: 500;
  cursor: pointer;
  border: 1px solid var(--color-border);
  background: var(--color-surface-2);
  color: var(--color-text-muted);
}

.topbar-btn:hover {
  background: var(--color-surface-3);
  color: var(--color-text);
  border-color: var(--color-text-faint);
}

.topbar-btn.primary {
  background: var(--color-primary);
  color: var(--color-text-inverse);
  border-color: var(--color-primary);
}

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

.topbar-icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  border-radius: var(--radius-md);
  border: 1px solid transparent;
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
}

.topbar-icon-btn:hover {
  background: var(--color-surface-2);
  color: var(--color-text);
}

/* Mobile hamburger */
.hamburger-btn {
  display: none;
}

/* --- Main Content --- */
.main-content {
  grid-area: main;
  overflow: auto;
  background: var(--color-bg);
  position: relative;
}

/* ============================================================
   PAGE VIEWS
   ============================================================ */

.page-view {
  display: none;
  height: 100%;
}

.page-view.active {
  display: flex;
  flex-direction: column;
}

/* --- Dashboard --- */
.dashboard-view {
  padding: var(--space-6);
  overflow-y: auto;
  gap: var(--space-6);
}

/* A role without obstacle.view (2.75.0): the notice, and nothing else. The
   panels would only count an empty list, which reads as an empty register. */
.dash-no-view {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-surface);
  color: var(--color-text);
}
.dash-no-view[hidden] { display: none; }
.dash-no-view p {
  margin: var(--space-1) 0 0;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
.dashboard-view.no-view > :not(.dash-no-view) { display: none !important; }

.kpi-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: var(--space-4);
}

.kpi-card {
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  padding: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.kpi-label {
  font-size: var(--text-xs);
  font-weight: 500;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.kpi-value {
  font-size: var(--text-xl);
  font-weight: 700;
  color: var(--color-text);
  font-feature-settings: 'tnum' 1;
}

.kpi-icon {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  margin-bottom: var(--space-2);
}

.kpi-icon.cyan  { background: var(--color-primary-dim); color: var(--color-primary); }
.kpi-icon.red   { background: var(--color-error-dim);   color: var(--color-error); }
.kpi-icon.amber { background: var(--color-warning-dim); color: var(--color-warning); }
.kpi-icon.green { background: var(--color-success-dim); color: var(--color-success); }

.dashboard-section-title {
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--color-text);
  margin-bottom: var(--space-2);
}

.recent-table-wrap {
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

.recent-table-wrap table {
  font-size: var(--text-sm);
}

.recent-table-wrap th {
  background: var(--color-surface-2);
  font-weight: 600;
  color: var(--color-text-muted);
  text-align: left;
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border-bottom: 1px solid var(--color-divider);
}

.recent-table-wrap td {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-divider);
  color: var(--color-text);
}

.recent-table-wrap tr:last-child td {
  border-bottom: none;
}

.recent-table-wrap tr:hover td {
  background: var(--color-primary-dim);
}

/* A recent row opens its obstacle (2.72.0). */
.recent-table-wrap tr.dash-click { cursor: pointer; }

/* --- Dashboard panels (2.72.0, decision #25) --- */
.dashboard-pair {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: var(--space-5);
  align-items: start;
}

.dash-panel {
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  padding: var(--space-3);
}

.dash-note {
  margin-top: var(--space-2);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

/* One line of a panel. A <button> when it opens something, a <div> when not:
   the reset below is what makes the two look alike. */
.dash-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  width: 100%;
  padding: var(--space-2);
  border: 0;
  border-radius: var(--radius-md);
  background: none;
  color: var(--color-text);
  font: inherit;
  font-size: var(--text-sm);
  text-align: left;
  cursor: pointer;
}
.dash-row:hover, .dash-row:focus-visible { background: var(--color-primary-dim); }
.dash-row.static { cursor: default; }
.dash-row.static:hover { background: none; }
.dash-row .dash-n {
  flex: 0 0 auto;
  min-width: 3.2em;
  text-align: right;
  font-weight: 700;
  font-feature-settings: 'tnum' 1;
}
.dash-row .dash-text { flex: 1 1 auto; min-width: 0; }
.dash-row .dash-sub, .dash-sub-inline { font-size: var(--text-xs); color: var(--color-text-muted); }
.dash-row .dash-sub { display: block; }
.dash-row .dash-go { flex: 0 0 auto; color: var(--color-text-faint); }

.dash-bar {
  flex: 0 0 28%;
  height: 8px;
  border-radius: 4px;
  background: var(--color-surface-3);
  overflow: hidden;
}
.dash-bar > span { display: block; height: 100%; background: var(--color-warning); border-radius: 4px; }
.dash-bar.wide { flex: 1 1 45%; }

.dash-empty {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

/* The type chart, across: a name on one line, never broken in two. */
.type-chart .type-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 0 1 38%; }

.dash-legend {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  margin-top: var(--space-2);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.dash-legend i {
  display: inline-block;
  width: 9px;
  height: 9px;
  border-radius: 50%;
  margin-right: 4px;
  vertical-align: -1px;
}

/* Activity: weeks on the left, the last operations on the right. */
.activity-panel {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.4fr);
  gap: var(--space-5);
  align-items: start;
}
.activity-weeks {
  display: flex;
  align-items: stretch;
  gap: 4px;
  height: 120px;
  padding: 0 var(--space-1);
  border-bottom: 1px solid var(--color-divider);
}
.activity-week {
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}
.activity-bar { display: block; width: 100%; background: var(--color-primary); border-radius: 3px 3px 0 0; }
.activity-file { display: block; height: 4px; margin-top: 2px; border-radius: 2px; }
.activity-file.on { background: var(--color-text-faint); }
.activity-axis {
  display: flex;
  justify-content: space-between;
  margin-top: var(--space-1);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}
.activity-list { display: flex; flex-direction: column; }

/* KPI cards are buttons now (2.72.0): reset, and keep every label and figure on
   ONE line — "TOTAL OBSTACLES" and "TEMPORARY ACTIVE" broke in two at 175 px,
   and "1848.5 m" put its "m" on a line of its own (measured 04.10.2026). */
.kpi-card.kpi-button {
  font: inherit;
  color: inherit;
  text-align: left;
  cursor: pointer;
  width: 100%;
}
.kpi-card.kpi-button:hover, .kpi-card.kpi-button:focus-visible { border-color: var(--color-primary); }
.kpi-label, .kpi-value, .kpi-sub { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.kpi-sub { font-size: var(--text-xs); color: var(--color-text-muted); }

@media (max-width: 900px) {
  .dashboard-pair, .activity-panel { grid-template-columns: minmax(0, 1fr); }
}

/* --- Map + Table Split View --- */
.map-table-view {
  flex-direction: column;
}

.map-container {
  flex: 1;
  min-height: 300px;
  position: relative;
}

.map-container #map {
  width: 100%;
  height: 100%;
}

.resize-handle {
  height: 6px;
  background: var(--color-divider);
  cursor: row-resize;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

.resize-handle::after {
  content: '';
  width: 40px;
  height: 3px;
  border-radius: 2px;
  background: var(--color-text-faint);
}

.resize-handle:hover {
  background: var(--color-primary-dim);
}

.table-panel {
  height: 300px;
  min-height: 120px;
  display: flex;
  flex-direction: column;
  background: var(--color-surface);
  border-top: 1px solid var(--color-divider);
}

.table-toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-2) var(--space-4);
  gap: var(--space-3);
  border-bottom: 1px solid var(--color-divider);
  flex-shrink: 0;
  /* The filter panel hangs off THIS, not off its button — see .obs-filter-panel. */
  position: relative;
}

.table-toolbar-left {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.table-toolbar-right {
  display: flex;
  align-items: center;
  gap: var(--space-2);
}

.search-input {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
  min-width: 200px;
}

.search-input input {
  border: none;
  outline: none;
  background: transparent;
  font-size: var(--text-sm);
  color: var(--color-text);
  width: 100%;
}

.search-input input::placeholder {
  color: var(--color-text-faint);
}

.search-input i {
  color: var(--color-text-faint);
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-full);
  font-size: var(--text-xs);
  font-weight: 500;
  cursor: pointer;
  border: 1px solid var(--color-border);
  background: var(--color-surface-2);
  color: var(--color-text-muted);
}

.filter-chip:hover, .filter-chip.active {
  border-color: var(--color-primary);
  color: var(--color-primary);
  background: var(--color-primary-dim);
}

.obstacle-count {
  font-size: var(--text-xs);
  color: var(--color-text-faint);
  font-feature-settings: 'tnum' 1;
}

.data-table-wrap {
  flex: 1;
  overflow: auto;
}

.data-table {
  font-size: var(--text-sm);
  min-width: 900px;
}

.data-table th {
  background: var(--color-surface-2);
  font-weight: 600;
  color: var(--color-text-muted);
  text-align: left;
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border-bottom: 1px solid var(--color-divider);
  position: sticky;
  top: 0;
  z-index: 2;
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}

.data-table th:hover {
  color: var(--color-text);
}

.data-table th .sort-icon {
  display: inline-block;
  margin-left: var(--space-1);
  opacity: 0.4;
  font-size: 0.7em;
}

.data-table th.sorted .sort-icon {
  opacity: 1;
  color: var(--color-primary);
}

.data-table td {
  padding: var(--space-2) var(--space-3);
  border-bottom: 1px solid var(--color-divider);
  color: var(--color-text);
  white-space: nowrap;
}

.data-table tr:hover td {
  background: var(--color-primary-dim);
}

.data-table tr.selected td {
  background: var(--color-primary-glow);
}

/* A row brought into view by scrollIntoView() — a marker clicked, a selection
   sent from AutoCAD — stops BELOW the sticky header, not under it. MEASURED on
   staging on 30.09.2026 (2.68.0): the table turned to the right page and marked
   the row, and a row scrolled in from above sat at the top edge of
   .data-table-wrap, 0 to 45 px, under a header 39 px tall — marked, and not
   seen. With a margin as tall as the header it stopped at 39 px; 3rem is a
   little more, so a taller header font still clears it. */
#obstacleTableBody tr {
  scroll-margin-top: 3rem;
}

/* AOWEB — the rows of a selection sent from AutoCAD (public/cad-focus.js).
   Amber like the rings on the map, with a bar at the left edge, because the
   light theme's amber tint is faint enough to miss on its own. */
.data-table tr.cad-focus-row td {
  background: var(--color-warning-dim);
}
.data-table tr.cad-focus-row td:first-child {
  box-shadow: inset 3px 0 0 var(--color-warning);
}
/* The rings on the map, one per obstacle. HTML, not SVG: see cadFocusRings()
   in cad-focus.js for why an svg in a pane of its own draws nothing here. The
   dark edge inside and out keeps the ring readable on the street map and on
   the satellite layer alike. */
.leaflet-marker-icon.cad-focus-ring {
  box-sizing: border-box;
  border: 3px solid var(--color-warning);
  border-radius: 50%;
  background: transparent;
  box-shadow: 0 0 0 2px rgba(0, 0, 0, 0.45), inset 0 0 0 2px rgba(0, 0, 0, 0.45);
  pointer-events: none;
}

/* Type badges */
.type-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  padding: 2px var(--space-2);
  border-radius: var(--radius-full);
  font-size: 0.6875rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.type-badge.tower    { background: var(--color-primary-dim); color: var(--color-primary); }
.type-badge.building { background: var(--color-info-dim);    color: var(--color-info); }
.type-badge.antenna  { background: var(--color-warning-dim); color: var(--color-warning); }
.type-badge.chimney  { background: var(--color-error-dim);   color: var(--color-error); }
.type-badge.crane    { background: var(--color-success-dim); color: var(--color-success); }
.type-badge.mast,
.type-badge.cable,
.type-badge.windturbine,
.type-badge.other    { background: var(--color-surface-3);   color: var(--color-text-muted); }

/* Action buttons in table */
.row-actions {
  display: flex;
  gap: var(--space-1);
}

/* Workflow buttons carry a word, not only a glyph. An operator looking for
   "withdraw" was looking for a word; until 2026-08-08 it appeared only in a
   tooltip. Hidden below 900px, where the mobile card is used instead. */
.workflow-btn-text {
  margin-left: 4px;
  font-size: 11px;
  font-weight: 600;
}
.row-action-btn.workflow-btn { width: auto; padding: 0 8px; }
@media (max-width: 900px) {
  .workflow-btn-text { display: none; }
  .row-action-btn.workflow-btn { width: 28px; padding: 0; }
}

/* The same moves, offered where the operator already is: the obstacle form. */
#obsWorkflowActions .workflow-form-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 13px;
}
#obsWorkflowActions .workflow-form-btn.withdraw { color: #f87171; border-color: #f8717155; }
#obsWorkflowActions .workflow-form-btn.publish  { color: #34d399; border-color: #34d39955; }

.row-action-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-sm);
  border: none;
  background: transparent;
  color: var(--color-text-faint);
  cursor: pointer;
}

.row-action-btn:hover {
  background: var(--color-surface-3);
  color: var(--color-text);
}

.row-action-btn.danger:hover {
  background: var(--color-error-dim);
  color: var(--color-error);
}

/* ============================================================
   MODAL
   ============================================================ */

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  backdrop-filter: blur(4px);
  z-index: 9000;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--space-6);
}

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

.modal {
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-xl);
  width: 100%;
  max-width: 600px;
  max-height: 85vh;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: var(--shadow-lg);
}

@media (max-width: 480px) {
  .modal-overlay {
    padding: var(--space-3);
    align-items: flex-start;
    padding-top: var(--space-8);
  }
  .modal {
    max-height: 90vh;
    border-radius: var(--radius-lg);
  }
  .modal-header {
    padding: var(--space-4);
  }
  .modal-body {
    padding: var(--space-4);
  }
  .modal-footer {
    padding: var(--space-3) var(--space-4);
  }
}

/* The obstacle form is 41 fields, 18 of them full-width, each with a note
   under it — far more than the 600 px every other modal is sized for.
   Measured on staging, scroll height of the form body against an 862 px
   viewport:

     600 px   column 247 px   3853 px   4.47 screens
     880 px   column 380 px   3494 px   4.05
     1000 px  column 437 px   3179 px   3.69      <- chosen
     1200 px  column 532 px   3000 px   3.48
     1400 px  column 627 px   2933 px   3.40

   1000 px is the knee: the first 400 px of extra width save 674 px of
   scrolling, the next 400 save 246. Past it the label-to-field eye travel
   grows for very little.

   THREE columns at 1000 px was measured too and is WORSE than two — 3584 px
   against 3179 — because narrower columns wrap each field's note onto more
   lines. The obvious change would have made the window taller. */
#obstacleModal .modal {
  max-width: 1000px;
}

.modal-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-5) var(--space-6);
  border-bottom: 1px solid var(--color-divider);
  flex-shrink: 0;
}

.modal-title {
  font-size: var(--text-lg);
  font-weight: 600;
  color: var(--color-text);
}

.modal-close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border-radius: var(--radius-md);
  border: none;
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
}

.modal-close:hover {
  background: var(--color-surface-3);
  color: var(--color-text);
}

.modal-body {
  padding: var(--space-5) var(--space-6);
  overflow-y: auto;
  flex: 1;
  min-height: 0;
}

.modal-footer {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-6);
  border-top: 1px solid var(--color-divider);
}

/* Form */
.form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-4);
}

.form-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* `full-width` means "span this grid" — whatever the element is. It was
   implemented for ONE element type, `.form-group`, and three kinds of element
   in the obstacle form carry the class:

     .form-group.full-width          18x  — spanned, correctly
     .form-section-divider           2x   — did not span
     <div class="full-width">        1x   — did not span

   So "Source, Survey & Classification" sat beside the first field of its own
   section with its rule half the width of the form; and #modelV3Fields — the
   container holding ten fields, itself a repeat(auto-fit, minmax(220px, 1fr))
   grid — was squeezed into one 460 px column and fitted two sub-columns where
   it had room for four.
   Measured on staging: spanning it took the form body from 3179 px of
   scrolling to 2730 px, without narrowing a single field (auto-fit keeps the
   220 px minimum, so it gains columns rather than shrinking them).
   Markup that states an intention CSS implements for only one selector reads
   correctly in both files and is wrong only on screen. */
.form-group.full-width {
  grid-column: 1 / -1;
}
.form-grid > .full-width {
  grid-column: 1 / -1;
}

.form-label {
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

.form-input, .form-select, .form-textarea {
  padding: var(--space-2) var(--space-3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
  color: var(--color-text);
  font-size: var(--text-sm);
}

.form-input:focus, .form-select:focus, .form-textarea:focus {
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px var(--color-primary-dim);
  outline: none;
}

.form-input::placeholder { color: var(--color-text-faint); }

.form-select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' fill='%238b92a8' viewBox='0 0 24 24'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  padding-right: 32px;
}

.form-textarea {
  min-height: 80px;
  resize: vertical;
}

.form-hint {
  font-size: var(--text-xs);
  color: var(--color-text-faint);
}

.form-checkbox-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-1) 0;
}

.form-checkbox-row input[type="checkbox"] {
  accent-color: var(--color-primary);
  width: 16px;
  height: 16px;
}

.form-checkbox-row label {
  font-size: var(--text-sm);
  color: var(--color-text);
}

/* Buttons */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-4);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
  border: 1px solid var(--color-border);
  background: var(--color-surface-2);
  color: var(--color-text-muted);
  white-space: nowrap;
}

.btn:hover {
  background: var(--color-surface-3);
  color: var(--color-text);
}

.btn.primary {
  background: var(--color-primary);
  color: var(--color-text-inverse);
  border-color: var(--color-primary);
}

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

.btn.danger {
  background: var(--color-error);
  color: #fff;
  border-color: var(--color-error);
}

.btn.danger:hover {
  opacity: 0.85;
}

/* A button that cannot be pressed has to LOOK like it (decision 44, Vedran
   15.10.2026). Until 2.74.0 only pagination and the 2FA dialog said so; the
   import's "Continue →" and "Import N Obstacles", which wait for a radius
   since 2.73.0, kept the full colour and the hand pointer — measured on
   14.10.2026: the same background, opacity and cursor as a live button. The
   :hover rules come after the ones above so a disabled button does not light
   up either; each restates its own resting colours. */
.btn:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}
.btn:disabled:hover {
  background: var(--color-surface-2);
  color: var(--color-text-muted);
}
.btn.primary:disabled:hover {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-text-inverse);
}
.btn.danger:disabled:hover {
  background: var(--color-error);
  border-color: var(--color-error);
  color: #fff;
  opacity: 0.45;
}

/* ============================================================
   IMPORT/EXPORT PANEL
   ============================================================ */

.import-export-view {
  padding: var(--space-6);
  overflow-y: auto;
  gap: var(--space-6);
}

/* --- Import / Export: one panel ---------------------------------------
   Replaces the six-card layout. The direction, the format and the scope are
   each chosen once, in that order, and the button says what will happen.  */

.ie-panel {
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  max-width: 780px;
  display: flex;
  flex-direction: column;
  gap: var(--space-5);
}

.ie-segmented {
  display: inline-flex;
  background: var(--color-surface-3);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  padding: 3px;
  gap: 3px;
  align-self: flex-start;
}

.ie-seg-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-5);
  border: none;
  background: transparent;
  border-radius: calc(var(--radius-md) - 3px);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: background var(--transition-interactive), color var(--transition-interactive);
}

.ie-seg-btn i { width: 15px; height: 15px; }

.ie-seg-btn.active {
  background: var(--color-surface);
  color: var(--color-primary);
  box-shadow: 0 1px 2px rgba(0, 0, 0, .12);
}

.ie-step { display: flex; flex-direction: column; }

.ie-step-label {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin-bottom: var(--space-3);
}

.ie-format-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: var(--space-3);
}

.ie-format-opt {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text);
  cursor: pointer;
  transition: border-color var(--transition-interactive), background var(--transition-interactive);
}

.ie-format-opt:hover { background: var(--color-surface-3); }
.ie-format-opt i { width: 17px; height: 17px; color: var(--color-text-muted); flex-shrink: 0; }
.ie-format-opt input { position: absolute; opacity: 0; pointer-events: none; }

.ie-format-opt.selected {
  border-color: var(--color-primary);
  background: color-mix(in srgb, var(--color-primary) 8%, transparent);
}
.ie-format-opt.selected i { color: var(--color-primary); }

/* The radio itself is invisible, so the focus ring has to be drawn on the
   label — otherwise tabbing through the formats moves an outline nobody
   can see. */
.ie-format-opt:focus-within {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.ie-format-note {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  line-height: 1.6;
  margin-top: var(--space-3);
}

.ie-scope { display: flex; flex-direction: column; gap: var(--space-1); }

.ie-scope-opt {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  color: var(--color-text);
  cursor: pointer;
  transition: background var(--transition-interactive);
}

.ie-scope-opt:hover { background: var(--color-surface-3); }
.ie-scope-text { flex: 1; }

.ie-scope-count {
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  background: var(--color-surface-3);
  border-radius: var(--radius-sm);
  padding: 2px var(--space-2);
  min-width: 34px;
  text-align: center;
}

/* Empty is not the same as "some" — a scope that would produce nothing must
   look different before the click, not after. */
.ie-scope-opt.empty .ie-scope-text,
.ie-scope-opt.empty .ie-scope-count { opacity: .5; }

.ie-warn {
  font-size: var(--text-sm);
  line-height: 1.6;
  color: var(--color-text);
  background: color-mix(in srgb, #fbbf24 14%, transparent);
  border-left: 3px solid #fbbf24;
  border-radius: var(--radius-sm);
  padding: var(--space-3) var(--space-4);
  margin-top: var(--space-3);
}

.ie-field-actions {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-3);
}
.ie-field-actions .btn { font-size: var(--text-xs); }

.ie-action {
  margin-top: var(--space-5);
  padding-top: var(--space-5);
  border-top: 1px solid var(--color-divider);
}

.ie-cards {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
  gap: var(--space-5);
}

.ie-card {
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.ie-card-title {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--color-text);
}

.ie-card-title i {
  color: var(--color-primary);
}

.ie-card-desc {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  line-height: 1.6;
}

.drop-zone {
  border: 2px dashed var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-8) var(--space-4);
  text-align: center;
  cursor: pointer;
  transition: border-color 0.2s, background 0.2s;
}

.drop-zone:hover, .drop-zone.dragover {
  border-color: var(--color-primary);
  background: var(--color-primary-dim);
}

.drop-zone-icon {
  color: var(--color-text-faint);
  margin-bottom: var(--space-2);
}

.drop-zone-text {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.drop-zone-hint {
  font-size: var(--text-xs);
  color: var(--color-text-faint);
  margin-top: var(--space-1);
}

/* ============================================================
   TOAST NOTIFICATION
   ============================================================ */

.toast-container {
  position: fixed;
  bottom: var(--space-6);
  right: var(--space-6);
  z-index: 11000;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  pointer-events: none;
}

.toast {
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  padding: var(--space-3) var(--space-5);
  box-shadow: var(--shadow-lg);
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-text);
  pointer-events: auto;
  animation: toastIn 0.3s ease-out;
  max-width: 360px;
}

.toast.success { border-left: 3px solid var(--color-success); }
.toast.error   { border-left: 3px solid var(--color-error); }
.toast.info    { border-left: 3px solid var(--color-info); }
/* Done, but not all of it: Batch Edit that skipped what it may not change (2.72.0). */
.toast.warning { border-left: 3px solid var(--color-warning); }

@keyframes toastIn {
  from { opacity: 0; transform: translateY(12px); }
  to   { opacity: 1; transform: translateY(0); }
}

.toast.removing {
  animation: toastOut 0.25s ease-in forwards;
}

@keyframes toastOut {
  to { opacity: 0; transform: translateY(12px); }
}

/* ============================================================
   MAP STYLES
   ============================================================ */

.leaflet-container {
  background: var(--color-bg) !important;
  font-family: var(--font-body) !important;
  /* Leaflet numbers its own panes: 200 tiles, 400, 600 markers, 700 popup,
     1000 controls. Without this line .leaflet-container creates no stacking
     context — position: relative, z-index: auto — so those panes compete in the
     ROOT one, against the rest of the app. Measured in Chrome: the .map-status
     pill (z-index 10) had never once been visible, and the obstacle drawer
     (then z-index 61) slid in under the tiles. isolation: isolate closes the
     panes inside the map. The map is overflow: hidden and nothing inside it
     paints outside its box anyway — every screen was checked before this
     line was written. */
  isolation: isolate;
}

.obstacle-popup {
  font-family: var(--font-body);
  max-width: 100%;
  box-sizing: border-box;
}

.obstacle-popup h3 {
  font-size: var(--text-sm);
  font-weight: 600;
  margin-bottom: var(--space-1);
  color: #1a1d2b;
}

.obstacle-popup .popup-row {
  display: flex;
  justify-content: space-between;
  font-size: var(--text-xs);
  padding: 2px 0;
  color: #5f6780;
}

.obstacle-popup .popup-row span:last-child {
  font-weight: 600;
  color: #1a1d2b;
}

.obstacle-popup .popup-actions {
  display: flex;
  /* Without wrapping, the buttons ran straight out of the popup: Leaflet
     caps the bubble at 300 px but a flex row will happily overflow it. */
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-top: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid #e4e7ed;
}

.obstacle-popup .popup-btn {
  padding: 3px 8px;
  font-size: 0.6875rem;
  border-radius: var(--radius-sm);
  cursor: pointer;
  border: 1px solid #bcc1ce;
  background: #f7f8fa;
  color: #5f6780;
  font-weight: 600;
  /* A label never breaks mid-word; it moves to the next line whole. */
  white-space: nowrap;
  flex: 0 0 auto;
}

.obstacle-popup .popup-btn:hover {
  background: #e4e7ed;
}

.obstacle-popup .popup-btn.primary {
  background: #0891b2;
  border-color: #0891b2;
  color: #fff;
}

/* Custom map marker */
.obstacle-marker-icon {
  background: none;
  border: none;
}

/* ============================================================
   RESPONSIVE
   ============================================================ */

@media (max-width: 768px) {
  .app-layout {
    grid-template-columns: 1fr;
    grid-template-areas:
      "topbar"
      "main";
  }

  .sidebar {
    position: fixed;
    left: -260px;
    top: 0;
    bottom: 0;
    width: 260px;
    transition: left 0.3s ease;
    box-shadow: var(--shadow-lg);
    z-index: 50;
  }

  .sidebar.open {
    left: 0;
  }

  .hamburger-btn {
    display: inline-flex;
  }

  .sidebar-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.4);
    z-index: 40;
    display: none;
  }

  .sidebar-overlay.open {
    display: block;
  }

  .form-grid {
    grid-template-columns: 1fr;
  }

  .table-toolbar {
    flex-wrap: wrap;
  }

  .search-input {
    min-width: 140px;
  }

  .kpi-grid {
    grid-template-columns: 1fr;
  }
}

/* Tablet landscape — sidebar visible but compact */
@media (min-width: 769px) and (max-width: 1024px) {
  .app-layout {
    grid-template-columns: 200px 1fr;
  }
  .sidebar-logo-text {
    font-size: var(--text-xs);
  }
}

/* ============================================================
   CONFIRM DIALOG
   ============================================================ */

.confirm-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0,0,0,0.5);
  backdrop-filter: blur(2px);
  z-index: 9500;
  display: none;
  align-items: center;
  justify-content: center;
}

.confirm-overlay.open {
  display: flex;
}

.confirm-box {
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  max-width: 400px;
  width: 90%;
  text-align: center;
  box-shadow: var(--shadow-lg);
}

.confirm-box h3 {
  font-size: var(--text-base);
  font-weight: 600;
  margin-bottom: var(--space-2);
}

.confirm-box p {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  margin-bottom: var(--space-5);
}

.confirm-actions {
  display: flex;
  gap: var(--space-3);
  justify-content: center;
}

/* ============================================================
   EMPTY STATE
   ============================================================ */

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  padding: var(--space-12);
  text-align: center;
  color: var(--color-text-faint);
}

.empty-state i {
  width: 48px;
  height: 48px;
  margin-bottom: var(--space-4);
  opacity: 0.4;
}

.empty-state-title {
  font-size: var(--text-base);
  font-weight: 600;
  color: var(--color-text-muted);
  margin-bottom: var(--space-1);
}

.empty-state-desc {
  font-size: var(--text-sm);
  max-width: 320px;
}

/* Leaflet draw toolbar fix */
.leaflet-draw-toolbar a {
  background-color: var(--color-surface) !important;
  color: var(--color-text) !important;
}

/* Status indicator for map */
.map-status {
  position: absolute;
  bottom: var(--space-2);
  left: var(--space-2);
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  z-index: 10;
  font-feature-settings: 'tnum' 1;
  pointer-events: none;
}

/* ============================================================
   ENHANCEMENTS
   ============================================================ */

/* --- Animations --- */
@keyframes fadeIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

@keyframes slideUp {
  from { opacity: 0; transform: translateY(16px); }
  to { opacity: 1; transform: translateY(0); }
}

@keyframes scaleIn {
  from { opacity: 0; transform: scale(0.95); }
  to { opacity: 1; transform: scale(1); }
}

.modal-overlay.open .modal {
  animation: scaleIn 0.25s cubic-bezier(0.16, 1, 0.3, 1);
}

.page-view.active {
  animation: fadeIn 0.2s ease-out;
}

.kpi-card {
  animation: slideUp 0.3s ease-out both;
}
.kpi-card:nth-child(1) { animation-delay: 0ms; }
.kpi-card:nth-child(2) { animation-delay: 60ms; }
.kpi-card:nth-child(3) { animation-delay: 120ms; }
.kpi-card:nth-child(4) { animation-delay: 180ms; }

.data-table tbody tr {
  animation: fadeIn 0.15s ease-out both;
}

/* --- Mobile Card View --- */
.mobile-cards {
  display: none;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-3);
  overflow-y: auto;
  flex: 1;
}

.mobile-card {
  background: var(--color-surface-2);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}

.mobile-card:hover, .mobile-card:active {
  border-color: var(--color-primary);
  background: var(--color-primary-dim);
}

.mobile-card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-2);
}

.mobile-card-name {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text);
}

.mobile-card-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2) var(--space-4);
  margin-bottom: var(--space-3);
}

.mobile-card-field {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.mobile-card-label {
  font-size: 0.625rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-faint);
}

.mobile-card-value {
  font-size: var(--text-xs);
  color: var(--color-text);
  font-feature-settings: 'tnum' 1;
}

.mobile-card-actions {
  display: flex;
  gap: var(--space-2);
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-divider);
}

.mobile-card-actions .row-action-btn {
  flex: 1;
  width: auto;
  height: 34px;
  font-size: var(--text-xs);
  gap: var(--space-1);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--radius-md);
  background: var(--color-surface-3);
}

@media (max-width: 640px) {
  .data-table-wrap {
    display: none;
  }
  .mobile-cards {
    display: flex;
  }
}

/* --- Bulk Select --- */
.bulk-bar {
  display: none;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-2) var(--space-4);
  background: var(--color-primary-dim);
  border-bottom: 1px solid var(--color-primary);
  flex-shrink: 0;
}

.bulk-bar.active {
  display: flex;
}

.bulk-bar-left {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-primary);
}

.data-table th .col-checkbox,
.data-table td .col-checkbox {
  accent-color: var(--color-primary);
  width: 16px;
  height: 16px;
  cursor: pointer;
}

/* --- Keyboard Shortcut Hints --- */
.kbd {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 2px 6px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--color-border);
  background: var(--color-surface-2);
  font-size: 0.625rem;
  font-family: var(--font-mono);
  color: var(--color-text-faint);
  line-height: 1;
  min-width: 20px;
}

.shortcut-hint {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  margin-left: auto;
}

/* --- Command Palette --- */
.cmd-palette-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  backdrop-filter: blur(4px);
  z-index: 10000;
  display: none;
  justify-content: center;
  padding-top: 20vh;
}

.cmd-palette-overlay.open {
  display: flex;
}

.cmd-palette {
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  width: 100%;
  max-width: 480px;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
  animation: scaleIn 0.15s ease-out;
}

.cmd-palette-input {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-divider);
}

.cmd-palette-input input {
  flex: 1;
  border: none;
  outline: none;
  background: transparent;
  font-size: var(--text-base);
  color: var(--color-text);
}

.cmd-palette-input input::placeholder {
  color: var(--color-text-faint);
}

.cmd-palette-input i {
  color: var(--color-text-faint);
}

.cmd-palette-results {
  max-height: 300px;
  overflow-y: auto;
  padding: var(--space-2);
}

.cmd-palette-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.cmd-palette-item:hover, .cmd-palette-item.highlighted {
  background: var(--color-primary-dim);
  color: var(--color-text);
}

.cmd-palette-item i {
  color: var(--color-text-faint);
  width: 16px;
  height: 16px;
  flex-shrink: 0;
}

.cmd-palette-item .cmd-item-secondary {
  margin-left: auto;
  font-size: var(--text-xs);
  color: var(--color-text-faint);
}

.cmd-palette-footer {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-2) var(--space-4);
  border-top: 1px solid var(--color-divider);
  font-size: var(--text-xs);
  color: var(--color-text-faint);
}

/* --- Coord Toggle --- */
.coord-toggle {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  overflow: hidden;
  font-size: var(--text-xs);
}

.coord-toggle button {
  padding: 2px 8px;
  background: transparent;
  color: var(--color-text-muted);
  border: none;
  cursor: pointer;
  font-weight: 600;
  font-size: inherit;
}

.coord-toggle button.active {
  background: var(--color-primary-dim);
  color: var(--color-primary);
}

/* --- Import Preview --- */
.import-preview {
  background: var(--color-surface-2);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  margin-top: var(--space-4);
  max-height: 320px;
  overflow: auto;
}

.import-preview table {
  font-size: var(--text-xs);
  min-width: 600px;
}

.import-preview th {
  background: var(--color-surface-3);
  padding: var(--space-2) var(--space-3);
  text-align: left;
  font-weight: 600;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  border-bottom: 1px solid var(--color-divider);
  position: sticky;
  top: 0;
  z-index: 2;
}

.import-preview td {
  padding: var(--space-1) var(--space-3);
  border-bottom: 1px solid var(--color-divider);
  color: var(--color-text);
  white-space: nowrap;
}

.import-preview-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  margin-top: var(--space-3);
  flex-wrap: wrap;
}

/* --- Import progress and outcome --- */
/* A file of several thousand obstacles takes long enough that silence reads as
   a hang; the bar reports batches as they land. */
.import-progress { margin-top: var(--space-3); }

.import-progress-bar {
  height: 10px;
  border-radius: 6px;
  background: var(--color-surface-3);
  overflow: hidden;
}

.import-progress-fill {
  height: 100%;
  background: var(--color-primary, #2563eb);
  border-radius: 6px;
  transition: width 0.25s ease;
}

.import-progress-text {
  margin-top: 6px;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

.import-stage-done .import-preview { margin-top: var(--space-2); }

/* --- Import stage: mapping and preview, full page width --- */
/* These steps carry a table of every column in the file and a row per
   obstacle, so they sit below the cards rather than inside one. */
.import-stage:empty { display: none; }

.import-stage {
  margin-top: var(--space-5);
  padding: var(--space-4);
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
}

.import-stage .import-preview { margin-top: var(--space-2); }
.import-stage .import-preview-actions { margin-bottom: 0; }

/* Column mapping: label, chooser and sample value on one line each. */
.csv-map-table { width: 100%; border-collapse: collapse; }
.csv-map-table td { padding: 5px 0; vertical-align: middle; }
.csv-map-table td:first-child {
  white-space: nowrap;
  padding-right: var(--space-4);
  font-size: var(--text-sm);
  width: 1%;
}
.csv-map-table td:nth-child(2) { width: 320px; padding-right: var(--space-4); }
.csv-map-table select { width: 100%; }
/* The sample is a hint, never a reason for the row to grow: it truncates. */
.csv-map-sample {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  max-width: 340px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 720px) {
  .csv-map-table td:nth-child(2) { width: auto; }
  .csv-map-sample { max-width: 160px; }
}

.import-preview-count {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  font-weight: 600;
}

/* ─── Updating obstacles from a file (v2.60.0) ─────────────────────────────
 *
 * DELIBERATELY NOT .data-table, AND THAT IS THE WHOLE NOTE.
 *
 * .data-table carries `min-width: 900px; white-space: nowrap` because it was
 * written for the obstacle list, which has eighteen columns. Three times now a
 * table written elsewhere has picked up that class, inherited both rules, and
 * given the page a horizontal scrollbar that hid the right-hand columns —
 * most recently the OLS penetration list in v2.58.3. The diff below has a cell
 * that holds a chip per changed field, so it is the worst possible candidate
 * for a nowrap table. It gets its own class and wraps.
 */
.upd-step { margin-top: var(--space-1); }

.upd-muted {
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}
.upd-error {
  margin: var(--space-2) 0 0;
  font-size: var(--text-sm);
  color: var(--color-error);
}
.upd-error:empty { display: none; }

.upd-inline {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 10px;
  font-size: var(--text-sm);
  cursor: pointer;
}

/* The identifier column: the one choice without which nothing else matters. */
.upd-uid {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 10px;
  margin: 0 0 12px;
}
.upd-uid-label { font-size: var(--text-sm); font-weight: 600; margin-bottom: 6px; }
.upd-uid select { max-width: 420px; }
.upd-uid .upd-muted { margin: 6px 0 0; font-size: var(--text-xs); }

.upd-table {
  width: 100%;
  min-width: 0;                 /* see the note above */
  border-collapse: collapse;
  font-size: var(--text-sm);
}
.upd-table th {
  text-align: left;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--color-text-muted);
  border-bottom: 1px solid var(--color-divider);
  padding: 6px 8px 6px 0;
  white-space: nowrap;
}
.upd-table td {
  padding: 6px 8px 6px 0;
  vertical-align: top;
  border-bottom: 1px solid var(--color-divider);
  overflow-wrap: break-word;
}
.upd-table select { width: 100%; }
.upd-table tr:last-child td { border-bottom: none; }

/* The sample value is a hint, never a reason for the row to grow. */
.upd-sample {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  max-width: 260px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.upd-actions {
  display: flex;
  gap: var(--space-2);
  margin: var(--space-3) 0 0;
}

/* Counts first: a reader wants the four numbers before the four hundred rows. */
.upd-summary {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin: 0 0 var(--space-3);
  padding: var(--space-2) var(--space-4);
  background: var(--color-surface-3);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
}

/* One field that would move, as "label  old → new". Wraps; never truncates —
 * a diff the operator is about to approve must be readable in full. */
.upd-chip {
  display: inline-block;
  margin: 0 6px 4px 0;
  padding: 2px 8px;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-sm);
  background: var(--color-surface-2);
  font-size: var(--text-xs);
  font-family: var(--font-mono);
  overflow-wrap: anywhere;
}
.upd-chip b { font-family: var(--font-body); font-weight: 600; }
.upd-chip i { font-style: normal; color: var(--color-text-muted); }

/* Four hundred rows must not push the Apply button off the page. */
.upd-scroll {
  max-height: 48vh;
  overflow-y: auto;
  overflow-x: hidden;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  padding: 0 var(--space-3);
}

.upd-details { margin: var(--space-3) 0 0; }
.upd-details > summary {
  cursor: pointer;
  font-size: var(--text-sm);
  font-weight: 600;
  padding: 4px 0;
}

@media (max-width: 720px) {
  .upd-sample { max-width: 120px; }
  .upd-summary { gap: var(--space-2); }
}

/* --- CSV Validation States --- */
.import-summary {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-4);
  margin-bottom: var(--space-2);
  padding: var(--space-2) var(--space-4);
  background: var(--color-surface-3);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  font-weight: 600;
  flex-wrap: wrap;
  gap: var(--space-3);
}

.import-summary-item {
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.import-summary-item.ok  { color: var(--color-success); }
.import-summary-item.err { color: var(--color-error); }
.import-summary-item.warn { color: var(--color-warning); }

.import-status-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-weight: 700;
  font-size: 0.85em;
  line-height: 1;
}

.import-status-icon.ok   { color: var(--color-success); }
.import-status-icon.err  { color: var(--color-error); }
.import-status-icon.warn { color: var(--color-warning); }

/* Valid row — subtle green left border */
.import-preview tr.import-row-valid td:first-child {
  border-left: 3px solid var(--color-success);
  padding-left: calc(var(--space-3) - 3px);
}

.import-preview tr.import-row-valid {
  background: rgba(52, 211, 153, 0.04);
}

/* Warning row — yellow left border */
.import-preview tr.import-row-warning td:first-child {
  border-left: 3px solid var(--color-warning);
  padding-left: calc(var(--space-3) - 3px);
}

.import-preview tr.import-row-warning {
  background: rgba(251, 191, 36, 0.05);
}

/* Error row — red left border */
.import-preview tr.import-row-error td:first-child {
  border-left: 3px solid var(--color-error);
  padding-left: calc(var(--space-3) - 3px);
}

.import-preview tr.import-row-error {
  background: rgba(248, 113, 113, 0.05);
}

/* Message rows (hold the inline error/warning text) */
.import-preview tr.import-row-error-msg-row td,
.import-preview tr.import-row-warning-msg-row td {
  padding-top: 0;
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-divider);
  background: transparent;
}

.import-error-msg {
  display: inline-block;
  margin-right: var(--space-3);
  font-size: var(--text-xs);
  color: var(--color-error);
  font-weight: 500;
}

.import-warning-msg {
  display: inline-block;
  margin-right: var(--space-3);
  font-size: var(--text-xs);
  color: var(--color-warning);
  font-weight: 500;
}

/* --- Export field groups ---------------------------------------------------
   The list went from 12 fields to 43 when every field an obstacle carries was
   offered. Forty-three checkboxes in one grid is a wall you scan rather than
   read, so they sit under group headings: Identification, Position, Extent and
   elevation, Marking and lighting, Status and validity, Provenance.
   Colours come from the theme, never a literal — a hard-coded grey here is
   invisible in one of the two themes, which is how this project lost an
   afternoon on 15 August. */
/* The container is .column-select-grid — three columns — and the groups became
   items in it. Two things went wrong, both visible only on screen: a margin-top
   meant for stacked groups pushed five of six down by 16px inside their row, and
   grid stretch gave a two-field group the height of a fourteen-field one, so
   Marking and lighting sat in a 610px box mostly empty.
   Groups now stack full width and the FIELDS flow into columns inside each one.
   Found by looking at the rebuilt page; every assertion passed throughout. */
.ie-fields-grouped { display: block; }

/* The column picker reuses .obs-filter-panel wholesale — the same anchoring,
   the same measured max-height, the same close-on-outside-click. Only the list
   inside is its own.

   It KEEPS the filter panel's width on purpose. This rule first said
   `max-width: 420px`, on the reasoning that a column name is short — and it
   did nothing at all: `.obs-filter-panel` sets max-width too, carries the same
   specificity, and stands later in this file, so it won. Seen in the browser,
   measured at 760 px. Worth keeping anyway: the panel is short (it gets only
   the room under the toolbar), so width is what buys visible rows — 18 columns
   across three columns of names is six rows, across two it is nine. */
.obs-cols-panel { /* width comes from .obs-filter-panel */ }
.oc-list {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  gap: 2px var(--space-4);
}
.oc-row {
  display: flex; align-items: center; gap: var(--space-2);
  padding: 3px 0; font-size: var(--text-sm); cursor: pointer;
}
.oc-row input { cursor: pointer; }
/* A column that is always there says so rather than offering a tick that does
   nothing: the row checkbox and the actions column are not the user's to hide. */
.oc-row.fixed { opacity: .55; cursor: default; }
.oc-row.fixed input { cursor: default; }
.ie-field-group + .ie-field-group { margin-top: var(--space-4); }

.ie-field-group-name {
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--color-text-muted);
  padding-bottom: var(--space-1);
  margin-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-divider);
}

/* --- Column Selection Modal Grid --- */
.column-select-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(190px, 1fr));
  gap: var(--space-2) var(--space-4);
}

.column-select-item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text);
  cursor: pointer;
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
  transition: background var(--transition-interactive);
}

.column-select-item:hover {
  background: var(--color-surface-3);
}

.column-select-item input[type="checkbox"] {
  flex-shrink: 0;
  width: 14px;
  height: 14px;
}

/* --- Dashboard Mini Map --- */
.dashboard-map-wrap {
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  height: 280px;
  overflow: hidden;
  cursor: pointer;
  position: relative;
}

.dashboard-map-wrap:hover::after {
  content: 'Click to view all obstacles';
  position: absolute;
  bottom: var(--space-3);
  left: 50%;
  transform: translateX(-50%);
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  z-index: 10;
  pointer-events: none;
  white-space: nowrap;
}

#dashboardMap {
  width: 100%;
  height: 100%;
}

/* --- Toast Undo --- */
.toast .undo-btn {
  padding: 2px 10px;
  border-radius: var(--radius-sm);
  background: var(--color-surface-3);
  border: 1px solid var(--color-border);
  color: var(--color-primary);
  font-size: var(--text-xs);
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
  margin-left: var(--space-2);
}

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

/* --- Height labels on map --- */
.height-label {
  background: none !important;
  border: none !important;
  box-shadow: none !important;
  white-space: nowrap;
  text-align: center;
}

/* A shadow alone could not hold the figure against both dark forest and bright
   snow or ploughed field, so the number sits on its own plate instead. */
.height-label-chip {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 9px;
  background: rgba(17, 24, 39, 0.82);
  border: 1px solid rgba(255, 255, 255, 0.35);
  color: #fff;
  font-size: 12.5px;
  font-weight: 700;
  line-height: 1.35;
  letter-spacing: 0.01em;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.45);
}

/* The label's lines (v2.70.0): who it is, how high, the rest. The UID is the
   register's handle and leads; the name follows in regular weight; both
   heights share one line; type, status and radius are small and quieter. */
.height-label-chip .hl-uid { font-weight: 700; letter-spacing: 0.02em; }
.height-label-chip .hl-name { font-weight: 500; }
.height-label-chip .hl-heights { font-weight: 700; }
.height-label-chip .hl-other { font-size: 11px; font-weight: 500; opacity: 0.85; }

/* --- Export Filtered Toggle --- */
.export-toggle {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.export-toggle label {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.export-toggle input[type="checkbox"] {
  accent-color: var(--color-primary);
}

/* --- Dashboard grid responsive --- */
@media (max-width: 768px) {
  #dashboardView > div:nth-child(2) {
    grid-template-columns: 1fr !important;
  }
}

/* --- Click-to-add cursor on map --- */
.map-click-mode {
  cursor: crosshair !important;
}
.map-click-mode .leaflet-interactive {
  cursor: crosshair !important;
}

/* --- Map click hint --- */
.map-click-hint {
  position: absolute;
  top: var(--space-2);
  left: 50%;
  transform: translateX(-50%);
  background: var(--color-primary);
  color: var(--color-text-inverse);
  font-size: var(--text-xs);
  font-weight: 600;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-full);
  z-index: 15;
  pointer-events: none;
  animation: fadeIn 0.2s ease-out;
}

/* ─── History Modal ────────────────────────────────────────── */
.history-modal {
  max-width: 560px;
}

.history-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-8) var(--space-4);
  text-align: center;
}

/* ─── Revision Timeline ───────────────────────────────────── */
.revision-timeline {
  display: flex;
  flex-direction: column;
}

.revision-entry {
  display: flex;
  gap: var(--space-3);
  position: relative;
}

.revision-line {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex-shrink: 0;
  width: 20px;
}

.revision-dot {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  border: 2.5px solid var(--color-text-faint);
  background: var(--color-surface);
  flex-shrink: 0;
  margin-top: 4px;
  position: relative;
  z-index: 1;
}

.revision-entry.latest .revision-dot {
  width: 14px;
  height: 14px;
  border-width: 3px;
  box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.15);
}

.revision-connector {
  width: 2px;
  flex: 1;
  background: var(--color-divider);
  min-height: 16px;
}

.revision-content {
  flex: 1;
  min-width: 0;
  padding-bottom: var(--space-5);
}

.revision-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.revision-action {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-sm);
  font-weight: 600;
}

.revision-timestamp {
  font-size: var(--text-xs);
  color: var(--color-text-faint);
  font-feature-settings: 'tnum' 1;
  white-space: nowrap;
}

.revision-user {
  display: flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin-top: var(--space-1);
}

/* ─── Revision Changes Detail ─────────────────────────────── */
.revision-changes {
  margin-top: var(--space-2);
  background: var(--color-bg);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  overflow: hidden;
}

.revision-change-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-xs);
  border-bottom: 1px solid var(--color-divider);
}

.revision-change-row:last-child {
  border-bottom: none;
}

.revision-field-label {
  font-weight: 600;
  color: var(--color-text-muted);
  min-width: 100px;
  flex-shrink: 0;
}

.revision-change-from {
  color: var(--color-error);
  text-decoration: line-through;
  opacity: 0.7;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 140px;
}

.revision-change-to {
  color: var(--color-success);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 140px;
}

/* ─── User Prompt Modal ───────────────────────────────────── */
.user-prompt-modal {
  max-width: 400px;
}

/* ─── Current User Button (sidebar) ───────────────────────── */
#currentUserBtn .user-btn-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

@media (max-width: 480px) {
  .revision-change-row {
    flex-wrap: wrap;
  }
  .revision-field-label {
    min-width: auto;
    width: 100%;
  }
  .revision-change-from,
  .revision-change-to {
    max-width: 100px;
  }
  .history-modal {
    max-width: 100%;
  }
}

/* ─── Auth Modal ─────────────────────────────────────────── */
/* OPAQUE (2.72.0, decision #28). When a session ends the sign-in opens over
   whatever page was showing, and the register stays in the DOM. MEASURED on
   staging 04.10.2026: through 85 % black and a 4 px blur the dashboard's
   figures (4148, 4143, 3070) and the map were still recognisable. The page
   background colour of the theme, with nothing behind it showing through. */
#loginOverlay {
  z-index: 9500;
  background: var(--color-bg);
  backdrop-filter: none;
}

.auth-modal {
  max-width: 420px;
  width: 100%;
}

.auth-modal-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-6) var(--space-6) var(--space-3);
  text-align: center;
}

.auth-subtitle {
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  margin: 0;
}

.auth-error {
  background: rgba(239, 68, 68, 0.1);
  border: 1px solid rgba(239, 68, 68, 0.3);
  color: var(--color-error);
  font-size: var(--text-sm);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-3);
}

.auth-switch {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: var(--space-2);
  margin-top: var(--space-4);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.auth-switch-btn {
  background: none;
  border: none;
  color: var(--color-primary);
  cursor: pointer;
  font-size: var(--text-sm);
  font-weight: 600;
  padding: 0;
}

.auth-switch-btn:hover {
  text-decoration: underline;
}

.btn.full-width {
  width: 100%;
  justify-content: center;
  margin-top: var(--space-3);
}

/* ─── User Button Sidebar ────────────────────────────────── */
.user-btn-info {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  overflow: hidden;
  line-height: 1.3;
}

.user-btn-name {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 140px;
}

.user-btn-role {
  font-size: 0.65rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  opacity: 0.9;
}

/* ─── Role Badge ─────────────────────────────────────────── */
.role-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  font-size: var(--text-xs);
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--role-color) 15%, transparent);
  color: var(--role-color);
  border: 1px solid color-mix(in srgb, var(--role-color) 25%, transparent);
}

/* ─── User Status ────────────────────────────────────────── */
.user-status {
  font-size: var(--text-xs);
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
}

.status-active {
  background: rgba(34, 197, 94, 0.12);
  color: var(--color-success);
}

.status-inactive {
  background: rgba(239, 68, 68, 0.12);
  color: var(--color-error);
}


/* ─── Verify action button ──────────────────────────────── */
.row-action-btn.verify {
  color: var(--color-warning);
}

.row-action-btn.verify:hover {
  background: rgba(251, 191, 36, 0.12);
}

/* Topbar verify button active state */
.topbar-btn.active {
  background: var(--color-primary);
  color: #fff;
}

/* ─── User Management ────────────────────────────────────── */
.user-mgmt-view {
  padding: var(--space-5);
}

/* The Aerodromes (OLS) page was the ONLY view without a second class, and the
   second class is what carries the padding: dashboard-view and
   import-export-view set space-6, user-mgmt-view space-5, map-table-view
   deliberately none because the map fills the frame. #olsView had `page-view`
   alone, so its table ran into the browser edge on every side and the ACTIONS
   column was cut off against it.
   space-5, matching user-mgmt-view: the same kind of page, a searchable list
   with a toolbar. No overflow-y — measured on staging, user-mgmt-view scrolls
   with `visible` too, and adding one here would have introduced a nested
   scrollbar the other list page does not have. */
.ols-admin-view {
  padding: var(--space-5);
}

.user-mgmt-toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-bottom: var(--space-4);
  flex-wrap: wrap;
}

.user-mgmt-toolbar .search-input {
  flex: 1;
  min-width: 200px;
}

.user-mgmt-table-wrap {
  overflow-x: auto;
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
}

.user-table th,
.user-table td {
  padding: var(--space-2) var(--space-3);
}

/* ─── Auth responsive ────────────────────────────────────── */
@media (max-width: 480px) {
  .auth-modal {
    max-width: 100%;
    margin: var(--space-2);
  }

  .user-mgmt-toolbar {
    flex-direction: column;
    align-items: stretch;
  }

  .user-mgmt-table-wrap {
    font-size: var(--text-xs);
  }
}

/* ═══════════════════════════════════════════════════════════
   NEW FEATURES CSS
   ═══════════════════════════════════════════════════════════ */

/* ─── Real-time Form Validation ─────────────────────────── */
.form-required {
  color: var(--color-error);
  font-weight: 600;
}

.form-validation-msg {
  display: block;
  font-size: var(--text-xs);
  min-height: 1.2em;
  margin-top: 2px;
  transition: color 0.2s;
}

.form-validation-msg.error {
  color: var(--color-error);
}

.form-validation-msg.valid {
  color: var(--color-warning);
}

.form-input.input-error {
  border-color: var(--color-error);
  box-shadow: 0 0 0 2px rgba(248, 113, 113, 0.15);
}

.form-input.input-valid {
  border-color: var(--color-success);
  box-shadow: 0 0 0 2px rgba(52, 211, 153, 0.1);
}

/* ─── Inline Table Editing ──────────────────────────────── */
td.inline-editing {
  padding: 0 !important;
  background: var(--color-bg) !important;
}

.inline-edit-input {
  width: 100%;
  height: 100%;
  padding: var(--space-2) var(--space-2);
  border: 2px solid var(--color-primary);
  border-radius: var(--radius-sm);
  background: var(--color-surface);
  color: var(--color-text);
  font-size: var(--text-sm);
  font-family: inherit;
  outline: none;
  box-shadow: 0 0 0 3px rgba(34, 211, 238, 0.15);
}

.inline-edit-input:focus {
  border-color: var(--color-primary);
}

/* Editable cell hint on hover */
.data-table tbody tr[data-id] td:nth-child(2),
.data-table tbody tr[data-id] td:nth-child(6),
.data-table tbody tr[data-id] td:nth-child(7) {
  cursor: text;
  position: relative;
}

.data-table tbody tr[data-id] td:nth-child(2):hover,
.data-table tbody tr[data-id] td:nth-child(6):hover,
.data-table tbody tr[data-id] td:nth-child(7):hover {
  outline: 1px dashed var(--color-divider);
  outline-offset: -1px;
  border-radius: var(--radius-sm);
}

/* ─── Batch Edit Modal ──────────────────────────────────── */
.batch-field {
  display: grid;
  grid-template-columns: 180px 1fr;
  gap: var(--space-3);
  align-items: center;
  margin-bottom: var(--space-3);
}

.batch-check-label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text);
  cursor: pointer;
  user-select: none;
}

.batch-check-label input[type="checkbox"] {
  accent-color: var(--color-primary);
  width: 16px;
  height: 16px;
}

.batch-field .form-input:disabled,
.batch-field .form-select:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* ─── Distance Measurement ──────────────────────────────── */
.distance-popup .leaflet-popup-content-wrapper {
  background: var(--color-surface, #1a1b23);
  border: 1px solid var(--color-divider, #2a2b35);
  border-radius: var(--radius-md, 8px);
  color: var(--color-text, #e1e3ea);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3);
}

.distance-popup .leaflet-popup-tip {
  background: var(--color-surface, #1a1b23);
  border: 1px solid var(--color-divider, #2a2b35);
}

.distance-label {
  text-align: center;
  padding: var(--space-1, 4px);
  font-size: var(--text-sm, 0.875rem);
  line-height: 1.4;
}

.distance-label strong {
  font-size: 1.1em;
  color: var(--color-primary, #22d3ee);
}

/* ─── Active state for topbar buttons ───────────────────── */
.topbar-btn.active {
  background: var(--color-primary);
  color: var(--color-bg);
  border-color: var(--color-primary);
}

/* ─── Area Filter Button ────────────────────────────────── */
.area-filter-btn {
  color: var(--color-text, #333);
}

.area-filter-btn.active {
  background: var(--color-primary, #22d3ee) !important;
  color: var(--color-bg, #fff) !important;
}

/* ─── Improved Export Cards Grid ────────────────────────── */
.ie-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: var(--space-4);
}

/* ─── Batch Edit Responsive ─────────────────────────────── */
@media (max-width: 600px) {
  .batch-field {
    grid-template-columns: 1fr;
    gap: var(--space-1);
  }
}

/* ═══════════════════════════════════════════════════════════
   NOTIFICATION SYSTEM
   ═══════════════════════════════════════════════════════════ */

/* ─── Notification Bell ─────────────────────────────────── */
.notification-bell {
  position: relative;
  background: transparent;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  padding: var(--space-2);
  cursor: pointer;
  color: var(--color-text-muted);
  transition: all 0.2s;
}

.notification-bell:hover {
  color: var(--color-primary);
  border-color: var(--color-primary);
  background: rgba(34, 211, 238, 0.05);
}

.notification-badge {
  position: absolute;
  top: -4px;
  right: -4px;
  background: var(--color-error);
  color: #fff;
  font-size: 0.65rem;
  font-weight: 700;
  min-width: 18px;
  height: 18px;
  padding: 0 4px;
  border-radius: 99px;
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  border: 2px solid var(--color-bg);
  animation: badgePulse 2s ease-in-out infinite;
}

@keyframes badgePulse {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.1); }
}

/* ─── Notification Panel (dropdown) ─────────────────────── */
.notification-panel {
  position: fixed;
  top: 52px;
  right: var(--space-4);
  width: 400px;
  max-height: 70vh;
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.35), 0 0 0 1px rgba(255, 255, 255, 0.03);
  z-index: 1100;
  display: none;
  flex-direction: column;
  overflow: hidden;
}

.notification-panel.open {
  display: flex;
  animation: panelSlideIn 0.2s ease-out;
}

@keyframes panelSlideIn {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: translateY(0); }
}

.notification-panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-divider);
}

.notification-panel-header h3 {
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text);
  margin: 0;
}

/* ─── Notification Summary Grid ─────────────────────────── */
.notification-panel-summary {
  padding: var(--space-3) var(--space-4);
  border-bottom: 1px solid var(--color-divider);
}

.notif-summary-grid {
  display: flex;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.notif-summary-item {
  display: flex;
  flex-direction: column;
  align-items: center;
  flex: 1;
  min-width: 60px;
  padding: var(--space-2) var(--space-2);
  border-radius: var(--radius-md);
  background: var(--color-bg);
}

.notif-summary-item.added { border-left: 3px solid var(--color-success); }
.notif-summary-item.updated { border-left: 3px solid var(--color-primary); }
.notif-summary-item.deleted { border-left: 3px solid var(--color-error); }
.notif-summary-item.transitions { border-left: 3px solid var(--color-warning); }

.notif-summary-num {
  font-size: var(--text-lg);
  font-weight: 700;
  font-feature-settings: 'tnum' 1;
  line-height: 1;
}

.notif-summary-item.added .notif-summary-num { color: var(--color-success); }
.notif-summary-item.updated .notif-summary-num { color: var(--color-primary); }
.notif-summary-item.deleted .notif-summary-num { color: var(--color-error); }
.notif-summary-item.transitions .notif-summary-num { color: var(--color-warning); }

.notif-summary-label {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin-top: 2px;
}

.notif-since {
  font-size: var(--text-xs);
  color: var(--color-text-faint);
  margin-top: var(--space-2);
}

/* ─── Notification List ─────────────────────────────────── */
.notification-panel-list {
  overflow-y: auto;
  max-height: 350px;
  padding: var(--space-2) 0;
}

.notif-item {
  display: flex;
  gap: var(--space-3);
  padding: var(--space-2) var(--space-4);
  transition: background 0.15s;
}

.notif-item:hover {
  background: var(--color-bg);
}

.notif-item-icon {
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin-top: 2px;
}

.notif-created .notif-item-icon,
.notif-imported .notif-item-icon { background: rgba(52, 211, 153, 0.12); color: var(--color-success); }
.notif-updated .notif-item-icon { background: rgba(34, 211, 238, 0.12); color: var(--color-primary); }
.notif-deleted .notif-item-icon { background: rgba(248, 113, 113, 0.12); color: var(--color-error); }
.notif-restored .notif-item-icon { background: rgba(96, 165, 250, 0.12); color: var(--color-info); }

.notif-item-content {
  flex: 1;
  min-width: 0;
}

.notif-item-title {
  font-size: var(--text-sm);
  font-weight: 500;
  color: var(--color-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.notif-item-meta {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin-top: 1px;
}

.notif-action-label {
  font-weight: 600;
}

.notif-created .notif-action-label,
.notif-imported .notif-action-label { color: var(--color-success); }
.notif-updated .notif-action-label { color: var(--color-primary); }
.notif-deleted .notif-action-label { color: var(--color-error); }
.notif-restored .notif-action-label { color: var(--color-info); }

.notif-time {
  float: right;
  color: var(--color-text-faint);
}

.notif-change-fields {
  display: block;
  font-size: 0.7rem;
  color: var(--color-text-faint);
  margin-top: 2px;
  font-style: italic;
}

.notif-empty {
  padding: var(--space-6);
  text-align: center;
  color: var(--color-text-faint);
  font-size: var(--text-sm);
}

.notif-more {
  padding: var(--space-2) var(--space-4);
  text-align: center;
  color: var(--color-text-faint);
  font-size: var(--text-xs);
  font-style: italic;
}

/* ─── Dashboard Change Banner ───────────────────────────── */
.change-banner {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
  background: linear-gradient(135deg, rgba(34, 211, 238, 0.06) 0%, rgba(167, 139, 250, 0.06) 100%);
  border: 1px solid rgba(34, 211, 238, 0.15);
  border-radius: var(--radius-lg);
  position: relative;
  animation: bannerSlideIn 0.35s ease-out;
}

@keyframes bannerSlideIn {
  from { opacity: 0; transform: translateY(-12px); }
  to { opacity: 1; transform: translateY(0); }
}

.change-banner-icon {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: rgba(34, 211, 238, 0.1);
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--color-primary);
  margin-top: 2px;
}

.change-banner-content {
  flex: 1;
}

.change-banner-content strong {
  font-size: var(--text-sm);
  color: var(--color-text);
  display: block;
  margin-bottom: var(--space-1);
}

.change-banner-content p {
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  margin: 0;
  display: flex;
  gap: 0;
  flex-wrap: wrap;
  align-items: center;
}

.change-stat {
  font-weight: 600;
  font-feature-settings: 'tnum' 1;
}

.change-stat.added { color: var(--color-success); }
.change-stat.updated { color: var(--color-primary); }
.change-stat.deleted { color: var(--color-error); }
.change-stat.transitions { color: var(--color-warning); }

.change-stat-sep {
  color: var(--color-text-faint);
  margin: 0 var(--space-1);
}

.change-banner-close {
  position: absolute;
  top: var(--space-2);
  right: var(--space-2);
  background: none;
  border: none;
  color: var(--color-text-faint);
  cursor: pointer;
  padding: var(--space-1);
  border-radius: var(--radius-sm);
  transition: all 0.15s;
}

.change-banner-close:hover {
  color: var(--color-text);
  background: var(--color-bg);
}

/* ─── Panel Responsive ──────────────────────────────────── */
@media (max-width: 480px) {
  .notification-panel {
    width: calc(100vw - var(--space-4));
    right: var(--space-2);
    max-height: 60vh;
  }

  .notif-summary-grid {
    gap: var(--space-2);
  }
}

/* ═══════════════════════════════════════════════════════════
   WORKFLOW, GEOMETRY & TEMPORARY OBSTACLE CSS
   ═══════════════════════════════════════════════════════════ */

/* ─── Status Badge (table cell) ─────────────────────────── */
.status-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: var(--text-xs);
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 99px;
  color: var(--status-color, #8b92a8);
  background: color-mix(in srgb, var(--status-color, #8b92a8) 12%, transparent);
  white-space: nowrap;
}

/* A value the form shows but does not offer to change. Sits at the same
   height as a .form-select so the row does not jump when it replaces one. */
.form-static {
  display: flex;
  align-items: center;
  min-height: 38px;
}

/* ─── Status Filter Chips ───────────────────────────────── */
/* The status chips lived here. They are a segment inside the filter panel now
   (.of-seg in #fStatus), and --chip-color went with them: it was set inline on
   each chip, so every var(--chip-color, ...) left behind named a property
   nothing declared. */

/* ─── Workflow Action Buttons ───────────────────────────── */
.workflow-btn.submit { color: var(--color-warning) !important; }
.workflow-btn.approve { color: var(--color-primary) !important; }
.workflow-btn.reject { color: var(--color-error) !important; }
.workflow-btn.publish { color: var(--color-success) !important; }
.workflow-btn.withdraw { color: var(--color-error) !important; }
.workflow-btn.reactivate { color: var(--color-info) !important; }

.workflow-btn:hover {
  transform: scale(1.15);
}

/* ─── Geometry Indicator ────────────────────────────────── */
.geom-indicator {
  display: inline-flex;
  align-items: center;
  color: var(--color-text-faint);
  margin-left: 4px;
  vertical-align: middle;
}

/* ─── Temporary Obstacle Badges ─────────────────────────── */
.temp-badge {
  display: inline-block;
  font-size: 0.6rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  padding: 1px 5px;
  border-radius: 3px;
  margin-left: 4px;
  vertical-align: middle;
}

.temp-badge.active {
  background: rgba(251, 146, 44, 0.15);
  color: #fb923c;
}

.temp-badge.expired {
  background: rgba(248, 113, 113, 0.15);
  color: #f87171;
  text-decoration: line-through;
}

/* ─── Expired Row Styling ───────────────────────────────── */
.row-expired td {
  opacity: 0.5;
}

.row-expired td:first-child {
  opacity: 1;
}

/* ─── Form Section Divider ──────────────────────────────── */
.form-section-divider {
  font-size: var(--text-xs);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-text-faint);
  padding: var(--space-2) 0;
  margin-top: var(--space-2);
  border-top: 1px solid var(--color-divider);
}

/* ─── Temp Date Fields ──────────────────────────────────── */
.temp-date-group .form-input[type="date"] {
  color-scheme: dark;
}

[data-theme="light"] .temp-date-group .form-input[type="date"] {
  color-scheme: light;
}

/* ─── Dashboard KPI Grid (8 cards) ──────────────────────── */
/* THE WHOLE LADDER IN ONE PLACE (2.72.0): how many cards across at every
   window width, chosen so that a card is never narrower than its texts.
   MEASURED in Chromium 07.10.2026: a card needs about 216 px on a desktop
   ("1848.5 m" at 36 px, "1 expired · 1 not yet in force") and 205 px on a
   tablet. The auto-fill grid that stood here gave 171–189 px from 1200 to
   1920 px windows — 7 + 1 cards at Vedran's 1613 px — and the texts broke in
   two (2.71.1) or, once they were kept on one line, were cut; at 769 px three
   1fr columns grew past the window and pushed Max Elevation off the screen.
   minmax(0, 1fr): a column never grows wider than its share, whatever is in
   it; the ellipsis on .kpi-label/.kpi-value/.kpi-sub is the last guard.
   Widths below are the WINDOW: the sidebar is 240 px from 1025 px, 200 px
   from 769 px, and gone below that. */
.kpi-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-3);
}
@media (min-width: 702px) and (max-width: 768px) {   /* no sidebar: 3 × 209–232 px */
  .kpi-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 902px) and (max-width: 1199px) {  /* 3 × 210–295 px */
  .kpi-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1200px) {                          /* 4 × 219 px and up: two even rows */
  .kpi-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
/* A phone leaves 120–170 px for a text, two cards across: there a label or a
   sub-line may take two lines rather than lose its end (MEASURED 07.10.2026:
   "1 expired · 1 not yet in force" cut at 390 px, "Temporary Active" at 360).
   The figure itself stays on one line. */
@media (max-width: 480px) {
  .kpi-label, .kpi-sub { white-space: normal; overflow: visible; text-overflow: clip; }
}

/* ─── UID loading spinner ────────────────────────────────── */
@keyframes spin {
  to { transform: rotate(360deg); }
}

/* ─── Password show/hide toggle ─────────────────────────── */
.input-pw-wrap {
  position: relative;
  display: flex;
  align-items: center;
}

.input-pw-wrap .form-input {
  width: 100%;
  padding-right: 40px;
}

.pw-toggle-btn {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  width: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--color-text-faint);
  border-radius: 0 var(--radius-md) var(--radius-md) 0;
  transition: color 0.15s;
  flex-shrink: 0;
}

.pw-toggle-btn:hover {
  color: var(--color-text-muted);
}

.pw-toggle-btn:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: -2px;
}

/* ============================================================
   MOBILE & TABLET OPTIMISATION
   Breakpoints:
     ≤640px  — phones (portrait)
     641–768px — phones (landscape) / small tablets
     769–1024px — tablets
   ============================================================ */

/* ─── 1. Topbar — collapse text labels on small screens ─── */
@media (max-width: 640px) {
  .topbar {
    padding: 0 var(--space-3);
  }

  /* Hide text, keep icon on Add Obstacle button */
  .topbar-btn span,
  .topbar-btn-label {
    display: none;
  }

  /* Make topbar buttons icon-only squares */
  .topbar-btn {
    padding: var(--space-2);
    min-width: 36px;
    justify-content: center;
  }

  /* Keep primary (Add) button slightly wider with a + icon */
  #addObstacleBtn {
    width: 36px;
    height: 36px;
    padding: 0;
    border-radius: var(--radius-md);
  }

  .topbar-right {
    gap: var(--space-2);
  }

  .topbar-title {
    font-size: var(--text-base);
  }
}

/* ─── 2. Map + table split view on mobile ──────────────────
   On phones: map takes ~45% of screen, table below.
   Resize handle hidden (no mouse to drag it).
   On tablets portrait: map 40%, table 60%.           ────── */
@media (max-width: 768px) {
  .map-table-view {
    display: flex;
    flex-direction: column;
    height: 100%;
    overflow: hidden;
  }

  .map-container {
    flex: 0 0 42%;
    min-height: 200px;
    max-height: 320px;
  }

  .resize-handle {
    display: none;
  }

  .table-panel {
    flex: 1 1 auto;
    height: auto;
    min-height: 0;
    overflow: hidden;
  }
}

@media (max-width: 480px) {
  .map-container {
    flex: 0 0 38%;
    max-height: 240px;
  }
}

/* ─── 3. Mobile card — enrich with UID + status ────────── */
@media (max-width: 640px) {
  .mobile-card {
    padding: var(--space-3);
  }

  .mobile-card-header {
    flex-wrap: wrap;
    gap: var(--space-1);
    margin-bottom: var(--space-2);
  }

  .mobile-card-name {
    font-size: var(--text-sm);
    flex: 1 1 100%;
  }

  /* Action buttons — full width, easier to tap */
  .mobile-card-actions {
    gap: var(--space-2);
    flex-wrap: wrap;
  }

  .mobile-card-actions .row-action-btn {
    flex: 1 1 calc(50% - var(--space-1));
    min-width: 0;
    height: 38px;
    font-size: var(--text-xs);
    border-radius: var(--radius-md);
  }

  /* Minimum tap target — 44px per Apple HIG / Material */
  .row-action-btn,
  .topbar-icon-btn,
  .pw-toggle-btn {
    min-height: 44px;
    min-width: 44px;
  }
}

/* ─── 4. Table toolbar wrapping on tablets ─────────────── */
@media (max-width: 900px) {
  .table-toolbar {
    flex-direction: column;
    align-items: stretch;
    gap: var(--space-2);
    padding: var(--space-2) var(--space-3);
  }

  .table-toolbar-left,
  .table-toolbar-right {
    width: 100%;
    display: flex;
    flex-wrap: wrap;
    gap: var(--space-2);
    align-items: center;
  }

  .search-input {
    flex: 1;
    min-width: 0;
  }

  #filterChips {
    flex-wrap: wrap;
  }

  .obstacle-count {
    margin-left: auto;
    white-space: nowrap;
  }
}

/* ─── 5. Dashboard — single column on phones ───────────── */
@media (max-width: 640px) {
  /* Every pair stacked. This rule used to say `.dashboard-view > div:nth-child(2)`,
     which is the KPI wrapper — the change banner is the first div — so the
     chart and the map never stacked (2.72.0). */
  .dashboard-pair {
    grid-template-columns: minmax(0, 1fr) !important;
    gap: var(--space-3) !important;
  }

  .dashboard-map-wrap {
    height: 200px;
  }

  #dashboardMap {
    height: 200px !important;
  }

  /* KPI grid: 2 columns on phone (the ladder is under "Dashboard KPI Grid") */
  .kpi-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: var(--space-2);
  }

  .kpi-card {
    padding: var(--space-3);
  }

  /* Hide recent table on very small screens — too wide */
  .recent-table-wrap {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }
}

/* ─── 6. Modal — full screen on phones ─────────────────── */
@media (max-width: 640px) {
  .modal-overlay {
    padding: 0;
    align-items: flex-end;
  }

  .modal {
    width: 100%;
    max-width: 100%;
    max-height: 92dvh;
    border-radius: var(--radius-lg) var(--radius-lg) 0 0;
    margin: 0;
  }

  .history-modal {
    max-height: 92dvh;
  }

  .modal-footer {
    padding-bottom: env(safe-area-inset-bottom, 12px);
  }

  /* Form inputs — easier to tap */
  .form-input,
  .form-select,
  .form-textarea {
    font-size: 16px; /* prevents iOS auto-zoom on focus */
    min-height: 44px;
  }

  .form-textarea {
    min-height: auto;
  }
}

/* ─── 7. Sidebar — swipe-friendly on touch ─────────────── */
@media (max-width: 768px) {
  .sidebar {
    /* Already slides in via JS, just make it feel native */
    -webkit-overflow-scrolling: touch;
    overscroll-behavior: contain;
  }

  .sidebar-nav-item {
    min-height: 44px;
    padding: var(--space-3) var(--space-5);
  }
}

/* ─── 8. Scroll helpers — prevent rubber-band bleed ────── */
@media (max-width: 768px) {
  .main-content {
    overscroll-behavior: contain;
    -webkit-overflow-scrolling: touch;
  }

  .mobile-cards {
    overscroll-behavior-y: contain;
    -webkit-overflow-scrolling: touch;
    /* Extra bottom padding for home indicator (iOS) */
    padding-bottom: calc(var(--space-3) + env(safe-area-inset-bottom, 0px));
  }

  .table-panel {
    overscroll-behavior: contain;
  }
}

/* ─── 9. Leaflet map controls — bigger tap targets ─────── */
@media (max-width: 768px) {
  .leaflet-control-zoom a {
    width: 36px !important;
    height: 36px !important;
    line-height: 36px !important;
    font-size: 18px !important;
  }

  .leaflet-control-zoom {
    margin-bottom: 12px !important;
    margin-left: 12px !important;
  }

  /* Map attribution — collapse on mobile */
  .leaflet-control-attribution {
    font-size: 9px !important;
  }
}

/* ─── 10. Notification panel — full width on phone ─────── */
@media (max-width: 640px) {
  .notification-panel {
    left: 0 !important;
    right: 0 !important;
    width: auto !important;
    max-height: 60dvh;
    border-radius: 0 0 var(--radius-lg) var(--radius-lg);
  }
}

/* ─── 11. Confirm dialog — full width on phone ─────────── */
@media (max-width: 480px) {
  .confirm-box {
    width: 94%;
    padding: var(--space-4);
  }
}

/* ─── 12. History modal revision entries — readable ────── */
@media (max-width: 480px) {
  .revision-entry {
    gap: var(--space-2);
  }

  .revision-header {
    flex-direction: column;
    align-items: flex-start;
    gap: 2px;
  }

  .revision-timestamp {
    font-size: 10px;
  }

  .revision-change-row {
    flex-wrap: wrap;
    gap: 4px;
  }

  .revision-field-label {
    width: 100%;
    margin-bottom: 2px;
  }
}

/* ─── 13. Auth modal — centered, full width on phone ────── */
@media (max-width: 480px) {
  .auth-modal {
    margin: var(--space-3);
    width: calc(100% - 2 * var(--space-3));
  }

  .auth-modal-header {
    padding: var(--space-4) var(--space-4) var(--space-2);
  }
}

/* ─── 14. Tablet landscape — keep sidebar, shrink table ── */
@media (min-width: 769px) and (max-width: 1024px) {
  .map-container {
    min-height: 250px;
  }

  .table-panel {
    height: 280px;
  }

  .form-grid {
    grid-template-columns: 1fr 1fr;
  }

  /* The KPI columns are no longer set here: three 1fr columns at 769 px grew
     past the window (2.72.0). See "Dashboard KPI Grid". */
}

/* ─── 15. Safe area insets (notch, home indicator) ─────── */
@supports (padding: env(safe-area-inset-bottom)) {
  @media (max-width: 768px) {
    .topbar {
      padding-left: max(var(--space-3), env(safe-area-inset-left));
      padding-right: max(var(--space-3), env(safe-area-inset-right));
    }

    .sidebar {
      padding-bottom: env(safe-area-inset-bottom);
    }
  }
}


/* ============================================================
   Coordinate Format — DDM/DMS sub-inputs
   ============================================================ */
#coordDDM > div, #coordDDM2 > div,
#coordDMS > div, #coordDMS2 > div {
  flex-wrap: wrap;
}

#coordDDM .form-input, #coordDDM2 .form-input,
#coordDMS .form-input, #coordDMS2 .form-input,
#coordDDM .form-select, #coordDDM2 .form-select,
#coordDMS .form-select, #coordDMS2 .form-select {
  min-width: 0;
}

/* Remove number input spinners to save space */
#coordDDM input[type="number"], #coordDDM2 input[type="number"],
#coordDMS input[type="number"], #coordDMS2 input[type="number"] {
  -moz-appearance: textfield;
}
#coordDDM input[type="number"]::-webkit-inner-spin-button,
#coordDDM2 input[type="number"]::-webkit-inner-spin-button,
#coordDMS input[type="number"]::-webkit-inner-spin-button,
#coordDMS2 input[type="number"]::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

/* ============================================================
   Pagination Controls
   ============================================================ */
.pagination-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-3) var(--space-4);
  border-top: 1px solid var(--color-divider);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  gap: var(--space-3);
  flex-wrap: wrap;
}

.pagination-bar select {
  background: var(--color-bg);
  color: var(--color-text);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  padding: 2px 6px;
  font-size: var(--text-xs);
}

.pagination-pages {
  display: flex;
  align-items: center;
  gap: 2px;
}

.pagination-pages button {
  background: none;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  padding: 2px 8px;
  cursor: pointer;
  font-size: var(--text-xs);
  min-width: 28px;
  text-align: center;
}

.pagination-pages button:hover {
  background: var(--color-surface-offset);
  color: var(--color-text);
}

.pagination-pages button.active {
  background: var(--color-primary);
  color: var(--color-text-inverse);
  border-color: var(--color-primary);
}

.pagination-pages button:disabled {
  opacity: 0.4;
  cursor: not-allowed;
}

/* ─── Pagination bar ─────────────────────────────────── */
.pagination-bar {
  flex-shrink: 0;
}
.pagination-bar .btn:disabled {
  opacity: 0.4;
  pointer-events: none;
}

/* ─── DDM/DMS coordinate inputs — prevent overflow ──── */
#coordDDM > div, #coordDDM2 > div,
#coordDMS > div, #coordDMS2 > div {
  flex-wrap: wrap;
}
#coordDDM .form-input, #coordDDM2 .form-input,
#coordDMS .form-input, #coordDMS2 .form-input {
  min-width: 50px;
}

/* ─── Polygon point editor ───────────────────────────── */
.poly-point-row .form-input {
  padding: 4px 8px;
  font-size: var(--text-sm);
}

@media (max-width: 480px) {
  .pagination-bar {
    flex-direction: column;
    gap: var(--space-2);
    align-items: stretch !important;
    text-align: center;
  }
  #paginationButtons {
    justify-content: center;
  }
}

/* ─── Row action dropdown menu ───────────────────────── */
/* The obstacle row's three-dot menu was removed on 09.09.2026 and its rules
   with it — .row-action-dropdown, .row-menu and the four button states. They
   were the second half of a fix made two days earlier: an opaque fill and the
   heavier border, because over a table of the same surface the card recipe
   measured 1.06:1 against the rows. The panel is gone, so the contrast problem
   is gone with it. --shadow-popover went too: it was introduced for this panel
   and this panel was its only user, in both themes. */

/* ============================================================
   PERSONNEL MANAGEMENT (v2.3.0)
   Annunciator stats · tabs · account drawer · security chips
   ============================================================ */

/* ─── Header + annunciator stats strip ─── */
.pm-header {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--space-4);
  flex-wrap: wrap;
  margin-bottom: var(--space-4);
}

.pm-sub {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

.pm-stats {
  display: flex;
  gap: var(--space-2);
  flex-wrap: wrap;
}

.pm-stat {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  min-width: 108px;
}

.pm-stat.warn {
  border-color: color-mix(in srgb, var(--color-warning) 45%, var(--color-divider));
  box-shadow: 0 0 12px color-mix(in srgb, var(--color-warning) 18%, transparent);
}

.pm-led {
  width: 7px; height: 7px;
  border-radius: 50%;
  flex-shrink: 0;
}
.led-cyan  { background: var(--color-primary); box-shadow: 0 0 6px var(--color-primary-glow); }
.led-green { background: var(--color-success); box-shadow: 0 0 6px color-mix(in srgb, var(--color-success) 40%, transparent); }
.led-amber { background: var(--color-warning); box-shadow: 0 0 6px color-mix(in srgb, var(--color-warning) 40%, transparent); }
.led-red   { background: var(--color-error);   box-shadow: 0 0 6px color-mix(in srgb, var(--color-error) 40%, transparent); }

.pm-stat-value {
  font-size: var(--text-lg);
  font-weight: 700;
  font-feature-settings: 'tnum' 1;
  line-height: 1;
  color: var(--color-text);
}

.pm-stat-label {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
}

/* ─── Tabs ─── */
.pm-tabs {
  display: flex;
  gap: var(--space-1);
  border-bottom: 1px solid var(--color-divider);
  margin-bottom: var(--space-4);
}

.pm-tab {
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-sm);
  font-weight: 600;
  color: var(--color-text-muted);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

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

.pm-tab.active {
  color: var(--color-primary);
  border-bottom-color: var(--color-primary);
}

/* ─── Toolbar refinements ─── */
.pm-filter { max-width: 170px; font-size: var(--text-xs); }
.pm-toolbar-spacer { flex: 1; }

/* ─── Users table cells ─── */
.pm-mono { font-family: var(--font-mono); }

.pm-row { cursor: pointer; }
.pm-row:hover td { background: var(--color-surface-2); }
.pm-row-inactive .pm-user-name,
.pm-row-inactive .pm-user-sub { opacity: 0.55; }

.pm-user-cell {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-width: 220px;
}

.pm-avatar {
  width: 32px; height: 32px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.02em;
  flex-shrink: 0;
  color: var(--role-color, var(--color-primary));
  background: color-mix(in srgb, var(--role-color, var(--color-primary)) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--role-color, var(--color-primary)) 35%, transparent);
}

.pm-avatar-lg { width: 44px; height: 44px; font-size: 14px; }

.pm-user-name { font-weight: 600; font-size: var(--text-sm); }
.pm-user-sub  { font-size: var(--text-xs); color: var(--color-text-muted); }

/* ─── Security chips ─── */
.pm-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 10.5px;
  font-weight: 600;
  padding: 2px 8px;
  border-radius: 999px;
  white-space: nowrap;
}
.chip-ok     { background: var(--color-success-dim); color: var(--color-success); }
.chip-warn   { background: var(--color-warning-dim); color: var(--color-warning); }
.chip-danger { background: var(--color-error-dim);   color: var(--color-error); }
.chip-muted  { background: var(--color-surface-3);   color: var(--color-text-muted); }

/* ─── Account drawer ─── */
.pm-drawer-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(10, 12, 18, 0.45);
  backdrop-filter: blur(2px);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--transition-slow);
  z-index: 1049;
}
.pm-drawer-backdrop.open { opacity: 1; pointer-events: auto; }

/* The obstacle drawer's backdrop does not dim and does not blur.
   The request was for details in a panel on the right INSTEAD OF a window that
   blurs everything behind it — and measured, this backdrop blurs by 2 px and
   darkens by 45%. Honouring the panel and keeping the blur would have answered
   half the complaint. It stays in the markup because it is what closes the
   drawer on a click outside; it just stops obscuring the list, which is the one
   thing you want to keep reading while a record is open.
   #userDrawer and #olsDrawer are untouched: there, the drawer IS the task. */
.pm-drawer-backdrop.see-through { background: transparent; backdrop-filter: none; }

/* And it does not swallow clicks either.
   Transparent was only half of it: the backdrop is fixed to the whole viewport
   and, while open, took every click on the page — so with a record open you
   could not click the NEXT row without closing the drawer first. Measured by
   driving two clicks in a browser; the second one never landed.
   A panel you can see past but not reach past is still a modal. This one is
   not: the list stays clickable, clicking another obstacle swaps the record,
   and the drawer closes on its × or on Escape. */
.pm-drawer-backdrop.see-through.open { pointer-events: none; }

.pm-drawer {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: min(400px, 94vw);
  background: var(--color-surface);
  border-left: 1px solid var(--color-border);
  box-shadow: var(--shadow-lg);
  transform: translateX(102%);
  transition: transform var(--transition-slow);
  /* 1050, not 61 — measured against what actually competes with it. At 61 the
     drawer slid in UNDER the map: head visible above the map's top edge, body
     hidden behind tiles. Two things were wrong, and both are fixed:
       1. .leaflet-container made no stacking context, so Leaflet's panes fought
          in the ROOT one (tiles 200 … controls 1000). See isolation: isolate
          there — that alone would already be enough for this drawer.
       2. 61 said nothing about where the drawer belongs. 1050 clears Leaflet's
          own 1000 and stays under the notification panel (1100), modals (9000+)
          and toasts (11000), so the order reads as intended, not as luck.
     The headless check could not see any of this: it used elementFromPoint, and
     Leaflet sets pointer-events: none on tiles — hit-testing walks straight
     through them while painting does not. Proven instead on the running app in
     Chrome, before and after. */
  z-index: 1050;
  display: flex;
  flex-direction: column;
}
.pm-drawer.open { transform: translateX(0); }

.pm-drawer-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-bottom: 1px solid var(--color-divider);
  background:
    repeating-linear-gradient(0deg,  transparent 0 23px, var(--color-primary-dim) 23px 24px),
    repeating-linear-gradient(90deg, transparent 0 23px, var(--color-primary-dim) 23px 24px),
    var(--color-surface-2);
}

.pm-drawer-id { display: flex; align-items: center; gap: var(--space-3); min-width: 0; }
.pm-drawer-name { font-weight: 700; font-size: var(--text-base); line-height: 1.2; }
.pm-drawer-username { font-size: var(--text-xs); color: var(--color-text-muted); }

.pm-drawer-body {
  padding: var(--space-4) var(--space-5);
  overflow-y: auto;
  flex: 1;
}

.pm-drawer-badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin-bottom: var(--space-4);
}

.pm-meta-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-3) var(--space-4);
  margin-bottom: var(--space-5);
}

.pm-meta-item:first-child { grid-column: 1 / -1; }
.pm-meta-k {
  font-size: 10px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-faint);
  margin-bottom: 2px;
}
.pm-meta-v { font-size: var(--text-sm); color: var(--color-text); overflow-wrap: anywhere; }

.pm-drawer-section-label {
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.08em;
  color: var(--color-text-faint);
  margin: var(--space-4) 0 var(--space-2);
  padding-top: var(--space-3);
  border-top: 1px solid var(--color-divider);
}

.pm-actions {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-2);
}

.pm-action-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  background: var(--color-surface-2);
  color: var(--color-text-muted);
  font-size: var(--text-xs);
  font-weight: 600;
  text-align: left;
}
.pm-action-btn:hover { background: var(--color-surface-3); color: var(--color-text); }
.pm-action-btn.warn   { color: var(--color-warning); border-color: color-mix(in srgb, var(--color-warning) 40%, var(--color-border)); }
.pm-action-btn.danger { color: var(--color-error);   border-color: color-mix(in srgb, var(--color-error) 40%, var(--color-border)); }
.pm-action-btn.danger:hover { background: var(--color-error-dim); }

.pm-temp-form { grid-column: 1 / -1; }
.pm-temp-actions {
  display: flex;
  gap: var(--space-2);
  justify-content: flex-end;
  margin-top: var(--space-3);
}

/* ─── Drawer event feed ─── */
.pm-events { display: flex; flex-direction: column; gap: var(--space-2); }

.pm-event {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-2) var(--space-3);
  background: var(--color-surface-2);
  border-radius: var(--radius-md);
  border: 1px solid var(--color-divider);
}

.pm-event-dot {
  width: 7px; height: 7px;
  border-radius: 50%;
  margin-top: 5px;
  flex-shrink: 0;
}
.tone-ok      { background: var(--color-success); }
.tone-warn    { background: var(--color-warning); }
.tone-danger  { background: var(--color-error); }
.tone-neutral { background: var(--color-text-faint); }

.pm-event-type { font-size: var(--text-xs); font-weight: 600; }
.pm-event-meta { font-size: 10.5px; color: var(--color-text-faint); }

.pm-events-empty {
  font-size: var(--text-xs);
  color: var(--color-text-faint);
  padding: var(--space-3);
  text-align: center;
}

/* ─── Provisioning radios ─── */
.pm-radio {
  display: flex;
  align-items: flex-start;
  gap: var(--space-2);
  padding: var(--space-2) 0;
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  cursor: pointer;
}
.pm-radio input { margin-top: 2px; accent-color: var(--color-primary); }
.pm-radio strong { color: var(--color-text); }

/* ─── Forced password modal ─── */
.pm-forced { z-index: 10500; }
.pm-forced-note {
  font-size: var(--text-xs);
  color: var(--color-warning);
  background: var(--color-warning-dim);
  border: 1px solid color-mix(in srgb, var(--color-warning) 30%, transparent);
  border-radius: var(--radius-md);
  padding: var(--space-2) var(--space-3);
  margin-bottom: var(--space-4);
}

/* ─── Licence agreement (2.75.0, decision 35) ─── */
.modal.pm-eula { max-width: 820px; }
.pm-eula .modal-header { gap: var(--space-3); }
.pm-eula-lang { display: flex; gap: var(--space-1); margin-left: auto; }
.pm-eula-lang .btn[aria-pressed="true"] {
  border-color: var(--color-primary);
  color: var(--color-primary);
  background: var(--color-primary-dim);
}
.pm-eula .modal-body { display: flex; flex-direction: column; gap: var(--space-2); min-height: 0; }
.pm-eula-text {
  flex: 1 1 auto;
  min-height: 0;
  max-height: 60vh;
  overflow-y: auto;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
  padding: var(--space-3) var(--space-4);
  font-size: var(--text-sm);
  line-height: 1.55;
  color: var(--color-text);
}
.pm-eula-text h2 { font-size: var(--text-base); margin: 0 0 var(--space-2); }
.pm-eula-text h3 { font-size: var(--text-sm); margin: var(--space-4) 0 var(--space-2); }
.pm-eula-text p { margin: 0 0 var(--space-2); }
.pm-eula-text ul { margin: 0 0 var(--space-2); padding-left: var(--space-5); }
.pm-eula-text li { margin: 0 0 2px; }
.pm-legal-line {
  font-size: 10.5px;
  color: var(--color-text-muted);
  opacity: 0.75;
  margin-top: 4px;
  letter-spacing: 0.02em;
}
.pm-legal-link {
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  color: var(--color-primary);
  cursor: pointer;
}
.pm-legal-link:hover { text-decoration: underline; }

/* ─── Auth overlay additions ─── */
.auth-forgot {
  text-align: center;
  margin-top: var(--space-3);
}

.auth-success {
  background: var(--color-success-dim);
  border: 1px solid color-mix(in srgb, var(--color-success) 30%, transparent);
  color: var(--color-success);
  font-size: var(--text-xs);
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-md);
  margin-bottom: var(--space-4);
}

/* ─── Responsive ─── */
@media (max-width: 720px) {
  .pm-header { align-items: flex-start; flex-direction: column; }
  .pm-stats { width: 100%; }
  .pm-stat { flex: 1; min-width: 0; }
  .pm-filter { max-width: none; flex: 1; }
  .pm-actions { grid-template-columns: 1fr; }
  .pm-meta-grid { grid-template-columns: 1fr; }
}

/* ─── Modal mini-map (pick-on-map, v2.4.0) ─────────────────── */
.modal-map {
  height: 260px;
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

@media (max-width: 768px) {
  .modal-map { height: 200px; }
}

/* ─── Cross-field warnings (v2.4.0 item 3) ─────────────────── */
.form-warning-box {
  background: color-mix(in srgb, var(--color-warning) 12%, transparent);
  border: 1px solid var(--color-warning);
  border-radius: var(--radius-lg);
  padding: var(--space-2) var(--space-3);
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.form-warning-item {
  color: var(--color-warning);
  font-size: var(--text-sm);
  line-height: 1.4;
}

/* ─── Organization combobox (v2.4.0 Wave A UX debt) ────────── */
.org-combo { position: relative; }

.org-combo-panel {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  max-height: 220px;
  overflow-y: auto;
  background: var(--color-surface);
  border: 1px solid var(--color-divider);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  z-index: 30;
}

.org-combo-item {
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-text);
  cursor: pointer;
}

.org-combo-item:hover,
.org-combo-item.active {
  background: var(--color-primary-dim);
}

.org-combo-create {
  color: var(--color-primary);
  border-top: 1px solid var(--color-divider);
  font-weight: 500;
}

.org-combo-empty {
  padding: var(--space-2) var(--space-3);
  font-size: var(--text-sm);
  color: var(--color-text-faint);
}

/* --- OLS analysis: list beside a map --- */
/* Counting how many obstacles sit inside an aerodrome's reach says nothing
   about which ones, or where; the list names them and the map places them. */
/* The list gets the room the table needs; the MAP is what gives way. Beside
   the list while both fit, underneath it when they do not - and underneath it
   the list is as wide as the dialog, which is the only width at which the five
   columns fit a laptop. The two flex-basis values decide where that line
   falls; nothing breaks if content moves it.

   Measured 16.09.2026 in Chrome on staging, with the real fonts loaded: the
   five columns want 752 px to stay on one line each, and can be squeezed to
   498 px before anything is hidden. */
.ols-analyse-split {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4);
  margin-top: var(--space-3);
  /* so the rules below can ask how wide THIS SPLIT is rather than how wide the
     window is: the same window holds this dialog at 909 px and at 1400 px, and
     a media query cannot tell those apart. */
  container-type: inline-size;
}

.ols-analyse-list { flex: 1 1 760px; min-width: 0; max-height: 52vh; overflow: auto; }

.ols-analyse-tabs {
  display: flex;
  gap: var(--space-2);
  margin-bottom: var(--space-2);
  position: sticky;
  top: 0;
  background: var(--color-surface);
  padding-bottom: 4px;
  z-index: 1;
}

.ols-tab {
  padding: 5px 12px;
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  color: var(--color-text-muted);
  font-size: var(--text-sm);
  font-weight: 600;
  cursor: pointer;
}

.ols-tab.active {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: #fff;
}

/* --- OLS analysis: surfaces chosen before the run, depth filter after (v2.70.0) --- */
/* The tabs row also carries the minimum-penetration field; on a narrow list it
   drops to its own line rather than pushing the tabs out of view. */
.ols-analyse-tabs { flex-wrap: wrap; align-items: center; }
.ols-an-minpen {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--text-sm);
  color: var(--color-text-muted);
  white-space: nowrap;
}
.ols-an-minpen input {
  width: 64px;
  padding: 3px 6px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  color: var(--color-text);
  font: inherit;
  text-align: right;
}
.ols-an-minpen input.invalid { border-color: var(--color-error); }

.ols-an-surfaces {
  margin-top: 10px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 6px 10px;
  font-size: var(--text-sm);
}
.ols-an-surfaces > summary { cursor: pointer; }
.ols-an-surfaces-title { font-weight: 600; }
#olsAnalyseSurfacesSummary.ols-an-error { color: var(--color-error); }
.ols-an-surfaces-body { margin-top: 8px; }
.ols-an-group-head {
  margin: 8px 0 4px;
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
}
.ols-an-group-head:first-child { margin-top: 0; }
.ols-an-link {
  margin-left: 6px;
  padding: 0;
  border: 0;
  background: none;
  color: var(--color-primary);
  font: inherit;
  text-transform: none;
  letter-spacing: 0;
  font-weight: 500;
  cursor: pointer;
}
.ols-an-row { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 14px; padding: 2px 0; }
.ols-an-rw { min-width: 150px; color: var(--color-text-muted); font-size: var(--text-xs); }
.ols-an-check { display: inline-flex; align-items: center; gap: 6px; cursor: pointer; }
.ols-an-swatch { width: 9px; height: 9px; border-radius: 2px; flex: none; }
.ols-an-note { margin-top: 4px; font-size: var(--text-xs); color: var(--color-text-muted); }

.ols-analyse-map { flex: 1 1 360px; position: relative; min-width: 0; }

#olsAnalyseMap {
  width: 100%;
  height: 52vh;
  border-radius: var(--radius-md);
  border: 1px solid var(--color-border);
}

.ols-analyse-map-hint {
  position: absolute;
  left: 10px;
  bottom: 10px;
  padding: 5px 10px;
  border-radius: var(--radius-md);
  background: rgba(17, 24, 39, 0.82);
  color: #fff;
  font-size: var(--text-xs);
  pointer-events: none;
}

.ols-analyse-row { cursor: pointer; }
.ols-analyse-row:hover { background: var(--color-surface-offset); }
.ols-analyse-row.selected { background: var(--color-primary-dim, #dbeafe); }

/* THE LIST IS A .data-table, AND .data-table CARRIES min-width:900px.

   Written for the obstacle table and its eighteen columns; inherited here by a
   list that had 477 px of a 909 px dialog. 424 px of every row - Worst and
   Surface entire, and the right half of Top - sat behind a horizontal
   scrollbar. Reported 16.09.2026 with a screenshot of the scrollbar; measured
   in Chrome on staging before and after (477 of 900 shown, then 890 of 890).

   SECOND TIME. #cadTokenTableWrap above is the first, 23.08.2026, same cause
   and same fix. Nothing in 81 suites saw either one: they read markup and
   state, and a column pushed out of view is neither.

   The cells may also wrap, which .data-table forbids for the obstacle table.
   The name cell may break inside a word as a last resort - unlike the CAD
   dialog, where `anywhere` split "Administrator" in a column that was always
   narrow. Here the column is roomy at every width this dialog is used at, and
   the break can only happen below ~500 px of list, where the choice is a
   broken word or a hidden column. A hidden column is worse. */
#olsAnalyseResults .data-table { min-width: 0; width: 100%; }
#olsAnalyseResults th,
#olsAnalyseResults td { white-space: normal; overflow-wrap: break-word; }
#olsAnalyseResults td:nth-child(2) { overflow-wrap: anywhere; }

/* Stacked: the map has dropped below the list and is now as wide as the whole
   dialog. It does not need half the dialog's height as well, and at 52vh the
   list above it would push it off the bottom.

   1136 px is not a guess and not a screen size: it is 760 + 360 + the 16 px
   gap, the exact width at which the two stop fitting side by side. `not
   (min-width: ...)` rather than `max-width: 1135px` so the two conditions are
   the same condition, fractional widths included. */
@container not (min-width: 1136px) {
  .ols-analyse-list { max-height: 34vh; }
  #olsAnalyseMap { height: 30vh; min-height: 240px; }
}

/* --- Import: error detail panel --- */
/* A count of failures is not actionable on its own. Opening it shows which
   rows were refused, on which field and with what value - the three things
   needed to fix the source file. */
.import-with-panel {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: var(--space-3);
  align-items: start;
}

button.import-summary-item {
  font: inherit;
  border: 1px solid transparent;
  cursor: default;
}

button.import-summary-item.clickable { cursor: pointer; }
button.import-summary-item.clickable:hover { border-color: var(--color-error); }
button.import-summary-item.open { border-color: var(--color-error); }
button.import-summary-item[disabled] { opacity: 0.55; }

.import-error-panel {
  width: 340px;
  max-height: 46vh;
  overflow: auto;
  border: 1px solid var(--color-error);
  border-radius: var(--radius-md);
  background: var(--color-surface);
}

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

.import-error-panel-head {
  position: sticky;
  top: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 8px 12px;
  background: var(--color-error);
  color: #fff;
  font-size: var(--text-sm);
  font-weight: 700;
}

.import-error-panel-close {
  background: none;
  border: none;
  color: #fff;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  padding: 0 2px;
}

.import-error-panel-body { padding: 10px 12px; }

.import-error-entry {
  padding: 8px 0;
  border-bottom: 1px solid var(--color-divider, #e5e7eb);
}

.import-error-entry:last-child { border-bottom: none; }

.import-error-entry-row {
  font-size: var(--text-xs);
  font-weight: 700;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin-bottom: 3px;
}

.import-error-entry-msg { font-size: var(--text-sm); line-height: 1.4; }

.import-error-entry-val {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin-top: 2px;
}

@media (max-width: 1000px) {
  .import-with-panel { grid-template-columns: 1fr; }
  .import-error-panel { width: auto; }
}

/* --- Sidebar map settings --- */
/* These used to float over the map in the same corner as the OLS panel, where
   one covered the other. The sidebar has the room and they are settings, not
   map furniture. */
/* LARGER SINCE 2.75.0. Vedran, 21.10.2026: the options under Map and Label
   shows "su dosta male i teško čitljive". They were 12.5 px, their group
   headings 10.5 px and the two sub-headings 10 px, beside navigation links in
   var(--text-sm); the options now read at that same size, and the
   sub-headings at the size of the section headings above them. */
.sidebar-check {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 0 4px 2px;
  font-size: var(--text-sm);
  line-height: 1.35;
  color: var(--color-text-muted);
  cursor: pointer;
}

.sidebar-check.strong { font-weight: 700; color: var(--color-text); }
.sidebar-check input { flex: 0 0 auto; }

/* Map label fields under their headings (v2.70.0). Each used to carry a
   "z15+" badge for the zoom it is drawn from; Vedran asked for them to go
   (21.10.2026), and the status line under the map already names a ticked
   field that waits for a zoom (mapLabelWaitingNote). */
.map-label-group + .map-label-group { margin-top: 6px; }
.map-label-group-title {
  margin: 6px 0 1px 2px;
  font-size: var(--text-xs);
  font-weight: 600;
  color: var(--color-text-faint, var(--color-text-muted));
}

.sidebar-sub-label {
  margin: 10px 0 3px;
  font-size: 0.6875rem;     /* .sidebar-section-label's size: under MAP, not above it */
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-text-muted);
}

.sidebar-tool { padding-left: 2px; }

.ols-sidebar-panel {
  display: flex;
  flex-direction: column;
  gap: 5px;
  font-size: 12.5px;
}

.ols-sidebar-panel select,
.ols-sidebar-panel button { max-width: 100%; }

/* ─── Reason dialog (withdrawal, return to author) ─────────
   Replaces two window.prompt calls. The three withdrawal reasons produce
   different AIXM, so the choice has to be readable at a glance rather than
   mapped to a digit the operator types. */
.reason-choices {
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.reason-choice {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 10px 12px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md, 6px);
  cursor: pointer;
  line-height: 1.35;
}
.reason-choice:hover { border-color: var(--color-primary); }
.reason-choice:has(input:checked) {
  border-color: var(--color-primary);
  background: color-mix(in srgb, var(--color-primary) 8%, transparent);
}

/* The version label when the page is older than the server. Not decoration:
   it is the difference between "you are on 2.17.0" and "the interface in
   front of you is enforcing last week's rules". */
.version-stale {
  color: var(--color-error) !important;
  opacity: 1 !important;
  cursor: help;
}

/* ─── Type filter — dropdown with checkboxes (v2.40.0) ──────────────────────
   Replaced a row of chips, one per type present in the data. The codebook
   holds 97 types, so the row would overflow long before that; and a single
   choice could not say "towers and cranes", which mattered once the filter
   started governing the map as well as the table. */
.type-filter { position: relative; }
.type-filter-btn {
  display: flex; align-items: center; gap: var(--space-2);
  background: var(--color-surface-offset); color: var(--color-text);
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
  padding: 7px 11px; font: inherit; font-size: var(--text-sm); cursor: pointer;
  min-width: 200px;
}
.type-filter-btn:hover { border-color: var(--color-text-muted); }
.type-filter-btn.narrowed { border-color: var(--color-primary); }
.type-filter-btn .tf-label { flex: 1; text-align: left; }
.type-filter-btn .tf-count { color: var(--color-text-muted); font-size: var(--text-xs); }

.type-filter-panel {
  position: absolute; z-index: 40; top: calc(100% + 6px); left: 0; width: 288px;
  background: var(--color-surface); border: 1px solid var(--color-border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg, 0 18px 44px rgba(0,0,0,.28));
  overflow: hidden;
}
.type-filter-panel[hidden] { display: none; }
.tf-tools { display: flex; gap: var(--space-2); padding: 10px 12px;
            border-bottom: 1px solid var(--color-border); }
.tf-tools button { flex: 1; background: transparent; color: var(--color-primary);
                   border: 1px solid var(--color-border); border-radius: 7px;
                   padding: 5px; font: inherit; font-size: var(--text-xs); cursor: pointer; }
.tf-tools button:hover { border-color: var(--color-primary); }
.tf-search { padding: 10px 12px; border-bottom: 1px solid var(--color-border); }
.tf-search input { width: 100%; background: var(--color-surface-offset);
                   border: 1px solid var(--color-border); border-radius: 7px;
                   padding: 6px 9px; color: var(--color-text); font: inherit;
                   font-size: var(--text-sm); }
.tf-list { max-height: 248px; overflow-y: auto; padding: 6px 0; }
.tf-opt { display: flex; align-items: center; gap: var(--space-2);
          padding: 6px 12px; cursor: pointer; font-size: var(--text-sm); }
.tf-opt:hover { background: var(--color-surface-offset); }
.tf-opt input { accent-color: var(--color-primary); }
.tf-opt .tf-name { flex: 1; }
.tf-opt .tf-n { color: var(--color-text-muted); font-size: var(--text-xs); }
.tf-foot { padding: 8px 12px; border-top: 1px solid var(--color-border);
           color: var(--color-text-muted); font-size: var(--text-xs); }

/* ─── Filter panel — ranges and choices that the toolbar could not hold ─────
   Search, the type filter, five status chips, DD/DMS and a counter were already
   in that row; three ranges need two fields each. Everything below lives behind
   one button, and the status chips moved in with it.

   Every colour here is a variable that EXISTS — checked against the :root block,
   not assumed. No fallback colour inside var(): it is a
   promise no theme keeps, and it is exactly how --color-danger left the type
   filter unreadable in the light theme. Design: docs/ui-mockups/G-ploca-filtara.html */
/* Static on purpose: the panel is 760 px and the button sits in the middle of
   the toolbar, so anchoring the panel to the button pushed it off the right
   edge of the window — Radius was cut in half and Withdrawn was off-screen.
   Found by rendering the real page; the fragment harness had the toolbar
   starting at x=0 and it fitted there. It hangs off .table-toolbar instead,
   which spans the width, so it cannot leave the page at any window size. */
.obs-filter { position: static; }
.obs-filter-btn {
  display: flex; align-items: center; gap: var(--space-2);
  background: var(--color-surface-offset); color: var(--color-text);
  border: 1px solid var(--color-border); border-radius: var(--radius-md);
  padding: 7px 11px; font: inherit; font-size: var(--text-sm); cursor: pointer;
  white-space: nowrap;
}
.obs-filter-btn:hover { border-color: var(--color-text-muted); }
.obs-filter-btn.narrowed { border-color: var(--color-primary); }
.of-badge {
  background: var(--color-primary); color: var(--color-text-inverse);
  border-radius: var(--radius-full); padding: 0 6px; min-width: 18px;
  text-align: center; font-size: var(--text-xs); font-weight: 600;
}
.of-badge[hidden] { display: none; }

.obs-filter-panel {
  position: absolute; z-index: 40; top: calc(100% - 2px);
  /* Both edges named, so the panel is bounded by the toolbar at every width;
     max-width keeps it at 760 px on a wide screen. 760 because the three ranges
     fold into two columns below that and leave the third range on its own. */
  left: var(--space-4); right: var(--space-4); max-width: 760px;
  /* Scrolls rather than running off the bottom. The toolbar sits between the
     map and the table, so the space below it is about 240 px whatever the
     window size — measured 164 px of this panel below the fold at 1280x720,
     1440x900 and 1500x1180 alike, and the bottom 164 px is where the count of
     records held back and Clear all live. openFilterPanel() sets the height. */
  overflow-y: auto;
  background: var(--color-surface); border: 1px solid var(--color-border);
  border-radius: var(--radius-lg); box-shadow: var(--shadow-lg);
  padding: var(--space-4);
}
.obs-filter-panel[hidden] { display: none; }

.of-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
           gap: var(--space-4); }
.of-grid fieldset { border: 1px solid var(--color-border); border-radius: var(--radius-md);
                    padding: 10px 12px; margin: 0; min-width: 0; }
.of-grid legend { color: var(--color-text-muted); font-size: var(--text-xs);
                  text-transform: uppercase; letter-spacing: .06em; padding: 0 6px; }
.of-range { display: flex; align-items: center; gap: var(--space-2); }
.of-range input { width: 100%; min-width: 0; background: var(--color-surface-offset);
                  border: 1px solid var(--color-border); border-radius: 7px;
                  padding: 6px 9px; color: var(--color-text); font: inherit;
                  font-size: var(--text-sm); }
.of-range span { color: var(--color-text-muted); font-size: var(--text-xs); }
.of-hint { color: var(--color-text-muted); font-size: var(--text-xs); margin-top: 7px; }

/* Segments get a row of their own, two per line. Sharing the ranges' grid put
   five status options into a 232 px column: they wrapped onto three lines and
   "In Review" broke across two. Status gets the wider of the two columns —
   five options and the longest words; an even split pushed "Withdrawn" onto a
   line of its own. */
.of-seg-row { grid-template-columns: minmax(0, 0.78fr) minmax(0, 1.22fr);
              margin-top: var(--space-4); }
@media (max-width: 700px) { .of-seg-row { grid-template-columns: 1fr; } }

/* Buttons size to their words rather than stretching to an equal share. Equal
   shares gave every option the width of the narrowest column, and "In Review"
   broke across two lines inside its own button. */
.of-seg { display: flex; gap: 6px; flex-wrap: wrap; }
.of-seg button { flex: 0 1 auto; padding: 6px 11px; background: transparent;
                 color: var(--color-text-muted); border: 1px solid var(--color-border);
                 border-radius: 7px; font: inherit;
                 font-size: var(--text-xs); cursor: pointer; white-space: nowrap; }
.of-seg button:hover { border-color: var(--color-text-muted); }
.of-seg button.on { border-color: var(--color-primary); color: var(--color-primary); }

/* Records held back only because nobody entered the value. Named, not swallowed:
   "there are none" and "we do not know about them" are different answers. */
.of-unknown { margin-top: var(--space-4); padding: 10px 12px;
              border: 1px solid var(--color-warning); border-radius: 9px;
              background: var(--color-warning-dim); color: var(--color-warning);
              font-size: var(--text-xs); }
.of-unknown[hidden] { display: none; }
.of-unknown div + div { margin-top: 4px; }

.of-foot { display: flex; gap: var(--space-3); align-items: center; flex-wrap: wrap;
           margin-top: var(--space-4); padding-top: var(--space-3);
           border-top: 1px solid var(--color-divider); }
.of-result { color: var(--color-text-muted); font-size: var(--text-sm); margin-right: auto; }
.of-foot button { background: transparent; color: var(--color-primary);
                  border: 1px solid var(--color-border); border-radius: 8px;
                  padding: 6px 14px; font: inherit; font-size: var(--text-sm); cursor: pointer; }
.of-foot button:hover { border-color: var(--color-primary); }

/* Active filters, visible with the panel shut. */
.of-pills { display: flex; gap: 7px; flex-wrap: wrap;
            padding: var(--space-2) var(--space-4); border-bottom: 1px solid var(--color-divider); }
.of-pills[hidden] { display: none; }
.of-pill { display: flex; align-items: center; gap: 7px;
           border: 1px solid var(--color-primary); color: var(--color-primary);
           border-radius: var(--radius-full); padding: 3px 11px; font-size: var(--text-xs); }
.of-pill b { cursor: pointer; opacity: .7; font-weight: 700; }
.of-pill b:hover { opacity: 1; }

/* ─── CAD access dialog ──────────────────────────────────────
   The one screen in the application that shows a credential, and only for a
   moment. The reveal block is deliberately loud: it is the single chance to
   copy the value, and a person who scrolls past it has lost the token.

   No new colours are introduced here — every value below is an existing
   token from the palette above, so the contrast measurements in TC-104 keep
   covering this dialog without being re-run against a private set. */

.cad-token-new {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-3);
  align-items: flex-end;
  margin-bottom: var(--space-4);
}

.cad-token-new .form-group { flex: 1 1 240px; margin-bottom: 0; }
.cad-token-new .cad-token-ttl { flex: 0 0 150px; }
.cad-token-new .cad-token-go  { flex: 0 0 auto; }

.cad-token-reveal {
  border: 1px solid var(--color-primary);
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
  padding: var(--space-4);
  margin-bottom: var(--space-4);
}

.cad-token-reveal-head {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
  color: var(--color-text);
  font-size: var(--text-sm);
}

.cad-token-reveal-head i { width: 18px; height: 18px; }

.cad-token-reveal-row {
  display: flex;
  gap: var(--space-3);
  align-items: center;
}

/* user-select:all so one click selects the whole string: a token broken across
   a hand-made selection is a token that fails at the other end with no clue
   why. word-break because it is 47 characters with no spaces in it. */
.cad-token-reveal-row code {
  flex: 1 1 auto;
  font-family: var(--font-mono);
  font-size: var(--text-sm);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  padding: var(--space-2) var(--space-3);
  user-select: all;
  word-break: break-all;
}

.cad-status { font-weight: 600; font-size: var(--text-sm); }
.cad-status.active  { color: var(--color-success); }
.cad-status.expired { color: var(--color-text-muted); }
.cad-status.revoked { color: var(--color-error); }

.btn.btn-sm {
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-sm);
}

/* .data-table carries min-width:900px — it was written for the obstacle table
   and its eighteen columns. Six columns in a 760px dialog do not need it, and
   inheriting it pushed Status and the Revoke button off the right edge behind
   a horizontal scrollbar.
   
   Found 23.08.2026 in a screenshot. TC-107 had 48 assertions passing at the
   time and not one of them could see it: they all read markup and state, and
   this was neither. Same lesson as the icons that never drew, and the drawer
   that sat under the map. */
#cadTokenTableWrap { overflow-x: auto; }
#cadTokenTableWrap .data-table { min-width: 0; width: 100%; }

/* .data-table also sets white-space:nowrap on every cell — right for the
   obstacle table, wrong here, because the name is free text up to a hundred
   characters and a long one pushed the table wider than the dialog. Found
   23.08.2026 in the screenshot taken to confirm the FIRST width fix: one row
   fitted, two rows with a long name did not.

   Fixed column widths were tried and rejected the same afternoon. Guessing a
   width for the action column left the Revoke button overflowing its cell by
   ONE pixel (table 712px inside a 710px box) — a number nobody could have
   predicted from the stylesheet, and one that would move again the first time
   a button label changed.

   So: the columns size to their content, and exactly one of them is allowed to
   give way. The name is the only free text, so it is the one that wraps, and
   its max-width is what the table shrinks into when the rest have taken what
   they need. Nothing here depends on a measured pixel. */
/* The administrator's list carries a seventh column — whose token it is — and
   that column is free text too (a full name), so it gets the same treatment as
   the token name beside it: it wraps, it is capped, and it is one of the two
   columns that give way when the dialog runs out of room. MEASURED 17.09.2026,
   Chrome, seven columns in the 760 px dialog: 0 px hidden. */
#cadTokenTableWrap.has-owner th:nth-child(2),
#cadTokenTableWrap.has-owner td:nth-child(2) {
  white-space: normal;
  overflow-wrap: break-word;
  max-width: 200px;
}

/* --- CAD requests in the notification panel --- */
/* The only block in that panel that asks the reader to do something, so it
   reads as a list of buttons rather than a list of events. */
.notif-cad {
  border-bottom: 1px solid var(--color-divider);
  padding: var(--space-2) 0;
}

.notif-cad-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: var(--space-2) var(--space-4);
  font-size: var(--text-xs);
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--color-text-muted);
}

.notif-cad-row {
  display: block;
  width: 100%;
  text-align: left;
  border: 0;
  border-left: 3px solid var(--color-primary);
  background: transparent;
  padding: var(--space-2) var(--space-4);
  cursor: pointer;
  font: inherit;
  color: inherit;
}

.notif-cad-row:hover { background: var(--color-surface-offset); }
.notif-cad-row.approved { border-left-color: var(--color-success); }
.notif-cad-row.denied { border-left-color: var(--color-error); }

.notif-cad-who {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-size: var(--text-sm);
}

.notif-cad-user { font-size: var(--text-xs); color: var(--color-text-muted); }

/* The reason, kept to two lines: it is what the decision is about, and a
   paragraph of it would push the next request off the panel. */
.notif-cad-reason {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin-top: 2px;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* .data-table forbids wrapping on every cell, which is right for the obstacle
   table on a page as wide as the window and wrong in a dialog. The two free-text
   columns were let out of it on 23.08.2026; the seventh column (Owner) made the
   rest worth letting out too. MEASURED 17.09.2026, Chrome, the administrator's
   seven columns with a long name and a long username:

     dialog 980 px -> 0 px hidden      dialog 957 px -> 0 px hidden
     dialog 652 px -> 74 px hidden     (was 183 px before this rule)

   Nothing wraps while there is room: the rows stay one line each at every width
   the dialog is normally used at. */
#cadTokenTableWrap th,
#cadTokenTableBody td { white-space: normal; }

#cadTokenTableWrap th:first-child,
#cadTokenTableBody td:first-child {
  white-space: normal;
  /* break-word, not anywhere: anywhere lets a column shrink below its longest
     word and then splits the word, which is how Administrator came to read
     Administrato / r in the status column on 23.08.2026. break-word keeps a
     word whole unless it cannot fit on a line of its own. */
  overflow-wrap: break-word;
  max-width: 220px;
}

.cad-download {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  margin-bottom: var(--space-4);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface-2);
}

.cad-download-text { display: flex; flex-direction: column; gap: 2px; }
.cad-download-text strong { font-size: var(--text-sm); }
.cad-download .btn { flex: 0 0 auto; white-space: nowrap; }

/* ─── Two-factor sign-in (2.69.0) ─────────────────────────────────────── */
/* The profile row, the set-up steps, the key to read off, the recovery codes. */
.profile-mfa-row {
  display: flex; align-items: center; justify-content: space-between; gap: var(--space-3);
}
.btn.small { padding: var(--space-1) var(--space-3); font-size: var(--text-xs); }
.mfa-steps { margin: 0 0 var(--space-4); padding-left: 1.25rem; display: grid; gap: var(--space-3); }
.mfa-entry {
  display: flex; align-items: center; gap: var(--space-3); flex-wrap: wrap;
  margin: var(--space-1) 0;
}
.mfa-entry > span { min-width: 4.5rem; color: var(--color-text-muted); font-size: var(--text-sm); }
/* One line: 39 characters a person copies by eye must not break in the middle.
   MEASURED in Chromium at 500 px: at --text-base the key wrapped after the
   sixth group. */
.mfa-key {
  font-family: var(--font-mono); font-size: var(--text-sm); letter-spacing: 0.02em;
  white-space: nowrap;
  padding: var(--space-1) var(--space-2); border-radius: var(--radius-sm);
  background: var(--color-surface-2); border: 1px solid var(--color-border);
  user-select: all;
}
.mfa-steps a { color: var(--color-primary); }
/* Same measurement: at --text-base each code broke at a dash, two lines each. */
.mfa-codes {
  list-style: none; margin: var(--space-3) 0; padding: var(--space-3);
  display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--space-2) var(--space-4);
  border: 1px solid var(--color-border); border-radius: var(--radius-md); background: var(--color-surface-2);
  font-size: var(--text-sm); user-select: all;
}
.mfa-codes li { white-space: nowrap; }
.mfa-confirm { display: flex; align-items: center; gap: var(--space-2); margin-top: var(--space-3); font-size: var(--text-sm); }
/* "Done" stays locked until the codes are saved — and has to LOOK locked. */
#mfaModal .btn:disabled { opacity: 0.45; cursor: not-allowed; }
