:root {
    /* Clean, light theme. Neutrals are the original cool white/grey (no warm
       tint on the page or menu bars); colour comes only from the subtle sage
       selection and the faint per-kind card tints. */
    --bg: #f4f5f7;             /* light page (as it was) */
    --surface: #ffffff;        /* menu bars / cards — white */
    --soft: #f4f6f8;           /* table-header / soft fill */
    --card: #ffffff;
    --ink: #1d2330;
    --muted: #6b7280;
    --line: #e4e7ec;
    --brand: #8a6d3b;          /* gold — used only for the small "Admin" wordmark */
    --accent: #5f7a58;         /* muted sage — SELECTION + focus/hover accent */
    --accent-soft: rgba(95,122,88,.14);
    --sel-bg: #eaf0e6;         /* very soft sage tint for the selected nav item */
    --sel-ink: #4a6243;        /* darker sage for its text/icon */
    --primary: #1d2330;
    --primary-ink: #ffffff;
    --ok: #17803d;
    --ok-bg: #e6f4ea;
    --off: #9aa1ac;
    --danger: #b42318;
    --danger-bg: #fdeceb;
    --radius: 10px;
    --shadow: 0 1px 2px rgba(16,24,40,.06), 0 8px 24px rgba(16,24,40,.08);
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

/* The `hidden` attribute must beat display rules below (.modal, .login-wrap
   set display, which would otherwise override [hidden]'s display:none). */
[hidden] { display: none !important; }

html, body { height: 100%; }
body {
    margin: 0;
    background: var(--bg);
    color: var(--ink);
    font-size: 15px;
    line-height: 1.45;
}
#app-view { min-height: 100vh; display: flex; align-items: stretch; }

/* ---------- Buttons ---------- */
.btn {
    font: inherit;
    border: 1px solid transparent;
    border-radius: 8px;
    padding: 8px 14px;
    cursor: pointer;
    background: #fff;
    color: var(--ink);
    transition: background .12s, border-color .12s, opacity .12s;
    white-space: nowrap;
}
.btn:hover { background: #f0f1f3; }
.btn-primary { background: var(--primary); color: var(--primary-ink); border-color: var(--primary); }
.btn-primary:hover { opacity: .9; background: var(--primary); }
.btn-ghost { background: transparent; border-color: var(--line); }
.btn-ghost:hover { background: #fff; border-color: #cfd4dc; }
.btn-block { width: 100%; }
/* r29 (r29-backup): a link drawn as a button is never underlined. */
a.btn, a.btn:hover { text-decoration: none; }
/* r29b (review F4): a link, a label or a span dressed as a button is a button-sized box at EVERY width — the shell's
   `label { display: block }` made the hidden-picker label (« Ajouter un scan ») a full-width bar on a desktop; on a
   touch phone min-height (below) needs a box too. */
a.btn, label.btn, span.btn { display: inline-flex; align-items: center; justify-content: center; }
.btn-sm { padding: 5px 10px; font-size: 13px; }
/* One step below btn-sm, for tables where every row carries an action and the
   buttons were setting the row height. */
.btn-xs { padding: 3px 8px; font-size: 12px; line-height: 1.45; }
/* Present to a screen reader, absent on screen. Not `display:none`, which
   removes it from the accessibility tree as well and defeats the point. */
.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; }
.btn-danger { color: var(--danger); border-color: #f2c4c0; background: #fff; }
.btn-danger:hover { background: var(--danger-bg); }

/* ---------- Brand ---------- */
.brand { font-weight: 700; letter-spacing: .3px; font-size: 18px; }
.brand span { color: var(--brand); font-weight: 600; }

/* ---------- Login ---------- */
.login-wrap { min-height: 100vh; display: grid; place-items: center; padding: 24px; }
.login-card {
    background: var(--card); width: 100%; max-width: 360px; padding: 28px;
    border-radius: var(--radius); box-shadow: var(--shadow);
    display: flex; flex-direction: column; gap: 14px;
}
.login-card .brand { font-size: 22px; }
.login-sub { margin: -6px 0 4px; color: var(--muted); }
.login-step { display: flex; flex-direction: column; gap: 14px; }
.login-step[hidden] { display: none; }
.login-hint { margin: -8px 0 0; color: var(--muted); font-size: 13px; line-height: 1.5; }
.login-link { background: none; border: none; color: var(--accent); font: inherit; font-size: 13.5px; font-weight: 600; cursor: pointer; padding: 2px; text-align: center; }
.login-link:hover { text-decoration: underline; }
.login-check { display: flex; align-items: center; gap: 8px; font-size: 13.5px; font-weight: 400; }
.login-check input { width: auto; margin: 0; }
#twofa-code, #reset-code { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 22px; letter-spacing: 6px; text-align: center; }

/* ---------- First paint (r29) ----------
   index.html shows #boot-loading until showLogin() / showApp() hide it. It fades in after 250 ms, so a load that is
   fast enough never shows it; a slow one (a cold host, 4G) shows the brand and « Chargement… » instead of a white page. */
.boot-loading {
    min-height: 100vh; min-height: 100dvh; display: grid; place-content: center; justify-items: center; gap: 10px;
    padding: 24px 16px; text-align: center; color: var(--muted);
    animation: boot-fade-in .3s ease-out .25s both;
}
.boot-loading .brand { color: var(--ink); font-size: 22px; }
.boot-loading-text { margin: 0; display: inline-flex; align-items: center; gap: 8px; font-size: 14px; }
.boot-loading-dot {
    width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
    animation: boot-pulse 1s ease-in-out infinite alternate;
}
@keyframes boot-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes boot-pulse { from { opacity: .25; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) {
    .boot-loading { animation: none; }
    .boot-loading-dot { animation: none; }
}

/* ---------- Sidebar nav (vertical on desktop, drawer on mobile) ---------- */
.sidebar {
    width: 238px; flex: none; background: var(--surface); border-right: 1px solid var(--line);
    display: flex; flex-direction: column; position: sticky; top: 0; height: 100vh; height: 100dvh; overflow-y: auto; overflow-x: hidden;
}
.sidebar-top { display: flex; align-items: center; justify-content: space-between; padding: 16px 16px 12px; }
.sidebar-x { display: none; background: none; border: none; font-size: 26px; line-height: 1; color: var(--muted); cursor: pointer; }
.universe-nav { flex: 1 1 auto; overflow-y: auto; overflow-x: hidden; display: flex; flex-direction: column; gap: 2px; padding: 4px 10px 10px; }
/* Tools of the ACTIVE universe, nested right under its name. */
.uni-tools { display: flex; flex-direction: column; gap: 2px; margin: 2px 0 6px 15px; padding-left: 7px; border-left: 2px solid var(--line); }
.uni-tools:empty { display: none; }
.uni-tools .nav-link { font-size: 13.5px; padding: 6px 10px; }
.sidebar-foot { border-top: 1px solid var(--line); padding: 12px; display: flex; flex-direction: column; gap: 10px; }
.su-btn { width: 100%; display: flex; align-items: center; justify-content: center; gap: 7px; padding: 9px 12px; border: 1px solid var(--line); border-radius: 8px; background: var(--soft); color: var(--ink); font: inherit; font-weight: 600; font-size: 14px; cursor: pointer; min-width: 0; }
.su-btn:hover { border-color: var(--accent); }
.su-btn #who { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.su-btn .su-ico { flex: none; color: var(--muted); font-size: 13px; }
/* Account modal */
.acct-modal .acct-row { display: flex; flex-direction: column; gap: 3px; padding: 10px 0; border-bottom: 1px solid var(--line); }
.acct-modal .acct-row:last-child { border-bottom: none; }
.acct-modal .acct-label { font-size: 11px; text-transform: uppercase; letter-spacing: .4px; color: var(--muted); font-weight: 700; }
.acct-modal .acct-val { font-size: 14.5px; color: var(--ink); }
.acct-modal .acct-chips { display: flex; flex-wrap: wrap; gap: 6px; }
/* Current-hotel indicator under the brand. */
.sidebar-hotel { margin: 4px 14px 12px; padding: 7px 12px; background: var(--sel-bg); border: 1px solid rgba(95,122,88,.28); border-radius: 10px; display: flex; flex-direction: column; gap: 2px; transition: border-color .12s; }
.sidebar-hotel:hover { border-color: var(--accent); }
.sidebar-hotel .sh-label { font-size: 9.5px; text-transform: uppercase; letter-spacing: .7px; color: var(--muted); font-weight: 700; }
.sidebar-hotel .sh-pick { position: relative; display: flex; align-items: center; }
.sidebar-hotel .sh-btn {
    -webkit-appearance: none; appearance: none; width: 100%; margin: 0; padding: 1px 22px 1px 0;
    border: none; background: transparent; font: inherit; font-size: 14.5px; font-weight: 700; color: var(--sel-ink); cursor: pointer;
    display: flex; align-items: center; text-align: left;
}
.sidebar-hotel .sh-btn:focus { outline: none; box-shadow: none; }
.sidebar-hotel .sh-btn-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.sidebar-hotel .sh-static { font-size: 14.5px; font-weight: 700; color: var(--sel-ink); }
.sidebar-hotel .sh-caret { position: absolute; right: 2px; pointer-events: none; color: var(--accent); font-size: 10px; transition: transform .14s; }
.sidebar-hotel .sh-btn[aria-expanded="true"] .sh-caret { transform: rotate(180deg); }
/* Custom (themed) option list — a native <select> popup can't be styled. */
.sidebar-hotel .sh-menu {
    position: absolute; top: calc(100% + 6px); left: -4px; right: -4px; z-index: 50;
    margin: 0; padding: 4px; list-style: none;
    background: var(--card); border: 1px solid rgba(95,122,88,.28); border-radius: 10px;
    box-shadow: 0 10px 26px rgba(0,0,0,.16); max-height: 260px; overflow-y: auto;
}
.sidebar-hotel .sh-opt {
    padding: 8px 10px; border-radius: 7px; font-size: 14px; font-weight: 600; color: var(--ink);
    cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.sidebar-hotel .sh-opt:hover { background: var(--sel-bg); }
.sidebar-hotel .sh-opt.is-sel { background: var(--accent); color: #fff; }
/* A tool's own hotel selector, locked to the master picker in the sidebar. */
select.hotel-locked { background: var(--soft); color: var(--muted); cursor: not-allowed; opacity: .9; }
.sidebar-foot-btns { display: flex; gap: 8px; }
.sidebar-foot-btns .btn { flex: 1; }

.nav-link {
    display: flex; align-items: center; gap: 9px; width: 100%; min-width: 0;
    padding: 8px 11px; border-radius: 8px; text-decoration: none;
    color: var(--muted); font-weight: 600; font-size: 14px; border: 1px solid transparent;
    cursor: pointer; background: none;
}
/* Never let a long label push a horizontal scrollbar — clip with an ellipsis. */
.nav-link > span:last-child { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nav-ico { flex: none; }
.nav-link:hover { background: #f0f1f3; color: var(--ink); }
.nav-link.active { background: var(--sel-bg); color: var(--sel-ink); }
.nav-link.active:hover { background: var(--sel-bg); color: var(--sel-ink); }
/* Monochrome line-icon set (public/core/icons.js). stroke = currentColor, so
   each icon follows the nav text colour: muted idle, ink on hover, sage active. */
.nav-ico { display: inline-flex; align-items: center; }
.nav-ico svg { width: 17px; height: 17px; }
.mono-ico { display: inline-flex; align-items: center; vertical-align: -.12em; }
.mono-ico svg { width: 1em; height: 1em; }
.who { color: var(--muted); font-size: 13px; }

/* Main column (to the right of the sidebar). */
.app-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; min-height: 100vh; }
.mobile-bar { display: none; align-items: center; gap: 12px; padding: 10px 16px; background: var(--surface); border-bottom: 1px solid var(--line); }
.hamburger { background: none; border: 1px solid var(--line); border-radius: 8px; font-size: 18px; line-height: 1; padding: 5px 11px; cursor: pointer; color: var(--ink); }
.sidebar-backdrop { position: fixed; inset: 0; background: rgba(16,24,40,.4); z-index: 60; }

/* Below the desktop width the sidebar becomes a slide-in drawer. */
@media (max-width: 900px) {
    #app-view { display: block; }
    .sidebar {
        position: fixed; left: 0; top: 0; height: 100vh; height: 100dvh; width: 264px; z-index: 70;
        transform: translateX(-100%); transition: transform .22s ease; box-shadow: var(--shadow);
    }
    .sidebar.open { transform: none; }
    .sidebar-x { display: block; }
    .mobile-bar { display: flex; }
    .app-main { min-height: 100vh; }
}

/* ---------- Layout ---------- */
.container { max-width: 1080px; width: 100%; margin: 0 auto; padding: 22px 20px 48px; flex: 1; }
.site-footer {
    border-top: 1px solid var(--line); background: var(--card);
    padding: 14px 20px; color: var(--muted); font-size: 13px; text-align: center;
}

/* ---------- Universe home (tool picker) ---------- */
.home-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 14px; }
.home-card { display: flex; gap: 14px; align-items: flex-start; padding: 18px; background: var(--card);
    border: 1px solid var(--line); border-radius: 12px; text-decoration: none; color: var(--ink);
    box-shadow: var(--shadow); transition: border-color .12s, transform .06s; }
.home-card:hover { transform: translateY(-2px); box-shadow: 0 2px 6px rgba(16,24,40,.07), 0 14px 32px rgba(16,24,40,.12); }
/* Cards stay plain white; only the tool ICON carries colour, per kind
   (settings = blue, generate = green, action = violet). Top-bar icons stay
   monochrome (they follow the nav text colour). */
.home-card.kind-settings .home-ico { color: #3f6fd0; }
.home-card.kind-generate .home-ico { color: #2e9e5b; }
.home-card.kind-action .home-ico { color: #7b4bcc; }
.home-section { font-size: 12.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .6px; color: var(--muted); margin: 26px 2px 12px; padding-top: 4px; border-top: 1px solid var(--line); }
.home-section:first-of-type { }
.subbar-div { display: block; height: 1px; background: var(--line); margin: 8px 8px; }
/* The section a group of sidebar tools belongs to, named the way the universe
   landing page names it. A rule on its own only says “something changed”. */
.subbar-label { display: block; padding: 2px 10px 4px; font-size: 10.5px; font-weight: 700;
    text-transform: uppercase; letter-spacing: .5px; color: var(--muted); opacity: .75; }
.home-ico { flex: none; display: inline-flex; }
.home-ico svg { width: 26px; height: 26px; }
.home-card-title { font-weight: 700; font-size: 16px; }
.home-card-desc { color: var(--muted); font-size: 13px; margin-top: 3px; }

/* ---------- Tool header / toolbar ---------- */
.tool-head { margin-bottom: 16px; }
.tool-head h1 { margin: 0 0 2px; font-size: 22px; }
.tool-head p { margin: 0; color: var(--muted); font-size: 14px; }
.toolbar {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    margin: 16px 0; flex-wrap: wrap;
}
.toolbar-left { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.toolbar-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* r29 (r29-hrplan): when the filters fill the line, the actions wrap onto the next one on the RIGHT, not under the
   first filter — five screens carried a private copy of this rule. */
.toolbar > .toolbar-actions { margin-left: auto; }
.hint { margin: 0; color: var(--muted); font-size: 13px; line-height: 1.5; }
/* AdminShell.historyToggle (r28, deprecated by r29 — no hint text any more): one toolbar item. */
.history-toggle { display: inline-flex; align-items: center; gap: 8px; flex-wrap: wrap; min-width: 0; }
/* r29 — AdminShell.olderButton: the row centred UNDER a table (owner, 10 Oct: « at the bottom of the table centered a
   button saying view older »). « Afficher plus » (paging) and « Voir les plus anciens » side by side; wraps on a
   phone. Outside the panel it keeps the page's rhythm; inside one (after .table-scroll) it keeps off the panel edge. */
.table-older { display: flex; justify-content: center; align-items: center; flex-wrap: wrap; gap: 8px; margin: 12px 0 4px; }
.panel > .table-older { margin: 0; padding: 12px 16px; border-top: 1px solid var(--line); }
.table-older:empty { display: none; }
/* r29: inside a panel that already has padding (.panel-pad, Home's .dash-panel) the row needs no rule nor padding of its own. */
.panel.panel-pad > .table-older, .dash-panel > .table-older { margin: 12px 0 0; padding: 0; border-top: 0; }
/* A panel that holds a form, filters, text or buttons rather than an edge-to-edge table: .panel-pad gives it the
   padding every such panel needs (r29: « buttons are edge to edge not respecting any margins or paddings »). */
.panel-pad { padding: 16px 18px; }
@media (max-width: 720px) { .panel-pad { padding: 12px; } }
/* An actions row closing a padded panel (or a section in it): right-aligned, wraps, never touches the edge. */
.panel-foot { display: flex; justify-content: flex-end; align-items: center; flex-wrap: wrap; gap: 8px; margin-top: 14px; }
.count { color: var(--muted); font-size: 14px; }
.inline { display: inline-flex; align-items: center; gap: 8px; font-size: 14px; color: var(--muted); }
/* Filters share one toolbar line. 170px was generous enough that four of them
   overflowed and wrapped; the ceiling stops one long option widening the row. */
.inline select, .inline input { min-width: 140px; max-width: 220px; }
/* r29 fix (DS-4): a filter keeps its label on one line — the toolbar wraps whole filters instead of shrinking one and
   breaking « Sous-catégorie » after its hyphen (Achats ▸ Tarifs at 1024 px). A multi-select's list inside wraps as
   before; on a phone a label longer than the screen may wrap (see the ≤ 720 px block). */
.inline { white-space: nowrap; flex: none; }
.inline .msel-pop { white-space: normal; }

/* ---------- Multi-select checkbox dropdown (tool filters) ---------- */
.msel { position: relative; display: inline-block; }
.msel-btn { display: inline-flex; align-items: center; gap: 6px; max-width: 230px; padding: 8px 10px; border: 1px solid var(--line); border-radius: 8px; background: #fff; color: var(--ink); font: inherit; font-size: 14px; cursor: pointer; }
.msel-btn:hover { border-color: var(--accent); }
.msel-lbl { color: var(--muted); white-space: nowrap; }
.msel-cur { font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 140px; }
.msel-caret { color: var(--muted); font-size: 10px; margin-left: 2px; }
.msel.msel-on .msel-btn { border-color: var(--accent); background: var(--accent-soft); }
.msel-pop { position: absolute; z-index: 40; top: calc(100% + 4px); left: 0; min-width: 210px; max-width: calc(100vw - 40px); max-height: 320px; overflow-y: auto; background: var(--card); border: 1px solid var(--line); border-radius: 10px; box-shadow: var(--shadow); padding: 6px; }
.msel-tools { display: flex; justify-content: flex-end; padding: 2px 4px 6px; border-bottom: 1px solid var(--line); margin-bottom: 4px; }
.msel-clear { background: none; border: none; color: var(--accent); font: inherit; font-size: 12.5px; font-weight: 600; cursor: pointer; padding: 2px 4px; }
.msel-opt { display: flex; align-items: center; gap: 8px; padding: 6px 8px; border-radius: 6px; font-size: 14px; font-weight: 400; cursor: pointer; }
.msel-opt:hover { background: var(--soft); }
.msel-opt input { width: auto; min-width: 0; margin: 0; }
.msel-opt span { flex: 1; }
.msel-empty { padding: 8px; color: var(--muted); text-align: center; }
.msel-chip { display: inline-flex; align-items: center; gap: 6px; padding: 6px 10px; border-radius: 999px; background: var(--accent-soft); color: var(--ink); font-size: 13px; font-weight: 600; }
.msel-chip-x { background: none; border: none; color: var(--muted); font-size: 16px; line-height: 1; cursor: pointer; padding: 0; }
.msel-chip-x:hover { color: var(--ink); }

/* ---------- Settings-form panels (Admin ▸ Email, Integrations, …) ----------
   Shared because more than one tool uses them; a tool-local view.css is only
   loaded while that tool is open, so anything reused must live here. */
.settings-panel { padding: 18px; margin-bottom: 16px; }
.settings-panel form > label, .settings-panel > label { margin-bottom: 12px; display: block; }
.settings-h { margin: 4px 0 12px; font-size: 14px; }
.settings-h:not(:first-child) { margin-top: 20px; }
.settings-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
.settings-actions input[type=email], .settings-actions input[type=text] { width: auto; min-width: 240px; margin-top: 0; }

/* CSV import result */
.csv-imp { display: flex; flex-direction: column; gap: 12px; }
.csv-ok { color: var(--accent); font-weight: 600; margin: 4px 0 0; }
.csv-bad { color: #b23b3b; font-weight: 600; margin: 8px 0 4px; }
.csv-errs { margin: 0; padding-left: 18px; color: var(--muted); font-size: 13px; max-height: 220px; overflow-y: auto; }

/* Error log detail */
.el-detail { display: flex; flex-direction: column; gap: 10px; }
.el-detail .tkv-cell { }
.el-stack { background: #1d2330; color: #e6e8ec; border-radius: 8px; padding: 12px; font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12px; line-height: 1.5; white-space: pre-wrap; word-break: break-word; max-height: 320px; overflow-y: auto; margin: 4px 0 0; }

/* Preventive "due today" heads-up on the maintenance dashboard */
.pm-banner { background: #fff8e6; border: 1px solid #e6c34d; border-radius: 10px; padding: 12px 14px; margin-bottom: 16px; }
.pm-banner-h { font-weight: 700; color: #8a6d1a; margin-bottom: 6px; }
.pm-banner-list { margin: 0 0 8px; padding-left: 20px; color: var(--ink); font-size: 14px; line-height: 1.6; }

/* clickable analytics chart rows / slices (drill-through to Tickets) */
.mbar-row.is-link, .pie-leg.is-link { cursor: pointer; border-radius: 6px; }
.mbar-row.is-link:hover, .pie-leg.is-link:hover { background: var(--accent-soft); }
.mkpi.is-link { cursor: pointer; }
.mkpi.is-link:hover { border-color: var(--accent); }
.pie-svg .pie-slice { cursor: pointer; }
.pie-svg .pie-slice:hover { opacity: .82; }

/* ---------- Forms ---------- */
label { display: block; font-size: 14px; font-weight: 500; }
label small { font-weight: 400; color: var(--muted); margin-left: 4px; }
/* input:not([type]) — an input written without a type IS a text box (the HTML default); without it here such
   a field rendered as a bare browser box (Formations « Employé », 18 Sep). Views should still write type="text". */
input:not([type]), input[type=text], input[type=password], input[type=date], input[type=number], input[type=search],
input[type=time], input[type=email], input[type=url], input[type=tel], input[type=month], input[type=color],
input[type=week], input[type=datetime-local],
select, textarea {
    width: 100%; margin-top: 5px; padding: 9px 10px; border: 1px solid var(--line);
    border-radius: 8px; font: inherit; color: var(--ink); background: #fff;
    box-sizing: border-box; min-width: 0;
}
/* r29: datetime-local and week joined the list above — Ventes ▸ Prévisions tarifaires « Quel tarif affichions-nous ? »
   (a datetime-local) rendered as a bare browser box (owner, 10 Oct: « does not have css »). A colour well keeps a
   usable height and no text padding. */
input[type=color] { height: 40px; padding: 4px; cursor: pointer; }
/* Date/time controls have a browser minimum width that overflows narrow
   containers (esp. mobile) — cap them to the container. */
input[type=date], input[type=time], input[type=datetime-local], input[type=month], input[type=week] { max-width: 100%; }
/* iOS Safari renders date/time as a native control that ignores width and adds
   an internal margin to the value, so it sits a few px wider than its box.
   Scoped to iOS (desktop keeps its native picker). */
@supports (-webkit-touch-callout: none) {
    input[type=date], input[type=time] { -webkit-appearance: none; appearance: none; min-height: 40px; }
    input[type=date]::-webkit-date-and-time-value, input[type=time]::-webkit-date-and-time-value { text-align: left; margin: 0; }
}
input:focus, select:focus, textarea:focus {
    outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft);
}
textarea { resize: vertical; }
fieldset { border: 1px solid var(--line); border-radius: 10px; padding: 12px 14px 14px; margin: 0; }
legend { font-size: 13px; font-weight: 600; color: var(--muted); padding: 0 6px; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.grid-3 { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 12px; }
.checkbox { display: flex; align-items: center; gap: 8px; font-weight: 500; }
.checkbox input { width: auto; margin: 0; }

/* ---------- r29: every control styled with no class (docs/UI-CONVENTIONS.md) ----------
   The owner, 10 Oct 2026: « some of these inputs really dont have any css … we need a convention ». A view writes the
   plain element inside its <label>; the rules below give it the house look. A tool's view.css only adds LAYOUT. */
/* File picker — the box like a field, its button like a ghost button (« choose file input for arret maladie is not in
   the css convention »). :where() keeps the box at zero specificity: a tool that hides the picker behind its own
   button (label.btn > input[hidden]) or lays it over one (.db-upin) is never disturbed. */
:where(input[type=file]) {
    display: block; width: 100%; max-width: 100%; min-width: 0; margin-top: 5px; padding: 5px;
    border: 1px solid var(--line); border-radius: 8px; background: #fff;
    font: inherit; font-size: 13.5px; color: var(--muted); cursor: pointer;
}
input[type=file]::file-selector-button {
    font: inherit; font-size: 13px; font-weight: 500; margin: 0 10px 0 0; padding: 5px 11px;
    border: 1px solid var(--line); border-radius: 6px; background: var(--soft); color: var(--ink); cursor: pointer;
}
input[type=file]::-webkit-file-upload-button {
    font: inherit; font-size: 13px; font-weight: 500; margin: 0 10px 0 0; padding: 5px 11px;
    border: 1px solid var(--line); border-radius: 6px; background: var(--soft); color: var(--ink); cursor: pointer;
}
input[type=file]:hover::file-selector-button { border-color: #cfd4dc; background: #fff; }
input[type=file]:disabled::file-selector-button { cursor: not-allowed; }
/* Checkbox and radio: the sage accent, one size (min-* so a tool's `width: auto` keeps it), aligned with the text
   that follows inside their label. Margin at zero specificity: a flex row with its own gap sets margin: 0. */
input[type=checkbox], input[type=radio] {
    accent-color: var(--accent); width: 16px; height: 16px; min-width: 16px; min-height: 16px; max-width: none;
    flex: none; cursor: pointer;
}
:where(input[type=checkbox], input[type=radio]) { margin: 0 6px 0 0; vertical-align: -3px; }
/* r29 fix1 (R29P-RV1-02): a box ALONE in a table cell (select-all / row pick: HR ▸ Employés, Achats ▸ Fiches produit)
   has no text to keep apart from — no right margin, so a centred cell centres it. Zero specificity, like the rule above. */
:where(td, th) > :where(input[type=checkbox], input[type=radio]):only-child { margin: 0; vertical-align: middle; }
/* Keyboard focus: the same sage ring on every button and on the file picker (fields have theirs above). */
.btn:focus-visible, input[type=file]:focus-visible, input[type=checkbox]:focus-visible, input[type=radio]:focus-visible {
    outline: 2px solid var(--accent); outline-offset: 2px;
}
/* States. Disabled: greyed, no pointer. Read-only: the soft fill (shown, never typed). Invalid: the danger border —
   only once the user has touched the field (:user-invalid), or when a script says so (aria-invalid="true"). */
input:disabled, select:disabled, textarea:disabled { background: var(--soft); color: var(--muted); cursor: not-allowed; }
input[type=checkbox]:disabled, input[type=radio]:disabled { cursor: not-allowed; opacity: .55; }
input[readonly], textarea[readonly] { background: var(--soft); }
input[aria-invalid="true"], select[aria-invalid="true"], textarea[aria-invalid="true"] { border-color: var(--danger); }
input:user-invalid, select:user-invalid, textarea:user-invalid { border-color: var(--danger); }
.btn:disabled, .btn[aria-disabled="true"] { opacity: .5; cursor: not-allowed; }
/* A filter label in a toolbar (.inline): its control sits on the text's line, not 5 px below it. */
.inline > select, .inline > input { margin-top: 0; }

.form-error { background: var(--danger-bg); color: var(--danger); padding: 8px 10px; border-radius: 8px; font-size: 14px; }
.form-ok { background: var(--ok-bg); color: var(--ok); padding: 8px 10px; border-radius: 8px; font-size: 14px; }

/* ---------- Table ---------- */
.panel { background: var(--card); border: 1px solid var(--line); border-radius: var(--radius); overflow: hidden; box-shadow: var(--shadow); }
/* r29 fix (DS-1): a scroller is the positioned box of what it holds. A screen-reader-only label (.sr-only, absolutely
   positioned) in a wide table had no positioned ancestor: neither the table's scroller nor the panel (which scrolls on
   a phone) clipped it, and it widened the PAGE — RH ▸ Congés at 390 px was 433 px wide, its dialog's × off-screen. */
.panel, .table-scroll { position: relative; }

/* Standardized "API — for other tools" panel (AdminShell.apiDoc). Shared by
   every tool that documents a public API, so they all look identical. */
.api-doc { margin-top: 18px; padding: 16px 18px; }
.api-doc-head { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
.api-doc-head .rg-h2 { margin: 0; font-size: 15px; }
.api-doc-badge { font-size: 10.5px; font-weight: 700; letter-spacing: .4px; text-transform: uppercase; color: var(--sel-ink); background: var(--sel-bg); border-radius: 999px; padding: 2px 9px; white-space: nowrap; }
.api-doc .hint { margin: 4px 0 12px; }
.api-doc-table td, .api-doc-table th { vertical-align: middle; }
.api-ep { display: inline-block; font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12.5px; background: var(--soft); border: 1px solid var(--line); border-radius: 6px; padding: 2px 7px; white-space: nowrap; }
.api-ep-try { font-size: 12.5px; font-weight: 600; color: var(--accent); text-decoration: none; white-space: nowrap; }
.api-ep-try:hover { text-decoration: underline; }
.api-doc-note { margin: 10px 0 0; color: var(--muted); }
/* Sub-sections of an API panel: parameters, response fields, sample, errors.
   Each table sizes its own columns, so the leading code column is pinned to keep
   them aligned down the page instead of jumping per section. */
.api-doc-h3 { margin: 18px 0 8px; font-size: 12px; font-weight: 700; letter-spacing: .4px; text-transform: uppercase; color: var(--muted); }
.api-doc-base { margin: 0 0 12px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
/* r29 (r29-hrother): the shell's own « nothing here / a type name » note — app.js borrowed the accounts tool's .ac-none,
   unstyled until Comptes had been opened in that session. */
.muted-note { color: var(--muted); font-size: 13px; font-style: italic; }
.api-doc-table { table-layout: fixed; width: 100%; min-width: 520px; }
.api-doc-table td { word-break: break-word; }
/* Only the narrow reference tables (parameters / fields / errors) get the fixed
   210px key column. The endpoint table is left auto so full absolute URLs have
   room — pinning it made long endpoints run over the description beside them. */
.api-doc-sub td:first-child, .api-doc-sub th:first-child { width: 210px; }
/* .api-ep is nowrap by default so short chips stay on one line; inside a
   fixed-layout doc table it must be allowed to wrap or it overflows its cell. */
.api-doc-table .api-ep { white-space: normal; overflow-wrap: anywhere; max-width: 100%; }
/* r38 (census): the base URL above the tables wraps too — a production origin (« https://…/api/kiosks/palm-appart-club »,
   about 52 characters) is wider than a phone's panel: Kiosques ▸ API was 58 px too wide at 320 px. */
.api-doc-base .api-ep { white-space: normal; overflow-wrap: anywhere; min-width: 0; max-width: 100%; }
/* r38: the panel's tables are .stack (one card per row on a phone — the 520 px minimum made every API panel 208 px
   wider than a 390 px phone); the panel's own padding is the cards' edge. */
.api-doc-table.stack { --stack-pad: 0px; --stack-label: 104px; }
.api-eg { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12px; color: var(--muted); white-space: nowrap; }
.api-doc-json { margin: 0; padding: 12px 14px; background: var(--soft); border: 1px solid var(--line); border-radius: 8px; overflow-x: auto; }
.api-doc-json code { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12.5px; line-height: 1.55; white-space: pre; }
/* required / optional pills, matching the access badges in Admin ▸ API */
.ax { display: inline-block; font-size: 10.5px; font-weight: 700; letter-spacing: .3px; text-transform: uppercase; border-radius: 999px; padding: 2px 8px; white-space: nowrap; }
.ax-req { color: #7a3d3d; background: #f6e7e7; }
.ax-opt { color: var(--muted); background: var(--soft); }
/* r29 fix 2 (R29-SHELLPERF-RV2-01): the access badge AdminShell.apiDoc prints in every tool's API panel head
   (« Sans connexion » / « Session requise »). Admin ▸ API keeps its own bordered copy, scoped to .ad-table. */
.ax-pub { color: #1e7a43; background: rgba(46,158,91,.12); }
.ax-ses { color: var(--muted); background: var(--soft); }
/* r29b: « Clé requise » — an API guarded by a secret key, not a session (Admin ▸ Sauvegarde's NAS pull); the blue of
   Admin ▸ API's « Clé d'accès ». */
.ax-key { color: #2b53a5; background: rgba(63,111,208,.12); }
/* r38 (the census of 18 Oct 2026 — every tool and tab at 1366 / 1024 / 390 px with realistic data: no table wider than its
   box): a table's wrapper no longer scrolls sideways (the owner, 16 Oct: « every table in the app »). A table that does not
   fit is a bug — it fits by the shell's own means (« Tables that fit their box », below), never by a scroll. A panel that
   is its own table box (.panel.table-scroll) clips like every panel: overflow-x: visible beside the panel's
   overflow-y: hidden would compute to auto — a scroll again. */
.table-scroll { overflow-x: visible; }
.panel.table-scroll { overflow-x: hidden; }

/* Small "remove row" button (used by editable tables). */
.rg-x { border: none; background: #f0f1f3; color: var(--danger); width: 32px; height: 36px; border-radius: 8px; cursor: pointer; font-size: 17px; flex: none; line-height: 1; }
.rg-x:hover { background: var(--danger-bg); }
.data-table { width: 100%; border-collapse: collapse; }
.data-table th, .data-table td { text-align: left; padding: 12px 14px; border-bottom: 1px solid var(--line); vertical-align: middle; }
.data-table th { font-size: 12px; text-transform: uppercase; letter-spacing: .5px; color: var(--muted); background: #fafbfc; }
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr:hover { background: #fafbfc; }
.actions-col { text-align: right; white-space: nowrap; }

/* ---------- Tables that fit their box (r38) ----------
   The owner, 16 Oct 2026: no table scrolls sideways, at any width (docs/UI-CONVENTIONS.md §1). Every table's wrapper,
   .table-scroll, is a size container named `dt`: a table answers to the width of its OWN box (the sidebar, a dialog,
   a padded panel count), not to the window's. The scale, the box's width:
     sm  under 640 px    a phone (a 390 px phone's list is 348 px), a window just past the drawer (901 px: 621 px)
     md  under 800 px    also a 1024 px laptop beside the sidebar (744 px)
     lg  under 1024 px   also a 1280 px screen (1000 px; 1015 px with no scrollbar) — from 1320 px the list is 1038 px (the 1080 px column)
   · table.stack / table.stack-md / table.stack-lg: one card per row under sm / md / lg (lg: a designed wide grid
     only — a list keeps its condensed rows from 800 px) — every cell a line, its column's name before its
     value (td[data-label]; AdminShell.labelCells copies each th's text there), a cell without a label a full-width
     line (the record's title), td.actions-col its own line with its buttons wrapped. Everything in a card wraps
     (white-space: pre-line, inherited by what the cell holds — the user's own line breaks stay; <pre> / <code> /
     <textarea> keep theirs): a nowrap chip or name that made the column wide cannot run past the card. Columns hidden to fit the
     TABLE (.hide-sm, .hide-cq-*) come back in the card; a tool's own rule (its prefix) can still leave one out.
   · .hide-cq-sm / .hide-cq-md / .hide-cq-lg: a secondary column (its th AND its td) hidden under sm / md / lg.
   · td.cell-cut: one line ending in « … » (the screen puts the whole text in the cell's title). Its column is as
     narrow as its header — it gives way first — unless the tool gives that th a share (`width: 28%`, measured: it
     then keeps that share and still gives way when the box narrows). In a card it wraps in full.
   A tool tunes a card only through --stack-label (the label's width) / --stack-pad (the line's side padding). */
.table-scroll { container: dt / inline-size; }
.data-table td.cell-cut { max-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@container dt (max-width: 1023.98px) { .hide-cq-lg { display: none; } }
@container dt (max-width: 799.98px) { .hide-cq-md { display: none; } }
@container dt (max-width: 639.98px) { .hide-cq-sm { display: none; } }
@container dt (max-width: 639.98px) {
    /* display at no specificity (:where): a cell or row a tool hides (its class, `hidden`) stays hidden */
    :where(table.stack, table.stack > tbody, table.stack > tfoot, table.stack > * > tr:not([hidden]), table.stack > * > tr:not([hidden]) > :not([hidden])) { display: block; }
    :where(table.stack > * > tr:not([hidden]) > :not([hidden])) { display: flow-root; }
    table.stack > thead { display: none; }
    .table-scroll table.stack { width: 100%; min-width: 0; max-width: none; table-layout: auto; }
    .table-scroll table.stack > * > tr { padding: 8px 0; border-bottom: 1px solid var(--line); }
    .table-scroll table.stack > *:last-child > tr:last-child { border-bottom: 0; }
    .table-scroll table.stack > * > tr > * { width: auto; min-width: 0; max-width: none; height: auto; border: 0; text-align: left;
        white-space: pre-line; overflow: visible; text-overflow: clip; overflow-wrap: break-word; padding: 3px var(--stack-pad, 14px); }
    .table-scroll table.stack > * > tr > [data-label]:not([data-label=""]) { padding-left: calc(var(--stack-pad, 14px) + var(--stack-label, 112px) + 10px); }
    .table-scroll table.stack > * > tr > [data-label]:not([data-label=""])::before { content: attr(data-label); float: left; box-sizing: border-box;
        width: var(--stack-label, 112px); margin-left: calc(-10px - var(--stack-label, 112px)); padding: 2px 0 0; text-align: left; font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 11px; line-height: 1.5;
        font-weight: 600; font-style: normal; letter-spacing: .3px; text-transform: uppercase; color: var(--muted); overflow-wrap: break-word; }
    .table-scroll table.stack > * > tr > .actions-col { padding-top: 8px; }
    .table-scroll table.stack > * > tr > * .row-actions { display: flex; flex-wrap: wrap; justify-content: flex-start; }
    .table-scroll table.stack > * > tr > * :not(pre, code, textarea) { white-space: inherit; }
    .table-scroll table.stack > * > tr > .empty { padding: 40px 20px; text-align: center; }
    .table-scroll table.stack > * > tr:not([hidden]) > :is(.hide-sm, .hide-cq-sm, .hide-cq-md, .hide-cq-lg):not([hidden]) { display: flow-root; }
}
/* the same card, under md, for table.stack-md (test/r38-shell-stack.test.js keeps the three blocks identical) */
@container dt (max-width: 799.98px) {
    :where(table.stack-md, table.stack-md > tbody, table.stack-md > tfoot, table.stack-md > * > tr:not([hidden]), table.stack-md > * > tr:not([hidden]) > :not([hidden])) { display: block; }
    :where(table.stack-md > * > tr:not([hidden]) > :not([hidden])) { display: flow-root; }
    table.stack-md > thead { display: none; }
    .table-scroll table.stack-md { width: 100%; min-width: 0; max-width: none; table-layout: auto; }
    .table-scroll table.stack-md > * > tr { padding: 8px 0; border-bottom: 1px solid var(--line); }
    .table-scroll table.stack-md > *:last-child > tr:last-child { border-bottom: 0; }
    .table-scroll table.stack-md > * > tr > * { width: auto; min-width: 0; max-width: none; height: auto; border: 0; text-align: left;
        white-space: pre-line; overflow: visible; text-overflow: clip; overflow-wrap: break-word; padding: 3px var(--stack-pad, 14px); }
    .table-scroll table.stack-md > * > tr > [data-label]:not([data-label=""]) { padding-left: calc(var(--stack-pad, 14px) + var(--stack-label, 112px) + 10px); }
    .table-scroll table.stack-md > * > tr > [data-label]:not([data-label=""])::before { content: attr(data-label); float: left; box-sizing: border-box;
        width: var(--stack-label, 112px); margin-left: calc(-10px - var(--stack-label, 112px)); padding: 2px 0 0; text-align: left; font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 11px; line-height: 1.5;
        font-weight: 600; font-style: normal; letter-spacing: .3px; text-transform: uppercase; color: var(--muted); overflow-wrap: break-word; }
    .table-scroll table.stack-md > * > tr > .actions-col { padding-top: 8px; }
    .table-scroll table.stack-md > * > tr > * .row-actions { display: flex; flex-wrap: wrap; justify-content: flex-start; }
    .table-scroll table.stack-md > * > tr > * :not(pre, code, textarea) { white-space: inherit; }
    .table-scroll table.stack-md > * > tr > .empty { padding: 40px 20px; text-align: center; }
    .table-scroll table.stack-md > * > tr:not([hidden]) > :is(.hide-sm, .hide-cq-sm, .hide-cq-md, .hide-cq-lg):not([hidden]) { display: flow-root; }
}
/* the same card, under lg, for table.stack-lg (a designed wide grid: Home ▸ Procédures' step editor) */
@container dt (max-width: 1023.98px) {
    :where(table.stack-lg, table.stack-lg > tbody, table.stack-lg > tfoot, table.stack-lg > * > tr:not([hidden]), table.stack-lg > * > tr:not([hidden]) > :not([hidden])) { display: block; }
    :where(table.stack-lg > * > tr:not([hidden]) > :not([hidden])) { display: flow-root; }
    table.stack-lg > thead { display: none; }
    .table-scroll table.stack-lg { width: 100%; min-width: 0; max-width: none; table-layout: auto; }
    .table-scroll table.stack-lg > * > tr { padding: 8px 0; border-bottom: 1px solid var(--line); }
    .table-scroll table.stack-lg > *:last-child > tr:last-child { border-bottom: 0; }
    .table-scroll table.stack-lg > * > tr > * { width: auto; min-width: 0; max-width: none; height: auto; border: 0; text-align: left;
        white-space: pre-line; overflow: visible; text-overflow: clip; overflow-wrap: break-word; padding: 3px var(--stack-pad, 14px); }
    .table-scroll table.stack-lg > * > tr > [data-label]:not([data-label=""]) { padding-left: calc(var(--stack-pad, 14px) + var(--stack-label, 112px) + 10px); }
    .table-scroll table.stack-lg > * > tr > [data-label]:not([data-label=""])::before { content: attr(data-label); float: left; box-sizing: border-box;
        width: var(--stack-label, 112px); margin-left: calc(-10px - var(--stack-label, 112px)); padding: 2px 0 0; text-align: left; font-family: system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; font-size: 11px; line-height: 1.5;
        font-weight: 600; font-style: normal; letter-spacing: .3px; text-transform: uppercase; color: var(--muted); overflow-wrap: break-word; }
    .table-scroll table.stack-lg > * > tr > .actions-col { padding-top: 8px; }
    .table-scroll table.stack-lg > * > tr > * .row-actions { display: flex; flex-wrap: wrap; justify-content: flex-start; }
    .table-scroll table.stack-lg > * > tr > * :not(pre, code, textarea) { white-space: inherit; }
    .table-scroll table.stack-lg > * > tr > .empty { padding: 40px 20px; text-align: center; }
    .table-scroll table.stack-lg > * > tr:not([hidden]) > :is(.hide-sm, .hide-cq-sm, .hide-cq-md, .hide-cq-lg):not([hidden]) { display: flow-root; }
}

/* ---------- Line-item editor table ----------
   Rows of inputs being typed into, as opposed to .data-table, which
   displays saved records. Borderless and tight so a form full of lines
   still reads as one table. Column widths stay with each tool;
   everything visual lives here. */
.line-table { width: 100%; border-collapse: collapse; }
.line-table th { text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .4px;
    color: var(--muted); padding: 2px 6px 6px; white-space: nowrap; }
.line-table td { padding: 3px 6px; vertical-align: middle; }
.line-table th.line-num, .line-table td.line-num { text-align: right; }
.line-table input, .line-table select { margin-top: 0; }
/* Fixed width so a long amount cannot steal room from the price input. */
.line-table td.line-total { width: 104px; font-weight: 700;
    font-family: ui-monospace, Menlo, Consolas, monospace; white-space: nowrap; }
.line-foot { display: flex; align-items: center; justify-content: space-between; margin-top: 8px; gap: 12px; }
.line-grand { font-size: 15px; }
.line-grand strong { margin-left: 6px; }
.row-actions { display: inline-flex; gap: 6px; justify-content: flex-end; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 13px; color: var(--muted); }
.empty { padding: 40px 20px; text-align: center; color: var(--muted); }

/* ---------- Status pill / toggle (shared: active/inactive) ---------- */
.pill { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; font-weight: 600; padding: 3px 10px; border-radius: 999px; cursor: pointer; border: 1px solid transparent; user-select: none; }
.pill-on { background: var(--ok-bg); color: var(--ok); }
.pill-off { background: #eef0f3; color: #5f6673; }   /* r29 fix (DS-3): --off on this grey read 2.28:1; this is 5.06:1 (AA) */
.pill .dot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
/* r29 (r29-hrplan): the status pills several HR / Chef screens each defined (same colours) — once, here. */
.pill.pill-warn { background: #fff7ed; color: #9a3412; } .pill.pill-warn .dot { background: #f97316; }
.pill.pill-open { background: #fef2f2; color: #b91c1c; } .pill.pill-open .dot { background: #ef4444; }
.pill.pill-red { background: var(--danger-bg); color: var(--danger); } .pill.pill-red .dot { background: #e5484d; }
.pill.pill-info { background: #e8f0fe; color: #2f57b0; } .pill.pill-info .dot { background: #3159b3; }
.pill.pill-violet { background: #f2ecfb; color: #6f3fc0; } .pill.pill-violet .dot { background: #7c4dcf; }

/* ---------------------------------------------------------------------------
   Maintenance (CMMS) — shared classes across every tool in the universe.
--------------------------------------------------------------------------- */
.tag { display: inline-block; font-size: 12px; font-weight: 600; padding: 2px 9px; border-radius: 999px; background: var(--sel-bg); color: var(--sel-ink); white-space: nowrap; }
/* (.viewtoggle / .vt, the Liste / Calendrier switch, became the shared .tabs.tabs-seg — r29b, « Tabs » below.) */
tr.mrow { cursor: pointer; }
tr.mrow:hover { background: var(--soft); }
.mtk { display: inline-block; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .3px; padding: 2px 9px; border-radius: 999px; white-space: nowrap; }
.mtk-open { background: #dbeafe; color: #1e40af; }
.mtk-in_progress { background: #fef3c7; color: #92400e; }
.mtk-on_hold { background: #e5e7eb; color: #374151; }
.mtk-resolved { background: #dcfce7; color: #166534; }
.mtk-closed { background: #e5e7eb; color: #4b5563; }
.mtk-danger { background: #fee2e2; color: #991b1b; }
/* Audit filter bar: keep every filter on one line instead of full-width rows. */
.mau-filters { gap: 10px; }
.mau-filters .inline { gap: 6px; }
.mau-filters .inline select { min-width: 120px; }
.mau-filters .inline input[type=date] { min-width: 0; width: auto; }
.mau-filters input[type=search] { width: auto; min-width: 130px; max-width: 180px; margin-top: 0; }
.mpri { display: inline-block; font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 6px; white-space: nowrap; }
.mpri-low { background: #eef2f6; color: #64748b; }
.mpri-medium { background: #dbeafe; color: #1e40af; }
.mpri-high { background: #ffedd5; color: #9a3412; }
.mpri-urgent { background: #fee2e2; color: #991b1b; }
.mdetail-meta { color: var(--muted); font-size: 13px; margin-bottom: 8px; }
.mdetail-h { font-size: 13px; text-transform: uppercase; letter-spacing: .4px; color: var(--muted); margin: 18px 0 8px; }
/* Ticket photo attachments */
.mt-photos .mt-imgs { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 8px; }
.mt-thumb { position: relative; width: 76px; height: 76px; border-radius: 8px; overflow: hidden; border: 1px solid var(--line); cursor: pointer; background: var(--soft); }
.mt-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.mt-thumb-x { position: absolute; top: 2px; right: 2px; width: 20px; height: 20px; border: none; border-radius: 50%; background: rgba(0,0,0,.6); color: #fff; font-size: 15px; line-height: 1; cursor: pointer; display: grid; place-items: center; padding: 0; }
.mt-addphoto { display: inline-block; cursor: pointer; }
.mt-addphoto .btn { display: inline-flex; align-items: center; }
.mt-imgstatus { margin-left: 10px; font-size: 12.5px; color: var(--muted); }
.mt-lightbox { display: grid; place-items: center; }
.mt-lightbox img { max-width: 100%; max-height: 80vh; border-radius: 8px; }
.cm-badge { display: inline-block; font-size: 11px; font-weight: 700; padding: 1px 7px; border-radius: 999px; background: var(--sel-bg); color: var(--sel-ink); vertical-align: middle; }
/* Ticket view (read-only) */
/* overflow-wrap: a title or a description holding a pasted link or a serial number wraps inside
   the dialog. Without it the dialog grew to its 720 px maximum on a 390 px phone, its × and
   buttons off-screen (WALK-08, r19); on a desktop the text ran past the card. */
.tkv-title { margin: 0 0 8px; font-size: 18px; overflow-wrap: anywhere; }
.tkv-badges { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.tkv-desc { margin: 0 0 12px; white-space: pre-wrap; overflow-wrap: anywhere; }
.tkv-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 16px; margin-bottom: 12px; }
.tkv-cell { min-width: 0; }
.tkv-cellval { font-size: 14.5px; color: var(--ink); overflow-wrap: anywhere; }
.tkv-res { margin-bottom: 12px; }
.tkv-res p { margin: 3px 0 0; white-space: pre-wrap; }
/* Comments */
.cm-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 12px; }
.cm-item { background: var(--soft); border-radius: 8px; padding: 8px 30px 8px 10px; font-size: 13.5px; position: relative; }
.cm-item.cm-auto { background: transparent; border-left: 2px solid var(--line); border-radius: 0; padding: 4px 30px 4px 10px; color: var(--muted); font-size: 12.5px; }
.cm-when { color: var(--muted); font-size: 11.5px; margin-left: 6px; }
.cm-chg { font-weight: 600; color: var(--ink); }
.cm-body { margin-top: 3px; white-space: pre-wrap; overflow-wrap: anywhere; }
.cm-del { position: absolute; top: 6px; right: 6px; border: none; background: none; color: var(--muted); font-size: 16px; line-height: 1; cursor: pointer; }
.cm-add { display: flex; gap: 8px; align-items: flex-end; margin-bottom: 8px; }
.cm-add textarea { margin-top: 0; }
@media (max-width: 720px) { .tkv-grid { grid-template-columns: 1fr; } }
.mkpi-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 20px; }
.mkpi { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 14px 16px; }
.mkpi-val { font-size: 28px; font-weight: 800; line-height: 1.1; color: var(--ink); }
.mkpi-lbl { font-size: 12.5px; color: var(--muted); margin-top: 4px; }
.mkpi.is-warn .mkpi-val { color: #b45309; }
.mkpi.is-bad .mkpi-val { color: #b42318; }
.mchart-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.mchart-grid + .mchart-grid { margin-top: 16px; }
.mchart { background: var(--card); border: 1px solid var(--line); border-radius: 12px; padding: 16px; }
.mchart h3 { margin: 0 0 12px; font-size: 14px; }
.mchart-wide { grid-column: 1 / -1; }
.col-wrap { width: 100%; overflow-x: auto; }
.col-svg { display: block; width: 100%; max-width: 560px; height: auto; margin: 0 auto; }
.col-axis { stroke: var(--line); stroke-width: 1; }
.col-val { font-size: 8px; fill: var(--muted); }
.col-cat { font-size: 9px; fill: var(--muted); text-transform: capitalize; }
.col-legend { justify-content: center; margin-top: 8px; }
.mbar-row { display: grid; grid-template-columns: 140px 1fr 46px; align-items: center; gap: 10px; margin-bottom: 8px; }
.mbar-lbl { font-size: 12.5px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mbar-track { background: var(--soft); border-radius: 6px; height: 16px; overflow: hidden; }
.mbar-fill { height: 100%; border-radius: 6px; background: var(--accent); min-width: 2px; }
.mbar-val { font-size: 12.5px; font-weight: 700; text-align: right; color: var(--ink); }
.pie-wrap { display: flex; gap: 14px; align-items: center; flex-wrap: wrap; }
.pie-svg { width: 110px; height: 110px; flex: none; }
.pie-legend { display: flex; flex-direction: column; gap: 4px; flex: 1; min-width: 130px; }
.pie-leg { display: flex; align-items: center; gap: 7px; font-size: 12.5px; }
.pie-dot { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.pie-leg-lbl { flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--ink); }
.pie-leg-val { color: var(--muted); white-space: nowrap; }
.mcal-head { display: flex; align-items: center; justify-content: center; gap: 16px; margin: 4px 0 12px; }
.mcal-title { font-size: 15px; text-transform: capitalize; min-width: 170px; text-align: center; }
/* r29b (SW1-01, the shared half): minmax(0, 1fr) — a plain 1fr track grows to its nowrap chips' min-content and the
   month scrolls sideways on a phone (Déplacements' own grid did, 195 px). A column is a seventh, never more. */
.mcal-grid { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); gap: 6px; }
.mcal-cell { min-width: 0; }
.mcal-wd { margin-bottom: 6px; }
.mcal-wdh { text-align: center; font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px; color: var(--muted); }
.mcal-cell { min-height: 84px; border: 1px solid var(--line); border-radius: 8px; padding: 4px; display: flex; flex-direction: column; gap: 3px; background: var(--card); }
.mcal-empty { border: none; background: transparent; }
.mcal-today { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }
.mcal-day { font-size: 11.5px; font-weight: 700; color: var(--muted); }
.mcal-today .mcal-day { color: var(--accent); }
.mcal-item { display: block; width: 100%; text-align: left; border: none; border-radius: 5px; padding: 2px 6px; font: inherit; font-size: 11px; font-weight: 600; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; background: var(--sel-bg); color: var(--sel-ink); border-left: 3px solid var(--accent); }
/* `.mcal-item.is-over` (light + dark) lived here until r3-platform. Nothing has
 * emitted it since the preventive calendar stopped painting every past occurrence
 * overdue — the class name appears in exactly one place now, a test asserting it is
 * NOT in the rendered grid (test/m2-preventive-screens-view.test.js). Dead paint is
 * worse than no paint: the next person to add an overdue state finds a rule that
 * looks wired and is not. */
.mcal-more { font-size: 10.5px; color: var(--muted); padding-left: 4px; }
@media (max-width: 720px) { .mchart-grid { grid-template-columns: 1fr; } .mcal-cell { min-height: 58px; } }
/* r29 fix1 (DS-2): the dark-mode copies of .mtk-* / .mpri-* lived here. The console is a light theme (no :root
   token is redefined for prefers-color-scheme: dark, the body and cards stay light), so on a phone or PC in dark mode
   they painted dark chips on a white card beside light ones. Chips are light only until a real dark theme exists
   (docs/UI-CONVENTIONS.md §6). */

/* ---------- Modal ---------- */
/* While a dialog is open the shell locks the page behind it (app.js, lockPage),
   so the screen shows ONE scrollbar: this overlay's. overscroll-behavior keeps a
   wheel or a swipe that reaches the end of the dialog from being handed to the
   page underneath. */
.modal { position: fixed; inset: 0; background: rgba(16,24,40,.45); display: grid; place-items: start center; padding: 40px 16px; overflow-y: auto; overscroll-behavior: contain; z-index: 50; }
.modal-card { background: var(--card); width: 100%; max-width: 720px; border-radius: 14px; box-shadow: var(--shadow); }
.modal-narrow { max-width: 420px; }
.modal-head { display: flex; align-items: center; justify-content: space-between; padding: 16px 20px; border-bottom: 1px solid var(--line); }
/* min-width + overflow-wrap: a title typed without spaces (a link, a booking reference) wraps
   instead of widening the dialog past a phone screen and pushing its × out of reach (R18GR-3). */
.modal-head h2 { margin: 0; font-size: 18px; min-width: 0; overflow-wrap: anywhere; }
.modal-close { background: none; border: none; font-size: 26px; line-height: 1; color: var(--muted); cursor: pointer; }
.modal-body { padding: 18px 20px 20px; display: flex; flex-direction: column; gap: 14px; }
.modal-form { display: flex; flex-direction: column; gap: 14px; }
.modal-foot { display: flex; justify-content: flex-end; gap: 10px; padding-top: 4px; }
/* r29 fix 2 (R29-SHELLPERF-RV2-02): confirmDialog's question is plain text (textContent); a caller that joins its
   sentences with "\n" (Tickets' « Retirer ce ticket à … » + what the change undoes) gets its line breaks back, and a
   long name or file name with no space wraps inside the narrow dialog instead of widening it on a phone. */
.confirm-dialog .confirm-text { white-space: pre-line; overflow-wrap: anywhere; }

/* ---------- Toast ---------- */
.toast { position: fixed; left: 50%; bottom: 26px; transform: translateX(-50%); background: var(--ink); color: #fff; padding: 10px 16px; border-radius: 8px; box-shadow: var(--shadow); font-size: 14px; z-index: 80; }

/* ---- home dashboard ---- */
.nav-home { margin-right: 4px; }
.dash-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 18px; }
.dash-head h1 { display: flex; align-items: center; gap: 9px; }
.dash-head p { color: var(--muted); margin-top: 3px; }
.dash-hotel { align-self: center; }
.dash-panel { padding: 16px 18px; margin-bottom: 16px; }
.dash-phead { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 12px; }
.dash-phead h2 { display: flex; align-items: center; gap: 8px; font-size: 15px; margin: 0; }
.dash-phead .mono-ico svg { width: 18px; height: 18px; }
.dash-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start; }
.dash-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); gap: 12px; }
.dash-card { border: 1px solid var(--line); border-radius: 12px; padding: 11px 14px; background: var(--soft); display: flex; flex-direction: column; gap: 2px; text-align: left; width: 100%; font: inherit; cursor: pointer; transition: border-color .12s, background .12s; }
button.dash-card:hover { border-color: var(--accent); background: #fff; }
/* overflow-wrap: a title typed without spaces (a pasted link) wraps inside its card. Without it
   the text ran past the card and the panel (overflow: hidden) cut it off, at every width (r19). */
.dash-card .dash-card-title { font-size: 14.5px; font-weight: 600; color: var(--ink); overflow-wrap: anywhere; }
.dash-by { display: block; font-size: 11.5px; color: var(--muted); margin-top: 1px; }
.dash-date { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.dash-rowbtns { display: flex; gap: 2px; }
.dash-ic { border: none; background: transparent; cursor: pointer; color: var(--muted); font-size: 13px; padding: 2px 5px; border-radius: 6px; }
.dash-ic:hover { background: var(--line); color: var(--ink); }
.dash-empty { color: var(--muted); font-size: 13px; padding: 10px 2px; }
/* r29: the Home screens' former inline styles (app.js), as classes. */
.dash-subhead { margin: 14px 0 8px; }
tr.is-voided, [data-retired] { opacity: .6; }
.doc-retired { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
.dash-read-body { overflow-wrap: anywhere; }
.modal-foot .foot-spacer { flex: 1; }
/* A panel heading several screens share (r29: it had no base rule — a tool's own .rg-h2 rule still wins). */
.rg-h2 { font-size: 15px; margin: 0 0 12px; }
.dash-notes-scroll { max-height: 320px; overflow-y: auto; border: 1px solid var(--line); border-radius: 10px; }
.dash-notes { width: 100%; border-collapse: collapse; }
.dash-notes th { position: sticky; top: 0; background: var(--soft); text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .3px; color: var(--muted); padding: 8px 12px; border-bottom: 1px solid var(--line); }
.dash-notes td { padding: 8px 12px; border-bottom: 1px solid var(--line); font-size: 13.5px; }
.dash-notes tbody tr:last-child td { border-bottom: none; }
.dash-notes .dash-ntitle { font-weight: 600; color: var(--ink); }
.dash-notes .dash-nopen { text-align: right; width: 1%; white-space: nowrap; }
/* r38: Home ▸ Historique's two archive tables are .stack (one card per line in a box under 640 px), lined up with
   the table's own 12 px cells. */
.dash-notes.stack { --stack-pad: 12px; --stack-label: 96px; }
/* overflow-wrap: a plain body holding a long link wraps; it widened the reader to the link's
   width (630 px at 390 — the × off-screen, the dialog scrolling sideways, r19). */
.dash-read .dash-read-body { margin: 8px 0 4px; line-height: 1.55; color: var(--ink); white-space: pre-wrap; overflow-wrap: anywhere; }
/* r9 — notes de service: a rich body (the server's sanitised subset), its « Responsable »,
   the plain-text preview in the notes table, and the note form's editor + select. */
.dash-read .dash-read-body.dash-rich { white-space: normal; overflow-wrap: anywhere; }
/* Margins on both sides (they collapse between blocks), so a first line typed before Enter —
   which a browser leaves as bare text in front of the first <p> — is set apart like a paragraph. */
.dash-rich p { margin: 8px 0; }
.dash-rich h3, .dash-rich h4 { margin: 12px 0 4px; font-size: 15px; }
.dash-rich ul, .dash-rich ol { margin: 8px 0; padding-left: 22px; }
.dash-rich li { margin: 2px 0; }
.dash-rich > :first-child { margin-top: 0; }
.dash-rich > :last-child { margin-bottom: 0; }
.dash-resp-read { font-size: 13px; font-weight: 600; color: var(--ink); margin-top: 4px; overflow-wrap: anywhere; }
.dash-notes .dash-resp { font-weight: 500; overflow-wrap: anywhere; }
/* The plain-text start of a body, clamped to TWO lines: in the notes table (r9) and on an
   annonce card (r10). Never HTML — the screen escapes the server's preview. */
.dash-notes .dash-prev, .dash-card .dash-prev { display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2; overflow: hidden;
    font-size: 12.5px; font-weight: 400; color: var(--muted); margin-top: 3px; overflow-wrap: anywhere; }
.dash-card .dash-prev { line-height: 1.4; white-space: normal; }
/* r21 — À surveiller ▸ entries from the guest journal: ONE condensed line each, the type (a small
   badge) and the title, cut with « … » however long; the whole entry opens on a click. A line
   never wraps and never widens the panel (min-width: 0 down the flex chain). Here, not in the
   journal's view.css: a tool's stylesheet is only loaded while that tool is open. */
.dash-jlist { display: flex; flex-direction: column; min-width: 0; border: 1px solid var(--line); border-radius: 10px; overflow: hidden; }
.dash-jrow { display: flex; align-items: center; gap: 8px; width: 100%; min-width: 0; min-height: 38px; margin: 0; padding: 7px 12px;
    border: none; border-bottom: 1px solid var(--line); border-radius: 0; background: var(--card); font: inherit; font-size: 13.5px; color: var(--ink); text-align: left; cursor: pointer; }
.dash-jrow:last-child { border-bottom: none; }
.dash-jrow:hover { background: var(--soft); }
.dash-jrow:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.dash-jtype { flex: none; display: inline-block; max-width: 45%; padding: 1px 8px; border-radius: 999px; font-size: 11.5px; font-weight: 600; line-height: 1.55;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis; background: var(--sel-bg); color: var(--sel-ink); vertical-align: middle; }
.dash-jtype-request { background: #eff6ff; color: #1e40af; }
.dash-jtype-complaint { background: #fef2f2; color: #b91c1c; }
.dash-jtype-compliment { background: #ecfdf5; color: #047857; }
.dash-jtype-info { background: #f5f3ff; color: #6d28d9; }
.dash-jtitle { flex: 1 1 auto; min-width: 0; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The entry opened from that line: every field, then the detail (.dash-read-body, as typed). */
.dash-jtop { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.dash-jgrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 10px 16px; margin: 12px 0 0; }
.dash-jgrid .dash-jf { min-width: 0; }
.dash-jgrid dt { font-size: 11px; font-weight: 600; letter-spacing: .3px; text-transform: uppercase; color: var(--muted); }
.dash-jgrid dd { margin: 2px 0 0; font-size: 14px; color: var(--ink); overflow-wrap: anywhere; }
.dash-jlabel { margin-top: 14px; font-size: 11px; font-weight: 600; letter-spacing: .3px; text-transform: uppercase; color: var(--muted); }
.dash-notes .dash-ntitle { overflow-wrap: anywhere; }
.dp-resp { display: flex; flex-direction: column; gap: 3px; }
.dp-resp input { margin-top: 0; }
.dp-rich { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
.dp-rich-l { font-size: 14px; font-weight: 500; }
.dp-rich .rt-area { min-height: 160px; overflow-wrap: anywhere; }
.dp-rich .rt-bar { flex-wrap: wrap; }

/* home tabs: documentation / procedures / graphics */
.home-head-right { display: flex; align-items: center; gap: 10px; }
.doc-sec { padding: 16px 18px; margin-bottom: 14px; }
.doc-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 10px; }
.doc-head h2 { font-size: 15px; margin: 0; }
.doc-desc { line-height: 1.55; color: var(--ink); white-space: pre-wrap; }
.doc-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; align-items: start; }
.doc-list { display: flex; flex-direction: column; gap: 2px; }
.doc-item { display: flex; align-items: flex-start; justify-content: space-between; gap: 8px; padding: 8px 2px; border-bottom: 1px solid var(--line); }
.doc-item:last-child { border-bottom: none; }
.doc-item-main { min-width: 0; font-size: 13.5px; }
.doc-item-main strong { color: var(--ink); }
.doc-item-main > span { color: var(--muted); margin-left: 8px; }
.doc-role { color: var(--muted); font-weight: 400; font-size: 12px; }
.doc-cmeta { font-size: 12.5px; color: var(--muted); margin-top: 2px; }
.doc-cmeta a { color: var(--accent); }
.doc-item-actions { display: flex; gap: 2px; flex: none; }
.proc-list { padding: 6px 8px; }
.proc-item { display: flex; align-items: center; justify-content: space-between; gap: 8px; border-bottom: 1px solid var(--line); }
.proc-item:last-child { border-bottom: none; }
.proc-open { text-align: left; background: none; border: none; font: inherit; font-weight: 600; color: var(--ink); padding: 0; cursor: pointer; }
.proc-open:hover { color: var(--accent); }
.proc-read { line-height: 1.6; white-space: pre-wrap; color: var(--ink); }
/* r38: several departments' tags wrap under each other (each tag stays whole) instead of widening the list. */
.proc-dept { white-space: normal; }
.proc-emoji { margin-right: 6px; font-size: 15px; }
.proc-by { color: var(--muted); font-size: 13px; }
.proc-meta { color: var(--muted); font-size: 13px; margin: -2px 0 8px; }
.gfx-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 14px; }
.gfx-card { border: 1px solid var(--line); border-radius: 12px; overflow: hidden; background: var(--card); display: flex; flex-direction: column; }
.gfx-img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; background: var(--soft); display: block; }
.gfx-noimg { display: flex; align-items: center; justify-content: center; color: var(--muted); font-size: 13px; text-transform: uppercase; letter-spacing: .4px; }
.gfx-body { padding: 10px 12px; position: relative; }
.gfx-title { font-weight: 600; color: var(--ink); }
.gfx-desc { font-size: 12.5px; color: var(--muted); margin-top: 3px; }
.gfx-link { display: inline-block; margin-top: 6px; font-size: 12.5px; color: var(--accent); }
.gfx-actions { position: absolute; top: 8px; right: 8px; display: flex; gap: 2px; background: rgba(255,255,255,.85); border-radius: 6px; }
.am-photo { display: flex; gap: 14px; align-items: flex-start; }
@media (max-width: 820px) { .dash-cols, .doc-cols { grid-template-columns: 1fr; } }
@media (max-width: 720px) {
    .grid-2, .grid-3 { grid-template-columns: 1fr; }
    .hide-sm { display: none; }
}

/* Admin warning bar: outgoing email is failing */
.mailwarn { position: fixed; left: 16px; right: 16px; bottom: 16px; z-index: 200; display: flex; align-items: center; gap: 12px;
    background: #fff8e6; border: 1px solid #e6c34d; color: #8a6d1a; border-radius: 10px; padding: 12px 14px;
    box-shadow: var(--shadow); font-size: 13.5px; font-weight: 600; }
.mailwarn span { flex: 1; }
.mailwarn-x { background: none; border: none; font-size: 20px; line-height: 1; color: #8a6d1a; cursor: pointer; padding: 0 4px; }
@media (max-width: 640px) { .mailwarn { flex-wrap: wrap; } }

/* Liens utiles: the URL sits under its label like a contact's e-mail, so it
   reuses .doc-cmeta (sage link colour). URLs have no spaces, so allow a break
   anywhere or a long one pushes the panel wide. */
.doc-lurl { overflow-wrap: anywhere; }

/* Transparent artwork (logos) shows the page through it. Without a ground it
   read as solid black once the uploader flattened it to JPEG; the uploader now
   keeps PNG, and this chequerboard makes the transparency obvious instead of
   letting it blend into the card. Opaque images simply cover it. */
.gfx-img, .rc-preview img {
    background-color: #fff;
    background-image:
        linear-gradient(45deg, #e6e9ee 25%, transparent 25%, transparent 75%, #e6e9ee 75%),
        linear-gradient(45deg, #e6e9ee 25%, transparent 25%, transparent 75%, #e6e9ee 75%);
    background-size: 16px 16px;
    background-position: 0 0, 8px 8px;
}
/* Logos are wider than tall; cover crops them badly, so fit the whole mark in. */
.gfx-img { object-fit: contain; }
.gfx-card .gfx-link + .gfx-link { margin-left: 10px; }

/* Graphics file store: non-image files (PDF, AI, ZIP, fonts…) show a type badge
   instead of a broken <img>. The file itself is kept and downloaded verbatim. */
.gfx-noimg { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; }
.gfx-ext { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 15px; font-weight: 700; letter-spacing: .5px; color: var(--muted); }
.gfx-size { font-size: 11px; color: var(--muted); }
.gfx-fname { font-size: 11.5px; color: var(--muted); word-break: break-all; margin-top: 4px; font-family: ui-monospace, Menlo, Consolas, monospace; }

/* A PDF card shows its own first page. The browser's built-in viewer draws it
   in a frame at page size, which is then scaled down to the card — rendering it
   small directly would lay the page out for a tiny width and come out illegible.
   Nothing is stored and nothing is re-encoded, so this works for PDFs uploaded
   long before the preview existed. The type badge sits underneath and shows
   through until the frame reports it has loaded, so a browser with no PDF
   viewer simply keeps the old card instead of an empty box. */
.gfx-pdf { position: relative; display: block; overflow: hidden; background-color: var(--soft); background-image: none; }
.gfx-pdf-fallback { position: absolute; inset: 0; transition: opacity .2s ease; }
.gfx-pdf-frame { position: absolute; top: 0; left: 0; border: 0; transform-origin: top left;
    opacity: 0; transition: opacity .25s ease;
    pointer-events: none; }          /* the card owns the click, not the viewer */
.gfx-pdf.ready .gfx-pdf-frame { opacity: 1; }
.gfx-pdf.ready .gfx-pdf-fallback { opacity: 0; }

/* Contacts can run long. Cap the list at roughly six entries and scroll past
   that, so the panel never pushes the rest of the page down. */
.doc-list-cap { max-height: 320px; overflow-y: auto; }

/* Graphics type filter. Counts come from the whole set, so an empty category is
   still offered — it tells you the category exists and holds nothing. */
.gfx-filter { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 14px; }
.gfx-fbtn { font: inherit; font-size: 12.5px; padding: 5px 11px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); color: var(--muted); cursor: pointer; }
.gfx-fbtn:hover { border-color: var(--accent); color: var(--ink); }
.gfx-fbtn.on { background: var(--sel-bg); border-color: rgba(95,122,88,.35); color: var(--sel-ink); font-weight: 600; }

/* ---- Procedures (hotel SOPs) ---- */
.proc-ref { font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 12.5px; color: var(--muted); white-space: nowrap; }
.proc-ver { font-size: 11px; color: var(--muted); }
.proc-tag { display: inline-block; font-size: 11.5px; background: var(--soft); border: 1px solid var(--line); border-radius: 999px; padding: 1px 8px; margin: 1px 3px 1px 0; white-space: nowrap; }
.proc-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin-bottom: 10px; }
.proc-upd { font-size: 12px; color: var(--muted); }
.proc-facts { display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); gap: 8px 16px; padding: 12px 14px; background: var(--soft); border: 1px solid var(--line); border-radius: 10px; margin-bottom: 14px; }
.proc-f { display: flex; flex-direction: column; gap: 1px; }
.proc-f span { font-size: 10px; text-transform: uppercase; letter-spacing: .4px; color: var(--muted); font-weight: 700; }
.proc-f strong { font-size: 13.5px; }
.proc-read h3, .proc-read h4 { margin: 14px 0 4px; font-size: 14px; }
.proc-read p { margin: 0 0 8px; }
.proc-read ul, .proc-read ol { margin: 0 0 10px; padding-left: 22px; }
.proc-read li { margin: 2px 0; }

/* Rich-text editor for the procedure body. */
.rt-wrap { border: 1px solid var(--line); border-radius: 8px; overflow: hidden; }
.rt-bar { display: flex; gap: 2px; padding: 5px 6px; background: var(--soft); border-bottom: 1px solid var(--line); }
.rt-btn { font: inherit; font-size: 13px; min-width: 28px; height: 26px; border: 1px solid transparent; border-radius: 5px; background: transparent; color: var(--ink); cursor: pointer; }
.rt-btn:hover { background: #fff; border-color: var(--line); }
.rt-b { font-weight: 700; } .rt-i { font-style: italic; } .rt-u { text-decoration: underline; }
.rt-area { min-height: 200px; max-height: 380px; overflow-y: auto; padding: 10px 12px; background: var(--card); font-size: 14.5px; line-height: 1.5; }
.rt-area:focus { outline: none; box-shadow: inset 0 0 0 2px var(--accent-soft); }
.rt-area ul, .rt-area ol { padding-left: 22px; margin: 4px 0; }
.rt-area h3 { font-size: 15px; margin: 10px 0 4px; }
/* r30 — rich text with fields (AdminShell.richField, docs/UI-CONVENTIONS.md §11). .rt-area is a documented scroll box
   (UI-CONVENTIONS §7: text being edited). A chip is one block: never typed into, removed whole, opened by a click. */
.rt-sep { display: inline-block; width: 1px; align-self: stretch; margin: 2px 4px; background: var(--line); }
.rt-btn-text { width: auto; min-width: 0; padding: 0 8px; white-space: nowrap; }
.rt-bar:empty { display: none; }
.rt-wrap-plain .rt-area { min-height: 64px; }
.rt-atom { display: inline-block; max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom;
    padding: 0 6px; border: 1px solid var(--line); border-radius: 999px; background: var(--accent-soft); color: var(--ink);
    font-size: 13px; line-height: 1.6; user-select: all; -webkit-user-select: all; cursor: pointer; }
.rt-atom:hover { border-color: var(--accent); }
.rt-atom:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.rt-atom-s { background: var(--soft); color: var(--muted); }
/* r35: a table placed in the text */
.rt-atom-t { border-style: dashed; border-radius: 6px; background: var(--card); }
.rt-area table { border-collapse: collapse; margin: 6px 0; }
.rt-area td, .rt-area th { border: 1px solid var(--line); padding: 2px 6px; vertical-align: top; }
.rt-atom-missing { background: var(--danger-bg); border-color: var(--danger); color: var(--danger); }
/* an inline-block does not inherit its parent's underline: a chip inside <u> draws its own */
u .rt-atom { text-decoration: underline; }
@media (max-width: 720px) { .rt-bar { flex-wrap: wrap; } }

/* Department checkboxes in the SOP form. */
.pm-deps { border: 1px solid var(--line); border-radius: 8px; padding: 8px 12px 10px; }
.pm-deps legend { font-size: 11px; text-transform: uppercase; letter-spacing: .4px; color: var(--muted); font-weight: 700; padding: 0 4px; }
.pm-chks { display: flex; flex-wrap: wrap; gap: 6px 14px; }
.pm-chk { display: flex; align-items: center; gap: 6px; font-size: 13.5px; font-weight: 400; }
.pm-chk input { width: auto; margin: 0; }

/* Procedure body: rich text or a steps table, switched in the form. */
.pm-mode { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 16px; margin-top: 2px; }
.pm-mode-l { font-size: 11px; text-transform: uppercase; letter-spacing: .4px; color: var(--muted); font-weight: 700; }
.pm-mode label { display: flex; align-items: center; gap: 6px; font-size: 13.5px; font-weight: 400; margin: 0; }
.pm-mode input { width: auto; margin: 0; }
/* The six-column table does not fit the usual 720px form. */
.modal-card.modal-wide { max-width: min(1180px, 96vw); }
.prs-scroll { border: 1px solid var(--line); border-radius: 8px; }
.prs-edit { width: 100%; min-width: 920px; border-collapse: collapse; table-layout: fixed; }
.prs-edit th { font-size: 11px; text-transform: uppercase; letter-spacing: .3px; color: var(--muted); font-weight: 700; text-align: left; padding: 7px 6px; background: var(--soft); border-bottom: 1px solid var(--line); }
.prs-edit td { padding: 4px; border-bottom: 1px solid var(--line); vertical-align: top; }
.prs-edit tbody tr:last-child td { border-bottom: none; }
.prs-edit textarea { width: 100%; min-height: 56px; resize: vertical; font: inherit; font-size: 13px; line-height: 1.4; padding: 5px 6px; border: 1px solid var(--line); border-radius: 5px; }
.prs-n { width: 40px; text-align: center; color: var(--muted); font-weight: 700; }
.prs-act { width: 96px; white-space: nowrap; text-align: right; }
.prs-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 12px; margin-top: 8px; }
.prs-read th { white-space: normal; }
.prs-read td { white-space: pre-wrap; vertical-align: top; font-size: 13.5px; line-height: 1.45; }
/* The optional picture at the end of each step. */
/* r38: 1000 px (was 1080): from a 1024 px box the grid is a table (five 145 px text columns), under it one card per
   line (.stack-lg) — under a 1080 px box it used to scroll sideways (a 1024 px laptop's dialog: 943 px). */
.prs-edit { min-width: 1000px; }
.prs-imgcell { width: 140px; }
.prs-thumb img { display: block; max-width: 120px; max-height: 84px; border: 1px solid var(--line); border-radius: 4px; margin-bottom: 4px; }
.prs-imgbtn { cursor: pointer; }
.prs-read .prs-img { width: 196px; }
.prs-read .prs-img img { display: block; max-width: 180px; max-height: 140px; border: 1px solid var(--line); border-radius: 4px; }
/* Checklist: the tick box and the initials are blank on purpose — filled in by hand on paper. */
.prs-edit.prs-checklist { min-width: 760px; }
.prs-chk { width: 56px; text-align: center; }
.prs-box { display: inline-block; width: 14px; height: 14px; border: 1.5px solid var(--ink); vertical-align: middle; margin-top: 4px; }
.prs-ini { width: 96px; }
/* r38: in the checklist editor's cards the paper's tick box and initials (blank on purpose) are left out. */
@container dt (max-width: 799.98px) { .prs-edit.stack-md td.prs-chk, .prs-edit.stack-md td.prs-ini { display: none; } }
.prs-read .prs-ini { border-left: 1px solid var(--line); }
/* Job descriptions list: the covered titles under the sheet's title, and the head-count. */
.jd-titles { display: flex; flex-wrap: wrap; gap: 2px; margin-top: 4px; }
.jd-count { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
/* Inside a .data-table (whose th/td rule is more specific) the column alignments need the table in the selector. */
.data-table th.jd-count, .data-table td.jd-count { text-align: right; }
.data-table .prs-n, .data-table .prs-chk, .prs-edit th.prs-n, .prs-edit th.prs-chk { text-align: center; }
/* The code box sits in a row of inputs and selects: same top margin and height. */
.pm-code { margin-top: 5px; min-height: 40px; padding: 9px 10px; box-sizing: border-box; }
/* A department this account may not write for: kept on a document that has it, never added. */
.pm-chk-off { opacity: .55; }
/* Job description sections: paragraphs keep their line breaks; lists are bulleted. */
.jd-para { margin: 0 0 8px; line-height: 1.55; white-space: pre-wrap; color: var(--ink); }
.jd-list { margin: 0 0 10px; padding-left: 22px; line-height: 1.5; color: var(--ink); }
.jd-list li { margin: 2px 0; }
.jd-legacy { background: var(--soft); border: 1px solid var(--line); border-radius: 8px; padding: 8px 10px; }
/* A kept free-text sheet (r3 H2): its text, read-only, scrolls inside the form. */
.jd-legacy-text { max-height: 18rem; overflow: auto; border: 1px solid var(--line, #ddd); border-radius: 6px; padding: 8px 10px; }
/* The job-description form (jd-limits): a wider card for long texts, boxes that grow with their
   text up to a cap and then scroll, and a counter under each box. */
.modal-card.jd-modal { max-width: min(980px, 96vw); }
.jd-area { min-height: 7.5em; max-height: 60vh; overflow-y: auto; line-height: 1.45; }
.jd-meter { display: block; margin-top: 3px; font-size: 12px; color: var(--muted); text-align: right; font-variant-numeric: tabular-nums; }
.jd-meter.over { color: var(--danger); font-weight: 600; }
.jd-total { margin-top: -6px; font-size: 13px; }
.jd-line-err { display: block; margin-top: 4px; padding: 4px 8px; font-size: 13px; }
/* Reference documents in the SOP form and the reader. */
.pm-files { display: flex; flex-direction: column; gap: 6px; }
.pm-file { display: flex; align-items: center; gap: 8px; font-size: 13.5px; }
.pm-file .pm-file-new { font-size: 11px; color: var(--muted); }
.pm-file-add { cursor: pointer; align-self: flex-start; }
.proc-docs { display: flex; flex-wrap: wrap; gap: 6px 14px; margin: 4px 0 10px; }
.proc-docs a { font-size: 13.5px; }
.pm-grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 640px) { .pm-grid2 { grid-template-columns: 1fr; } }
/* The code is assigned by the server: shown, never typed. */
.pm-code { display: block; min-height: 36px; padding: 8px 10px; border: 1px dashed var(--line); border-radius: 8px; background: var(--soft); color: var(--ink); font-family: ui-monospace, Menlo, Consolas, monospace; font-size: 13px; }
.pm-refs { display: flex; flex-direction: column; gap: 6px; }

/* ---- Shift coverage Gantt ---- */
.gt { margin-top: 6px; }
/* The head row: « Jour / Semaine » and the days are two shared .tabs.tabs-seg bars (r29b); it wraps on a phone. */
.gt-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 10px; min-width: 0; }
/* On a phone the seven days share the width on ONE line (measured at 390 px: « Dim » wrapped alone onto a second row
   of the switch); 46 px each is room for « Lun » with 4 px either side. */
@media (max-width: 720px) {
    .gt-head .gt-days { display: flex; flex-wrap: nowrap; width: 100%; }
    .gt-head .gt-days .tab { flex: 1 1 0; padding-left: 4px; padding-right: 4px; }
}
.gt-legend { margin-left: auto; display: flex; align-items: center; gap: 4px; font-size: 11.5px; color: var(--muted); }
.gt-key { display: inline-block; width: 12px; height: 12px; border-radius: 3px; margin-left: 8px; }
/* Name gutter is fixed, the track takes the rest — so 24h always fits the width
   and the day view never needs horizontal scrolling. */
.gt-axis { position: relative; height: 14px; margin-left: 150px; border-bottom: 1px solid var(--line); }
.gt-h { position: absolute; top: 0; font-size: 10px; color: var(--muted); transform: translateX(-50%); }
.gt-rows { display: flex; flex-direction: column; gap: 3px; margin-top: 4px; }
.gt-row { display: flex; align-items: center; gap: 0; }
.gt-name { flex: 0 0 150px; font-size: 12.5px; padding-right: 10px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.gt-name small { color: var(--muted); font-size: 10.5px; margin-left: 5px; }
.gt-track { position: relative; flex: 1 1 auto; height: 20px; background: var(--soft); border-radius: 4px; overflow: hidden; }
.gt-bar { position: absolute; top: 2px; bottom: 2px; background: var(--sel-bg); border: 1px solid rgba(95,122,88,.45); border-radius: 3px; display: flex; align-items: center; overflow: hidden; }
.gt-bar-t { font-size: 10px; color: var(--sel-ink); padding: 0 4px; white-space: nowrap; }
.gt-next { position: absolute; right: 3px; top: 3px; font-size: 9.5px; color: var(--muted); }
.gt-off { position: absolute; left: 6px; top: 3px; font-size: 10.5px; color: var(--muted); }
.gt-cv { position: absolute; top: 0; bottom: 0; }
.gt-c0 { background: #f6c9c4; }
.gt-c1 { background: #fde68a; }
.gt-c2 { background: #cfe3c6; }
.gt-c3 { background: #9fc292; }
.gt-covrow { margin-top: 8px; }
.gt-covrow .gt-name { font-weight: 700; font-size: 12px; }
.gt-covrow .gt-track { height: 16px; }
.gt-warn { margin-top: 8px; font-size: 12.5px; color: #9a3412; background: #fefce8; border: 1px solid #fde68a; border-radius: 8px; padding: 7px 11px; }
.gt-ok { margin-top: 8px; font-size: 12.5px; color: #3f7d4e; }

/* ---- Planning view (shared by Chef de service and HR) ----
   These were living in chef-planning's own stylesheet, which only loads while
   that tool is open — so the HR planning page rendered them unstyled, with
   content flush against the panel edges. Shared markup belongs here. */
.cp-editor-top { margin-bottom: 10px; }
.cp-editor { padding: 14px 16px; width: 100%; max-width: 100%; box-sizing: border-box; }
/* Stacked panels on the planning page (grid, then coverage chart). */
.cp-editor + .cp-editor { margin-top: 16px; }
.cp-editor .rg-h2 { margin: 0 0 12px; font-size: 15px; }
/* The seven day columns need a usable width before the wrapper scrolls.
   min-width:0 let the table shrink to the viewport instead, so on a phone the
   columns collapsed to about 20px, the weekday headings ran together as
   "LUNMARMER…" and the dropdowns were too narrow to tap. A floor of 720px
   (168 for the name + seven columns of ~79) keeps it usable and lets
   .sc-grid-wrap do the scrolling it was always meant to do.
   r38 (census, 18 Oct 2026): the week no longer scrolls — its wrapper is also the shell's .table-scroll and the grid a
   .stack card table (a card per person under 640 px of box); both planning tools set the floor back to 0
   (`.cp-editor table.sc-grid { min-width: 0 }`), and .sc-grid-wrap lost its overflow-x: auto. */
.cp-editor .sc-grid { min-width: 720px; width: 100%; table-layout: fixed; }
.cp-editor .sc-grid td.sc-emp, .cp-editor .sc-grid th:first-child { width: 168px; }
.cp-bar { display: flex; justify-content: flex-end; gap: 10px; margin-top: 16px; padding: 4px 0 2px; }
/* A denial note sits between the header and the grid. */
.ce-denybox { margin-bottom: 14px; }

/* Toolbar filters share one line. Form controls are width:100% globally (right
   for stacked forms, wrong in a flex toolbar), so pull them back to auto. */
.toolbar-left > select, .toolbar-left > input[type=search], .toolbar-left > input[type=text] {
    width: auto; min-width: 160px; margin-top: 0;
}
.toolbar-left > input[type=search] { min-width: 220px; }
/* r29: the other controls a toolbar holds directly — a date, a month, a number — keep their own width too. */
.toolbar-left > input[type=date], .toolbar-left > input[type=month], .toolbar-left > input[type=week],
.toolbar-left > input[type=number], .toolbar-left > input[type=datetime-local], .toolbar-actions > select,
.toolbar-actions > input:not([type=checkbox]):not([type=radio]) { width: auto; margin-top: 0; }

/* ---- Gantt job filter ---------------------------------------------------
   The chips are built from the job titles present in the planning on screen,
   so the row is absent whenever a roster carries fewer than two of them. */
.gt-jobs { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 8px 0 10px; }
.gt-jobs-l { font-size: 11.5px; color: var(--muted); margin-right: 2px; }
.gt-job {
    font: inherit; font-size: 11.5px; line-height: 1;
    padding: 5px 10px; border: 1px solid var(--line); border-radius: 999px;
    background: var(--card); color: var(--ink); cursor: pointer;   /* r29: was var(--panel), a token the shell never defined */
}
.gt-job:hover { border-color: var(--muted); }
/* r29b (SW1-06): the selection tokens, as every other filter chip (Objets perdus) — --head is not a token (§6). */
.gt-job.on { background: var(--sel-bg); border-color: var(--accent); color: var(--sel-ink); font-weight: 600; }
.gt-job-clear { border-style: dashed; color: var(--muted); }
.gt-jobs-n { font-size: 11px; color: var(--muted); margin-left: auto; font-variant-numeric: tabular-nums; }
.gt-empty { padding: 14px 6px; font-size: 12.5px; color: var(--muted); }
/* The job sits under the name. Scoped to .gt-name-2l so the WEEK view, which
   uses an inline <small> for the date, is left exactly as it was. */
.gt-name-2l { white-space: normal; line-height: 1.2; }
.gt-name-2l small { display: block; margin-left: 0; font-size: 10px; color: var(--muted); font-weight: 400; line-height: 1.25; }

/* ---- the 6-character per-day note --------------------------------------
   Sits under the shift dropdown in the editor, and under the hours in the
   read-only grid. Hidden until the cell actually has a shift to qualify. */
.ce-note {
    display: block; width: 100%; margin-top: 3px;
    font-size: 10.5px; line-height: 1.2; text-align: center; text-transform: uppercase;
    letter-spacing: 0.4px; padding: 1px 2px; min-height: 0; height: auto;
}
.ce-note[hidden] { display: none; }
.sc-note {
    display: block; margin-top: 2px; font-size: 9.5px; font-weight: 700;
    letter-spacing: 0.4px; color: var(--ink);   /* r29b (SW1-06): was var(--head, …), an undefined token */
}
/* Job title under the employee name in the read-only planning grid. */
.sc-job { font-size: 10px; color: var(--muted); font-weight: 400; line-height: 1.3; margin-top: 1px; }

/* The week a date actually resolves to. Any day picked snaps to its Monday, so
   this line is the difference between choosing a week and guessing at one. */
.cp-weekline { margin-top: 6px; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.cp-weekline-warn { color: #b45309; font-weight: 600; }
/* ISO week number beside a week's start date, in lists and headings. */
.sc-wk {
    display: inline-block; margin-left: 6px; padding: 1px 6px;
    font-size: 10.5px; font-weight: 700; line-height: 1.5;
    border-radius: 999px; background: var(--soft, #eef1f5); color: var(--muted);
}

/* The per-day note is opt-in per employee: most people never need one, and a
   box under every filled cell was 42 inputs on a six-person week. */
.ce-notetog {
    display: flex; align-items: center; gap: 5px; margin-top: 4px;
    font-size: 10.5px; color: var(--muted); cursor: pointer; user-select: none;
}
.ce-notetog input { width: auto; margin: 0; flex: 0 0 auto; }
.ce-notetog span { line-height: 1.2; }

/* Roster-refresh dialog: what changed in the department while a draft was open. */
.ce-rd-h { font-size: 12.5px; font-weight: 700; margin: 12px 0 4px; }
.ce-rd-h:first-child { margin-top: 0; }
.ce-rd-list { margin: 0 0 4px; padding-left: 20px; font-size: 13px; }
.ce-rd-list li { margin: 2px 0; }
.ce-rd-job { color: var(--muted); font-size: 11.5px; }
.ce-rd-days { color: #b45309; font-size: 11.5px; font-weight: 600; }
.ce-rd-drop { margin-top: 10px; }

/* ---------------------------------------------------------------------------
   Leave requests (HR ▸ Congés and Chef ▸ Congés).
   Here rather than in either tool's view.css: tool CSS is loaded per tool and
   never unloaded, so a chef who had not opened HR ▸ Days off in the same session
   was getting this markup with none of its styling.
   --------------------------------------------------------------------------- */
/* Request list: a dot instead of a spelled-out pill, and a row you can open. */
.do-table th.do-st-col, .do-table td.do-st-col { width: 34px; text-align: center; }
.do-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; vertical-align: middle;
    box-shadow: 0 0 0 3px rgba(0,0,0,.05); }
.do-dot-pending { background: #f97316; }
.do-dot-approved { background: #16a34a; }
.do-dot-denied { background: #ef4444; }
.do-row { cursor: pointer; }
.do-row:hover { background: #f4f6ff; }

/* Detail modal. */
.do-dhead { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin-bottom: 12px; }
.do-dgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 16px; margin-bottom: 14px; }
.do-dcell { min-width: 0; }
.do-dval { font-size: 14.5px; color: var(--ink); overflow-wrap: anywhere; }
.do-denybox { background: #fef2f2; border: 1px solid #fecaca; border-radius: 8px; padding: 8px 10px;
    margin-bottom: 12px; font-size: 13.5px; color: #b91c1c; }
@media (max-width: 720px) { .do-dgrid { grid-template-columns: 1fr; } }

/* Dates set the height of every row when they wrap; they never need to. */
.do-table td.do-dates { white-space: nowrap; }
.do-table td, .do-table th { vertical-align: middle; }
/* ---------------------------------------------------------------------------
   Tabs — the ONE tab bar of the console (r29b, rule T1; docs/UI-CONVENTIONS.md « Tabs »).
     <div class="tabs" role="tablist" aria-label="…">
       <button type="button" class="tab" role="tab" aria-selected="true" data-tab="a">A <span class="tab-n">3</span></button>
       <button type="button" class="tab" role="tab" aria-selected="false" data-tab="b">B</button>
     </div>
   The variant .tabs.tabs-seg is the segmented switch (two or three views: Liste / Calendrier, Jour / Semaine).
   The selected tab is the one with aria-selected="true" (a bar of LINKS — <nav class="tabs"> > a.tab — marks the
   current page with aria-current="page" instead) — the look follows the ARIA state, so a tab that looks selected is
   announced selected. Arrow keys / Home / End move between the tabs (public/core/app.js).
   Why it can never scroll vertically (the owner, 10 Oct: « the menu bar has a vertical scroll wtf »): nothing pokes
   out of the box — no negative margin; the bar's line is an INSET shadow (inside the box) and the selected tab's
   underline is an inset shadow on the tab, painted over that line; the bar wraps instead of overflowing; and
   overflow-y: hidden as a belt (overflow-x then computes to auto: a single tab wider than a phone scrolls sideways).
   Tools never style .tabs / .tab (test/r29b-tabs-static.test.js); a tool's class on the bar is for layout only.
--------------------------------------------------------------------------- */
.tabs {
    display: flex; flex-wrap: wrap; align-items: stretch; gap: 2px; min-width: 0; max-width: 100%;
    margin: 0 0 14px; padding: 0; overflow-x: auto; overflow-y: hidden;
    box-shadow: inset 0 -1px 0 var(--line);
}
.tab {
    appearance: none; -webkit-appearance: none; flex: none; min-width: 0; max-width: 100%; margin: 0;
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    padding: 9px 14px; border: none; border-radius: 8px 8px 0 0; background: none; box-shadow: none;
    font: inherit; font-size: 14px; font-weight: 600; line-height: 1.3; color: var(--muted);
    cursor: pointer; white-space: nowrap; overflow-wrap: anywhere; text-align: center; text-decoration: none;
}
.tab:hover { color: var(--ink); background: var(--soft); box-shadow: inset 0 -1px 0 var(--line); }
.tab[aria-selected="true"], .tab[aria-current="page"] { color: var(--ink); background: none; box-shadow: inset 0 -2px 0 var(--accent); }
.tab:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.tab:disabled { color: var(--off); cursor: not-allowed; background: none; }
/* A count on a tab (« Réponses 12 »). */
.tab-n {
    display: inline-block; min-width: 20px; padding: 1px 6px; border-radius: 999px; background: var(--soft);
    color: var(--muted); font-size: 11.5px; font-weight: 600; line-height: 1.5; text-align: center;
}
.tab[aria-selected="true"] .tab-n, .tab[aria-current="page"] .tab-n { background: var(--sel-bg); color: var(--sel-ink); }
/* In a toolbar the bar is one of the filters: no rule under it, no margin. */
.toolbar .tabs { margin: 0; }
.toolbar .tabs:not(.tabs-seg) { box-shadow: none; }

/* The segmented switch: a framed row of two or three choices, the chosen one filled in sage. */
.tabs.tabs-seg {
    display: inline-flex; gap: 0; vertical-align: middle; margin: 0; box-shadow: none;
    border: 1px solid var(--line); border-radius: 8px; background: var(--surface);
}
.tabs-seg .tab { border-radius: 0; padding: 6px 12px; font-size: 13px; }
.tabs-seg .tab:hover { box-shadow: none; }
.tabs-seg .tab + .tab { border-left: 1px solid var(--line); }
.tabs-seg .tab[aria-selected="true"], .tabs-seg .tab[aria-current="page"] { background: var(--accent); color: #fff; box-shadow: none; }
.tabs-seg .tab[aria-selected="true"]:focus-visible, .tabs-seg .tab[aria-current="page"]:focus-visible { outline-color: #fff; outline-offset: -4px; }
.tabs-seg .tab[aria-selected="true"] .tab-n, .tabs-seg .tab[aria-current="page"] .tab-n { background: rgba(255,255,255,.22); color: #fff; }

/* The dates are the widest column and the one nobody reads character by character;
   a step down in size buys back more room than anything else in the row. */
.do-table td.do-dates { font-size: 12px; }
.do-table td.do-dates .do-sub { font-size: 10.5px; }
.do-table th.do-st-col, .do-table td.do-st-col { width: 24px; padding-left: 4px; padding-right: 4px; }
.do-table .do-by { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.do-ef-total { margin: 4px 0 2px; font-size: 13.5px; color: var(--muted); }
/* Used by the detail grid's field labels. It lived in the ACCOUNTS tool's
   stylesheet, one tool further away again. */
.ac-label { font-size: 14px; font-weight: 600; margin-bottom: 8px; }

/* The leave-request type pill and its sub-lines, shared by both congés screens.
   Duplicated in the two tools' stylesheets until the nowrap was added to one of
   them and not the other. */
.co-t { display: inline-block; padding: 2px 9px; border-radius: 999px; font-size: 12px; font-weight: 600; white-space: nowrap; }
.co-t-conge { background: #fef3c7; color: #92400e; }
.co-t-recup { background: #e0f2fe; color: #075985; }
.co-mat { color: var(--muted); font-size: 12px; }
.co-sub { font-size: 11px; color: var(--muted); }

/* ---------------------------------------------------------------------------
   Planning version history (HR ▸ Schedule). Two panes: the versions on the left,
   the chosen one's grid on the right with changed cells marked.
   --------------------------------------------------------------------------- */
.sc-hist { display: grid; grid-template-columns: 220px minmax(0, 1fr); gap: 16px; }
.sc-hist-list { display: flex; flex-direction: column; gap: 6px; max-height: 60vh; overflow: auto; }
.sc-hv-item { text-align: left; border: 1px solid var(--line); background: var(--card, #fff); border-radius: 8px;
    padding: 8px 10px; cursor: pointer; font: inherit; display: grid; gap: 2px; }
.sc-hv-item:hover { border-color: #cfd4dc; }
.sc-hv-item.on { border-color: var(--primary, #2f5d8a); background: var(--sel-bg, #eef4fb); }
.sc-hv-n { font-weight: 600; font-size: 13.5px; }
.sc-hv-meta { font-size: 11.5px; color: var(--muted); }
.sc-hv-why { font-size: 12px; color: var(--ink); font-style: italic; margin-top: 2px;
    overflow-wrap: anywhere; }
.sc-hist-body { min-width: 0; }
.sc-hb-load { color: var(--muted); padding: 12px; }
.sc-hb-head { margin-bottom: 10px; }
.sc-hb-meta { font-size: 12px; color: var(--muted); margin-top: 2px; }
.sc-hb-why { font-size: 13px; font-style: italic; margin-top: 4px; }
.sc-hb-sum { font-size: 12.5px; color: var(--muted); margin-top: 6px; font-weight: 600; }
/* A changed cell is outlined rather than filled, so the code stays the thing you
   read; the previous value is in the title. */
.sc-cell-diff { outline: 2px solid #c4661b; outline-offset: -2px; background: #fdf3e7; }
.sc-row-gone td { opacity: .55; text-decoration: line-through; text-decoration-color: var(--line); }
@media (max-width: 860px) { .sc-hist { grid-template-columns: 1fr; } }

/* ---------- r39: several VAT rates on one payment — AdminShell.vatLines (docs/UI-CONVENTIONS.md §8) ----------
   The « Taux TVA » select with a small « Plusieurs taux » under it; in its place, a group of 2 to 5 lines (rate,
   « Montant TTC à ce taux », ×) with « + Ligne » and « Un seul taux ». Each line's refusal sits under it. Fits a 390 px
   dialog: the rate column is 7 rem, the amount takes the rest, the × is a finger's 40 px. */
.vatl { position: relative; min-width: 0; }
.vatl-link {
    display: inline-flex; align-items: center; margin-top: 6px; padding: 0; border: 0; background: none;
    font: inherit; font-size: 13px; color: var(--sel-ink); text-decoration: underline; text-underline-offset: 2px; cursor: pointer;
}
.vatl-link:hover { color: var(--ink); }
.vatl-link:focus-visible, .vatl-x:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 6px; }
.vatl-mixed { min-width: 0; padding: 10px 12px 12px; }
.vatl-mixed > .vatl-legend { font-size: 14px; font-weight: 500; color: var(--ink); }
.vatl-legend small { font-weight: 400; color: var(--muted); margin-left: 4px; }
.vatl-head, .vatl-cells { display: grid; grid-template-columns: minmax(0, min(7rem, 36%)) minmax(0, 1fr) 40px; gap: 8px; align-items: center; }
.vatl-head { margin-bottom: 4px; font-size: 12px; font-weight: 600; color: var(--muted); }
.vatl-lines { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 8px; }
.vatl-line { min-width: 0; }
.vatl-cells > select, .vatl-cells > input { margin-top: 0; min-width: 0; }
.vatl-x {
    display: inline-flex; align-items: center; justify-content: center; width: 40px; height: 40px; padding: 0;
    border: 1px solid var(--line); border-radius: 8px; background: transparent; color: var(--muted); font: inherit; font-size: 18px; cursor: pointer;
}
.vatl-x:hover { color: var(--danger); border-color: var(--danger); background: var(--danger-bg); }
.vatl-x:disabled { opacity: .4; cursor: not-allowed; color: var(--muted); border-color: var(--line); background: transparent; }
.vatl-err { margin: 5px 0 0; font-size: 13px; color: var(--danger); overflow-wrap: anywhere; }
.vatl-foot { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; margin-top: 12px; }
.vatl-foot .vatl-link { margin-top: 0; }
/* a finger's 40 px on any touch screen (a query of its own: the shell's phone blocks above are read by their tests) */
@media (pointer: coarse) { .vatl-link { min-height: 40px; } }

/* ===========================================================================
   Phones (r19, 29 Sep 2026). The owner: « a lot of signalements are going to be
   made from a phone » — no sideways scrolling, easy to tap, everything visible.
   Every rule below sits in a phone-sized query (max-width: 720px) and, where it is
   about fingers, also a touch screen (pointer: coarse): a desktop or a laptop never
   matches either, so their layout is exactly what it was.
   =========================================================================== */
@media (max-width: 720px) {
    /* iOS Safari zooms the page in on a field whose text is under 16 px as soon as it
       gets the focus, and leaves it zoomed: the form then runs off the screen. The
       shell's fields inherited 14 px from their label. (input[type=text] first: the
       desktop form-control rule's line keeps its own, unique wording — a revert check
       picks it.) */
    input[type=text], input:not([type]), input[type=password], input[type=date], input[type=number], input[type=search],
    input[type=time], input[type=email], input[type=url], input[type=tel], input[type=month], input[type=datetime-local], input[type=week],
    select, textarea, .rt-area, .prs-edit textarea { font-size: 16px; }

    /* A header whose buttons sit beside a title or a filter wraps them to a second line
       instead of pushing the page wider than the screen. */
    .dash-phead, .doc-head, .home-head-right, .api-doc-head, .toolbar-actions, .row-actions,
    .modal-foot, .settings-actions, .line-foot, .cp-bar, .prs-foot, .rt-bar { flex-wrap: wrap; }
    .home-head-right { max-width: 100%; }
    /* …except a table row's buttons: wrapping let the actions column shrink to its widest
       button and stacked them — rows 2 to 3 times taller (HR ▸ Planning 90 → 295 px) with
       the buttons still past the right edge (PHONE-03). They stay on one line and the table
       scrolls sideways inside its panel. A tool that turns its rows into cards on a phone
       says so with its own, more specific rule (Tickets, Journal client, Caisse).
       r38: a table no longer scrolls (§1 of docs/UI-CONVENTIONS.md) — a .stack table's card puts
       the buttons on their own line and wraps them (« Tables that fit their box », above). */
    td .row-actions { flex-wrap: nowrap; }
    /* A toolbar filter is as wide as its longest option: one long building name (49
       characters) made Emplacements' « Bâtiment » 478 px and the page 500 px wide on a
       390 px phone (RV-FS-05). The filter row and its fields stop at the screen's width;
       a select shows the chosen option cut, its picker lists every option whole. */
    .toolbar-left, .toolbar-left > select, .toolbar-left > input[type=search], .toolbar-left > input[type=text] { max-width: 100%; }
    /* r29 fix (DS-4): a filter keeps one line while it fits; one longer than the screen wraps its label rather than
       widening the page. */
    .inline { white-space: normal; max-width: 100%; }
    /* (The shift Gantt's day buttons — « Dim » past the panel's edge at 360 px, RV-FS-04 — are a shared .tabs bar
       since r29b, and every .tabs wraps.) */

    /* Text typed without spaces (a pasted link, a booking reference, a serial number)
       breaks inside a panel or a dialog instead of widening it past the screen.
       `break-word`, not `anywhere`: both break a word that cannot fit on its line, but
       `anywhere` also lets the browser squeeze a box below its longest word — a filter's
       label, a heading beside a button, a view toggle then broke mid-word (« Utilisat/eur »,
       « Historiq/ue », « Calen/drier », « D/u », PHONE-02). */
    .panel, .home-card, .modal-body, .toast, .form-error, .form-ok, .hint { overflow-wrap: break-word; }
    /* `anywhere` only where the text is the user's own and must shrink with its box: Home's
       annonce / note / watch cards, a document's line, a visual's title. (Titles and cells
       holding user text already say so at every width: .tkv-title, .modal-head h2,
       .dash-card-title, .dash-read-body, .gl-detail…) */
    .dash-card, .doc-item-main, .gfx-body { overflow-wrap: anywhere; }
    /* A table keeps its words: `anywhere` there lets a column shrink to one letter, cutting
       dates and names mid-word (the 29 Sep R18GR-1 lesson). It scrolls inside its own box —
       the panel — instead. A header of a fixed-layout table (the API panels' 103 px columns)
       keeps its word whole and runs a few px into its padding, as before r19, instead of
       « OBLIGATOIR/E » (PHONE-02). A cell that wants to wrap anywhere says so itself.
       r38 (census, 18 Oct 2026): the panel no longer scrolls sideways on a phone either — it was there
       for the tables, and every table fits its box now (cards, hidden columns, cut cells: « Tables that
       fit their box »); a panel keeps its overflow: hidden at every width. */
    .panel table, .modal-body table { overflow-wrap: break-word; }
    .panel th, .modal-body th { overflow-wrap: normal; }

    /* Chef de service ▸ Planning: the per-day note box is 64 px wide (a 720 px grid that
       scrolls sideways on a phone). At 16 px with the generic 10 px side padding only 44 px
       were left and RESTO1 and RESTO2 both read « RESTC » (PHONE-01). It keeps 16 px (no
       iOS zoom while typing a note), thin padding and a fixed-width face: any 6-character
       code is 6 × 0.6 em = 58 px at 16 px, inside the box's 60 px, whatever the letters. */
    .cp-editor input.ce-note { padding: 1px 2px; letter-spacing: 0; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; }

    /* A dialog: the whole width of the screen, its title bar — and its × — pinned at the
       top while the rest scrolls under it. The backdrop keeps its 40 px at the bottom:
       a form's sticky Save bar reaches the screen's edge by exactly that much
       (gr-log view.css, bottom: -40px). The column is the screen's width, so nothing
       inside can make the card wider than the phone. */
    .modal { padding: 0 0 40px; grid-template-columns: minmax(0, 1fr); place-items: start stretch; }
    body .modal > .modal-card { width: 100%; max-width: none; min-width: 0; border-radius: 0 0 14px 14px; }
    .modal-head { position: sticky; top: 0; z-index: 3; background: var(--card); align-items: flex-start; gap: 8px; padding: 8px 8px 8px 20px; }
    /* A title of a paragraph (a pasted link) would make that pinned bar a third of the
       screen: past three lines the title scrolls inside the bar, whole but out of the way. */
    .modal-head h2 { padding-top: 9px; max-height: calc(3 * 1.45em + 9px); overflow-y: auto; }
    .modal-close { flex: none; min-width: 44px; min-height: 44px; }
    /* What a tool scrolls into view inside a dialog (scrollIntoView, block 'start' or
       'nearest') stops below that pinned bar (61 px with its ×) instead of under it. */
    .modal { scroll-padding-top: 64px; }
    /* …but the pinned bar's own controls are always in view: without this, giving the ×
       the focus (Tab, a switch or a screen reader) counted it as hidden under that
       padding and scrolled a dialog read down to 209 px back to 0 (RV-FS-02). A negative
       margin as tall as the padding puts them back inside the area in view. */
    .modal-head, .modal-head * { scroll-margin-top: -64px; }

    /* A message is as wide as the screen allows (it was held to half of it), above the
       iPhone's home bar. */
    .toast { left: 16px; right: 16px; transform: none; text-align: center; bottom: calc(16px + env(safe-area-inset-bottom, 0px)); }

    /* The bar with the menu button stays at the top, so the menu is one tap away however
       far down a list has been read. Under a dialog (50) and the drawer (60/70). */
    .mobile-bar { position: sticky; top: 0; z-index: 45; padding: 6px 12px; }
    /* …so what a tool scrolls to the top of the page stops below the bar (53 px on a touch
       phone) instead of under it: Fiche de fonction « Charger » hid « Nom du client » and
       the top of its field; Déplacements, Congés, Visuels and Devis scroll the same way
       (scrollIntoView, block 'start', PHONE-04). Anchors and focus scrolling honour it too. */
    html { scroll-padding-top: 60px; }
    /* …and the bar's own ☰ is not « under the bar »: focusing it (Tab, a switch, a screen
       reader) scrolled a list read down to 800 px back up to 394 (RV-FS-02). */
    .mobile-bar, .mobile-bar * { scroll-margin-top: -60px; }
    /* The drawer is wider, so the tools' names are read whole, and still leaves a strip
       of the page to tap to close it. */
    .sidebar { width: min(300px, calc(100vw - 56px)); }

    .login-wrap { min-height: 100vh; min-height: 100dvh; padding: 16px; }
}

/* Fingers: every control at least 40 px high (Apple asks 44, Google 48; 40 keeps the
   rows of a table compact). Icon buttons are 40 px squares. */
@media (max-width: 720px) and (pointer: coarse) {
    .btn, .btn-sm, .btn-xs { min-height: 40px; }
    /* A link or a label dressed as a button: min-height only applies to a box — since r29b the box is drawn at every
       width (`a.btn, label.btn, span.btn` near the top of the file). */
    /* (The dialog's × is 44 px on every phone, above.) */
    .sidebar-x, .hamburger, .mailwarn-x, .msel-chip-x, .dash-ic, .rg-x, .rt-btn { min-width: 40px; min-height: 40px; }
    .ck-chip, .st-pill { min-height: 40px; }   /* r29 (r29-hrplan): Contrôle pointage chips, planning-status pills */
    .nav-link, .sidebar-hotel .sh-btn, .su-btn, .msel-btn, .msel-opt, .login-link, .tab, .gfx-fbtn,
    .proc-open, .checkbox, .login-check, .pm-chk, .pm-mode label { min-height: 40px; }   /* r29: + the procedure's Texte / Tableau / Checklist radios (were 20 px) */
    /* « Effacer » at the top of every multi-select pop-up (Tickets ▸ Filtres ▸ Assigné à,
       Statut…) was 22 px high beside 40 px options (R19B-06). */
    .msel-clear { min-height: 40px; padding: 0 10px; }
    /* Hébergement ▸ Objets perdus: the status chips (Tous, En cours, Perdu… + 1 an) are
       plain buttons styled by the tool, not .btn, so the rule above missed them: 31 px
       high on the housekeeping account's phone (R19B-08). The tool sets no height. */
    .lf-chip { min-height: 40px; }
    /* A hotel in the picker's list: padding rather than min-height, so a long name keeps
       its ellipsis (a block's text-overflow). 11 + 20 + 11 = 42 px. */
    .sidebar-hotel .sh-opt { padding-top: 11px; padding-bottom: 11px; }
    /* Tools' own controls that are not .btn and were under a finger's size (RV-FS-04,
       measured at 360–390 px): tabs — Tarifs « Prix / Évolutions » 21 px high, Capex 34,
       WhatsApp 36, Objets perdus 39, Déplacements' « Liste / Calendrier » 38, the shift
       Gantt's « Jour / Semaine » 28 and its days 25; Comptes' ▸ 22 × 22 and 🗑 21 px wide;
       the ▲ / ▼ of Chef ▸ Planning and of the shift checklists 22 × 16 (their rows 78 → 93
       and 63 → 97 px: a finger can now move a line without hitting its neighbour).
       r29b: every one of those tabs is now the shared .tab (in the list above). */
    .ac-expand, .ac-ico, .ce-move .mv, .cl-move .mv { min-width: 40px; min-height: 40px; }
    /* Home ▸ À surveiller: a journal notice's line (38 px on a desk, a condensed log) is a
       finger's 40 px here — the lines touch, so a short one opened its neighbour (R21B-04). */
    .dash-jrow { min-height: 40px; }
    /* r29b (SW1-07): the planning Gantt's job filter chips (24 px) and the trips / occurrences of the two month
       calendars (Déplacements, Maintenance ▸ Préventif: 17 px) were under a finger's size at 390 px. */
    .gt-job, .mcal-item, .dp-cal-trip { min-height: 40px; }
}
