/* ═══════════════════════════════════════════════════════════════════════════
   AutoFlotte — DataBlox theme
   ───────────────────────────────────────────────────────────────────────────
   Loaded AFTER styles.css so it wins on equal specificity. It does three
   things, in order:

     1. Re-points the Bootstrap design tokens at DataBlox's palette (Tailwind
        violet accent + gray/slate neutrals). Nearly every component in the
        template reads `--bs-*`, so re-pointing the variables re-skins the app
        without touching markup; the handful of places the template compiled a
        literal hex into (`.btn-primary` & friends) are patched below.
     2. Rebuilds the sidebar as DataBlox's rail: a gray-100 surface beside the
        white content area, pill nav rows, and a collapse toggle riding the
        right border.
     3. Adapts the shell for the PWA on mobile — off-canvas drawer with a
        backdrop, safe-area insets, and touch-sized targets.

   Everything here is additive: no rule in styles.css was edited, so the
   template can still be re-generated.
   ═══════════════════════════════════════════════════════════════════════════ */

/* ── 1. Palette ──────────────────────────────────────────────────────────── */
/* DataBlox styles itself with Tailwind's default ramps: violet for the brand
   accent, gray for surfaces/chrome, slate for text. These are those literal
   values, named once here so the mappings below read as intent. */
:root {
  --af-violet-50: #f5f3ff;
  --af-violet-100: #ede9fe;
  --af-violet-200: #ddd6fe;
  --af-violet-300: #c4b5fd;
  --af-violet-400: #a78bfa;
  --af-violet-500: #8b5cf6;
  --af-violet-600: #7c3aed;
  --af-violet-700: #6d28d9;
  --af-violet-800: #5b21b6;

  /* The same shades as bare channels, for the `rgba(…, alpha)` and
     `--bs-*-rgb` forms Bootstrap needs. accent.js rewrites both lists
     together, so they can never drift apart. */
  --af-violet-300-rgb: 196, 181, 253;
  --af-violet-400-rgb: 167, 139, 250;
  --af-violet-600-rgb: 124, 58, 237;
  --af-violet-700-rgb: 109, 40, 217;

  /* The three faint tints, locked to their light values. The product mock-ups
     in showcase.css are drawn as screenshots of a light-mode app and are used
     on pages that can be in dark mode, so they read these instead of the
     shades below, which [data-bs-theme=dark] darkens. */
  --af-violet-50-lt: #f5f3ff;
  --af-violet-100-lt: #ede9fe;
  --af-violet-200-lt: #ddd6fe;

  --af-gray-50: #f9fafb;
  --af-gray-100: #f3f4f6;
  --af-gray-200: #e5e7eb;
  --af-gray-300: #d1d5db;
  --af-gray-400: #9ca3af;
  --af-gray-500: #6b7280;
  --af-gray-600: #4b5563;

  --af-slate-500: #64748b;
  --af-slate-600: #475569;
  --af-slate-700: #334155;
  --af-slate-800: #1e293b;
  --af-slate-900: #0f172a;

  /* Sidebar rail surface — the one tone that separates chrome from content. */
  --af-rail-bg: var(--af-gray-100);
  --af-rail-border: var(--af-gray-200);
}

/* ── 2. Bootstrap token remap (light) ────────────────────────────────────── */
:root,
[data-bs-theme=light] {
  /* Read off the ramp above rather than repeating its hexes: accent.js re-points
     --af-violet-* on <html> when the user picks another accent, and everything
     downstream of these tokens follows without a second list to keep in step. */
  --bs-primary: var(--af-violet-600);
  --bs-primary-rgb: var(--af-violet-600-rgb);
  --bs-primary-text-emphasis: var(--af-violet-800);
  --bs-primary-bg-subtle: var(--af-violet-50);
  --bs-primary-border-subtle: var(--af-violet-200);

  /* Secondary is the template's "ink" colour (headings on dark chips, etc.). */
  --bs-secondary: #1e293b;
  --bs-secondary-rgb: 30, 41, 59;
  --bs-secondary-text-emphasis: #0f172a;
  --bs-secondary-bg-subtle: #f1f5f9;
  --bs-secondary-border-subtle: #cbd5e1;

  /* Semantic colours move to Tailwind's ramps so status chips sit in the same
     family as the accent instead of the template's original saturated set. */
  --bs-success: #10b981;
  --bs-success-rgb: 16, 185, 129;
  --bs-success-text-emphasis: #047857;
  --bs-success-bg-subtle: #ecfdf5;
  --bs-success-border-subtle: #a7f3d0;

  --bs-info: #6366f1;
  --bs-info-rgb: 99, 102, 241;
  --bs-info-text-emphasis: #4338ca;
  --bs-info-bg-subtle: #eef2ff;
  --bs-info-border-subtle: #c7d2fe;

  --bs-warning: #f59e0b;
  --bs-warning-rgb: 245, 158, 11;
  --bs-warning-text-emphasis: #b45309;
  --bs-warning-bg-subtle: #fffbeb;
  --bs-warning-border-subtle: #fde68a;

  --bs-danger: #ef4444;
  --bs-danger-rgb: 239, 68, 68;
  --bs-danger-text-emphasis: #b91c1c;
  --bs-danger-bg-subtle: #fef2f2;
  --bs-danger-border-subtle: #fecaca;

  --bs-light: #f3f4f6;
  --bs-light-rgb: 243, 244, 246;
  --bs-dark: #0f172a;
  --bs-dark-rgb: 15, 23, 42;

  /* Text + surfaces */
  --bs-body-color: #475569;
  --bs-body-color-rgb: 71, 85, 105;
  --bs-body-bg: #ffffff;
  --bs-body-bg-rgb: 255, 255, 255;
  --bs-heading-color: #0f172a;
  --bs-secondary-color: rgba(71, 85, 105, 0.72);
  --bs-secondary-color-rgb: 71, 85, 105;
  --bs-secondary-bg: #f9fafb;
  --bs-secondary-bg-rgb: 249, 250, 251;
  --bs-tertiary-color: rgba(71, 85, 105, 0.5);
  --bs-tertiary-bg: #f9fafb;
  --bs-border-color: #e5e7eb;
  --bs-border-color-translucent: rgba(15, 23, 42, 0.08);

  --bs-link-color: var(--af-violet-600);
  --bs-link-color-rgb: var(--af-violet-600-rgb);
  --bs-link-hover-color: var(--af-violet-700);
  --bs-link-hover-color-rgb: var(--af-violet-700-rgb);

  /* DataBlox's content area is one flat white surface; the rail carries the
     only tone. */
  --custom-body-bg: #ffffff;
  --app-sidebar-bg: var(--af-rail-bg);
  --app-header-bg: #ffffff;
  --custom-modal-backdrop-bg: #0f172a;
}

/* ── 3. Dark theme ───────────────────────────────────────────────────────── */
/* DataBlox's dark mode is VS Code "Dark+": #1e1e1e base, #252526 for anything
   elevated (cards, header, rail), #333 hairlines. The accent lightens to
   violet-400 so it stays readable on those surfaces. */
[data-bs-theme=dark] {
  /* The three faint tints have to stay dark here — a 97%-lightness wash behind
     a chip would be a hole punched in a dark panel. Overriding them on the ramp
     rather than on the tokens keeps the mapping below identical in both themes,
     and gives accent.js one shape to generate. */
  --af-violet-50: #2c2545;
  --af-violet-100: #352c55;
  --af-violet-200: #4c3f75;

  --bs-primary: var(--af-violet-400);
  --bs-primary-rgb: var(--af-violet-400-rgb);
  --bs-primary-text-emphasis: var(--af-violet-300);
  --bs-primary-bg-subtle: var(--af-violet-50);
  --bs-primary-border-subtle: var(--af-violet-200);

  --bs-success: #34d399;
  --bs-success-rgb: 52, 211, 153;
  --bs-success-bg-subtle: #1c3329;
  --bs-info: #818cf8;
  --bs-info-rgb: 129, 140, 248;
  --bs-info-bg-subtle: #262a47;
  --bs-warning: #fbbf24;
  --bs-warning-rgb: 251, 191, 36;
  --bs-warning-bg-subtle: #322a18;
  --bs-danger: #f87171;
  --bs-danger-rgb: 248, 113, 113;
  --bs-danger-bg-subtle: #3a2526;

  /* Secondary was the one family left on its light values here. In light mode
     it is the template's ink — a near-black chip colour over a #f1f5f9 wash —
     and both were coming through unchanged on a dark page: every
     `bg-secondary-subtle` (the neutral stat tiles on Réservations, Véhicules,
     Clients and Maintenance, and the count badges beside them) was a
     near-white box holding the page's light text, and the "Timbre fiscal"
     series on the dashboard chart reads --bs-secondary and was drawing a
     #1e293b bar on a #252526 card. Flipped end for end, the way the four
     families above are. */
  --bs-secondary: #94a3b8;
  --bs-secondary-rgb: 148, 163, 184;
  --bs-secondary-text-emphasis: #cbd5e1;
  --bs-secondary-bg-subtle: #2a2d2e;
  --bs-secondary-border-subtle: #3f3f3f;

  --bs-body-bg: #1e1e1e;
  --bs-body-bg-rgb: 30, 30, 30;
  --bs-body-color: #d4d4d4;
  --bs-body-color-rgb: 212, 212, 212;
  --bs-heading-color: #e6e6e6;
  --bs-secondary-color: rgba(212, 212, 212, 0.7);
  --bs-secondary-bg: #252526;
  --bs-secondary-bg-rgb: 37, 37, 38;
  --bs-tertiary-bg: #2a2d2e;
  --bs-border-color: #333333;
  --bs-border-color-translucent: rgba(255, 255, 255, 0.08);
  --bs-light: #2a2d2e;
  --bs-light-rgb: 42, 45, 46;

  --bs-link-color: var(--af-violet-400);
  --bs-link-color-rgb: var(--af-violet-400-rgb);
  --bs-link-hover-color: var(--af-violet-300);

  --custom-body-bg: #1e1e1e;
  --app-sidebar-bg: #252526;
  --app-header-bg: #252526;

  --af-rail-bg: #252526;
  --af-rail-border: #333333;
  --af-gray-100: #2a2d2e;
  --af-gray-200: #333333;
  --af-gray-400: #8a8a8a;
  --af-gray-500: #9d9d9d;
  --af-gray-600: #b0b0b0;
  --af-slate-700: #cccccc;
  --af-slate-900: #e6e6e6;
}

/* Cards sit one surface step above the page in dark mode (the VS Code panel
   tone) — otherwise they vanish into the #1e1e1e background. */
[data-bs-theme=dark] .card,
[data-bs-theme=dark] .offcanvas,
[data-bs-theme=dark] .modal-content,
[data-bs-theme=dark] .dropdown-menu {
  --bs-card-bg: #252526;
  --bs-offcanvas-bg: #252526;
  --bs-modal-bg: #252526;
  --bs-dropdown-bg: #252526;
}

/* ── 4. Components the template compiled a literal hex into ──────────────── */
.btn-primary {
  --bs-btn-bg: var(--bs-primary);
  --bs-btn-border-color: var(--bs-primary);
  --bs-btn-hover-bg: var(--af-violet-700);
  --bs-btn-hover-border-color: var(--af-violet-700);
  --bs-btn-active-bg: var(--af-violet-800);
  --bs-btn-active-border-color: var(--af-violet-800);
  --bs-btn-disabled-bg: var(--bs-primary);
  --bs-btn-disabled-border-color: var(--bs-primary);
  --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
}

.btn-outline-primary {
  --bs-btn-color: var(--bs-primary);
  --bs-btn-border-color: var(--bs-primary);
  --bs-btn-hover-bg: var(--bs-primary);
  --bs-btn-hover-border-color: var(--bs-primary);
  --bs-btn-active-bg: var(--af-violet-700);
  --bs-btn-active-border-color: var(--af-violet-700);
  --bs-btn-disabled-color: var(--bs-primary);
  --bs-btn-disabled-border-color: var(--bs-primary);
  --bs-btn-focus-shadow-rgb: var(--bs-primary-rgb);
}

/* `.btn-info` and `.btn-outline-info` compiled the template's own violet
   (#7008E7) rather than reading --bs-info, so they were the one pair of
   components that could still paint a second, brighter purple next to the
   brand one. Point them back at the token. */
.btn-info {
  --bs-btn-bg: var(--bs-info);
  --bs-btn-border-color: var(--bs-info);
  --bs-btn-hover-bg: var(--bs-info-text-emphasis);
  --bs-btn-hover-border-color: var(--bs-info-text-emphasis);
  --bs-btn-active-bg: var(--bs-info-text-emphasis);
  --bs-btn-active-border-color: var(--bs-info-text-emphasis);
  --bs-btn-disabled-bg: var(--bs-info);
  --bs-btn-disabled-border-color: var(--bs-info);
  --bs-btn-focus-shadow-rgb: var(--bs-info-rgb);
}

.btn-outline-info {
  --bs-btn-color: var(--bs-info);
  --bs-btn-border-color: var(--bs-info);
  --bs-btn-hover-bg: var(--bs-info);
  --bs-btn-hover-border-color: var(--bs-info);
  --bs-btn-active-bg: var(--bs-info-text-emphasis);
  --bs-btn-active-border-color: var(--bs-info-text-emphasis);
  --bs-btn-disabled-color: var(--bs-info);
  --bs-btn-disabled-border-color: var(--bs-info);
  --bs-btn-focus-shadow-rgb: var(--bs-info-rgb);
}

/* Dark mode lightens the accent to violet-400 so it reads on the VS Code
   surfaces — and that turns every component that fills with the accent and
   writes white on top into pale-on-pale: the selected ledger tab, the current
   page in a pagination, the chosen agency in the switcher's menu. Bootstrap
   keeps each of those pairs in a per-component variable, declared on the
   component itself, so the ink has to be corrected there rather than on the
   root: the same correction .btn-primary carries below. */
[data-bs-theme=dark] .nav-pills {
  --bs-nav-pills-link-active-color: #1e1e1e;
}

[data-bs-theme=dark] .pagination {
  --bs-pagination-active-color: #1e1e1e;
}

[data-bs-theme=dark] .dropdown-menu {
  --bs-dropdown-link-active-color: #1e1e1e;
}

/* A pie tooltip is filled with the slice's own colour, so its ink follows the
   fill rather than the page. Light mode draws those slices from the saturated
   end of every ramp and takes white; dark mode draws them from the pastel end
   — violet-400, #34d399, #f87171 — and takes the same near-black the button
   and the pills above take. The class is set on the chart (dashboard.js). */
[data-bs-theme=dark] .apexcharts-tooltip.af-tooltip-onfill,
[data-bs-theme=dark] .apexcharts-tooltip.af-tooltip-onfill .apexcharts-tooltip-series-group {
  color: #1e1e1e;
}

