/* ==========================================================================
   KML Manager — postes MT
   
   Two states, one visual language. In consultation the chrome is cold and
   inert. In edit mode an amber rule runs the width of the app and the
   toolbar warms up, the way a consignation tag tells you a piece of plant
   is under intervention. You should never have to guess whether a drag
   will move a real substation.

   No webfonts: this application is expected to run on an isolated network,
   so everything is the system stack.
   ========================================================================== */

:root {
    /* chrome */
    --chrome-900: #161e27;
    --chrome-800: #1b2430;
    --chrome-700: #24313f;
    --chrome-600: #33455a;
    --chrome-line: #2f3f51;

    /* surface */
    --surface: #ffffff;
    --surface-sunken: #eef1f4;
    --surface-rail: #f6f8f9;
    --line: #d6dde3;
    --line-strong: #b8c4cd;

    /* ink */
    --ink: #16202b;
    --ink-muted: #61717f;
    --ink-invert: #e8eef3;
    --ink-invert-muted: #91a3b4;

    /* state */
    --edit: #e8a33d;
    --edit-deep: #b87610;
    --edit-wash: #fdf4e3;
    --select: #2d8fa8;
    --select-wash: #e3f2f5;
    --danger: #c0453b;
    --danger-wash: #fbeceb;
    --ok: #2f7d55;
    --ok-wash: #e7f3ec;

    --mono: ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono",
            Menlo, Consolas, monospace;
    --sans: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
            Arial, sans-serif;

    --rail-width: 250px;
    --radius: 2px;
    --speed: 120ms;
}

*, *::before, *::after { box-sizing: border-box; }

html, body {
    height: 100%;
    margin: 0;
    font-family: var(--sans);
    font-size: 14px;
    color: var(--ink);
    background: var(--surface-sunken);
    overflow: hidden;
}

/* Small caps label, used for every field label and section head. The
   instrument-panel vernacular: terse, uppercase, wide-tracked. */
.label {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--ink-muted);
}

.mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }

/* ==========================================================================
   Shell
   ========================================================================== */

.app {
    display: grid;
    /* mode-rule, toolbar, statusbar all size to content; .body takes the
       rest. The 1fr must land on .body — putting it on .statusbar (which
       wants a fixed 30px) balloons the strip and collapses the map/table
       area to zero height. */
    grid-template-rows: auto auto auto 1fr;
    /* Pin the single column to the viewport. Without this the implicit
       `auto` column grows to the toolbar's min-content width, so on a
       phone the whole layout overflows sideways and the map loses width. */
    grid-template-columns: minmax(0, 1fr);
    height: 100vh;
}

/* --- the hazard rule. 0 height when inert, 4px and amber when editing. */
.mode-rule {
    height: 0;
    background: var(--edit);
    transition: height var(--speed) ease;
}
body.editing .mode-rule { height: 4px; }

/* ==========================================================================
   Toolbar
   ========================================================================== */

.toolbar {
    display: flex;
    align-items: center;
    gap: 20px;
    padding: 0 16px;
    height: 52px;
    background: var(--chrome-800);
    color: var(--ink-invert);
    border-bottom: 1px solid var(--chrome-line);
    transition: background var(--speed) ease;
}
body.editing .toolbar { background: var(--chrome-900); }

.brand {
    display: flex;
    flex-direction: column;
    line-height: 1.15;
    padding-right: 16px;
    border-right: 1px solid var(--chrome-line);
    flex-shrink: 0;
}
.brand strong { font-size: 14px; font-weight: 650; letter-spacing: -0.01em; }
.brand span { font-size: 10px; letter-spacing: 0.08em; text-transform: uppercase;
              color: var(--ink-invert-muted); }

.toolgroup { display: flex; align-items: center; gap: 6px; }
.toolbar .spacer { flex: 1; }