/* The tile and the count inside a selected tab invert with it (§11l). */
[data-bs-theme=dark] .nav-pills .nav-link.active .af-tab-ic,
[data-bs-theme=dark] .nav-pills .nav-link.active .af-tab .badge {
  background-color: rgba(0, 0, 0, 0.18) !important;
  color: #1e1e1e !important;
}

/* Bootstrap compiles `.text-bg-*` with the text colour that suits the light
   theme's fill and writes it !important, so the badge kept white text after the
   accent lightened to violet-400 for dark mode — 2.7:1, the same mismatch the
   button below is corrected for. */
[data-bs-theme=dark] .text-bg-primary {
  color: #1e1e1e !important;
}

[data-bs-theme=dark] .btn-primary {
  --bs-btn-color: #1e1e1e;
  --bs-btn-hover-bg: var(--af-violet-300);
  --bs-btn-hover-border-color: var(--af-violet-300);
  --bs-btn-active-bg: var(--af-violet-300);
  --bs-btn-active-border-color: var(--af-violet-300);
  --bs-btn-hover-color: #1e1e1e;
  --bs-btn-active-color: #1e1e1e;
  --bs-btn-disabled-color: #1e1e1e;
}

/* DataBlox's cards: a 12px radius, a real hairline, and no coloured shadow. */
.card {
  --bs-card-border-radius: 0.75rem;
  --bs-card-inner-border-radius: calc(0.75rem - var(--bs-border-width));
  --bs-card-box-shadow: none;
  box-shadow: none;
}

/* ── 5. App shell ────────────────────────────────────────────────────────── */
.app-header {
  background-color: var(--app-header-bg);
  border-bottom: 1px solid var(--bs-border-color);
}

/* The burger is a fixed-height flex item in a stretch row; without this it
   pins to the top of the header band instead of sitting in it. */
.app-header-inner {
  align-items: center;
}

/* The template pads the content area 10px below the fixed header, which reads
   as a sliver of empty page under the header border before the breadcrumb.
   Every page already opens with its own head or card spacing. */
.app-wrapper {
  padding-top: 0;
}

.app-menubar {
  background-color: var(--af-rail-bg);
  border-right: 1px solid var(--af-rail-border);
  /* The template's 30px ambient shadow reads as a seam next to a flat rail. */
  box-shadow: none;
  /* Column flex so the nav takes the leftover height between the brand and the
     theme row (the template sized it with a calc() that assumed both). */
  display: flex;
  flex-direction: column;
  /* dvh tracks the collapsing mobile URL bar; 100vh above it would push the
     theme row under the browser chrome. */
  height: 100dvh;
}

.app-navbar-brand {
  background-color: var(--af-rail-bg);
  border-right: 1px solid var(--af-rail-border);
  border-bottom: 1px solid transparent;
  padding: 15px 20px;
  flex-shrink: 0;
}

.app-navbar-brand span {
  color: var(--af-slate-900);
}

/* The nav is the rail's only growing region: the brand sits above it and the
   theme row below, so it takes the leftover height and scrolls on its own.
   (The template hard-codes a calc() height that assumes a fixed footer.) */
.app-navbar.af-sidebar-scroll {
  height: auto;
  flex: 1 1 auto;
  min-height: 0;
  width: 100%;
  padding: 10px 12px;
}

/* ── 6. Sidebar nav — DataBlox rail rows ─────────────────────────────────── */
.af-nav {
  list-style: none;
  padding: 0;
  margin: 0;
}

.af-nav li {
  margin-bottom: 2px;
}

.af-nav-heading {
  font-size: 10.5px;
  font-weight: 700;
  color: var(--af-gray-400);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  padding: 14px 12px 6px;
  list-style: none;
}

.af-nav-divider {
  border-top: 1px solid var(--af-rail-border);
  margin: 8px 4px;
  list-style: none;
}

.af-nav-link {
  display: flex;
  align-items: center;
  gap: 12px;
  height: 40px;
  padding: 0 12px;
  border-radius: 12px;
  border: 1px solid transparent;
  color: var(--af-gray-500);
  text-decoration: none;
  font-size: 14px;
  font-weight: 500;
  white-space: nowrap;
  transition: background-color 0.15s ease, color 0.15s ease,
    border-color 0.15s ease, box-shadow 0.15s ease;
}

.af-nav-link i {
  font-size: 18px;
  line-height: 1;
  display: inline-flex;
  color: var(--af-gray-400);
  flex-shrink: 0;
  transition: color 0.15s ease;
}

.af-nav-link:hover {
  background-color: var(--af-gray-200);
  color: var(--af-gray-600);
}

.af-nav-link:hover i {
  color: var(--af-gray-600);
}

/* Active row: a white card lifted off the rail with a violet hairline — the
   DataBlox treatment, rather than a filled primary block. */
.af-nav-link.active,
.af-nav-link.active:hover {
  background-color: var(--bs-body-bg);
  border-color: var(--af-violet-300);
  color: var(--bs-primary);
  box-shadow: 0 1px 2px rgba(15, 23, 42, 0.06);
}

.af-nav-link.active i,
.af-nav-link.active:hover i {
  color: var(--bs-primary);
}

[data-bs-theme=dark] .af-nav-link.active,
[data-bs-theme=dark] .af-nav-link.active:hover {
  background-color: #2c2545;
  border-color: #4c3f75;
  box-shadow: none;
}

/* Rail footer (theme switch) */
.af-sidebar-footer {
  flex-shrink: 0;
  border-top: 1px solid var(--af-rail-border);
  padding: 12px 16px calc(12px + env(safe-area-inset-bottom));
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
}

.af-sidebar-footer .theme-btn {
  background-color: var(--af-gray-200);
  flex-shrink: 0;
}

/* ── 7. Collapse toggle ──────────────────────────────────────────────────── */
/* The chip riding the rail's right border, exactly as in DataBlox. Centred in
   the header band so it lines up with the search button across the seam, and
   above both the header and the content (which share z-index 9). */
.af-rail-toggle {
  position: absolute;
  top: calc(var(--app-header-height) / 2 - 13px);
  right: -13px;
  z-index: 11;
  height: 26px;
  width: 26px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  border: 1px solid var(--bs-border-color);
  background-color: var(--bs-body-bg);
  color: var(--af-gray-400);
  box-shadow: 0 1px 3px rgba(15, 23, 42, 0.08);
  transition: color 0.15s ease, border-color 0.15s ease;
}

.af-rail-toggle:hover {
  border-color: var(--af-violet-300);
  color: var(--bs-primary);
}

.af-rail-toggle i {
  font-size: 13px;
  line-height: 1;
  display: inline-flex;
}

/* Which chevron shows is purely a function of the rail's state, so the markup
   carries both and CSS picks one. */
.af-rail-toggle .af-icon-expand {
  display: none;
}

[data-app-sidebar=mini] .af-rail-toggle .af-icon-collapse {
  display: none;
}

[data-app-sidebar=mini] .af-rail-toggle .af-icon-expand {
  display: inline-flex;
}

/* ── 7b. Header search field ─────────────────────────────────────────────── */
/* The global search is drawn as the field it stands for. It is still a button —
   pressing it raises the modal that owns the real input — so nothing here may
   suggest a caret. */
.af-header-start {
  padding-left: 16px;
}

/* Far enough right to clear the collapse chip, which overhangs the rail's seam
   by 13px and would otherwise sit almost against the field. */
@media (min-width: 1200px) {
  .af-header-start {
    padding-left: 44px;
  }
}

.af-search-trigger {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  width: 280px;
  max-width: 100%;
  height: 38px;
  padding: 0 10px;
  border: 1px solid var(--bs-border-color);
  border-radius: 8px;
  background-color: var(--af-rail-bg);
  /* A solid slate rather than --bs-secondary-color: that token is 72% alpha,
     which lands at 3.56:1 on the rail and made the one control on the header
     that invites typing the faintest thing on it. The panel it opens reads off
     the same value — see §11s. */
  color: var(--af-slate-600);
  font-size: 0.875rem;
  text-align: left;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

[data-bs-theme=dark] .af-search-trigger {
  color: #b4b4b4;
}

.af-search-trigger:hover,
.af-search-trigger:focus-visible {
  border-color: var(--af-violet-300);
  background-color: var(--bs-body-bg);
}

.af-search-trigger-icon {
  display: inline-flex;
  flex-shrink: 0;
  font-size: 1rem;
  line-height: 1;
  color: var(--bs-primary);
}

.af-search-trigger-text {
  flex-grow: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.af-search-trigger-kbd {
  flex-shrink: 0;
  padding: 1px 6px;
  border: 1px solid var(--bs-border-color);
  border-radius: 5px;
  background-color: var(--bs-body-bg);
  color: var(--bs-secondary-color);
  font-size: 11px;
  line-height: 1.5;
}

/* A narrow header keeps the icon alone: the placeholder needs room it does not
   have, and the shortcut it advertises needs a keyboard. */
@media (max-width: 767.98px) {
  .af-search-trigger {
    width: 38px;
    padding: 0;
    justify-content: center;
  }

  .af-search-trigger-text,
  .af-search-trigger-kbd {
    display: none;
  }
}

/* Below the desktop breakpoint the rail is an off-canvas drawer driven by the
   header's burger, so the edge toggle would be meaningless. */
@media (max-width: 1199.98px) {
  .af-rail-toggle {
    display: none;
  }
}

/* ── 8. Collapsed (mini) rail ────────────────────────────────────────────── */
[data-app-sidebar=mini] .app-navbar-brand {
  justify-content: center;
  padding: 15px 10px;
}

/* Only the wordmark folds away — the mark itself is what tells you which app
   this rail belongs to, so it stays. Targeting the class rather than `span`
   matters: .af-logo-mark is a span too, and hiding both left the collapsed
   rail with no logo at all. */
[data-app-sidebar=mini] .app-navbar-brand .af-logo-word {
  display: none;
}

[data-app-sidebar=mini] .app-navbar.af-sidebar-scroll {
  padding: 10px 8px;
}

/* Collapsed rows are 40px squares centred in the rail, not full-width bars —
   the icon is the whole row, so the active chip should be square too. */
[data-app-sidebar=mini] .af-nav-link {
  justify-content: center;
  width: 40px;
  margin: 0 auto;
  padding: 0;
  gap: 0;
}

[data-app-sidebar=mini] .af-nav-link span {
  display: none;
}

[data-app-sidebar=mini] .af-nav-heading {
  display: none;
}

[data-app-sidebar=mini] .af-nav-divider {
  margin: 6px 4px;
}

[data-app-sidebar=mini] .af-sidebar-footer {
  justify-content: center;
  padding-left: 8px;
  padding-right: 8px;
}

[data-app-sidebar=mini] .af-sidebar-footer .af-sidebar-footer-label {
  display: none;
}

[data-app-sidebar=mini] .af-sidebar-footer .theme-btn {
  width: 58px;
  gap: 4px;
}

/* ── 9. Mobile drawer ────────────────────────────────────────────────────── */
/* The rail slides in over the content; the backdrop both dims the page and
   gives a tap target to dismiss it (main.js toggles `.show`). */
/* Above the fixed header (z-index 9) so the whole page dims, and below the
   drawer itself — Bootstrap's own offcanvas layer, so modals still win. */
.app-sidebar-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1040;
  background-color: rgba(15, 23, 42, 0.4);
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.3s ease, visibility 0.3s ease;
}

.app-sidebar-backdrop.show {
  opacity: 1;
  visibility: visible;
}

@media (min-width: 1200px) {
  .app-sidebar-backdrop {
    display: none;
  }
}

/* Locks the page behind an open drawer so a scroll gesture on the dimmed
   content doesn't move it. */
body.af-drawer-open {
  overflow: hidden;
}

@media (max-width: 1199.98px) {
  .app-menubar {
    /* Rides above the backdrop; the template's z-index 9 would put the drawer
       under it (and under the header). */
    z-index: 1045;
  }

  /* Only while open — the closed drawer parks 10px off-screen, so a permanent
     shadow would bleed a grey sliver down the left edge of every page. */
  .app-menubar.open {
    box-shadow: 8px 0 24px rgba(15, 23, 42, 0.12);
  }

  /* Off-canvas, the rail is the whole surface — the brand's right border was
     only there to line up with the header. */
  .app-navbar-brand {
    width: 100%;
    min-width: 0;
    border-right: 0;
    border-bottom: 1px solid var(--af-rail-border);
  }
}

/* ── 10. Mobile / PWA shell ──────────────────────────────────────────────── */
@media (max-width: 991.98px) {
  :root {
    --app-header-height: 64px;
  }

  .app-wrapper {
    padding-left: 16px;
    padding-right: 16px;
  }

  /* Comfortable thumb targets in the drawer. */
  .af-nav-link {
    height: 46px;
    font-size: 15px;
  }
}

/* The burger: same pill-button language as the rest of the chrome. */
.app-toggler {
  height: 40px;
  width: 40px;
  margin: 0 6px 0 12px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background-color: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: 10px;
  box-shadow: none;
  color: var(--af-gray-500);
}

.app-toggler:hover {
  color: var(--bs-primary);
  border-color: var(--af-violet-300);
}

[data-bs-theme=dark] .app-toggler {
  background-color: var(--bs-secondary-bg);
}

/* Installed-PWA insets: in standalone display the app draws under the status
   bar / home indicator (the viewport is `viewport-fit=cover`), so the fixed
   chrome has to pad itself out of those regions. */
@media (display-mode: standalone) {
  .app-header {
    height: calc(var(--app-header-height) + env(safe-area-inset-top));
    padding-top: env(safe-area-inset-top);
  }

  .app-header-start,
  .app-header-end {
    height: var(--app-header-height);
  }

  .app-wrapper {
    margin-top: calc(var(--app-header-height) + env(safe-area-inset-top));
    padding-bottom: calc(30px + env(safe-area-inset-bottom));
  }

  .app-menubar {
    padding-top: env(safe-area-inset-top);
  }

  /* The rail is padded down by the inset, so the toggle has to follow to stay
     level with the header's controls. */
  .af-rail-toggle {
    top: calc(env(safe-area-inset-top) + var(--app-header-height) / 2 - 13px);
  }
}

/* Left/right insets matter in landscape on notched phones. */
@media (max-width: 1199.98px) {
  .app-wrapper {
    padding-left: max(16px, env(safe-area-inset-left));
    padding-right: max(16px, env(safe-area-inset-right));
  }
}

/* ── 10b. Brand mark ─────────────────────────────────────────────────────── */
/* The wheel on its tile, drawn in CSS rather than served as logo.svg, because
   the tile has to follow the accent the user picked and a flat <img> cannot:
   the hex inside the file would stay violet next to a re-coloured app.
   The wheel itself is the same `wheel.svg`, used as a mask so the fill comes
   from CSS. Proportions are logo.svg's: a 45-unit tile with a 14-unit radius
   (31%) around a 35-unit wheel (78%). */
.af-logo-mark,
.af-loader-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  border-radius: 31%;
  background: var(--bs-primary);
}

.af-logo-mark::before,
.af-loader-mark::before {
  content: "";
  display: block;
  width: 78%;
  height: 78%;
  background-color: #fff;
  -webkit-mask: url("../images/wheel.svg") center / contain no-repeat;
  mask: url("../images/wheel.svg") center / contain no-repeat;
}

.af-logo-mark {
  width: 34px;
  height: 34px;
}

/* The page loader is the same mark with the wheel turning inside it. Keyframes
   come from .af-loader-spin's rule in styles.css. */
.af-loader-mark::before {
  animation: af-spin 1s linear infinite;
}

@media (prefers-reduced-motion: reduce) {
  .af-loader-mark::before {
    animation: none;
  }
}

/* The loader belongs to the page being loaded, so it centres on the content
   area rather than the window: styles.css pins it `inset: 0`, which put the
   mark in the middle of the whole screen — pulled left under the rail and up
   behind the header. Inset by the same two variables .app-wrapper uses and it
   lands in the middle of the space the page is about to fill. The rail's own
   width variable drops to 80px when it collapses, so this follows it. */
.af-page-loader {
  inset: var(--app-header-height) 0 0 var(--app-sidebar-width);
}

/* Below the desktop breakpoint the rail is a drawer over the page and
   .app-wrapper drops its margin, so the content starts at the left edge. */
@media (max-width: 1199.98px) {
  .af-page-loader {
    left: 0;
  }
}

/* ── 11. Avatars ─────────────────────────────────────────────────────────── */
/* Squircles, not circles. DataBlox draws every avatar as a rounded square whose
   radius steps with the box (see avatarRadius() in its lib/avatar): 12px up to
   64px, 16px above. The template sprinkles `rounded-circle` on avatars across
   ~17 files, so the utility is overridden here on `.avatar` rather than edited
   out of each one — and `.avatar` covers both person avatars and the icon chips
   built from the same primitive, which is what keeps the stat tiles on
   car-detail matching the dashboard's. */
.avatar {
  border-radius: 12px;
}

.avatar.rounded-circle {
  border-radius: 12px !important;
}

.avatar.avatar-xl,
.avatar.avatar-xxl {
  border-radius: 16px;
}

.avatar.avatar-xl.rounded-circle,
.avatar.avatar-xxl.rounded-circle {
  border-radius: 16px !important;
}

/* An avatar carrying a status dot must not clip: the dot sits on the corner,
   outside the box, and the markup pairs `avatar-status-*` with `overflow-hidden`
   (which a circular avatar needed to crop its photo). Releasing the clip is safe
   — `.avatar img` rounds itself off the inherited radius. */
.avatar[class*=avatar-status] {
  overflow: visible !important;
}

/* The dot's per-size `right` offsets were compensating for a circle's curve —
   on a square it just floats inside the box. Pin it to the corner, and square
   it off too so it echoes the box it is pinned to. */
.avatar[class*=avatar-status]::after,
.avatar[class*=avatar-status].avatar-xxs::after,
.avatar[class*=avatar-status].avatar-xs::after,
.avatar[class*=avatar-status].avatar-sm::after,
.avatar[class*=avatar-status].avatar-lg::after,
.avatar[class*=avatar-status].avatar-xl::after,
.avatar[class*=avatar-status].avatar-xxl::after {
  bottom: -2px;
  right: -2px;
  border-radius: 3px;
}

/* ── 11a. Squares, not circles ───────────────────────────────────────────── */
/* House rule for the whole app: nothing is a circle. Every icon chip, initials
   bubble and icon button is a squircle like the avatars above, so a stat tile,
   a table cell and a rail row all read as one family.

   `.rounded-circle` is sprinkled across ~20 fragments, partials and rendered
   strings, so the utility is neutralised here once rather than edited out of
   every call site — new markup that copies the template's habit lands square by
   default too. The radius is a percentage, not a px value, so the 20px delete
   badge on a photo thumbnail and the 44px lightbox arrow keep the same
   curve-to-box ratio instead of one of them coming out a lozenge. Every element
   carrying the class is a square box; the utility is never put on a wide one,
   where a percentage would draw elliptical corners. */
.rounded-circle {
  border-radius: 30% !important;
}

/* The template's own circles, drawn with a literal 50% instead of the utility,
   so the rule above cannot reach them: the theme switch and its sliding knob,
   round pagination, the Tagify avatar wraps, and the picker rows shared by the
   booking form and the calendar filters. */
.theme-btn {
  border-radius: 12px;
}

.theme-btn::after {
  border-radius: 8px;
}

.pagination-rounded .page-link {
  border-radius: 8px;
}

.tagify__tag__avatar-wrap {
  border-radius: 4px;
}

.tagify__dropdown__item__avatar-wrap {
  border-radius: 10px;
}

.cal-dd-avatar-wrap {
  border-radius: 10px;
}

.cal-cust-initial {
  border-radius: 5px;
}

/* Round survives in exactly two places, where a square would be wrong rather
   than merely different: a spinner is a rotating ring, and a square radio reads
   as a checkbox. Both are left as the template draws them. */

/* ── 11b. Header agency switcher ─────────────────────────────────────────── */
/* The template draws `.badge-standard` as a full pill. It is used in exactly
   one place — the agency/company chip in the header — and it sits in a row of
   squircles (the avatar beside it, the icon buttons around it), so square it
   off to match. The inner count badge follows one step tighter, the way a
   nested corner has to so the two curves stay concentric. */
.badge-standard {
  border-radius: 12px;
}

.badge-standard .badge {
  border-radius: 8px;
}

/* The chip names the agency you are looking at, so it sits in the middle of
   the header — but between the two clusters, not centred on the page over the
   top of them. It used to be absolutely centred, which reads the same while the
   header is wide and draws the chip over the search field, the calendar and the
   avatar the moment it is not. As a flex item it takes whatever the clusters
   leave and ellipsises the name instead of growing past them. */
.af-header-agency {
  display: flex;
  justify-content: center;
  flex: 1 1 auto;
  min-width: 0;
  margin: 0 12px;
}

.af-header-agency > div {
  min-width: 0;
  max-width: 100%;
}

/* `text-truncate` on its own does nothing to a flex item: its min-width is
   `auto`, so it refuses to shrink below the name it is holding. */
.af-header-agency .text-truncate {
  min-width: 0;
}

.af-header-agency .dropdown-menu {
  min-width: 260px;
}

/* While the rail is a drawer, the chip is the agency's name and nothing else.
   Measured on a 390px screen, "Agence :" and the car count take every pixel
   there is and leave the name exactly none — and the name is the whole point of
   the chip. Which of agency and company you are in is spelled out in the menu
   the chip opens, one tap away; the count is on the Véhicules page. */
@media (max-width: 991.98px) {
  .af-header-agency {
    margin: 0 8px;
  }

  .af-header-agency .badge-standard .text-muted,
  .af-header-agency .badge-standard > .badge {
    display: none;
  }

  /* The count badge was standing in for the chip's right padding and for most
     of its height, so both have to be stated once it is gone. */
  .af-header-agency .badge-standard {
    padding: 2px 10px;
    min-height: 32px;
    font-size: 13px;
  }

  /* Popper keeps a dropdown inside the viewport vertically, not horizontally,
     and 260px of menu does not fit beside a 360px phone's edge. */
  .af-header-agency .dropdown-menu {
    min-width: 0;
    width: max-content;
    max-width: calc(100vw - 24px);
  }
}

/* The header's "Nouvelle réservation" — the same card the Réservations page
   heads with (§11k), so the one action is drawn one way wherever it appears.
   It is hidden rather than shrunk on the small shell: the strip there is
   already holding a burger, a search field, the agency chip and an avatar, and
   the dashboard's own tile carries the action instead (`d-lg-none` on that
   one, this rule on this one — the two are complementary, so exactly one of
   them is on screen at any width). Plain CSS, no display utility: x-show has to
   be able to hide it for a read-only account, and an !important utility would
   outrank the inline style it hides with. */
.af-action.af-new-booking {
  display: none;
  /* A flex item in the header row: without this it is squeezed and the label
     folds onto a second line, which lifts the whole band. The agency chip
     beside it is the one thing in that row built to give ground. */
  flex-shrink: 0;
  white-space: nowrap;
  /* The page's card is sized by its two lines; a control in the header is sized
     by the row it stands in — 40px, level with the calendar button beside it,
     with the tile brought in to fit inside that. */
  block-size: 40px;
  padding-block: 0;
  padding-inline: 4px 12px;
  gap: 8px;
}

.af-new-booking .af-action-ic {
  width: 32px;
  height: 32px;
  border-radius: 9px;
  font-size: 0.875rem;
}

@media (min-width: 992px) {
  .af-action.af-new-booking {
    display: inline-flex;
  }
}

/* The card is drawn on --bs-body-bg, which is the page's own tone: on a light
   header the two match and the card is its border, in dark the header is the
   elevated #252526 and the page is #1e1e1e, so the same card came out as a
   hole punched in the header. Here it takes the surface it is actually on. */
[data-bs-theme=dark] .af-action.af-new-booking {
  background-color: var(--app-header-bg);
}

/* Below xxl the card is its icon tile and nothing else — 38px of tile inside a
   border the hover paints violet, which is 40px all told and level with the
   calendar button beside it. The rail arrives at 1200 and takes 260px off this
   row; at 1280 the two lines would leave the agency chip under 100px. */
@media (max-width: 1399.98px) {
  .af-action.af-new-booking {
    inline-size: 40px;
    padding: 0;
    justify-content: center;
    border-color: transparent;
    /* One step out from the tile's own radius, so the hover ring follows its
       curve instead of cutting across it. */
    border-radius: 12px;
  }

  /* Two classes deep, because the label carries `.af-action-t` as well and that
     rule — a class selector like this one, further down the file — sets it
     back to `display: block`. Left at one class the words stayed, spilling out
     of the 40px box over the calendar button beside it. */
  .af-new-booking .af-new-booking-text {
    display: none;
  }
}

/* Below the tablet breakpoint every pixel the rest of the header gives up is
   one the name keeps. The period selector is one page's chrome while the chip
   is which books you are reading, so it drops its icon and swaps "Toutes les
   années" for "Toutes" (the store's shortLabel, picked in the markup by
   breakpoint). The cap is sized so neither a year nor "Toutes" is ever what
   gets cut — the ellipsis is a backstop for a label neither anticipated. */
@media (max-width: 767.98px) {
  .af-header-start {
    padding-left: 4px;
  }

  .app-toggler {
    margin: 0 4px 0 8px;
  }

  .af-period-toggle {
    max-width: 88px;
  }

  .af-period-toggle > i {
    display: none;
  }

  .af-period-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
}

/* ── 11c. Accent swatches ────────────────────────────────────────────────── */
/* The colour picker in the profile panel (user-offcanvas.hbs). Squircles like
   every other small tile in the app, with the selection ring drawn outside the
   box so it never eats into the colour it is marking. */
.af-swatch {
  width: 26px;
  height: 26px;
  padding: 0;
  border: 0;
  border-radius: 8px;
  cursor: pointer;
  position: relative;
  transition: transform 0.15s ease, box-shadow 0.15s ease;
}

.af-swatch:hover {
  transform: scale(1.1);
}

.af-swatch.is-active {
  /* Two rings: a body-coloured gap, then the ink one. Reads on a white panel
     and on a dark one, and needs no extra element. */
  box-shadow: 0 0 0 2px var(--bs-body-bg), 0 0 0 4px var(--bs-heading-color);
  transform: none;
}

/* The pipette tile: an empty dashed square holding a native colour input that
   covers it entirely and is invisible, so the whole tile is the hit area. */
.af-swatch-pick {
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  border: 1px dashed var(--bs-border-color);
  color: var(--bs-secondary-color);
  font-size: 12px;
}

.af-swatch-pick:hover {
  border-color: var(--af-violet-400);
  color: var(--bs-primary);
}

.af-swatch-pick input[type=color] {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  cursor: pointer;
  border: 0;
  padding: 0;
}

/* ── 11d. Side panels — the contract head ────────────────────────────────── */
/* Every offcanvas wears the head of the paper contract: the French title on the
   left over a small context line, the Arabic one on the right, and the accent
   rule running the whole width below. The close button sits after the Arabic
   title so the two languages keep the edges of the panel. */
.af-panel-head {
  display: flex;
  align-items: flex-start;
  gap: 1rem;
  padding: 1.05rem 1.25rem 0.85rem;
  border-bottom: 2px solid var(--bs-primary);
}

.af-panel-head-titles {
  min-width: 0;
  flex: 1;
}

.af-panel-head .af-panel-title {
  margin: 0;
  font-size: 0.9375rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  line-height: 1.3;
  color: var(--bs-heading-color);
}

.af-panel-head .af-panel-sub {
  margin-top: 0.15rem;
  font-size: 0.8125rem;
  line-height: 1.3;
  color: var(--bs-secondary-color);
}

.af-panel-head .af-panel-title-ar {
  font-size: 1.0625rem;
  font-weight: 700;
  line-height: 1.4;
  white-space: nowrap;
  color: var(--bs-heading-color);
}

/* Bootstrap's .offcanvas-header gives the close button a negative margin that
   pulls it into the padding; here it is a flex item like any other. */
.af-panel-head .btn-close {
  margin: 0.15rem 0 0 0;
  flex-shrink: 0;
}

@media (max-width: 575.98px) {
  .af-panel-head {
    padding-inline: 1rem;
    gap: 0.65rem;
  }

  .af-panel-head .af-panel-title {
    font-size: 0.8125rem;
  }

  .af-panel-head .af-panel-title-ar {
    font-size: 0.9375rem;
  }
}

/* ── 11e. Side panels — footer actions ───────────────────────────────────── */
/* One footer everywhere: Annuler then the primary save on the left, anything
   destructive pushed to the far edge by .ms-auto.
   On phones the offcanvas is full-width and four buttons no longer fit on one
   line — they squeeze into each other, clip their labels and end up with
   different heights. Below sm they wrap into a grid of equal-width,
   equal-height buttons instead. */