/* --- the mode switch: the one loud element in the interface. */
.mode-switch {
    display: flex;
    align-items: stretch;
    border: 1px solid var(--chrome-600);
    border-radius: var(--radius);
    overflow: hidden;
    flex-shrink: 0;
}
.mode-switch button {
    appearance: none;
    border: 0;
    background: transparent;
    color: var(--ink-invert-muted);
    font: inherit;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    padding: 7px 14px;
    cursor: pointer;
    transition: background var(--speed), color var(--speed);
}
.mode-switch button:hover { color: var(--ink-invert); }
.mode-switch button[aria-pressed="true"] {
    background: var(--chrome-600);
    color: var(--ink-invert);
}
.mode-switch button[data-mode="edit"][aria-pressed="true"] {
    background: var(--edit);
    color: #2a1c05;
}

/* ==========================================================================
   Buttons
   ========================================================================== */

.btn {
    appearance: none;
    font: inherit;
    font-size: 13px;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 11px;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--speed), border-color var(--speed),
                color var(--speed);
}
.btn:hover:not(:disabled) { background: var(--surface-sunken); }
.btn:disabled { opacity: 0.4; cursor: not-allowed; }
/* display:inline-flex above outranks the UA [hidden]{display:none} rule
   (same origin/specificity, author beats UA) — spelled out explicitly so
   a hidden .btn (#btn-login after login, etc.) actually disappears. */
.btn[hidden] { display: none; }

/* On the dark toolbar. */
.toolbar .btn {
    background: transparent;
    border-color: var(--chrome-600);
    color: var(--ink-invert);
}
.toolbar .btn:hover:not(:disabled) { background: var(--chrome-700); }