.af-panel-actions {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

@media (max-width: 575.98px) {
  .af-panel-actions {
    flex-wrap: wrap;
    align-items: stretch;
  }

  .af-panel-actions > .btn {
    flex: 1 1 calc(50% - 0.25rem); /* two per row, gap = .5rem */
    min-width: 0;
    margin-left: 0 !important; /* neutralise .ms-auto so the grid stays even */
    justify-content: center;
    text-align: center;
    padding-inline: 0.5rem;
    font-size: 0.8125rem;
    line-height: 1.25;
  }
}

/* ── 11f. Contract recap ─────────────────────────────────────────────────── */
/* The invoice block in the booking panel, printed the way the contract prints
   it: dotted rules between the lines, the total in an accent band, and the
   amount spelled out underneath. */
.af-recap {
  border: 1px solid var(--bs-border-color);
  border-radius: 14px;
  background: var(--bs-body-bg);
  padding: 1rem 1.15rem 1.15rem;
}

.af-recap-row {
  display: flex;
  align-items: baseline;
  gap: 0.75rem;
  padding: 0.5rem 0;
  border-bottom: 1px dashed var(--bs-border-color);
}

.af-recap-label {
  color: var(--bs-secondary-color);
  min-width: 0;
}

/* Three columns, as on the contract: the label runs from the left, the figures
   share a fixed column of their own, and the Arabic keeps the right edge. */
.af-recap-value {
  flex: 0 0 auto;
  width: 7rem;
  margin-left: auto;
  text-align: right;
  font-weight: 600;
  color: var(--bs-heading-color);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* `text-align: right`, not `end`: these spans are dir="rtl", where `end` is the
   LEFT edge — which left the Arabic floating mid-row with the gutter empty
   beside it. */
.af-recap-ar {
  flex: 0 0 auto;
  width: 10.5rem;
  text-align: right;
  font-size: 0.8125rem;
  color: var(--bs-secondary-color);
}

.af-recap-total {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-top: 0.75rem;
  padding: 0.7rem 1rem;
  border-radius: 10px;
  background: var(--bs-primary-bg-subtle);
}

.af-recap-total-label {
  font-weight: 600;
  color: var(--bs-primary-text-emphasis);
}

.af-recap-total-value {
  margin-left: auto;
  text-align: right;
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--bs-primary);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* 10.5rem less the band's own inset, so the total lands in the same two columns
   as the lines above it. */
.af-recap-total-ar {
  flex: 0 0 auto;
  width: 9.5rem;
  text-align: right;
  font-size: 0.8125rem;
  color: var(--bs-primary-text-emphasis);
}

.af-recap-words {
  margin: 0.6rem 0 0;
  font-size: 0.8125rem;
  font-style: italic;
  color: var(--bs-secondary-color);
}

.af-recap-foot {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 1.5rem;
  justify-content: space-between;
  margin-top: 0.9rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--bs-border-color);
}

@media (max-width: 575.98px) {
  .af-recap {
    padding-inline: 0.85rem;
  }

  /* Below sm the three columns no longer fit; the Arabic gutter is the one that
     gives way, since the figure is what the row is there to show. */
  .af-recap-ar,
  .af-recap-total-ar {
    display: none;
  }
}

/* ── 11g. Side panels — a field that only reads ──────────────────────────── */
/* A value the panel shows but never edits (the vehicle of an existing
   maintenance). A disabled <select> says "you may change this, just not now";
   this reads as the record's subject instead — the brand mark, the model and the
   plate, on the same tinted surface the form controls sit on. */
.af-static-field {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  min-height: calc(1.5em + 1rem + 2px); /* .form-select's height */
  padding: 0.5rem 0.85rem;
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  background: var(--bs-tertiary-bg);
}

.af-static-field-logo {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 40px;
  height: 40px;
  border-radius: 10px;
  background: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
}

.af-static-field-logo img {
  width: 26px;
  height: 26px;
  object-fit: contain;
}

.af-static-field-body {
  min-width: 0;
  line-height: 1.25;
}

.af-static-field-title {
  font-weight: 600;
  color: var(--bs-heading-color);
}

.af-static-field-sub {
  margin-top: 0.1rem;
  font-size: 0.8125rem;
  color: var(--bs-secondary-color);
}

/* ── 11h. Immatriculation ────────────────────────────────────────────────── */
/* The plate is two numeric halves either side of a fixed "TN": the country code
   is a chip, never a field, so the format survives whatever is typed. Both
   halves share the width because either can run to four digits. */
.af-plate .input-group-text {
  background-color: var(--bs-tertiary-bg);
  color: var(--bs-heading-color);
  letter-spacing: 0.06em;
}

.af-plate .form-control {
  letter-spacing: 0.06em;
  flex: 1 1 0;
}

/* The same plate read back rather than typed — on the vehicle card, under the
   model. A chip, not a heading: it identifies the car without competing with
   its name, and the tabular figures keep 766 TN 8877 from wobbling. */
.af-plate-tag {
  display: inline-block;
  padding: 0.125rem 0.5rem;
  border: 1px solid var(--bs-border-color);
  border-radius: 6px;
  background-color: var(--bs-tertiary-bg);
  color: var(--bs-heading-color);
  font-size: 0.8125rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ── 11i. Vehicle photo gallery ──────────────────────────────────────────── */
/* The uploads bucket is private, so each photo is fetched as a blob once the
   page has already rendered. Two things follow: the slide keeps one height
   whether its photo has arrived or not — otherwise the card grows under the
   reader as each one lands — and a pending slide draws a skeleton instead of an
   <img>, which with an empty src prints its alt text across the gallery. */
/* The empty state is the gallery's box with nothing in it, not a short strip
   of text — the card keeps one height whether the vehicle has photos or not,
   and the "no photo" mark sits in the middle of it. Both selectors carry the
   height variable so the two states can never drift apart. */
.af-photo-swiper,
.af-photo-empty {
  --af-photo-height: 320px;
  height: var(--af-photo-height);
}

.af-photo-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  background-color: var(--bs-tertiary-bg);
}

.af-photo-slide {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  height: var(--af-photo-height);
  background-color: var(--bs-tertiary-bg);
}

.af-photo-slide img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.af-photo-skeleton {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background-image: linear-gradient(100deg,
      var(--bs-tertiary-bg) 40%,
      var(--bs-secondary-bg) 50%,
      var(--bs-tertiary-bg) 60%);
  background-size: 300% 100%;
  animation: af-photo-shimmer 1.4s ease-in-out infinite;
}

@keyframes af-photo-shimmer {
  from { background-position: 150% 0; }
  to   { background-position: -50% 0; }
}

@media (prefers-reduced-motion: reduce) {
  .af-photo-skeleton { animation: none; }
}

@media (max-width: 575.98px) {
  .af-photo-swiper,
  .af-photo-empty { --af-photo-height: 240px; }
}

/* ── 11j. Dashboard — "Top par jours loués" ──────────────────────────────── */
/* The list sits beside a 380px bar chart. Both cards stretch to the row, and
   the list scrolls inside its own body rather than making the row taller as
   more vehicles qualify. `min-height: 0` is what lets a flex child shrink
   below its content: without it `.card-body`'s `flex: 1 1 auto` grows to fit
   every row and `overflow-y` never has anything to scroll. The max-height is
   the fallback for the stacked layout, where nothing constrains the card. */
/* `overflow-y: auto` is not decoration here and must not be traded for
   `hidden`: SimpleBar reads the host's own computed overflow and ANDs it into
   whether the axis may scroll at all —
   `axis.y.isOverflowing = "hidden" !== overflowY && …`. Set the host to hidden
   and SimpleBar concludes the list does not overflow, hides its rail and puts
   `overflow: hidden` on .simplebar-content-wrapper, which kills the wheel. The
   host never grows a native bar of its own: .simplebar-wrapper takes the host's
   used height, so scrollHeight and clientHeight match. */
.af-scroll-list {
  min-height: 0;
  max-height: 26rem;
  overflow-y: auto;
}

/* Rows line up with the card header above them, which is inset by the card's
   own 20px — not Bootstrap's 16px `px-3`. */
.af-scroll-list .list-group-item {
  padding-inline: var(--bs-card-cap-padding-x, 20px);
}

/* A list row that opens a record — the counterpart of styles.css's .af-row-click
   for tables. Bootstrap's own .list-group-item-action would give the hover fill
   for free, but it also repaints the row's resting text to
   --bs-secondary-color, which would mute the very names the list exists to
   show. So the fill is taken from it and nothing else. */
.af-list-click {
  cursor: pointer;
  transition: background-color 0.15s ease;
}

.af-list-click:hover,
.af-list-click:focus-visible {
  background-color: var(--bs-list-group-action-hover-bg, var(--bs-tertiary-bg));
}

/* ── 11k. Page-head actions — an icon tile over two lines ────────────────── */
/* The action in a page head is the one control that has to be found without
   being read, so it is drawn the way the landing page draws its floating
   cards: a tinted squircle holding the icon, what the button does on the first
   line, and what that means on the second.

   The tint carries the meaning — green takes money in, red pays it out — which
   is why these are white cards rather than one filled primary button. Two
   filled buttons side by side compete for the same emphasis; two tinted icons
   read as two different things at a glance.

   Built straight on <a>/<button> instead of on `.btn`: the template's button
   padding and line-height are sized for one line of text, and every `.btn-*`
   variant would have to be unpainted again to get back to a plain surface. */
.af-action {
  display: inline-flex;
  align-items: center;
  gap: 11px;
  /* Logical padding, not `8px 16px 8px 9px`: the tile keeps the outer edge and
     the text keeps the breathing room in Arabic too. */
  padding-block: 8px;
  padding-inline: 9px 16px;
  background-color: var(--bs-body-bg);
  border: 1px solid var(--bs-border-color);
  border-radius: 14px;
  color: var(--bs-heading-color);
  text-align: start;
  text-decoration: none;
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

.af-action:hover,
.af-action:focus-visible {
  color: var(--bs-heading-color);
  border-color: var(--af-violet-300);
  background-color: var(--bs-secondary-bg);
}


.af-action:disabled,
.af-action.disabled {
  opacity: 0.55;
  pointer-events: none;
}

/* The tint itself comes from the Bootstrap `bg-*-subtle` / `text-*` pair at the
   call site, so a new action picks its colour the way the KPI tiles do. */
.af-action-ic {
  width: 38px;
  height: 38px;
  border-radius: 11px;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  font-size: 1rem;
}

/* The icon fonts ship their own line box, which sits the glyph a pixel or two
   below the middle of the tile unless it is collapsed here. */
.af-action-ic i {
  display: block;
  line-height: 1;
}

.af-action-t {
  display: block;
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1.25;
}

.af-action-s {
  display: block;
  font-size: 0.6875rem;
  line-height: 1.35;
  color: var(--bs-secondary-color);
}

/* On a phone the head is a stack, and a card that only spans its own text
   reads as a fragment of a row rather than a button. */
@media (max-width: 575.98px) {
  .af-action {
    width: 100%;
  }
}

/* House rule: no button casts a shadow. The template drops one under
   `.btn-shadow` and another under a pressed `.btn`; both are neutralised here
   once rather than edited out of ~30 call sites, so markup that copies the
   template's habit lands flat too. The focus ring is not a shadow in this
   sense — it is how a keyboard user knows where they are — and stays. */
.btn-shadow,
[data-bs-theme=dark] .btn-shadow {
  box-shadow: none;
}

.btn {
  --bs-btn-box-shadow: none;
  --bs-btn-active-shadow: none;
}

/* ── 11l. Tabs that name their column ────────────────────────────────────── */
/* The ledger tabs carry the same tints as the actions above them — money in is
   green wherever it appears on the page — so the tab, the button that creates
   the row and the amount in the table all agree on a colour. */
.af-tab {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
}

.af-tab-ic {
  width: 26px;
  height: 26px;
  border-radius: 8px;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  font-size: 0.8125rem;
}

.af-tab-ic i {
  display: block;
  line-height: 1;
}

/* The selected pill is filled with the accent, so its tile and count have to
   invert — a `bg-*-subtle` chip on top of it reads as a hole in the pill. */
.nav-pills .nav-link.active .af-tab-ic,
.nav-pills .nav-link.active .af-tab .badge {
  background-color: rgba(255, 255, 255, 0.22) !important;
  color: #ffffff !important;
}

/* ── 11m. Toasts ─────────────────────────────────────────────────────────── */
/* Saves and failures are announced top-right rather than inline, so nothing in
   the form moves when one arrives. The stack clears the fixed header — parked
   over it, a toast would sit on the agency switcher and the user menu. */
.af-toast-stack {
  position: fixed;
  top: calc(var(--app-header-height) + 0.75rem);
  right: 1rem;
  z-index: 1090;                        /* over the modal (1055) and the offcanvas (1045) */
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  width: min(380px, calc(100vw - 2rem));
  /* The gaps between cards are not a click shield — the page behind stays live. */
  pointer-events: none;
}

.af-toast {
  --af-toast-tone: var(--bs-secondary);
  --af-toast-tone-bg: var(--bs-secondary-bg);
  pointer-events: auto;
  display: flex;
  align-items: flex-start;
  gap: 0.75rem;
  padding: 0.75rem 0.875rem;
  border: 1px solid var(--bs-border-color);
  border-left: 3px solid var(--af-toast-tone);
  border-radius: 10px;
  background-color: var(--bs-body-bg);
  box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
  cursor: pointer;
  opacity: 0;
  transform: translateX(12px);
  transition: opacity 0.18s ease, transform 0.18s ease;
}

.af-toast-in {
  opacity: 1;
  transform: none;
}

.af-toast-success { --af-toast-tone: var(--bs-success); --af-toast-tone-bg: var(--bs-success-bg-subtle); }
.af-toast-danger  { --af-toast-tone: var(--bs-danger);  --af-toast-tone-bg: var(--bs-danger-bg-subtle);  }
.af-toast-warning { --af-toast-tone: var(--bs-warning); --af-toast-tone-bg: var(--bs-warning-bg-subtle); }
.af-toast-info    { --af-toast-tone: var(--bs-info);    --af-toast-tone-bg: var(--bs-info-bg-subtle);    }

/* A square tile, like every other icon in the app — see §11a. */
.af-toast-icon {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: 8px;
  background-color: var(--af-toast-tone-bg);
  color: var(--af-toast-tone);
  font-size: 14px;
}

.af-toast-icon i {
  display: block;
  line-height: 1;
}

.af-toast-body {
  flex: 1 1 auto;
  min-width: 0;
}

.af-toast-title {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--bs-heading-color);
}

/* An API message can be one long unbroken token (a file name, a URL); let it
   break anywhere rather than push the card off the right edge. */
.af-toast-text {
  font-size: 0.8125rem;
  color: var(--bs-secondary-color);
  overflow-wrap: anywhere;
}

/* With no title above it the message is the card's only line, so it carries the
   weight a title would have had. */
.af-toast-body > .af-toast-text:only-child {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--bs-body-color);
}

.af-toast-close {
  flex: 0 0 auto;
  width: 20px;
  height: 20px;
  padding: 0;
  border: 0;
  background: none;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  line-height: 1;
  color: var(--bs-secondary-color);
  opacity: 0.6;
}

.af-toast-close:hover { opacity: 1; }

@media (max-width: 991.98px) {
  .af-toast-stack {
    top: calc(var(--app-header-height) + env(safe-area-inset-top) + 0.5rem);
    right: 0.75rem;
    width: min(380px, calc(100vw - 1.5rem));
  }
}

@media (prefers-reduced-motion: reduce) {
  .af-toast {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* ── 11n. Booking panel — the contract layout ────────────────────────────── */
/* The booking form drawn as the paper contract, box for box: same bands, same
   bilingual labels, same order as contract.js writes to the printer. Chosen per
   agency (Agences → Formulaire de réservation).

   It is a form, not a print, so it is built from the app's own tokens rather
   than the print sheet's literal greys — the panel has a dark theme and the
   printer does not. What the paper draws with rules and grey fills, this draws
   with --bs-border-color and --bs-tertiary-bg.

   Inputs are borderless and fill their cell: the box already has the border,
   and a second one inside it turns the sheet into a wall of rectangles. Focus
   is what marks the cell being filled. */
.bkc-sheet {
  --bkc-line: var(--bs-border-color);
  --bkc-label: var(--bs-tertiary-bg);
  max-width: 940px;
  margin-inline: auto;
  font-size: 0.78125rem;
  line-height: 1.2;
}

.bkc-sheet table {
  width: 100%;
  border-collapse: collapse;
  table-layout: fixed;
}

/* Every cell of every grid: one hairline, one line of text. */
.bkc-sheet td {
  border: 1px solid var(--bkc-line);
  padding: 0 0.25rem;
  height: 1.65rem;
  vertical-align: middle;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* A label cell — grey on the paper, tinted here. Set solid: several carry their
   Arabic caption onto a second line inside the same row. */
.bkc-sheet .bkc-l {
  background: var(--bkc-label);
  color: var(--bs-secondary-color);
  line-height: 1.05;
}

.bkc-sheet .bkc-a {
  font-size: 0.6875rem;
  opacity: 0.85;
}

.bkc-sheet .bkc-ar {
  direction: rtl;
  font-size: 0.75rem;
}

.bkc-sheet .bkc-l.bkc-ar { text-align: right; }
.bkc-sheet .bkc-sub { font-size: 0.71rem; }
.bkc-sheet .bkc-c   { text-align: center; }

/* Column widths, from the printed sheet. Two departures from it, both because
   this one is typed into rather than written on: the vehicle grid's value
   columns are widened to hold a date picker, and the person grid's first column
   is trimmed by the point that buys them. */
.bkc-sheet col.bkc-p1 { width: 33%; }
.bkc-sheet col.bkc-p2 { width: 23%; }
.bkc-sheet col.bkc-p3 { width: 24%; }
.bkc-sheet col.bkc-p4 { width: 20%; }
.bkc-sheet col.bkc-g1 { width: 26%; }
.bkc-sheet col.bkc-g2 { width: 24%; }
.bkc-sheet col.bkc-q1 { width: 42%; }
.bkc-sheet col.bkc-q2 { width: 30%; }
.bkc-sheet col.bkc-q3 { width: 28%; }

/* A value cell the booking owns — white on the paper, the body surface here. */
.bkc-sheet .bkc-v {
  background: var(--bs-body-bg);
  color: var(--bs-heading-color);
  white-space: nowrap;
}

/* The bands the paper prints darker: Départ / Arrivée, Kilométrage, Carburant. */
.bkc-sheet tr.bkc-band td {
  background: var(--bkc-label);
  color: var(--bs-heading-color);
  text-align: center;
  font-weight: 600;
  font-size: 0.6875rem;
  line-height: 1.05;
}

.bkc-sheet tr.bkc-band .bkc-a { font-size: 0.625rem; }

/* ── The fields themselves ── */
/* A borderless box with nothing in it is indistinguishable from a box that
   cannot be filled at all — which is most of a fresh contract. The dashed rule
   under every field is what says "write here", the way the paper's own ruled
   boxes do; it steps aside for the focus outline. */
.bkc-sheet .bkc-in {
  width: 100%;
  min-width: 0;
  border: 0;
  border-bottom: 1px dashed var(--bs-border-color);
  border-radius: 3px 3px 0 0;
  background: transparent;
  padding: 0.1rem 0.15rem;
  font-size: 0.78125rem;
  font-family: inherit;
  color: var(--bs-heading-color);
}

.bkc-sheet .bkc-in:focus {
  outline: 2px solid var(--bs-primary);
  outline-offset: -1px;
  border-bottom-color: transparent;
  background: var(--bs-primary-bg-subtle);
}

.bkc-sheet .bkc-in::placeholder {
  color: var(--bs-secondary-color);
  opacity: 0.55;
}

/* A number already taken by another booking. The cell is too narrow for a
   sentence, so it is only marked here — the message sits under the head. */
.bkc-sheet .bkc-in--bad {
  background: var(--bs-danger-bg-subtle);
  color: var(--bs-danger-text-emphasis);
}

.bkc-sheet .bkc-in--num   { text-align: center; font-weight: 600; }
.bkc-sheet .bkc-in--money { text-align: right; font-variant-numeric: tabular-nums; }
.bkc-sheet .bkc-in--rate  { width: 6.5rem; text-align: right; font-variant-numeric: tabular-nums; }

/* The VAT rate is typed inside its own label — "TVA [19] % :" — so it is sized
   to the figure and not to the cell. */
.bkc-sheet .bkc-in--pct {
  width: 3.1rem;
  text-align: center;
}

/* The one control that keeps the browser's own arrow: without it a select in a
   contract cell reads as a value someone typed, not as a list to choose from. */
.bkc-sheet .bkc-in--select {
  cursor: pointer;
  padding-left: 0;
}

/* Chrome draws its own picker button inside date and time inputs; at this size
   it crowds the value out of the cell. */
.bkc-sheet .bkc-in[type="date"],
.bkc-sheet .bkc-in[type="time"] {
  min-height: 1.45rem;
}

.bkc-sheet .bkc-in[type="number"]::-webkit-outer-spin-button,
.bkc-sheet .bkc-in[type="number"]::-webkit-inner-spin-button {
  opacity: 0;
}

.bkc-sheet .bkc-in[type="number"]:hover::-webkit-inner-spin-button,
.bkc-sheet .bkc-in[type="number"]:focus::-webkit-inner-spin-button {
  opacity: 1;
}

/* ── Head: the two document numbers over the date ── */
.bkc-head {
  border: 1px solid var(--bkc-line);
  margin-bottom: 0.15rem;
}

.bkc-head td {
  border: 0;
  height: 1.9rem;
  padding: 0.1rem 0.5rem;
}

.bkc-head tr + tr td { border-top: 1px solid var(--bkc-line); }

.bkc-head .bkc-k {
  width: 45%;
  text-align: right;
  font-weight: 700;
  font-size: 0.875rem;
  white-space: nowrap;
  color: var(--bs-heading-color);
}

.bkc-head .bkc-num {
  width: 17%;
  text-align: center;
  font-variant-numeric: tabular-nums;
}

.bkc-head .bkc-headar {
  width: 21%;
  direction: rtl;
  text-align: right;
  font-weight: 700;
  font-size: 0.8125rem;
  color: var(--bs-heading-color);
}

.bkc-warn {
  display: flex;
  align-items: baseline;
  gap: 0.4rem;
  margin-bottom: 0.15rem;
  padding: 0.3rem 0.5rem;
  border-radius: 6px;
  background: var(--bs-danger-bg-subtle);
  color: var(--bs-danger-text-emphasis);
  font-size: 0.75rem;
}

/* ── The two-column bands ── */
.bkc-cols {
  display: flex;
  margin-bottom: 0.15rem;
}

.bkc-col {
  display: flex;
  flex-direction: column;
  flex: 1 1 50%;
  min-width: 0;
  border: 1px solid var(--bkc-line);
}

.bkc-col + .bkc-col { border-left: 0; }

.bkc-title {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.2rem 0.4rem;
  border-bottom: 1px solid var(--bkc-line);
  font-weight: 700;
  font-size: 0.9375rem;
  color: var(--bs-heading-color);
}

.bkc-title > span:first-child { flex: 1 1 auto; text-align: center; }
.bkc-title > .bkc-ar          { flex: 1 1 auto; text-align: center; }

/* The way out to the record the read-only cells come from. Small, and only
   there once something is picked. */
.bkc-title-edit {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 22px;
  border-radius: 5px;
  color: var(--bs-secondary-color);
  font-size: 0.75rem;
  text-decoration: none;
}

.bkc-title-edit:hover {
  background: var(--bs-primary-bg-subtle);
  color: var(--bs-primary);
}

/* The grid fills whatever height its neighbour sets, as on the sheet. */
.bkc-col .bkc-grid { height: 100%; }

/* ── The two pickers, sitting in the cells they name ── */
/* Tagify inside a contract cell: no border of its own, no rounded corners — the
   cell is the box. It is the one control allowed to be taller than a row, since
   it draws the vehicle's logo or the client's tile. */
.bkc-picker .tagify {
  border: 0;
  border-radius: 0;
  background: transparent;
  padding: 0;
  min-height: 1.6rem;
  --tag-bg: transparent;
}

.bkc-picker .tagify__input {
  margin: 0;
  padding: 0.1rem 0.15rem;
  font-size: 0.78125rem;
}

.bkc-picker .tagify__tag { margin: 0.1rem 0.15rem; }

.bkc-v--picker { padding: 0; white-space: normal; }

/* A name the client list does not answer to. Tagify drops unmatched text from
   its own box on blur, so this is what stands in for it — and it says plainly
   that saving the contract is what will create the record. */
.bkc-newclient {
  display: flex;
  align-items: center;
  gap: 0.3rem;
  margin: 0 0.15rem 0.15rem;
  padding: 0.15rem 0.35rem;
  border-radius: 5px;
  background: var(--bs-success-bg-subtle);
  color: var(--bs-success-text-emphasis);
  font-size: 0.6875rem;
  line-height: 1.3;
}

.bkc-newclient strong {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* "Créer la fiche" — enter the client now rather than on save. Pushed to the
   right of the chip so the name keeps the room it needs. */
.bkc-newclient-go {
  margin-left: auto;
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  border: 1px solid currentColor;
  border-radius: 4px;
  padding: 0.05rem 0.3rem;
  background: none;
  color: inherit;
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.3;
  white-space: nowrap;
}

.bkc-newclient-go:hover { background: var(--bs-success-border-subtle); }

.bkc-newclient-x {
  flex: 0 0 auto;
  border: 0;
  padding: 0 0.15rem;
  background: none;
  color: inherit;
  line-height: 1;
  opacity: 0.7;
}

.bkc-newclient-x:hover { opacity: 1; }

/* Adresse is one box on the paper and two columns in the record: the street
   takes the room, the town takes what it needs. The flex lives on a wrapper —
   a <td> made a flex container is no longer a table cell and drops its colspan. */
.bkc-split {
  display: flex;
  align-items: center;
  gap: 0.35rem;
}

.bkc-split .bkc-in { flex: 1 1 auto; }
.bkc-sheet .bkc-in--city { flex: 0 0 30%; }

/* "Délivré le" with no document number beside it has nowhere to be stored, so
   it waits for one rather than pretending to accept a date. */
.bkc-sheet .bkc-in:disabled {
  border-bottom-style: dotted;
  opacity: 0.45;
  cursor: not-allowed;
}

/* ── A field the paper has no box for ── */
.bkc-extra {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.3rem 0.4rem;
  border-top: 1px solid var(--bkc-line);
  background: var(--bs-tertiary-bg);
  font-size: 0.71rem;
  color: var(--bs-secondary-color);
}

.bkc-extra-l { font-weight: 600; white-space: nowrap; }

.bkc-extra .bkc-in--rate {
  flex: 0 0 auto;
  border: 1px solid var(--bkc-line);
  border-radius: 5px;
  background: var(--bs-body-bg);
  font-size: 0.78125rem;
}

.bkc-extra-undo {
  flex: 0 0 auto;
  border: 1px solid var(--bkc-line);
  border-radius: 5px;
  background: var(--bs-body-bg);
  color: var(--bs-secondary-color);
  padding: 0.1rem 0.35rem;
  line-height: 1;
}

.bkc-extra-hint {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── Lower band: the vehicle's state | the invoice ── */
.bkc-lower {
  display: flex;
  align-items: stretch;
  margin-bottom: 0.15rem;
}

.bkc-diags {
  display: flex;
  flex-direction: column;
  flex: 1 1 54%;
  min-width: 0;
  border: 1px solid var(--bkc-line);
  border-right: 0;
}

.bkc-diag {
  display: flex;
  flex-direction: column;
  flex: 1 1 50%;
  min-height: 0;
}

.bkc-diag + .bkc-diag { border-top: 1px solid var(--bkc-line); }

.bkc-diag-lbl {
  display: flex;
  align-items: center;
  padding: 0.2rem 0.4rem;
  border-bottom: 1px solid var(--bkc-line);
  font-weight: 700;
  font-size: 0.9375rem;
  color: var(--bs-heading-color);
}

.bkc-diag-lbl > span { flex: 1 1 50%; text-align: center; }

.bkc-diag-body {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex: 1 1 auto;
  padding: 0.35rem 0.4rem;
  min-height: 0;
}

/* The line drawing the paper is marked up on. It is here for recognition — the
   crosses are drawn on the printout — so it takes only the room it needs and
   leaves the rest to the photographs. */
.bkc-diag-img {
  flex: 0 0 auto;
  width: 44%;
  max-height: 84px;
  object-fit: contain;
  opacity: 0.75;
}

.bkc-shots {
  display: flex;
  flex-wrap: wrap;
  align-content: flex-start;
  gap: 0.3rem;
  flex: 1 1 auto;
  min-width: 0;
}

.bkc-shot {
  position: relative;
  width: 46px;
  height: 46px;
  border: 1px solid var(--bkc-line);
  border-radius: 5px;
  overflow: hidden;
}

.bkc-shot img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  cursor: zoom-in;
}

.bkc-shot--pending { border-style: dashed; }

.bkc-shot-x {
  position: absolute;
  top: 0;
  right: 0;
  width: 15px;
  height: 15px;
  padding: 0;
  border: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 7px;
  line-height: 1;
  color: #fff;
  background: var(--bs-danger);
}

/* The tile that adds photographs — dashed, like the empty slot it is. */
.bkc-shot--add {
  display: flex;
  align-items: center;
  justify-content: center;
  border-style: dashed;
  color: var(--bs-secondary-color);
  cursor: pointer;
}

.bkc-shot--add:hover {
  border-color: var(--bs-primary);
  color: var(--bs-primary);
}

.bkc-shot--add.disabled { pointer-events: none; opacity: 0.5; }

/* ── The invoice column ── */
.bkc-pricing {
  display: flex;
  flex-direction: column;
  flex: 1 1 46%;
  min-width: 0;
}

.bkc-price td {
  height: 1.55rem;
  padding: 0 0.4rem;
}

.bkc-price .bkc-pl { color: var(--bs-secondary-color); }

.bkc-price .bkc-pv {
  text-align: right;
  font-variant-numeric: tabular-nums;
  color: var(--bs-heading-color);
}

/* A figure the sheet works out rather than one that is typed. */
.bkc-price .bkc-pv--calc {
  background: var(--bs-tertiary-bg);
  font-weight: 600;
}

.bkc-price .bkc-par {
  direction: rtl;
  text-align: right;
  background: var(--bkc-label);
  color: var(--bs-secondary-color);
  font-size: 0.71rem;
}

.bkc-total { margin-top: 0.15rem; }

.bkc-total td {
  font-weight: 700;
  font-size: 0.875rem;
  background: var(--bs-primary-bg-subtle);
  color: var(--bs-primary-text-emphasis);
}

.bkc-total .bkc-pv { color: var(--bs-primary); }

.bkc-words { margin-top: 0.15rem; }

.bkc-words .bkc-pl { font-weight: 600; color: var(--bs-heading-color); }

.bkc-words .bkc-wv {
  text-align: center;
  font-weight: 600;
  color: var(--bs-heading-color);
  white-space: normal;
  height: auto;
  padding: 0.2rem 0.4rem;
}

/* ── The small print, and the two signature boxes ── */
.bkc-foot { border: 1px solid var(--bkc-line); }

.bkc-terms {
  padding: 0.25rem 0.5rem;
  border-bottom: 1px solid var(--bkc-line);
  font-size: 0.625rem;
  line-height: 1.25;
  text-align: justify;
  color: var(--bs-secondary-color);
}

.bkc-signs { display: flex; }

.bkc-sign {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  flex: 1 1 50%;
  min-width: 0;
  padding: 0.35rem 0.5rem 1.4rem;
  font-size: 0.71rem;
  color: var(--bs-secondary-color);
}

.bkc-sign + .bkc-sign { border-left: 1px solid var(--bkc-line); }

/* ── Suivi interne — everything the paper does not carry ── */
.bkc-internal {
  margin-top: 1rem;
  padding-top: 0.85rem;
  border-top: 1px dashed var(--bs-border-color);
  font-size: 0.875rem;
}

.bkc-internal-head {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
  color: var(--bs-secondary-color);
  font-size: 0.8125rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

.bkc-internal-head small {
  text-transform: none;
  letter-spacing: 0;
  font-weight: 400;
  font-style: italic;
}

/* Below md the two columns of the sheet no longer hold their labels — the paper
   is an A4 landscape of information and a phone is not. They stack, each block
   keeping its own grid, so nothing is lost, only re-flowed. */
@media (max-width: 767.98px) {
  .bkc-cols,
  .bkc-lower {
    flex-direction: column;
  }

  .bkc-col + .bkc-col,
  .bkc-diags {
    border-left: 1px solid var(--bkc-line);
  }

  .bkc-col + .bkc-col { border-top: 0; }

  .bkc-diags { border-right: 1px solid var(--bkc-line); }

  .bkc-diag-img { display: none; }

  .bkc-head .bkc-k { font-size: 0.75rem; }
}

/* ── 11o. Booking panel — the guided wizard ──────────────────────────────── */
/* The third drawing of the booking panel (Agences → Formulaire de réservation
   → « Assistant guidé »). Where the contract layout above puts sixty boxes on
   screen at once, this shows one question at a time.

   Everything here is sized up rather than down: 44px minimum hit targets, one
   column, generous leading. The panel it lives in is narrow on purpose
   (720px), so the eye never has to choose which half of the sheet to read.

   Bilingual throughout, like the contract it produces — the Arabic caption
   rides beside the French one in .bkw-ar, one notch smaller and dimmed, never
   competing with it for the first read. */
.bkw {
  --bkw-gap: 1rem;
  max-width: 620px;
  margin-inline: auto;
}

.bkw-ar {
  direction: rtl;
  font-size: 0.8125em;
  color: var(--bs-secondary-color);
  font-weight: 400;
}

/* ── The numbered bar ── */
.bkw-steps {
  display: flex;
  list-style: none;
  margin: 0 0 0.5rem;
  padding: 0;
  gap: 0.25rem;
}

.bkw-step { flex: 1 1 0; min-width: 0; }

.bkw-step-btn {
  width: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  border: 0;
  background: transparent;
  padding: 0.25rem 0.125rem;
  color: var(--bs-secondary-color);
  cursor: pointer;
}

.bkw-step-btn:disabled { cursor: default; opacity: 0.55; }

/* The step number: a square tile, like every other mark in the app. */
.bkw-step-n {
  width: 2rem;
  height: 2rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 0.5rem;
  border: 1.5px solid var(--bs-border-color);
  background: var(--bs-body-bg);
  font-size: 0.8125rem;
  font-weight: 600;
  line-height: 1;
  transition: background-color 0.15s, border-color 0.15s, color 0.15s;
}

.bkw-step-t {
  display: flex;
  flex-direction: column;
  align-items: center;
  line-height: 1.1;
  font-size: 0.75rem;
  font-weight: 500;
  text-align: center;
}

.bkw-step-t .bkw-ar { font-size: 0.6875rem; }

.bkw-step.is-current .bkw-step-btn { color: var(--bs-heading-color); }

.bkw-step.is-current .bkw-step-n {
  border-color: var(--af-violet-600);
  background: var(--af-violet-600);
  color: #fff;
}

.bkw-step.is-done .bkw-step-n {
  border-color: var(--af-violet-300);
  background: var(--af-violet-100);
  color: var(--af-violet-700);
}

/* The thin rule under the numbers — the same progress said a second way, for
   the reader who takes in a bar faster than a row of tiles. */
.bkw-bar {
  height: 3px;
  border-radius: 999px;
  background: var(--bs-tertiary-bg);
  overflow: hidden;
  margin-bottom: 1.25rem;
}

.bkw-bar > span {
  display: block;
  height: 100%;
  background: var(--af-violet-600);
  transition: width 0.25s ease;
}

/* ── A step ── */
.bkw-pane { display: flex; flex-direction: column; gap: var(--bkw-gap); }

.bkw-head { margin-bottom: -0.25rem; }

.bkw-head h5 {
  margin: 0;
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--bs-heading-color);
}

.bkw-head .bkw-ar { display: block; font-size: 0.9375rem; margin-top: 0.125rem; }

.bkw-field { display: flex; flex-direction: column; }

/* A label that carries its Arabic on the other edge of the line. */
.bkw-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
}

/* ── The search box the tiles and the client name share ── */
.bkw-search {
  position: relative;
  display: flex;
  align-items: center;
}

.bkw-search > i:first-child {
  position: absolute;
  left: 0.875rem;
  color: var(--bs-secondary-color);
  pointer-events: none;
  font-size: 0.9375rem;
  line-height: 1;
}

.bkw-search > input {
  width: 100%;
  min-width: 0;
  padding: 0.625rem 2.5rem;
  border: 1px solid var(--bs-border-color);
  border-radius: 0.5rem;
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  font-size: 0.9375rem;
}

.bkw-search--lg > input {
  padding-block: 0.875rem;
  font-size: 1.125rem;
  font-weight: 500;
}

.bkw-search > input:focus {
  outline: 0;
  border-color: var(--af-violet-400);
  box-shadow: 0 0 0 3px rgba(var(--af-violet-600-rgb), 0.12);
}

.bkw-search-x {
  position: absolute;
  right: 0.5rem;
  width: 1.75rem;
  height: 1.75rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 0.375rem;
  background: transparent;
  color: var(--bs-secondary-color);
  cursor: pointer;
}

.bkw-search-x:hover { background: var(--bs-tertiary-bg); }

/* ── Step 1: the vehicle tiles ── */
.bkw-tiles {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  max-height: 22rem;
  overflow-y: auto;
}

.bkw-tile {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  min-height: 3.5rem;
  padding: 0.625rem 2.75rem 0.625rem 0.75rem;
  border: 1.5px solid var(--bs-border-color);
  border-radius: 0.625rem;
  background: var(--bs-body-bg);
  text-align: left;
  cursor: pointer;
  transition: border-color 0.12s, background-color 0.12s;
}

.bkw-tile:hover { border-color: var(--af-violet-300); }

.bkw-tile.is-picked {
  border-color: var(--af-violet-600);
  background: var(--af-violet-50);
}

.bkw-tile-mark {
  flex: 0 0 auto;
  width: 2.25rem;
  height: 2.25rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 0.5rem;
  background: var(--bs-tertiary-bg);
  color: var(--bs-secondary-color);
  font-size: 1.0625rem;
}

.bkw-tile.is-picked .bkw-tile-mark {
  background: var(--af-violet-100);
  color: var(--af-violet-700);
}

.bkw-tile-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }

.bkw-tile-name {
  font-weight: 600;
  font-size: 0.9375rem;
  color: var(--bs-heading-color);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bkw-tile-sub { font-size: 0.8125rem; color: var(--bs-secondary-color); }

.bkw-tile-rate {
  flex: 0 0 auto;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--af-violet-700);
  white-space: nowrap;
}

.bkw-tile-badge {
  flex: 0 0 auto;
  padding: 0.125rem 0.4375rem;
  border-radius: 0.3125rem;
  font-size: 0.6875rem;
  font-weight: 600;
  white-space: nowrap;
}

.bkw-tile-badge--warning { background: var(--bs-warning-bg-subtle); color: var(--bs-warning-text-emphasis); }
.bkw-tile-badge--danger  { background: var(--bs-danger-bg-subtle);  color: var(--bs-danger-text-emphasis); }

/* The tick lives in the padding the tile reserves for it, so it can appear
   without shifting a single character of what is written beside it. */
.bkw-tile-check {
  position: absolute;
  right: 0.75rem;
  top: 50%;
  transform: translateY(-50%);
  width: 1.375rem;
  height: 1.375rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 0.3125rem;
  background: var(--af-violet-600);
  color: #fff;
  font-size: 0.6875rem;
  opacity: 0;
  transition: opacity 0.12s;
}

.bkw-tile.is-picked .bkw-tile-check { opacity: 1; }

.bkw-empty {
  margin: 0;
  padding: 1.5rem 0.5rem;
  text-align: center;
  color: var(--bs-secondary-color);
  font-size: 0.875rem;
}

/* ── A folded-away block that has been opened ── */
.bkw-sub {
  border: 1px solid var(--bs-border-color);
  border-radius: 0.625rem;
  padding: 0.875rem;
  background: var(--bs-tertiary-bg);
}

.bkw-sub-head {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  margin-bottom: 0.75rem;
  font-size: 0.8125rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--bs-secondary-color);
}

.bkw-sub-head .bkw-ar { text-transform: none; letter-spacing: 0; }

.bkw-sub-x {
  margin-left: auto;
  width: 1.5rem;
  height: 1.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 0.3125rem;
  background: transparent;
  color: var(--bs-secondary-color);
  cursor: pointer;
}

.bkw-sub-x:hover { background: var(--bs-body-bg); color: var(--bs-danger); }

/* ── Chips: durations, fuel levels ── */
.bkw-chips { display: flex; flex-wrap: wrap; gap: 0.375rem; }

.bkw-chip {
  min-height: 2.25rem;
  padding: 0.375rem 0.875rem;
  border: 1.5px solid var(--bs-border-color);
  border-radius: 0.5rem;
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
  transition: border-color 0.12s, background-color 0.12s, color 0.12s;
}

.bkw-chip:hover { border-color: var(--af-violet-300); }

.bkw-chip.is-on {
  border-color: var(--af-violet-600);
  background: var(--af-violet-600);
  color: #fff;
}

/* ── Step 2: départ | retour ── */
.bkw-when { display: flex; gap: 0.75rem; }

.bkw-when-side {
  flex: 1 1 50%;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.875rem;
  border: 1px solid var(--bs-border-color);
  border-radius: 0.625rem;
}

.bkw-when-lbl {
  display: flex;
  align-items: baseline;
  gap: 0.375rem;
  font-weight: 600;
  font-size: 0.875rem;
  color: var(--bs-heading-color);
}

.bkw-when-lbl > i { color: var(--af-violet-600); }

/* The day count, said once, large — the number the whole step is about. */
.bkw-figure {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding: 0.875rem 1rem;
  border-radius: 0.625rem;
  background: var(--af-violet-50);
  border: 1px solid var(--af-violet-200);
}

.bkw-figure-main { display: flex; align-items: baseline; gap: 0.375rem; }

.bkw-figure-n {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1;
  color: var(--af-violet-700);
}

.bkw-figure-u { font-size: 0.875rem; color: var(--bs-secondary-color); }
.bkw-figure-side { font-size: 0.8125rem; color: var(--bs-secondary-color); }

/* ── Step 3: the clients that answer to what is being typed ── */
.bkw-matches {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin-top: 0.5rem;
}

.bkw-match {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  width: 100%;
  min-height: 3rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--bs-border-color);
  border-radius: 0.5rem;
  background: var(--bs-body-bg);
  text-align: left;
  cursor: pointer;
}