.btn-primary {
    background: var(--select);
    border-color: var(--select);
    color: #fff;
}
.btn-primary:hover:not(:disabled) { background: #26788d; border-color: #26788d; }
.toolbar .btn-primary { background: var(--select); border-color: var(--select); color: #fff; }
.toolbar .btn-primary:hover:not(:disabled) { background: #26788d; }

.btn-danger { border-color: var(--danger); color: var(--danger); background: var(--surface); }
.btn-danger:hover:not(:disabled) { background: var(--danger-wash); }

.btn-ghost { border-color: transparent; background: transparent; }
.btn-ghost:hover:not(:disabled) { background: var(--surface-sunken); }

.btn-sm { padding: 3px 8px; font-size: 12px; }

/* Unsaved-changes indicator lives on the Save button itself. */
.btn .dot {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--edit);
    flex-shrink: 0;
    display: none;
}
body.dirty .btn .dot { display: block; }

:where(button, input, select, textarea, [tabindex]):focus-visible {
    outline: 2px solid var(--select);
    outline-offset: 1px;
}

/* ==========================================================================
   Status strip
   ========================================================================== */

.statusbar {
    display: flex;
    align-items: center;
    gap: 18px;
    padding: 0 16px;
    height: 30px;
    background: var(--surface);
    border-bottom: 1px solid var(--line);
    font-size: 12px;
    color: var(--ink-muted);
}
body.editing .statusbar { background: var(--edit-wash); border-bottom-color: #edd9b4; }

.statusbar .stat { display: flex; align-items: center; gap: 6px; }
.statusbar .stat b {
    font-family: var(--mono);
    font-weight: 600;
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}
.statusbar .spacer { flex: 1; }

.mode-tag {
    font-size: 10px;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    padding: 2px 8px;
    border-radius: var(--radius);
    background: var(--surface-sunken);
    color: var(--ink-muted);
    border: 1px solid var(--line);
}
body.editing .mode-tag {
    background: var(--edit);
    color: #2a1c05;
    border-color: var(--edit-deep);
}

/* ==========================================================================
   Body: rail + workspace
   ========================================================================== */

.body { display: grid; grid-template-columns: var(--rail-width) 1fr; min-height: 0; }
.body.rail-hidden { grid-template-columns: 0 1fr; }

.rail {
    background: var(--surface-rail);
    border-right: 1px solid var(--line);
    overflow-y: auto;
    padding: 14px;
    min-width: 0;
}
.body.rail-hidden .rail { display: none; }

.rail-section + .rail-section { margin-top: 18px; }
.rail-section > .label { display: block; margin-bottom: 8px; }

.field { margin-bottom: 10px; }
.field > label { display: block; margin-bottom: 3px; font-size: 11px;
                 font-weight: 600; color: var(--ink-muted); }

input[type="text"], input[type="number"], input[type="search"],
select, textarea {
    width: 100%;
    font: inherit;
    font-size: 13px;
    padding: 5px 7px;
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    background: var(--surface);
    color: var(--ink);
}
input:focus, select:focus, textarea:focus {
    outline: none;
    border-color: var(--select);
    box-shadow: 0 0 0 2px var(--select-wash);
}
textarea { resize: vertical; min-height: 62px; }

.check { display: flex; align-items: center; gap: 7px; font-size: 13px;
         cursor: pointer; padding: 3px 0; }
.check input { width: auto; margin: 0; }

.rail .empty { font-size: 12px; color: var(--ink-muted); line-height: 1.5; }

/* ==========================================================================
   Workspace — map over table, three layout presets
   ========================================================================== */

.workspace {
    display: grid;
    grid-template-rows: 1fr 1px 320px;
    min-height: 0;
    min-width: 0;
}
.workspace[data-layout="map"]   { grid-template-rows: 1fr 1px 0; }
.workspace[data-layout="table"] { grid-template-rows: 0 1px 1fr; }

.pane-map { position: relative; min-height: 0; background: var(--surface-sunken); }
.pane-table { min-height: 0; display: flex; flex-direction: column;
              background: var(--surface); overflow: hidden; }

.splitter { background: var(--line); cursor: row-resize; }
.splitter:hover { background: var(--select); }
.workspace[data-layout="map"] .splitter,
.workspace[data-layout="table"] .splitter { cursor: default; }

#map { position: absolute; inset: 0; z-index: 1; }
body.editing #map { cursor: crosshair; }
body.editing.placing #map { cursor: copy; }
body.picking #map, body.picking.editing #map { cursor: crosshair; }

/* Floating map controls, bottom-left so they clear Leaflet's own. */
.map-overlay {
    position: absolute;
    left: 10px;
    bottom: 22px;
    z-index: 500;
    display: flex;
    gap: 6px;
    align-items: center;
    flex-wrap: wrap;
    max-width: calc(100% - 20px);
    background: rgba(255, 255, 255, 0.94);
    border: 1px solid var(--line-strong);
    border-radius: var(--radius);
    padding: 5px 6px;
    backdrop-filter: blur(3px);
}
/* top: 3px, one offset for all three layouts, so the button row never
   jumps when you change layout. In "table" the map is a 0-height track
   and the switch lands on the table bar: the bar starts 1px below the
   pane top (the splitter track), so 3px here centres the 23px row in the
   ~27px bar (2px above / 2px below). The same tight inset reads fine over
   the map in "split"/"map". This used to be 10px, nudged back down only
   in the table case — which is what made it move. */
.layout-switch { position: absolute; right: 10px; top: 3px; z-index: 500;
                 display: flex; gap: 4px; }
.layout-switch .btn { background: rgba(255,255,255,0.94); }

/* "table" layout: the map is a 0-height track, so its floating children
   spill onto the table below. The overlay acts on a map that isn't there
   — hide it. The layout switch stays put (see above) on the right end of
   the table bar, which is kept clear of other controls. */
.workspace[data-layout="table"] .map-overlay { display: none; }

/* Leaflet marker states. The default pin is replaced by a small square:
   a substation is a point of plant, not a pushpin. */
.poste-hit { display: flex; align-items: center; justify-content: center; }
.poste-marker {
    width: 11px; height: 11px;
    background: var(--select);
    border: 2px solid #fff;
    border-radius: var(--radius);
    box-shadow: 0 0 0 1px rgba(0,0,0,0.35);
}
.poste-marker.is-selected {
    background: var(--edit);
    width: 15px; height: 15px;
    box-shadow: 0 0 0 1px rgba(0,0,0,0.4), 0 0 0 5px rgba(232,163,61,0.32);
}
/* A ticked poste: a hard dark ring, white-gapped, so it reads against any
   basemap and stays distinct from the amber "active" halo. */
.poste-marker.is-picked {
    box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--chrome-800);
}
.poste-marker.is-selected.is-picked {
    box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--chrome-800),
                0 0 0 8px rgba(232,163,61,0.35);
}
body.editing .poste-marker { border-radius: 50%; }

.leaflet-container { font-family: var(--sans); background: #dfe5ea; }
.leaflet-popup-content { margin: 10px 12px; font-size: 13px; min-width: 190px; }
.leaflet-popup-content-wrapper { border-radius: var(--radius); }
.popup-title { font-weight: 650; margin-bottom: 6px; }
.popup-grid { display: grid; grid-template-columns: auto 1fr; gap: 2px 10px;
              font-size: 12px; margin-bottom: 8px; }
.popup-grid dt { color: var(--ink-muted); }
.popup-grid dd { margin: 0; font-family: var(--mono); word-break: break-word; }
.popup-pick { display: flex; align-items: center; gap: 6px; font-size: 12px;
              margin-bottom: 8px; cursor: pointer; user-select: none; }
.popup-pick input { cursor: pointer; }
.popup-actions { display: flex; gap: 6px; }

.tiles-warning {
    position: absolute; left: 50%; top: 12px; transform: translateX(-50%);
    z-index: 600; background: var(--edit-wash); border: 1px solid var(--edit);
    color: var(--edit-deep); padding: 5px 12px; border-radius: var(--radius);
    font-size: 12px;
}

/* ==========================================================================
   Table
   ========================================================================== */

.table-bar {
    display: flex; align-items: center; gap: 12px;
    padding: 6px 10px;
    border-bottom: 1px solid var(--line);
    background: var(--surface-rail);
    flex-shrink: 0;
    font-size: 12px;
}
.table-bar .spacer { flex: 1; }

/* scroll-padding-top reserves the sticky-header band, so reveal()'s
   scrollIntoView never parks a selected row half-hidden behind the header. */
.table-scroll { flex: 1; overflow: auto; min-height: 0; scroll-padding-top: 36px; }

table.grid { border-collapse: separate; border-spacing: 0; width: 100%;
             font-size: 13px; }

table.grid thead th {
    position: sticky; top: 0; z-index: 3;
    background: var(--chrome-700);
    color: var(--ink-invert);
    text-align: left;
    font-size: 10px; font-weight: 700; letter-spacing: 0.08em;
    text-transform: uppercase;
    padding: 7px 9px;
    white-space: nowrap;
    cursor: pointer;
    user-select: none;
    border-right: 1px solid var(--chrome-line);
}
table.grid thead th:hover { background: var(--chrome-600); }
table.grid thead th.no-sort { cursor: default; }
table.grid thead th .sort-arrow { opacity: 0.45; margin-left: 4px; }
table.grid thead th[aria-sort] .sort-arrow { opacity: 1; color: var(--edit); }

table.grid tbody td {
    padding: 5px 9px;
    border-bottom: 1px solid var(--line);
    border-right: 1px solid var(--surface-sunken);
    white-space: nowrap;
    max-width: 280px;
    overflow: hidden;
    text-overflow: ellipsis;
}
table.grid tbody td.coord { font-family: var(--mono); font-size: 12px;
                            color: var(--ink-muted); text-align: right;
                            font-variant-numeric: tabular-nums; }
table.grid tbody tr:nth-child(even) { background: #fafbfc; }
table.grid tbody tr:hover { background: #eef1f3; }   /* neutral, not the selection tint */

/* Two row states, deliberately different so "actif" and "sélection" never
   look alike:
   - is-selected = the cursor ("poste actif"): a left bar only, no fill.
   - is-picked   = the ticked set ("sélection"): a solid block fill.
   A row that is both gets the fill and the bar together. */
table.grid tbody tr.is-selected {
    box-shadow: inset 4px 0 0 var(--select);
}
body.editing table.grid tbody tr.is-selected { box-shadow: inset 4px 0 0 var(--edit); }

/* The selection checkbox column. Present in both modes — selecting is not a
   mutation — so it is not gated on body.editing like td.editable is. */
table.grid th.col-check, table.grid td.col-check {
    width: 28px; text-align: center; padding-left: 0; padding-right: 0;
}
table.grid th.col-check { cursor: default; }
table.grid th.col-check input, table.grid td.col-check input { cursor: pointer; margin: 0; }

/* The ticked set: a solid block fill. The ticked checkbox carries the rest. */
table.grid tbody tr.is-picked { background: var(--select-wash); }

/* Cells are inert text until edit mode, then they invite a click. */
body.editing table.grid tbody td.editable { cursor: text; }
body.editing table.grid tbody td.editable:hover {
    box-shadow: inset 0 0 0 1px var(--edit);
    background: var(--edit-wash);
}
table.grid td.editing { padding: 0; }
table.grid td.editing input {
    width: 100%; border: 0; border-radius: 0;
    padding: 5px 8px; font-size: 13px;
    box-shadow: inset 0 0 0 2px var(--edit);
}
table.grid td.editing input:focus { box-shadow: inset 0 0 0 2px var(--edit); }

td.rowactions { white-space: nowrap; text-align: right; }

/* "Carte" column: shrink to the button so the name column keeps the width. */
table.grid th.col-map, table.grid td.col-map {
    width: 1%; white-space: nowrap; padding-left: 4px; padding-right: 4px;
}

.table-empty { padding: 34px 20px; text-align: center; color: var(--ink-muted);
               font-size: 13px; line-height: 1.6; }

.pager { display: flex; align-items: center; flex-wrap: wrap; gap: 6px 8px;
         padding: 6px 10px;
         border-top: 1px solid var(--line); background: var(--surface-rail);
         font-size: 12px; flex-shrink: 0; }
.pager .spacer { flex: 1; }
.pager select { width: auto; }
.pager-rows { margin-left: 6px; }

/* The bulk actions sit in the pager row on desktop, set apart from the
   paging nav by a rule. On a phone (see the max-width block) they leave the
   flow and float as a fixed bottom bar instead. Shown only while a
   selection is active. */
#bulk-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
                padding-left: 8px; border-left: 1px solid #e0cfa0; }
#bulk-actions[hidden] { display: none; }
#bulk-count { display: none; }   /* the pager's #selection-label shows it on desktop */

/* ==========================================================================
   Modals
   ========================================================================== */

.modal-backdrop {
    position: fixed; inset: 0; z-index: 2000;
    background: rgba(22, 30, 39, 0.55);
    display: flex; align-items: center; justify-content: center;
    padding: 20px;
}
.modal {
    background: var(--surface);
    border-radius: var(--radius);
    border-top: 3px solid var(--chrome-700);
    width: 100%; max-width: 560px;
    max-height: 88vh;
    display: flex; flex-direction: column;
    box-shadow: 0 18px 48px rgba(22, 30, 39, 0.3);
}
.modal.wide { max-width: 720px; }
.modal.danger { border-top-color: var(--danger); }

.modal-head { display: flex; align-items: center; justify-content: space-between;
              padding: 14px 18px; border-bottom: 1px solid var(--line); }
.modal-head h2 { margin: 0; font-size: 15px; font-weight: 650; }
.modal-body { padding: 18px; overflow-y: auto; }
.modal-foot { display: flex; gap: 8px; justify-content: flex-end;
              padding: 12px 18px; border-top: 1px solid var(--line);
              background: var(--surface-rail); }
.modal-foot .spacer { flex: 1; }

.modal p { margin: 0 0 12px; line-height: 1.55; }
.modal p:last-child { margin-bottom: 0; }

/* Help reference */
.help-section { margin-bottom: 16px; }
.help-section h3 { margin: 0 0 6px; font-size: 13px; font-weight: 650;
                   color: var(--ink); }
.help-section p { font-size: 12.5px; }
.help-dl { display: grid; grid-template-columns: minmax(120px, max-content) 1fr;
          gap: 4px 14px; margin: 0; font-size: 12.5px; line-height: 1.45; }
.help-dl dt { font-weight: 600; color: var(--ink); }
.help-dl dd { margin: 0; color: var(--ink-muted); }
@media (max-width: 560px) {
    .help-dl { grid-template-columns: 1fr; gap: 1px 0; }
    .help-dl dt { margin-top: 6px; }
}

.cols { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }

.chipset { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 4px; }
.chip { font-family: var(--mono); font-size: 12px; padding: 2px 7px;
        border-radius: var(--radius); background: var(--surface-sunken);
        border: 1px solid var(--line); }
.chip.add { background: var(--ok-wash); border-color: #b6d8c3; color: var(--ok); }
.chip.drop { background: var(--danger-wash); border-color: #ecc4c1; color: var(--danger); }

.choice { display: block; border: 1px solid var(--line-strong);
          border-radius: var(--radius); padding: 10px 12px; margin-bottom: 8px;
          cursor: pointer; }
.choice:hover { border-color: var(--select); background: var(--select-wash); }
.choice input { margin-right: 8px; }
.choice strong { font-size: 13px; }
.choice small { display: block; margin-left: 22px; margin-top: 2px;
                color: var(--ink-muted); line-height: 1.45; }
.choice:has(input:checked) { border-color: var(--select); background: var(--select-wash); }

.notice { padding: 9px 11px; border-radius: var(--radius); font-size: 12.5px;
          line-height: 1.5; margin-bottom: 12px; }
.notice.warn { background: var(--edit-wash); border: 1px solid #edd9b4; color: #6d4a08; }
.notice.bad  { background: var(--danger-wash); border: 1px solid #ecc4c1; color: #8a2f27; }

/* Duplicate report: one card per candidate group. */
.dup-group { border: 1px solid var(--line-strong); border-radius: var(--radius);
             padding: 10px 12px; margin-bottom: 12px; }
.dup-group-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
                  margin-bottom: 8px; }
table.dup-members { width: 100%; border-collapse: collapse; font-size: 12px;
                    margin-bottom: 8px; }
table.dup-members th { text-align: left; font-weight: 600; color: var(--ink-muted);
                       border-bottom: 1px solid var(--line); padding: 3px 6px; }
table.dup-members td { padding: 3px 6px; border-bottom: 1px solid var(--surface-sunken); }
table.dup-members td.dup-conflict { background: var(--edit-wash); }
table.dup-members td.mono { font-family: var(--mono); color: var(--ink-muted); }

/* Shown while the duplicate report is stepped aside to inspect the map. */
.dedup-resume {
    position: fixed; left: 50%; bottom: 24px; transform: translateX(-50%);
    z-index: 2500; display: flex; align-items: center; gap: 12px;
    background: var(--chrome-800); color: var(--ink-invert);
    padding: 8px 10px 8px 14px; border-radius: var(--radius);
    font-size: 13px; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.28);
}

.project-list { border: 1px solid var(--line); border-radius: var(--radius); }
.project-row { display: flex; align-items: center; gap: 10px; padding: 9px 11px;
               border-bottom: 1px solid var(--line); cursor: pointer; }
.project-row:last-child { border-bottom: 0; }
.project-row:hover { background: var(--select-wash); }
.project-row .grow { flex: 1; min-width: 0; }
.project-row strong { display: block; font-size: 13px; font-weight: 600; }
.project-row small { color: var(--ink-muted); font-size: 11.5px;
                     font-family: var(--mono); }

/* ==========================================================================
   Toasts
   ========================================================================== */

.toasts { position: fixed; right: 14px; bottom: 14px; z-index: 3000;
          display: flex; flex-direction: column; gap: 7px; max-width: 380px; }
.toast {
    background: var(--chrome-800); color: var(--ink-invert);
    padding: 9px 13px; border-radius: var(--radius); font-size: 13px;
    border-left: 3px solid var(--select);
    animation: toast-in 140ms ease-out;
    line-height: 1.45;
}
.toast.ok   { border-left-color: var(--ok); }
.toast.warn { border-left-color: var(--edit); }
.toast.bad  { border-left-color: var(--danger); }
@keyframes toast-in {
    from { opacity: 0; transform: translateY(6px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ==========================================================================
   Responsive + accessibility floor
   ========================================================================== */

@media (max-width: 900px) {
    /* The rail is display:none here, so .workspace is the only item left in
       the grid. It must land in a real track — a leading `0` track would
       swallow it (0 width, no map). One full-width column; the rail, when
       opened, floats over as a drawer. */
    .body,
    .body.rail-open { grid-template-columns: minmax(0, 1fr); }
    .rail { display: none; }
    .body.rail-open .rail { display: block; position: absolute; top: 0; bottom: 0;
                            left: 0; z-index: 900; width: var(--rail-width);
                            background: var(--surface); }
    .brand span { display: none; }
    .toolbar { gap: 10px; overflow-x: auto; min-width: 0; }

    /* The split layout's fixed 320px table row is taller than a landscape
       phone's whole workspace, which would leave the map at zero height.
       Make the table a fraction of what is available instead. */
    .workspace { grid-template-rows: 1fr 1px minmax(110px, 42%); }

    /* Status strip: wrap onto as many lines as it needs rather than
       overflowing off the right edge where the last stats get clipped. */
    .statusbar {
        height: auto; min-height: 30px;
        flex-wrap: wrap; gap: 3px 12px; padding: 5px 10px;
    }
    .statusbar .spacer { display: none; }

    /* Taller rows and a wider, inset checkbox column: a small box flush to a
       curved screen edge is awkward to tap. The whole left cell toggles. */
    table.grid tbody td { padding-top: 9px; padding-bottom: 9px; }
    table.grid th.col-check, table.grid td.col-check { width: 44px; padding-left: 14px; }
    table.grid td.col-check input { width: 18px; height: 18px; }

    /* Bulk actions: a fixed contextual bar pinned to the bottom of the
       screen (Gmail / Photos pattern). Inside the short, overflow:hidden
       table pane the wrapped action line was getting clipped or hidden
       under the phone's home indicator; position:fixed escapes both, and
       the safe-area inset keeps it above the gesture bar. */
    #bulk-actions:not([hidden]) {
        position: fixed; left: 0; right: 0; bottom: 0; z-index: 45;
        flex-wrap: nowrap; overflow-x: auto;   /* one row tall: scroll sideways, never grow up over the table */
        justify-content: safe center; gap: 6px;
        padding: 8px 10px;
        padding-bottom: calc(8px + env(safe-area-inset-bottom, 0px));
        border-left: 0; border-top: 1px solid #e0cfa0;
        background: var(--edit-wash);
        box-shadow: 0 -4px 16px rgba(0, 0, 0, 0.18);
    }
    #bulk-actions:not([hidden]) .btn { flex: none; }
    /* "Tout l'affiché" duplicates the header's select-all checkbox; drop it
       from the cramped fixed bar so the rest fit on one line. */
    #bulk-actions:not([hidden]) #btn-bulk-all { display: none; }

    /* Reserve the fixed bar's height at the foot of the table pane so it
       never sits over the pager or the last rows (which is what left only
       a row or two visible once something was selected). One btn-sm row:
       8 + 23 + 8 + 1px border, plus the gesture-bar inset. */
    .pane-table:has(#bulk-actions:not([hidden])) {
        padding-bottom: calc(40px + env(safe-area-inset-bottom, 0px));
    }

    #bulk-count { display: inline; font-weight: 600; margin-right: 2px; }
    /* the count now lives in the fixed bar, so drop the pager's copy */
    .pane-table:has(#bulk-actions:not([hidden])) #selection-label { display: none; }
}

@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: 0.01ms !important;
        transition-duration: 0.01ms !important;
    }
}

.sr-only {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