.bkw-match:hover { border-color: var(--af-violet-400); background: var(--af-violet-50); }

.bkw-match-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }

.bkw-match-name {
  font-weight: 600;
  font-size: 0.9375rem;
  color: var(--bs-heading-color);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bkw-match-sub { font-size: 0.8125rem; color: var(--bs-secondary-color); }

.bkw-match > i:last-child { color: var(--bs-secondary-color); font-size: 0.75rem; }

/* A sentence about what is about to happen — never a warning, since neither
   case is a mistake. */
.bkw-note {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  margin-top: 0.625rem;
  padding: 0.75rem 0.875rem;
  border-radius: 0.5rem;
  font-size: 0.875rem;
}

.bkw-note > i { font-size: 1rem; line-height: 1.3; }
.bkw-note div { display: flex; flex-direction: column; }
.bkw-note strong { font-weight: 600; }
.bkw-note span { color: var(--bs-secondary-color); }

.bkw-note--new { background: var(--af-violet-50); color: var(--af-violet-700); }
.bkw-note--ok  { background: var(--bs-success-bg-subtle); color: var(--bs-success-text-emphasis); }

/* ── The disclosure that folds everything optional away ── */
.bkw-more {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  align-self: flex-start;
  border: 0;
  background: transparent;
  padding: 0.375rem 0;
  color: var(--af-violet-700);
  font-size: 0.875rem;
  font-weight: 500;
  cursor: pointer;
}

.bkw-more:hover { text-decoration: underline; }
.bkw-more-hint { color: var(--bs-secondary-color); font-weight: 400; }

.bkw-fold {
  padding: 0.875rem;
  border: 1px dashed var(--bs-border-color);
  border-radius: 0.625rem;
  margin-inline: 0;
}

/* ── Step 4: the total, and how it is paid ── */
.bkw-total {
  padding: 1rem 1.125rem;
  border-radius: 0.75rem;
  background: var(--af-violet-600);
  color: #fff;
}

.bkw-total-l {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.5rem;
  font-size: 0.875rem;
  opacity: 0.85;
}

.bkw-total-l .bkw-ar { color: inherit; }

.bkw-total-v { font-size: 2rem; font-weight: 700; line-height: 1.15; }
.bkw-total-v small { font-size: 0.9375rem; font-weight: 500; opacity: 0.8; }

.bkw-total-words {
  margin-top: 0.25rem;
  font-size: 0.8125rem;
  opacity: 0.85;
  line-height: 1.3;
}

.bkw-pays {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: 0.5rem;
}

.bkw-pay {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.125rem;
  min-height: 4.25rem;
  padding: 0.625rem 0.5rem;
  border: 1.5px solid var(--bs-border-color);
  border-radius: 0.625rem;
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  font-size: 0.8125rem;
  font-weight: 500;
  cursor: pointer;
  transition: border-color 0.12s, background-color 0.12s;
}

.bkw-pay > i { font-size: 1.125rem; color: var(--bs-secondary-color); }
.bkw-pay:hover { border-color: var(--af-violet-300); }

.bkw-pay.is-on {
  border-color: var(--af-violet-600);
  background: var(--af-violet-50);
  color: var(--af-violet-700);
}

.bkw-pay.is-on > i { color: var(--af-violet-600); }
.bkw-pay .bkw-ar { font-size: 0.6875rem; }

/* ── Step 5: the read-back ── */
.bkw-recap {
  border: 1px solid var(--bs-border-color);
  border-radius: 0.625rem;
  overflow: hidden;
}

.bkw-recap-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  width: 100%;
  min-height: 2.875rem;
  padding: 0.5rem 0.875rem;
  border: 0;
  border-bottom: 1px solid var(--bs-border-color);
  background: var(--bs-body-bg);
  text-align: left;
  cursor: pointer;
}

.bkw-recap-row:last-child { border-bottom: 0; }
.bkw-recap-row:hover { background: var(--bs-tertiary-bg); }

.bkw-recap-l {
  flex: 0 0 9rem;
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.375rem;
  font-size: 0.8125rem;
  color: var(--bs-secondary-color);
}

.bkw-recap-v {
  flex: 1 1 auto;
  min-width: 0;
  font-weight: 600;
  font-size: 0.9375rem;
  color: var(--bs-heading-color);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bkw-recap-row > i { color: var(--bs-secondary-color); font-size: 0.8125rem; }

.bkw-shots { display: flex; flex-wrap: wrap; gap: 0.5rem; }

.bkw-shot {
  position: relative;
  width: 5rem;
  height: 5rem;
  border-radius: 0.5rem;
  overflow: hidden;
  border: 1px solid var(--bs-border-color);
}

.bkw-shot img { width: 100%; height: 100%; object-fit: cover; cursor: zoom-in; }

.bkw-shot-x {
  position: absolute;
  top: 0.125rem;
  right: 0.125rem;
  width: 1.25rem;
  height: 1.25rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 0;
  border-radius: 0.25rem;
  background: rgba(0, 0, 0, 0.55);
  color: #fff;
  font-size: 0.625rem;
  cursor: pointer;
}

.bkw-shot--add {
  display: flex;
  align-items: center;
  justify-content: center;
  border-style: dashed;
  color: var(--bs-secondary-color);
  cursor: pointer;
}

.bkw-shot--add:hover { border-color: var(--af-violet-400); color: var(--af-violet-600); }
.bkw-shot--add.disabled { pointer-events: none; opacity: 0.6; }

/* ── What the step still needs ── */
.bkw-alert {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 1rem;
  padding: 0.75rem 0.875rem;
  border-radius: 0.5rem;
  background: var(--bs-warning-bg-subtle);
  color: var(--bs-warning-text-emphasis);
  font-size: 0.875rem;
}

/* ── Retour | Suivant ── */
.bkw-nav {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 1.5rem;
  padding-top: 1rem;
  border-top: 1px solid var(--bs-border-color);
}

.bkw-nav .btn-lg { min-height: 3rem; padding-inline: 1.5rem; font-size: 0.9375rem; }

/* ── The panel once the booking exists ── */
.bkw-done {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding: 2rem 0 1rem;
  gap: 0.375rem;
}

.bkw-done-mark {
  width: 4rem;
  height: 4rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 1rem;
  background: var(--bs-success-bg-subtle);
  color: var(--bs-success);
  font-size: 1.75rem;
  margin-bottom: 0.5rem;
}

.bkw-done-title { margin: 0; font-weight: 600; color: var(--bs-heading-color); }
.bkw-done-sub   { margin: 0 0 1rem; color: var(--bs-secondary-color); }

.bkw-done-card {
  width: 100%;
  border: 1px solid var(--bs-border-color);
  border-radius: 0.625rem;
  overflow: hidden;
  margin-bottom: 1.25rem;
}

.bkw-done-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.625rem 0.875rem;
  border-bottom: 1px solid var(--bs-border-color);
  font-size: 0.875rem;
  text-align: left;
}

.bkw-done-row:last-child { border-bottom: 0; }
.bkw-done-row > span { color: var(--bs-secondary-color); }
.bkw-done-row > strong { color: var(--bs-heading-color); min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.bkw-done-row--total { background: var(--bs-tertiary-bg); }
.bkw-done-row--total > strong { font-size: 1.0625rem; }

.bkw-done-print { width: 100%; min-height: 3.25rem; }
.bkw-done-more { display: flex; gap: 0.5rem; margin-top: 0.75rem; }

/* Below sm the two date cards and the step captions no longer fit side by
   side. The numbers stay — they are the map — but their words step aside. */
@media (max-width: 575.98px) {
  .bkw-when { flex-direction: column; }
  .bkw-step-t { display: none; }
  .bkw-recap-l { flex-basis: 6.5rem; }
  .bkw-recap-l .bkw-ar { display: none; }
}

/* ── 11p. "Sur quel papier ?" ────────────────────────────────────────────── */
/* The question asked before every contract print. Two cards, and the card is
   the answer — there is no confirm button after it, so each one is sized like
   something you press rather than something you tick. The agency's own setting
   wears the accent border and the badge, and is the one focus lands on. */
.cpm-choices { display: flex; flex-direction: column; gap: 0.625rem; }

.cpm-choice {
  display: flex;
  align-items: flex-start;
  gap: 0.875rem;
  width: 100%;
  padding: 0.875rem 1rem;
  border: 1.5px solid var(--bs-border-color);
  border-radius: 0.75rem;
  background: var(--bs-body-bg);
  text-align: left;
  cursor: pointer;
  transition: border-color 0.12s, background-color 0.12s, box-shadow 0.12s;
}

.cpm-choice:hover { border-color: var(--af-violet-400); background: var(--af-violet-50); }

.cpm-choice:focus-visible {
  outline: 0;
  border-color: var(--af-violet-600);
  box-shadow: 0 0 0 3px rgba(var(--af-violet-600-rgb), 0.18);
}

.cpm-choice.is-default { border-color: var(--af-violet-600); }

.cpm-choice-mark {
  flex: 0 0 auto;
  width: 2.5rem;
  height: 2.5rem;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 0.5rem;
  background: var(--bs-tertiary-bg);
  color: var(--bs-secondary-color);
  font-size: 1.125rem;
}

.cpm-choice.is-default .cpm-choice-mark,
.cpm-choice:hover .cpm-choice-mark {
  background: var(--af-violet-100);
  color: var(--af-violet-700);
}

.cpm-choice-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 0.125rem; }

.cpm-choice-t {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  font-weight: 600;
  font-size: 1rem;
  color: var(--bs-heading-color);
}

.cpm-choice-ar {
  direction: rtl;
  font-size: 0.8125rem;
  font-weight: 400;
  color: var(--bs-secondary-color);
}

.cpm-choice-d { font-size: 0.8125rem; line-height: 1.4; color: var(--bs-secondary-color); }

.cpm-choice-badge {
  flex: 0 0 auto;
  align-self: center;
  padding: 0.1875rem 0.5rem;
  border-radius: 0.3125rem;
  background: var(--af-violet-100);
  color: var(--af-violet-700);
  font-size: 0.6875rem;
  font-weight: 600;
  white-space: nowrap;
}

.cpm-choice-go { flex: 0 0 auto; align-self: center; color: var(--bs-secondary-color); font-size: 0.8125rem; }

/* Under sm the badge and the Arabic caption are the first things to go — the
   two names and their descriptions are what the choice actually turns on. */
@media (max-width: 575.98px) {
  .cpm-choice-badge, .cpm-choice-go { display: none; }
}

/* ── 11q. Booking panel — the two document numbers ───────────────────────── */
/* Sharing a line on a phone leaves each of them half a 360px panel, and the
   "N°" chip was taking a third of what was left — enough that an eight-digit
   number no longer fit in its own box. The label a line above already reads
   "N° Contrat", so on that screen the chip is only saying it twice. Hiding it
   does not make the input the group's first child, so the corners it squared
   off for the chip have to be put back. */
@media (max-width: 575.98px) {
  .bk-doc-number .input-group-text {
    display: none;
  }

  .bk-doc-number .form-control {
    border-top-left-radius: var(--bs-border-radius);
    border-bottom-left-radius: var(--bs-border-radius);
  }
}

/* ── 11q-bis. Booking panel — the head of the paper ──────────────────────── */
/* The two document numbers and what can be done about the invoice, in a strip
   across the top of the panel rather than as two columns of the form grid.

   They were columns, and it did not survive the invoice becoming a thing you
   ask for: the N° Facture column grows a "Facturée le" date and a way out of
   it the moment a rental is billed, and in a grid row every column is as tall
   as the tallest — so the vehicle picker beside it sat alone at the top of a
   column three lines deep, with a hole underneath. Here the control grows
   sideways into space the strip already has, and the row below starts where
   the eye expects it. */
.bk-doc-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.75rem 1.25rem;
  padding: 0.875rem 1rem;
  border: 1px solid var(--bs-border-color);
  border-radius: 0.5rem;
  background: var(--bs-tertiary-bg);
}

/* Wide enough for eight digits behind the "N°" chip, and no wider — the two
   numbers are a label on the strip, not the subject of the form. */
.bk-doc-head-num {
  flex: 0 1 12.5rem;
  min-width: 9rem;
}

/* Takes the rest, and centres on the boxes rather than on their labels: the
   control is one line tall while the numbers are three (label, box, hint). */
.bk-doc-head-act {
  flex: 1 1 16rem;
  min-width: 0;
  padding-top: 1.9rem;
}

/* On a phone the strip is a stack, and a control padded down to meet a box
   that is now above it would just be a gap. */
@media (max-width: 575.98px) {
  .bk-doc-head-num { flex: 1 1 100%; }
  .bk-doc-head-act { padding-top: 0; }
}

/* ── 11q-ter. Booking panel — the rental behind a document ───────────────── */
/* Shown when the panel was opened from Duplicata or Facturations. The rental
   is context for the paperwork, not something the paperwork may change, so it
   is set as facts to read: no inputs, no pickers, nothing to tab into. */
.bk-doc-rental {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 2rem;
  padding: 0.875rem 1rem;
  border: 1px solid var(--bs-border-color);
  border-left: 3px solid var(--bs-secondary);
  border-radius: 0.5rem;
  background: var(--bs-secondary-bg);
}

.bk-doc-rental-item {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  min-width: 0;
}

.bk-doc-rental-k {
  font-size: 0.6875rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--bs-secondary-color);
}

.bk-doc-rental-v {
  font-weight: 600;
  line-height: 1.3;
}

.bk-doc-rental-s {
  font-size: 0.78rem;
  color: var(--bs-secondary-color);
}

/* Takes the whole last line of the strip, so the sentence reads as a footnote
   under the facts rather than as a fifth one. */
.bk-doc-rental-note {
  flex: 1 1 100%;
  margin: 0;
  font-size: 0.78rem;
  color: var(--bs-secondary-color);
}

.bk-doc-rental-note .fi {
  margin-right: 0.35rem;
  vertical-align: -1px;
}

/* ── 11r. "Nobody answers to that" — creating a client from the booking ──── */
/* A client search that finds nothing is the moment to enter the client, not a
   dead end. The three booking layouts each say so in their own idiom — this is
   the standard panel's: a strip under the picker, holding what was typed (a
   name, or the paper a number was read as) and the way to enter it. It sticks
   after the picker is blurred, because Tagify wipes unmatched text from its own
   box and this is then the only record of what the user typed. */
.bk-suggest {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-top: 0.375rem;
  padding: 0.5rem 0.625rem;
  border: 1px solid var(--af-violet-200, var(--bs-border-color));
  border-radius: 0.5rem;
  background: var(--af-violet-50, var(--bs-tertiary-bg));
  color: var(--af-violet-700, var(--bs-body-color));
  font-size: 0.8125rem;
  line-height: 1.35;
}

.bk-suggest > i:first-child { flex: 0 0 auto; font-size: 1rem; }

.bk-suggest-body { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; }

.bk-suggest-t {
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.bk-suggest-t strong { font-weight: 700; }

.bk-suggest-s { color: var(--bs-secondary-color); font-size: 0.75rem; }

.bk-suggest-go {
  flex: 0 0 auto;
  display: inline-flex;
  align-items: center;
  gap: 0.2rem;
  border: 1px solid currentColor;
  border-radius: 0.375rem;
  padding: 0.2rem 0.5rem;
  background: var(--bs-body-bg);
  color: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
}

.bk-suggest-go:hover { background: var(--af-violet-100, var(--bs-secondary-bg)); }

.bk-suggest-x {
  flex: 0 0 auto;
  border: 0;
  padding: 0 0.15rem;
  background: none;
  color: inherit;
  line-height: 1;
  opacity: 0.6;
}

.bk-suggest-x:hover { opacity: 1; }

/* The wizard's version of the same offer, inside the note it already showed. */
.bkw-note-go {
  margin-left: auto;
  flex: 0 0 auto;
  align-self: center;
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  border: 1px solid currentColor;
  border-radius: 0.375rem;
  padding: 0.3rem 0.625rem;
  background: var(--bs-body-bg);
  color: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  white-space: nowrap;
}

.bkw-note-go:hover { background: var(--af-violet-100, var(--bs-secondary-bg)); }

/* The client panel, opened over the booking panel. Bootstrap stacks neither
   offcanvases nor their backdrops, so the second backdrop and the panel it dims
   are lifted here: the booking panel (1045) falls under the new backdrop
   (1050), and the client panel sits over both. Opened on its own — from the
   Clients page — there is only one backdrop and nothing to lift. */
.offcanvas-backdrop ~ .offcanvas-backdrop { z-index: 1050; }

.offcanvas.af-oc-stacked { z-index: 1060; }

/* What the typed text was read as, when it was a number rather than a name.
   Said out loud above the form, because a wrong guess has to be correctable
   before the contract is printed, not after. */
.cf-detected {
  display: flex;
  align-items: flex-start;
  gap: 0.625rem;
  margin-bottom: 1rem;
  padding: 0.75rem 0.875rem;
  border-radius: 0.5rem;
  background: var(--af-violet-50, var(--bs-tertiary-bg));
  color: var(--af-violet-700, var(--bs-body-color));
  font-size: 0.875rem;
}

.cf-detected > i { font-size: 1rem; line-height: 1.3; }
.cf-detected div { display: flex; flex-direction: column; }
.cf-detected strong { font-weight: 600; }
.cf-detected span { color: var(--bs-secondary-color); }

/* The box the panel filled in for you, marked so it reads as an answer already
   given rather than one still owed. */
.form-control.is-prefilled {
  border-color: var(--af-violet-300, var(--bs-border-color));
  background: var(--af-violet-50, var(--bs-tertiary-bg));
}

/* ── 11s. Global search — the Ctrl+K panel ───────────────────────────────── */
/* The panel was reading as one grey wash. Everything in it — the section
   header, the client's name, their phone — came out of the template's
   .search-item in --bs-body-color, and the second line sat at 12px on top of
   that at 72% alpha; on a dark surface that is barely there at all. The
   hierarchy is drawn here instead: the name takes the heading colour, the
   details under it take a solid secondary colour, and every row gets a surface
   of its own so the list reads as rows rather than as prose.
   The sizing used to be an inline <style> in header.hbs, which put half the
   panel out of reach of the theme — it lives here now, phone sheet included. */
.af-search-modal .global-search-dialog {
  max-width: 640px;
  margin: 6vh auto 0;
}

.af-search-modal .global-search-content {
  /* The tokens the rows read. Named on the panel rather than on :root so the
     two themes differ in one place and the rules below are written once. */
  --af-si-hover: var(--af-gray-100);
  --af-si-border: var(--bs-border-color);
  --af-si-meta: var(--af-slate-600);

  border: 1px solid var(--bs-border-color);
  box-shadow: 0 24px 64px -16px rgba(15, 23, 42, 0.28), 0 2px 8px rgba(15, 23, 42, 0.06);
}

[data-bs-theme=dark] .af-search-modal .global-search-content {
  /* A panel floating over #1e1e1e needs an edge of its own, and a hover that is
     lighter than the panel. --bs-light cannot supply one: the template repoints
     it to #232323 in dark mode, darker than the #252526 panel, so the row being
     pointed at used to sink instead of lift. */
  --af-si-hover: #323233;
  --af-si-border: #3a3a3b;
  --af-si-meta: #b4b4b4;

  border-color: #3a3a3b;
  box-shadow: 0 24px 64px -16px rgba(0, 0, 0, 0.65);
}

/* ── The field ── */
.af-search-modal .global-search-head {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.875rem 1rem;
  border-bottom: 1px solid var(--af-si-border);
}

.af-search-modal .global-search-icon {
  flex-shrink: 0;
  font-size: 1.0625rem;
  line-height: 1;
  color: var(--bs-primary);
}

.af-search-modal .global-search-input {
  padding: 0 0.25rem;
  border: 0;
  background: transparent;
  box-shadow: none;
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--bs-heading-color);
}

.af-search-modal .global-search-input:focus {
  border: 0;
  background: transparent;
  box-shadow: none;
  color: var(--bs-heading-color);
}

/* The prompt lists what may be typed, so it has to stay legible — but never as
   legible as an answer already given. */
.af-search-modal .global-search-input::placeholder {
  color: var(--af-si-meta);
  opacity: 0.85;
  font-weight: 400;
}

.af-search-modal .global-search-kbd {
  flex-shrink: 0;
  padding: 0.125rem 0.4375rem;
  border: 1px solid var(--af-si-border);
  border-radius: 0.375rem;
  background: var(--af-si-hover);
  color: var(--af-si-meta);
  font-size: 0.6875rem;
  line-height: 1.4;
}

.af-search-modal .global-search-body {
  min-height: 168px;
  max-height: 480px;
  overflow-y: auto;
  padding: 0.625rem;
}

/* ── A group of rows ── */
.af-si-group-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.375rem 0.5rem 0.5rem;
}

.af-si-group-title {
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--af-si-meta);
}

.af-si-group-clear {
  padding: 0;
  border: 0;
  background: none;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--bs-primary);
}

.af-si-group-clear:hover {
  text-decoration: underline;
}

/* ── A row ── */
.af-si {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.125rem;
  padding: 0.5625rem 0.625rem;
  border: 1px solid transparent;
  border-radius: 0.625rem;
  color: var(--bs-body-color);
  text-decoration: none;
  transition: background-color 0.12s ease, border-color 0.12s ease;
}

.af-si:hover,
.af-si:focus-visible {
  background-color: var(--af-si-hover);
  border-color: var(--af-si-border);
  color: var(--bs-body-color);
  outline: none;
}

.af-si:hover .af-si-title,
.af-si:focus-visible .af-si-title {
  color: var(--bs-primary);
}

.af-si-avatar {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 40px;
  height: 40px;
  border-radius: 0.5rem;         /* squares, never circles — see §11a */
  font-size: 0.8125rem;
  font-weight: 700;
}

.af-si-avatar-client {
  background: var(--bs-success-bg-subtle);
  color: var(--bs-success);
}

.af-si-avatar-car {
  background: var(--bs-primary-bg-subtle);
  color: var(--bs-primary);
  font-size: 1.125rem;
}

.af-si-avatar-car img {
  width: 30px;
  height: 30px;
  object-fit: contain;
}

.af-si-main {
  flex: 1 1 auto;
  min-width: 0;
}

.af-si-line {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  min-width: 0;
}

.af-si-title {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--bs-heading-color);
}

.af-si-age {
  flex-shrink: 0;
  font-size: 0.75rem;
  color: var(--af-si-meta);
}

.af-si-ar {
  margin-inline-start: auto;
  flex-shrink: 0;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--af-si-meta);
}

/* Phone, e-mail, town, plate — the line under the name. Spaced rather than
   separated by bullets: x-if leaves its <template> in the child list, so a
   "every one but the first" rule would have counted the templates too. */
.af-si-meta {
  flex-wrap: wrap;
  gap: 0.125rem 0.75rem;
  margin-top: 0.1875rem;
  font-size: 0.75rem;
  color: var(--af-si-meta);
}

.af-si-meta > span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.af-si-plate {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--bs-body-color);
}

/* Why this row is here, when the row does not otherwise say: the CIN, the
   passport, the permis, the second phone, the matricule fiscale, or the
   contract number that led back to this client. */
.af-si-badge {
  display: inline-flex;
  align-items: center;
  gap: 0.3125rem;
  padding: 0.0625rem 0.4375rem;
  border-radius: 0.375rem;
  background: var(--bs-primary-bg-subtle);
  color: var(--bs-primary);
  font-size: 0.6875rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.af-si-badge-k {
  opacity: 0.75;
  font-weight: 500;
}

.af-si-status {
  flex-shrink: 0;
  font-size: 0.6875rem;
}

/* ── Nothing to show ── */
.af-si-empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.375rem;
  padding: 2.5rem 1.5rem;
  text-align: center;
  font-size: 0.875rem;
  color: var(--af-si-meta);
}

.af-si-empty-ic {
  font-size: 1.75rem;
  line-height: 1;
  color: var(--bs-primary);
  opacity: 0.45;
}

.af-si-empty-hint {
  max-width: 36ch;
  font-size: 0.75rem;
  opacity: 0.8;
}

/* On a phone the panel is a sheet off the bottom edge. */
@media (max-width: 575.98px) {
  .af-search-modal .global-search-dialog {
    max-width: 100%;
    margin: 0;
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    height: 78vh;
  }

  .af-search-modal .global-search-content {
    height: 100%;
    border-radius: 1rem 1rem 0 0 !important;
    border-bottom: 0;
    display: flex;
    flex-direction: column;
  }

  .af-search-modal .global-search-body {
    flex: 1 1 auto;
    max-height: none;
  }

  .af-search-modal.fade .global-search-dialog {
    transform: translateY(100%);
    transition: transform 0.3s ease-out;
  }

  .af-search-modal.show .global-search-dialog {
    transform: translateY(0);
  }
}

/* ── 12. Chrome details ──────────────────────────────────────────────────── */
/* SimpleBar's rail, to the pixel of DataBlox's: a 6px translucent slate pill
   inset 2px from the edge, drawn over the content rather than beside it. */
.simplebar-scrollbar::before {
  background: rgba(148, 163, 184, 0.45);
  border-radius: 9999px;
}

.simplebar-track.simplebar-vertical {
  width: 6px;
  right: 2px;
}

.simplebar-track.simplebar-horizontal {
  height: 6px;
  bottom: 2px;
}

/* SimpleBar's own stylesheet fades the rail to 0.5 while it is showing; the
   translucent fill above is already faint enough without halving it again. */
.simplebar-scrollbar.simplebar-visible::before {
  opacity: 1;
}

/* Focus rings pick up the new accent rather than the template's indigo. */
:root {
  --bs-focus-ring-color: rgba(var(--af-violet-600-rgb), 0.25);
}

.form-control:focus,
.form-select:focus {
  border-color: var(--af-violet-400);
  box-shadow: 0 0 0 3px rgba(var(--af-violet-600-rgb), 0.12);
}

/* ── 13. Dark mode: what the template compiled a light value into ────────── */
/* The template's stylesheet is a compiled build with a few literal colours
   baked into it. They are invisible in light mode — the literal *is* the light
   value — and wrong in every dark one. These are the two that matter. */

/* `.form-control { color: #000000 }`. Black text on the #1e1e1e field is
   1.06:1 — which is to say the value the user typed was not on the screen at
   all. Worse, the template also paints `::placeholder` in --bs-body-color at
   full opacity, so the *prompt* came out brighter than the answer: "exp:
   Tunis…" perfectly legible and the client's actual town not.
   A value takes the heading colour, the way it takes pure black in light mode.
   A prompt drops back to the secondary colour — in both themes, because a
   placeholder has never been an answer. */
.form-control,
.form-control:focus {
  color: var(--bs-heading-color);
}

.form-control::placeholder {
  color: var(--bs-secondary-color);
  opacity: 1;
}

/* A disabled or read-only box is still showing a value, just not an editable
   one — one step back from a live field, not two. */
.form-control:disabled,
.form-control[readonly] {
  color: var(--bs-body-color);
}

/* `--bs-light` is the app's faint wash — the hover behind a row, the fill of a
   quiet chip. §3 flips it to #2a2d2e for dark mode, but the template sets it
   again under `html[data-bs-theme=dark]`, the more specific selector, and wins
   with #232323: *darker* than the #252526 surfaces it is meant to lift off, so
   every one of those hovers sank instead of lifting. Matched selector for
   selector here so the intended value comes back. */
html[data-bs-theme=dark],
body[data-bs-theme=dark] {
  --bs-light: #2a2d2e;
  --bs-light-rgb: 42, 45, 46;
}

/* ── 14. One panel over another ──────────────────────────────────────────── */
/* Bootstrap has no notion of a second offcanvas over a first, and the client
   panel (560px) is narrower than the booking panel (920px) it opens over: the
   uncovered strip of the panel underneath stayed on screen, dimmed by the new
   backdrop, so two forms were up at once and nothing said which one the typing
   was going into. The one underneath steps aside while the one on top is open
   and comes back when it closes — _standDownPanelsBelow() in customer-form.js
   puts the class on and takes it off. */
.offcanvas.af-oc-under {
  opacity: 0;
  transform: translateX(2rem);
  visibility: hidden;
  pointer-events: none;
  /* Out on roughly the curve Bootstrap slides a panel in on, so the two read as
     one movement. `visibility` is held until the fade has finished going out,
     and snaps straight back on the way in — the class is simply removed, and
     with it this transition. */
  transition: opacity 0.2s ease-out, transform 0.25s ease-out, visibility 0s linear 0.25s;
}

.offcanvas.offcanvas-start.af-oc-under {
  transform: translateX(-2rem);
}

/* Nothing to animate for someone who asked for no animation — but it still has
   to leave, or the two panels are on screen together again. */
@media (prefers-reduced-motion: reduce) {
  .offcanvas.af-oc-under {
    transition: none;
  }
}

/* ── 15. The maintenance checklist ───────────────────────────────────────── */
/* A fiche's operations, ticked and priced one line at a time. Colours are read
   off the Bootstrap variables rather than written out, so the whole block
   follows the theme without a dark-mode copy of itself. */

.af-mt-tasks {
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  overflow: hidden;
}

.af-mt-task {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.625rem;
  padding: 0.5rem 0.625rem;
  border-bottom: 1px solid var(--bs-border-color);
}

.af-mt-task:last-child {
  border-bottom: 0;
}

/* A done line is tinted rather than struck through: it is the part of the fiche
   that carries a price, so it has to stay the most readable thing on it. */
.af-mt-task.is-done {
  background: var(--bs-success-bg-subtle);
}

.af-mt-task-label {
  flex: 1 1 8rem;
  min-width: 0;
  margin: 0;
  cursor: pointer;
  line-height: 1.25;
}

.af-mt-task.is-done .af-mt-task-label {
  font-weight: 600;
}

/* Wide enough for a four-figure price and its unit, and it does not shrink —
   on a narrow panel the label wraps above it instead. */
.af-mt-task-price {
  flex: 0 0 9.5rem;
  width: 9.5rem;
}

.af-mt-task-del {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  padding: 0;
  border: 0;
  border-radius: 0.375rem;      /* squares, never circles — see §11a */
  background: transparent;
  color: var(--bs-secondary-color);
  line-height: 1;
}

.af-mt-task-del:hover {
  background: var(--bs-danger-bg-subtle);
  color: var(--bs-danger);
}

/* ── The catalogue ──────────────────────────────────────────────────────── */
.af-mt-picker {
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  background: var(--bs-tertiary-bg);
  padding: 0.625rem;
}

.af-mt-picker-search {
  position: relative;
}

.af-mt-picker-search .fi {
  position: absolute;
  top: 50%;
  left: 0.625rem;
  transform: translateY(-50%);
  color: var(--bs-secondary-color);
  font-size: 0.85rem;
  pointer-events: none;
}

.af-mt-picker-search .form-control {
  padding-left: 2rem;
}

/* The whole catalogue is a long list; it scrolls inside its own box so the
   panel's own scroll still belongs to the fiche. */
.af-mt-picker-body {
  max-height: 15rem;
  overflow-y: auto;
  margin-top: 0.625rem;
}

.af-mt-picker-group + .af-mt-picker-group {
  margin-top: 0.625rem;
}

.af-mt-picker-title {
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--bs-secondary-color);
  margin-bottom: 0.25rem;
}

.af-mt-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  padding: 0.25rem 0.5rem;
  font-size: 0.8rem;
  line-height: 1.2;
  border: 1px solid var(--bs-border-color);
  border-radius: 0.5rem;        /* squares, never circles — see §11a */
  background: var(--bs-body-bg);
  color: var(--bs-body-color);
  text-align: left;
}

.af-mt-chip:hover {
  border-color: var(--bs-primary);
  color: var(--bs-primary);
}

/* An operation already on the fiche stays in the list, marked — clicking it a
   second time is how a line is taken back off. */
.af-mt-chip.is-on {
  background: var(--bs-primary-bg-subtle);
  border-color: var(--bs-primary);
  color: var(--bs-primary-text-emphasis);
  font-weight: 600;
}

.af-mt-chip .fi {
  font-size: 0.7rem;
  line-height: 1;
}

/* ── The total ──────────────────────────────────────────────────────────── */
/* Read, never typed: with a checklist the API sums the ticked lines itself. */
.af-mt-total {
  border: 1px solid var(--bs-border-color);
  border-radius: var(--bs-border-radius);
  background: var(--bs-tertiary-bg);
  padding: 0.75rem 0.875rem;
}
