/* ================================================================
   Valkos ERP — Tailwind-inspired Design System
   ================================================================ */

/* --- Design Tokens --- */
:root {
  --gray-50:  #f8fafc;
  --gray-100: #f1f5f9;
  --gray-200: #e2e8f0;
  --gray-300: #cbd5e1;
  --gray-400: #94a3b8;
  --gray-500: #64748b;
  --gray-600: #475569;
  --gray-700: #334155;
  --gray-800: #1e293b;
  --gray-900: #0f172a;

  --navy-50:  #e8edf4;
  --navy-100: #c0d0e2;
  --navy-200: #8da6c6;
  --navy-300: #5e7faa;
  --navy-400: #3a5a8a;
  --navy-500: #223b60;
  --navy-600: #1e3456;
  --navy-700: #1a2d4d;
  --navy-800: #172540;
  --navy-900: #141f38;

  --gold-50:  #fdf8eb;
  --gold-100: #f9ecc8;
  --gold-200: #f0d78a;
  --gold-300: #e7c254;
  --gold-400: #d4a843;
  --gold-500: #b8902e;
  --gold-600: #937325;
  --gold-700: #70571d;

  --red-50:   #fef2f2;
  --red-500:  #ef4444;
  --red-600:  #dc2626;

  --green-50:  #f0fdf4;
  --green-500: #22c55e;
  --green-600: #16a34a;

  --yellow-50:  #fefce8;
  --yellow-500: #eab308;

  --white:    #ffffff;

  --accent:   #d4a843;

  --shadow-sm:  0 1px 2px 0 rgba(0,0,0,0.05);
  --shadow:     0 1px 3px 0 rgba(0,0,0,0.1), 0 1px 2px -1px rgba(0,0,0,0.1);
  --shadow-md:  0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -2px rgba(0,0,0,0.1);
  --shadow-lg:  0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -4px rgba(0,0,0,0.1);

  --radius-sm: 0.25rem;
  --radius:    0.375rem;
  /* .stat-box has asked for this one since the tiles were written, and it was never
     defined — so every tile in the app had square corners. Same size as --radius. */
  --radius-md: 0.375rem;
  --radius-lg: 0.5rem;
  --radius-xl: 0.75rem;
  --radius-full: 9999px;
}

/* --- Reset --- */
*, *::before, *::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}

html { scroll-behavior: smooth; }

body {
  overflow-x: hidden;
  font-family: 'Inter', system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background-color: var(--gray-100);
  color: var(--gray-800);
  line-height: 1.6;
  min-height: 100vh;
}

a { color: inherit; text-decoration: none; }

/* --- Typography utilities --- */
.text-center  { text-align: center; }
.text-left    { text-align: left; }
.text-right   { text-align: right; }
.text-xs      { font-size: 0.75rem; line-height: 1rem; }
.text-sm      { font-size: 0.875rem; line-height: 1.25rem; }
.text-base    { font-size: 1rem; line-height: 1.5rem; }
/* Written on table cells since the bank screens and never defined until 2026-09-22, so a date
   like 2026-09-21 broke at its hyphen. Only ever on a cell inside a scrolling .table-wrapper. */
.whitespace-nowrap { white-space: nowrap; }
.text-lg      { font-size: 1.125rem; line-height: 1.75rem; }
.text-xl      { font-size: 1.25rem; line-height: 1.75rem; }
.text-2xl     { font-size: 1.5rem; line-height: 2rem; }
.font-medium  { font-weight: 500; }
.font-semibold { font-weight: 600; }
.font-bold    { font-weight: 700; }
.text-gray-200 { color: var(--gray-200); }
.text-gray-400 { color: var(--gray-400); }
.text-gray-500 { color: var(--gray-500); }
.text-gray-600 { color: var(--gray-600); }
.text-gray-800 { color: var(--gray-800); }
.text-white    { color: var(--white); }

/* --- Spacing utilities --- */
.mt-1 { margin-top: 0.25rem; }
.mt-2 { margin-top: 0.5rem; }
.mt-3 { margin-top: 0.75rem; }
.mt-4 { margin-top: 1rem; }
.mt-6 { margin-top: 1.5rem; }
.mt-8 { margin-top: 2rem; }
.mb-1 { margin-bottom: 0.25rem; }
.mb-2 { margin-bottom: 0.5rem; }
.mb-3 { margin-bottom: 0.75rem; }
.mb-4 { margin-bottom: 1rem; }
.mb-6 { margin-bottom: 1.5rem; }
.mb-8 { margin-bottom: 2rem; }
.my-4 { margin-top: 1rem; margin-bottom: 1rem; }
.p-3 { padding: 0.75rem; }
.p-4 { padding: 1rem; }
.p-5 { padding: 1.25rem; }
.p-6 { padding: 1.5rem; }
.px-3 { padding-left: 0.75rem; padding-right: 0.75rem; }
.px-4 { padding-left: 1rem; padding-right: 1rem; }
.px-5 { padding-left: 1.25rem; padding-right: 1.25rem; }
.py-2 { padding-top: 0.5rem; padding-bottom: 0.5rem; }
.py-3 { padding-top: 0.75rem; padding-bottom: 0.75rem; }
.ms-auto { margin-left: auto; }
.me-auto { margin-right: auto; }

/* --- Layout utilities --- */
.container {
  width: 100%;
  max-width: 1200px;
  margin-left: auto;
  margin-right: auto;
  padding-left: 1.5rem;
  padding-right: 1.5rem;
}
.w-full     { width: 100%; }
.max-w-md   { max-width: 28rem; margin-left: auto; margin-right: auto; }
.max-w-lg   { max-width: 32rem; margin-left: auto; margin-right: auto; }
.max-w-3xl  { max-width: 56rem; margin-left: auto; margin-right: auto; }
.flex       { display: flex; }
.inline-flex { display: inline-flex; }
.flex-col   { flex-direction: column; }
.flex-wrap  { flex-wrap: wrap; }
.flex-1     { flex: 1 1 0%; }
.items-center   { align-items: center; }
.items-start    { align-items: flex-start; }
.items-end      { align-items: flex-end; }
.justify-center  { justify-content: center; }
.justify-between { justify-content: space-between; }
.justify-end     { justify-content: flex-end; }
.gap-2 { gap: 0.5rem; }
.gap-3 { gap: 0.75rem; }
.gap-4 { gap: 1rem; }
.gap-6 { gap: 1.5rem; }
.grid       { display: grid; }
.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
@media (min-width: 768px) {
  .md\:grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .md\:grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

.d-inline { display: inline; }
.block    { display: block; }
.hidden   { display: none; }
/* Read by a screen reader, invisible on screen — for a label the sighted reader gets
   from the layout, such as the column headers of the summary tables on /project.
   Not display:none, which would hide it from the reader too. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* --- Decorations --- */
.bg-white     { background-color: var(--white); }
.bg-navy-800  { background-color: var(--navy-800); }
.rounded     { border-radius: var(--radius); }
.rounded-lg  { border-radius: var(--radius-lg); }
.rounded-xl  { border-radius: var(--radius-xl); }
.shadow      { box-shadow: var(--shadow); }
.shadow-md   { box-shadow: var(--shadow-md); }
.shadow-lg   { box-shadow: var(--shadow-lg); }
.border      { border: 1px solid var(--gray-200); }

/* ================================================================
   NAVBAR — elegant Tailwind-inspired top bar
   ================================================================ */
.navbar {
  position: sticky;
  top: 0;
  z-index: 250;
  background: var(--navy-900);
  border-bottom: 1px solid rgba(255,255,255,0.06);
  box-shadow: 0 1px 3px 0 rgba(0,0,0,0.3), 0 1px 2px -1px rgba(0,0,0,0.2);
}

/* THE BAR IS A CENTRED 1200 PX STRIP IN A FIXED PLACE (2026-10-10, the third shape of the day,
   decided with the user: a bar running the whole window did not convince, a strip that followed
   the content column moved between pages and slid when the rail collapsed — "the elements keep
   their place, only the side navigation's button comes and goes"). The .navbar leaves a 2.75rem
   slot at each side on every page — the button's width, so the strip is centred in the window
   and stands exactly where the dashboard's 1200 px content container stands, whatever the page
   and whatever the rail does; the left slot holds the button on a page with a rail and stays
   empty on one without. The strip is 1200 on the mail screens too (their content column is
   1600). It is a QUERY CONTAINER, so the clipping thresholds below (the links' words, the
   brand's name) are measured against the room it actually has — its content box, 1152 px at
   most — with one number each. inline-size containment applies no layout containment, so the
   phone's fixed overlay is unaffected; still, a phone switches the container off, its links
   being a column in that overlay where no @container rule may fire. */
.navbar {
  padding: 0 2.75rem;
}
.navbar-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 4rem;                       /* fixed 64px bar height */
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 1.5rem;
  container-type: inline-size;
  container-name: navbar;
}

/* The side navigation's one control (layout.html #sidebarToggle), before the logo — only on a page
   that has a rail. Absolutely placed in the .navbar (sticky, so its containing block), it stands at
   the window's edge in the bar's left slot, exactly as wide as the rail's strip of marks and right
   above it. */
.nav-sidebar-toggle {
  display: none;
  align-items: center;
  justify-content: center;
  position: absolute;
  left: 0;
  top: 0;
  width: 2.75rem;
  height: 4rem;
  padding: 0;
  background: none;
  border: none;
  color: var(--gray-400);
  cursor: pointer;
  z-index: 201;                       /* above the phone bar's ::after, like the logo */
  transition: background-color 0.15s, color 0.15s;
}
.nav-sidebar-toggle:hover {
  background-color: rgba(255,255,255,0.08);
  color: var(--white);
}
.nav-sidebar-toggle:focus-visible {
  outline: 2px solid var(--gold-400);
  outline-offset: -2px;
}
.nav-sidebar-toggle > .vk-mark { width: 20px; height: 20px; }
body:has(#appSidebar) .nav-sidebar-toggle { display: inline-flex; }
/* A narrow desk (a tablet on its side) has no room for the right-hand slot: the strip keeps the
   left one for the button and sits closer to its edges — a SUPERADMIN's marks and the company's
   name at its floor need 696 px of strip content (measured 2026-10-10), which a 769 px window
   gives only this way. The places are still fixed for a given width. */
@media (min-width: 769px) and (max-width: 899px) {
  .navbar { padding-right: 0; }
  .navbar-inner { padding: 0 0.75rem; }
  .nav-brand { margin-right: 1rem; }
}


.nav-brand {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--white);
  letter-spacing: -0.025em;
  white-space: nowrap;
  flex-shrink: 0;
  margin-right: 1.5rem;
  position: relative;
  z-index: 201;
}
.nav-brand:hover { color: var(--gold-400); }

.nav-logo {
  height: 1.75rem;
  width: auto;
}

.navbar-nav {
  display: flex;
  list-style: none;
  align-items: center;
  gap: 0.25rem;
  margin: 0;
  padding: 0;
  flex-shrink: 0;
}

/* 0.5rem at the sides (it was 0.75): with the words AND the company's name a SUPERADMIN's seven
   links need every one of the strip's 1152 px. */
.nav-link {
  display: inline-flex;
  align-items: center;
  padding: 0.5rem;
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--gray-400);
  border-radius: var(--radius);
  transition: background-color 0.15s, color 0.15s;
  white-space: nowrap;
}
.nav-link:hover {
  background-color: rgba(255,255,255,0.08);
  color: var(--white);
}

.nav-user {
  display: inline-flex;
  align-items: center;
  padding: 0.5rem 0.75rem;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--gray-400);
  white-space: nowrap;
}

.nav-right {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-left: auto;
  min-width: 0;                       /* may shrink: the company's name gives way inside it */
}
/* THE COMPANY'S NAME IS THE ONE ELASTIC THING IN THE BAR (2026-10-10, the user: the name must be
   there): when the strip fills, the switcher shrinks to its floor and the name takes an ellipsis —
   it is never hidden, and the button's title carries it whole. Everything else keeps its width. */
.nav-right > .nav-mail,
.nav-right > .nav-notifications,
.nav-right > .nav-account { flex-shrink: 0; }
.nav-right > .nav-company { min-width: 6.5rem; }

/* --- Mobile Notification Bell (direct link, next to hamburger) --- */
.nav-notifications-mobile {
  display: none;
  align-items: center;
  justify-content: center;
  position: relative;
  color: var(--gray-400);
  padding: 0.5rem;
  z-index: 201;
  text-decoration: none;
  transition: color 0.15s;
}
.nav-notifications-mobile:hover {
  color: var(--white);
}
.nav-notifications-mobile svg {
  display: block;
}
.nav-notifications-mobile .nav-notification-badge {
  top: 0.1rem;
  right: 0;
}

/* --- Hamburger Button --- */
.nav-hamburger {
  display: none;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0.5rem;
  margin-left: auto;
  z-index: 201;
  flex-direction: column;
  justify-content: center;
  gap: 5px;
  width: 2rem;
  height: 2rem;
}
.nav-hamburger span {
  display: block;
  width: 100%;
  height: 2px;
  background: var(--white);
  border-radius: 1px;
  transition: transform 0.3s ease, opacity 0.3s ease;
  transform-origin: center;
}
.navbar.nav-open .nav-hamburger span:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}
.navbar.nav-open .nav-hamburger span:nth-child(2) {
  opacity: 0;
}
.navbar.nav-open .nav-hamburger span:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

/* --- Mobile Overlay (transparent on desktop) --- */
.nav-mobile-overlay {
  display: contents;
}

/* --- Mobile Menu --- */
@media (max-width: 768px) {
  .navbar { padding: 0 0 0 2.75rem; }   /* the button's slot; the hamburger keeps the right edge */
  .navbar-inner { container-type: normal; padding-left: 0.5rem; }
  /* Pushed against the hamburger: with space-between alone it hung half-way along the bar. */
  .nav-notifications-mobile {
    display: flex;
    margin-left: auto;
  }
  .nav-hamburger {
    display: flex;
    margin-left: 0;
  }

  .navbar::after {
    content: '';
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 4rem;
    background: var(--navy-900);
    z-index: 200;
  }

  .nav-mobile-overlay {
    display: flex;
    flex-direction: column;
    position: fixed;
    top: 4rem;
    left: 0;
    width: 100%;
    height: calc(100vh - 4rem);
    height: calc(100dvh - 4rem);
    background: var(--navy-900);
    z-index: 200;
    padding: 1.5rem;
    padding-top: 4.5rem;
    transform: translateY(-100%);
    visibility: hidden;
    transition: transform 0.3s ease, visibility 0.3s;
  }
  .navbar.nav-open .nav-mobile-overlay {
    transform: translateY(0);
    visibility: visible;
  }

  .navbar-nav {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    flex: 1 1 0;
    min-height: 0;
    overflow-y: auto;
  }
  .navbar-nav li {
    border-bottom: 1px solid rgba(255,255,255,0.06);
  }
  .navbar-nav .nav-link {
    display: flex;
    padding: 0.875rem 0.5rem;
    font-size: 1rem;
  }

  /* Dropdown accordion in mobile */
  .nav-dropdown { position: static; }
  .nav-dropdown-toggle::after {
    margin-left: auto;
    transition: transform 0.3s ease;
  }
  .nav-dropdown.open .nav-dropdown-toggle::after {
    transform: rotate(180deg);
  }
  .nav-dropdown-menu {
    display: block;
    position: static;
    background: transparent;
    border: none;
    border-radius: 0;
    box-shadow: none;
    padding: 0;
    min-width: 0;
    max-height: 0;
    overflow: hidden;
    transition: max-height 0.3s ease;
  }
  .nav-dropdown:hover .nav-dropdown-menu {
    max-height: 0;
  }
  .nav-dropdown.open .nav-dropdown-menu {
    max-height: 30rem;
  }
  .nav-dropdown-menu li {
    border-bottom: 1px solid rgba(255,255,255,0.06);
  }
  .nav-dropdown-menu .nav-link {
    padding: 0.875rem 0.5rem 0.875rem 2rem;
    font-size: 1rem;
  }
  .nav-dropdown-menu .nav-dropdown-header {
    padding-left: 2rem;
  }

  .nav-right {
    margin-left: 0;
    margin-top: auto;
    padding-top: 1.5rem;
    border-top: 1px solid rgba(255,255,255,0.1);
    flex-wrap: wrap;
    justify-content: center;
    flex-shrink: 0;
  }
  .nav-right > .nav-company {
    position: absolute;
    top: 1rem;
    left: 1.5rem;
    right: 1.5rem;
    width: auto;
    margin: 0;
  }

  body.nav-open {
    overflow: hidden;
  }
}

/* ================================================================
   MAIN CONTENT — proper spacing below navbar
   ================================================================ */
.main-content {
  padding-top: 2rem;
  padding-bottom: 3rem;
}

/* ================================================================
   CARD
   ================================================================ */
.card {
  background-color: var(--white);
  border-radius: var(--radius-lg);
  border: 1px solid var(--gray-200);
  box-shadow: var(--shadow);
  padding: 1.5rem;
}
.card-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--gray-800);
  margin-bottom: 0.75rem;
}
.card-platform-logo {
  height: 25px;
  /* Fixed slot, not the image's own ratio: platform logos range from 19px to
     136px wide at this height, and the badge row starts right after the logo —
     without a constant slot every card's badges sat somewhere else. */
  width: 100px;
  object-fit: contain;
  object-position: left center;
  margin-bottom: 0.75rem;
  display: block;
  align-self: flex-start;
}
.card-platform-logo.mb-0 {
  margin-bottom: 0;
}
/* Platform cards live in a 28rem column; long unbreakable values (callback
   URLs, OAuth scope names, account e-mails) must wrap instead of escaping
   or being clipped by the card. */
.card code { overflow-wrap: anywhere; }
.card .flex-1 { min-width: 0; }
.break-anywhere { overflow-wrap: anywhere; }
/* Text written one step per line keeps its lines, e.g. a task's instructions. */
.text-pre-line { white-space: pre-line; }
/* Inline validation text, e.g. the file-size message js/upload-guard.js writes. */
.text-danger { color: var(--red-600); }
/* Platform column on the invoice list shows ICONS ONLY; alt/title carry the name, and
   the help cursor says hovering will tell. A sender's own logo may be a wide wordmark,
   so it is held to the width of the widest built-in one (5.5 x 16 px). */
.col-platform { text-align: center; }
.col-platform [title] { cursor: help; }
.col-platform .sender-logo { max-width: 5.5rem; object-fit: contain; }
/* An issuer's logo (V150, an Autopay operator's) is a wordmark we drew from the platform's own,
   its margins trimmed: held like a sender's. */
.col-platform .issuer-logo { max-width: 5.5rem; object-fit: contain; }
/* Every logo in the column is held to one width, so a long wordmark (Tesla) shrinks
   instead of stretching the column. */
.col-platform .platform-logo { max-width: 3.25rem; object-fit: contain; }
/* Neutral 16px inline platform logo — the fragments/platform-logo.html size.
   It flows inline, so several can sit in a row. */
.platform-logo {
  height: 16px;
  width: auto;
  vertical-align: middle;
}
/* "Add invoices" (/invoices/upload): the files chosen, and the result's lines inside its alerts. */
.upload-file-list {
  margin: 0.5rem 0 0;
  padding-left: 1.25rem;
  font-size: 0.875rem;
  color: var(--gray-700);
  overflow-wrap: anywhere;
}
.upload-result-list {
  margin: 0.25rem 0 0;
  padding-left: 1.25rem;
  overflow-wrap: anywhere;
}
/* A platform with no wordmark (MANUAL, documents added by hand) wears a ValkOS mark in the
   logo's box: the same 16px height, the mark centred in it. */
.platform-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
}
/* Several logos side by side (fleet list/candidates, unlinked drivers, the
   vehicle header). Every asset in /integration-logos is a WORDMARK, 1.7x to
   5.5x wider than tall, so two adjacent ones read as one run-on string. Each
   gets its own bordered chip with a constant slot — the .card-platform-logo
   idea (a fixed slot, not the image's own ratio), which also keeps a row of
   chips from jumping width between rows. Scoped to a container
   class on purpose: the single-logo invoice cell (.col-platform) has no such
   ancestor and keeps the bare rule above. */
.platform-logos {
  display: flex;
  /* Load-bearing: without it a four-platform cell's min-content is all four
     chips on one line (376px), which a narrow viewport can only answer with a
     horizontal scrollbar; wrapping lets the column reflow to 2x2 instead. */
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}
/* box-sizing: border-box (the reset) — 88x26 INCLUDES the padding and border,
   so the slot the logo is fitted into is 74x16 and the chip's outer footprint
   is a flat 88px. 74/16 = 4.625 is above every wordmark's ratio except mapon's
   5.45:1, so bolt/tesla/autopay/uber/fortum render at exactly the 16px height
   they had before and only mapon is scaled down (74x13.6) — the trade-off for
   a column that never moves. To retune, change ONLY the width: height 26px is
   what yields the 16px slot. */
.platform-logos .platform-logo {
  width: 88px;
  height: 26px;
  padding: 0.25rem 0.375rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  /* White, not gray-50: the chip must stay visible on a hovered row. */
  background-color: var(--white);
  object-fit: contain;
  object-position: center;
  flex-shrink: 0;
}
/* The fragment falls back to bare text for a platform with no asset, and
   th:replace makes that span a direct child here too — same box, or it would
   read as a run-on string again. */
.platform-logos > span {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 88px;
  height: 26px;
  padding: 0.25rem 0.375rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  background-color: var(--white);
  color: var(--gray-600);
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1;
  white-space: nowrap;
  overflow: hidden;
  flex-shrink: 0;
}
/* One frame for every vehicle picture, list thumbnail and detail card alike.
   The sources disagree about shape — Uber's catalogue renders are ~200x98
   (measured off the cached JPEGs; ImageSanitizer only ever scales down, so
   they keep the CDN's native ratio), Tesla's and an operator's phone upload
   are anything at all — so the BOX is fixed and the picture is contained
   inside it. Nothing is ever cropped; a picture that does not match the ratio
   gets bands of the box's own background instead.

   The box itself is TRANSPARENT: Uber's catalogue renders are cut-out PNGs and
   the sanitiser now keeps that (see ImageSanitizer), so a grey fill would put
   back the very box the transparency removes. The stand-in of a car without a
   photo has no background for the same reason, and is drawn in this box's own
   2:1 frame (fleet/service/VehiclePlaceholder: the type's mark, a vehicle seen
   from the side, or the fleet's front-view car while no type is known).

   The photo endpoint serves that stand-in when no photo exists, so no <img>
   here ever needs a hasPhoto branch. */
:root { --vehicle-photo-ratio: 2 / 1; }

/* The vehicle page's "now" card (fleet/vehicle-live.html): the map static/js/vehicle-map.js draws once the card
   lands. A fixed height, or Leaflet has nothing to draw into; a little less on a phone. */
.vehicle-live-map { height: 20rem; border-radius: 8px; overflow: hidden; }
@media (max-width: 640px) { .vehicle-live-map { height: 15rem; } }
.vehicle-live-technical summary { cursor: pointer; font-size: 0.875rem; color: var(--gray-600); }

.vehicle-photo-lg {
  width: 240px;
  max-width: 100%;
  aspect-ratio: var(--vehicle-photo-ratio);
  border-radius: 8px;
  object-fit: contain;
  object-position: center;
  display: block;
  /* Load-bearing, and the reason this used to render as a tall crop: as a flex
     item beside the property list, align-items:stretch gave the <img> a
     DEFINITE height of the whole row, which both beats aspect-ratio and hands
     object-fit a 240x500 box to fill. Same class of fix as .card-platform-logo
     below. */
  align-self: flex-start;
}
/* ---- A registration plate (fragments/plate.html, static/js/plate.js) ----
   Drawn one way wherever a plate is shown: white, a dark frame and the blue band on
   the left, as on a Norwegian plate; config/Plates decides the spelling ("EK 12345").
   Inline-block, so it sits in a line of text, a table cell, a card or a map popup
   alike. A plate that is a LINK keeps the look — it is what the eye searches a page
   for — and says so by a navy frame on hover and a focus ring, never by turning into
   ordinary link text.
   ONE MEASURE PER SIZE (2026-10-21, the user): every standard plate of a size is the same
   box, whatever its letters — a min-width that holds "MM 88888" (two of the widest letters
   a plate uses, five digits; W, wider still, only grows the box), measured in Inter 700 in
   headless Chromium and Firefox (95.4 / 83.7 / 173.5 px) and rounded up to 1/16 rem; the
   digits are tabular so every number is as wide as every other; the height is a whole-pixel
   line-height in rem, the same in every context (the mail list no longer shrinks its own);
   the text is centred in the white part — symmetric padding, and a text-indent equal to the
   letter-spacing, which is also added after the LAST letter and would pull the text left.
   Vertically Inter centres its capitals in the line box by itself (its ascender minus half
   the cap height is half the content area). A longer plate — personalised, foreign — grows
   and is never cut. Rem throughout: a plate is the same in a 13 px mail row and a 16 px card. */
:root { --plate-band: #1e40af; }
.plate {
  display: inline-block;
  box-sizing: border-box;
  min-width: 6rem;
  padding: 0.0625rem 0.4375rem;
  border: 1.5px solid var(--gray-800);
  border-left: 6px solid var(--plate-band);
  border-radius: 3px;
  background: var(--white);
  color: var(--gray-900);
  font-size: 0.8125rem;
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  line-height: 1.125rem;
  letter-spacing: 0.04em;
  text-indent: 0.04em;
  text-align: center;
  text-transform: uppercase;
  white-space: nowrap;
}
.plate--sm {
  min-width: 5.25rem;
  padding: 0 0.3125rem;
  border-width: 1px;
  border-left-width: 4px;
  font-size: 0.75rem;
  line-height: 1rem;
}
.plate--lg {
  min-width: 10.875rem;
  padding: 0.125rem 0.75rem;
  border-width: 2px;
  border-left-width: 10px;
  border-radius: 5px;
  font-size: 1.5rem;
  line-height: 1.875rem;
}
.plate--link { text-decoration: none; transition: border-color 0.15s, box-shadow 0.15s; }
.plate--link:hover {
  border-color: var(--navy-500);
  border-left-color: var(--plate-band);
  box-shadow: 0 0 0 2px var(--navy-100);
}
.plate--link:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 2px; }
/* A plate with a card (data-vehicle-card, static/js/plate-card.js) held by a finger: the long
   press is the card's, not the browser's — no link preview, no selection. */
@media (hover: none) {
  .plate[data-vehicle-card] { -webkit-touch-callout: none; -webkit-user-select: none; user-select: none; }
}

/* ---- The car's card on a plate (static/js/plate-card.js, fleet/plate-card.html) ----
   One element for the page, fixed to the viewport beside the plate it is about, above the
   compose dock (210) and under the navbar (250), which the script keeps it clear of. Nothing
   in it can be clicked, so with a mouse it takes no pointer at all and never steals the
   plate's hover; opened by a finger it takes the tap that closes it (.is-touch). The photo is
   a THUMBNAIL beside the name (2026-10-21, the user: across the card it was too large and cut),
   the gallery's size and rule — 2:1, object-fit contain, the whole picture on a light ground —
   with the image's own width and height, so the card's size is known before the picture
   arrives and the script places it once. */
.plate-card {
  position: fixed;
  z-index: 240;
  width: 22rem;
  max-width: calc(100vw - 1rem);
  overflow: hidden;
  border: 1px solid var(--gray-200);
  border-radius: 0.5rem;
  background: var(--white);
  box-shadow: 0 10px 24px rgba(15, 23, 42, 0.16), 0 2px 6px rgba(15, 23, 42, 0.08);
  color: var(--gray-900);
  font-size: 0.8125rem;
  line-height: 1.35;
  text-align: start;
  pointer-events: none;
}
.plate-card[hidden] { display: none; }
.plate-card.is-touch { pointer-events: auto; }
@media (prefers-reduced-motion: no-preference) {
  .plate-card { opacity: 0; transform: translateY(2px); transition: opacity 0.12s ease-out, transform 0.12s ease-out; }
  .plate-card.is-visible { opacity: 1; transform: none; }
}
.plate-card-main { display: flex; flex-direction: column; gap: 0.25rem; padding: 0.75rem; }
.plate-card-top { display: flex; align-items: flex-start; gap: 0.625rem; }
.plate-card-photo {
  flex: none;
  width: 8rem;
  aspect-ratio: var(--vehicle-photo-ratio);
  overflow: hidden;
  border-radius: var(--radius);
  background: var(--gray-50);
}
.plate-card-photo img { display: block; width: 100%; height: 100%; object-fit: contain; }
.plate-card-ident { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }
.plate-card-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.375rem 0.625rem; }
.plate-card-type { font-size: 0.75rem; color: var(--gray-600); }
.plate-card-name {
  font-size: 0.9375rem;
  font-weight: 600;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.plate-card-meta { color: var(--gray-600); }
.plate-card-vin { line-height: 1; }
.plate-card-facts {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  margin: 0.375rem 0 0;
  padding: 0.5rem 0 0;
  border-top: 1px solid var(--gray-100);
  list-style: none;
}
.plate-card-fact { display: flex; flex-wrap: wrap; align-items: center; gap: 0.125rem 0.5rem; min-width: 0; }
.plate-card-fact-text { display: flex; flex-direction: column; min-width: 0; }
.plate-card-fact-main { min-width: 0; font-weight: 500; }
.plate-card-fact-sub { font-size: 0.75rem; color: var(--gray-500); }
.plate-card-fact-warn { color: #854d0e; }
.plate-card-fact-quiet { font-weight: 400; color: var(--gray-600); }
.plate-card-keys .vehicle-card-keys { position: relative; left: auto; bottom: auto; flex-shrink: 0; }
.plate-card-keys { flex-wrap: nowrap; }
/* A fact led by its mark: the line under it starts where the words above do (the mark's 16 px and gap). */
.plate-card-fact:not(.plate-card-keys) { flex-direction: column; align-items: flex-start; gap: 0; }
.plate-card-fact:not(.plate-card-keys) > .plate-card-fact-sub { padding-inline-start: 1.3125rem; }
.plate-card-inspection.is-overdue .plate-card-fact-main { color: var(--red-600); }
.plate-card-inspection.is-soon .plate-card-fact-main { color: #854d0e; }
.plate-card-inspection.is-missing .plate-card-fact-main { color: var(--gray-600); }

/* ---- A VIN (fragments/vin.html) ----
   Deliberately nothing like a plate: seventeen characters read one at a time, so a quiet
   monospace field split into its ISO 3779 parts (WMI · VDS · VIS) by fleet/service/Vins.
   Four things here are load-bearing:
   - the BORDER, not the fill, is what separates the field from the row under the cursor:
     --gray-100 and the .table hover's --gray-50 are two per cent of luminance apart;
   - direction:ltr pins the flex MAIN AXIS, not the script: under an rtl ancestor
     flex-direction:row reverses and the VIN would read VIS-VDS-WMI;
   - .vin-group{nowrap} plus flex-wrap make the field's minimum width one GROUP (eight
     characters) instead of all seventeen, which is what lets it sit in .spec-grid's value
     column on a phone. It then breaks between groups; the <code> it replaces only fitted
     by breaking mid-number, wherever .card code{overflow-wrap:anywhere} happened to land;
   - user-select:all selects the whole number in one click, which is why the groups carry no
     text between them — what gets copied is the VIN a portal will accept, without spaces.
   No slashed zero: the stack that actually resolves on the server has no such glyph feature,
   and a legal VIN carries neither O nor I to confuse with one. */
.vin {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0 0.45em;
  padding: 0.0625rem 0.4375rem;
  border: 1px solid var(--gray-300);
  border-radius: 3px;
  background: var(--gray-100);
  color: var(--gray-900);
  font-family: ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;
  font-size: 0.8125rem;
  line-height: 1.35;
  font-variant-ligatures: none;
  direction: ltr;
  user-select: all;
}
.vin-group { white-space: nowrap; }
.vin--sm {
  gap: 0 0.35em;
  padding: 0 0.3125rem;
  font-size: 0.75rem;
  line-height: 1.3;
}

/* ---- Vehicle cards: /fleet and the dashboard's fleet (fragments/vehicle-card.html) ----
   The card is the link; the photo, the plate and the model are the shared head, and each
   page adds its own foot (the year on /fleet, the month's money on the dashboard). */
.vehicle-gallery {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 8.5rem), 1fr));
  gap: 0.75rem;
  list-style: none;
  margin: 0.75rem 0 0;
  padding: 0;
}
.vehicle-gallery--large {
  grid-template-columns: repeat(auto-fill, minmax(min(100%, 10.5rem), 1fr));
  gap: 1rem;
  margin: 0;
}
.vehicle-card {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  height: 100%;
  padding: 0.625rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  background: var(--white);
  color: inherit;
  text-decoration: none;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.vehicle-card:hover { border-color: var(--navy-300); box-shadow: var(--shadow-md); }
.vehicle-card:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 2px; }
.vehicle-card-photo { display: block; background: var(--gray-50); border-radius: var(--radius); }
.vehicle-card-img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: var(--vehicle-photo-ratio);
  object-fit: contain;
}
.vehicle-card .plate { align-self: flex-start; margin-top: 0.375rem; }
.vehicle-card-model {
  font-size: 0.75rem;
  color: var(--gray-500);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.vehicle-card-meta { font-size: 0.75rem; color: var(--gray-400); font-variant-numeric: tabular-nums; }

/* The EU-kontroll flag over a vehicle's photo on a card. Since the Inspection column
   went, this icon is the only place /fleet shows the deadline at all.

   The wrapper exists solely as the containing block: an <img> takes no children.
   line-height:0 kills the baseline gap an inline image otherwise leaves underneath —
   the same note .photo-crop-stage carries, the only other relative wrapper around an
   <img> here.

   The flag itself is shaped like .nav-notification-badge: absolutely placed and
   pointer-events:none, so it never swallows the click that opens the card. */
.vehicle-thumb-wrap {
  position: relative;
  display: inline-block;
  line-height: 0;
}
.deadline-flag {
  position: absolute;
  top: -6px;
  right: -6px;
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  background: var(--white);
  box-shadow: 0 0 0 2px var(--flag-ring, var(--gray-300));
  line-height: 0;
  pointer-events: none;
}
.deadline-flag .vk-mark { display: block; width: 16px; height: 16px; }
/* A white disc keeps each mark readable on any photo. The three states have shapes of their own
   (the overdue state, the days-left hourglass, the inspections mark for a date not filled in —
   /fleet only) and words in the title; the ring repeats them, grey for missing data. */
.deadline-flag--overdue { --flag-ring: var(--red-600); color: var(--red-600); }
.deadline-flag--soon    { --flag-ring: var(--yellow-500); }
.deadline-flag--missing { --flag-ring: var(--gray-400); }
/* The same deadline on /fleet/{id}, in words — "in 5 days" / "1 day overdue" beside
   the date, quieter than the date so the badge still carries the alarm. */
.deadline-count { color: var(--gray-500); }

/* Who holds the keys (fragments/key-holder.html, from the custody register): the holder's
   face in the photo's bottom-left corner — the deadline flag keeps the top-right — with a
   key badge shaped like the podium's medal. Greyed (the crew's is-away look) when no ride
   has confirmed the holder for fleet.custody.stale-days; a plain grey disc for "keys
   returned". pointer-events:none like the flag, so it never swallows the click that opens
   the card. The NAME is the line under the model: the face alone says nothing to a screen
   reader, and two drivers can look alike at 32 px. The same fragment sits in .keys-holder
   on /fleet/{id} (64 px) and in .keys-history tables (28 px), which only resize it. */
.vehicle-card-keys {
  position: absolute;
  left: -4px;
  bottom: -4px;
  display: inline-block;
  line-height: 0;
  pointer-events: none;
}
.vehicle-card-keys .avatar {
  display: block;
  width: 32px;
  height: 32px;
  border: 2px solid var(--white);
  box-shadow: 0 0 0 1px var(--gray-200);
  background: var(--gray-100);
}
.vehicle-card-keys.is-stale .avatar { filter: grayscale(1); opacity: 0.55; }
.vehicle-card-keys-empty { border-radius: 50%; }
/* The key handovers' mark (measure:key-handovers, S16) on a WHITE disc ringed in navy — the photo
   flags' pattern, so the navy-and-gold mark needs no surface of its own; keys returned: a grey ring
   and the mark greyed. */
.key-badge {
  position: absolute;
  right: -5px;
  bottom: -3px;
  display: grid;
  place-items: center;
  width: 1.125rem;
  height: 1.125rem;
  border-radius: 50%;
  background: var(--white);
  box-shadow: 0 0 0 1.5px var(--navy-500), 0 0 0 2.5px var(--white);
}
.key-badge .vk-mark { display: block; width: 12px; height: 12px; }
.vehicle-card-keys--returned .key-badge { box-shadow: 0 0 0 1.5px var(--gray-400), 0 0 0 2.5px var(--white); }
.vehicle-card-keys--returned .key-badge .vk-mark { filter: grayscale(1); opacity: 0.6; }
.vehicle-card-holder {
  display: flex;
  align-items: center;
  gap: 0.25rem;
  min-width: 0;
  font-size: 0.75rem;
  color: var(--gray-600);
}
.vehicle-card-holder > .vk-mark { flex-shrink: 0; width: 14px; height: 14px; }
.vehicle-card-holder-name { overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }

/* A car in a workshop today (fragments/vehicle-card.html, from the workshop stays): the workshop's
   mark on a white disc ringed in yellow, in the photo's bottom-RIGHT corner — the deadline flag keeps the top-right, the key holder's
   face the bottom-left — shaped like the key badge, pointer-events:none so it never swallows
   the click that opens the card. The NAME of the workshop is the line under the model, like
   the holder's name: the mark alone says nothing to a screen reader. */
.vehicle-card-workshop {
  position: absolute;
  right: -4px;
  bottom: -4px;
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  border: 2px solid var(--yellow-500);
  background: var(--white);
  pointer-events: none;
}
.vehicle-card-workshop .vk-mark { display: block; width: 14px; height: 14px; }
.vehicle-card-workshop-name {
  display: block;
  min-width: 0;
  font-size: 0.75rem;
  color: var(--gray-600);
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}
.keys-holder { display: flex; align-items: center; gap: 1rem; }
.keys-holder .vehicle-card-keys,
.keys-history .vehicle-card-keys { position: relative; left: auto; bottom: auto; pointer-events: auto; }
.keys-holder .vehicle-card-keys .avatar { width: 64px; height: 64px; }
.keys-holder .key-badge { width: 1.625rem; height: 1.625rem; right: -4px; bottom: -4px; }
.keys-holder .key-badge .vk-mark { width: 18px; height: 18px; }
.keys-history .vehicle-card-keys .avatar { width: 28px; height: 28px; }
.keys-history .key-badge { display: none; }
.platform-form {
  overflow: hidden;
}
.settings-card-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.375rem;
  margin-bottom: 1.5rem;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--gray-200);
  text-align: center;
}
.settings-card-header-icon {
  width: 2rem;
  height: 2rem;
  color: var(--navy-600);
  flex-shrink: 0;
}
.settings-card-title {
  font-size: 1.125rem;
  font-weight: 600;
  color: var(--gray-900);
  margin: 0;
}

/* ================================================================
   FORMS
   ================================================================ */
.form-label {
  display: block;
  margin-bottom: 0.375rem;
  font-weight: 500;
  font-size: 0.875rem;
  color: var(--gray-700);
}
.form-input,
.form-control {
  width: 100%;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  padding: 0.625rem 0.875rem;
  font-size: 0.9375rem;
  color: var(--gray-800);
  background-color: var(--white);
  transition: border-color 0.15s, box-shadow 0.15s;
}
/* A file input's intrinsic width is ~250px and, as a flex item, that is also its
   automatic minimum — beside an Upload button it cannot fit a 264px card on a
   phone and pans the page. Let it shrink like any other .form-input; the file
   name is what gets clipped, never the button. */
input[type="file"].form-input { min-width: 0; }
.form-input:focus,
.form-control:focus {
  outline: none;
  border-color: var(--navy-500);
  box-shadow: 0 0 0 3px rgba(34,59,96,0.15);
}
.form-input::placeholder,
.form-control::placeholder { color: var(--gray-400); }

.form-input:disabled,
.form-control:disabled {
  background-color: var(--gray-100);
  border-color: var(--gray-200);
  color: var(--gray-500);
  cursor: not-allowed;
}

.form-control--sm {
  padding: 0.375rem 0.625rem;
  font-size: 0.875rem;
}

/* --- Collapsible checkbox filter (the invoice list's platform picker) --- */
/* A <details> dressed as a form control: closed it is one field naming what is
   ticked, open it drops a bordered list below. The panel is absolutely positioned
   so opening it does not shove the table down the page; the wrapper therefore has
   to establish the containing block. */
.filter-checklist {
  position: relative;
  display: block;
}
.filter-checklist-summary {
  /* Same box as .form-input, so it lines up with Status and Search beside it. */
  min-width: 12rem;
  max-width: 100%;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  padding: 0.625rem 2rem 0.625rem 0.875rem;
  font-size: 0.9375rem;
  color: var(--gray-800);
  background-color: var(--white);
  cursor: pointer;
  /* One line whatever is ticked: three platforms and two senders must not grow
     the filter bar to two rows. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* The default marker would sit inside the padding box and break the ellipsis;
     the caret below replaces it. Both rules are needed — WebKit ignores the
     list-style and needs its own pseudo-element switched off. */
  list-style: none;
}
.filter-checklist-summary::-webkit-details-marker { display: none; }
.filter-checklist-summary::after {
  content: "";
  position: absolute;
  right: 0.875rem;
  top: 1.15rem;
  border: 0.3rem solid transparent;
  border-top-color: var(--gray-500);
}
.filter-checklist[open] > .filter-checklist-summary::after {
  top: 0.85rem;
  border-top-color: transparent;
  border-bottom-color: var(--gray-500);
}
.filter-checklist-summary:focus-visible {
  outline: none;
  border-color: var(--navy-500);
  box-shadow: 0 0 0 3px rgba(34,59,96,0.15);
}
.filter-checklist-panel {
  position: absolute;
  z-index: 20;
  top: calc(100% + 0.25rem);
  left: 0;
  min-width: 100%;
  /* Never wider than the viewport minus the page padding — a long sender name
     must not be the reason a phone scrolls sideways. */
  max-width: min(22rem, calc(100vw - 3rem));
  max-height: 16rem;
  overflow-y: auto;
  padding: 0.5rem;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  background: var(--white);
  box-shadow: var(--shadow-md);
}
.filter-checklist-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.25rem 0.375rem;
  border-radius: var(--radius-sm);
  font-size: 0.875rem;
  white-space: nowrap;
  cursor: pointer;
}
.filter-checklist-item:hover { background: var(--gray-100); }
.filter-checklist-item input[type="checkbox"] { flex: none; }
.filter-checklist-item .platform-logo { flex: none; }
/* The single-choice variant (fragments/filter-choice.html): radios dressed as a dropdown, every
   entry with its icon. The radio stays in the page, visually hidden, so it keeps the focus and
   the arrow keys; the chosen entry is tinted and ticked, never marked by colour alone. */
.filter-choice-summary { display: flex; align-items: center; gap: 0.5rem; }
.filter-choice-text { overflow: hidden; text-overflow: ellipsis; }
.filter-choice-icon { display: inline-flex; flex: none; color: var(--gray-600); }
/* A one-tone state mark takes the entry's tone; a measure's mark keeps its own palette. */
.filter-choice-icon--ok { color: #16a34a; }
.filter-choice-icon--muted { color: var(--gray-400); }
.filter-choice-icon--cat { color: var(--pie); }
.filter-choice-item { position: relative; padding-right: 1.75rem; }
.filter-choice-item input[type="radio"] {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}
.filter-choice-item:has(input:checked) { background: var(--navy-50); color: var(--navy-700); font-weight: 600; }
.filter-choice-item:has(input:checked)::after {
  content: "";
  position: absolute;
  right: 0.625rem;
  top: 50%;
  width: 0.375rem;
  height: 0.625rem;
  margin-top: -0.375rem;
  border: solid var(--navy-600);
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
.filter-choice-item:has(input:focus-visible) { outline: 2px solid var(--navy-400); outline-offset: -2px; }
/* Registered senders hang under the E-mail row they narrow, a platform's issuers (V150)
   under their platform. */
.filter-checklist-item-child { padding-left: 1.5rem; }
/* The pictures an entry may wear besides a mark (fragments/filter-choice — the mail list's filters):
   a person's face with their rows' colour as a dot on its corner, a car's plate. An entry with no
   picture keeps the place of one, so the words of a list line up. */
.filter-choice-icon:empty { width: 16px; }
.filter-choice-face { position: relative; display: inline-flex; }
.filter-choice-face .avatar { width: 20px; height: 20px; }
.filter-choice-face .area-dot {
  position: absolute;
  right: -2px;
  bottom: -2px;
  box-shadow: 0 0 0 2px var(--white);
}
.filter-choice-icon--plate .plate { margin: 0; }
/* A group of entries under its heading (fragments/filter-choice :: section) — the list's <optgroup>,
   quiet like the row menus' group headings (.mail-menu-group). */
.filter-choice-section + .filter-choice-section,
.filter-choice-item + .filter-choice-section,
.filter-choice-section + .filter-choice-item { margin-top: 0.25rem; padding-top: 0.25rem; border-top: 1px solid var(--gray-200); }
.filter-choice-section-title {
  padding: 0.25rem 0.375rem 0.125rem;
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--gray-400);
}
/* Open, a list floats over whatever box would clip it (app.js [data-choice]: the mail list's filter
   panel scrolls inside a sticky head) — placed under its field, or over it when there is more room
   there, never under the navbar. Above the sidebar's strip, below the navbar and an open rail. */
.filter-checklist-panel.is-floating { position: fixed; z-index: 150; }

/* --- Native checkboxes & radios (design-system styled) --- */
input[type="checkbox"],
input[type="radio"] {
  appearance: none;
  -webkit-appearance: none;
  width: 1.125rem;
  height: 1.125rem;
  border: 1px solid var(--gray-300);
  background-color: var(--white);
  flex-shrink: 0;
  vertical-align: middle;
  cursor: pointer;
  transition: border-color 0.15s, background-color 0.15s, box-shadow 0.15s;
}
input[type="checkbox"] { border-radius: var(--radius-sm); }
input[type="radio"]    { border-radius: var(--radius-full); }

/* The tick, the "some" bar and the select's caret below are ValkOS glyphs (glyph:check, glyph:minimize,
   glyph:caret-down in fragments/glyph-marks.html) as data URIs — the same filled geometry, painted. */
input[type="checkbox"]:checked {
  background-color: var(--navy-500);
  border-color: var(--navy-500);
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3e%3cpolygon fill='%23fff' points='3.5,12.5 6,10 9.5,13.5 18,5 20.5,7.5 9.5,18.5'/%3e%3c/svg%3e");
  background-size: 0.75rem;
  background-position: center;
  background-repeat: no-repeat;
}
/* "Some of them" — a select-all with part of the rows ticked (js/row-select.js, mail-list.js).
   Without its own look the box read as empty: appearance:none drops the native dash. */
input[type="checkbox"]:indeterminate {
  background-color: var(--navy-500);
  border-color: var(--navy-500);
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3e%3cpolygon fill='%23fff' points='5,10.5 19,10.5 19,13.5 5,13.5'/%3e%3c/svg%3e");
  background-size: 0.75rem;
  background-position: center;
  background-repeat: no-repeat;
}
input[type="radio"]:checked {
  border-color: var(--navy-500);
  background: radial-gradient(var(--navy-500) 0 40%, var(--white) 45%);
}

input[type="checkbox"]:hover:not(:disabled),
input[type="radio"]:hover:not(:disabled) {
  border-color: var(--gold-400);
}

input[type="checkbox"]:focus-visible,
input[type="radio"]:focus-visible {
  outline: none;
  border-color: var(--navy-500);
  box-shadow: 0 0 0 3px rgba(34,59,96,0.15);
}

input[type="checkbox"]:disabled,
input[type="radio"]:disabled {
  background-color: var(--gray-100);
  border-color: var(--gray-200);
  cursor: not-allowed;
}
input[type="checkbox"]:checked:disabled {
  background-color: var(--gray-400);
  border-color: var(--gray-400);
}
input[type="radio"]:checked:disabled {
  border-color: var(--gray-400);
  background: radial-gradient(var(--gray-400) 0 40%, var(--gray-100) 45%);
}

/* --- Native selects (design-system styled) --- */
select,
select.form-input,
select.form-control,
select.form-select {
  width: 100%;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  padding: 0.625rem 2.5rem 0.625rem 0.875rem;
  font-size: 0.9375rem;
  color: var(--gray-800);
  background-color: var(--white);
  transition: border-color 0.15s, box-shadow 0.15s;
  appearance: none;
  -webkit-appearance: none;
  cursor: pointer;
  background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3e%3cpolygon fill='%2364748b' points='4,8.5 6.5,6 12,11.5 17.5,6 20,8.5 12,16.5'/%3e%3c/svg%3e");
  background-repeat: no-repeat;
  background-position: right 0.75rem center;
  background-size: 1rem;
}
select:focus,
select.form-input:focus,
select.form-control:focus,
select.form-select:focus {
  outline: none;
  border-color: var(--navy-500);
  box-shadow: 0 0 0 3px rgba(34,59,96,0.15);
}
select:disabled,
select.form-input:disabled,
select.form-control:disabled,
select.form-select:disabled {
  background-color: var(--gray-100);
  border-color: var(--gray-200);
  color: var(--gray-500);
  cursor: not-allowed;
}
select[multiple],
select[size] {
  background-image: none;
  padding-right: 0.875rem;
}

/* A picker that sits in a table row next to buttons. A native select sizes itself
   to its widest OPTION, and on /users/unlinked-drivers every row carries its own
   list of eligible users — so without a fixed width the column jumps from row to
   row and the buttons after it never line up. */
.form-select--fixed {
  width: 14rem;
  max-width: 100%;
}

/* A list narrowed as you type (fragments/live-search.html, static/js/live-search.js): the field
   with the search mark inside its start, and beside it how many of the list are shown. The field
   gives way on a phone (min-width 0 down the chain) instead of panning the page. */
.live-search {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  flex: 1 1 18rem;
  min-width: 0;
}
.live-search-field {
  position: relative;
  flex: 1 1 14rem;
  min-width: 0;
  max-width: 28rem;
}
.live-search-field > .vk-mark {
  position: absolute;
  inset-inline-start: 0.75rem;
  top: 50%;
  transform: translateY(-50%);
  width: 16px;
  height: 16px;
  pointer-events: none;
}
.live-search-field > .form-input { padding-inline-start: 2.25rem; }
/* Never display:none while empty: a live region has to be in the page before its text changes. */
.live-search-status { font-size: 0.875rem; color: var(--gray-500); white-space: nowrap; }
/* [hidden] has to beat the display of a grid, a flex root, an alert and a table row. */
[data-live-search][hidden],
[data-live-search-list][hidden],
[data-live-search-empty][hidden],
[data-search][hidden] { display: none !important; }
/* Without JavaScript the field stays hidden, and so does the row holding it. */
.live-search-row:has(> [data-live-search][hidden]) { display: none; }
/* The table's last row has no bottom border; with rows hidden that is the last one SHOWN. */
[data-live-search-list] .table tbody tr:not([hidden]):not(:has(~ tr:not([hidden]))) td { border-bottom: none; }

/* The language of a generated file, beside its export buttons (fragments/export-language).
   A row member, not a form field: label and select on one line, never the full width a
   native select takes by default, and wrapping with the buttons on a phone. */
.export-language {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  max-width: 100%;
}
.export-language-label {
  font-size: 0.875rem;
  color: var(--gray-600);
  white-space: nowrap;
}
select.export-language-select {
  width: auto;
  min-width: 0;
  max-width: 16rem;
  padding: 0.375rem 2.25rem 0.375rem 0.75rem;
  font-size: 0.875rem;
}
/* With JavaScript the select is hidden and the language is asked for after the click
   (static/js/export-language.js); the span is inline-flex, which would beat [hidden]. */
.export-language[hidden] { display: none; }
.export-language-dialog { width: min(24rem, calc(100% - 2rem)); }
.export-language-choices {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  margin: 0.75rem 0 1rem;
}
.export-language-choice {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  min-height: 2.5rem;
  padding: 0.375rem 0.75rem;
  border: 1px solid var(--gray-200);
  border-radius: 6px;
  cursor: pointer;
}
.export-language-choice:hover { background: var(--gray-50); }
.export-language-choice:has(input:checked) {
  border-color: var(--navy-500);
  background: var(--navy-50);
}
.export-language-choice:has(input:focus-visible) {
  outline: 2px solid var(--navy-500);
  outline-offset: 2px;
}
.export-language-choice input { margin: 0; flex: none; }

.form-label-locked {
  display: flex;
  align-items: center;
  gap: 0.375rem;
}
.form-label-locked .lock-icon {
  display: inline-flex;
  flex-shrink: 0;
  cursor: help;
}
.lock-icon > .vk-mark { width: 14px; height: 14px; }

/* ================================================================
   BUTTONS
   ================================================================ */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  padding: 0.5rem 1rem;
  font-weight: 500;
  font-size: 0.875rem;
  line-height: 1.25rem;
  border-radius: var(--radius);
  border: 1px solid transparent;
  cursor: pointer;
  text-decoration: none;
  /* A .btn is usually a flex item (actions cells, toolbars), and a flex item's
     automatic minimum size is its min-content width — the longest WORD — so a
     squeezed row silently broke "Create user" across two lines. */
  white-space: nowrap;
  transition: background-color 0.15s, box-shadow 0.15s, color 0.15s;
}
.btn:hover  { filter: none; }
.btn:active { transform: translateY(1px); }

.btn-primary   { background-color: var(--navy-500); color: var(--white); }
.btn-primary:hover { background-color: var(--navy-600); }
.btn-secondary { background-color: var(--gray-600); color: var(--white); }
.btn-secondary:hover { background-color: var(--gray-700); }
.btn-success   { background-color: var(--green-600); color: var(--white); }
.btn-success:hover { background-color: #15803d; }
.btn-danger    { background-color: var(--red-600); color: var(--white); }
.btn-danger:hover { background-color: #b91c1c; }
.btn-warning   { background-color: var(--yellow-500); color: var(--gray-800); }
.btn-warning:hover { background-color: #ca8a04; }

.btn-outline {
  background-color: transparent;
  border-color: var(--gray-400);
  color: var(--gray-700);
}
.btn-outline:hover {
  background-color: var(--gray-100);
  border-color: var(--gray-500);
  color: var(--gray-900);
}
.btn-navbar-outline {
  background-color: transparent;
  border-color: rgba(255,255,255,0.35);
  color: var(--gray-100);
}
.btn-navbar-outline:hover {
  background-color: rgba(255,255,255,0.1);
  border-color: rgba(255,255,255,0.55);
  color: var(--white);
}
.btn-outline-primary {
  border-color: var(--navy-500);
  color: var(--navy-500);
  background-color: transparent;
}
.btn-outline-primary:hover {
  background-color: var(--navy-50);
}
.btn-sm { padding: 0.375rem 0.625rem; font-size: 0.8125rem; }
/* Icon-only button: square padding, label lives in title/aria-label. */
.btn-icon { padding: 0.375rem; }
/* A button whose label is a whole sentence ("I confirm there was no activity on those days") may wrap
   it: kept on one line it was wider than a 360 px phone in Polish and Urdu and panned the page. */
.btn-wrap { white-space: normal; text-align: start; }
.btn-wrap .vk-mark-label > .vk-mark { flex-shrink: 0; }

/* ================================================================
   ALERTS
   ================================================================ */
.alert {
  padding: 0.875rem 1rem;
  border-radius: var(--radius);
  margin-bottom: 1rem;
  font-size: 0.875rem;
  font-weight: 500;
  border: 1px solid transparent;
}
.alert-danger  { background-color: var(--red-50); border-color: #fca5a5; color: #991b1b; }
.alert-success { background-color: var(--green-50); border-color: #86efac; color: #166534; }
.alert-info    { background-color: var(--navy-50); border-color: var(--navy-200); color: var(--navy-700); }
.alert-warning { background-color: var(--yellow-50); border-color: #fde68a; color: #854d0e; }
/* AN ALERT LEADS WITH ITS SEVERITY (S14b): its FIRST child is the mark of its variant
   (fragments/mark :: markOf('state:success' | 'state:info' | 'state:warning' | 'state:error')),
   placed at the inline start on the middle of the first line — absolutely, so the content needs no
   wrapper and a flex alert's own layout is untouched. AlertMarkConventionTest holds every template
   to it. A list's bullets hang outside it (the reset zeroes padding), so a list keeps an indent. */
.alert { position: relative; padding-inline-start: calc(1rem + 1.25em + 0.625rem); }
.alert > .vk-mark:first-child {
  position: absolute;
  inset-inline-start: 1rem;
  top: 1rem;
  top: calc(0.875rem + (1lh - 1.25em) / 2);
  width: 1.25em;
  height: 1.25em;
}
.alert-success > .vk-mark:first-child { color: var(--green-600); }
.alert-info    > .vk-mark:first-child { color: var(--navy-500); }
.alert-warning > .vk-mark:first-child { color: #b45309; }
.alert-danger  > .vk-mark:first-child { color: var(--red-600); }
:where(.alert) :is(ul, ol) { padding-inline-start: 1.25em; }
/* A link inside an alert is the way out of what the alert says. The global a rule takes
   colour and underline away, which left such links looking like the sentence around them. */
.alert a[href] { font-weight: 600; text-decoration: underline; text-underline-offset: 2px; }

/* ================================================================
   TABLES
   ================================================================ */
.table-wrapper {
  /* Load-bearing: the wrapper must be the containing block of what is absolutely positioned
     inside it. Without it an absolute box — the sorted header's .sr-only "descending" — takes
     the page as its containing block, escapes the wrapper's scrolling and widens the PAGE: on a
     phone /invoices panned sideways by the full width of the table (868 px on 360). */
  position: relative;
  overflow-x: auto;
  width: 100%;
  border-radius: var(--radius-lg);
  border: 1px solid var(--gray-200);
  box-shadow: var(--shadow);
  background-color: var(--white);
}
.table {
  width: 100%;
  border-collapse: collapse;
  text-align: left;
}
.table th,
.table td {
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--gray-200);
}
.table th {
  background-color: var(--gray-50);
  font-weight: 600;
  font-size: 0.75rem;
  color: var(--gray-500);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.table tbody tr { transition: background-color 0.1s; }
.table tbody tr:hover { background-color: var(--gray-50); }
.table tbody tr:last-child td { border-bottom: none; }

/* ================================================================
   BADGES
   ================================================================ */
.badge {
  display: inline-block;
  padding: 0.125rem 0.625rem;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.025em;
  border-radius: var(--radius-full);
}
.bg-success   { background-color: #dcfce7; color: #166534; }
.bg-warning   { background-color: var(--yellow-50); color: #854d0e; }
.bg-danger    { background-color: var(--red-50); color: #991b1b; }
.bg-info      { background-color: var(--navy-50); color: var(--navy-700); }
.bg-secondary { background-color: var(--gray-100); color: var(--gray-600); }
.bg-purple    { background-color: #f3e8ff; color: #6b21a8; }
.bg-blue      { background-color: #dbeafe; color: #1e40af; }
.bg-gold      { background-color: #fef3c7; color: #92400e; }
/* Four more for task categories (and so for project areas), none of them a status colour. */
.bg-teal      { background-color: #ccfbf1; color: #115e59; }
.bg-orange    { background-color: #ffedd5; color: #9a3412; }
.bg-pink      { background-color: #fce7f3; color: #9d174d; }
.bg-brown     { background-color: #efe7dd; color: #6b4226; }
/* The company owner's badge — deliberately not a BadgeColors name (no category or person picks
   it): the navy of the logo with gold words, the one badge that is dark. */
.bg-owner     { background-color: var(--navy-900); color: var(--gold-300); }
/* A person's role badges in one wrapping row (fragments/role :: rolesOf). */
.role-badges  { display: inline-flex; flex-wrap: wrap; gap: 0.25rem; vertical-align: middle; }
/* A role badge wears its mark (config/RoleMark) before its words, on one line. */
.role-badge { display: inline-flex; align-items: center; gap: 0.3rem; padding-left: 0.375rem; vertical-align: middle; }
.role-badge > .vk-mark { width: 16px; height: 16px; }

/* --- Hidden Username Badge --- */
.badge-hidden-username {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.25rem 0.625rem;
  border-radius: 0.375rem;
  background-color: var(--gray-100);
  color: var(--gray-500);
  font-size: 0.8125rem;
  font-weight: 500;
}
.badge-hidden-username svg {
  flex-shrink: 0;
}
.badge-hidden-username .vk-mark { width: 14px; height: 14px; }

/* ================================================================
   ROLE FORM GROUPS
   ================================================================ */
:root {
  --role-danger: #991b1b;
  --role-gold: #92400e;
  --role-warning: #854d0e;
  --role-success: #166534;
  --role-blue: #1e40af;
  --role-purple: #6b21a8;
  --role-secondary: #475569;
  --role-teal: #115e59;
  --role-owner: var(--navy-700);
}
.role-group-title {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--gray-500);
  margin-bottom: 0.5rem;
}
.role-name { font-weight: 600; }
.role-desc {
  font-size: 0.75rem;
  color: var(--gray-400);
  margin-left: 0.25rem;
}
.role-admin-box {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.625rem 0.75rem;
  border: 1px solid var(--red-50);
  border-left: 3px solid #991b1b;
  border-radius: var(--radius);
  background-color: #fef2f2;
}
.role-admin-box .role-desc {
  margin-left: auto;
}
/* The owner's box leads the administrative group: the logo's gold edge on a pale gold ground. */
.role-owner-box {
  border-color: var(--gold-100);
  border-left-color: var(--gold-400);
  background-color: var(--gold-50);
}
/* ADMIN held ticked while OWNER is ticked: it reads as part of the owner's grant. */
.role-admin-box.role-held { opacity: 0.6; }
.role-admin-box.role-held input { cursor: not-allowed; }
.role-admin-confirm {
  margin-top: 0.375rem;
  padding: 0.375rem 0.75rem;
  font-size: 0.8125rem;
  font-weight: 500;
  color: #991b1b;
  background-color: #fef2f2;
  border-radius: var(--radius);
}

/* ================================================================
   DESCRIPTION LIST (detail views)
   ================================================================ */
.dl-grid {
  display: grid;
  grid-template-columns: 10rem 1fr;
  gap: 0.75rem 1.5rem;
  align-items: baseline;
}
.dl-dt {
  font-weight: 600;
  font-size: 0.875rem;
  color: var(--gray-500);
}
.dl-dd {
  margin: 0;
  color: var(--gray-800);
  font-size: 0.9375rem;
  /* An e-mail address or an external id is one unbreakable word; at ≤640px the
     grid is a single 1fr column, so without this it widens the card past the phone. */
  overflow-wrap: anywhere;
}
@media (max-width: 640px) {
  .dl-grid { grid-template-columns: 1fr; gap: 0.25rem 0; }
  .dl-dt { margin-bottom: 0; }
  .dl-dd { margin-bottom: 0.75rem; }
}

/* --- One vehicle line in a platform card (fragments/vehicle-line.html) ---
   The plate first (a link to the fleet vehicle when it is one of the company's), make/
   model beside it, several stacked when a platform assigns more than one. */
.linked-vehicle {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  flex-wrap: wrap;
}
.linked-vehicle + .linked-vehicle { margin-top: 0.25rem; }
.vehicle-model { color: var(--gray-500); }

/* --- Proportional star rating (fragments/rating-stars.html) ---
   The two layers of .totp-countdown / .totp-countdown-bar further down, applied to a
   star silhouette: a grey strip, and a gold copy clipped to the value's percentage.

   Deliberately NOT inline-flex. .dl-grid aligns its cells on the baseline, and a flex
   container whose items are centred hands the grid a baseline synthesized from its
   bottom edge — the number would then ride visibly high against the label in the other
   column. Left in normal flow, .dl-dd keeps its own text baseline and lines up for
   free; vertical-align drops the strip onto the digits' cap band.

   .rating-stars must never get padding: the fill's percentage width resolves against
   its padding box, which would silently desync the clip from the stars.

   The 0% fallback is load-bearing, not defensive habit. A missing or malformed value
   (a comma decimal separator under pl/no is the realistic way) makes the declaration
   invalid, width falls back to auto, and an absolutely positioned box with left:0
   shrink-to-fits to the full 70px — so the failure mode without it is five gold stars
   for everyone, which looks perfectly fine. */
.rating-stars {
  position: relative;
  display: inline-block;
  margin-right: 0.25rem;
  vertical-align: -0.125em;
  color: var(--gray-300);
}
.rating-stars svg { display: block; }
.rating-stars-fill {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: var(--rating-fill, 0%);
  overflow: hidden;
  color: var(--gold-400);
}

/* Label/value pairs in as many columns as fit. Deliberately auto-fit rather
   than a viewport media query: what constrains this grid is the width of the
   column beside the vehicle photo (viewport minus sidebar, padding and photo),
   and any viewport threshold that reads well with the sidebar collapsed
   overflows with it open. */
.spec-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 0.75rem 1.5rem;
}
.spec-grid > div {
  display: grid;
  grid-template-columns: 6.5rem 1fr;
  gap: 0 0.75rem;
  align-items: baseline;
}
@media (max-width: 480px) {
  .spec-grid > div { grid-template-columns: 1fr; }
}
/* Label OVER value at every width — the ≤480px arrangement above, for a section
   whose labels are whole phrases ("Next inspection (EU-kontroll)", longer in
   Polish), which the 6.5rem label column broke over three lines while baseline
   alignment pinned the value to the first. The 20rem floor puts two tiles side by
   side only where such a label still fits on one line; min() keeps a lone tile
   inside a phone-width card. The row gap is the Notes paragraph's mb-1. */
.spec-grid--stacked {
  grid-template-columns: repeat(auto-fit, minmax(min(20rem, 100%), 1fr));
}
.spec-grid--stacked > div {
  grid-template-columns: 1fr;
  gap: 0.25rem 0;
}
/* Horizontal rule inside a card — the .photo-candidates-block idiom, named for
   reuse. */
.card-section {
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--gray-200);
}

/* ================================================================
   PAGE-SPECIFIC HELPERS
   ================================================================ */
.page-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 1rem;
  margin-bottom: 1.5rem;
}
.page-title {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--gray-900);
}
.page-title-row {
  display: inline-flex;
  align-items: center;
  gap: 0.625rem;
}

/* THE PERSON'S HEADER (users/view.html, 2026-10-22, decided with the user): back, the photo and the
   name, and every action, sticking under the navbar on the page's own ground and reaching the
   column's edges (.container pads 1.5rem), with a shadow once stuck (app.js, [data-sticky-head]).
   ONE HEIGHT, never shrinking on scroll: a header that changes height as it sticks fights the
   browsers' scroll anchoring — the page jumps, and at the edge it flickers. Narrower, the buttons
   keep their marks alone (the words stay for a screen reader and in the title), and on a phone
   Profile and the driver report move into "More". z-index 60: over the timeline's tip (30), under
   the rail (100), the plate's card (240) and the navbar (250). */
.person-head {
  position: sticky;
  top: 4rem;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 0.5rem 1rem;
  margin: -0.75rem -1.5rem 1.25rem;
  padding: 0.75rem 1.5rem;
  background: var(--gray-100);
  container-type: inline-size;
  container-name: person-head;
  transition: box-shadow 0.15s ease;
}
.person-head.is-stuck { box-shadow: 0 10px 12px -12px rgb(15 23 42 / 0.35); }
.person-head-back { width: 2.25rem; height: 2.25rem; margin-inline-start: -0.5rem; }
.person-head-back > .vk-mark { width: 1.25rem; height: 1.25rem; }
.person-head-name {
  display: flex;
  align-items: center;
  gap: 0.5rem 0.75rem;
  flex: 1 1 auto;
  min-width: 0;
  margin: 0;
}
.person-head-photo {
  flex: none;
  width: 3.5rem;
  height: 3.5rem;
  border: 2px solid var(--white);
  box-shadow: 0 0 0 1px var(--gray-200);
}
.person-head-name.is-blocked .person-head-photo { filter: grayscale(1); opacity: 0.6; }
.person-head-text { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.person-head-name .expectation-flag { flex: none; }
.person-head-name .expectation-flag .vk-mark { width: 1.25rem; height: 1.25rem; }
.person-head-badge { flex: none; font-size: 0.75rem; }
.person-head-actions { flex: none; justify-content: flex-end; }
.action-menu-panel .action-menu-item--narrow { display: none; }
@container person-head (max-width: 56rem) {
  .person-head-actions > .btn { padding-inline: 0.625rem; }
  .person-head-actions > .btn > .vk-mark-label { gap: 0; }
  .person-head-actions > .btn > .vk-mark-label > span {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
  }
}
@container person-head (max-width: 30rem) {
  .person-head-name { gap: 0.5rem; font-size: 1.125rem; }
  .person-head-photo { width: 2.5rem; height: 2.5rem; }
  .person-head-actions:has(.action-menu) > .person-head-in-menu { display: none; }
  .person-head-actions .action-menu-panel .action-menu-item--narrow { display: flex; }
  .person-head-actions .action-menu-trigger { padding-inline: 0.625rem; gap: 0; }
  .person-head-actions .action-menu-trigger > .action-menu-words {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
  }
}
/* A link scrolled to (an anchor, a focused timeline item) stops under the header, not behind it;
   Leaflet's panes (z-index 400) are kept inside the map, or they would paint over the header. */
html:has(.person-head) { scroll-padding-top: 9.5rem; }
#location-map { isolation: isolate; }

/* A MENU OF ACTIONS (details.action-menu; users/view.html's "More"): a disclosure, not a role=menu —
   Tab walks its items, and app.js closes it on a click elsewhere and on Escape (the focus back on
   its button); without JavaScript the browser's own <details> still opens and closes it. The items
   are links or the submit buttons of their own forms, one row each with the action's mark; the
   dangerous ones come last, under a rule, in red. */
.action-menu { position: relative; }
.action-menu > summary { list-style: none; }
.action-menu > summary::-webkit-details-marker { display: none; }
.action-menu-caret { display: inline-flex; transition: transform 0.2s ease; }
.action-menu[open] .action-menu-caret { transform: rotate(180deg); }
.action-menu[open] > .btn-outline { background-color: var(--gray-100); border-color: var(--gray-500); color: var(--gray-900); }
.action-menu-panel {
  position: absolute;
  top: calc(100% + 0.375rem);
  inset-inline-end: 0;
  z-index: 1;
  min-width: 15rem;
  max-width: calc(100vw - 2rem);
  padding: 0.375rem;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
.action-menu-panel form { margin: 0; }
.action-menu-item {
  display: flex;
  align-items: center;
  width: 100%;
  min-height: 2.5rem;
  padding: 0.5rem 0.75rem;
  border: 0;
  border-radius: var(--radius);
  background: none;
  color: var(--gray-800);
  font: inherit;
  font-size: 0.875rem;
  text-align: start;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
}
.action-menu-item:hover { background: var(--gray-100); color: var(--gray-900); }
.action-menu-item:focus-visible { outline: 2px solid var(--navy-400); outline-offset: -2px; }
.action-menu-item.is-danger { color: var(--red-600); }
.action-menu-item.is-danger:hover { background: var(--red-50); color: #b91c1c; }
.action-menu-item.is-success { color: var(--green-600); }
.action-menu-item.is-success:hover { background: var(--green-50); }
.action-menu-sep { margin: 0.375rem 0.25rem; border: 0; border-top: 1px solid var(--gray-200); }

/* A CARD FOLDED UNDER ITS TITLE (details.card.card-fold; the cost cards of users/view.html, closed by
   default — decided with the user on 2026-10-22): closed, the title, what the card adds up to (cut
   with "…") and a caret; opened, the body, and the summary line gives way to it. On a phone the
   summary goes under the title. */
.card.card-fold { padding: 0; }
.card-fold > summary { list-style: none; }
.card-fold > summary::-webkit-details-marker { display: none; }
.card-fold-summary {
  display: grid;
  grid-template-columns: minmax(0, auto) minmax(0, 1fr) auto;
  grid-template-areas: "title hint chevron";
  align-items: center;
  column-gap: 1rem;
  min-height: 3.5rem;
  padding: 0.875rem 1.5rem;
  border-radius: var(--radius-lg);
  cursor: pointer;
}
.card-fold[open] > .card-fold-summary { border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
.card-fold-summary:hover { background: var(--gray-50); }
.card-fold-summary:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 2px; }
.card-fold-title { grid-area: title; margin: 0; }
.card-fold-hint {
  grid-area: hint;
  justify-self: end;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.875rem;
  color: var(--gray-600);
  font-variant-numeric: tabular-nums;
}
.card-fold-figure { font-weight: 600; color: var(--gray-800); }
.card-fold-chevron { grid-area: chevron; display: inline-flex; color: var(--gray-500); transition: transform 0.2s ease; }
.card-fold[open] .card-fold-chevron { transform: rotate(180deg); }
.card-fold[open] .card-fold-hint { display: none; }
.card-fold-body { padding: 0.25rem 1.5rem 1.5rem; }
@media (max-width: 640px) {
  .card-fold-summary {
    grid-template-columns: minmax(0, 1fr) auto;
    grid-template-areas: "title chevron" "hint hint";
    row-gap: 0.25rem;
  }
  .card-fold-hint { justify-self: start; }
}

/* The account screen's facts in two columns where there is room: who the person is here, and how to
   reach them. */
.person-facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 24rem), 1fr));
  gap: 0.75rem 2.5rem;
  align-items: start;
}



/* ================================================================
   DETAIL LIST (key-value metadata)
   ================================================================ */
.detail-list {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0;
  margin: 0;
}
.detail-row {
  display: contents;
}
.detail-row dt {
  grid-column: 1;
  color: var(--gray-500);
  font-size: 0.875rem;
  font-weight: 500;
  padding: 0.375rem 1.5rem 0.375rem 0;
  white-space: nowrap;
}
.detail-row dd {
  grid-column: 2;
  color: var(--gray-800);
  font-size: 0.875rem;
  padding: 0.375rem 0;
  margin: 0;
}

/* ================================================================
   TOOLTIP (native title with improved cursor)
   ================================================================ */
.tooltip-cell { cursor: help; }
.row-disabled { opacity: 0.5; }
.row-clickable,
tr[data-row-href] { cursor: pointer; }
.row-clickable:hover,
tr[data-row-href]:hover { background: var(--gray-50, #f8fafc); }

/* ================================================================
   LOGIN PAGE — standalone centered layout
   ================================================================ */
.login-page {
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: linear-gradient(135deg, var(--navy-900) 0%, var(--navy-700) 100%);
  background-attachment: fixed;
  padding: 1.5rem;
  overflow-y: auto;
}

@media (max-height: 500px) {
  .login-page {
    align-items: flex-start;
    padding-top: 2rem;
    padding-bottom: 2rem;
  }
}

.login-card {
  background-color: var(--white);
  border-radius: var(--radius-xl);
  box-shadow: var(--shadow-lg);
  padding: 2.5rem 2rem;
  width: 100%;
  max-width: 24rem;
}

.login-header {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 2rem;
}

.login-logo {
  height: 5rem;
  width: auto;
}

.login-brand {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--navy-800);
  letter-spacing: -0.025em;
}

.login-brand-erp {
  font-size: 0.9rem;
  font-weight: 500;
  color: var(--navy-400);
  letter-spacing: 0.05em;
  vertical-align: middle;
  margin-left: 0.15em;
}

.login-ai-badge {
  width: 100%;
  border-top: 1px solid var(--gray-200);
  padding-top: 0.65rem;
  margin-top: 0.15rem;
  text-align: center;
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.13em;
  text-transform: none;
  color: var(--gold-500);
}

/* --- Navbar Dropdown --- */
.nav-dropdown {
  position: relative;
}
.nav-dropdown-toggle {
  cursor: pointer;
}
.nav-dropdown-toggle::after {
  content: "\25BE";
  margin-left: 0.25rem;
  font-size: 0.75rem;
}
.nav-dropdown-menu {
  list-style: none;
}
.nav-dropdown-header {
  padding: 0.375rem 1rem;
  font-size: 0.6875rem;
  font-weight: 700;
  color: var(--gold-400);
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
@media (min-width: 769px) {
  .nav-dropdown-menu {
    display: none;
    position: absolute;
    top: 100%;
    left: 0;
    min-width: 12rem;
    background: var(--navy-800);
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: var(--radius);
    padding: 0.5rem 0;
    z-index: 100;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
  }
  .nav-dropdown:hover .nav-dropdown-menu {
    display: block;
  }
  .nav-dropdown-menu .nav-link {
    display: block;
    padding: 0.375rem 1rem;
    font-size: 0.8125rem;
    border-radius: 0;
  }
}

/* --- Account Dropdown (avatar + username) --- */
.nav-account {
  position: relative;
  /* On top of the 0.5rem .nav-right gap, so the avatar reads as belonging to the
     username next to it rather than to the company switcher on its left. */
  margin-left: 0.5rem;
}
.nav-account-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--gray-400);
  padding: 0.25rem 0.5rem;
  border-radius: var(--radius);
  transition: background-color 0.15s, color 0.15s;
}
.nav-account-toggle:hover {
  background-color: rgba(255,255,255,0.08);
  color: var(--white);
}
/* The span keeps .nav-user for its type styling, but its own horizontal padding
   would re-open the very gap the toggle's gap exists to control. */
.nav-account-toggle .nav-user {
  padding: 0;
}
.nav-account-chevron {
  flex-shrink: 0;
  display: inline-flex;
  opacity: 0.7;
  transition: transform 0.15s;
}
.nav-account-chevron > .vk-mark,
.nav-company-chevron > .vk-mark { width: 12px; height: 12px; }
.nav-account-menu {
  list-style: none;
  /* It scrolls when taller than the window: a thin light thumb on its navy, never the default
     white track. */
  scrollbar-width: thin;
  scrollbar-color: rgba(255,255,255,0.25) transparent;
}
/* The menu's groups: personal settings, the viewer's own work, the SUPERADMIN's
   administration screens, logout. */
.nav-account-divider {
  height: 1px;
  background: rgba(255,255,255,0.12);
  margin: 0.375rem 0;
  list-style: none;
  padding: 0;
  border: 0;
}
/* A group's heading: words alone, like a rail's section title. */
.nav-account-heading {
  padding: 0.25rem 1rem 0.125rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--gray-500);
}
/* "My tasks" says how many the viewer owes (task-badge.js): the bell badge's gold
   pill, but in the row, at its end. */
.nav-menu-count {
  margin-inline-start: auto;
  min-width: 1.125rem;
  height: 1.125rem;
  padding: 0 0.3rem;
  border-radius: 9999px;
  background: var(--accent);
  /* Navy on gold: white words on the gold pill read at about 2:1. */
  color: var(--navy-900);
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.125rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.nav-menu-count[hidden] { display: none; }
/* Logout is a CSRF-protected POST, so it is a <button> that has to pass for a
   menu row — same trick as .nav-company-item. */
.nav-account-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  background: none;
  border: none;
  cursor: pointer;
  text-align: left;
  color: var(--gray-300);
  font-family: inherit;
  padding: 0.375rem 1rem;
  font-size: 0.8125rem;
}
.nav-account-item:hover {
  background-color: rgba(255,255,255,0.08);
  color: var(--white);
}
.nav-account-item svg {
  flex-shrink: 0;
  opacity: 0.85;
}
@media (min-width: 769px) {
  .nav-account-menu {
    display: none;
    position: absolute;
    top: 100%;
    right: 0;
    min-width: 12rem;
    /* Profile, tasks, absences, payroll and a SUPERADMIN's administration: on a short
       window the menu scrolls rather than running off the bottom of the screen. */
    max-height: calc(100vh - 4.5rem);
    overflow-y: auto;
    background: var(--navy-800);
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: var(--radius);
    padding: 0.5rem 0;
    z-index: 100;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
  }
  .nav-account:hover .nav-account-menu {
    display: block;
  }
  .nav-account:hover .nav-account-chevron {
    transform: rotate(180deg);
  }
  .nav-account-menu .nav-link {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 1rem;
    font-size: 0.8125rem;
    border-radius: 0;
  }
  .nav-account-menu .nav-link svg {
    flex-shrink: 0;
    opacity: 0.85;
  }
}

@media (max-width: 768px) {
  /* .nav-right is a centred, wrapping footer row down here — the desktop offset
     from the company switcher would just look lopsided. */
  .nav-account {
    margin-left: 0;
  }
  .nav-account:hover .nav-account-menu {
    display: none;
  }
  .nav-account.open .nav-account-menu {
    display: block;
  }
  .nav-account.open .nav-account-chevron {
    transform: rotate(180deg);
  }
  /* Opens upward: .nav-right sits at the bottom of the mobile overlay, so a
     downward menu would be clipped. */
  .nav-account-menu {
    display: none;
    position: absolute;
    bottom: 100%;
    top: auto;
    right: 0;
    left: auto;
    min-width: 12rem;
    /* It rises from the bottom of the overlay: never past the top of the screen. */
    max-height: calc(100dvh - 9rem);
    overflow-y: auto;
    background: var(--navy-800);
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: var(--radius);
    padding: 0.5rem 0;
    z-index: 100;
    box-shadow: 0 -4px 12px rgba(0,0,0,0.3);
    margin-bottom: 0.25rem;
  }
  .nav-account-menu .nav-link,
  .nav-account-item {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.75rem 1rem;
    font-size: 0.9375rem;
  }
  .nav-account-menu .nav-link svg,
  .nav-account-item svg {
    flex-shrink: 0;
    opacity: 0.85;
  }
}

/* --- Mail dropdown in the navbar (the envelope; its menu lists the mailboxes) --- */
.nav-mail {
  position: relative;
}
.nav-mail-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--gray-400);
  padding: 0.5rem;
  border-radius: var(--radius);
  transition: background-color 0.15s, color 0.15s;
}
.nav-mail-toggle:hover {
  background-color: rgba(255,255,255,0.08);
  color: var(--white);
}
.nav-mail-toggle svg {
  display: block;
}
.nav-mail-menu {
  list-style: none;
}
@media (min-width: 769px) {
  .nav-mail-menu {
    display: none;
    position: absolute;
    top: 100%;
    right: 0;
    min-width: 11rem;
    background: var(--navy-800);
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: var(--radius);
    padding: 0.5rem 0;
    z-index: 100;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
  }
  .nav-mail:hover .nav-mail-menu {
    display: block;
  }
  .nav-mail-menu .nav-link {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.375rem 1rem;
    font-size: 0.8125rem;
    border-radius: 0;
  }
  .nav-mail-menu .nav-link svg {
    flex-shrink: 0;
    opacity: 0.85;
  }
}
@media (max-width: 768px) {
  .nav-mail-toggle {
    padding: 0.75rem 0.5rem;
  }
  .nav-mail:hover .nav-mail-menu {
    display: none;
  }
  .nav-mail.open .nav-mail-menu {
    display: block;
  }
  .nav-mail-menu {
    display: none;
    position: absolute;
    bottom: 100%;
    top: auto;
    right: auto;
    left: 0;
    min-width: 10rem;
    background: var(--navy-800);
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: var(--radius);
    padding: 0.5rem 0;
    box-shadow: 0 -4px 12px rgba(0,0,0,0.3);
    margin-bottom: 0.25rem;
  }
  .nav-mail-menu .nav-link {
    display: flex;
    align-items: center;
    gap: 0.625rem;
    padding: 0.75rem 1rem;
    font-size: 0.9375rem;
  }
  .nav-mail-menu .nav-link svg {
    flex-shrink: 0;
    opacity: 0.85;
  }
}

/* --- Company Switcher --- */
.nav-company {
  position: relative;
}
.nav-company-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  background: rgba(255,255,255,0.06);
  border: 1px solid rgba(255,255,255,0.1);
  cursor: pointer;
  color: var(--gold-300);
  padding: 0.375rem 0.625rem;
  border-radius: var(--radius);
  font-size: 0.8125rem;
  font-weight: 500;
  transition: background-color 0.15s, border-color 0.15s;
  white-space: nowrap;
  max-width: min(14rem, 100%);        /* follows .nav-company when the bar shrinks it */
  min-width: 0;
}
.nav-company-toggle:hover {
  background-color: rgba(255,255,255,0.1);
  border-color: rgba(255,255,255,0.2);
}
.nav-company-toggle svg {
  flex-shrink: 0;
}
.nav-company-name {
  overflow: hidden;
  text-overflow: ellipsis;
  min-width: 0;
}
.nav-company-chevron {
  flex-shrink: 0;
  display: inline-flex;
  transition: transform 0.15s;
}
.nav-company-menu {
  list-style: none;
  padding: 0;
  margin: 0;
}
.nav-company-item {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--gray-300);
  padding: 0.375rem 1rem;
  font-size: 0.8125rem;
  transition: background-color 0.15s, color 0.15s;
  /* A company's name is ONE line (2026-10-14, the user): one too long for the menu ends in "…",
     its title carrying it whole. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.nav-company-item:hover {
  background-color: rgba(255,255,255,0.08);
  color: var(--white);
}
@media (min-width: 769px) {
  .nav-company-menu {
    display: none;
    position: absolute;
    top: 100%;
    right: 0;
    /* Its content's width, not the toggle's: shrink-to-fit inside the narrow .nav-company would
       stop at the min-width and wrap every longer name. Anchored right, it grows leftwards. */
    width: max-content;
    min-width: 12rem;
    max-width: min(24rem, calc(100vw - 2rem));
    background: var(--navy-800);
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: var(--radius);
    padding: 0.75rem 0 0.5rem;
    z-index: 100;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
  }
  .nav-company:hover .nav-company-menu {
    display: block;
  }
  .nav-company:hover .nav-company-chevron {
    transform: rotate(180deg);
  }
}
@media (max-width: 768px) {
  .nav-company {
    width: 100%;
    margin-bottom: 0.25rem;
  }
  .nav-company-toggle {
    width: 100%;
    justify-content: center;
    max-width: none;
  }
  .nav-company:hover .nav-company-menu {
    display: none;
  }
  .nav-company.open .nav-company-menu {
    display: block;
  }
  .nav-company.open .nav-company-chevron {
    transform: rotate(180deg);
  }
  .nav-company-menu {
    display: none;
    position: absolute;
    top: 100%;
    bottom: auto;
    right: auto;
    left: 0;
    width: 100%;
    margin-top: 0.25rem;
    background: var(--navy-800);
    border: 1px solid rgba(255,255,255,0.1);
    border-radius: var(--radius);
    padding: 0.5rem 0 0.75rem;
    box-shadow: 0 -4px 12px rgba(0,0,0,0.3);
  }
  .nav-company-item {
    padding: 0.75rem 1rem;
    font-size: 0.9375rem;
  }
}

/* --- Company Selection Page --- */
.company-select-list {
  max-width: 28rem;
  margin: 0 auto;
}
.company-select-card {
  padding: 0;
  overflow: hidden;
}
.company-select-btn {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  background: none;
  border: none;
  cursor: pointer;
  padding: 1rem 1.25rem;
  color: var(--gray-800);
  transition: background-color 0.15s;
  text-align: left;
}
.company-select-btn:hover {
  background-color: rgba(0,0,0,0.04);
}
.company-select-info {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
}
.company-select-name {
  font-weight: 600;
  font-size: 1rem;
}
.company-select-arrow {
  color: var(--gray-500);
  flex-shrink: 0;
}

/* --- Notifications: colour by SEVERITY ---
   A notification is coloured by how much it matters (NotificationSeverity.cssClass(): a tint
   and a bar), never by its recipient's role — ADMIN's badge colour is red, which on a row
   would read as "critical". The role is a chip in its own colour (notifications/parts ::
   chips). Text colours are the dark end of each hue so they stay readable on the tint; the
   bell's menu is navy-800, where only the light end shows. */
:root {
  --sev-critical-bar: var(--red-600);
  --sev-critical-text: #b91c1c;
  --sev-critical-bg: var(--red-50);
  --sev-critical-on-dark: #f87171;
  --sev-warning-bar: #f59e0b;
  --sev-warning-text: #b45309;
  --sev-warning-bg: #fffbeb;
  --sev-warning-on-dark: #fbbf24;
  --sev-info-bar: var(--navy-400);
  --sev-info-text: var(--navy-500);
  --sev-info-bg: #f4f7fb;
  --sev-info-on-dark: #93c5fd;
  --sev-success-bar: var(--green-600);
  --sev-success-text: #15803d;
  --sev-success-bg: var(--green-50);
  --sev-success-on-dark: #4ade80;
}
.sev-critical { --sev-bar: var(--sev-critical-bar); --sev-text: var(--sev-critical-text); --sev-bg: var(--sev-critical-bg); --sev-on-dark: var(--sev-critical-on-dark); }
.sev-warning  { --sev-bar: var(--sev-warning-bar);  --sev-text: var(--sev-warning-text);  --sev-bg: var(--sev-warning-bg);  --sev-on-dark: var(--sev-warning-on-dark); }
.sev-info     { --sev-bar: var(--sev-info-bar);     --sev-text: var(--sev-info-text);     --sev-bg: var(--sev-info-bg);     --sev-on-dark: var(--sev-info-on-dark); }
.sev-success  { --sev-bar: var(--sev-success-bar);  --sev-text: var(--sev-success-text);  --sev-bg: var(--sev-success-bg);  --sev-on-dark: var(--sev-success-on-dark); }

/* --- Notification Bell Dropdown --- */
.nav-notifications {
  position: relative;
}
.nav-notifications-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  position: relative;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--gray-400);
  padding: 0.5rem;
  border-radius: var(--radius);
  transition: background-color 0.15s, color 0.15s;
  text-decoration: none;
}
.nav-notifications-toggle:hover {
  background-color: rgba(255,255,255,0.08);
  color: var(--white);
}
.nav-notifications-toggle svg {
  display: block;
}
.nav-notification-badge {
  position: absolute;
  top: 0.15rem;
  left: calc(100% - 1rem);
  min-width: 1rem;
  height: 1rem;
  padding: 0 0.25rem;
  font-size: 0.625rem;
  font-weight: 700;
  line-height: 1rem;
  text-align: center;
  color: var(--white);
  background: var(--accent);
  border-radius: 9999px;
  pointer-events: none;
}
.nav-notification-menu {
  display: none;
  position: absolute;
  top: 100%;
  right: 0;
  width: 23rem;
  max-width: calc(100vw - 2rem);
  background: var(--navy-800);
  border: 1px solid rgba(255,255,255,0.1);
  border-radius: var(--radius);
  padding: 0;
  list-style: none;
  z-index: 100;
  box-shadow: 0 4px 12px rgba(0,0,0,0.3);
  overflow: hidden;
}
.nav-notifications:hover .nav-notification-menu,
.nav-notifications:focus-within .nav-notification-menu {
  display: block;
}
.notif-dropdown-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.5rem 0.875rem;
  border-bottom: 1px solid rgba(255,255,255,0.08);
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--gray-300);
}
.notif-readall-btn {
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--accent);
  white-space: nowrap;
}
.notif-readall-btn:hover { text-decoration: underline; }
.notif-readall-btn[hidden] { display: none; }
.nav-notification-list {
  max-height: 28rem;
  overflow-y: auto;
  /* A light default scrollbar glares on the navy menu (Firefox draws one at five rows). */
  scrollbar-width: thin;
  scrollbar-color: rgba(255,255,255,0.25) transparent;
}
/* One row: a link to the notification (icon, subject, preview) and, when the reader may
   open it, the action link beside the time — two links side by side, never nested. */
.nav-notification-item {
  display: flex;
  gap: 0.625rem;
  align-items: flex-start;
  padding: 0.625rem 0.875rem 0.625rem calc(0.875rem - 3px);
  border-left: 3px solid var(--sev-on-dark, transparent);
  border-bottom: 1px solid rgba(255,255,255,0.05);
  color: var(--gray-300);
  transition: background-color 0.12s;
}
.nav-notification-item:hover { background-color: rgba(255,255,255,0.06); }
.nav-notification-item.unread { background-color: rgba(255,255,255,0.035); }
.notif-item-icon {
  flex: none;
  margin-top: 0.1rem;
  color: var(--sev-on-dark, var(--gray-400));
}
.notif-item-body {
  flex: 1;
  min-width: 0;
}
.notif-item-link {
  display: block;
  color: inherit;
  text-decoration: none;
}
.notif-item-link:hover .notif-subject { color: var(--white); text-decoration: underline; }
.nav-notification-item.unread .notif-subject {
  font-weight: 600;
  color: var(--white);
}
.notif-subject {
  display: block;
  font-size: 0.8125rem;
  font-weight: 500;
  line-height: 1.3;
  margin-bottom: 0.125rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.notif-preview {
  display: block;
  font-size: 0.6875rem;
  color: var(--gray-500);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.notif-item-meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: 0.25rem;
  font-size: 0.6875rem;
  color: var(--gray-500);
}
.notif-item-action {
  font-weight: 600;
  color: var(--sev-on-dark, var(--accent));
  text-decoration: none;
  white-space: nowrap;
}
.notif-item-action:hover { text-decoration: underline; }
.nav-notification-empty {
  padding: 1.5rem 0.875rem;
  font-size: 0.8125rem;
  color: var(--gray-500);
  text-align: center;
}
.nav-notification-viewall {
  display: block;
  padding: 0.5rem 0.875rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--accent);
  text-decoration: none;
  transition: background-color 0.12s;
  white-space: nowrap;
}
.nav-notification-viewall:hover {
  background-color: rgba(255,255,255,0.04);
}
.notif-dropdown-footer {
  border-top: 1px solid rgba(255,255,255,0.08);
}
.notif-permission-btn {
  display: none;
  align-items: center;
  gap: 0.4rem;
  width: 100%;
  padding: 0.45rem 0.875rem;
  background: none;
  border: none;
  border-bottom: 1px solid rgba(255,255,255,0.06);
  cursor: pointer;
  color: var(--red-400, #f87171);
  font-size: 0.72rem;
  text-align: left;
}
.notif-permission-btn > .vk-mark { width: 14px; height: 14px; flex: none; }
.notif-permission-btn:hover {
  background: rgba(255,255,255,0.04);
}
.notif-footer-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
}
.notif-sound-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--gray-400);
  padding: 0.45rem 0.875rem;
  transition: color 0.15s;
  flex-shrink: 0;
}
.notif-sound-btn:hover {
  color: var(--white);
}
.notif-sound-btn.sound-muted {
  color: var(--gray-600);
}

@media (max-width: 768px) {
  .nav-notifications {
    display: none;
  }
}

/* --- Notification Pages --- */
.notif-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  margin-bottom: 1rem;
}
.notif-toolbar .notif-page-controls { margin-bottom: 0; }
.notif-filters {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  border-bottom: 1px solid var(--gray-200);
}
.notif-filter {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.5rem 0.75rem;
  margin-bottom: -1px;
  border-bottom: 2px solid transparent;
  color: var(--gray-600);
  font-size: 0.875rem;
  font-weight: 500;
  text-decoration: none;
}
.notif-filter:hover { color: var(--navy-700); }
.notif-filter.active {
  color: var(--navy-700);
  border-bottom-color: var(--accent);
  font-weight: 600;
}
.notif-filter-count {
  min-width: 1.25rem;
  padding: 0 0.4rem;
  border-radius: var(--radius-full);
  background: var(--gray-100);
  color: var(--gray-600);
  font-size: 0.75rem;
  text-align: center;
}
.notif-empty {
  padding: 2.5rem 1rem;
  text-align: center;
  border: 1px dashed var(--gray-300);
  border-radius: var(--radius);
  background: var(--white);
}
.notif-cards {
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
}
.notif-card {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 0.875rem;
  padding: 0.875rem 1rem 0.875rem calc(1rem - 4px);
  background: var(--sev-bg, var(--white));
  border: 1px solid var(--gray-200);
  border-left: 4px solid var(--sev-bar, var(--gray-300));
  border-radius: var(--radius);
}
.notif-card.unread { box-shadow: var(--shadow-sm, 0 1px 2px rgba(0,0,0,0.06)); }
.notif-card-icon,
.notif-detail-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 50%;
  background: var(--white);
  color: var(--sev-text, var(--navy-500));
  border: 1px solid var(--gray-200);
}
.notif-card-main { min-width: 0; }
.notif-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}
.notif-sev {
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--sev-text, var(--gray-600));
}
.notif-card-time {
  margin-left: auto;
  font-size: 0.75rem;
  color: var(--gray-500);
}
.notif-card-subject {
  display: block;
  margin: 0.125rem 0 0.25rem;
  font-weight: 500;
  color: var(--navy-900);
  text-decoration: none;
  overflow-wrap: anywhere;
}
.notif-card-subject:hover { text-decoration: underline; }
.notif-card.unread .notif-card-subject { font-weight: 700; }
.notif-card-text {
  margin: 0 0 0.5rem;
  color: var(--gray-700);
  font-size: 0.875rem;
  line-height: 1.45;
  white-space: pre-line;
  overflow-wrap: anywhere;
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.notif-card-actions {
  align-self: center;
  justify-content: flex-end;
}
.notif-card-actions form { margin: 0; }
.notif-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem;
}
.notif-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.1rem 0.5rem;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-full);
  background: var(--white);
  color: var(--gray-600);
  font-size: 0.6875rem;
  font-weight: 600;
  white-space: nowrap;
}
.notif-chip-open {
  border-color: var(--sev-bar, var(--gray-300));
  color: var(--sev-text, var(--gray-600));
}
.notif-unread-dot {
  display: inline-block;
  width: 0.5rem;
  height: 0.5rem;
  background: var(--accent);
  border-radius: 50%;
  vertical-align: middle;
}
@media (max-width: 640px) {
  .notif-card { grid-template-columns: auto minmax(0, 1fr); }
  .notif-card-actions {
    grid-column: 1 / -1;
    justify-content: flex-start;
  }
}

.notif-page-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 2fr);
  gap: 1.5rem;
  min-height: 50vh;
}
.notif-sidebar {
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--white);
}
.notif-sidebar-list {
  list-style: none;
  padding: 0;
  margin: 0;
  max-height: 70vh;
  overflow-y: auto;
}
.notif-sidebar-item {
  display: block;
  padding: 0.75rem 1rem 0.75rem calc(1rem - 3px);
  text-decoration: none;
  color: var(--navy-900);
  border-bottom: 1px solid var(--gray-100);
  border-left: 3px solid var(--sev-bar, transparent);
  transition: background-color 0.12s;
}
.notif-sidebar-item:hover {
  background-color: var(--gray-50);
}
.notif-sidebar-item.active {
  background-color: var(--sev-bg, var(--gray-50));
}
.notif-sidebar-item.unread .notif-sidebar-subject {
  font-weight: 600;
}
.notif-sidebar-item .notif-unread-dot {
  margin-right: 0.375rem;
}
.notif-sidebar-subject {
  display: block;
  font-size: 0.875rem;
  font-weight: 500;
  line-height: 1.3;
  margin-bottom: 0.125rem;
  overflow-wrap: anywhere;
}
.notif-sidebar-date {
  font-size: 0.75rem;
  color: var(--gray-500);
}
.notif-detail {
  border: 1px solid var(--gray-200);
  border-left: 4px solid var(--sev-bar, var(--gray-300));
  border-radius: var(--radius);
  background: var(--white);
  padding: 1.5rem;
  min-width: 0;
}
.notif-detail-head {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  margin-bottom: 0.75rem;
}
.notif-detail-icon { background: var(--sev-bg, var(--white)); }
.notif-detail-subject {
  font-size: 1.25rem;
  font-weight: 600;
  margin-bottom: 0.25rem;
  color: var(--navy-900);
  overflow-wrap: anywhere;
}
.notif-detail-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem 1rem;
  font-size: 0.8125rem;
  color: var(--gray-500);
  margin-bottom: 1rem;
  padding-bottom: 0.75rem;
  border-bottom: 1px solid var(--gray-100);
}
.notif-detail-content {
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--navy-800);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  margin-bottom: 1rem;
}
.notif-detail-actions {
  margin-top: 1.25rem;
}
.notif-detail-actions form { margin: 0; }

/* The announcement form. */
.notif-form fieldset {
  border: 0;
  padding: 0;
  margin: 0 0 1rem;
  min-width: 0;
}
.notif-severity-choice,
.notif-role-choice {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.375rem;
}
.notif-severity-option {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0.35rem 0.75rem;
  border: 1px solid var(--gray-300);
  border-left: 4px solid var(--sev-bar);
  border-radius: var(--radius);
  background: var(--sev-bg);
  color: var(--sev-text);
  font-size: 0.875rem;
  font-weight: 600;
  cursor: pointer;
}

@media (max-width: 768px) {
  .notif-page-layout {
    grid-template-columns: 1fr;
  }
}

/* Map Markers --- */
.marker-dot {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  border: 2px solid var(--white);
  box-shadow: 0 1px 4px rgba(0,0,0,0.3);
}

/* --- Dashboard Stats --- */
.stats-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 0.75rem;
}
.stat-box {
  display: flex;
  flex-direction: column;
  align-items: center;
  padding: 0.75rem 0.5rem;
  border-radius: var(--radius-md);
  background-color: var(--gray-50);
  border: 1px solid var(--gray-200);
}
a.stat-box-link {
  text-decoration: none;
  color: inherit;
  transition: border-color 0.15s, box-shadow 0.15s;
  cursor: pointer;
}
a.stat-box-link:hover {
  border-color: var(--navy-300);
  box-shadow: 0 2px 8px rgba(0,0,0,0.08);
}
.stat-value {
  font-size: 1.5rem;
  font-weight: 700;
  color: var(--navy-500);
  line-height: 1.2;
}
.stat-value.stat-active {
  color: var(--green-600);
}
.stat-label {
  font-size: 0.75rem;
  color: var(--gray-500);
  margin-top: 0.25rem;
}
.stat-label-row {
  display: flex;
  align-items: baseline;
  gap: 0.25rem;
  margin-top: 0.25rem;
}
.stat-label-row .stat-label {
  margin-top: 0;
}
.stat-since {
  font-size: 0.625rem;
  color: var(--gray-400);
}
@media (max-width: 480px) {
  .stats-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}
.marker-dot.driving {
  background: var(--green-500);
}
.marker-dot.standing {
  background: var(--gold-400);
}
.marker-dot.offline {
  background: var(--gray-400);
}
.map-popup {
  font-size: 0.8125rem;
  line-height: 1.5;
}
.map-popup strong {
  font-size: 0.875rem;
}
#map {
  position: relative;
  z-index: 0;
}

/* --- Route Map Legend --- */
.routes-map-legend {
  display: flex;
  gap: 1.25rem;
  font-size: 0.8125rem;
  color: var(--gray-600);
}
.legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}
.legend-line {
  display: inline-block;
  width: 24px;
  height: 3px;
  border-radius: 2px;
}
.legend-line.legend-route {
  background: #22c55e;
}
.legend-line.legend-stop {
  background: #ef4444;
  background-image: repeating-linear-gradient(
    90deg, #ef4444 0, #ef4444 5px, transparent 5px, transparent 9px
  );
}
.legend-marker {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
}
.legend-marker.legend-start {
  background: var(--green-500);
}
.legend-marker.legend-end {
  background: var(--gray-400);
}

/* --- Error Pages --- */
.error-page {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 4rem 1rem;
  min-height: 50vh;
}
.error-icon {
  width: 6rem;
  height: 6rem;
  margin-bottom: 1.5rem;
}
.error-icon .vk-mark {
  width: 100%;
  height: 100%;
}
/* 404 and 403 wear navy-and-gold marks (action:search, action:lock); the one-tone states take a colour. */
.error-icon--500 { color: var(--yellow-500); }
.error-icon--generic { color: var(--gray-400); }
.error-code {
  font-size: 5rem;
  font-weight: 700;
  line-height: 1;
  color: var(--gray-800);
  margin-bottom: 0.5rem;
}
.error-title {
  font-size: 1.5rem;
  font-weight: 600;
  color: var(--gray-700);
  margin-bottom: 0.75rem;
}
.error-message {
  font-size: 1rem;
  color: var(--gray-500);
  max-width: 28rem;
  margin-bottom: 2rem;
}
.error-details {
  font-size: 0.8125rem;
  color: var(--gray-400);
  background: var(--gray-50);
  border: 1px solid var(--gray-200);
  border-radius: 0.5rem;
  padding: 0.75rem 1.25rem;
  margin-bottom: 2rem;
  max-width: 32rem;
  word-break: break-word;
}

/* ================================================================
   UNIVERSAL SIDEBAR LAYOUT
   Usage: wrap page content in .page-sidebar-layout to get the
   collapsible dark-navy sidebar.  The wrapper uses negative margins
   to "break out" of the .container / .main-content padding, and a page
   with a rail takes the WHOLE window as its frame: the rail is pinned to
   the window's left edge and reserves its width as the wrapper's
   padding-left, and the content column is CENTRED IN WHAT THE RAIL
   LEAVES, keeping the page's width limit — equal gutters either side,
   wide or collapsed. It used to sit centred in the 1200 px container (a
   hole between rail and content on a wide screen), then for a day flush
   after the rail (a void on the right, the content under the window's
   edge once collapsed) — 2026-10-10, decided with the user. The navbar's
   strip shares this frame (see NAVBAR).
   ================================================================ */

main.container:has(.page-sidebar-layout) { max-width: none; }

/* ── Outer wrapper ──────────────────────────────────────────────── */
.page-sidebar-layout {
  min-height: calc(100vh - 4rem);
  /* Escape .main-content padding (2rem top, 3rem bottom)
     and .container padding (1.5rem each side) */
  margin: -2rem -1.5rem -3rem;
  padding-left: 15rem;                 /* the rail's width */
  transition: padding-left 0.25s ease;
}
.page-sidebar-layout:has(.app-sidebar.collapsed) {
  padding-left: 2.75rem;
}

/* ── Sidebar panel ──────────────────────────────────────────────── */
.app-sidebar {
  width: 15rem;
  background: var(--navy-800);
  display: flex;
  flex-direction: column;
  position: fixed;
  left: 0;
  top: 4rem;                     /* align flush below the 64 px navbar */
  height: calc(100vh - 4rem);
  overflow: hidden;
  z-index: 100;
  transition: width 0.25s ease;
  view-transition-name: app-sidebar;
}

/* Keep sidebar visually static during same-origin page navigations */
::view-transition-old(app-sidebar),
::view-transition-new(app-sidebar) {
  animation: none;
  mix-blend-mode: normal;
}

.app-sidebar.collapsed {
  width: 2.75rem;
}

/* ── Scrollable body ────────────────────────────────────────────── */
.app-sidebar-body {
  flex: 1;
  overflow-y: auto;
  overflow-x: hidden;
  opacity: 1;
  transition: opacity 0.15s ease;
  scrollbar-width: thin;
  scrollbar-color: rgba(255,255,255,0.12) transparent;
}
.app-sidebar-body::-webkit-scrollbar { width: 4px; }
.app-sidebar-body::-webkit-scrollbar-track { background: transparent; }
.app-sidebar-body::-webkit-scrollbar-thumb { background: rgba(255,255,255,0.14); border-radius: 2px; }

/* Collapsed, the rail is its icons: the entries (and the back and settings links) stay, centred,
   their words kept for a screen reader and in the title; titles, account cards and the profile's
   avatar go. A badge shrinks to a gold dot on the icon. A phone shows the rail this way too, always,
   until the navbar's button opens it over the content (.mobile-open). */
.app-sidebar.collapsed .app-sidebar-section:not(:has(.app-sidebar-back)) { display: none; }
.app-sidebar.collapsed .app-sidebar-section {
  padding: 0.375rem 0;
  text-align: center;
}
.app-sidebar.collapsed .app-sidebar-back {
  justify-content: center;
  padding: 0.5rem;
}
.app-sidebar.collapsed .app-sidebar-back span,
.app-sidebar.collapsed .app-sidebar-label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.app-sidebar.collapsed .app-sidebar-nav { padding: 0.25rem 0; }
.app-sidebar.collapsed .app-sidebar-nav ~ .app-sidebar-nav {
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}
.app-sidebar.collapsed .app-sidebar-nav a {
  justify-content: center;
  padding: 0.5625rem 0;
}
.app-sidebar.collapsed .app-sidebar-nav .app-sidebar-badge {
  position: absolute;
  top: 0.375rem;
  left: calc(50% + 0.3rem);
  min-width: 0;
  width: 0.5rem;
  height: 0.5rem;
  padding: 0;
  margin: 0;
  font-size: 0;
  line-height: 0;
  box-shadow: 0 0 0 2px var(--navy-800);
}
.app-sidebar.collapsed .app-sidebar-nav a:focus-visible,
.app-sidebar.collapsed .app-sidebar-back:focus-visible {
  outline: 2px solid var(--gold-400);
  outline-offset: -3px;
}

/* ── Section divider ────────────────────────────────────────────── */
.app-sidebar-section {
  padding: 0.875rem 1rem;
  border-bottom: 1px solid rgba(255, 255, 255, 0.07);
}

/* ── Section title ──────────────────────────────────────────────── */
.app-sidebar-section-title {
  margin: 0;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--gray-400, #9ca3af);
}

/* ── Back link ──────────────────────────────────────────────────── */
.app-sidebar-back {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  color: var(--gray-400);
  font-size: 0.8125rem;
  font-weight: 500;
  padding: 0.3rem 0.5rem;
  border-radius: var(--radius);
  transition: color 0.15s, background 0.15s;
  white-space: nowrap;
}
.app-sidebar-back:hover {
  color: var(--white);
  background: rgba(255, 255, 255, 0.07);
}

/* ── Account info ───────────────────────────────────────────────── */
.app-sidebar-account-name {
  color: var(--gray-200);
  font-size: 0.875rem;
  font-weight: 600;
  margin: 0 0 0.2rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.app-sidebar-account-email {
  color: var(--gray-500);
  font-size: 0.75rem;
  margin: 0;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Navigation list ────────────────────────────────────────────── */
.app-sidebar-nav {
  padding: 0.5rem 0;
}
.app-sidebar-nav ul {
  list-style: none;
  margin: 0;
  padding: 0;
}
.app-sidebar-nav a {
  position: relative;
  display: flex;
  align-items: center;
  gap: 0.625rem;
  padding: 0.5rem 1rem;
  font-size: 0.875rem;
  color: var(--gray-400);
  white-space: nowrap;
  overflow: hidden;
  transition: background 0.15s, color 0.15s;
}
/* Every entry leads with its mark (the "Marks" block styles it on the rail) and names itself in a label. */
.app-sidebar-label {
  flex: 1;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}
.app-sidebar-nav .app-sidebar-badge {
  float: none;
  flex: none;
}
/* Count pill inside a sidebar entry (the invoice rail's "Potential invoices"). */
.app-sidebar-badge {
  float: right;
  margin-left: 0.5rem;
  min-width: 1.25rem;
  padding: 0 0.4rem;
  border-radius: var(--radius-full);
  background: var(--gold-400);
  color: var(--navy-800);
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1.25rem;
  text-align: center;
}
/* A registered sender's logo beside its name (the candidate screen). */
.sender-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  white-space: nowrap;
}
.sender-chip .platform-logo { flex: none; }
/* A registered sender without a logo, in a column of icons: its initials
   (invoice/sender-fragments :: mark). Grey, because colours in the invoice
   table mean a status. */
.sender-initials {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.25rem;
  height: 1.25rem;
  padding: 0 0.25rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  background: var(--gray-100);
  color: var(--gray-700);
  font-size: 0.625rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  line-height: 1;
  vertical-align: middle;
  white-space: nowrap;
}
.filter-checklist-item .sender-initials { flex: none; }
/* An e-mailed invoice no registered sender claimed: its category's mark
   (static/images/categories/, invoice/sender-fragments :: mark). A SQUARE drawing, where the
   logos beside it are wordmarks, so it is sized by its own height and never stretched. */
.category-mark { width: auto; object-fit: contain; }
.invoice-summary-mark .category-mark { height: 1.25rem; }
.sender-logo-lg {
  height: 48px;
  width: auto;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  background: var(--white);
  padding: 2px;
}
.app-sidebar-nav a:hover {
  background: rgba(255, 255, 255, 0.07);
  color: var(--gray-200);
}
.app-sidebar-nav a.active {
  background: var(--gold-400);
  color: var(--navy-900);
  font-weight: 600;
}

/* ── Main content area ──────────────────────────────────────────── */
/* Centred in what the rail leaves (the wrapper's padding-left), never wider than the page would be
   without a rail (the mail screens and the vault are allowed their 1600 px, set beside their own
   rules); on a desk narrower than the limit it fills the room after the rail. */
.app-sidebar-main {
  min-width: 0;
  max-width: 1200px;
  margin: 0 auto;
  padding: 2rem 1.5rem 3rem;
}

/* ── Backdrop (behind the rail opened over a phone) ─────────────── */
.app-sidebar-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 199;
}
.app-sidebar-backdrop.active {
  display: block;
}

/* ── Phone (≤ 768 px) ───────────────────────────────────────────── */
/* The rail stays on screen as its strip of marks — a tap on a mark goes there at once — and the
   navbar's button opens it to its full width OVER the content, with the backdrop; the content
   keeps its place either way. It used to be hidden off-screen behind a small tab at the bottom
   left (decided with the user, 2026-10-10). */
@media (max-width: 768px) {
  .app-sidebar.mobile-open {
    width: 15rem;
    z-index: 200;               /* over the content and the backdrop (199), under the navbar (250) */
    box-shadow: 4px 0 24px rgba(0, 0, 0, 0.3);
  }
  /* Without the script (which collapses it before the first paint) the rail must not cover the
     page: only the panel the button opened is wide. */
  .app-sidebar:not(.mobile-open) { width: 2.75rem; }
  .page-sidebar-layout,
  .page-sidebar-layout:has(.app-sidebar.collapsed) { padding-left: 2.75rem; }
  .app-sidebar-main { padding: 1.25rem 0.75rem 2.5rem; }
}

/* ================================================================
   EMAIL MODULE
   ================================================================ */

/* --- Email Module --- */
.email-accounts-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: 1rem;
}

.email-toolbar { flex-wrap: wrap; gap: 0.5rem; }
.email-search-input { min-width: 0; flex: 1 1 8rem; }

.email-unread { font-weight: 600; }
/* Read or unread by weight and darkness alone (2026-10-09, the user — the gold dot is gone): an unread
   message is bold and dark, a read one regular and a step greyer, so new mail stands out without a mark
   of its own. The row's ground stays the person's tint and the star's gold. */
.mail-table tbody tr td.mail-cell-from,
.mail-table tbody tr td.mail-cell-subject { color: var(--gray-600); }
.mail-table tbody tr.email-unread td.mail-cell-from,
.mail-table tbody tr.email-unread td.mail-cell-subject,
.mail-table tbody tr.email-unread .mail-date { color: var(--gray-900); }

/* ── The mailbox list ────────────────────────────────────────────────────────────────────
   One template renders every view of a mailbox — Inbox, Sent, each container — so these
   rules are the whole look. Row tints per person are .mail-row-<colour>, defined with the
   badge palette below the table rules. */

/* The mail screens and the password vault take a wider column than a page's 1200 px: a list of
   messages or of logins is what gains from width. The limit sits on the column centred beside the
   rail (.app-sidebar-main), and the navbar's strip takes the same 1600 (see NAVBAR). :has() keeps
   layout.html untouched (the .page-sidebar-layout:has(.app-sidebar.collapsed) precedent). */
main.container:has(.mail-screen, .vault-screen) .app-sidebar-main { max-width: 1600px; }

/* [hidden] has to beat .flex and .btn, which set a display of their own. */
.mail-list [hidden],
.mail-menu[hidden],
[data-row-select] [hidden],
.row-select-menu[hidden] { display: none !important; }

/* THE LIST'S HEAD sticks under the navbar while the list scrolls (2026-10-09, decided with the user):
   the view's name, the search, the filters, the chips and the selection bar, in one block on the
   page's own ground, reaching to the column's edges; it wears a shadow only once it is stuck
   (mail-list.js toggles .is-stuck from a sentinel above it). An open filter panel scrolls inside it
   rather than covering the whole screen. On a phone the head is tall (the toolbar stacks), so there
   only the selection bar sticks. */
.mail-list-head {
  position: sticky;
  top: 4rem;
  z-index: 20;
  margin: -0.75rem -1.5rem 0;
  padding: 0.75rem 1.5rem 0.125rem;
  background: var(--gray-100);
  transition: box-shadow 0.15s ease;
}
.mail-list-head.is-stuck { box-shadow: 0 10px 12px -12px rgb(15 23 42 / 0.35); }
.mail-list-head .mail-filter-panel { max-height: 60vh; overflow: auto; }
.mail-list-head-sentinel { height: 1px; margin-bottom: -1px; }
@media (max-width: 768px) {
  .mail-list-head { display: contents; }
  .mail-list-head .mail-selection-bar {
    position: sticky;
    top: 4rem;
    z-index: 20;
    margin-inline: -1rem;
    padding-inline: 1.5rem;
    background: var(--gray-100);
  }
  .mail-list-head .mail-selection-bar.is-stuck { box-shadow: 0 10px 12px -12px rgb(15 23 42 / 0.35); }
}
/* The pages at the bar's right end: a chevron each way and "2 / 5"; a side with nowhere to go is
   dimmed in place, so nothing shifts. */
.mail-pagination { margin-inline-start: auto; }
.mail-pages { display: inline-flex; align-items: center; gap: 0.125rem; color: var(--gray-600); font-size: 0.8125rem; }
.mail-pages-where { padding: 0 0.25rem; white-space: nowrap; font-variant-numeric: tabular-nums; }
.mail-pages-step.is-off {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  color: var(--gray-300);
}

/* Above the table, instead of a header row: select-all, the count, and the actions —
   the last two only once something is ticked (static/js/mail-list.js). */
.mail-selection-bar,
.row-select-bar { min-height: 2.25rem; margin-bottom: 0.5rem; padding: 0 0.5rem; }
.mail-select-all,
.row-select-all {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  cursor: pointer;
}
.mail-select-all input,
.row-select-all input { cursor: pointer; }
.mail-selection-count,
.row-select-count { font-size: 0.8125rem; font-weight: 500; color: var(--gray-600); }
.mail-action-danger:hover { color: var(--red-600); }
.btn-outline.mail-action-danger:hover { border-color: var(--red-600); background-color: var(--red-50); }

/* Compact rows: 13px, one line each, the full text in the cell's title. A fixed layout so
   a long subject yields to the other columns instead of pushing them off the table. The
   line height equals the row buttons' height, so swapping the date for them keeps the row
   still. */
.mail-table { table-layout: fixed; font-size: 0.8125rem; line-height: 1.5rem; }
.mail-table td {
  padding: 0.375rem 0.75rem;
  vertical-align: middle;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mail-col-check { width: 2.75rem; }
.mail-col-from { width: 15rem; }
/* The faces of the people a message is assigned to (2026-10-09: the name went to the title). */
.mail-col-assignee { width: 4.5rem; }
/* Wide enough for the whole action set of an EXPANDED row — eight 24px buttons with their
   gaps plus the cell's padding — because the buttons take the date's place and a set that
   does not fit is clipped at the left edge, silently, by the cell's overflow. */
.mail-col-date { width: 14.5rem; }
.mail-table td.mail-cell-check { padding-right: 0; text-overflow: clip; }
.mail-cell-check input { vertical-align: middle; cursor: pointer; }
/* The design-system checkbox, a size down for a 13px row (the bar's select-all matches). */
.mail-list input[type="checkbox"] { width: 1rem; height: 1rem; }
/* A badge sized for a 13px row rather than for a page. */
.mail-table .badge { font-size: 0.625rem; line-height: 1rem; padding: 0 0.4375rem; }
/* The vehicle a message is about, in front of its subject (V98): the .badge rule above,
   for a plate — which since 2026-10-21 is the small plate of every other screen (one measure
   per size), so only its margin is the list's. The cell ellipsises its overflow, so what gets
   cut is the subject's tail and never the plate. */
.mail-table .plate--sm {
  margin-right: 0.375rem;
  vertical-align: baseline;
}
.mail-table td.mail-cell-date {
  text-align: right;
  color: var(--gray-500);
  font-variant-numeric: tabular-nums;
}

/* Sent shows the recipient, and "+2" for the ones that did not fit. */
.mail-recipients-more { margin-left: 0.25rem; color: var(--gray-500); font-size: 0.75rem; }
.mail-assignee {
  display: inline-flex;
  align-items: center;
  max-width: 100%;
  vertical-align: middle;
}
.mail-assignee .avatar { width: 1.375rem; height: 1.375rem; box-shadow: 0 0 0 2px var(--white); }
/* Several faces overlap, the first on top (V153). */
.mail-assignee .avatar + .avatar { margin-inline-start: -0.5rem; }
.mail-assignee-more { margin-inline-start: 0.25rem; color: var(--gray-500); font-size: 0.6875rem; font-weight: 600; }

/* A ticked row reads as selected. One class more than the assignee tint, so it wins; the
   tint's left bar stays. */
.mail-table tbody tr.row-clickable.is-selected,
.mail-table tbody tr.row-clickable.is-selected:hover {
  background-color: color-mix(in srgb, var(--navy-50) 80%, white);
}

/* The row's own actions take the date's place while the pointer is on the row, while the
   keyboard is inside it, or while one of its menus is open. A device without hover keeps
   the date: ticking the row and the bar above do the same there. An OPEN row shows them the
   whole time, in the date's place, and its subject makes room for them (the block below). */
.mail-row-actions { display: none; align-items: center; justify-content: flex-end; gap: 0.125rem; }
@media (hover: hover) {
  .mail-table tbody tr:hover .mail-date { display: none; }
  .mail-table tbody tr:hover .mail-row-actions { display: flex; }
}
.mail-table tbody tr.is-menu-open .mail-date,
.mail-table tbody tr.is-expanded .mail-date,
.mail-table tbody tr:has(:focus-visible) .mail-date { display: none; }
.mail-table tbody tr.is-menu-open .mail-row-actions,
.mail-table tbody tr.is-expanded .mail-row-actions,
.mail-table tbody tr:has(:focus-visible) .mail-row-actions { display: flex; }

/* Reply and "create a rule" are about the message one is READING, so they appear only while the
   row is open — and Reply, the main action of a message, wears the colour to say so. */
.mail-row-actions .mail-action-expanded { display: none; }
.mail-table tbody tr.is-expanded .mail-row-actions .mail-action-expanded { display: inline-flex; }
.btn-icon-ghost.mail-action-reply { color: var(--navy-500); background-color: var(--navy-50); }
/* Its mark keeps its own colours, so the hover deepens the ground (row-action-primary's way). */
.btn-icon-ghost.mail-action-reply:hover { color: var(--navy-500); background-color: var(--navy-100); }

/* A borderless icon button for dense rows, 24px square. */
.btn-icon-ghost {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 1.5rem;
  height: 1.5rem;
  padding: 0;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  color: var(--gray-500);
  cursor: pointer;
  transition: background-color 0.15s, color 0.15s;
}
.btn-icon-ghost:hover { background-color: rgba(15, 23, 42, 0.08); color: var(--gray-900); }
.btn-icon-ghost:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 1px; }

/* The container and people menus: one panel each, outside the table (its wrapper scrolls
   sideways and would clip a panel inside a cell), pinned under the button that opened it. */
.mail-menu,
.row-select-menu {
  position: fixed;
  z-index: 1050;
  min-width: 13rem;
  max-width: min(20rem, calc(100vw - 1rem));
  max-height: min(22rem, calc(100vh - 2rem));
  overflow-y: auto;
  padding: 0.25rem;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
.mail-menu form { margin: 0; }
.mail-menu-title {
  padding: 0.375rem 0.625rem 0.25rem;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--gray-500);
}
.mail-menu-sep,
.row-select-menu-sep { height: 1px; margin: 0.25rem 0; background: var(--gray-200); }
/* A heading inside a menu's list (the category menu's groups): quieter than the menu's title. */
.mail-menu-group {
  padding: 0.25rem 0.625rem 0.125rem;
  font-size: 0.6875rem;
  font-weight: 600;
  color: var(--gray-400);
}
.mail-menu-item,
.row-select-menu-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  width: 100%;
  padding: 0.375rem 0.625rem;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  color: var(--gray-800);
  font: inherit;
  font-size: 0.8125rem;
  text-align: left;
  white-space: nowrap;
  cursor: pointer;
}
.mail-menu-item:hover,
.mail-menu-item:focus-visible,
.row-select-menu-item:hover,
.row-select-menu-item:focus-visible { background-color: var(--gray-100); outline: none; }
.mail-menu-item > svg { flex-shrink: 0; color: var(--gray-500); }
.mail-menu-item > .avatar { flex-shrink: 0; width: 1.25rem; height: 1.25rem; }
/* The category menu's marks and the vehicle menu's plates (2026-10-09): a mark at the size the status
   column draws it, a category without one keeping the column; a plate never squeezed, the make and
   model beside it the part that gives way. */
.mail-menu-item > .vk-mark { width: 18px; height: 18px; }
.mail-menu-icon { flex-shrink: 0; width: 18px; }
.mail-menu-item > .plate { flex-shrink: 0; }
.mail-menu--vehicles { max-width: min(22rem, calc(100vw - 1rem)); }
.mail-menu-label,
.row-select-menu-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.mail-menu-item > .mail-menu-check { visibility: hidden; color: var(--navy-500); }
.mail-menu-check { display: inline-flex; flex-shrink: 0; }
.mail-menu-check > .vk-mark { width: 14px; height: 14px; }
.mail-menu-item.is-current { font-weight: 600; }
.mail-menu-item.is-current > .mail-menu-check { visibility: visible; }
/* Some of the ticked messages hold the person, some do not (the people menu, V153). */
.mail-menu-item.is-mixed > .mail-menu-check { visibility: visible; opacity: 0.4; }

/* Select-all and the caret that opens the selection menu read as ONE control. */
.mail-select-split,
.row-select-split {
  display: inline-flex;
  align-items: center;
  border-radius: var(--radius);
  transition: background-color 0.15s;
}
.mail-select-split:hover,
.mail-select-split:focus-within,
.row-select-split:hover,
.row-select-split:focus-within { background-color: rgba(15, 23, 42, 0.06); }
.mail-select-caret,
.row-select-caret {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.125rem;
  height: 1.75rem;
  margin-left: -0.25rem;
  padding: 0;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  color: var(--gray-500);
  cursor: pointer;
}
.mail-select-caret svg,
.row-select-caret svg { width: 0.875rem; height: 0.875rem; }
.mail-select-caret:hover,
.mail-select-caret[aria-expanded="true"],
.row-select-caret:hover,
.row-select-caret[aria-expanded="true"] { color: var(--gray-900); }
.mail-select-caret:focus-visible,
.row-select-caret:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 1px; }

/* Refresh beside select-all: as tall as that control, spinning while the request waits for the
   mail server (mail-list.js). */
.mail-refresh { width: 1.75rem; height: 1.75rem; }
.mail-collapse-all { width: 1.75rem; height: 1.75rem; }
.mail-refresh[disabled] { cursor: progress; }
.mail-refresh.is-busy svg { animation: mail-refresh-spin 0.9s linear infinite; }
@keyframes mail-refresh-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .mail-refresh.is-busy svg { animation: none; opacity: 0.5; }
}

/* --- Search: the field with its address suggestions, the filter panel, the chips --- */
/* The field, Filters and Search on ONE line (2026-10-21, the user): the form never wraps and the
   field is what gives way. On a phone the column toolbar used to drop Search under the field. No
   WIDTH on .mail-search: it would become the form's minimum and push the row off a phone's screen. */
.mail-search-form { flex-wrap: nowrap; min-width: 0; }
.mail-search { position: relative; flex: 1 1 14rem; min-width: 0; }
/* On a narrow phone the two buttons are their marks alone — the words stay for a screen reader —
   or the field would be left ~75 px. */
@media (max-width: 480px) {
  .mail-search-form .mail-filter-label,
  .mail-search-form .mail-search-submit .vk-mark-label > span {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
}
.mail-search .email-search-input { width: 100%; }
.mail-suggest[hidden],
.mail-filter-panel[hidden] { display: none !important; }
.mail-suggest {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  z-index: 1060;
  min-width: 100%;
  width: max-content;
  max-width: min(26rem, calc(100vw - 2rem));
  max-height: 20rem;
  overflow-y: auto;
  margin: 0;
  padding: 0.25rem;
  list-style: none;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
}
.mail-suggest-item {
  display: flex;
  flex-direction: column;
  padding: 0.375rem 0.625rem;
  border-radius: var(--radius);
  font-size: 0.8125rem;
  line-height: 1.25rem;
  cursor: pointer;
}
.mail-suggest-item:hover,
.mail-suggest-item.is-active { background-color: var(--gray-100); }
.mail-suggest-name { color: var(--gray-900); font-weight: 500; }
.mail-suggest-address { color: var(--gray-500); overflow-wrap: anywhere; }
.mail-suggest mark { background: transparent; color: inherit; font-weight: 700; padding: 0; }

.mail-filter-toggle { display: inline-flex; align-items: center; gap: 0.375rem; }
.mail-filter-count {
  min-width: 1.125rem;
  padding: 0 0.3125rem;
  border-radius: var(--radius-full);
  background: var(--navy-500);
  color: var(--white);
  font-size: 0.6875rem;
  font-weight: 600;
  line-height: 1.125rem;
  text-align: center;
}
.mail-filter-panel {
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
  margin-bottom: 1rem;
  padding: 1rem;
}
.mail-filter-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.75rem;
}
.mail-filter-field { display: flex; flex-direction: column; gap: 0.25rem; margin: 0; min-width: 0; }
.mail-filter-grid .filter-choice-field { min-width: 0; }
.mail-filter-grid .filter-checklist-summary { width: 100%; min-width: 0; }
/* A box wears the mark its rows wear for the same thing, in the same colour (.mail-flag*). */
.mail-filter-check { display: inline-flex; align-items: center; gap: 0.375rem; font-size: 0.875rem; cursor: pointer; }

.mail-chips { margin-bottom: 0.5rem; padding: 0 0.5rem; }
.mail-result-count { margin-right: 0.25rem; font-size: 0.8125rem; font-weight: 600; color: var(--gray-700); }
.mail-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  max-width: 100%;
  padding: 0.125rem 0.375rem 0.125rem 0.625rem;
  border: 1px solid var(--navy-100);
  border-radius: var(--radius-full);
  background: var(--navy-50);
  color: var(--navy-500);
  font: inherit;
  font-size: 0.75rem;
  line-height: 1.25rem;
  cursor: pointer;
}
.mail-chip > span { max-width: 16rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-chip svg { flex-shrink: 0; width: 0.75rem; height: 0.75rem; }
.mail-chip:hover { border-color: var(--navy-400); background: var(--navy-100); }
.mail-chip:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 1px; }
.mail-chips-clear { font-size: 0.75rem; }

/* Where a result lives, in a search over all messages. */
.mail-location {
  display: inline-block;
  margin-right: 0.375rem;
  padding: 0 0.4375rem;
  border-radius: var(--radius-full);
  background: var(--gray-100);
  color: var(--gray-600);
  font-size: 0.6875rem;
  line-height: 1.125rem;
  vertical-align: middle;
}

/* The message screen's toolbar: the list bar's icon buttons, back first and Reply at the
   far end. */
.mail-toolbar-sep { width: 1px; height: 1.25rem; background: var(--gray-200); }
.mail-toolbar-reply { display: inline-flex; align-items: center; gap: 0.375rem; margin-left: auto; }
.email-attachment-icon { display: inline-flex; color: var(--gray-500); }

@media (max-width: 1100px) {
  .mail-col-from { width: 10rem; }
}

/* ── The message list's row (email/list-parts.html :: rows, 2026-10-10) ─────────────────────────
   Six columns: the tick, who, the STATUS marks (a narrow column of their own, the user's wish — for a
   day they rode in the subject's line), the subject with the car at its end, the people, the whole date.
   The status column holds the AI's category and the language, which give way when it is narrow, and the
   states of the STATE family in their own colours (possible fraud, possible spam, a reply still owed),
   the AI's reading of the attachments and the files sent as links, which never do. Under the subject,
   the files the message carries as small cards — only such a row grows, and its cells sit at the top so
   every first line stays on one line. The row's actions come up OVER the end of the row (the background
   they inherit from the row hides what lies under them), so the date column is only as wide as a date. */
.mail-table--messages .mail-col-from { width: 13rem; }
.mail-col-status { width: 6rem; }
/* The whole date with its hour (2026-10-10, the user — at 8.5rem the hour was cut): measured in Inter at
   13 px in Chromium and Firefox, "28.08.2026 23:59" is 115 px, "28/08/2026 23:59" 118 and
   "2026-08-28 23:59" 125; 9.5rem less the paddings leaves 132. The subject gives the room. */
.mail-table--messages .mail-col-date { width: 9.5rem; }
.mail-table--messages tbody tr { background-color: var(--white); }
.mail-table--messages td.mail-cell-date {
  position: relative;
  overflow: visible;
  background-color: inherit;
  padding-inline-start: 0.5rem;
}
.mail-table--messages .mail-date { display: block; overflow: hidden; text-overflow: ellipsis; }
.mail-table--messages .mail-row-actions {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0.5rem;
  z-index: 1;
  padding-inline-start: 0.5rem;
  background-color: inherit;
}

/* The subject's line: the marks, the subject (the one thing that gives way, with an ellipsis) and the
   car at the far end. */
.mail-subject-line { display: flex; align-items: center; gap: 0.375rem; min-width: 0; min-height: 1.5rem; }
.mail-subject-text { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-subject-line .mail-location { flex: none; }
/* The paperclip's box is as tall as the line (the mark alone is 13 px): centred in a closed row, at the
   top of an open one, it stands in the same place — it used to jump 5.5 px up on opening. */
.mail-subject-line .mail-clip { flex: none; display: inline-flex; align-items: center; min-height: 1.5rem; }
.mail-table td.mail-cell-status { padding-inline: 0.25rem 0.5rem; text-overflow: clip; }
.mail-status { display: flex; align-items: center; gap: 0.25rem; min-width: 0; height: 1.5rem; }
.mail-status-about { display: flex; align-items: center; gap: 0.25rem; min-width: 0; overflow: hidden; }
/* The states never give way; the category and the language do. One group, side by side. */
.mail-status-flags { display: flex; align-items: center; gap: 0.25rem; flex: none; }
.mail-status .mail-category { display: inline-flex; flex: none; }
.mail-status .mail-category .vk-mark { width: 16px; height: 16px; }
/* The language, quietly (2026-10-09, decided with the user — a pill, a flag, the code "no" and the whole
   name were each too loud, wrong or too long): the translate mark in grey, its name in the title. */
.mail-lang { display: inline-flex; flex: none; filter: grayscale(1); opacity: 0.5; }
.mail-lang .vk-mark { width: 14px; height: 14px; }
.mail-flag { display: inline-flex; flex: none; }
.mail-flag .vk-mark { width: 15px; height: 15px; }
.mail-flag--fraud { color: var(--red-600, #dc2626); }
.mail-flag--spam { color: #b45309; }
.mail-flag--waiting { color: #1d4ed8; }
.mail-flag--action { color: var(--navy-800, #223b60); }
.mail-flag.is-overdue { color: var(--red-600, #dc2626); }
.mail-flag--starred { color: var(--gold-400); }
.mail-status .mail-reading,
.mail-status .mail-links { flex: none; margin: 0; }
/* The car at the line's end: the plate the row is about, or — when the text names several of the
   fleet's cars (V151) — the first of them in a DASHED frame, a hint rather than a decision, and "+N";
   the plates in the title. */
/* As tall as the line (the plate itself is 18 px), so it sits where it sits in a closed row whatever the
   line's alignment — an open row's line aligns to its top. */
.mail-vehicle { display: inline-flex; align-items: center; gap: 0.25rem; flex: none; min-height: 1.5rem; margin-inline-start: auto; padding-inline-start: 0.5rem; }
/* The plate keeps its size (one measure per size, the plate block): the subject is what gives way. A
   pathological plate wider than the line is still cut rather than pushing the row. */
.mail-table .mail-vehicle .plate--sm { flex: none; max-width: 100%; margin: 0; overflow: hidden; text-overflow: ellipsis; }
.mail-vehicle.is-maybe .plate { border-style: dashed; border-left-style: solid; background: var(--gray-50); }
.mail-vehicle-more { flex: none; color: var(--gray-600); font-size: 0.6875rem; font-weight: 600; }

/* An open row reads its whole subject, wrapped. */
.mail-table tbody tr.is-expanded .mail-subject-line { align-items: flex-start; }
.mail-table tbody tr.is-expanded .mail-subject-text { white-space: normal; overflow: visible; }
/* An open row's files are the preview's cards, right under it — the row's own would say it twice. */
.mail-table tbody tr.is-expanded .mail-att-chips { display: none; }
/* Every cell of a message row sits at the TOP: its first line is the same line in every cell, so a row
   that grows — the files under the subject, or an open row's subject wrapping — grows downwards and
   nothing on that line moves (2026-10-21: centred, opening a long subject slid the sender, the faces
   and the checkbox down while the clip and the plate stayed up). */
.mail-table--messages tbody tr > td { vertical-align: top; }
.mail-table--messages tbody tr:has(.mail-att-chips) .mail-row-actions { align-items: flex-start; padding-top: 0.375rem; }
/* An OPEN row stays ONE line (2026-10-21, the user; wider than 1280 px — the two-line grid below has its
   own actions line). The actions take the date's place on its first line, and the subject gives up the
   room they need — mail-preview.js measures it into --mail-open-shift — so the plate and the faces move
   left of them instead of under them. */
@media (min-width: 1281px) {
  .mail-table--messages tbody tr.is-expanded .mail-row-actions { align-items: flex-start; padding-top: 0.375rem; }
  .mail-table--messages tbody tr.is-expanded td.mail-cell-subject {
    padding-inline-end: calc(0.75rem + var(--mail-open-shift, 0px));
  }
  .mail-table--messages tbody tr.is-expanded td.mail-cell-assignee { overflow: visible; }
  .mail-table--messages tbody tr.is-expanded .mail-assignee {
    position: relative;
    inset-inline-start: calc(-1 * var(--mail-open-shift, 0px));
  }
}
/* One line of cards that never wraps on a wide screen: they share the room EQUALLY (one basis for
   all), so a long name does not squeeze its neighbours to an initial; each keeps a few letters and
   its whole name in the title. A phone lets them wrap instead (below). */
.mail-att-chips {
  container: mail-att / inline-size;
  display: flex;
  gap: 0.375rem;
  margin-top: 0.25rem;
  overflow: hidden;
}
.mail-att-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.3125rem;
  flex: 0 1 11rem;
  min-width: 4.5rem;
  max-width: max-content;
  height: 1.5rem;
  padding: 0 0.5rem 0 0.25rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-full);
  background: var(--white);
  color: var(--gray-700);
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1;
  text-decoration: none;
}
.mail-att-chip:hover { border-color: var(--gray-300); color: var(--gray-900); box-shadow: 0 1px 2px rgb(0 0 0 / 0.08); }
.mail-att-chip:focus-visible { outline: 2px solid var(--gold-500, #d4a843); outline-offset: 1px; }
/* The file's kind (subject:file-*), its own picture: no disc behind it. */
.mail-att-chip-icon { display: inline-flex; align-items: center; flex: none; }
.mail-att-chip-icon svg { width: 14px; height: 14px; }
.mail-att-chip-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-att-chip-size { flex: none; color: var(--gray-500); font-weight: 400; font-variant-numeric: tabular-nums; }
.mail-att-more { flex: none; align-self: center; color: var(--gray-500); font-size: 0.75rem; font-weight: 600; }
/* A narrow line of cards gives the names the room the sizes took; the size stays in the title. */
@container mail-att (max-width: 28rem) {
  .mail-att-chip-size { display: none; }
}

/* Mid-width: a narrower sender column leaves the subject its room. */
@media (max-width: 1366px) {
  .mail-table--messages .mail-col-from { width: 11.5rem; }
}

/* TWO LINES A MESSAGE from 1280 px down (decided with the user, 2026-10-09, at 1100 px; moved to 1280 the
   same day, when the car's own column left the one-line subject 8rem at 1110 px): who and when on the
   first — the tick, the sender, the person's face, a short date (the time for today) — and what on
   the second — the star, the status marks, the subject — with the files under it. The check and the
   date cells have no box here (display: contents), so their pieces take places of their own: the
   star under the tick, the chevron at the end of the subject's line. The row's actions come up on a
   line of their own once the row is open (or a menu or the keyboard is in it) — never on hover,
   which would push the rows below; the selection bar does the same for ticked rows. The person's
   colour is the row's left bar. */
@media (max-width: 1280px) {
  .mail-table--messages colgroup { display: none; }
  .mail-table--messages tbody { display: block; }
  .mail-table--messages tbody tr {
    display: grid;
    grid-template-columns: 2.25rem auto minmax(0, 1fr) auto auto;
    grid-template-areas:
      "check  from    from    assignee date"
      "star   status  subject subject  expand"
      ".      chips   chips   chips    chips"
      ".      actions actions actions  actions";
    align-items: start;
    padding: 0.375rem 0.625rem 0.375rem 0;
    border-bottom: 1px solid var(--gray-200);
  }
  .mail-table--messages tbody tr:last-child { border-bottom: none; }
  .mail-table--messages tbody tr[class*="mail-row-"] { box-shadow: inset 4px 0 0 var(--mail-bar); }
  .mail-table--messages td { display: block; min-width: 0; padding: 0; border-bottom: none; }
  .mail-table--messages td.mail-cell-check,
  .mail-table--messages td.mail-cell-date { display: contents; }
  .mail-table--messages .mail-cell-check input { grid-area: check; justify-self: center; align-self: center; margin: 0; }
  .mail-table--messages .mail-cell-check .mail-star { grid-area: star; justify-self: center; margin: 0; }
  .mail-table--messages .mail-cell-check .mail-expand { grid-area: expand; justify-self: end; margin: 0 0 0 0.25rem; }
  .mail-table--messages td.mail-cell-from { grid-area: from; padding-inline-end: 0.5rem; }
  .mail-table--messages td.mail-cell-assignee { grid-area: assignee; padding-inline-end: 0.5rem; }
  .mail-table--messages td.mail-cell-assignee:not(:has(.mail-assignee)) { display: none; }
  .mail-table--messages td.mail-cell-status { grid-area: status; padding: 0 0.5rem 0 0; }
  .mail-table--messages td.mail-cell-status:not(:has(.mail-category, .mail-lang, .mail-flag:not([hidden]), .mail-reading, .mail-links)) { display: none; }
  /* The subject cell has no box either: its line (the subject, the car) takes its place on the second
     line, its cards a line of their own across the row. */
  .mail-table--messages td.mail-cell-subject { display: contents; }
  .mail-table--messages .mail-subject-line { grid-area: subject; }
  .mail-table--messages .mail-att-chips { grid-area: chips; }
  .mail-table--messages tbody tr .mail-date {
    display: block;
    grid-area: date;
    justify-self: end;
    color: var(--gray-500);
    font-size: 0.75rem;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
  }
  .mail-table--messages .mail-row-actions {
    grid-area: actions;
    position: static;
    flex-wrap: wrap;
    justify-content: flex-start;
    padding: 0.375rem 0 0;
    background: none;
  }
  .mail-table--messages tbody tr:hover .mail-row-actions { display: none; }
  .mail-table--messages tbody tr.is-expanded .mail-row-actions,
  .mail-table--messages tbody tr.is-menu-open .mail-row-actions,
  .mail-table--messages tbody tr:has(:focus-visible) .mail-row-actions { display: flex; }
  .mail-table--messages tbody tr.mail-preview-row { display: block; padding: 0; box-shadow: none; }
  .mail-table--messages tbody tr.mail-preview-row > td { display: block; padding: 0 0.5rem 0.625rem; }
}
/* A phone: the cards wrap rather than shrink to nothing, each as wide as its name (to the line's
   width), the size in the title alone. */
@media (max-width: 640px) {
  .mail-table--messages .mail-att-chips { flex-wrap: wrap; row-gap: 0.25rem; }
  .mail-table--messages .mail-att-chip { flex: 0 1 auto; max-width: 100%; }
  .mail-table--messages .mail-att-chip-size { display: none; }
}

/* --- The list's expanding preview (mail-preview.js) and the mail body frame (mail-body.js) --- */
.mail-col-check { width: 6rem; }
.mail-expand {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  margin-left: 0.25rem;
  color: var(--gray-500);
  vertical-align: middle;
  /* A symbol, not a button: the whole row opens it and the row is what the keyboard focuses.
     Hidden without leaving a gap: the row does not shift when it appears. */
  visibility: hidden;
  transition: color 0.15s;
}
.mail-expand svg { width: 1rem; height: 1rem; transition: transform 0.2s ease; }
/* A row scrolled to (?expand=) lands just below the navbar, never hidden behind it. */
.mail-table tbody tr[data-mail-item],
.mail-card[data-mail-item] { scroll-margin-top: calc(4.5rem + var(--mail-head-h, 0px)); }
.mail-table tbody tr[data-mail-item]:focus,
.mail-card:focus { outline: none; }
.mail-table tbody tr[data-mail-item]:focus-visible { outline: 2px solid var(--navy-400); outline-offset: -2px; }
.mail-card:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 1px; }
.mail-table tbody tr[data-mail-item]:focus-visible .mail-expand { visibility: visible; }
@media (hover: hover) {
  .mail-table tbody tr:hover .mail-expand { visibility: visible; }
}
/* A touch device has no hover to reveal it. */
@media (hover: none) {
  .mail-expand { visibility: visible; }
}
.mail-table tbody tr.is-expanded .mail-expand { visibility: visible; color: var(--navy-500); }
.mail-table tbody tr.is-expanded .mail-expand svg { transform: rotate(180deg); }
.mail-table tbody tr.row-clickable.is-expanded { background-color: color-mix(in srgb, var(--navy-50) 55%, white); }
/* An open row shows its whole subject, wrapped in the same cell — the ellipsis is for scanning
   the list, not for reading the message one has opened. */
.mail-table tbody tr.is-expanded td.mail-cell-subject {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
}

/* The conversation screen (email/thread.html): a card per message, newest on top. The stripe on
   the left is the way it went — gold received, navy sent, the colours of the two badges — and the
   header says who wrote it and to whom. mail-preview.js opens the body into the card's own
   .mail-preview, which here is part of the card rather than a box of its own: a conversation
   scrolls as one page, not as boxes inside boxes. */
.mail-conversation-heading { display: flex; flex-direction: column; min-width: 0; flex: 1 1 16rem; }
.mail-conversation-caption { margin: 0; font-size: 0.8125rem; color: var(--gray-500); }
.mail-conversation { display: flex; flex-direction: column; gap: 0.75rem; }
.mail-card {
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-left: 4px solid var(--gold-400);
  border-radius: var(--radius-lg);
  min-width: 0;
}
.mail-card--sent { border-left-color: var(--navy-400); }
/* The message the reader came from. */
.mail-card.is-source { box-shadow: 0 0 0 2px var(--navy-100); }
/* The list's unread rows are bold throughout; a card says it with the dot and the sender alone. */
.mail-card.email-unread { font-weight: inherit; }
.mail-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: 0.375rem 0.625rem;
  padding: 0.625rem 0.875rem;
  cursor: pointer;
  border-radius: var(--radius-lg);
}
.mail-card-head:hover { background: var(--gray-50, #f8fafc); }
.mail-card.is-expanded .mail-card-head { border-radius: var(--radius-lg) var(--radius-lg) 0 0; }
.mail-card .mail-expand { visibility: visible; margin: 0.125rem 0 0; flex-shrink: 0; }
.mail-card.is-expanded .mail-expand { color: var(--navy-500); }
.mail-card.is-expanded .mail-expand svg { transform: rotate(180deg); }
.mail-card-head .mail-direction { margin-top: 0.125rem; flex-shrink: 0; }
.mail-card-parties {
  flex: 1 1 18rem;
  min-width: 0;
  display: grid;
  grid-template-columns: max-content minmax(0, 1fr);
  gap: 0.125rem 0.625rem;
  margin: 0;
  font-size: 0.875rem;
  line-height: 1.45;
}
.mail-card-parties dt { color: var(--gray-500); font-weight: 400; }
.mail-card-parties dd { margin: 0; min-width: 0; overflow-wrap: anywhere; color: var(--gray-700); }
.mail-party-name { color: var(--gray-900); font-weight: 500; }
.mail-card-from .mail-party-name { font-weight: 600; }
.mail-card.email-unread .mail-card-from .mail-party-name { font-weight: 700; }
.mail-party-address { color: var(--gray-500); }
/* Several recipients: each one whole on a line if it fits, the comma drawn here — a flex row
   drops the whitespace the template leaves between them, which put a space before every comma. */
.mail-card-to { display: flex; flex-wrap: wrap; gap: 0 0.375rem; }
.mail-card-to .mail-party { min-width: 0; }
.mail-card-to .mail-party:not(:last-child)::after { content: ","; color: var(--gray-500); }
/* The mailbox itself: a chip, so "who is me" never has to be read out of an address. */
.mail-you {
  display: inline-block;
  padding: 0 0.4rem;
  border-radius: 999px;
  background: var(--navy-50);
  color: var(--navy-700);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.35rem;
}
.mail-card-meta {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  margin-left: auto;
  padding-top: 0.125rem;
  font-size: 0.8125rem;
  color: var(--gray-500);
  white-space: nowrap;
}
.mail-card-meta .mail-clip svg { width: 13px; height: 13px; vertical-align: -2px; }
/* A SENT row's files carried as LINKS (V119): the chain beside the subject — amber when one runs
   out within the week, grey when none serves any more; the words are in the title. */
/* The files-sent-as-links mark keeps its own colours, so its state rides beside it: an amber dot
   while a link runs out within the week, the mark greyed once none serves (the words: title, sr-only). */
.mail-links { position: relative; display: inline-flex; flex-shrink: 0; vertical-align: middle; }
.mail-links svg { width: 0.875rem; height: 0.875rem; }
.mail-links.is-expiring::after {
  content: "";
  position: absolute;
  top: -2px;
  right: -3px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background-color: #b45309;
  box-shadow: 0 0 0 1.5px var(--white);
}
.mail-links.is-gone .vk-mark { filter: grayscale(1); opacity: 0.5; }
.mail-card .mail-preview {
  border: 0;
  border-top: 1px solid var(--gray-200);
  border-radius: 0 0 var(--radius-lg) var(--radius-lg);
}
/* The header already says who, to whom and when. */
.mail-conversation .mail-preview-meta { display: none; }
@media (max-width: 640px) {
  .mail-card-head { padding: 0.5rem 0.625rem; }
  /* Chevron, direction and date on the first line; who wrote to whom under them, full width. */
  .mail-card-parties { order: 1; flex-basis: 100%; }
  .mail-card .mail-preview { padding: 0.75rem 0.625rem; }
}

.mail-table tbody tr.mail-preview-row > td {
  padding: 0 0.75rem 0.75rem;
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  background-color: color-mix(in srgb, var(--navy-50) 55%, white);
}
/* An opened message (email/preview.html), read top to bottom (2026-10-09, decided with the user): who
   and when, a fraud warning, the STRIP that says what the message is (the category, large, with its mark;
   the summary in the reader's language; the reply it waits for), the body on a white PAPER with larger
   type, and UNDER it the details as cards — side by side where the preview is wide enough. Nothing here
   scrolls on its own: the page scrolls, and the translated bar sticks under the list's head. */
.mail-preview {
  container: mail-preview / inline-size;
  padding: 0.875rem 1rem 1rem;
  background: var(--gray-50);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  font-size: 0.875rem;
  line-height: 1.5;
}
.mail-preview-body { display: flex; flex-direction: column; gap: 0.625rem; min-width: 0; }
.mail-preview-paper {
  min-width: 0;
  padding: 1.25rem 1.5rem;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  box-shadow: 0 1px 2px rgb(15 23 42 / 0.05);
  color: var(--gray-900);
  font-size: 0.9375rem;
  line-height: 1.6;
}
/* The strip: navy-50 with the brand's gold edge, the category in the heading's weight. */
.mail-preview-lead {
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  padding: 0.75rem 1rem;
  background: color-mix(in srgb, var(--navy-50) 65%, white);
  border: 1px solid var(--navy-100);
  border-left: 4px solid var(--gold-400);
  border-radius: var(--radius-lg);
}
.mail-preview-lead-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.375rem 0.625rem; }
.mail-preview-lead .mail-analysis-category { font-size: 1rem; font-weight: 700; color: var(--navy-800); }
.mail-preview-lead .mail-analysis-category .vk-mark { width: 24px; height: 24px; }
.mail-preview-lead .mail-analysis-summary { font-size: 1rem; line-height: 1.5; color: var(--gray-900); }
.mail-preview-lead-note { margin: 0; color: var(--gray-500); font-size: 0.8125rem; }
.mail-preview-lead-action { margin: 0 0 0 auto; }
/* What the message asks of the company (V154): the state's mark and the words on one line, red once overdue. */
.mail-preview-lead .mail-analysis-action { display: inline-flex; align-items: center; gap: 0.375rem; color: var(--navy-800); font-size: 0.9375rem; }
.mail-preview-lead .mail-analysis-action > .vk-mark { width: 20px; height: 20px; flex: none; }
.mail-preview-lead .mail-analysis-action.is-overdue { color: #dc2626; }
.mail-analysis-face { width: 24px; height: 24px; border-radius: 50%; object-fit: cover; vertical-align: middle; margin-inline-end: 0.25rem; }
.mail-analysis-holder { display: inline-flex; align-items: center; gap: 0.25rem; }
/* The details: a white card each, in a smaller type than the body. */
.mail-preview-details {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(18rem, 100%), 1fr));
  gap: 0.75rem;
  align-items: start;
  min-width: 0;
  font-size: 0.8125rem;
}
.mail-preview-details > * {
  min-width: 0;
  margin: 0;
  padding: 0.625rem 0.75rem;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: 0.5rem;
}
.mail-preview-details form { min-width: 0; max-width: 100%; }
/* The original's download: a quiet link under the body, on the right. */
.mail-preview-foot { display: flex; justify-content: flex-end; margin-top: -0.125rem; }
.mail-preview-source { color: var(--gray-500); font-size: 0.75rem; text-decoration: none; }
.mail-preview-source:hover { color: var(--navy-700); text-decoration: underline; }
.mail-preview-source .vk-mark { width: 14px; height: 14px; }
/* The list's wrapper must not be a scroll container, or the translated bar could stick to nothing
   once the preview stopped scrolling on its own: clip still clips what is absolutely positioned
   inside, and never makes a scroll container. */
.table-wrapper:has(> .mail-table--messages) { overflow-x: clip; }
/* The AI's reading of the attachments, in one mark: the colour is the kind, the tooltip the
   whole reading. Same vocabulary as invoice/candidate-fragments :: readingBadge. */
.mail-reading { display: inline-flex; align-items: center; vertical-align: middle; margin-left: 0.25rem; }
.mail-reading svg { width: 0.875rem; height: 0.875rem; }
.mail-reading-invoice { color: #166534; }
.mail-reading-receipt { color: var(--navy-700); }
.mail-reading-handwritten { color: #854d0e; }
.mail-reading-other,
.mail-reading-none { color: var(--gray-400); }
/* How many documents a mail of several PDFs holds, beside the one mark; the tooltip names them. */
.mail-reading-count { margin-inline-start: 0.125rem; font-size: 0.6875rem; font-weight: 600; line-height: 1;
  font-variant-numeric: tabular-nums; }

.mail-preview-content { display: flex; flex-direction: column; gap: 0.75rem; }
.mail-preview-meta { display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; font-size: 0.8125rem; color: var(--gray-500); }
/* What the AI read in a message (V148): the sender as ValkOS knows it, then the block — category,
   summary, reply and deadline, the numbers it names, the category picker. Words carry every fact;
   the badge colours only repeat them. */
.mail-preview-known a { color: var(--navy-700); text-decoration: underline; }
.mail-analysis { border: 1px solid var(--gray-200); border-radius: 0.5rem; padding: 0.625rem 0.75rem;
  margin: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.mail-analysis-phishing { margin: 0; display: flex; flex-wrap: wrap; gap: 0.25rem 0.5rem; }
.mail-analysis-phishing-hint { font-size: 0.8125rem; }
.mail-analysis-category { font-weight: 600; }
.mail-analysis-summary { margin: 0; font-size: 0.9375rem; }
.mail-analysis-facts { display: flex; flex-wrap: wrap; gap: 0.25rem 1.25rem; margin: 0; font-size: 0.8125rem; }
.mail-analysis-facts > div { display: flex; gap: 0.375rem; }
.mail-analysis-facts dt { color: var(--gray-500); }
.mail-analysis-facts dd { margin: 0; font-variant-numeric: tabular-nums; }
.mail-analysis-actions form { margin: 0; }
.mail-analysis-when { margin: 0; }
/* The settings screen's list of the categories, each with its mark. */
.mail-analysis-categories { list-style: none; padding: 0; margin: 0; display: grid;
  grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); gap: 0.375rem 1rem; }
.mail-preview-from { color: var(--gray-900); font-weight: 600; overflow-wrap: anywhere; }
/* A message's attachments (email/preview.html, AttachmentCards): a line saying how many and how
   much with "Download all", then one card per file — the kind's mark on a tinted square, the name
   on one line (the whole name in its title), the kind and size under it, a download mark that
   darkens on hover. A grid of equal columns that never goes under 14rem, so a phone gets one. The
   tint is a hint only: the kind is also a word on the card. */
.mail-attachments { display: flex; flex-direction: column; gap: 0.5rem; }
.mail-attachments-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.5rem; }
.mail-attachments-title { display: inline-flex; align-items: center; gap: 0.375rem; font-size: 0.8125rem; font-weight: 600; color: var(--gray-700); }
.mail-attachments-title .mail-attachment-size { font-weight: 400; }
.mail-attachments-all svg { width: 0.875rem; height: 0.875rem; }
.mail-attachment-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(14rem, 100%), 1fr));
  gap: 0.5rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.mail-attachment-grid > li { min-width: 0; }
.mail-attachment-card {
  --att-tint: var(--gray-500);
  display: flex;
  align-items: center;
  gap: 0.625rem;
  min-width: 0;
  padding: 0.5rem 0.625rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  background: #fff;
  color: var(--gray-900);
  text-decoration: none;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.mail-attachment-card:hover { border-color: var(--gray-300); box-shadow: 0 1px 3px rgb(0 0 0 / 0.08); }
.mail-attachment-card:focus-visible { outline: 2px solid var(--gold-500, #d4a843); outline-offset: 2px; }
.mail-attachment-card.is-pdf { --att-tint: #d93025; }
.mail-attachment-card.is-image { --att-tint: #1a73e8; }
.mail-attachment-card.is-sheet { --att-tint: #188038; }
.mail-attachment-card.is-document { --att-tint: #2b579a; }
.mail-attachment-card.is-archive { --att-tint: #6d4c41; }
.mail-attachment-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: var(--radius);
  background: color-mix(in srgb, var(--att-tint) 12%, #fff);
}
.mail-attachment-icon svg { width: 1.25rem; height: 1.25rem; } /* the file kind's mark on the kind's pale ground */
.mail-attachment-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.mail-attachment-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.8125rem; font-weight: 600; }
.mail-attachment-download { display: inline-flex; flex-shrink: 0; }
.mail-attachment-download svg { width: 1rem; height: 1rem; }
.mail-attachment-size { color: var(--gray-500); font-size: 0.75rem; }
/* What the invoice module made of the attachments, a card of the details column. */
.mail-preview-documents {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.mail-preview-reading { font-size: 0.8125rem; }
/* One document: the row that says what it is, and under it what was read from it. They get a
   column of their own so the facts sit closer to their own row than to the next document's. */
.mail-preview-document { display: flex; flex-direction: column; gap: 0.25rem; }
.mail-preview-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.125rem 0.75rem;
  font-size: 0.75rem;
  color: var(--gray-500);
  overflow-wrap: anywhere;
}
/* The amount is what the eye goes to first. The app's links are colour-inherit, so the vehicle
   is marked by its underline alone. */
.mail-preview-fact-amount { color: var(--gray-900); font-weight: 600; }
.mail-preview-facts a { text-decoration: underline; }
.mail-preview-notscanned { margin: 0; color: var(--gray-500); font-size: 0.8125rem; }
/* The files a Sent message carried as LINKS (V119, email/preview.html): the documents block's
   shape — a titled group above the body, one soft blue card per file with its state, its facts
   and the sender's three actions. */
.mail-preview-shared {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.mail-preview-shared-title {
  margin: 0;
  font-size: 0.6875rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--gray-500);
}
.mail-preview-shared-list { display: flex; flex-direction: column; gap: 0.5rem; margin: 0; padding: 0; list-style: none; }
.mail-preview-shared-file {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid #bfdbfe;
  border-radius: var(--radius);
  background: #eff6ff;
  font-size: 0.8125rem;
}
.mail-preview-shared-icon { display: inline-flex; flex-shrink: 0; } /* the file kind's mark */
.mail-preview-shared-icon svg { width: 1rem; height: 1rem; }
.mail-preview-shared-name { font-weight: 600; color: var(--gray-900); }
a.mail-preview-shared-name { text-decoration: underline; }
.mail-preview-shared-facts {
  display: flex;
  flex-wrap: wrap;
  gap: 0.125rem 0.75rem;
  font-size: 0.75rem;
  color: var(--gray-500);
}
.mail-preview-shared-history { font-size: 0.75rem; color: var(--gray-600); }
.mail-preview-shared-history summary { cursor: pointer; color: var(--navy-600); }
.mail-preview-shared-history ul { margin: 0.25rem 0 0; padding-left: 1rem; }
.mail-preview-shared-history li { display: flex; flex-wrap: wrap; gap: 0.125rem 0.75rem; }
.mail-preview-shared-actions { padding-top: 0.25rem; }
.mail-preview-shared-form { margin: 0; }
.mail-preview-shared-form .form-input { width: auto; padding: 0.25rem 1.75rem 0.25rem 0.5rem; font-size: 0.8125rem; }
[data-copy-link].is-copied { color: #166534; border-color: #86efac; }
.mail-preview-failed p { margin: 0; color: var(--gray-700); }

.mail-body-frame { display: block; width: 100%; min-height: 1.5rem; border: 0; }
.mail-body-plain { margin: 0; white-space: pre-wrap; word-break: break-word; font: inherit; }
.mail-body-empty { margin: 0; color: var(--gray-500); }
/* Links in a text mail (MailText.linkify): coloured and underlined — the app's own links are neither
   (a { color: inherit }) — and a long address wraps instead of widening the preview. */
.mail-body-plain a,
.email-body-plain a {
  color: #1d4ed8;
  text-decoration: underline;
  overflow-wrap: anywhere;
}
.mail-body-plain a:hover,
.email-body-plain a:hover { color: #1e40af; }
.mail-body-plain a:focus-visible,
.email-body-plain a:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 1px; border-radius: 2px; }

/* ── A message translated by the AI (static/js/mail-translate.js, email/translation.html) ──
   The button stands in the preview's tools beside formatted/plain; once translated its place is
   taken by the translation's bar, which sticks to the top of the preview while the reader scrolls
   (the white shadow covers the preview's own top padding, so text never shows above the bar). */
.mail-translate-start { display: inline-flex; align-items: center; gap: 0.375rem; }
.mail-translate-start svg,
.mail-translate-status svg { flex-shrink: 0; width: 1rem; height: 1rem; }
.mail-translate-icon { display: inline-flex; }
.mail-translate-spinner {
  display: none;
  width: 0.875rem;
  height: 0.875rem;
  border: 2px solid var(--navy-100);
  border-top-color: var(--navy-500);
  border-radius: 50%;
  animation: mail-refresh-spin 0.8s linear infinite;
}
.mail-translate.is-busy .mail-translate-icon { display: none; }
.mail-translate.is-busy .mail-translate-spinner { display: inline-block; }
.mail-translate.is-busy [data-translate-again] svg { animation: mail-refresh-spin 0.8s linear infinite; }
.mail-translate.is-translated .mail-translate-start { display: none; }
.mail-translate-status {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  color: var(--gray-600);
  font-size: 0.8125rem;
}
.mail-preview-tools.is-translated {
  position: sticky;
  top: calc(4rem + var(--mail-head-h, 0px));
  z-index: 2;
  padding-block: 0.375rem;
  background: var(--gray-50);
  border-bottom: 1px solid var(--gray-200);
}
/* Original | Translation | Side by side, and Formatted | Plain text: one segmented control each,
   words alone (two values of one kind wear no picture). The plan calendar's Routes | Drivers | Vehicles
   (views of a list, each wearing its mark) and Week | Month are the same control, its current segment a
   link with aria-current. */
:is(.mail-translate-views, .mail-body-views, .plan-cal-switch) { display: inline-flex; flex-wrap: wrap; }
:is(.mail-translate-views, .mail-body-views, .plan-cal-switch) .btn { border-radius: 0; }
:is(.mail-translate-views, .mail-body-views, .plan-cal-switch) .btn + .btn { margin-left: -1px; }
:is(.mail-translate-views, .mail-body-views, .plan-cal-switch) .btn:first-child { border-radius: var(--radius) 0 0 var(--radius); }
:is(.mail-translate-views, .mail-body-views, .plan-cal-switch) .btn:last-child { border-radius: 0 var(--radius) var(--radius) 0; }
:is(.mail-translate-views, .mail-body-views, .plan-cal-switch) :is(.btn[aria-pressed="true"], .btn[aria-current="true"]) {
  position: relative;
  z-index: 1;
  background-color: var(--navy-50);
  border-color: var(--navy-400);
  color: var(--navy-500);
}
.mail-translate-notes { display: flex; flex-direction: column; gap: 0.375rem; }
.mail-translate-notes:empty { display: none; }
.mail-translate-note { margin: 0; color: var(--gray-600); font-size: 0.8125rem; }
.mail-translate-note--warning {
  padding: 0.375rem 0.625rem;
  border-radius: var(--radius);
  background: var(--gold-50);
  color: var(--gold-700);
}
.mail-translate-error {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  padding: 0.375rem 0.625rem;
  border-radius: var(--radius);
  background: var(--red-50);
  color: var(--red-600);
  font-size: 0.8125rem;
}
/* The original and the translation: one column, or two side by side. The frames are as tall as
   their documents, so the preview's own scrolling moves both columns together. */
.mail-body-compare { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem; }
.mail-body-compare.has-translation[data-translate-view="both"] {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
}
.mail-body-compare.has-translation[data-translate-view="translation"] > .mail-body-side--original,
.mail-body-compare[data-translate-view="original"] > .mail-body-side--translation { display: none; }
.mail-body-side { min-width: 0; }
.mail-body-side-label {
  display: none;
  margin: 0 0 0.375rem;
  color: var(--gray-500);
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}
.mail-body-compare.has-translation[data-translate-view="both"] .mail-body-side-label { display: block; }
.mail-body-compare.has-translation[data-translate-view="both"] > .mail-body-side--translation {
  padding-left: 1rem;
  border-left: 2px solid var(--gold-200);
}
.mail-translate-subject { margin: 0 0 0.5rem; color: var(--gray-900); font-weight: 600; overflow-wrap: anywhere; }
.mail-translate-subject-label { margin-inline-end: 0.25rem; color: var(--gray-500); font-weight: 400; }

/* A mailbox's filing rules (email/rules.html): a rule's conditions read as one line joined by
   dots, a rule switched off is dimmed, and a rule into the Trash says so in red. */
.mail-rule-summary { color: var(--gray-700); }
.mail-rule-summary:hover { color: var(--navy-500); }
.mail-rule-cond + .mail-rule-cond::before { content: " · "; color: var(--gray-400); }
.mail-rules tr.mail-rule-off .mail-rule-summary { color: var(--gray-400); }
.mail-rule-trash { color: #b91c1c; font-weight: 600; }

/* An action taken on the list without a reload (static/js/mail-actions.js): the rows it is working
   on dim until the server answers, and the ones that leave the view fade rather than blink out.
   The dimming is the ONLY thing shown before the answer — whether a message still belongs in this
   view is the server's to say. */
.mail-table tbody tr.is-pending { opacity: 0.45; }
.mail-table tbody tr.is-pending td { cursor: progress; }
.mail-table tbody tr.is-leaving { opacity: 0; transition: opacity 0.15s ease-out; }
@media (prefers-reduced-motion: reduce) {
  .mail-table tbody tr.is-leaving { transition: none; }
}

/* The toast's button ("Undo"), beside its text. */
.vk-toast-text { margin-right: 0.75rem; }
.vk-toast-action {
  border: 0;
  border-radius: var(--radius);
  padding: 0.125rem 0.5rem;
  background: rgba(255, 255, 255, 0.2);
  color: inherit;
  font: inherit;
  font-weight: 600;
  cursor: pointer;
}
.vk-toast-action:hover { background: rgba(255, 255, 255, 0.32); }
.vk-toast-action:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }

.mail-visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}
/* ---------------------------------------------------------------------------
   Rows that open under themselves and trade their date for their actions
   (fragments/row-preview.html, js/row-preview.js, js/pdf-frame.js) — the mail list's
   mechanism for any table; the mail list keeps its own .mail-* copy.

   The last cell stacks the date and the actions in ONE grid cell, so the column is as wide
   as the wider of the two and nothing moves when they swap. The actions are hidden by
   opacity, never display: a keyboard still tabs to them, and :has(:focus-visible) shows
   them the moment it does. A device without hover sees them on an opened row; a table
   without previews (--touch-actions) shows them always there; a table whose actions belong
   to the opened row alone (--open-actions: the day board, the routes) shows them only there.
   --------------------------------------------------------------------------- */
.row-hover-table td.row-cell-when { width: 1%; text-align: right; white-space: nowrap; }
/* The script shows "collapse all" only while a row is open — [hidden] has to beat .btn's
   inline-flex, or the button stood on every list all the time. */
[data-row-preview-collapse-all][hidden],
[data-selected-count][hidden] { display: none !important; }
.row-when-stack { display: inline-grid; justify-items: end; align-items: center; vertical-align: middle; }
.row-when-stack > * { grid-area: 1 / 1; }
.row-when { color: var(--gray-500); font-variant-numeric: tabular-nums; transition: opacity 0.12s; }
/* The date and the actions share one grid area, and an element at opacity 0 is a stacking
   context painted like a positioned z-index 0 box: the hidden date would lie OVER the shown
   actions and take the pointer — no tooltip, and a click opened the row instead of the button.
   So the actions sit above the date for good, and a hidden date never takes the pointer. */
.row-actions {
  position: relative;
  z-index: 1;
  display: inline-flex;
  align-items: center;
  justify-content: flex-end;
  gap: 0.125rem;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.12s;
}
.row-actions form { display: inline-flex; margin: 0; }
/* A date alone in its stack (a list with no row actions, /invoices) never hides. */
@media (hover: hover) {
  .row-hover-table tbody tr[data-row-item]:hover .row-actions { opacity: 1; pointer-events: auto; }
  .row-hover-table tbody tr[data-row-item]:hover .row-when:not(:only-child) { opacity: 0; pointer-events: none; }
}
.row-hover-table tbody tr[data-row-item]:has(:focus-visible) .row-actions,
.row-hover-table tbody tr[data-row-item].is-expanded .row-actions { opacity: 1; pointer-events: auto; }
.row-hover-table tbody tr[data-row-item]:has(:focus-visible) .row-when:not(:only-child),
.row-hover-table tbody tr[data-row-item].is-expanded .row-when:not(:only-child) { opacity: 0; pointer-events: none; }
@media (hover: none) {
  .row-hover-table--touch-actions .row-actions { opacity: 1; pointer-events: auto; }
  .row-hover-table--touch-actions .row-when:not(:only-child) { opacity: 0; pointer-events: none; }
}
/* A row's actions only while it is OPEN (2026-10-23, decided with the user — the day board and the
   routes): under the pointer and with the keyboard on the row the last cell keeps its status. Hidden by
   visibility, not opacity alone, so the keyboard does not walk through buttons nobody sees: it reaches
   the row, Enter opens it, and then the actions. Each selector outranks the hover and focus rules above. */
.row-hover-table.row-hover-table--open-actions tbody tr[data-row-item]:not(.is-expanded) .row-actions {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}
.row-hover-table.row-hover-table--open-actions tbody tr[data-row-item]:not(.is-expanded) .row-when:not(:only-child) {
  opacity: 1;
  pointer-events: auto;
}
/* A row the live search hid takes its opened panel with it (fragments/live-search). */
.row-hover-table tbody tr[hidden] + tr.row-preview-row { display: none; }
.row-hover-table thead th:last-child { text-align: right; }
.row-hover-table th.row-col-check,
.row-hover-table td.row-cell-check { width: 4rem; white-space: nowrap; }
.row-cell-check input[type="checkbox"] { vertical-align: middle; }
/* A ticked row reads as selected (static/js/row-select.js), the mail list's tint. */
[data-row-select] tbody tr.is-selected,
[data-row-select] tbody tr.is-selected:hover { background-color: color-mix(in srgb, var(--navy-50) 80%, white); }
/* One task opened under its row on /tasks (templates/tasks/preview.html). */
.task-preview-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 1.25rem; }
@media (max-width: 720px) { .task-preview-grid { grid-template-columns: minmax(0, 1fr); } }
.task-preview-heading { margin: 0 0 0.375rem; font-size: 0.8125rem; font-weight: 600; color: var(--gray-700); }
.task-preview-heading + .entry-list { margin-bottom: 0.75rem; }
/* A table with a selection bar above it: the bar sits flush with the table's cells. */
.row-select-bar { margin-bottom: 0.5rem; }
.row-select-bar .form-input { width: 16rem; max-width: 100%; padding: 0.3125rem 0.625rem; font-size: 0.8125rem; }
.row-select-bar .row-select-summary { margin-left: auto; font-size: 0.8125rem; color: var(--gray-500); }
.row-hover-table th.row-col-expand,
.row-hover-table td.row-cell-expand { width: 2.25rem; padding-right: 0; white-space: nowrap; }
.row-cell-expand .row-expand { margin-left: 0; }
/* A row action's kind is said by its GROUND: its ValkOS mark keeps its own colours (the button's
   colour no longer reaches it). A destructive one turns red under the pointer and the keyboard; the
   row's main action (approve a leave request) rests on navy-50. The colour stays for the stroke icons
   still left (the compose window's, the last ones). */
.btn-icon-ghost.row-action-danger:hover,
.btn-icon-ghost.row-action-danger:focus-visible,
.btn-icon-ghost.mail-action-danger:hover,
.btn-icon-ghost.mail-action-danger:focus-visible {
  color: var(--red-600);
  background-color: var(--red-50);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--red-600) 45%, transparent);
}
.btn-icon-ghost.row-action-primary { color: var(--navy-500); background-color: var(--navy-50); }
.btn-icon-ghost.row-action-primary:hover { background-color: var(--navy-100); }
.row-actions .btn-icon-ghost:disabled { cursor: default; }
.row-actions .btn-icon-ghost:disabled:hover { background-color: transparent; color: var(--gray-500); }
/* An action that belongs to a row being READ (a bank payment, the source mail): only while it is open. */
.row-actions .row-action-expanded { display: none; }
.row-hover-table tbody tr.is-expanded .row-actions .row-action-expanded { display: inline-flex; }
.row-hover-table tbody tr[data-row-item][data-preview-id] { cursor: pointer; }
/* An opened row wears the brand's gold, a stripe down its left edge carried into the panel under it:
   a ticked row is navy and a row at rest its category's tint, so neither can be read for it. */
.row-hover-table tbody tr.is-expanded { background-color: var(--gold-100); }
.row-hover-table tbody tr.is-expanded > td:first-child { box-shadow: inset 3px 0 0 var(--gold-400); }

/* The chevron beside the checkbox: a symbol, not a button — the whole row opens it and the row is
   what the keyboard focuses. Hidden without leaving a gap, shown on hover, focus and while open. */
.row-expand {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  margin-left: 0.25rem;
  color: var(--gray-500);
  vertical-align: middle;
  visibility: hidden;
  transition: color 0.15s;
}
.row-expand svg { width: 1rem; height: 1rem; transition: transform 0.2s ease; }
.row-hover-table tbody tr[data-row-item]:focus { outline: none; }
.row-hover-table tbody tr[data-row-item]:focus-visible { outline: 2px solid var(--navy-400); outline-offset: -2px; }
.row-hover-table tbody tr[data-row-item]:focus-visible .row-expand { visibility: visible; }
@media (hover: hover) {
  .row-hover-table tbody tr:hover .row-expand { visibility: visible; }
}
@media (hover: none) {
  .row-expand { visibility: visible; }
}
.row-hover-table tbody tr.is-expanded .row-expand { visibility: visible; color: var(--navy-500); }
.row-hover-table tbody tr.is-expanded .row-expand svg { transform: rotate(180deg); }

.row-hover-table tbody tr.row-preview-row > td {
  padding: 0 0.75rem 0.75rem;
  white-space: normal;
  background-color: var(--gold-50);
  box-shadow: inset 3px 0 0 var(--gold-400);
}
.row-preview {
  padding: 0.875rem 1rem;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  font-size: 0.875rem;
  line-height: 1.5;
  text-align: left;
}
.row-preview-loading { display: flex; flex-direction: column; gap: 0.75rem; }
.row-preview-failed p { margin: 0 0 0.5rem; }
/* A table inside an opened row is the row's content, not a second page: no card around it. */
.row-preview .table-wrapper { margin: 0; }

/* An opened invoice row (invoice/previews.html): the facts beside the document, one column on a
   narrow screen with the facts first. */
.doc-preview { display: grid; grid-template-columns: minmax(14rem, 20rem) minmax(0, 1fr); gap: 1rem; align-items: start; }
.doc-preview-facts .dl-grid { grid-template-columns: 7rem minmax(0, 1fr); gap: 0.5rem 0.75rem; }
.doc-preview-facts .dl-dt { font-size: 0.75rem; }
.doc-preview-facts .dl-dd { font-size: 0.8125rem; }
.doc-preview-facts .badge { vertical-align: middle; }
.doc-preview-wide { grid-column: 1 / -1; }
/* An opened statement row (invoice/previews :: bankRow): the transfer's message, then its
   invoices or the proposals, each opening on its PDF in place. */
.bank-preview { display: flex; flex-direction: column; gap: 0.5rem; }
.bank-preview-message { margin: 0; white-space: pre-wrap; overflow-wrap: anywhere; }
.bank-preview .table-wrapper { margin-bottom: 0.25rem; }
/* The app leaves links looking like their text (a { color: inherit }); inside an opened row a
   link among plain facts must read as one. */
.doc-preview-facts .dl-dd a:not(.badge):not(.plate),
.bank-preview-more,
.bank-why a { color: var(--navy-600); text-decoration: underline; text-underline-offset: 2px; }
.doc-preview-heading { margin: 0 0 0.5rem; font-size: 0.875rem; font-weight: 600; color: var(--gray-700); }
@media (max-width: 900px) {
  .doc-preview { grid-template-columns: minmax(0, 1fr); }
}

/* A PDF in the page (fragments/pdf-frame): a bar with the name and "Open PDF", then the
   browser's viewer. The frame is never display:none (it would not load); a phone shows the bar
   alone. */
.pdf-frame { display: flex; flex-direction: column; gap: 0.5rem; }
.pdf-frame-bar { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.5rem; }
.pdf-frame-name { min-width: 0; overflow-wrap: anywhere; font-size: 0.8125rem; color: var(--gray-500); }
.pdf-frame-body { position: relative; }
.pdf-frame-view {
  display: block;
  width: 100%;
  height: min(75vh, 56rem);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  background: var(--gray-100);
}
.pdf-frame-status { margin: 0 0 0.5rem; }
.pdf-frame.is-failed .pdf-frame-view { height: 0; border: 0; }
.pdf-frame.is-phone .pdf-frame-body { display: none; }

/* Up to a tablet the table scrolls sideways. An OPENED row keeps its last cell — its actions —
   at the right edge of the screen, where they are tapped; a row at rest scrolls like the rest,
   or a pinned date would cover every row's description. The opened row itself is as wide as
   the screen rather than the table. */
@media (max-width: 900px) {
  .row-hover-table tbody tr.is-expanded td.row-cell-when {
    position: sticky;
    right: 0;
    background-color: var(--gold-100);
    box-shadow: -6px 0 6px -6px rgba(15, 23, 42, 0.2);
  }
  .row-hover-table tbody tr.row-preview-row > td { padding: 0 0 0.75rem; }
  .row-hover-table tbody tr.row-preview-row .row-preview {
    position: sticky;
    left: 0;
    width: calc(100vw - 2rem);
    max-width: 100%;
    box-sizing: border-box;
  }
}

/* The one shimmer in this application. It began as the mail preview's and is now also what
   stands in for a dashboard card while its own request is in flight, so the two cannot drift
   into looking like different products. The .mail-skeleton names are kept as aliases because
   the mail templates and their ITs still say them. */
.vk-skeleton,
.mail-skeleton {
  height: 0.75rem;
  border-radius: var(--radius);
  background: linear-gradient(90deg, var(--gray-100) 25%, var(--gray-200) 37%, var(--gray-100) 63%);
  background-size: 400% 100%;
  animation: vk-skeleton-shimmer 1.4s ease infinite;
}
.mail-skeleton-meta { width: 40%; height: 0.625rem; }
.mail-skeleton-short { width: 60%; }
.mail-skeleton-paper { display: flex; flex-direction: column; gap: 0.625rem; }
@keyframes vk-skeleton-shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}

/* A kept place is shaped like what will land in it: a card that will hold a chart must not be
   stood in for by three short lines, or the page jumps when the real thing arrives. */
.vk-skeleton-body { display: flex; flex-direction: column; gap: 0.75rem; margin-top: 0.75rem; }
.vk-skeleton-line { display: block; }
.vk-skeleton-kpi { display: block; height: 2rem; width: 45%; }
.vk-skeleton-kpis { display: flex; gap: 1rem; }
.vk-skeleton-kpis .vk-skeleton-kpi { flex: 1; width: auto; }
.vk-skeleton-chart { display: block; height: 9rem; }
.vk-skeleton-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr)); gap: 0.75rem; }
.vk-skeleton-tile { display: block; height: 5rem; }
.vk-skeleton-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr)); gap: 0.75rem; }
.vk-skeleton-card { display: block; height: 7rem; }
.vk-skeleton-faces { display: flex; flex-wrap: wrap; gap: 0.5rem; }
.vk-skeleton-face { display: block; width: 2.5rem; height: 2.5rem; border-radius: 50%; }
.vk-deferred-failed { display: flex; flex-direction: column; gap: 0.75rem; align-items: flex-start; }
@media (prefers-reduced-motion: reduce) {
  .vk-skeleton,
  .mail-skeleton { animation: none; }
  .mail-expand svg { transition: none; }
  .mail-translate-spinner,
  .mail-translate.is-busy [data-translate-again] svg { animation: none; }
}
@media (max-width: 900px) {
  .mail-preview { padding: 0.625rem; }
  .mail-preview-paper { padding: 0.875rem 1rem; }
  /* No side by side on a phone: the switch is two tabs, the translation first. */
  .mail-translate-both { display: none; }
  .mail-translate-views .btn:nth-last-child(2) { border-radius: 0 var(--radius) var(--radius) 0; }
}


.email-compose-body { resize: vertical; min-height: 12rem; }

/* ── AI compose assistant ───────────────────────────────────────────────────────────────── */
/* The writing assistant is a BAR at the foot of the message's box (2026-10-09, decided with the user —
   it was a button of the window's bar opening a panel inside the box): folded, one quiet line behind a
   hairline under the text; a click on it rolls the assistant up over the WHOLE text — the bar at its
   top, the settings, the chat filling the box, the line to write in at the bottom — and a click on the
   bar, Escape or "Insert" rolls it down again. */
.email-compose-body-wrapper {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  min-height: 0;
  background: var(--white);
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  transition: border-color 0.15s, box-shadow 0.15s;
}
/* The page without a window: an open assistant covers the box, so the box takes room for a chat. */
.mail-compose-page .email-compose-body-wrapper:has(.ai-compose-panel.is-open) { min-height: 22rem; }
.email-compose-body-wrapper:has(.email-compose-body:focus) {
  border-color: var(--navy-500);
  box-shadow: 0 0 0 3px rgba(34,59,96,0.15);
}
/* The text itself has no frame of its own: the box is its frame — and the application's letters, not
   the browser's monospace default for a textarea. */
.email-compose-body { font-family: inherit; line-height: 1.5; }
.email-compose-body-wrapper > .email-compose-body,
.email-compose-body-wrapper > .email-compose-body:focus {
  flex: 1 1 auto;
  min-width: 0;
  border: 0;
  border-radius: var(--radius) var(--radius) 0 0;
  box-shadow: none;
  background: transparent;
}

/* ── Compose form ───────────────────────────────────────────────────────────────────────── */
.mail-compose-form .mail-compose-field { margin-bottom: 0.65rem; }

.compose-reply-subject {
  text-align: center;
  color: var(--gray-900);
  font-weight: 500;
  margin-bottom: 1rem;
}

@keyframes ai-spin { to { transform: rotate(360deg); } }

.ai-compose-panel {
  /* relative: the anchor of the chat's sr-only speaker labels. NEVER a sideways scroll: what is too
     wide is cut or wraps (2026-10-09, the user). */
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
  flex: 0 0 auto;
  min-width: 0;
  min-height: 0;
  overflow: hidden;
  background: color-mix(in srgb, var(--navy-50) 35%, white);
  border-top: 1px dashed var(--gray-200);
  border-radius: 0 0 var(--radius) var(--radius);
  padding: 0.125rem 0.375rem;
}
.ai-compose-panel[hidden] { display: none; }
/* Folded: the bar alone. */
.ai-compose-panel:not(.is-open) > :not(.ai-compose-bar) { display: none; }
/* Open: over the whole box, revealed from its foot upwards. */
.ai-compose-panel.is-open {
  position: absolute;
  inset: 0;
  z-index: 3;
  border-top: 0;
  border-radius: var(--radius);
  padding: 0.25rem 0.5rem 0.5rem;
  animation: ai-roll-up 0.18s ease-out;
}
@keyframes ai-roll-up {
  from { clip-path: inset(100% 0 0 0); }
  to { clip-path: inset(0 0 0 0); }
}
@media (prefers-reduced-motion: reduce) {
  .ai-compose-panel.is-open { animation: none; }
}

/* The bar: the ✦, the assistant's name, the spinner while it writes, and the caret saying which way
   a click rolls it. */
.ai-compose-bar {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  width: 100%;
  min-width: 0;
  min-height: 1.75rem;
  flex: 0 0 auto;
  padding: 0.125rem 0.25rem;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  color: var(--navy-800);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
  text-align: start;
  cursor: pointer;
}
.ai-compose-bar:hover { background: color-mix(in srgb, var(--navy-50) 80%, white); }
.ai-compose-bar:focus-visible { outline: 2px solid var(--navy-400); outline-offset: -2px; }
.ai-compose-bar > .vk-mark { flex: none; width: 1rem; height: 1rem; } /* action:generate, the assistant's mark */
.ai-compose-bar-label { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ai-compose-bar-caret { display: inline-flex; flex: none; color: var(--gray-500); transition: rotate 0.15s ease; }
.ai-compose-bar-caret .vk-mark { width: 14px; height: 14px; }
.ai-compose-bar[aria-expanded="true"] .ai-compose-bar-caret { rotate: 180deg; }
.ai-compose-bar .btn-ai-spinner {
  display: none;
  flex: none;
  width: 0.875rem;
  height: 0.875rem;
  border: 2px solid var(--gold-400);
  border-top-color: transparent;
  border-radius: 50%;
  animation: ai-spin 0.7s linear infinite;
}
.ai-compose-bar.loading .btn-ai-spinner { display: block; }

/* ONE line of settings, and "start again" at its end. */
.ai-compose-panel-header {
  display: flex;
  align-items: center;
  gap: 0.375rem;
  min-width: 0;
  flex: 0 0 auto;
}
.ai-compose-panel-tools { display: inline-flex; gap: 0.125rem; flex: 0 0 auto; margin-inline-start: auto; }
.ai-compose-panel-tools .btn-icon-ghost { width: 1.75rem; height: 1.75rem; }

/* The four settings as icon menus in one row (fragments/choice-menu.html, js/choice-menu.js):
   a pill showing the choice's icon and label, the list in the mail menus' look under it. */
.ai-compose-options { display: flex; flex-wrap: wrap; gap: 0.25rem; min-width: 0; flex: 1 1 auto; }
/* The first pill names two choices ("Formal · Polite and professional"): cut short so the settings keep
   to one line in a window — the whole choice is in its title. */
.ai-compose-options .choice-menu:first-child .choice-menu-trigger { max-width: 11rem; }
.choice-menu { position: relative; display: inline-block; max-width: 100%; min-width: 0; }
.choice-menu-trigger {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  min-width: 0;
  max-width: 100%;
  padding: 0.25rem 0.55rem;
  border: 1px solid var(--gray-300);
  border-radius: 999px;
  background: var(--white);
  color: var(--gray-800);
  font: inherit;
  font-size: 0.75rem;
  line-height: 1.2;
  white-space: nowrap;
  cursor: pointer;
  transition: background-color 0.15s, border-color 0.15s;
}
.choice-menu-trigger:hover { background: var(--gray-50); border-color: var(--gray-400); }
.choice-menu-trigger[aria-expanded="true"] { background: var(--navy-50); border-color: var(--navy-400); }
.choice-menu-trigger:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 1px; }
.choice-menu-icon { display: inline-flex; }
.choice-menu-icon[hidden] { display: none; } /* an item in words: no box */
.choice-menu-trigger > svg { width: 0.75rem; height: 0.75rem; color: var(--gray-500); }
.choice-menu-current { min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.choice-menu-item[aria-selected="true"] { font-weight: 600; }
.choice-menu-item[aria-selected="true"] > .mail-menu-check { visibility: visible; }
/* A form field as a choice menu (the expectation rule's measure): the trigger takes the select's
   box, and the select it drives shows until the script has bound the menu — then only the
   trigger does. Without JavaScript nothing binds, so the plain select stays. */
.choice-menu--field { display: block; }
.choice-menu--field:not([data-choice-bound]) > .choice-menu-trigger { display: none; }
.choice-menu--field[data-choice-bound] > select[data-choice] { display: none; }
.choice-menu--field > .choice-menu-trigger {
  width: 100%; gap: 0.5rem; padding: 0.625rem 0.875rem; border-radius: var(--radius);
  font-size: 0.9375rem; line-height: 1.5;
}
.choice-menu--field .choice-menu-current { flex: 1; text-align: start; }

/* The chat: the writer at the right in navy, the assistant at the left in grey, newest at the
   bottom; a draft is a white sheet inside the assistant's bubble with its two buttons. */
.ai-chat {
  flex: 1 1 auto;
  min-height: 0;
  overflow-x: hidden;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 0.125rem 0;
}
.ai-compose-hint {
  margin: auto 0;
  padding: 0.5rem 1rem;
  font-size: 0.78rem;
  color: var(--gray-500);
  text-align: center;
}
.ai-msg {
  max-width: 92%;
  padding: 0.45rem 0.65rem;
  border-radius: 0.75rem;
  font-size: 0.8125rem;
  line-height: 1.45;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.ai-msg--user { align-self: flex-end; background: var(--navy-500); color: var(--white); border-bottom-right-radius: 0.25rem; }
.ai-msg--assistant { align-self: flex-start; background: var(--gray-100); color: var(--gray-800); border-bottom-left-radius: 0.25rem; }
.ai-msg--thinking { color: var(--gray-500); }
/* A card of questions (mail-compose.js questionCard, 2026-10-21): radio buttons for a question of
   one answer, checkboxes for one of several, a line for an answer of one's own, one "Confirm
   answers". A white sheet like the draft's; the bubble takes its full width for it. */
.ai-msg--assistant:has(> .ai-questions) { width: 92%; }
.ai-questions {
  display: flex;
  flex-direction: column;
  gap: 0.6rem;
  margin-top: 0.4rem;
  padding: 0.55rem 0.6rem;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  color: var(--gray-900);
  white-space: normal;
}
.ai-question { margin: 0; padding: 0; border: 0; min-width: 0; }
.ai-question-text { padding: 0; font-weight: 600; overflow-wrap: anywhere; }
.ai-question-hint { margin: 0.1rem 0 0; font-size: 0.72rem; color: var(--gray-500); }
.ai-options { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.35rem; }
.ai-option {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  max-width: 100%;
  min-height: 1.75rem;
  padding: 0.25rem 0.6rem 0.25rem 0.45rem;
  border: 1px solid var(--navy-200);
  border-radius: 999px;
  background: var(--white);
  color: var(--navy-700);
  font-size: 0.75rem;
  line-height: 1.3;
  cursor: pointer;
}
.ai-option > span { min-width: 0; overflow-wrap: anywhere; }
.ai-option input[type="radio"],
.ai-option input[type="checkbox"] { width: 0.875rem; height: 0.875rem; margin: 0; }
.ai-option input[type="checkbox"]:checked { background-size: 0.6rem; }
.ai-option:hover { background: var(--navy-50); border-color: var(--navy-400); }
.ai-option:has(input:checked) { background: var(--navy-50); border-color: var(--navy-500); color: var(--navy-800); }
.ai-option:has(input:focus-visible) { outline: 2px solid var(--navy-400); outline-offset: 1px; }
.ai-option-other {
  display: block;
  width: 100%;
  margin-top: 0.35rem;
  padding: 0.25rem 0.5rem;
  font: inherit;
  font-size: 0.75rem;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  background: var(--white);
  color: var(--gray-800);
}
/* "Other answer": an option of the group with its line inside the pill. The pill takes what the
   others leave and wraps under them when that is too little; the line has no frame of its own (the
   pill's ring is its focus), and its words are grey while the option is not ticked — they do not count. */
.ai-option--other { flex: 1 1 10rem; min-width: 8rem; cursor: default; }
.ai-option--other .ai-option-other {
  flex: 1 1 auto;
  width: auto;
  min-width: 0;
  margin: 0;
  padding: 0;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: inherit;
  cursor: text;
}
.ai-option--other .ai-option-other:focus { box-shadow: none; }
.ai-option--other:not(:has(input:checked)) .ai-option-other { color: var(--gray-500); }
.ai-option--other:focus-within { outline: 2px solid var(--navy-400); outline-offset: 1px; }
.ai-option-other:focus { outline: none; border-color: var(--navy-400); box-shadow: 0 0 0 2px rgba(34,59,96,.12); }
.ai-questions-actions { display: flex; flex-wrap: wrap; gap: 0.375rem; }
.ai-questions-actions .btn { max-width: 100%; white-space: normal; text-align: start; }
/* Answered, or passed over by what the writer said instead: kept in the log as it was, nothing in
   it acts (setFrozen disables every control) and the button is gone. */
.ai-questions.is-answered { background: var(--gray-50); }
.ai-questions.is-answered .ai-questions-actions { display: none; }
.ai-questions.is-answered .ai-option { cursor: default; background: transparent; border-color: var(--gray-200); color: var(--gray-500); }
.ai-questions.is-answered .ai-option:has(input:checked) { border-color: var(--gray-400); color: var(--gray-800); }
.ai-questions.is-answered .ai-option-other { background: transparent; color: var(--gray-800); }
.ai-questions.is-answered .ai-option-other:placeholder-shown { display: none; }
.ai-questions.is-answered .ai-option--other:not(:has(input:checked)),
.ai-questions.is-answered .ai-option--other:has(.ai-option-other:placeholder-shown) { display: none; }
.ai-chips { display: flex; flex-wrap: wrap; gap: 0.3rem; margin-top: 0.35rem; }
.ai-chip {
  padding: 0.2rem 0.55rem;
  border: 1px solid var(--navy-200);
  border-radius: 999px;
  background: var(--white);
  color: var(--navy-700);
  font: inherit;
  font-size: 0.75rem;
  line-height: 1.3;
  text-align: left;
  cursor: pointer;
}
.ai-chip:hover { background: var(--navy-50); border-color: var(--navy-400); }
.ai-chip:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 1px; }
.ai-draft {
  margin-top: 0.4rem;
  padding: 0.5rem 0.6rem;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  color: var(--gray-900);
  white-space: pre-wrap;
}
/* The subject "Insert" puts into an empty subject field, above the text it names. */
.ai-draft-subject { margin-top: 0.4rem; color: var(--gray-900); font-weight: 600; overflow-wrap: anywhere; }
.ai-draft-subject-label { color: var(--gray-500); font-weight: 500; }
.ai-draft-actions { display: flex; flex-wrap: wrap; gap: 0.375rem; margin-top: 0.4rem; }
.ai-draft-actions .btn { max-width: 100%; white-space: normal; text-align: start; }

/* The line to the assistant and its send button. */
.ai-compose-composer { display: flex; align-items: flex-end; gap: 0.375rem; flex: 0 0 auto; }
.ai-compose-input {
  flex: 1 1 auto;
  min-width: 0;
  max-height: 5rem;
  resize: none;
  font: inherit;
  font-size: 0.8125rem;
  padding: 0.35rem 0.5rem;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius);
  background: var(--white);
  color: var(--gray-800);
}
.ai-compose-input:focus {
  outline: none;
  border-color: var(--navy-400);
  box-shadow: 0 0 0 2px rgba(34,59,96,.12);
}
.ai-compose-send { width: 2rem; height: 2rem; }
.ai-compose-send:disabled { opacity: 0.5; cursor: default; }

.ai-compose-error {
  flex: 0 0 auto;
  font-size: 0.78rem;
  color: var(--red-600);
  line-height: 1.4;
  overflow-wrap: anywhere;
}
@media (prefers-reduced-motion: no-preference) {
  .ai-msg--thinking { animation: ai-pulse 1.2s ease-in-out infinite; }
  @keyframes ai-pulse { 50% { opacity: 0.55; } }
}

/* ── Toast notification ────────────────────────────────────────────────────────────────── */
#vk-toast {
  position: fixed;
  bottom: 1.5rem;
  right: 1.5rem;
  z-index: 9999;
  padding: 0.75rem 1.25rem;
  border-radius: 0.5rem;
  font-size: 0.9rem;
  font-weight: 500;
  box-shadow: 0 4px 16px rgba(0,0,0,.18);
  max-width: 22rem;
  opacity: 0;
  transform: translateY(0.5rem);
  transition: opacity 0.25s ease, transform 0.25s ease;
  pointer-events: none;
}
#vk-toast.vk-toast--visible {
  opacity: 1;
  transform: translateY(0);
  pointer-events: auto;
}
/* The toast leads with its state's mark, like an alert (toast.js takes it from the sprite). */
#vk-toast { display: flex; align-items: center; gap: 0.5rem; }
#vk-toast .vk-toast-mark { width: 1.25em; height: 1.25em; flex: none; }
#vk-toast.vk-toast--success .vk-toast-mark { color: var(--green-600); }
#vk-toast.vk-toast--error .vk-toast-mark { color: var(--red-600); }
#vk-toast.vk-toast--success {
  background-color: var(--green-50);
  border: 1px solid #86efac;
  color: #166534;
}
#vk-toast.vk-toast--error {
  background-color: var(--red-50);
  border: 1px solid #fca5a5;
  color: #991b1b;
}

/* ── Browser push notification permission banner ─────────────────────────────────────── */
#push-permission-banner {
  position: fixed;
  bottom: 1.5rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 8000;
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  background: var(--navy-500);
  color: #fff;
  padding: 0.85rem 1.25rem;
  border-radius: 0.6rem;
  box-shadow: 0 4px 20px rgba(0,0,0,.28);
  max-width: 42rem;
  width: calc(100% - 2rem);
  font-size: 0.875rem;
}
.push-banner-msg {
  flex: 1;
  min-width: 0;
  line-height: 1.4;
}
.push-banner-actions {
  display: flex;
  gap: 0.5rem;
  flex-shrink: 0;
}
#push-permission-banner .btn-primary {
  background-color: var(--gold-400, #d4a843);
  color: var(--navy-700, #1a2d49);
  border-color: transparent;
}
#push-permission-banner .btn-primary:hover {
  background-color: var(--gold-300, #e0bc6a);
}
#push-permission-banner .btn-outline {
  border-color: rgba(255,255,255,.45);
  color: #fff;
}
#push-permission-banner .btn-outline:hover {
  background: rgba(255,255,255,.12);
}
@media (max-width: 520px) {
  #push-permission-banner {
    bottom: 0;
    left: 0;
    right: 0;
    transform: none;
    width: 100%;
    border-radius: 0.6rem 0.6rem 0 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0.75rem;
  }
  .push-banner-actions {
    flex-direction: column;
  }
  .push-banner-actions .btn {
    width: 100%;
    justify-content: center;
    padding: 0.65rem 1rem;
    min-height: 2.75rem;
  }
}

/* ── Preview-as-user strip ───────────────────────────────────────────────────
   The geometry of #push-permission-banner above, one z-index band higher so it
   stays over the mail compose dock (210) and that banner (8000). Bottom-fixed
   rather than a band above the navbar: "4rem" is hardcoded as "below the navbar"
   in several rules here and in layout.html's own <style>, and a strip in the
   document flow would leave all of them a bar's height wrong.
   Gold while read-only, red once changes are unlocked — the state has to be
   readable from across the room. */
#vk-preview-banner {
  position: fixed;
  bottom: 1.5rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 8100;
  display: flex;
  align-items: center;
  gap: 1rem;
  flex-wrap: wrap;
  background: var(--gold-400);
  color: var(--navy-700);
  padding: 0.7rem 1.1rem;
  border-radius: 0.6rem;
  box-shadow: 0 4px 20px rgba(0,0,0,.28);
  max-width: 44rem;
  width: calc(100% - 2rem);
  font-size: 0.875rem;
}
#vk-preview-banner.preview-bar--writing {
  background: var(--red-600);
  color: #fff;
}
.preview-bar-msg {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 0.5rem;
  line-height: 1.4;
  font-weight: 600;
}
.preview-bar-mode {
  text-transform: uppercase;
  letter-spacing: .04em;
  font-size: .75rem;
  padding: .1rem .45rem;
  border-radius: .25rem;
  background: rgba(255,255,255,.22);
}
.preview-bar-actions {
  display: flex;
  gap: 0.5rem;
  flex-shrink: 0;
}
#vk-preview-banner .btn-primary {
  background-color: var(--navy-700);
  color: #fff;
  border-color: transparent;
}
#vk-preview-banner .btn-primary:hover {
  background-color: var(--navy-500);
}
#vk-preview-banner .btn-outline {
  border-color: rgba(26,45,77,.45);
  color: var(--navy-700);
}
#vk-preview-banner .btn-outline:hover {
  background: rgba(26,45,77,.1);
}
#vk-preview-banner.preview-bar--writing .btn-outline {
  border-color: rgba(255,255,255,.55);
  color: #fff;
}
#vk-preview-banner.preview-bar--writing .btn-outline:hover {
  background: rgba(255,255,255,.14);
}
#vk-preview-banner.preview-bar--writing .btn-primary {
  background-color: #fff;
  color: var(--red-600);
}
@media (max-width: 520px) {
  #vk-preview-banner {
    bottom: 0;
    left: 0;
    right: 0;
    transform: none;
    width: 100%;
    border-radius: 0.6rem 0.6rem 0 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0.75rem;
  }
  .preview-bar-actions {
    flex-direction: column;
  }
  .preview-bar-actions form,
  .preview-bar-actions .btn {
    width: 100%;
    justify-content: center;
    padding: 0.65rem 1rem;
    min-height: 2.75rem;
  }
}

/* ── Notification page settings strip ──────────────────────────────────────── */
.notif-page-controls {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 1.25rem;
  flex-wrap: wrap;
}
.notif-page-permission-btn {
  width: auto !important;
  border: 1px solid var(--red-400, #f87171) !important;
  border-bottom: 1px solid var(--red-400, #f87171) !important;
  border-radius: 0.375rem;
  padding: 0.4rem 0.75rem !important;
  font-size: 0.8rem !important;
}
.notif-page-sound-btn {
  color: var(--navy-500, #223b60) !important;
  border: 1px solid var(--gray-300, #d1d5db);
  border-radius: 0.375rem;
  padding: 0.4rem 0.6rem !important;
}
.notif-page-sound-btn:hover {
  color: var(--navy-700, #1a2d49) !important;
  background: var(--gray-100, #f3f4f6);
}
.notif-page-sound-btn.sound-muted {
  color: var(--gray-500, #6b7280) !important;
}

.email-body-plain pre {
  white-space: pre-wrap;
  word-break: break-word;
  font-size: 0.875rem;
  margin: 0;
}

.email-body-html {
  max-width: 100%;
  overflow-x: auto;
}
.email-body-html * { max-width: 100%; }

.email-body-tabs { display: flex; gap: 0.5rem; }

.email-attachment-list { list-style: none; }

/* The direction badges of the conversation screen (email/thread.html). */
.badge-sent {
  background: var(--navy-50, #eef1f7);
  color: var(--navy-700, #1a2d49);
  border: 1px solid var(--navy-200, #bacae4);
  padding: 0.15rem 0.5rem;
  border-radius: 0.25rem;
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
}
.badge-received {
  background: var(--gold-50, #fdf8ec);
  color: var(--gold-800, #7d5c10);
  border: 1px solid var(--gold-200, #edd9a3);
  padding: 0.15rem 0.5rem;
  border-radius: 0.25rem;
  font-size: 0.75rem;
  font-weight: 600;
  white-space: nowrap;
}

@media (max-width: 768px) {
  .email-layout {
    grid-template-columns: 1fr;
  }
  .email-sidebar {
    border-bottom: 1px solid var(--gray-200);
    padding-bottom: 1rem;
  }
  .email-toolbar {
    flex-direction: column;
    align-items: stretch;
  }
  .email-toolbar > form {
    display: flex;
    gap: 0.5rem;
  }
  .email-search-input {
    width: 100%;
  }
}

/* ================================================================
   DIALOG (native <dialog>)
   ================================================================ */
.app-dialog {
  border: none;
  border-radius: var(--radius-lg);
  padding: 1.5rem;
  max-width: 28rem;
  width: calc(100% - 2rem);
  box-shadow: var(--shadow-lg);
  color: var(--gray-800);
  /* Restore native modal centering — the global `* { margin: 0 }` reset
     (top of this file) otherwise overrides the UA `dialog { margin: auto }`
     and pins the dialog to the top-left of the modal layer. */
  margin: auto;
}
.app-dialog::backdrop {
  background-color: rgba(0, 0, 0, 0.4);
}
.app-dialog-title {
  margin: 0 0 0.5rem;
  font-size: 1.125rem;
  font-weight: 600;
}
.app-dialog-body {
  margin: 0 0 1.25rem;
  color: var(--gray-700);
}
.app-dialog-actions {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.app-dialog-actions--row {
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* Recipient address list + in-dialog editor */
.recipient-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.375rem;
}
.recipient-editor-list {
  list-style: none;
  margin: 0 0 0.75rem;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}
.recipient-editor-row {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}
.recipient-editor-row .form-input {
  flex: 1 1 auto;
}
.recipient-editor-add {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1.25rem;
}
.recipient-editor-add .form-input {
  flex: 1 1 auto;
}

/* ================================================================
   DATEPICKER (custom input[type=date] UI — js/datepicker.js)
   ================================================================ */
.vk-datepicker {
  position: relative;
  display: block;
}
.vk-datepicker > input {
  padding-right: 2.5rem;
}
.vk-datepicker-toggle {
  position: absolute;
  right: 0.5rem;
  top: 50%;
  transform: translateY(-50%);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: transparent;
  padding: 0;
  color: var(--gray-500);
  cursor: pointer;
}
.vk-datepicker-toggle:hover { color: var(--navy-500); }
.vk-datepicker-toggle:disabled {
  color: var(--gray-300);
  cursor: default;
}
.vk-datepicker-toggle svg {
  width: 1.125rem;
  height: 1.125rem;
}
/* The date's mark (subject:date) is navy and gold, so the toggle answers the pointer by the mark's
   strength, the rail's way: 0.75 at rest, whole under the pointer. */
.vk-datepicker-toggle .vk-mark { opacity: 0.75; }
.vk-datepicker-toggle:hover .vk-mark,
.vk-datepicker-toggle:focus-visible .vk-mark { opacity: 1; }
.vk-datepicker-toggle:disabled .vk-mark { opacity: 0.4; filter: grayscale(1); }
.vk-datepicker-popup {
  position: absolute;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 0.75rem;
  width: 19rem;
  z-index: 100;
}
.vk-datepicker-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 0.5rem;
}
.vk-datepicker-label {
  font-weight: 600;
  font-size: 0.9375rem;
  color: var(--gray-800);
}
.vk-datepicker-nav-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2rem;
  height: 2rem;
  border: none;
  background: transparent;
  border-radius: var(--radius);
  color: var(--gray-600);
  cursor: pointer;
}
.vk-datepicker-nav-btn:hover {
  background: var(--gold-50);
  color: var(--navy-500);
}
.vk-datepicker-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 2px;
}
.vk-datepicker-weekday {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--gray-500);
  text-align: center;
  padding: 0.25rem 0;
}
.vk-datepicker-day {
  aspect-ratio: 1;
  border: none;
  background: transparent;
  border-radius: var(--radius);
  cursor: pointer;
  font: inherit;
  font-size: 0.875rem;
  color: var(--gray-800);
}
.vk-datepicker-day:hover { background: var(--gold-50); }
.vk-datepicker-day.is-outside { color: var(--gray-400); }
.vk-datepicker-day.is-today { box-shadow: inset 0 0 0 1px var(--gold-400); }
.vk-datepicker-day.is-selected,
.vk-datepicker-day.is-selected:hover {
  background: var(--navy-500);
  color: var(--white);
}
.vk-datepicker-day:disabled {
  color: var(--gray-300);
  cursor: default;
}
.vk-datepicker-day:disabled:hover { background: transparent; }
.vk-datepicker-day:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(34,59,96,0.15);
}
.vk-datepicker-footer {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--gray-100);
}
.vk-datepicker-footer-btn {
  border: none;
  background: transparent;
  padding: 0.25rem 0.375rem;
  border-radius: var(--radius);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--navy-500);
  cursor: pointer;
}
.vk-datepicker-footer-btn:hover {
  color: var(--gold-500);
  text-decoration: underline;
}
@media (max-width: 480px) {
  /* JS sets inline top/left — override with !important and center in viewport */
  .vk-datepicker-popup {
    position: fixed !important;
    left: 50% !important;
    top: 50% !important;
    transform: translate(-50%, -50%);
  }
}

/* ── The password vault (vault/) and its two-factor codes ─────────────── */
.totp-card {
  display: flex;
  flex-direction: column;
}
.totp-issuer {
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--gray-800);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.totp-account {
  font-size: 0.8125rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.totp-code-row {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}
.totp-code {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 1.75rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--navy-500);
  text-align: center;
  padding: 0.25rem 0;
  user-select: all;
}
.totp-copy {
  flex: none;
}
.totp-copy.is-copied {
  color: var(--green-600);
  border-color: var(--green-600);
}
/* The copy mark and the check vault.js swaps in for a moment: out of the baseline, like an svg alone. */
.totp-copy-icon, .totp-copy-check { display: inline-flex; }
.totp-countdown {
  height: 4px;
  border-radius: var(--radius-full);
  background-color: var(--gray-200);
  overflow: hidden;
}
.totp-countdown-bar {
  /* block: in a table cell the bar is a <span>, and an inline box ignores width and height —
     the track showed grey and never moved. */
  display: block;
  height: 100%;
  width: 100%;
  background-color: var(--gold-500);
  transition: width 1s linear;
}
/* The two marks of a copy button take turns through the hidden attribute — which the
   inline-flex above would otherwise beat. The same for everything the vault's scripts hide. */
.totp-copy [hidden], .vault-copy [hidden],
.vault-totp-dialog [hidden], [data-vault-form] [hidden], .vault-generator[hidden] { display: none !important; }
/* A code in a table cell or a property: the card's big digits, at text size. */
.totp-code--inline { font-size: 1rem; letter-spacing: 0.02em; padding: 0; text-align: start; }
/* A code and its copy button on one line, its countdown under them, as wide as that line. */
.vault-code-line { display: flex; align-items: center; gap: 0.25rem; }
.vault-code-line > button { flex: none; }
.totp-countdown--inline { display: block; width: 100%; height: 3px; }

/* "Vault open — it locks after 15 minutes without activity", and the Lock button. */
.vault-strip {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
  padding: 0.5rem 0.75rem;
  margin-bottom: 1rem;
  border: 1px solid var(--navy-100);
  border-radius: var(--radius);
  background-color: var(--navy-50);
  color: var(--navy-700);
  font-size: 0.875rem;
}
.vault-strip .vault-lock-form { margin-inline-start: auto; }
/* The door: one narrow card in the middle of the page. */
.vault-door { max-width: 28rem; margin: 2rem auto; }

/* A secret's placeholder (or its value once shown) with its two buttons; a value with its copy. */
.vault-secret, .vault-plain, .vault-code {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem;
  min-width: 0;
  max-width: 100%;
}
/* A code is a small column: the code and its copy button, the countdown under them. */
.vault-code { flex-direction: column; flex-wrap: nowrap; align-items: stretch; }
.vault-secret-value {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  letter-spacing: 0.05em;
  color: var(--gray-500);
}
.vault-secret.is-shown .vault-secret-value {
  color: var(--gray-900);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
.vault-plain-value { overflow-wrap: anywhere; }
.vault-copy.is-copied, .totp-copy.is-copied { color: var(--green-600); }
.vault-plain-multiline { white-space: pre-wrap; overflow-wrap: anywhere; margin: 0; }

/* The list, the mail list's way: fixed columns on a wide page (vault-screen) and 13 px type. Only
   the name column flexes; every other cell keeps its content inside its own width — an ellipsis,
   or a revealed secret wrapping — so on a computer the table never scrolls sideways. Below 46rem
   (a phone) the wrapper scrolls, never the page (the .invoice-table pattern). */
.vault-table { table-layout: fixed; min-width: 50rem; font-size: 0.8125rem; }
.vault-table th, .vault-table td { padding: 0.5rem 0.75rem; }
.vault-table td { vertical-align: top; }
/* Shares of the table, so a wider screen widens every column and not the name alone (it took 47 %
   at 1600 px with fixed widths). At the 50rem minimum each still holds its content: six dots and
   two buttons, a code and its copy button, the date or three row actions. */
.vault-table .vault-col-check { width: 4.25rem; }
.vault-table .vault-col-login { width: 20%; }
.vault-table .vault-col-secret { width: 17%; }
.vault-table .vault-col-code { width: 14%; }
.vault-table .vault-col-updated { width: 13%; }
/* The kind's mark before the name (beside its FIRST line when it wraps); the address, category and
   validity under it, smaller. */
.vault-name { display: inline-flex; align-items: flex-start; gap: 0.375rem; max-width: 100%;
  font-size: 0.875rem; font-weight: 600; color: var(--gray-800); }
.vault-name > .vk-mark { flex: none; margin-top: 0.2em; }
.vault-title { min-width: 0; overflow-wrap: anywhere; }
.vault-name-meta { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.5rem; margin-top: 0.125rem;
  min-width: 0; font-size: 0.75rem; }
.vault-url {
  color: var(--navy-500);
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.vault-filter-q { flex: 1 1 16rem; }
/* A login, a secret and a code each keep to one line beside their buttons — a value broken mid-word
   ("07019912 / 345") reads as two values — and the login gives way with an ellipsis (its whole text
   in the title). A secret once shown wraps under its buttons instead of widening the column. */
.vault-table .vault-plain, .vault-table .vault-secret { display: flex; flex-wrap: nowrap; }
.vault-table .vault-code { display: inline-flex; }
.vault-table .vault-plain-value {
  flex: 0 1 auto;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.vault-table .vault-plain > button, .vault-table .vault-secret > button { flex: none; }
.vault-table .vault-secret:not(.is-shown) .vault-secret-value,
.totp-code--inline { white-space: nowrap; }
.vault-table .vault-secret.is-shown { flex-wrap: wrap; }
.vault-table .vault-secret.is-shown .vault-secret-value { flex-basis: 100%; min-width: 0; }
.vault-table .totp-code--inline { font-size: 0.875rem; }

/* An entry: its properties label beside value, a label over its value on a phone. */
.vault-props {
  display: grid;
  grid-template-columns: minmax(8rem, max-content) 1fr;
  gap: 0.5rem 1.25rem;
  margin: 0 0 1rem;
}
.vault-props > div { display: contents; }
.vault-props dt { color: var(--gray-500); font-size: 0.8125rem; padding-top: 0.125rem; }
.vault-props dd { margin: 0; min-width: 0; font-size: 0.875rem; }
@media (max-width: 480px) {
  .vault-props { grid-template-columns: 1fr; gap: 0.125rem; }
  .vault-props dd { margin-bottom: 0.5rem; }
}
.vault-link { color: var(--navy-500); overflow-wrap: anywhere; }
.vault-age { margin-inline-start: 0.5rem; }
.vault-setup-key { margin-top: 0.25rem; }
.vault-setup-key summary { cursor: pointer; color: var(--gray-500); }
.vault-section, .vault-notes, .vault-move { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--gray-200); }
.vault-section-title { font-size: 0.875rem; font-weight: 600; color: var(--gray-700); margin: 0 0 0.5rem; }
.vault-files, .vault-history { list-style: none; margin: 0 0 0.5rem; padding: 0; display: grid; gap: 0.375rem;
  font-size: 0.875rem; }
.vault-notes .vault-plain-multiline { font-size: 0.875rem; }
/* The wide vault page is for the list: an entry and its form keep to a readable width. */
.vault-main--detail { max-width: 72rem; }
.vault-main--form { max-width: 60rem; }
.vault-history-what { font-weight: 500; }
.vault-upload .form-input { flex: 1 1 14rem; min-width: 0; }
.vault-actions { margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--gray-200); }

/* The form: a custom field is a label, a value and its "secret" box on one line where there is room. */
.vault-field-row { padding: 0.5rem 0; border-bottom: 1px dashed var(--gray-200); }
.vault-field-label { flex: 1 1 10rem; min-width: 0; }
.vault-field-value { flex: 2 1 14rem; min-width: 0; }
.vault-secret-field { flex: 1 1 16rem; min-width: 0; }
.vault-generated {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 1rem;
  overflow-wrap: anywhere;
  min-height: 1.5rem;
}

/* ============================================================
   User profile wizard (step indicator above the section cards)
   ============================================================ */

.wizard-steps {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 1.25rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--gray-200);
  border-left: 3px solid var(--gold-400);
  border-radius: 6px;
  background: var(--gray-50);
  max-width: 28rem;
}

.wizard-step-label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--navy-700);
  flex: 1;
}

/* Sub-tabs (e.g. urlop / sick leave inside the absences card) */
.subtab-nav {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1rem;
}

.subtab-nav a {
  padding: 0.35rem 0.75rem;
  border-radius: 6px;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--gray-600);
  text-decoration: none;
}

.subtab-nav a.subtab-active {
  color: var(--navy-700);
  background: var(--gold-100);
  border-bottom: 2px solid var(--gold-500);
}

/* ============================================================
   Avatars — every people-listing view (navbar, user list/view,
   profile rail + Contact tab, absence queue, driver linking,
   notification admin). Point an <img> at /users/{id}/photo;
   the endpoint serves a default SVG, so no hasPhoto check.
   ============================================================ */

.avatar {
  border-radius: 50%;
  object-fit: cover;
  background: var(--gray-100);
  flex-shrink: 0;
}

.avatar-sm {
  width: 28px;
  height: 28px;
}

.avatar-lg {
  width: 96px;
  height: 96px;
  border: 2px solid var(--gray-200);
}

/* ============================================================
   Photo crop (profile Contact tab → crop screen)
   ============================================================ */

.photo-crop-stage {
  position: relative;
  display: inline-block;
  max-width: 100%;
  overflow: hidden; /* clips the box's mask shadow to the image area */
  line-height: 0; /* no baseline gap under the img */
  user-select: none;
}

.photo-crop-stage img {
  max-width: 100%;
  height: auto;
  display: block;
}

.photo-crop-box {
  position: absolute;
  border: 2px solid var(--gold-500);
  border-radius: 4px;
  cursor: move;
  touch-action: none; /* pointer events handle the drag, not scrolling */
  /* Oversized shadow dims everything outside the box — mask without extra DOM. */
  box-shadow: 0 0 0 9999px rgba(15, 23, 42, 0.55);
}

.photo-crop-handle {
  position: absolute;
  right: -8px;
  bottom: -8px;
  width: 16px;
  height: 16px;
  background: var(--gold-500);
  border: 2px solid #fff;
  border-radius: 50%;
  cursor: nwse-resize;
  touch-action: none;
}

/* ============================================================
   Period picker (fragments/period-picker.html + js/period-picker.js)
   Reusable two-mode control: whole month, or from–to range.
   ============================================================ */

.period-picker-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}

.period-picker-toggle {
  display: inline-flex;
  border: 1px solid var(--gray-300);
  border-radius: 8px;
  overflow: hidden;
}

.period-picker-mode {
  border: none;
  background: #fff;
  color: var(--navy-700);
  padding: 8px 14px;
  font-size: 0.875rem;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease;
}

.period-picker-mode + .period-picker-mode {
  border-left: 1px solid var(--gray-300);
}

.period-picker-mode:hover {
  background: var(--gray-100);
}

.period-picker-mode.active {
  background: var(--navy-700);
  color: var(--gold-400);
  font-weight: 600;
}

.period-picker-fields {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  flex-wrap: wrap;
}
/* .form-input is width:100%; once the span wraps, that 100% resolves against the
   span's own width and every field would take a line of its own, on desktop too.
   Intrinsic width keeps both modes on one line where there is room. */
.period-picker-fields .form-input { width: auto; }

.period-picker-fields[hidden] {
  display: none;
}

.period-picker-date-label {
  font-size: 0.8125rem;
  color: var(--gray-600);
}

/* ============================================================
   Payroll (templates/payroll/payroll.html + combined exports)
   Moved out of the old bolt/payroll.html inline <style> so every
   payroll surface — and the print rules — share one definition.
   ============================================================ */

.payroll-wrap { max-width: 1200px; margin: 0 auto; }

/* Car cost arrangements: the drivers and the cars of the form, a grid of checkboxes. */
.arrangement-choices { display: grid; grid-template-columns: repeat(auto-fill, minmax(14rem, 1fr)); gap: 0.35rem 1rem; }
.arrangement-kind-hint { display: block; }
/* A car's table of costs: its plate (and its sum, with several cars) above it. */
.arrangement-car-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.5rem; }
/* The sheet's "Summary": the totals and the recipient side by side under the heading, which already
   brings the gap the grid's own top margin would double. */
.payroll-totals.arrangement-summary { margin-top: 0; }
/* Its two boxes are the cards of the sections under them (Drivers, Car costs): white, framed, shadowed. */
.arrangement-summary > .totals-card {
  background-color: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  padding: 1rem 1.25rem;
}
.arrangement-summary > .totals-card > .totals-row:last-child { border-bottom: none; }
.arrangement-summary-cars { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: 0.375rem; }

.payroll-id-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 20px; }
.payroll-id-grid div { padding: 3px 0; font-size: 0.875rem; }
.payroll-id-grid strong { color: #223b60; margin-right: 6px; }

.payroll-section-title { font-size: 1.125rem; color: #223b60; margin: 18px 0 10px; }

.payroll-table-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.payroll-table { width: 100%; border-collapse: collapse; font-size: 0.875rem; }
.payroll-table th { background: #223b60; color: #fff; padding: 7px 5px; text-align: right; font-weight: 600; white-space: nowrap; }
.payroll-table th:first-child { text-align: left; }
.payroll-table td { padding: 5px; border-bottom: 1px solid #e5e7eb; text-align: right; white-space: nowrap; }
.payroll-table td:first-child { text-align: left; font-weight: 500; white-space: normal; min-width: 120px; }
.payroll-table tr:nth-child(even) td { background: #f8f9fa; }
.payroll-table tfoot td { background: #223b60 !important; color: #fff; font-weight: 700; }
/* A short table (a name, one input, a badge) as wide as its content, not the card: the
   reconcile panel's bonus table left a gulf between the driver and the field at 100 %. */
.payroll-table--fit { width: auto; }
.payroll-table--fit th:first-child, .payroll-table--fit td:first-child { padding-right: 1.5rem; }
.payroll-table--fit th:last-child, .payroll-table--fit td:last-child { text-align: left; padding-left: 1rem; }
.payroll-week-dates { display: block; font-size: 0.85em; color: #6b7280; font-weight: 400; white-space: nowrap; }

.payroll-totals { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; margin-top: 16px; }
.totals-card { background: #f8f9fa; border-radius: 8px; padding: 14px; }
.totals-card h3 { font-size: 0.9rem; margin-bottom: 8px; }
.totals-row { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; padding: 5px 0; border-bottom: 1px dashed #d1d5db; font-size: 0.875rem; }
.totals-row.total { font-weight: 700; border-bottom: 2px solid #223b60; font-size: 1rem; color: #223b60; }
.totals-row.final { font-weight: 700; background: #d4a843; color: #223b60; padding: 9px 12px; border-radius: 6px; margin-top: 8px; font-size: 1.05rem; border-bottom: none; }
/* The gross PAYOUT column — wage plus tips, the amount tax is withheld from — wears the
   footer's gold: a gold head and foot, a pale gold body, the figure in bold navy. The selectors
   carry a tr so they outrank the zebra rule, and the foot needs the !important the foot has. */
.payroll-table th.payroll-col-payout { background: #d4a843; color: #223b60; }
.payroll-table tr td.payroll-col-payout { background: #fbf3df; color: #223b60; font-weight: 700; }
.payroll-table tfoot td.payroll-col-payout { background: #d4a843 !important; color: #223b60; }
/* The payslip's ring sits beside the footer card, in the second column of .payroll-totals. */
.payroll-ring { margin: 0; display: flex; flex-direction: column; gap: 0.5rem; min-width: 0; }
.payroll-ring-caption { font-size: 0.9rem; font-weight: 600; color: #223b60; }
.payroll-donut .dash-donut-centre strong { font-size: 0.9375rem; }

.payroll-actions { display: flex; gap: 8px; flex-wrap: wrap; margin: 16px 0; }
.input-inline { width: 110px; }

@media (max-width: 768px) {
  .payroll-id-grid { grid-template-columns: 1fr; }
  .payroll-id-grid div { font-size: 0.8125rem; }
  .payroll-table { font-size: 0.75rem; }
  .payroll-table th { padding: 5px 4px; font-size: 0.7rem; }
  .payroll-table td { padding: 4px; font-size: 0.75rem; }
  .payroll-table td:first-child { min-width: 90px; }
  .payroll-totals { grid-template-columns: 1fr; gap: 12px; margin-top: 12px; }
  .totals-card { padding: 12px; }
  .totals-row { font-size: 0.8125rem; }
  .totals-row.final { font-size: 0.95rem; padding: 8px 10px; }
  .input-inline { width: 100%; }
}
/* On a phone the fit table must still fit: a fixed-width field instead of 100 % of a
   header-wide cell, the header and the status badge free to wrap. */
@media (max-width: 768px) {
  .payroll-table--fit th, .payroll-table--fit td:last-child { white-space: normal; }
  .payroll-table--fit .input-inline { width: 6.5rem; }
  .payroll-table--fit .badge { white-space: normal; }
  .payroll-table--fit th:first-child, .payroll-table--fit td:first-child { padding-right: 0.75rem; }
  .payroll-table--fit th:last-child, .payroll-table--fit td:last-child { padding-left: 0.5rem; }
}

@media print {
  .no-print, .app-sidebar, .navbar, header, footer, .payroll-actions { display: none !important; }
  body { background: #fff; }
  .payroll-wrap { max-width: none; }
}

/* ============================================================
   Platform photo picker (profile Contact tab, vehicle detail):
   one row per candidate — preview, logo chip, adopt button.
   ============================================================ */

.photo-candidates-block {
  margin-top: 1rem;
  padding-top: 1rem;
  border-top: 1px solid var(--gray-200);
}

.photo-candidates {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1rem;
  margin-top: 0.75rem;
}

/* The vehicle preview stays a thumbnail next to its chip and buttons —
   the 320px .vehicle-photo-lg is for the hero image above. */
.photo-candidate-vehicle {
  /* 200px is the native width of Uber's catalogue renders, so the preview is
     shown at 1:1 rather than upscaled. */
  max-width: 200px;
  /* .photo-candidates centres its row; .vehicle-photo-lg's align-self:flex-start
     would override that for this img, so put it back. Equal specificity — this
     rule wins only because it comes later in the file. */
  align-self: center;
}

/* --- First-login credentials (users/credentials.html + password fields) --- */
.credential-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
}
.credential-row .form-input {
  flex: 1 1 9rem;
  min-width: 0;
  font-family: ui-monospace, "DejaVu Sans Mono", Menlo, Consolas, monospace;
}
.qr-preview {
  display: block;
  width: 200px;
  max-width: 100%;
  height: auto;
  border: 1px solid var(--gray-200);
  border-radius: 8px;
}

/* ================================================================
   PROJECT MODULE (/project) — progress meter, CSS bars, timeline
   Single-hue marks on purpose: the bars compare magnitude, identity
   is carried by the label next to each bar, not by colour.
   ================================================================ */
.project-hero {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.5rem;
}
.project-hero-text { flex: 1 1 16rem; min-width: 0; }
.project-hero-counts {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem 1.5rem;
  font-size: 0.875rem;
  color: var(--gray-600);
}
.project-hero-counts strong { color: var(--gray-900); font-size: 1.125rem; margin-right: 0.25rem; }
.progress-track {
  height: 10px;
  background: var(--navy-50);
  border-radius: var(--radius-full);
  overflow: hidden;
}
.progress-fill {
  height: 100%;
  background: var(--navy-500);
  border-radius: var(--radius-full);
  transition: width 0.4s ease;
}
.progress-fill.is-complete { background: var(--green-600); }
/* The bar ends in a hatched tail: the percent covers the milestones recorded so
   far, and the tail says the scale is open — more work is added as it is agreed.
   A sibling of the track, not an overlay, so a 100 % fill still reaches its end. */
.progress-row { display: flex; align-items: center; gap: 0.375rem; }
.progress-row > .progress-track { flex: 1 1 auto; min-width: 0; }
/* A task's done / all bar (tasks/fragments :: progress): a fixed width, so it keeps its
   size in a narrow table cell and beside a heading, where the row itself has no width.
   The width is a WIDTH, not a flex-basis: the bar is empty, and Firefox leaves flex-basis
   out when it sizes a container to its content (a table cell, an inline-flex chip), so a
   `flex: 0 0 5rem` bar spilled out of its cell onto the next column there. */
.task-progress > .progress-track { flex: none; width: 5rem; height: 6px; }
.progress-open-tail {
  flex-shrink: 0;
  width: 2.5rem;
  height: 10px;
  border-radius: 0 var(--radius-full) var(--radius-full) 0;
  background: repeating-linear-gradient(135deg, var(--gray-300) 0 3px, transparent 3px 7px);
  cursor: help;
}
.progress-note {
  display: flex;
  align-items: flex-start;
  gap: 0.375rem;
  font-size: 0.8125rem;
  line-height: 1.35;
  color: var(--gray-500);
}
.progress-note svg { flex-shrink: 0; margin-top: 2px; width: 14px; height: 14px; }
/* "Project status as of <date>" under the page title — the one date the figures describe. */
.status-as-of {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--navy-500);
}
.progress-percent {
  font-size: 2.5rem;
  font-weight: 700;
  color: var(--navy-500);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}
.stat-value.stat-warn { color: var(--gold-600); }
.stat-value.stat-ok   { color: var(--green-600); }
/* A figure never breaks across lines: the tile formats it short instead
   (fragments :: moneyTile / hoursTile — whole units, the unit small). */
.stat-box .stat-value { font-variant-numeric: tabular-nums; white-space: nowrap; }
.stat-figure { display: inline-flex; align-items: baseline; gap: 0.25rem; }
.stat-unit { font-size: 0.75em; font-weight: 600; color: var(--gray-500); }
@media (max-width: 480px) {
  .stat-box .stat-value { font-size: 1.25rem; }
}

/* Horizontal CSS bars: label | track | value. Thin marks, rounded data end,
   square at the baseline; the width comes from the server as a whole percent. */
.bar-list { display: flex; flex-direction: column; gap: 0.5rem; }
.bar-row {
  display: grid;
  grid-template-columns: minmax(5rem, 9rem) 1fr auto;
  gap: 0.75rem;
  align-items: center;
  font-size: 0.875rem;
}
.bar-label { color: var(--gray-600); overflow-wrap: anywhere; }
.bar-track { height: 12px; background: var(--gray-100); border-radius: 0 4px 4px 0; overflow: hidden; }
.bar-fill  { height: 100%; background: var(--navy-400); border-radius: 0 4px 4px 0; min-width: 2px; }
.bar-value {
  color: var(--gray-800);
  font-weight: 600;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

/* Vertical timeline: a month heading with a filled dot, items with a small dot. */
.timeline {
  position: relative;
  margin-left: 0.5rem;
  padding-left: 1.5rem;
  border-left: 2px solid var(--gray-200);
}
.timeline-month {
  position: relative;
  margin: 1.25rem 0 0.5rem;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--gray-500);
}
.timeline-month:first-child { margin-top: 0; }
.timeline-month::before {
  content: '';
  position: absolute;
  left: calc(-1.5rem - 8px);
  top: 0;
  width: 12px;
  height: 12px;
  border-radius: 50%;
  background: var(--navy-500);
  border: 2px solid var(--white);
}
.timeline-item {
  position: relative;
  margin-bottom: 0.75rem;
  padding: 0.75rem 1rem;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
}
.timeline-item::before {
  content: '';
  position: absolute;
  left: calc(-1.5rem - 6px);
  top: 1.1rem;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--gray-300);
}
.timeline-item.in-progress::before { background: var(--gold-400); }
.timeline-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.timeline-title { font-weight: 600; color: var(--gray-900); }
.timeline-meta { font-size: 0.8125rem; color: var(--gray-500); margin-left: auto; white-space: nowrap; }
.timeline-desc { margin-top: 0.375rem; font-size: 0.875rem; color: var(--gray-600); white-space: pre-line; }

.roadmap-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
  gap: 1rem;
}
.roadmap-card { display: flex; flex-direction: column; gap: 0.5rem; }
.roadmap-card .timeline-desc { flex: 1; }
.roadmap-estimate {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.5rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--gray-200);
  font-size: 0.8125rem;
  color: var(--gray-600);
}
.roadmap-estimate strong { color: var(--gray-900); font-variant-numeric: tabular-nums; }

/* The two summary cards on /project: a list, two lines a row. As table cells beside
   three badges that never wrapped, the title got a word or two per line; here it has the
   card's whole width, and what describes it sits underneath — wrapping onto a line of its
   own on a narrow screen, so no phone rule is needed. */
.milestone-list { list-style: none; }
.milestone-item { padding: 0.625rem 0; border-bottom: 1px solid var(--gray-100); }
.milestone-item:last-child { border-bottom: none; }
.milestone-title {
  font-weight: 500;
  color: var(--gray-900);
  line-height: 1.35;
  overflow-wrap: anywhere;
}
.milestone-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem 0.75rem;
  margin-top: 0.3125rem;
  font-size: 0.8125rem;
  color: var(--gray-500);
}
.milestone-end {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin-left: auto;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.milestone-done svg { color: var(--green-600); }
.milestone-done .vk-mark { width: 14px; height: 14px; flex: none; }
/* A state named in a heading or a legend wears its place's colour, as in a status column. */
:is(h1, h2, h3, h4, .card-title, .invoice-section-title, .plan-cal-legend) .vk-mark[data-mark="state:error"] { color: var(--red-600); }
:is(h1, h2, h3, h4, .card-title, .invoice-section-title, .plan-cal-legend) .vk-mark[data-mark="state:warning"] { color: #b45309; }
:is(.plan-cal-legend, .plan-cal-unplanned, .plan-cal-kpis) .vk-mark[data-mark="state:needs-action"] { color: var(--gold-700); }
:is(h1, h2, h3, h4, .card-title, .invoice-section-title) .vk-mark[data-mark="state:not-sent"] { color: var(--gray-400); }
.dash-clear > .vk-mark { width: 18px; height: 18px; color: var(--green-600); flex: none; }
/* Work in flight wears the timeline's gold, as a rule down its left edge. */
.milestone-item.is-in-progress { padding-left: 0.75rem; box-shadow: inset 3px 0 0 var(--gold-400); }
/* A heading inside a card: the timeline's month label, one size down. */
.milestone-group {
  margin: 0.875rem 0 0.125rem;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--gray-500);
}
.milestone-group:first-of-type { margin-top: 0; }

/* A milestone's area (project/fragments :: areaTag): a neutral pill with a dot in the
   colour of the task category of that name. Neutral on purpose — on these screens a
   tinted badge in green, gold or navy reads as a status. The seven non-status dot hues
   were checked for separation (dataviz validate_palette, all pairs, normal vision);
   the name beside every dot carries the identity where two hues are close. */
.area-tag {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  padding: 0 0.5rem 0 0.4375rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-full);
  background: var(--gray-50);
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.25rem;
  color: var(--gray-700);
  white-space: nowrap;
}
.area-dot { flex-shrink: 0; width: 8px; height: 8px; border-radius: 50%; background: var(--gray-400); }
.area-dot.area-secondary { background: #94a3b8; }
.area-dot.area-blue      { background: #2563eb; }
.area-dot.area-purple    { background: #9333ea; }
.area-dot.area-teal      { background: #0d9488; }
.area-dot.area-orange    { background: #ea580c; }
.area-dot.area-pink      { background: #db2777; }
.area-dot.area-brown     { background: #92400e; }
.area-dot.area-success   { background: #16a34a; }
.area-dot.area-warning   { background: #eab308; }
.area-dot.area-danger    { background: #dc2626; }
.area-dot.area-info      { background: #223b60; }
.area-dot.area-gold      { background: #d4a843; }

/* The task behind a milestone (project/fragments :: taskChip): a short bar and "3/8",
   a link when the reader's company owns the task. */
.milestone-task {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-size: 0.75rem;
  color: var(--gray-600);
  text-decoration: none;
  white-space: nowrap;
}
/* A width, not a flex-basis (see .task-progress above): with `flex: 0 0 4rem` Firefox sized
   the chip as if the bar were empty, and the right-aligned chip stuck out of the card. */
.milestone-task > .progress-track { display: block; flex: none; width: 4rem; height: 6px; }
.milestone-task .progress-fill { display: block; }
a.milestone-task:hover { color: var(--navy-500); }
a.milestone-task:hover > .progress-track { box-shadow: 0 0 0 1px var(--navy-200); }

/* A table ordered by dragging (js/table-order.js: a mailbox's filing rules). The handle is
   a real button: reachable by Tab and moved with the arrow keys, and touch-action keeps a
   finger's drag from scrolling the page instead of moving the row. */
.reorder-table .drag-cell { width: 2.5rem; padding-left: 0.5rem; padding-right: 0; }
.drag-handle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.75rem;
  height: 1.75rem;
  border: 0;
  border-radius: var(--radius);
  background: transparent;
  color: var(--gray-400);
  cursor: grab;
  touch-action: none;
}
.drag-handle:hover { color: var(--gray-700); background: var(--gray-100); }
.drag-handle:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 1px; color: var(--gray-700); }
body.is-reordering, body.is-reordering * { cursor: grabbing !important; user-select: none; }
.reorder-table tr.is-dragging td { background: var(--navy-50); }
.reorder-table tr.is-saved td { animation: reorder-row-saved 1.2s ease-out; }
@keyframes reorder-row-saved {
  from { background: var(--green-50); }
  to   { background: transparent; }
}
/* A group's heading row: the timeline's month label, inside the table. */
.milestone-manage .milestone-group-head th {
  padding-top: 1.25rem;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--gray-500);
  background: transparent;
  border-bottom: 1px solid var(--gray-200);
}
@media (prefers-reduced-motion: reduce) {
  .reorder-table tr.is-saved td { animation: none; }
}

.table .month-total td { background: var(--gray-50); font-weight: 600; }
/* /admin/company-functions: the matrix (features down, companies across) scrolls in a box of its
   own, with the companies' row frozen at its top and the features' column at its start. Its own
   box is the only way to keep both: a box that scrolls sideways is a scroll container in BOTH
   axes, so a sticky head inside it can never stick to the page. THE PAGE IS EXACTLY THE WINDOW
   under the navbar (4rem and its 1px border), a column in which the heading, the alerts and the
   intro keep their height and the box takes what is left — so the table alone scrolls, never the
   page (2026-10-23, the user). The box is as tall as its table and shrinks to the room (no white
   field under a short table); below 12rem it stops shrinking and a very low window scrolls the
   page after all. #vk-nav-loader, the outer main's other child, is fixed and takes no room. The
   lines are inset shadows — with border-collapse a border stays where the table painted it while
   the sticky cell moves (the .plan-cal way). scroll-padding keeps a button reached by Tab from
   landing under the frozen row or column. */
main.main-content:has(> .company-functions-page) {
  display: flex;
  flex-direction: column;
  height: calc(100vh - 4rem - 1px);
  height: calc(100dvh - 4rem - 1px);
  padding-bottom: 1.5rem;
}
.company-functions-page {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
}
.table-wrapper.company-functions-wrapper {
  flex: 0 1 auto;
  min-height: 12rem;
  overflow: auto;
  scroll-padding-block-start: 3.5rem;
  scroll-padding-inline-start: 11rem;
}
.company-functions thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  border-bottom: 0;
  box-shadow: inset 0 -1px 0 var(--gray-200);
}
.company-functions thead th:first-child,
.company-functions th[scope="row"] {
  position: sticky;
  inset-inline-start: 0;
  z-index: 1;
  width: 11rem;
  min-width: 11rem;
  box-shadow: inset -1px 0 0 var(--gray-200);
}
.company-functions thead th:first-child { z-index: 3; box-shadow: inset -1px -1px 0 var(--gray-200); }
/* The group's name ("Platforms", "Functions") spans the row; it stays at the box's start. */
.company-functions-group { display: inline-block; position: sticky; inset-inline-start: 1rem; }
@media (max-width: 640px) {
  .company-functions thead th:first-child,
  .company-functions th[scope="row"] { width: 8.5rem; min-width: 8.5rem; padding-inline: 0.5rem; }
  .table-wrapper.company-functions-wrapper { scroll-padding-inline-start: 8.5rem; }
}
.table td.num, .table th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.table .cell-summary { min-width: 9rem; max-width: 28rem; overflow-wrap: anywhere; }
/* An e-mail address has no spaces, so without a bound it sets the column's minimum
   width and pushes the action buttons off the screen. Wrapping mid-address is the
   lesser evil — the full header hangs in the cell's tooltip anyway. */
.table td.cell-email { min-width: 9rem; max-width: 16rem; overflow-wrap: anywhere; }
/* /admin/audit: the key=value details wrap anywhere instead of widening the table. */
.table td.audit-details { min-width: 14rem; max-width: 32rem; overflow-wrap: anywhere; color: var(--gray-600); }

/* /admin/ai-models: wider than a form page, since the table carries a model and a limit per feature.
   A limit field is as wide as six digits, never the cell; a refused one says so by its border. */
.ai-models-page { max-width: 72rem; margin-left: auto; margin-right: auto; }
.ai-models-page .ai-limit-input { width: 8.5rem; min-width: 0; }
.ai-models-page .ai-model-select { min-width: 15rem; }
.ai-models-page .form-input[aria-invalid="true"] { border-color: var(--red-500); }
.ai-models-page .form-input[aria-invalid="true"]:focus { box-shadow: 0 0 0 3px rgba(220, 38, 38, 0.15); }
.ai-limit-total { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1rem; }
/* A period reads "from – to"; letting it break at the dash keeps the table on one screen. */
.table td.cell-period { min-width: 7.5rem; }

@media (max-width: 768px) {
  .bar-row { grid-template-columns: minmax(4rem, 6rem) 1fr auto; gap: 0.5rem; }
  .timeline-meta { margin-left: 0; white-space: normal; }
}

/* ---------------------------------------------------------------------------
   /users/unlinked-drivers. A driver who actually DROVE lately is the one somebody has
   to act on, so the row sorts to the top and is tinted; the "Active" badge beside the
   name says the same thing in WORDS, so the state never rests on colour alone — the
   bank statement's rule below. The bar is an inset box-shadow, because a border-left
   on a <tr> does not render with collapsed table borders, and :hover is restated or
   .table tbody tr:hover takes the tint away.
   --------------------------------------------------------------------------- */
.unlinked-table tr.unlinked-row-active { background-color: color-mix(in srgb, #dcfce7 55%, white); }
.unlinked-table tr.unlinked-row-active:hover { background-color: color-mix(in srgb, #dcfce7 85%, white); }
.unlinked-table tr.unlinked-row-active td:first-child { box-shadow: inset 4px 0 0 #166534; }
.unlinked-active-badge { margin-left: 0.5rem; }
/* "From Bolt's ride history · last ride …" — a row the platform's own list did not name,
   said in the same quiet line as "drove 3 days ago". */
.unlinked-drove,
.unlinked-history {
  display: block;
  font-size: 0.75rem;
  line-height: 1rem;
  font-weight: 400;
  color: var(--gray-500);
}

/* ---------------------------------------------------------------------------
   Bank statement (/invoices/bank). A row's status shows twice: as its marks (words in
   their titles) and as a coloured bar on its first cell — inset box-shadow, because a
   border-left on a <tr> does not render with collapsed table borders. Only a row that
   WAITS FOR SOMEBODY has a background: red for money out with no invoice, amber for sums
   that disagree and for money in with no income invoice (a gap, not an error). A decided row has none. The :hover twins keep the tint under the pointer,
   one shade darker, since .table tbody tr:hover would otherwise take it away.
   --------------------------------------------------------------------------- */
.stats-grid-5 { grid-template-columns: repeat(5, 1fr); }
.stats-grid-6 { grid-template-columns: repeat(6, 1fr); }
.stats-grid-7 { grid-template-columns: repeat(7, 1fr); }
.bank-tile { color: inherit; }
.bank-tile-active { box-shadow: inset 0 0 0 2px var(--navy-700); }
.bank-table tr.bank-row-unlinked { background-color: var(--red-50); }
.bank-table tr.bank-row-unlinked td:first-child { box-shadow: inset 4px 0 0 #dc2626; }
.bank-table tr.bank-row-partial { background-color: var(--yellow-50); }
.bank-table tr.bank-row-partial td:first-child { box-shadow: inset 4px 0 0 #d97706; }
.bank-table tr.bank-row-linked td:first-child { box-shadow: inset 4px 0 0 #16a34a; }
.bank-table tr.bank-row-noinvoice { color: var(--gray-500); }
.bank-table tr.bank-row-noinvoice td:first-child { box-shadow: inset 4px 0 0 #cbd5e1; }
.bank-table tr.bank-row-income { background-color: var(--yellow-50); }
.bank-table tr.bank-row-income td:first-child { box-shadow: inset 4px 0 0 #d97706; }
.bank-table tbody tr.bank-row-unlinked:hover { background-color: #fee2e2; }
.bank-table tbody tr.bank-row-partial:hover,
.bank-table tbody tr.bank-row-income:hover { background-color: #fef9c3; }
.bank-amount-in { color: #166534; }
.bank-amount-out { color: var(--red-600); }
.bank-difference { color: #b45309; }
.bank-always { padding-bottom: 0.5rem; }
/* Why a proposal is offered and what held the automatic match back (bank-fragments :: why):
   two short lines under the document's name, the reasons in words, never in colour alone. */
.bank-why { margin-top: 0.35rem; font-size: 0.75rem; line-height: 1.4; color: var(--gray-500); }
.bank-why-line { margin: 0.1rem 0 0; }
.bank-why-label { font-weight: 600; color: var(--gray-700); }
.bank-why-auto { color: var(--navy-600); font-weight: 600; }
.bank-why-row {
  padding: 0.5rem 0.75rem;
  border-left: 3px solid var(--navy-200);
  background-color: var(--navy-50);
  border-radius: 0.25rem;
}
.bank-why-row p { margin: 0; }
.bank-why-row p + p { margin-top: 0.25rem; }
/* A card a badge links to (#proposals, #bank-payment): lands clear of the sticky 4rem .navbar. */
.anchor-target { scroll-margin-top: 5rem; }
.bank-detail-card { border-left: 4px solid var(--gray-100); }
.bank-card-unlinked { border-left-color: #dc2626; }
.bank-card-partial { border-left-color: #d97706; }
.bank-card-linked { border-left-color: #16a34a; }
.bank-card-noinvoice { border-left-color: #cbd5e1; }
.bank-card-income { border-left-color: var(--navy-200); }
.bank-card-salary,
.bank-card-reimbursement { border-left-color: #14b8a6; }
@media (max-width: 1100px) {
  .stats-grid-6 { grid-template-columns: repeat(3, 1fr); }
  .stats-grid-7 { grid-template-columns: repeat(4, 1fr); }
}
@media (max-width: 768px) {
  .stats-grid-5 { grid-template-columns: repeat(2, 1fr); }
  .stats-grid-6 { grid-template-columns: repeat(2, 1fr); }
  .stats-grid-7 { grid-template-columns: repeat(2, 1fr); }
}

/* ── Invoice printout ───────────────────────────────────────────────────────
   The merged PDF is shown in a frame the page can drive (window.print()).
   Never display:none — a hidden frame does not load the browser's PDF viewer,
   and then there is nothing to print. */
.print-frame {
  width: 100%;
  height: 70vh;
  min-height: 420px;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  background-color: var(--gray-50);
}
/* The page switches between its states with the hidden attribute, and .flex, .btn and
   .alert all set a display that would otherwise win over it. */
.print-page [hidden] { display: none !important; }
/* While the printout is made: ONE bar for the whole wait (documents, then composing the
   file, then bringing it to the browser), the count beside the estimate, failures below. */
.print-progress {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  max-width: 40rem;
}
/* Failed or cancelled takes the card's place at the card's width, so the page does not jump. */
.print-stopped { max-width: 40rem; }
.print-progress-phase { margin: 0; font-weight: 600; color: var(--gray-800); }
.print-progress .progress-track { height: 12px; }
.print-progress .progress-fill { width: 0; }
.print-progress-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.25rem 1rem;
  min-height: 1.25rem;
  font-size: 0.875rem;
  color: var(--gray-600);
  font-variant-numeric: tabular-nums;
}
.print-progress-failed {
  display: flex;
  align-items: flex-start;
  gap: 0.375rem;
  margin: 0;
  font-size: 0.875rem;
  color: #854d0e;
}
.print-progress-failed svg { flex-shrink: 0; margin-top: 3px; width: 14px; height: 14px; }
/* "Keep this tab open": muted, so it does not compete with the bar or the failures. */
.print-progress-hint {
  display: flex;
  align-items: flex-start;
  gap: 0.375rem;
  margin: 0;
  font-size: 0.875rem;
  color: var(--gray-500);
}
.print-progress-hint svg { flex-shrink: 0; margin-top: 3px; width: 14px; height: 14px; }
/* Work with no count to show (composing the file): a highlight sweeping the filled bar. */
.progress-fill.is-indeterminate {
  background-image: linear-gradient(90deg, transparent 0%, rgba(255, 255, 255, 0.45) 50%, transparent 100%);
  background-size: 40% 100%;
  background-repeat: no-repeat;
  animation: progress-sweep 1.2s ease-in-out infinite;
}
@keyframes progress-sweep {
  from { background-position: -60% 0; }
  to   { background-position: 160% 0; }
}
@media (prefers-reduced-motion: reduce) {
  .print-progress .progress-fill { transition: none; }
  .progress-fill.is-indeterminate { animation: none; }
}

/* ── Invoice printout: choosing pages ───────────────────────────────────────
   A card per multi-page document: the choice as pills (first / all / "Pages:"), and its pages
   as paper tiles — gold-framed with a tick when printed, dashed and faded when left out, so the
   state reads without colour too. The bar under the cards stays in view while they scroll. */
.print-choose { display: flex; flex-direction: column; gap: 1rem; }
.print-choose-head h2 { margin: 0 0 0.25rem; font-size: 1.25rem; color: var(--gray-900); }
.print-choose-head h2:focus { outline: none; }
.print-choose-head p { margin: 0; max-width: 48rem; color: var(--gray-600); }
.print-docs { display: grid; gap: 1rem; }
.print-doc {
  display: flex;
  flex-direction: column;
  gap: 0.75rem;
  min-width: 0;
  padding: 1rem 1.25rem;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
  transition: border-color 0.15s ease;
}
.print-doc.is-invalid { border-color: var(--red-500); }
.print-doc-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: space-between;
  gap: 0.25rem 1rem;
}
.print-doc-title { min-width: 0; overflow-wrap: anywhere; }
.print-doc-title strong { color: var(--navy-500); font-weight: 600; }
.print-doc-meta { margin-inline-start: 0.5rem; color: var(--gray-600); font-size: 0.875rem; }
.print-doc-meta-part { white-space: nowrap; }
.print-doc-pages {
  padding: 0.125rem 0.625rem;
  border-radius: var(--radius-full);
  background: var(--gray-100);
  color: var(--gray-600);
  font-size: 0.8125rem;
  white-space: nowrap;
}
/* The three choices: pills, the radio kept for the keyboard and screen readers but not drawn. */
.print-seg { display: flex; flex-wrap: wrap; gap: 0.5rem; min-width: 0; margin: 0; padding: 0; border: 0; }
.print-seg-option,
.print-seg-custom-label {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  cursor: pointer;
}
.print-seg-option {
  position: relative;
  min-height: 2.5rem;
  padding: 0.375rem 0.875rem;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-full);
  background: var(--white);
  color: var(--gray-700);
  font-size: 0.875rem;
  transition: border-color 0.15s ease, background-color 0.15s ease, color 0.15s ease;
}
.print-seg-option input[type="radio"] {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: 0;
  opacity: 0;
  pointer-events: none;
}
.print-seg-option:hover { border-color: var(--navy-300); }
.print-seg-option:has(input[type="radio"]:checked) {
  border-color: var(--navy-400);
  background: var(--navy-50);
  color: var(--navy-600);
  font-weight: 600;
}
.print-seg-option:has(input[type="radio"]:focus-visible) {
  outline: 2px solid var(--navy-400);
  outline-offset: 2px;
}
.print-seg-option--custom { padding-inline-end: 0.3125rem; }
.print-seg-input.form-input {
  width: 9rem;
  min-width: 0;
  min-height: 2rem;
  padding: 0.25rem 0.625rem;
  border-radius: var(--radius-full);
  font-weight: 400;
  font-variant-numeric: tabular-nums;
}
.print-doc.is-invalid .print-seg-input { border-color: var(--red-500); }
.print-doc-error { margin: 0; color: var(--red-600); font-size: 0.8125rem; }
.print-doc-note { margin: 0; color: var(--gray-500); font-size: 0.8125rem; }
.print-choose-single { margin: 0; color: var(--gray-600); font-size: 0.875rem; }
.print-thumbs-more { align-self: flex-start; }

.print-thumbs {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(6.5rem, 1fr));
  gap: 0.875rem;
  margin: 0;
  padding: 0;
  list-style: none;
}
.print-thumb { position: relative; display: flex; flex-direction: column; align-items: center; gap: 0.25rem; min-width: 0; }
.print-thumb-toggle {
  position: relative;
  display: block;
  width: 100%;
  max-width: 11rem;
  padding: 0;
  border: 0;
  border-radius: var(--radius);
  background: none;
  cursor: pointer;
}
.print-thumb-toggle:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 3px; }
.print-thumb-paper {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 210 / 297;
  overflow: hidden;
  background: var(--white);
  border: 2px solid var(--gold-400);
  border-radius: var(--radius);
  box-shadow: var(--shadow-sm);
  transition: border-color 0.15s ease, opacity 0.15s ease, transform 0.15s ease, box-shadow 0.15s ease, filter 0.15s ease;
}
/* The shared shimmer is a line of text by default; here it is a whole sheet. */
.print-thumb-paper.vk-skeleton { height: auto; border-radius: var(--radius); }
.print-thumb-paper canvas { display: block; width: 100%; height: 100%; }
.print-thumb-number {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  color: var(--gray-400);
  font-size: 1.5rem;
  font-weight: 600;
}
.print-thumb.is-drawn .print-thumb-number { display: none; }
.print-thumb-toggle:hover .print-thumb-paper { transform: translateY(-2px); box-shadow: var(--shadow-md); }
.print-thumb-check {
  position: absolute;
  top: 0.375rem;
  inset-inline-end: 0.375rem;
  display: grid;
  place-items: center;
  width: 1.375rem;
  height: 1.375rem;
  border-radius: var(--radius-full);
  background: var(--gold-400);
  color: var(--navy-800);
  box-shadow: var(--shadow-sm);
}
.print-thumb.is-excluded .print-thumb-paper {
  border-style: dashed;
  border-color: var(--gray-300);
  opacity: 0.45;
  filter: grayscale(1);
}
.print-thumb.is-excluded .print-thumb-check {
  border: 1.5px solid var(--gray-300);
  background: var(--white);
  color: transparent;
}
.print-thumb-label { color: var(--gray-600); font-size: 0.75rem; font-variant-numeric: tabular-nums; }
.print-thumb.is-excluded .print-thumb-label { color: var(--gray-400); text-decoration: line-through; }
.print-thumb-zoom {
  position: absolute;
  bottom: 1.5rem;
  inset-inline-end: 0.375rem;
  display: grid;
  place-items: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-full);
  background: rgba(255, 255, 255, 0.94);
  color: var(--navy-500);
  box-shadow: var(--shadow-sm);
  cursor: zoom-in;
  opacity: 0;
  transition: opacity 0.15s ease;
}
.print-thumb:hover .print-thumb-zoom,
.print-thumb:focus-within .print-thumb-zoom { opacity: 1; }
.print-thumb-zoom:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 2px; }
/* A touch screen has no hover: the magnifier is simply there, and a finger's size. */
@media (hover: none) {
  .print-thumb-zoom { opacity: 1; width: 2.5rem; height: 2.5rem; }
}
.print-doc.is-preview-broken .print-thumb-zoom { display: none; }

.print-choose-bar {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem 1.25rem;
  margin-top: 0.25rem;
  padding: 0.875rem 1rem;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  box-shadow: 0 -4px 16px rgba(15, 23, 42, 0.08);
}
.print-choose-bulk {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  color: var(--gray-600);
  font-size: 0.875rem;
}
.print-seg-buttons { display: inline-flex; flex-wrap: wrap; gap: 0.375rem; }
.print-choose-summary { margin: 0; color: var(--gray-800); font-weight: 600; font-variant-numeric: tabular-nums; }
.print-choose-bulk .btn { min-height: 2.5rem; }
.print-choose-actions { margin-inline-start: auto; }
.print-choose-actions .btn-primary { display: inline-flex; align-items: center; gap: 0.5rem; }

/* The large view: a fixed width, so the page can be fitted to it before it is drawn. */
.print-lightbox.app-dialog { width: min(64rem, calc(100% - 2rem)); max-width: none; padding: 1rem 1.25rem; }
.print-lightbox-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: 0.75rem; }
.print-lightbox-head .app-dialog-title { margin: 0; overflow-wrap: anywhere; }
.print-lightbox-close { flex-shrink: 0; display: grid; place-items: center; width: 2.5rem; height: 2.5rem; padding: 0; }
.print-lightbox-stage { display: flex; align-items: center; justify-content: center; gap: 0.75rem; min-width: 0; }
.print-lightbox-paper {
  min-width: 8rem;
  min-height: 12rem;
  line-height: 0;
  background: var(--white);
  border: 1px solid var(--gray-200);
  box-shadow: var(--shadow-md);
}
.print-lightbox-paper.vk-skeleton { height: auto; border-radius: 0; }
.print-lightbox-paper canvas { display: block; max-width: 100%; }
.print-lightbox-nav {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  padding: 0;
  border-radius: var(--radius-full);
}
.print-lightbox-nav:disabled { opacity: 0.35; cursor: default; }
.print-lightbox-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem 1rem;
  margin-top: 0.75rem;
}
.print-lightbox-include { display: inline-flex; align-items: center; gap: 0.5rem; min-height: 2.5rem; font-weight: 500; cursor: pointer; }

@media (max-width: 640px) {
  .print-doc { padding: 0.875rem; }
  .print-doc-meta { display: block; margin-inline-start: 0; }
  .print-thumbs { grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 0.625rem; }
  .print-choose-bar { gap: 0.5rem; padding: 0.625rem 0.75rem; }
  .print-choose-summary { font-size: 0.875rem; }
  .print-choose-actions { margin-inline-start: 0; width: 100%; }
  .print-choose-actions .btn { flex: 1 1 auto; justify-content: center; }
  .print-lightbox.app-dialog { padding: 0.75rem; }
  .print-lightbox-stage { gap: 0.25rem; }
}
@media (prefers-reduced-motion: reduce) {
  .print-doc,
  .print-seg-option,
  .print-thumb-paper,
  .print-thumb-zoom { transition: none; }
  .print-thumb-toggle:hover .print-thumb-paper { transform: none; }
}

/* ── Entry lists on a detail screen ─────────────────────────────────────────
   NOT .detail-list, which is a dt/dd grid: its rows are display:contents, so an
   <li> carrying a varying number of children pours them into the two columns and
   every row lands somewhere different. /tasks/{id} borrowed it for four lists of
   exactly that shape — assignees, audience, subtasks and files — and all four
   scattered. This is the list idiom those want: a main column that takes the
   space, and a meta group that keeps to the right and drops under it on a phone. */
.entry-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
}
.entry-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  padding: 0.625rem 0;
  border-bottom: 1px solid var(--gray-100);
}
.entry-row:last-child { border-bottom: none; }
.entry-main {
  flex: 1 1 12rem;
  min-width: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.5rem;
}
.entry-title { font-weight: 500; overflow-wrap: anywhere; }
/* A confirmation note is a sentence, not a chip: its own line, full width. */
.entry-note {
  flex: 1 0 100%;
  margin: 0;
  font-size: 0.875rem;
  color: var(--gray-600);
  overflow-wrap: anywhere;
}
.entry-meta {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
  margin-left: auto;
  font-size: 0.875rem;
  color: var(--gray-600);
}
.entry-meta .progress-track { width: 3.5rem; height: 6px; }
.entry-count { font-variant-numeric: tabular-nums; white-space: nowrap; }
/* Nobody owes this one yet, so it cannot complete by itself. Said in the row
   because that is where the reader is asking what it is waiting for. */
.entry-waiting { color: var(--gray-400); font-style: italic; white-space: nowrap; }
/* Finished or abandoned: muted and struck through, so what is LEFT is what stands out. */
.entry-row.is-closed .entry-title { text-decoration: line-through; }
.entry-row.is-closed .entry-title,
.entry-row.is-closed .entry-meta { color: var(--gray-400); }

/* A comment is a paragraph with a byline, so it stacks rather than sitting in a row. */
.comment-list { list-style: none; margin: 0; padding: 0; }
.comment-item {
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--gray-100);
}
.comment-item:last-child { border-bottom: none; }
.comment-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
  margin-bottom: 0.25rem;
}
/* The form is the flex item, not the button inside it — pushing the button would
   move nothing. */
.comment-head > form { margin: 0 0 0 auto; }
.comment-author { font-weight: 600; color: var(--gray-800); }
.comment-when { font-size: 0.75rem; color: var(--gray-500); }
.comment-body {
  margin: 0;
  font-size: 0.875rem;
  color: var(--gray-800);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

/* The mailbox list's row tint per assignee (V87): the badge palette above, paled for a
   whole row, with the badge's text colour as a left bar — border-left on a <tr> does not
   render with collapsed borders, hence the inset shadow (the .bank-row-unlinked idiom).
   Placed after .table tbody tr:hover so the tint survives hovering; the :hover twin is
   one shade darker so the row still answers the pointer. */
/* The person's colour as the row's left bar: on the first cell in the table, on the row itself in the
   message list's two-line layout (≤1100 px), where that cell has no box of its own. */
.mail-table tbody tr[class*="mail-row-"] td:first-child { box-shadow: inset 4px 0 0 var(--mail-bar); }
.mail-table tbody tr.mail-row-secondary { background-color: color-mix(in srgb, var(--gray-100) 55%, white); }
.mail-table tbody tr.mail-row-secondary:hover { background-color: color-mix(in srgb, var(--gray-100) 85%, white); }
.mail-table tbody tr.mail-row-secondary { --mail-bar: var(--gray-600); }
.mail-table tbody tr.mail-row-success { background-color: color-mix(in srgb, #dcfce7 55%, white); }
.mail-table tbody tr.mail-row-success:hover { background-color: color-mix(in srgb, #dcfce7 85%, white); }
.mail-table tbody tr.mail-row-success { --mail-bar: #166534; }
.mail-table tbody tr.mail-row-warning { background-color: color-mix(in srgb, var(--yellow-50) 55%, white); }
.mail-table tbody tr.mail-row-warning:hover { background-color: color-mix(in srgb, var(--yellow-50) 85%, white); }
.mail-table tbody tr.mail-row-warning { --mail-bar: #854d0e; }
.mail-table tbody tr.mail-row-danger { background-color: color-mix(in srgb, var(--red-50) 55%, white); }
.mail-table tbody tr.mail-row-danger:hover { background-color: color-mix(in srgb, var(--red-50) 85%, white); }
.mail-table tbody tr.mail-row-danger { --mail-bar: #991b1b; }
.mail-table tbody tr.mail-row-info { background-color: color-mix(in srgb, var(--navy-50) 55%, white); }
.mail-table tbody tr.mail-row-info:hover { background-color: color-mix(in srgb, var(--navy-50) 85%, white); }
.mail-table tbody tr.mail-row-info { --mail-bar: var(--navy-700); }
.mail-table tbody tr.mail-row-purple { background-color: color-mix(in srgb, #f3e8ff 55%, white); }
.mail-table tbody tr.mail-row-purple:hover { background-color: color-mix(in srgb, #f3e8ff 85%, white); }
.mail-table tbody tr.mail-row-purple { --mail-bar: #6b21a8; }
.mail-table tbody tr.mail-row-blue { background-color: color-mix(in srgb, #dbeafe 55%, white); }
.mail-table tbody tr.mail-row-blue:hover { background-color: color-mix(in srgb, #dbeafe 85%, white); }
.mail-table tbody tr.mail-row-blue { --mail-bar: #1e40af; }
.mail-table tbody tr.mail-row-gold { background-color: color-mix(in srgb, #fef3c7 55%, white); }
.mail-table tbody tr.mail-row-gold:hover { background-color: color-mix(in srgb, #fef3c7 85%, white); }
.mail-table tbody tr.mail-row-gold { --mail-bar: #92400e; }
.mail-table tbody tr.mail-row-teal { background-color: color-mix(in srgb, #ccfbf1 55%, white); }
.mail-table tbody tr.mail-row-teal:hover { background-color: color-mix(in srgb, #ccfbf1 85%, white); }
.mail-table tbody tr.mail-row-teal { --mail-bar: #115e59; }
.mail-table tbody tr.mail-row-orange { background-color: color-mix(in srgb, #ffedd5 55%, white); }
.mail-table tbody tr.mail-row-orange:hover { background-color: color-mix(in srgb, #ffedd5 85%, white); }
.mail-table tbody tr.mail-row-orange { --mail-bar: #9a3412; }
.mail-table tbody tr.mail-row-pink { background-color: color-mix(in srgb, #fce7f3 55%, white); }
.mail-table tbody tr.mail-row-pink:hover { background-color: color-mix(in srgb, #fce7f3 85%, white); }
.mail-table tbody tr.mail-row-pink { --mail-bar: #9d174d; }
.mail-table tbody tr.mail-row-brown { background-color: color-mix(in srgb, #efe7dd 55%, white); }
.mail-table tbody tr.mail-row-brown:hover { background-color: color-mix(in srgb, #efe7dd 85%, white); }
.mail-table tbody tr.mail-row-brown { --mail-bar: #6b4226; }

/* A STARRED message (the server's \Flagged) sits on gold — after the person tints so it wins
   over them, while the person's colour stays as the left bar (only the background is set here).
   Deeper than the gold person tint above, so the two cannot be mistaken. An open row keeps its
   navy (.row-clickable.is-expanded is the more specific rule). */
.mail-table tbody tr.mail-starred { background-color: color-mix(in srgb, var(--gold-200) 42%, white); }
.mail-table tbody tr.mail-starred:hover { background-color: color-mix(in srgb, var(--gold-200) 62%, white); }

/* The star itself, beside the checkbox: always there (unlike the chevron) — the hollow star
   (state:not-starred) that turns into the solid gold one (state:starred) when the message is
   starred. The button holds both marks and shows one by its class, which setRowStarred flips.
   A real button, so a click on it stars instead of opening the row (mail-preview.js lets buttons
   through). */
.mail-star {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 1.5rem;
  height: 1.5rem;
  margin-left: 0.25rem;
  padding: 0;
  border: none;
  border-radius: 0.25rem;
  background: none;
  color: var(--gray-400);
  vertical-align: middle;
  cursor: pointer;
  transition: color 0.15s;
}
.mail-star svg { width: 1rem; height: 1rem; }
.mail-star:hover { color: var(--gold-500); }
.mail-star:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 1px; }
.mail-star:not(.is-starred) .vk-mark[data-mark="state:starred"],
.mail-star.is-starred .vk-mark[data-mark="state:not-starred"] { display: none; }
.mail-star.is-starred { color: var(--gold-400); }
.mail-star.is-starred:hover { color: var(--gold-500); }

/* ── Compose windows (email/compose-window.html, js/mail-compose.js) ─────────────────────────
   The Gmail shape: a dock at the bottom right of the mail screens, windows standing side by
   side in it, each a card in the list's own idiom — the navy bar, the ghost icon buttons, the
   form fields the page uses. Minimized keeps the bar alone; maximized is a dialog in the middle
   of the screen over a dimmed list. On a phone one window is the whole screen. */
.mail-compose-dock {
  position: fixed;
  right: 1rem;
  bottom: 0;
  /* Above the sidebar rail (100), its mobile panel and backdrop (199/200); BELOW the navbar
     (250), which stays clickable — a maximized window therefore starts under it rather than
     hiding behind it. The list's own menus (240/245) still paint on top, which is what they
     are for; the AI panel is part of the window, inside the message's box. */
  z-index: 210;
  display: flex;
  align-items: flex-end;
  gap: 0.75rem;
  /* Never wider than the screen: windows side by side shrink to share it (a window's own overflow is
     hidden, so it may) — they used to run off the screen's left edge. */
  max-width: calc(100vw - 2rem);
  pointer-events: none;            /* the dock is empty space; the windows catch the pointer */
}
.mail-compose-dock:empty { display: none; }
.mail-compose-window {
  pointer-events: auto;
  display: flex;
  flex-direction: column;
  width: 34rem;
  max-width: calc(100vw - 2rem);
  /* Taller than it was (36rem): the assistant lives inside the message's box now, and nothing in the
     window scrolls the window. */
  height: 42rem;
  max-height: calc(100vh - 5rem);   /* the 4rem navbar plus a little air */
  background: #fff;
  border: 1px solid var(--gray-200);
  border-bottom: 0;
  border-radius: var(--radius-lg) var(--radius-lg) 0 0;
  box-shadow: var(--shadow-lg);
  overflow: hidden;
}
.mail-compose-window-bar {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4rem 0.5rem 0.4rem 0.9rem;
  background: var(--navy-500);
  color: #fff;
  cursor: default;
  flex: 0 0 auto;
  user-select: none;
}
.mail-compose-window-title {
  flex: 1 1 auto;
  min-width: 0;
  font-size: 0.875rem;
  font-weight: 600;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.mail-compose-window-buttons { display: flex; gap: 0.125rem; flex: 0 0 auto; }
.mail-compose-window-bar .btn-icon-ghost { color: #fff; }
.mail-compose-window-bar .btn-icon-ghost:hover { background: rgba(255,255,255,0.15); color: #fff; }
/* NOTHING IN A WINDOW SCROLLS THE WINDOW (2026-10-09, the user: neither sideways nor up and down).
   The window has its height; the form is a column in it where every row keeps its own size and the
   message's box alone takes what is left — the text and the assistant's chat inside it scroll in
   their own, the files in their bounded area, the signature's preview in its own. */
.mail-compose-window-body {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  min-height: 0;
  overflow: hidden;
  padding: 0.75rem 0.9rem 0;
}
.mail-compose-window .mail-compose-actions {
  margin-top: auto;
  padding-bottom: 0.75rem;
  background: var(--white);
}
.mail-compose-window.is-minimized { height: auto; }
.mail-compose-window.is-minimized .mail-compose-window-body { display: none; }
/* "Maximize" is a dialog: 2rem clear of the navbar (4rem, the height .app-sidebar also aligns to
   — covering it hid the window's own title bar), of both sides and of the bottom, no wider than
   64rem and centred by the auto margins a fixed box with both edges set gets. */
.mail-compose-window.is-maximized {
  position: fixed;
  z-index: 2;
  inset: 6rem 2rem 2rem 2rem;
  margin-inline: auto;
  width: auto;
  max-width: 64rem;
  height: auto;
  max-height: none;
  border-bottom: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  box-shadow: 0 24px 48px -12px rgba(15, 23, 42, 0.45);
}
/* The dim behind it is the dock's own ::before — nothing to create or keep in step. It starts under
   the navbar, which stays usable; the other windows, not positioned, stay under it; and a click on
   it lands on the dock itself, which mail-compose.js reads as "restore". */
.mail-compose-dock:has(.mail-compose-window.is-maximized)::before {
  content: "";
  position: fixed;
  inset: 4rem 0 0 0;
  z-index: 1;
  background: rgba(15, 23, 42, 0.45);
  pointer-events: auto;
}
/* The form inside a window: a column filling the window's body, every row its own size, the
   message's field ALONE flexing — its box takes what is left (never under 7rem), the text in it
   never under 3rem. The header rows are compact, Gmail's way: the label on the field's own line. */
.mail-compose-window .mail-compose-form { display: flex; flex-direction: column; flex: 1 1 auto; min-height: 0; }
.mail-compose-window .mail-compose-form > * { flex: 0 0 auto; }
.mail-compose-window .mail-compose-field { margin-bottom: 0.375rem; }
.mail-compose-window .form-label { font-size: 0.75rem; margin-bottom: 0.15rem; }
.mail-compose-window .form-input { font-size: 0.875rem; padding: 0.35rem 0.5rem; }
.mail-compose-window .mail-compose-field:not(:has(.email-compose-body)) {
  display: grid;
  grid-template-columns: 4.5rem minmax(0, 1fr);
  align-items: center;
  column-gap: 0.5rem;
}
.mail-compose-window .mail-compose-field:not(:has(.email-compose-body)) .form-label { margin-bottom: 0; }
/* "From" is not a field one types in: the address as text. */
.mail-compose-window .mail-compose-from .form-input {
  border-color: transparent;
  background: transparent;
  padding-inline: 0;
  color: var(--gray-600);
  box-shadow: none;
}
.mail-compose-window .mail-compose-field:has(.email-compose-body) {
  flex: 1 1 0;
  min-height: 6rem;
  display: flex;
  flex-direction: column;
}
/* The message's own label is read by a screen reader from the text (aria-label); the box says it. */
.mail-compose-window .mail-compose-field:has(.email-compose-body) > .form-label {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
}
.mail-compose-window .email-compose-body-wrapper { flex: 1 1 0; min-height: 0; }
.mail-compose-window .email-compose-body-wrapper > .email-compose-body { flex: 1 1 0; min-height: 3rem; resize: none; }
/* An open assistant needs room for a chat worth reading. */
.mail-compose-window .mail-compose-field:has(.ai-compose-panel.is-open) { min-height: 17rem; }
/* A signature shown is shown WHOLE (2026-10-10, the user — it was cut to 5rem and scrolled): the text
   field above it is what narrows; only a very tall one scrolls in its own. */
.mail-compose-window .mail-compose-form > .mail-compose-signature { flex: 0 0 auto; }
.mail-compose-window .mail-compose-signature-preview { max-height: 12rem; }
/* The files' area takes room only when it holds something — empty, it left a gap over the bar. */
.mail-compose-window .mail-compose-form > .mail-compose-files-area { flex: 0 1 auto; min-height: 0; }
.mail-compose-files-area:not(:has(li, .mail-compose-files-foot)) { display: none; }
/* A screen too low for even that (a phone on its side): the body scrolls after all — better than a
   Send button cut off below the window's edge. */
@media (max-height: 30rem) {
  .mail-compose-window-body { overflow-y: auto; }
  .mail-compose-window .mail-compose-form { flex: 0 0 auto; }
  .mail-compose-window .mail-compose-field:has(.email-compose-body) { flex: 0 0 auto; height: 12rem; }
}
/* To: "CC" at the right end inside the field (Gmail's place), which leaves it room. */
.mail-suggest-anchor:has(> .mail-compose-cc-toggle:not([hidden])) > .form-input { padding-inline-end: 2.75rem; }
.mail-compose-cc-toggle {
  position: absolute;
  inset-inline-end: 0.375rem;
  top: 50%;
  transform: translateY(-50%);
  padding: 0.125rem 0.375rem;
  border: 0;
  border-radius: var(--radius-sm);
  background: none;
  color: var(--navy-400);
  font: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  cursor: pointer;
}
.mail-compose-cc-toggle:hover { color: var(--navy-600); text-decoration: underline; }
.mail-compose-cc-toggle:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 1px; }
.mail-compose-cc-toggle[hidden],
.mail-compose-field[hidden] { display: none !important; }
.mail-suggest-anchor { position: relative; }
.mail-compose-form .mail-suggest { max-height: 14rem; max-width: min(26rem, 100%); }
/* Subject: the AI's button at the right end inside the field (2026-10-10), where "CC" stands in To.
   It is a .btn-icon-ghost, whose inline-flex beats the browser's own [hidden] — hence the override. */
.mail-suggest-anchor:has(> .mail-compose-subject-ai:not([hidden])) > .form-input { padding-inline-end: 2.25rem; }
.mail-compose-subject-ai {
  position: absolute;
  inset-inline-end: 0.375rem;
  top: 50%;
  transform: translateY(-50%);
}
.mail-compose-subject-ai[hidden] { display: none !important; }
.mail-compose-subject-ai .btn-ai-spinner {
  display: none;
  width: 0.875rem;
  height: 0.875rem;
  border: 2px solid var(--gold-400);
  border-top-color: transparent;
  border-radius: 50%;
  animation: ai-spin 0.7s linear infinite;
}
.mail-compose-subject-ai.loading .vk-mark { display: none; }
.mail-compose-subject-ai.loading .btn-ai-spinner { display: block; }
/* The proposals are as wide as the field and wrap: a subject is read whole. */
.mail-compose-form .mail-subject-suggest { width: 100%; max-width: 100%; }
.mail-suggest-text { color: var(--gray-900); overflow-wrap: anywhere; }
/* What is going on, or why nothing came: a bubble where the list opens, taking no room — the window
   never scrolls. Empty, it draws nothing (it stays in the page as a live region). */
.mail-subject-note {
  position: absolute;
  top: calc(100% + 4px);
  inset-inline: 0;
  z-index: 1060;
  pointer-events: none;
}
.mail-subject-note:not(:empty) {
  padding: 0.375rem 0.625rem;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  color: var(--gray-600);
  font-size: 0.8125rem;
  line-height: 1.25rem;
}

/* The files, Gmail's way: chips above the bar, each with its kind, name, size and a remove button,
   the running total at the end; a file on its way up is the same chip with a thin bar along its
   foot. */
.mail-compose-files-row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.375rem 0.75rem; margin: 0.25rem 0 0.375rem; }
.mail-compose-files,
.mail-compose-uploads { display: flex; flex-wrap: wrap; gap: 0.375rem; min-width: 0; margin: 0; padding: 0; list-style: none; }
.mail-compose-uploads:not(:empty) { margin-bottom: 0.375rem; }
/* ONE bounded area for the server's chips and the ones on their way: in the window about two rows
   of chips, then it scrolls INSIDE — a dozen files used to push Send out of the window. The foot
   line (totals, the links' lifetime) sticks to the area's bottom edge while the chips scroll. */
.mail-compose-files-area { min-width: 0; }
.mail-compose-window .mail-compose-files-area { max-height: 6.5rem; overflow-y: auto; overscroll-behavior: contain; }
.mail-compose-window .mail-compose-file-name { max-width: 9rem; }
.mail-compose-window .mail-compose-file-chip { padding-top: 0.125rem; padding-bottom: 0.125rem; }
.mail-compose-file-chip {
  position: relative;
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  max-width: 100%;
  padding: 0.25rem 0.25rem 0.25rem 0.5rem;
  overflow: hidden;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  background: var(--gray-50);
  font-size: 0.8125rem;
  line-height: 1.25rem;
}
.mail-compose-file-icon { display: inline-flex; flex-shrink: 0; } /* the file kind's mark, or the links' */
.mail-compose-file-icon svg { width: 1rem; height: 1rem; }
.mail-compose-file-name {
  min-width: 0;
  max-width: 14rem;
  overflow: hidden;
  color: var(--gray-800);
  font-weight: 500;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.mail-compose-file-size { color: var(--gray-500); font-size: 0.75rem; white-space: nowrap; }
.mail-compose-file-remove svg { width: 0.875rem; height: 0.875rem; }
.mail-compose-file-remove:hover { color: var(--red-600); }
.mail-compose-total { color: var(--gray-500); font-size: 0.75rem; white-space: nowrap; }
.mail-compose-file-chip.is-uploading { padding-right: 0.5rem; color: var(--gray-500); }
/* The same chip while the file is HASHED rather than sent (the dedup check): a dashed bar. */
.mail-compose-file-chip.is-hashing .mail-compose-file-progress {
  background: repeating-linear-gradient(90deg, var(--gold-500) 0 6px, transparent 6px 10px);
}
.mail-compose-file-progress {
  position: absolute;
  left: 0;
  bottom: 0;
  width: 0;
  height: 2px;
  background: var(--navy-400);
  transition: width 0.15s;
}
/* A message whose files go as LINKS to the copies ValkOS keeps rather than inside it (V119) — all of
   them, one kind per message: every chip a soft blue with the links' mark, so the message reads as
   links at a glance. The switch for the whole message sits in the foot. */
.mail-compose-file-chip.is-link { border-color: #bfdbfe; background: #eff6ff; }
.mail-compose-file-chip.is-link .mail-compose-file-progress { background: #1e40af; }
.mail-compose-total--links { color: #1e40af; }
/* The foot: "3.2 of 18 MB · As links: 1 (1.4 GB) · Links valid for [30 days]" on one small line. */
.mail-compose-files-foot {
  position: sticky;
  bottom: 0;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem 0.75rem;
  padding: 0.125rem 0 0.25rem;
  background: var(--white);
  font-size: 0.75rem;
  color: var(--gray-600);
}
.mail-compose-link-ttl-row { display: inline-flex; align-items: center; gap: 0.375rem; margin: 0; white-space: nowrap; }
/* The message's switch (all as links / all as attachments): a button as small as the foot's words. */
.mail-compose-files-foot .mail-compose-delivery {
  padding: 0.125rem 0.5rem;
  font-size: 0.75rem;
  line-height: 1.25rem;
}
.mail-compose-files-foot .mail-compose-delivery .vk-mark { width: 0.875rem; height: 0.875rem; }
.mail-compose-delivery-note { color: #1e40af; }
.mail-compose-link-ttl-row .mail-compose-link-ttl {
  width: auto;
  padding: 0.125rem 1.75rem 0.125rem 0.5rem;
  font-size: 0.75rem;
  line-height: 1.25rem;
}
/* A phone: two rows of chips under a foot that wraps to two lines (the window is the whole screen). */
@media (max-width: 480px) {
  .mail-compose-window .mail-compose-files-area { max-height: 8.5rem; }
  .mail-compose-window .mail-compose-file-size { display: none; }
  .mail-compose-window .mail-compose-file-name { max-width: 7rem; }
}

/* The bar under it all: Send, the attach mark, the status line — and the discard mark at the far end. */
.mail-compose-actions { gap: 0.375rem 0.5rem; padding-top: 0.5rem; border-top: 1px solid var(--gray-100); }
.mail-compose-actions .btn-icon-ghost { width: 2rem; height: 2rem; }
.mail-compose-actions .btn-icon-ghost svg { width: 1.125rem; height: 1.125rem; }
.mail-compose-actions-end { display: inline-flex; align-items: center; gap: 0.25rem; margin-left: auto; }
.mail-compose-status { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mail-compose-attach { position: relative; }
.mail-compose-attach:focus-within { outline: 2px solid var(--navy-400); outline-offset: 1px; }
/* The native file control stays where nothing drives it — the page without the script, or without
   a draft to upload into — and the paperclip then sits beside it. A form the script drives
   (is-enhanced) keeps the control focusable but out of sight: the paperclip is its label. */
.mail-compose-form:not(.is-enhanced) .mail-compose-attach { width: auto; gap: 0.375rem; padding: 0 0.25rem; }
.mail-compose-form:not(.is-enhanced) .mail-compose-file { max-width: 16rem; font-size: 0.8125rem; }
.mail-compose-form.is-enhanced .mail-compose-file {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  opacity: 0;
  clip-path: inset(50%);
  pointer-events: none;
}

/* Files dragged over the form: say where they go. The overlay lets the drag events through. */
.mail-compose-form { position: relative; }
.mail-compose-drop {
  position: absolute;
  inset: 0;
  z-index: 5;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  border: 2px dashed var(--navy-300);
  border-radius: var(--radius-lg);
  background: rgba(232, 237, 244, 0.92);
  color: var(--navy-500);
  font-weight: 600;
  pointer-events: none;
}
.mail-compose-drop svg { width: 2rem; height: 2rem; }
.mail-compose-drop[hidden] { display: none !important; }
/* The attachment mark in a row, cut down to the row's 13 px. */
.mail-cell-subject .mail-clip svg { width: 13px; height: 13px; vertical-align: -2px; }
.mail-compose-status:empty { display: none; }
.mail-compose-error[hidden] { display: none !important; }
/* A draft's row in the list: no chevron to turn, only the trash among its actions. */
.mail-table tbody tr[data-draft-id] .mail-row-actions > :not(.mail-action-trash) { display: none; }
/* A phone: the dock is a column the width of the screen — the newest open window fills it,
   every other window stands minimized as a bar under it, and minimizing the open one brings
   the previous one back. */
@media (max-width: 900px) {
  .mail-compose-dock {
    right: 0; left: 0; gap: 0;
    flex-direction: column; align-items: stretch;
    max-height: calc(100vh - 4rem);
  }
  .mail-compose-window { flex: 0 1 auto; min-height: 0; width: auto; max-width: none; border-radius: 0; border-left: 0; border-right: 0; }
  .mail-compose-window.is-minimized { flex-shrink: 0; }
  .mail-compose-window:not(.is-minimized) { height: calc(100vh - 4rem); max-height: none; }
  .mail-compose-window:not(.is-minimized):has(~ .mail-compose-window:not(.is-minimized)) { display: none; }
  /* No room for a dialog: maximized is the whole screen under the navbar, with nothing to dim. */
  .mail-compose-window.is-maximized {
    inset: 4rem 0 0 0;
    max-width: none;
    margin: 0;
    border-radius: 0;
    border-left: 0;
    border-right: 0;
  }
  .mail-compose-dock:has(.mail-compose-window.is-maximized)::before { display: none; }
}

/* ==========================================================================
   Dashboard (/)
   One column of sections, read top to bottom: what waits for you, how the company is
   doing, who and what earned it. Everything is drawn by the server — the chart included —
   so nothing here depends on a script.

   CHART COLOURS are not the brand navy and gold as they stand: the brand navy is too grey
   to read as a series colour (it fails the chroma floor of a categorical palette). These
   two keep the family and pass every check of the palette validator — lightness band,
   chroma, colour-blind separation (ΔE 29.7) and normal-vision separation; the gold's
   contrast against white is below 3:1, which the chart answers the way the rule asks,
   with visible figures above it and a table view under it. The heatmap is ONE hue, light
   to dark — a magnitude, never a rainbow.
   ========================================================================== */
:root {
  --chart-in:  #2d63b5;
  --chart-out: #c8962e;
  /* The rings of the bank statement (a counterparty's identity, not in/out): five slots in
     a fixed order, run through the palette validator in ring order — the last slot sits next
     to the first — with the light "rest" grey checked against each neighbour. Three slots are
     under 3:1 on white, which the rings answer with a visible legend and a table view. */
  --pie-1: #2a78d6;
  --pie-2: #eb6834;
  --pie-3: #1baf7a;
  --pie-4: #eda100;
  --pie-5: #e87ba4;
  --pie-rest: #cbd5e1;
  /* An invoice's cost category (V110) has ONE colour, used twice: at full strength on the
     invoice list's category ring, and as the pale tint of the list's rows (a 7 % mix with
     white) — so a row and its slice cannot drift apart. The Tailwind 500 step of the hues the
     rows always had; each slice is also named in the legend and in its tooltip, never told by
     colour alone. */
  --cat-fuel: #f59e0b;
  --cat-charging: #10b981;
  --cat-parking: #3b82f6;
  --cat-tolls: #6366f1;
  --cat-service: #f97316;
  --cat-insurance: #14b8a6;
  --cat-financing: #8b5cf6;
  --cat-subscription: #ec4899;
  --cat-infrastructure: #0ea5e9;
  --cat-accounting: #84cc16;
  --cat-taxes_fees: #f43f5e;
  --cat-other: #94a3b8;
  --heat-1: #d6e2f5;
  --heat-2: #9dbbe6;
  --heat-3: #5b8bd1;
  --heat-4: #2d63b5;
}

.dash { display: flex; flex-direction: column; gap: 1.25rem; }
.dash > .alert { margin-bottom: 0; }
.dash-header .page-title { margin: 0; }
.dash-date { margin: 0.25rem 0 0; color: var(--gray-500); font-size: 0.9375rem; }
.dash-date::first-letter { text-transform: uppercase; }

/* Two sections side by side where there is room; one alone takes the whole width. */
.dash-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 26rem), 1fr));
  gap: 1.25rem;
  align-items: start;
}

.dash-card-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.75rem;
  margin-bottom: 0.75rem;
}
.dash-card-head .card-title { margin: 0; }
.dash-card-count { font-size: 0.8125rem; color: var(--gray-500); }
.dash-card-link {
  margin-left: auto;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--navy-500);
  text-decoration: none;
  white-space: nowrap;
}
.dash-card-link:hover { text-decoration: underline; }
.dash-partial {
  margin-left: auto;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--gold-700);
  background: var(--gold-50);
  border: 1px solid var(--gold-100);
  border-radius: var(--radius-full);
  padding: 0.125rem 0.625rem;
}
.dash-note { margin: 0; font-size: 0.8125rem; color: var(--gray-500); }
.dash-swatch {
  display: inline-block;
  width: 0.625rem;
  height: 0.625rem;
  border-radius: 2px;
  flex-shrink: 0;
}
.dash-swatch--in  { background: var(--chart-in); }
.dash-swatch--out { background: var(--chart-out); }
.bar-fill.dash-fill--in  { background: var(--chart-in); }
.bar-fill.dash-fill--out { background: var(--chart-out); }

/* ---- Attention: pills that link to where the thing is decided ---- */
.dash-chips { display: flex; flex-wrap: wrap; gap: 0.5rem; list-style: none; margin: 0; padding: 0; }
.dash-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.4375rem 0.5rem 0.4375rem 0.875rem;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-full);
  box-shadow: var(--shadow-sm);
  color: var(--gray-800);
  font-size: 0.875rem;
  font-weight: 500;
  text-decoration: none;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.dash-chip:hover { border-color: var(--navy-300); box-shadow: var(--shadow-md); }
.dash-chip-count {
  display: inline-grid;
  place-items: center;
  min-width: 1.5rem;
  height: 1.5rem;
  padding: 0 0.4375rem;
  border-radius: var(--radius-full);
  background: var(--navy-500);
  color: var(--white);
  font-size: 0.75rem;
  font-weight: 700;
}
.dash-chip--urgent { background: var(--red-50); border-color: #fecaca; color: #b91c1c; }
.dash-chip--urgent .dash-chip-count { background: var(--red-600); }
.dash-chip--urgent svg { color: var(--red-600); }
.dash-clear {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  margin: 0;
  padding: 0.4375rem 0.875rem;
  background: var(--green-50);
  border: 1px solid #bbf7d0;
  border-radius: var(--radius-full);
  color: #166534;
  font-size: 0.875rem;
  font-weight: 500;
}

/* ---- Money: two months of figures, then the chart and the bank beside it ---- */
.dash-kpis {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 1.25rem 3.5rem;
  margin-bottom: 1.25rem;
}
.dash-kpi-group { display: flex; flex-direction: column; gap: 0.625rem; min-width: 0; }
.dash-kpi-group-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem 0.625rem;
  margin: 0;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--gray-700);
}
.dash-kpi-month { display: inline-block; }
.dash-kpi-month::first-letter { text-transform: uppercase; }
.dash-kpi-group-head .dash-partial { margin-left: 0; }
.dash-kpi-row { display: flex; flex-wrap: wrap; align-items: flex-start; gap: 1rem 1.5rem; }
/* A figure's comparison line wraps under it rather than widening the pair beyond its month.
   The cap is the comparison line's and not the whole figure's: a figure and its currency never
   part (nowrap below), and a KPI capped as a whole put a six-digit hero's "NOK" on a line of its
   own the moment its digits went tabular. */
.dash-kpi-row .dash-delta { max-width: 15rem; }
/* The two months side by side once the card can hold both pairs. */
@container (min-width: 50rem) {
  .dash-kpis { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); align-items: start; gap: 1.25rem 2rem; }
}
.dash-closed {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--gray-600);
  background: var(--gray-50);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-full);
  padding: 0.125rem 0.625rem;
}
.dash-kpi { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; }
.dash-kpi-label {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--gray-600);
}
/* The one hero figure of the page, in the same sans as everything else. Its digits are
   tabular, like every counted figure's (the Motion block at the end of this file): it used to
   be proportional for looks, and counting up, its currency wrapped under it and back. */
.dash-hero-figure {
  font-size: 3rem;
  font-weight: 700;
  line-height: 1.05;
  letter-spacing: -0.02em;
  color: var(--gray-900);
}
/* The figure and its currency on ONE line (the podium's rule): a unit alone on a line of its
   own is read as a figure of its own. */
.dash-hero-figure, .dash-kpi-figure { white-space: nowrap; }
.dash-hero-figure small, .dash-kpi-figure small {
  font-size: 1rem;
  font-weight: 600;
  letter-spacing: 0;
  color: var(--gray-500);
  margin-left: 0.25rem;
}
.dash-kpi-figure { font-size: 1.75rem; font-weight: 700; line-height: 1.1; color: var(--gray-800); }
.dash-delta {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem;
  font-size: 0.8125rem;
  font-weight: 600;
}
.dash-delta--good { color: var(--green-600); }
.dash-delta--bad  { color: var(--red-600); }
.dash-delta--flat { color: var(--gray-500); }
.dash-delta-note { font-weight: 400; color: var(--gray-500); }

.dash-chart { margin: 0; }
.dash-chart-legend {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1rem;
  margin-bottom: 0.75rem;
  font-size: 0.8125rem;
  color: var(--gray-600);
}
.dash-legend-item { display: inline-flex; align-items: center; gap: 0.375rem; }
.dash-legend-currency { margin-left: auto; font-weight: 600; color: var(--gray-400); }
.dash-chart-body {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  grid-template-rows: 14rem auto;
  column-gap: 0.5rem;
}
.dash-chart-axis { position: relative; min-width: 2.5rem; }
.dash-chart-tick {
  position: absolute;
  right: 0;
  transform: translateY(50%);
  font-size: 0.6875rem;
  line-height: 1;
  color: var(--gray-400);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.dash-chart-plot { position: relative; border-bottom: 1px solid var(--gray-300); }
.dash-chart-grid { position: absolute; inset: 0; pointer-events: none; }
.dash-chart-gridline { position: absolute; left: 0; right: 0; height: 1px; background: var(--gray-100); }
.dash-chart-gridline.dash-level-0 { display: none; }
/* Grid lines and their labels are placed by LEVEL, a class — no number reaches a style. */
.dash-level-0 { bottom: 0; }
.dash-level-1 { bottom: 25%; }
.dash-level-2 { bottom: 50%; }
.dash-level-3 { bottom: 75%; }
.dash-level-4 { bottom: 100%; }
.dash-chart-columns { position: relative; display: flex; height: 100%; }
.dash-chart-group {
  position: relative;
  flex: 1 1 0;
  min-width: 0;
  display: flex;
  justify-content: center;
  border-radius: var(--radius) var(--radius) 0 0;
  outline: none;
}
.dash-chart-group:hover, .dash-chart-group:focus-visible { background: rgba(34, 59, 96, 0.05); }
.dash-chart-group:focus-visible { box-shadow: inset 0 0 0 2px var(--navy-300); }
.dash-chart-bars {
  display: flex;
  align-items: flex-end;
  justify-content: center;
  gap: 2px;
  width: 72%;
  height: 100%;
}
/* Thin marks: at most 24 px, a 4 px rounded data end, square at the baseline. */
.dash-bar {
  flex: 0 1 24px;
  min-width: 3px;
  border-radius: 4px 4px 0 0;
}
.dash-bar--in  { background: var(--chart-in); }
.dash-bar--out { background: var(--chart-out); }
.dash-chart-group.is-partial .dash-bar { opacity: 0.5; }
.dash-chart-tip {
  display: none;
  position: absolute;
  top: 0.25rem;
  left: 50%;
  transform: translateX(-50%);
  z-index: 5;
  flex-direction: column;
  gap: 0.1875rem;
  min-width: 11rem;
  padding: 0.5rem 0.625rem;
  background: var(--gray-900);
  color: var(--white);
  border-radius: var(--radius);
  box-shadow: var(--shadow-md);
  font-size: 0.75rem;
  white-space: nowrap;
  pointer-events: none;
}
.dash-chart-group:hover .dash-chart-tip,
.dash-chart-group:focus-visible .dash-chart-tip { display: flex; }
.dash-chart-group:nth-child(-n+2) .dash-chart-tip { left: 0; transform: none; }
.dash-chart-group:nth-last-child(-n+2) .dash-chart-tip { left: auto; right: 0; transform: none; }
.dash-chart-tip-note { color: var(--gold-200); }
.dash-chart-tip-row { display: flex; align-items: center; gap: 0.375rem; }
.dash-chart-tip-row b { margin-left: auto; padding-left: 0.75rem; font-variant-numeric: tabular-nums; }
.dash-chart-months { grid-column: 2; display: flex; }
.dash-chart-months span {
  flex: 1 1 0;
  min-width: 0;
  padding-top: 0.375rem;
  text-align: center;
  font-size: 0.6875rem;
  color: var(--gray-500);
  white-space: nowrap;
  overflow: hidden;
}
.dash-chart-months span.is-partial { color: var(--navy-500); font-weight: 600; }
/* A narrow chart names every other month — counted from the END, so the month in progress
   keeps its label (counting from the start hid the twelfth). The chart's own width decides,
   since beside the bank statement it is narrow on a wide screen too. */
.dash-chart { container-type: inline-size; }
@container (max-width: 34rem) {
  .dash-chart-months span:nth-last-child(even) { visibility: hidden; }
}
/* A report's dense chart (a month by day, rep-columns--dense): every fourth column named. */
@container (max-width: 34rem) {
  .rep-columns--dense .dash-chart-months span:not(:nth-last-child(4n+1)) { visibility: hidden; }
}
.dash-money-foot {
  margin-top: 1rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--gray-100);
  font-size: 0.8125rem;
  color: var(--gray-600);
}
.dash-money-foot p { margin: 0.25rem 0; }
.dash-foot-figure {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  margin-left: 0.75rem;
  font-weight: 600;
  color: var(--gray-800);
  white-space: nowrap;
}

/* The chart and the bank statement side by side where the CARD is wide enough, one under the
   other elsewhere — a container query, since the card's width, not the window's, decides. */
.dash-money { container-type: inline-size; }
.dash-money-body { display: grid; gap: 1.5rem; }
.dash-money-main { min-width: 0; }
.dash-bank { min-width: 0; container-type: inline-size; }
.dash-money-body.has-bank .dash-bank { padding-top: 1.25rem; border-top: 1px solid var(--gray-100); }
@container (min-width: 60rem) {
  .dash-money-body.has-bank { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 2rem; }
  .dash-money-body.has-bank .dash-bank {
    padding: 0 0 0 1.75rem;
    border-top: 0;
    border-left: 1px solid var(--gray-100);
  }
}
.dash-bank-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.75rem;
  margin-bottom: 0.875rem;
}
.dash-bank-title { margin: 0; font-size: 0.9375rem; font-weight: 600; color: var(--gray-800); }
.dash-bank-partial { margin-bottom: 0.75rem; color: var(--gold-700); }
.dash-bank > .dash-note + .dash-note { margin-top: 0.375rem; }
.dash-rings {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 11rem), 1fr));
  gap: 1.5rem 1.25rem;
  margin-bottom: 0.5rem;
}
.dash-ring-figure { display: flex; flex-direction: column; gap: 0.625rem; min-width: 0; margin: 0; }
.dash-ring-caption { font-size: 0.8125rem; font-weight: 600; color: var(--gray-600); }

/* ---- A ring: SVG arcs of int thousandths, HTML for every word ---- */
.dash-donut { display: flex; flex-direction: column; gap: 0.625rem; min-width: 0; }
.dash-donut-ring { position: relative; width: 10rem; height: 10rem; margin: 0 auto; flex: none; }
.dash-donut-svg { display: block; width: 100%; height: 100%; overflow: visible; }
.dash-pie-0 { --pie: var(--pie-rest); }
.dash-pie-1 { --pie: var(--pie-1); }
.dash-pie-2 { --pie: var(--pie-2); }
.dash-pie-3 { --pie: var(--pie-3); }
.dash-pie-4 { --pie: var(--pie-4); }
.dash-pie-5 { --pie: var(--pie-5); }
.dash-arc, .dash-arc-hatch { fill: none; stroke-width: 16; }
.dash-arc { stroke: var(--pie); }
.dash-slice { transition: opacity 0.15s; }
.dash-donut-svg:hover .dash-slice { opacity: 0.45; }
.dash-donut-svg .dash-slice:hover { opacity: 1; }
/* A legend line pointed at (js/donut-link.js): its slice stands, thicker, the rest fade. */
.dash-donut-svg.is-linked .dash-slice { opacity: 0.3; }
.dash-donut-svg.is-linked .dash-slice.is-active { opacity: 1; }
.dash-donut-svg.is-linked .dash-slice.is-active :is(.dash-arc, .dash-arc-hatch) { stroke-width: 21; }
@media (prefers-reduced-motion: no-preference) {
  .dash-arc, .dash-arc-hatch { transition: stroke-width 0.15s; }
}
.dash-donut-centre {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  pointer-events: none;
}
.dash-donut-centre strong {
  font-size: 1.375rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--gray-900);
  font-variant-numeric: tabular-nums;
}
.dash-donut-centre span { max-width: 5.5rem; font-size: 0.6875rem; line-height: 1.25; color: var(--gray-500); }
.dash-donut-total { margin: 0; text-align: center; font-size: 0.8125rem; color: var(--gray-600); }
.dash-donut-legend {
  display: flex;
  flex-direction: column;
  gap: 0.3125rem;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.8125rem;
}
.dash-donut-legend li {
  display: grid;
  grid-template-columns: 0.625rem minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 0.5rem;
}
.dash-donut-legend .dash-swatch { background-color: var(--pie); }
.dash-donut-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--gray-700); }
.dash-donut-amount { font-weight: 600; color: var(--gray-800); font-variant-numeric: tabular-nums; }
.dash-donut-without {
  grid-column: 2 / -1;
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  font-size: 0.75rem;
  color: var(--gray-500);
}
.dash-swatch--hatch {
  background-color: var(--pie);
  background-image: repeating-linear-gradient(45deg, rgba(255, 255, 255, 0.85) 0 1.5px, transparent 1.5px 3px);
}
.dash-swatch--key { --pie: var(--gray-500); background-color: var(--pie); }
.dash-donut-key { display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; margin: 0; font-size: 0.75rem; color: var(--gray-500); }

/* ---- Which month the ranking, the suppliers and the fleet describe ---- */
.dash-period {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 0.75rem;
  scroll-margin-top: 5rem;
}
.dash-period-label { font-size: 0.8125rem; font-weight: 600; color: var(--gray-600); }
.dash-period-options {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  padding: 0.1875rem;
  background: var(--gray-100);
  border-radius: var(--radius-full);
}
.dash-period-option {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  min-height: 2.5rem;
  padding: 0.25rem 0.875rem;
  border-radius: var(--radius-full);
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--gray-600);
  text-decoration: none;
}
.dash-period-option > span { display: inline-block; }
.dash-period-option > span::first-letter { text-transform: uppercase; }
.dash-period-option:hover { color: var(--navy-600); }
.dash-period-option.is-active { background: var(--white); color: var(--gray-900); font-weight: 600; box-shadow: var(--shadow-sm); }
.dash-period-option small { font-size: 0.75rem; font-weight: 500; color: var(--gold-700); }

/* ---- Documents and payments: four tiles, a share, a stacked bar, the split in words ---- */
.dash-link-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 15rem), 1fr));
  gap: 1rem;
  margin-top: 0.875rem;
}
.dash-link-tile {
  display: flex;
  flex-direction: column;
  gap: 0.625rem;
  min-width: 0;
  padding: 1rem;
  border: 1px solid var(--gray-100);
  border-radius: var(--radius-lg);
}
.dash-link-title { margin: 0; font-size: 0.875rem; font-weight: 600; color: var(--gray-700); }
.dash-link-figure { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.5rem; margin: 0; }
.dash-link-figure strong {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--gray-900);
  font-variant-numeric: tabular-nums;
}
.dash-link-figure span { font-size: 0.8125rem; color: var(--gray-500); }
/* Segments are separated by a white rule INSIDE their width, so the widths still add up to
   100 % — a flex gap would push the last one out of the bar. */
.dash-meter {
  display: flex;
  height: 0.5rem;
  overflow: hidden;
  background: var(--gray-100);
  border-radius: var(--radius-full);
}
.dash-meter-seg { display: block; flex: none; height: 100%; min-width: 3px; box-sizing: border-box; }
.dash-meter-seg + .dash-meter-seg { border-left: 2px solid var(--white); }
.dash-meter-bank-linked, .dash-meter-vehicles-auto, .dash-meter-documents-accepted { background-color: var(--chart-in); }
.dash-meter-vehicles-manual, .dash-meter-documents-duplicate { background-color: var(--heat-3); }
/* A collective invoice covers several cars (V141): decided, but on none of them. */
.dash-meter-vehicles-several { background-color: var(--heat-2); }
.dash-meter-bank-noinvoice, .dash-meter-vehicles-none, .dash-meter-documents-rejected { background-color: var(--gray-400); }
.dash-meter-bank-tolink, .dash-meter-vehicles-undecided, .dash-meter-documents-new { background-color: var(--chart-out); }
.dash-link-lines {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: 0.8125rem;
  color: var(--gray-600);
}
/* Swatch, words, count — and the amount, which drops under them, right-aligned, when the
   tile is too narrow for one line. */
.dash-link-lines li { display: flex; flex-wrap: wrap; align-items: center; gap: 0 0.5rem; }
.dash-link-lines li > span:nth-child(2) { flex: 1 1 auto; min-width: 0; }
.dash-link-lines b { color: var(--gray-800); font-variant-numeric: tabular-nums; }
.dash-link-amount { margin-left: auto; color: var(--gray-500); font-variant-numeric: tabular-nums; white-space: nowrap; }
.dash-link-open { margin: auto 0 0; align-self: flex-start; }
.dash-link-sources { display: flex; flex-direction: column; gap: 0.25rem; list-style: none; margin: 0; padding: 0; }
.dash-link-source {
  display: grid;
  grid-template-columns: 3.75rem minmax(0, 1fr) 3.5rem auto;
  align-items: center;
  column-gap: 0.5rem;
  padding: 0.1875rem 0.25rem;
  border-radius: var(--radius);
  font-size: 0.8125rem;
  color: var(--gray-700);
  text-decoration: none;
}
.dash-link-source:hover { background: var(--gray-50); }
.dash-link-source-logo { display: flex; align-items: center; height: 1rem; }
.dash-link-source-logo .platform-logo { max-width: 3.75rem; max-height: 1rem; }
.dash-link-source-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dash-link-source-bar { width: 3.5rem; height: 8px; }
.dash-link-source-bar .bar-fill { display: block; }
.dash-link-source-count { color: var(--gray-600); font-variant-numeric: tabular-nums; white-space: nowrap; }

/* ---- Driver ranking: a podium, then the list ---- */
.dash-podium {
  display: flex;
  justify-content: center;
  align-items: flex-end;
  gap: 0.75rem;
  list-style: none;
  margin: 0.25rem 0 1rem;
  padding: 0;
}
.dash-podium-place { flex: 1 1 0; max-width: 11rem; min-width: 0; }
.dash-place-1 { order: 2; }
.dash-place-2 { order: 1; }
.dash-place-3 { order: 3; }
.dash-podium-link {
  --medal: #b8bec8;
  --medal-tint: #f3f4f6;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.25rem;
  padding: 1rem 0.5rem 0.875rem;
  text-align: center;
  text-decoration: none;
  color: inherit;
  border: 1px solid var(--gray-100);
  border-radius: var(--radius-lg);
  background: linear-gradient(180deg, var(--medal-tint), var(--white) 70%);
  transition: box-shadow 0.15s, transform 0.15s;
}
.dash-podium-link:hover { box-shadow: var(--shadow-md); transform: translateY(-1px); }
.dash-place-1 .dash-podium-link { --medal: var(--gold-400); --medal-tint: var(--gold-50); padding-top: 1.5rem; }
.dash-place-3 .dash-podium-link { --medal: #c58a5c; --medal-tint: #fbefe6; }
.dash-podium-avatar { position: relative; display: inline-block; }
.dash-podium-avatar .avatar { width: 64px; height: 64px; border: 3px solid var(--medal); }
.dash-place-1 .dash-podium-avatar .avatar { width: 76px; height: 76px; }
.dash-medal {
  position: absolute;
  right: -4px;
  bottom: -4px;
  display: grid;
  place-items: center;
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  border: 2px solid var(--white);
  background: var(--medal);
  color: var(--white);
  font-size: 0.75rem;
  font-weight: 700;
}
.dash-podium-name {
  margin-top: 0.25rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--gray-900);
  /* break-word, not anywhere: a name breaks inside a word only when the word alone is
     wider than the column, never merely to fill a line. */
  overflow-wrap: break-word;
  /* ROOM FOR TWO LINES, whether the name needs them or not. The three cards stand on one
     baseline (align-items: flex-end), so a name that wraps where its neighbour's does not
     carries that card's amount, rides, rating and chip a line out of step with theirs —
     which is what a 360 px phone showed. The height of this box is the one thing here the
     data controls, so it is the one thing pinned. 1.25, not the body's 1.6: two lines of a
     name, not of prose, and 2.5em is exactly those two. Centred, so a one-line name keeps
     as much air above as below instead of sitting on a hole. A third line (three long
     words) still pushes its own card — rare enough to accept, and nothing is ever cut. */
  display: flex;
  align-items: center;
  justify-content: center;
  line-height: 1.25;
  min-height: 2.5em;
}
.dash-podium-amount {
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--gray-900);
  /* The figure and its currency on ONE line, on all three cards. Inter's figures are
     PROPORTIONAL: "31 191" is some 6 px narrower than "32 222" at the same size, so the
     currency sat beside one card's amount and dropped under the next one's — and that card
     grew a line the others did not have. Safe from the page's side: .dash-podium-place is
     flex: 1 1 0 with min-width: 0, so nothing in here can widen the row; an amount too long
     for its card reaches into the card's own padding and no further (the sizes in the two
     phone blocks below keep that inside a couple of px). */
  white-space: nowrap;
  /* Tabular figures, because the three amounts compare digit for digit down the podium (a
     ranking is read that way). The counted figure inside would have them anyway: every counter
     does, so that counting up cannot move the layout (the Motion block at the end of this file). */
  font-variant-numeric: tabular-nums;
}
.dash-podium-amount small { font-size: 0.7em; font-weight: 600; color: var(--gray-500); }
.dash-podium-meta {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  align-items: center;
  gap: 0.25rem 0.5rem;
  font-size: 0.75rem;
  color: var(--gray-500);
  /* Two lines kept, always. The rating is per DRIVER, not per podium — a driver who only
     drives for Bolt has none (the template's th:if) — so without this the rated cards are a
     line taller than the unrated one, and on one baseline that drops its amount and name
     below theirs. The rides and the rating never share a line in a card this narrow, so two
     is also what a rated card really uses: 3.2em is two of the body's 1.6 lines, plus this
     row's own gap between them. */
  min-height: calc(3.2em + 0.25rem);
}
/* Each part keeps to its own line. "Przejazdy: 212" is longer than "Przejazdy: 9", so the
   rides text wrapped on one card and not on the next — a per-card difference again, and in
   Polish it is the ordinary case. A word reaching a px into the padding is nothing; a third
   line moves every row under it. */
.dash-podium-meta > span { white-space: nowrap; }
/* Nobody on this podium is rated at all: then there is no second line to keep room for. */
.dash-podium:not(:has(.rating-stars)) .dash-podium-meta { min-height: 0; }

/* The delta chip is per DRIVER too — DashboardMoney.changePercent answers null for someone
   with nothing to compare against, a driver who started this month — and the fragment then
   renders NOTHING. CSS cannot keep a row for an element the server never wrote, so the foot
   is always in the markup and reserves the line either way. 1.3rem is the chip's own line
   box (0.8125rem x 1.6). */
.dash-podium-foot { display: flex; justify-content: center; min-height: 1.3rem; }
/* Nobody has a delta (a company's first month): no line to keep. */
.dash-podium:not(:has(.dash-delta)) .dash-podium-foot { min-height: 0; }
/* The foot is exactly one line, so the chip must not become two. */
.dash-podium-link .dash-delta { justify-content: center; flex-wrap: nowrap; }
.dash-podium-link .dash-delta-note { display: none; }

.dash-rank-list, .dash-supplier-list {
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  list-style: none;
  margin: 0;
  padding: 0;
}
.dash-rank-row, .dash-supplier-row {
  display: grid;
  align-items: center;
  gap: 0.625rem;
  padding: 0.375rem 0.5rem;
  border-radius: var(--radius);
  color: inherit;
  text-decoration: none;
  font-size: 0.875rem;
}
.dash-rank-row { grid-template-columns: 1.25rem 28px minmax(0, 1fr) minmax(3rem, 30%) auto; }
.dash-rank-row:hover, .dash-supplier-row:hover { background: var(--gray-50); }
.dash-rank-row, .dash-supplier-row { min-height: 2.5rem; }
.dash-rank-pos { text-align: right; font-weight: 600; color: var(--gray-400); font-variant-numeric: tabular-nums; }
.dash-rank-name, .dash-supplier-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; color: var(--gray-800); }
/* Spans, so they need a box: a width on an inline element is ignored and the bar stays empty. */
.dash-rank-bar, .dash-supplier-bar { display: block; height: 8px; }
.dash-rank-bar .bar-fill, .dash-supplier-bar .bar-fill { display: block; }
.dash-rank-amount, .dash-supplier-amount {
  font-weight: 600;
  color: var(--gray-900);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  text-align: right;
}
.dash-rank-rest {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.5rem;
  margin: 0.5rem 0 0;
  padding: 0 0.5rem;
  font-size: 0.8125rem;
  color: var(--gray-600);
}
.dash-rank-rest b { color: var(--gray-800); }
.dash-rank-rest a { color: var(--navy-500); font-weight: 600; }
.dash-inline-link { display: inline-block; padding: 0.375rem 0; color: var(--navy-500); font-weight: 600; text-decoration: none; }
.dash-inline-link:hover { text-decoration: underline; }
.dash-empty { margin-bottom: 0.5rem; }
.dash-rank-unlinked { color: var(--gold-700); }

/* ---- Suppliers ---- */
.dash-supplier-row { grid-template-columns: 5.5rem minmax(0, 1fr) minmax(3rem, 26%) auto; }
.dash-supplier-logo { display: flex; align-items: center; height: 1.25rem; }
.dash-supplier-logo img { max-width: 5.5rem; max-height: 1.25rem; height: auto; object-fit: contain; }

/* ---- A driver's own month ---- */
.dash-my-grid { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 1.25rem 3rem; }
.dash-my-stat { display: flex; flex-direction: column; gap: 0.25rem; }

/* ---- Fleet: the shared vehicle cards (.vehicle-card), with the month's money as their foot ---- */
.dash-fleet-money {
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
  margin-top: auto;
  padding-top: 0.375rem;
  border-top: 1px solid var(--gray-100);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--gray-800);
  font-variant-numeric: tabular-nums;
}
.dash-fleet-in, .dash-fleet-out { display: inline-flex; align-items: center; gap: 0.3125rem; }
.dash-fleet .dash-note + .dash-note, .dash-fleet .vehicle-gallery + .dash-note { margin-top: 0.75rem; }

/* ---- Activity heatmap: CSS grid, a level class per day ---- */
.dash-heatmap-wrap { overflow-x: auto; }
.dash-heatmap { display: flex; align-items: stretch; gap: 3px; margin-top: 0.75rem; }
.dash-heat-weekdays {
  display: grid;
  grid-template-rows: 1rem repeat(7, 1fr);
  gap: 3px;
  padding-right: 0.25rem;
  font-size: 0.625rem;
  color: var(--gray-400);
}
.dash-heat-weekdays span { display: flex; align-items: center; }
/* ONE column of exactly the week's width. With the implicit `auto` column the month label
   (nowrap) set the column's minimum, so on a phone — a week 14 px wide, "wrz" or "sep." wider
   — every week in which a month begins got wider columns and, through aspect-ratio, bigger
   squares. The label now overflows over the empty labels beside it instead. */
.dash-heat-week {
  flex: 1 1 0;
  min-width: 0.875rem;
  max-width: 2rem;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  grid-template-rows: 1rem repeat(7, auto);
  gap: 3px;
}
.dash-heat-month { min-width: 0; font-size: 0.625rem; line-height: 1rem; color: var(--gray-500); white-space: nowrap; overflow: visible; }
.dash-heat-cell { display: block; aspect-ratio: 1; border-radius: 3px; }
.dash-heat-0 { background: var(--gray-100); }
.dash-heat-1 { background: var(--heat-1); }
.dash-heat-2 { background: var(--heat-2); }
.dash-heat-3 { background: var(--heat-3); }
.dash-heat-4 { background: var(--heat-4); }
.dash-heat-cell.is-future { background: transparent; box-shadow: inset 0 0 0 1px var(--gray-100); }
.dash-heat-legend {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 3px;
  margin-top: 0.625rem;
  font-size: 0.6875rem;
  color: var(--gray-500);
}
.dash-heat-legend .dash-heat-cell { width: 0.75rem; }
.dash-heat-legend span:first-child { margin-right: 0.25rem; }
.dash-heat-legend span:last-child { margin-left: 0.25rem; }

/* ---- Crew: faces, who is away today ---- */
.dash-crew-out { display: flex; flex-wrap: wrap; gap: 0.5rem; margin: 0 0 0.75rem; }
.dash-crew-tag {
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.125rem 0.625rem;
  border-radius: var(--radius-full);
}
.dash-crew-tag--leave { background: var(--gold-50); color: var(--gold-700); }
.dash-crew-tag--sick { background: var(--red-50); color: var(--red-600); }
.dash-crew .dash-note { margin-bottom: 0.75rem; }
.dash-crew-faces { display: flex; flex-wrap: wrap; gap: 0.5rem; list-style: none; margin: 0; padding: 0; }
.dash-crew-face { position: relative; display: block; padding: 2px; border-radius: 50%; transition: transform 0.15s; }
.dash-crew-face:hover { transform: scale(1.06); }
.dash-crew-face .avatar {
  display: block;
  width: 40px;
  height: 40px;
  border: 2px solid var(--white);
  box-shadow: 0 0 0 1px var(--gray-200);
}
.dash-crew-face.is-away .avatar { filter: grayscale(1); opacity: 0.55; }
.dash-crew-dot {
  position: absolute;
  right: 1px;
  bottom: 1px;
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 50%;
  border: 2px solid var(--white);
}
.dash-crew-dot--leave { background: var(--gold-400); }
.dash-crew-dot--sick { background: var(--red-500); }
.dash-crew-more a {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  margin: 2px;
  border-radius: 50%;
  background: var(--gray-100);
  color: var(--gray-600);
  font-size: 0.75rem;
  font-weight: 600;
  text-decoration: none;
}

/* ---- The platform screens that have no other entry point: a card of link chips, one row
   per platform led by its logo. Chips and not bare coloured text — at rest a 13 px
   undecorated link in a grey strip read as a footnote, and this is the only way into eight
   screens. ---- */
.dash-tool-groups { display: flex; flex-direction: column; gap: 0.625rem; list-style: none; margin: 0; padding: 0; }
.dash-tool-group { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem; }
.dash-tools-platform { display: inline-flex; align-items: center; min-width: 4.5rem; }
.dash-tools-platform .platform-logo { height: 16px; }
.dash-tool-list { display: flex; flex-wrap: wrap; gap: 0.5rem; list-style: none; margin: 0; padding: 0; }
.dash-tool {
  display: inline-flex;
  align-items: center;
  min-height: 2.5rem;
  padding: 0.375rem 0.875rem;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-full);
  box-shadow: var(--shadow-sm);
  color: var(--gray-800);
  font-size: 0.875rem;
  font-weight: 500;
  text-decoration: none;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.dash-tool:hover { border-color: var(--navy-300); box-shadow: var(--shadow-md); }

/* ---- The block the month switch governs (ranking, suppliers, fleet) and the fleet's legend ---- */
.dash-focus { display: flex; flex-direction: column; gap: 1.25rem; }
.dash-fleet-legend { margin: 0 0 0.75rem; }
.dash-heat-table th[scope="row"] { white-space: nowrap; font-weight: 500; }

/* ---- Keyboard: every interactive element of the page shows where the focus is, in the
   app's one ring (the .plate--link / .vehicle-card rule). Most of these paint a hover state
   only, and the UA's default outline was lost under border-radius and box-shadow. ---- */
.dash-chip:focus-visible, .dash-period-option:focus-visible, .dash-card-link:focus-visible,
.dash-link-source:focus-visible, .dash-podium-link:focus-visible, .dash-rank-row:focus-visible,
.dash-supplier-row:focus-visible, .dash-inline-link:focus-visible, .dash-crew-face:focus-visible,
.dash-crew-more a:focus-visible, .dash-tool:focus-visible {
  outline: 2px solid var(--navy-400);
  outline-offset: 2px;
}

@media (max-width: 640px) {
  /* Sixteen weeks at 12 px with 2 px gaps fit a 360 px phone, so the week in progress —
     the right-hand column — is on screen without scrolling the strip. */
  .dash-heatmap, .dash-heat-weekdays, .dash-heat-week { gap: 2px; }
  .dash-heat-week { min-width: 0.75rem; }
  .dash-hero-figure { font-size: 2.25rem; }
  .dash-kpi-figure { font-size: 1.5rem; }
  .dash-chart-body { grid-template-rows: 11rem auto; }
  .dash-chart-tip { min-width: 9rem; }
  .dash-rank-row { grid-template-columns: 1.25rem 28px minmax(0, 1fr) auto; }
  .dash-rank-bar { display: none; }
  .dash-supplier-row { grid-template-columns: 4rem minmax(0, 1fr) auto; }
  .dash-supplier-bar { display: none; }
  .dash-podium { gap: 0.375rem; }
  .dash-podium-link { padding: 0.75rem 0.25rem 0.625rem; }
  .dash-place-1 .dash-podium-link { padding-top: 1.125rem; }
  .dash-podium-name { font-size: 0.8125rem; }
  .dash-podium-avatar .avatar { width: 48px; height: 48px; }
  .dash-place-1 .dash-podium-avatar .avatar { width: 56px; height: 56px; }
  /* 14 px, not 16: the widest screen this block still has to fit is ~640 px, but the
     narrowest is 360, where the page's and the card's 1.5rem gutters leave each card about
     73 px of content. Six tabular figures, the group separator and a 0.65em currency want
     some 78 px of that at 14 px, and 90 at 16 — which is why the currency used to drop. */
  .dash-podium-amount { font-size: 0.875rem; }
  .dash-podium-amount small { font-size: 0.65em; }
  /* The strip is 70 px and "4,87" another 25, together wider than the card — so the number
     fell under the stars and that card grew a line the unrated one never had. 5:1 is the
     fragment's own 120x24 viewBox, so every star stays exactly 20 % of the strip and
     --rating-fill still cuts where it did; both layers are the same selector, so the clip
     cannot desync. Scoped to the podium: /users/{id} keeps the strip at full size. */
  .dash-podium-meta .rating-stars svg { width: 55px; height: 11px; }
}

/* The narrowest phones. At 360 px the page's 1.5rem gutters and the card's own 1.5rem leave
   the podium 262 px: with 0.375rem gaps that is an 83 px card and 73 px of content — too
   little for a six-figure amount beside its currency at any size still worth reading. The
   gap and the card's side padding hand back 5 px, one step of type another 6. Only the
   podium changes here, and only below the width a 390 or 430 px phone uses. The winner's
   step survives the padding shorthand: .dash-place-1's padding-top above is more specific. */
@media (max-width: 400px) {
  .dash-podium { gap: 0.25rem; }
  .dash-podium-link { padding: 0.625rem 0.125rem 0.5rem; }
  .dash-podium-name { font-size: 0.75rem; }
  .dash-podium-amount { font-size: 0.8125rem; }
  .dash-podium-meta { font-size: 0.6875rem; }
  .dash-podium-meta .rating-stars svg { width: 45px; height: 9px; }
}

/* A platform card reached from a notification's button (admin/companies/platforms.html). */
.platform-card-focus {
  box-shadow: 0 0 0 2px var(--accent);
}

/* ── While the next page is on its way ─────────────────────────────────────
   Moved here from an inline <style> in layout.html: this is where the design system lives,
   and style.css is a blocking <link> in the same <head>, so nothing can flash before it.

   The BAR is the default and the PANEL is opt-in (data-nav-loader="full"), because a thin
   line leaves the page the reader came from on screen while a full panel blanks it — and a
   click that never navigates, such as a download in the same tab, then leaves the whole
   screen empty instead of one hairline running. */
#vk-progress {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  z-index: 300;
  pointer-events: none;
  opacity: 0;
  transition: opacity 0.15s ease;
}
#vk-progress.is-active { opacity: 1; }
#vk-progress span {
  display: block;
  height: 100%;
  width: 30%;
  background: linear-gradient(90deg, transparent, var(--gold, #d4a843), transparent);
  /* An indeterminate sweep, not a bar creeping to 90%: nothing here knows how far along the
     response is, and a progress bar that invents its own number is a lie the reader learns
     to distrust. */
  animation: vk-progress-sweep 1.1s ease-in-out infinite;
}
@keyframes vk-progress-sweep {
  0% { transform: translateX(-100%); }
  100% { transform: translateX(433%); }
}

#vk-nav-loader {
    display: none;
    position: fixed;
    inset: 4rem 0 0 0;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 1.5rem;
    background: var(--gray-100, #f3f4f6);
    z-index: 50;
}
main.vk-loading #vk-nav-loader {
    display: flex;
}
main.vk-loading > :not(#vk-nav-loader) {
    visibility: hidden;
    pointer-events: none;
}
main.vk-loading .app-sidebar {
    visibility: visible;
}
.vk-nav-spinner {
    width: 36px;
    height: 36px;
    border: 4px solid rgba(34, 59, 96, 0.15);
    border-top-color: #d4a843;
    border-radius: 50%;
    animation: vk-nav-spin 0.8s linear infinite;
}
@keyframes vk-nav-spin {
    to { transform: rotate(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  /* Still says "busy", without anything moving. The spinner had no such guard at all. */
  #vk-progress span {
    animation: none;
    width: 100%;
    opacity: 0.6;
    background: var(--gold, #d4a843);
  }
  .vk-nav-spinner { animation: none; }
}

/* ── Mail signatures, the company logo, who sent a message (V115) ─────────────────────────────
   The editor (settings/signature-form.html) is the form and, beside it, the signature as the mail
   shows it — on a white "paper" so a navy-on-grey page does not colour the judgement. One column
   under 900 px, the preview then under the form. */
.signature-editor {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 24rem);
  gap: 1.5rem;
  align-items: start;
}
@media (max-width: 900px) {
  .signature-editor { grid-template-columns: minmax(0, 1fr); }
}
.signature-templates { border: 0; padding: 0; margin: 0 0 1rem 0; min-width: 0; }
.signature-template-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9.5rem, 1fr));
  gap: 0.5rem;
}
.signature-template-option { position: relative; display: block; cursor: pointer; }
.signature-template-option input {
  position: absolute; opacity: 0; width: 1px; height: 1px; pointer-events: none;
}
.signature-template-card {
  display: flex; flex-direction: column; gap: 0.25rem; height: 100%;
  padding: 0.625rem 0.75rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  background: var(--white);
}
.signature-template-option:hover .signature-template-card { border-color: var(--navy-200); }
.signature-template-option input:checked + .signature-template-card {
  border-color: var(--gold-400);
  box-shadow: 0 0 0 2px var(--gold-100);
}
.signature-template-option input:focus-visible + .signature-template-card {
  outline: 2px solid var(--navy-400); outline-offset: 2px;
}
.signature-template-name { font-weight: 600; color: var(--navy-600); }
/* A small drawing of each layout: i = picture, b = lines of text. */
.signature-template-sketch {
  display: grid; grid-template-columns: auto 1fr; column-gap: 0.375rem; row-gap: 3px;
  align-items: center; height: 2.25rem; margin-bottom: 0.25rem;
}
.signature-template-sketch i {
  grid-row: 1 / span 3; width: 1.5rem; height: 1.5rem; border-radius: 50%; background: var(--navy-100);
}
.signature-template-sketch b { display: block; height: 4px; border-radius: 2px; background: var(--gray-300); }
.signature-template-sketch b:first-of-type { width: 70%; background: var(--navy-300); }
.signature-template-sketch b:nth-of-type(2) { width: 50%; }
.signature-template-sketch b:nth-of-type(3) { width: 85%; }
.signature-template-sketch.is-classic i,
.signature-template-sketch.is-minimal i { display: none; }
.signature-template-sketch.is-classic,
.signature-template-sketch.is-minimal { grid-template-columns: 1fr; }
.signature-template-sketch.is-classic { border-left: 3px solid var(--gold-400); padding-left: 0.375rem; }
.signature-template-sketch.is-company i {
  grid-row: auto; grid-column: 1 / -1; width: 2.5rem; height: 0.625rem; border-radius: 2px;
}
.signature-template-sketch.is-company { grid-template-columns: 1fr; }
.signature-pictures { display: flex; flex-direction: column; gap: 0.375rem; }

.signature-preview-panel { position: sticky; top: 5rem; min-width: 0; }
@media (max-width: 900px) {
  .signature-preview-panel { position: static; }
}
.signature-preview-mail {
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  padding: 1rem;
  overflow-x: auto;
}
.signature-preview-body { margin: 0 0 1rem 0; font-size: 0.8125rem; }
.signature-preview:empty::before { content: "—"; color: var(--gray-400); }

.company-logo-row { display: flex; flex-wrap: wrap; gap: 1.25rem; align-items: flex-start; }
.company-logo-frame {
  display: flex; align-items: center; justify-content: center;
  width: 14rem; max-width: 100%; min-height: 6rem; padding: 0.75rem;
  border: 1px dashed var(--gray-300); border-radius: var(--radius-lg);
  /* A checkerboard, so a transparent logo shows that it is. */
  background: repeating-conic-gradient(var(--gray-100) 0% 25%, var(--white) 0% 50%) 50% / 16px 16px;
}
.company-logo-frame img { max-width: 100%; max-height: 6rem; height: auto; }
.company-logo-form { flex: 1 1 16rem; min-width: 0; }

.signature-mailboxes { list-style: none; margin: 0; padding: 0; display: grid; gap: 0.25rem; }
.signature-mailboxes a {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem;
  padding: 0.5rem 0.75rem; border-radius: var(--radius);
  color: inherit; text-decoration: none; min-height: 2.5rem;
}
.signature-mailboxes a:hover { background: var(--gray-50); }
.signature-mailboxes a.is-active { background: var(--navy-50); font-weight: 600; }
.signature-mailboxes a:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 2px; }

/* The compose window's choice: ONE line — the select, the eye that shows the preview on request
   (hidden by default: it took up to 9rem of a 36rem window) and the pen to the editor. */
.mail-compose-signature { display: flex; flex-direction: column; gap: 0.375rem; margin-bottom: 0.5rem; }
.mail-compose-signature-bar { display: flex; align-items: center; gap: 0.25rem; min-width: 0; }
.mail-compose-signature .mail-compose-signature-select {
  width: auto; max-width: 100%; min-width: 9rem;
  padding: 0.2rem 1.75rem 0.2rem 0.5rem;
  font-size: 0.8125rem;
}
.mail-compose-signature-bar .btn-icon-ghost { width: 1.75rem; height: 1.75rem; flex-shrink: 0; }
.mail-compose-signature-bar .btn-icon-ghost svg { width: 1rem; height: 1rem; }
.mail-compose-signature-peek[aria-expanded="true"] { background: var(--navy-50); }
.mail-compose-signature-peek:disabled { opacity: 0.4; cursor: default; }
.mail-compose-signature-edit { color: var(--navy-500); }
.mail-compose-signature-preview {
  max-height: 8rem; overflow: auto;
  padding: 0.5rem 0.75rem;
  border: 1px dashed var(--gray-300); border-radius: var(--radius);
  background: var(--white);
}
.mail-compose-signature-preview[hidden] { display: none; }

/* The invoice mail's signature as it will go out (/invoices/settings). */
.invoice-signature-preview {
  padding: 0.5rem 0.75rem;
  border: 1px dashed var(--gray-300); border-radius: var(--radius);
  background: var(--white);
}
.invoice-signature-preview[hidden] { display: none; }
.invoice-signature-edit { color: var(--navy-500); text-decoration: underline; }

/* Who sent a message of the Sent view: a face, the ValkOS mark or a phone before the recipient. */
.mail-sent-by {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.125rem; height: 1.125rem; margin-inline-end: 0.375rem;
  vertical-align: -0.25rem; flex: none; color: var(--gray-500);
}
.mail-sent-by-face { width: 1.125rem; height: 1.125rem; border-radius: 50%; object-fit: cover; }
.mail-sent-by-initial {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.125rem; height: 1.125rem; border-radius: 50%;
  background: var(--navy-100); color: var(--navy-700); font-size: 0.625rem; font-weight: 700;
}
.mail-sent-by-valkos { width: 1rem; height: 1rem; }
.mail-sent-by.is-outside svg { width: 0.9375rem; height: 0.9375rem; }
.mail-sent-by-line {
  display: inline-flex; align-items: center; gap: 0.125rem;
  font-size: 0.8125rem; color: var(--gray-600);
}

/* ── Files sent as links (V119): the sender's list and the recipient's page ─────────────────
   /email/accounts/{id}/shared-files is a row-hover table (the row's actions take the expiry
   date's place); the row an opened Sent message pointed at (?highlight=) wears the gold stripe an
   opened row wears elsewhere, so it is found without a search. /share/{token} is the login-card
   shell with one card naming the file. */
.shared-files-table td { vertical-align: top; }
.shared-files-cell-file { min-width: 14rem; }
.shared-files-icon { display: inline-flex; vertical-align: middle; margin-right: 0.25rem; } /* the file kind's mark */
.shared-files-icon svg { width: 0.875rem; height: 0.875rem; }
.shared-files-subject,
.shared-files-recipients {
  display: block;
  max-width: 18rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.shared-files-subject { margin-top: 0.125rem; font-size: 0.75rem; }
.row-select-bar select.shared-files-days { width: auto; padding-right: 1.75rem; }
.row-hover-table tbody tr.is-highlighted { background-color: color-mix(in srgb, var(--gold-200) 42%, white); }
.row-hover-table tbody tr.is-highlighted > td:first-child { box-shadow: inset 3px 0 0 var(--gold-400); }
@media (max-width: 720px) {
  .shared-files-subject,
  .shared-files-recipients { max-width: 10rem; }
}

.share-card { max-width: 26rem; }
.share-logo { height: auto; max-height: 4rem; max-width: 12rem; object-fit: contain; }
.share-company { font-size: 1.125rem; text-align: center; overflow-wrap: anywhere; }
.share-file-card {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  margin: 0 0 1rem;
  padding: 0.875rem 1rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  background: var(--gray-50);
}
.share-file-icon { display: inline-flex; flex-shrink: 0; } /* the file kind's mark */
.share-file-icon svg { width: 2.25rem; height: 2.25rem; }
.share-file-details { min-width: 0; }
.share-file-name { margin: 0; font-weight: 600; color: var(--gray-900); overflow-wrap: anywhere; }
.share-file-size { margin: 0; color: var(--gray-500); }
/* "Download all" (V121): the files the ZIP will hold, one line each — the kind's icon, the name,
   the size, and a button for that file alone. */
.share-bundle-list { display: flex; flex-direction: column; gap: 0.375rem; margin: 0 0 0.75rem; padding: 0; list-style: none; }
.share-bundle-file {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
}
.share-bundle-icon { display: inline-flex; flex-shrink: 0; }
.share-bundle-icon svg { width: 1.125rem; height: 1.125rem; }
.share-bundle-name { flex: 1; min-width: 0; font-weight: 600; font-size: 0.875rem; color: var(--gray-900); overflow-wrap: anywhere; }
/* Each file on its own too (2026-09-28): a square download button at the row's end. */
.share-bundle-one { margin: 0; flex-shrink: 0; }
.share-bundle-one-btn { display: inline-flex; align-items: center; justify-content: center; width: 2.5rem; height: 2.5rem; padding: 0; }

/* ── Link appearance (V120): /email/accounts/{id}/shared-files/settings ───────────────────
   Each card is the fields on the left and a preview beside them (under them on a phone): the
   public page in a srcdoc frame filled by static/js/shared-file-settings.js (srcdoc, never src —
   every response says X-Frame-Options: DENY), and the block of links as the mail will carry it.
   The layout choice is a list of bordered rows, the ticked one tinted gold like a chosen
   signature layout. */
.shared-files-settings-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 26rem);
  gap: 1.5rem;
  align-items: start;
}
@media (max-width: 900px) {
  .shared-files-settings-grid { grid-template-columns: minmax(0, 1fr); }
}
.shared-files-settings-preview { min-width: 0; }
.shared-files-checks { display: flex; flex-direction: column; gap: 0.5rem; margin-bottom: 1rem; }
.shared-files-check {
  display: flex; align-items: flex-start; gap: 0.5rem;
  min-height: 1.5rem; cursor: pointer;
}
.shared-files-check input { flex: none; margin-top: 0.2rem; }
.shared-files-check-hint { margin-left: 1.5rem; }
.share-landing-preview {
  display: block;
  width: 100%;
  height: 34rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  background: var(--gray-50);
}
.shared-files-mail-preview {
  max-width: 40rem;
  padding: 1rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  background: var(--white);
  overflow: auto;
}
.shared-files-mail-preview:empty::before { content: "—"; color: var(--gray-400); }
.shared-files-template-choice { border: 0; padding: 0; margin: 0 0 1rem 0; min-width: 0; }
.shared-files-template-choice legend { padding: 0; }
.shared-files-template-list { display: grid; gap: 0.5rem; }
.shared-files-template-option {
  display: flex; align-items: flex-start; gap: 0.625rem;
  padding: 0.625rem 0.75rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-lg);
  background: var(--white);
  cursor: pointer;
}
.shared-files-template-option:hover { border-color: var(--navy-200); }
.shared-files-template-option input { flex: none; margin-top: 0.2rem; }
.shared-files-template-option:has(input:checked) {
  border-color: var(--gold-400);
  background: var(--gold-50);
  box-shadow: 0 0 0 2px var(--gold-100);
}
.shared-files-template-option:has(input:focus-visible) { outline: 2px solid var(--navy-400); outline-offset: 2px; }
.shared-files-template-text { display: flex; flex-direction: column; gap: 0.125rem; min-width: 0; }
.shared-files-template-name { font-weight: 600; color: var(--navy-600); }
.shared-files-template-desc { overflow-wrap: anywhere; }
select.shared-files-ttl { width: auto; max-width: 100%; min-width: 14rem; }

/* ── Reports (reporting/): the report screen, its blocks and the composer ─────────────────
   One block for the module. The blocks reuse the dashboard's chart classes (.dash-chart,
   .dash-donut, .dash-meter) and add only what a report needs: KPI tiles that fit any number,
   a totals line per currency, status badges, a table of contents beside the sections. */
.rep-view-head { margin-bottom: 1.25rem; }
.rep-view-sub {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.5rem;
  margin: 0.25rem 0 0.75rem;
  font-size: 0.9375rem;
  color: var(--gray-600);
}
.rep-view-period { font-weight: 600; color: var(--gray-800); }
.rep-view-period::first-letter { text-transform: uppercase; }
.rep-view-dot { color: var(--gray-400); }
.rep-view-note { margin: 0.5rem 0; }
.rep-view-actions { margin-top: 0.5rem; }
/* The driver report's head (/users/{id}/report): the platforms the person drives for and their
   rating, one line that wraps; a logo a little larger than the 16 px inline one. */
.driver-report-facts {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem 1.5rem;
  margin: 0 0 0.5rem;
  font-size: 0.9375rem;
  color: var(--gray-700);
}
.driver-report-platforms { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; }
.driver-report-platforms .platform-logo { height: 20px; }
.driver-report-rating { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.rep-view-body { display: block; }
.rep-sections { display: flex; flex-direction: column; gap: 1.25rem; min-width: 0; }
.rep-section { container-type: inline-size; }
.rep-section-note { margin: 0; }
/* The table of contents: a quiet list that follows the reader on a wide screen. */
.rep-toc { display: none; }
@media (min-width: 1100px) {
  .rep-view-body { display: grid; grid-template-columns: 12rem minmax(0, 1fr); gap: 1.5rem; align-items: start; }
  .rep-toc { display: block; position: sticky; top: 5rem; }
}
.rep-toc-title {
  margin: 0 0 0.5rem;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--gray-500);
}
.rep-toc ol { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.125rem; }
.rep-toc a {
  display: block;
  padding: 0.375rem 0.625rem;
  border-left: 2px solid var(--gray-200);
  font-size: 0.8125rem;
  color: var(--gray-600);
  text-decoration: none;
}
.rep-toc a:hover { color: var(--navy-700); border-color: var(--navy-300); }
.rep-toc a[aria-current] { color: var(--navy-800); border-color: var(--gold-500); font-weight: 600; }
.rep-left-out { display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.875rem; color: var(--gray-600); }
.rep-left-out-line { margin: 0; }
.rep-empty-state { text-align: center; padding: 2rem 1rem; color: var(--gray-600); }
.rep-empty-state .btn { margin-top: 0.75rem; }

/* KPI tiles: as many as the section has, each a label over a figure. */
.rep-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr));
  gap: 1rem 1.5rem;
  margin: 0 0 1.25rem;
}
.rep-kpi { display: flex; flex-direction: column; gap: 0.125rem; min-width: 0; }
.rep-kpi-label { font-size: 0.8125rem; font-weight: 500; color: var(--gray-600); }
.rep-kpi-figure {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--gray-800);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.rep-kpi-unit { font-size: 0.875rem; font-weight: 600; color: var(--gray-500); margin-left: 0.25rem; }
.rep-kpi-hint { font-size: 0.75rem; color: var(--gray-500); }
.rep-kpi-delta { margin-top: 0.125rem; font-size: 0.75rem; }
.rep-block-title {
  margin: 0.25rem 0 0.5rem;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--gray-800);
}
.rep-table + .rep-table, .rep-kpis + .rep-table, .rep-columns + .rep-table, .rep-ring + .rep-table { margin-top: 1rem; }
.rep-table .table td, .rep-table .table th { vertical-align: middle; }
.rep-row--muted td { color: var(--gray-500); }
.rep-row--warning td { background: var(--red-50); }
.rep-total td { font-weight: 600; background: var(--gray-50); border-top: 2px solid var(--gray-300); }
.rep-total-currency { margin-left: 0.375rem; font-weight: 500; color: var(--gray-500); }
.rep-empty { color: var(--gray-400); }
.rep-person { color: inherit; text-decoration: underline; text-decoration-color: var(--gray-300); }
.rep-person:hover { text-decoration-color: currentColor; }
/* A status badge: the word carries the meaning, the colour only helps. */
.rep-status--ok    { background-color: #dcfce7; color: #166534; }
.rep-status--warn  { background-color: var(--yellow-50); color: #854d0e; }
.rep-status--error { background-color: var(--red-50); color: #991b1b; }
.rep-status--muted { background-color: var(--gray-100); color: var(--gray-600); }
.rep-note { margin: 0.5rem 0; }

/* Chart colours by name: the dashboard's pair, the ring's five slots, the categories'. A
   swatch and a bar wear the same class, so a legend and its column cannot drift apart. */
.rep-bar--in { background: var(--chart-in); }
.rep-bar--out { background: var(--chart-out); }
.rep-bar--pie-1 { background: var(--pie-1); }
.rep-bar--pie-2 { background: var(--pie-2); }
.rep-bar--pie-3 { background: var(--pie-3); }
.rep-bar--pie-4 { background: var(--pie-4); }
.rep-bar--pie-5 { background: var(--pie-5); }
.rep-bar--pie-rest { background: var(--pie-rest); }
.rep-bar--cat-fuel { background: var(--cat-fuel); }
.rep-bar--cat-charging { background: var(--cat-charging); }
.rep-bar--cat-parking { background: var(--cat-parking); }
.rep-bar--cat-tolls { background: var(--cat-tolls); }
.rep-bar--cat-service { background: var(--cat-service); }
.rep-bar--cat-insurance { background: var(--cat-insurance); }
.rep-bar--cat-financing { background: var(--cat-financing); }
.rep-bar--cat-subscription { background: var(--cat-subscription); }
.rep-bar--cat-infrastructure { background: var(--cat-infrastructure); }
.rep-bar--cat-accounting { background: var(--cat-accounting); }
.rep-bar--cat-taxes_fees { background: var(--cat-taxes_fees); }
.rep-bar--cat-other { background: var(--cat-other); }
.rep-columns .dash-chart-bars { width: 84%; }
.rep-ring-body { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem 2rem; }
.rep-ring-body .dash-donut-legend { flex: 1 1 14rem; min-width: 0; }
.rep-ring-percent { color: var(--gray-500); font-variant-numeric: tabular-nums; }
.rep-strip-bar { margin-bottom: 0.5rem; }
.rep-slot-1 { background: var(--pie-1); }
.rep-slot-2 { background: var(--pie-2); }
.rep-slot-3 { background: var(--pie-3); }
.rep-slot-4 { background: var(--pie-4); }
.rep-slot-5 { background: var(--pie-5); }
.rep-slot-6, .rep-slot-7, .rep-slot-8, .rep-slot-9 { background: var(--pie-rest); }
.rep-strip-lines { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.25rem 1.25rem; font-size: 0.8125rem; }
.rep-strip-lines li { display: inline-flex; align-items: center; gap: 0.375rem; }

/* The home: templates as cards. */
.rep-hero { text-align: center; padding: 2.5rem 1.5rem; }
.rep-hero-text { max-width: 36rem; margin: 0.5rem auto 1.25rem; color: var(--gray-600); }
.rep-home-section { margin-bottom: 2rem; }
.rep-home-heading { font-size: 1rem; font-weight: 600; color: var(--gray-700); margin: 0 0 0.75rem; }
.rep-cards {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(18rem, 1fr));
  gap: 1rem;
}
.rep-card-template { display: flex; flex-direction: column; gap: 0.625rem; margin: 0; }
.rep-card-name { margin: 0; font-size: 1.0625rem; font-weight: 600; }
.rep-card-name a { color: var(--navy-800); text-decoration: none; }
.rep-card-name a:hover { text-decoration: underline; }
.rep-card-desc { margin: 0.125rem 0 0; font-size: 0.875rem; color: var(--gray-600); }
.rep-card-meta { margin: 0; font-size: 0.8125rem; color: var(--gray-500); display: flex; flex-wrap: wrap; gap: 0.25rem 0.5rem; }
.rep-card-areas { list-style: none; margin: 0; padding: 0; display: flex; flex-wrap: wrap; gap: 0.25rem; }
.rep-area-chip {
  font-size: 0.75rem;
  font-weight: 500;
  padding: 0.125rem 0.5rem;
  border-radius: var(--radius-full);
  background: var(--navy-50);
  color: var(--navy-700);
}
.rep-card-actions { margin-top: auto; padding-top: 0.25rem; }
.rep-card-suggested { border-style: dashed; }
.btn-danger-outline { color: var(--red-600); border-color: var(--red-200, #fecaca); }
.btn-danger-outline:hover { background: var(--red-50); }

@media print {
  .rep-view-actions, .rep-toc, .app-sidebar { display: none !important; }
  .rep-section { break-inside: avoid; }
}

/* The composer: the period and the scope on top, the catalog beside the report being built. */
.rep-composer { display: flex; flex-direction: column; gap: 1.25rem; }
.rep-params { margin: 0; }
.rep-params-title { margin: 0 0 0.75rem; font-size: 0.9375rem; font-weight: 600; color: var(--gray-700); }
.rep-params-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 1rem 1.5rem; }
.rep-scope-group { display: flex; flex-direction: column; gap: 0.25rem; min-width: 12rem; }
/* The comparison toggle sits with the period it changes; its hint wraps under it, never widens the row. */
.rep-compare { display: flex; flex-direction: column; gap: 0.25rem; max-width: 22rem; }
.rep-compare .form-hint { font-size: 0.75rem; color: var(--gray-500); }
.rep-scope-panel { max-height: 18rem; overflow-y: auto; min-width: 16rem; }
.rep-scope-empty { margin: 0; padding: 0.5rem 0.75rem; font-size: 0.875rem; color: var(--gray-500); }
.rep-composer-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1.25rem; align-items: start; }
/* One column: the report being built comes first — it is what the reader is making, and the
   catalog under it is long. Two columns: the catalog on the left, the report sticky beside it. */
.rep-picked-pane { order: -1; }
@media (min-width: 1024px) {
  .rep-composer-grid { grid-template-columns: minmax(0, 3fr) minmax(18rem, 2fr); }
  .rep-picked-pane { order: 0; position: sticky; top: 5rem; }
}
.rep-pane-title { margin: 0 0 0.25rem; font-size: 1rem; font-weight: 600; color: var(--gray-800); }
.rep-pane-hint { margin: 0 0 0.75rem; font-size: 0.8125rem; color: var(--gray-500); }
.rep-area { margin-bottom: 1.25rem; }
.rep-area-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 0.5rem; margin: 0 0 0.5rem; }
.rep-area-title {
  margin: 0;
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--gray-500);
}
.rep-picks { display: grid; grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr)); gap: 0.75rem; }
.rep-pick { margin: 0; padding: 0.75rem 0.875rem; border: 1px solid var(--gray-200); transition: border-color 0.15s, box-shadow 0.15s; }
.rep-pick:has(.rep-pick-box:checked) { border-color: var(--navy-500); box-shadow: inset 0 0 0 1px var(--navy-500); background: var(--navy-50); }
.rep-pick:has(.rep-pick-box:focus-visible) { outline: 2px solid var(--navy-400); outline-offset: 2px; }
.rep-pick--gated { opacity: 0.7; background: var(--gray-50); }
.rep-pick-row { display: flex; align-items: flex-start; gap: 0.625rem; }
.rep-pick-box { flex: none; margin-top: 0.2rem; width: 1rem; height: 1rem; }
.rep-pick-body { display: flex; flex-direction: column; gap: 0.25rem; min-width: 0; cursor: pointer; }
.rep-pick--gated .rep-pick-body { cursor: default; }
.rep-pick-title { font-weight: 600; color: var(--gray-800); }
.rep-pick-desc { font-size: 0.8125rem; color: var(--gray-600); line-height: 1.4; }
.rep-pick-chips { display: flex; flex-wrap: wrap; gap: 0.25rem; margin-top: 0.125rem; }
.rep-chip {
  font-size: 0.6875rem;
  font-weight: 500;
  padding: 0.0625rem 0.4375rem;
  border-radius: var(--radius-full);
  background: var(--gray-100);
  color: var(--gray-600);
}
.rep-chip--audience { background: var(--yellow-50); color: #854d0e; }
.rep-chip--compare { background: var(--navy-50); color: var(--navy-700); }
.rep-pick-gate { font-size: 0.75rem; color: var(--gray-500); font-style: italic; }
.rep-options { margin-top: 0.5rem; padding-left: 1.625rem; }
.rep-options-summary { cursor: pointer; font-size: 0.8125rem; color: var(--navy-600); }
.rep-options-body { display: flex; flex-direction: column; gap: 0.375rem; padding: 0.5rem 0 0; }
.rep-option { display: flex; align-items: center; gap: 0.5rem; font-size: 0.8125rem; color: var(--gray-700); }
.rep-option--choice { justify-content: space-between; }
.rep-picked-fold { border: 1px solid var(--gray-200); border-radius: var(--radius); background: #fff; padding: 0.875rem 1rem; }
.rep-picked-summary { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; cursor: pointer; list-style: none; }
.rep-picked-summary::-webkit-details-marker { display: none; }
.rep-picked-count { font-size: 0.8125rem; font-weight: 500; color: var(--gray-500); }
.rep-picked { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.375rem; counter-reset: rep; }
.rep-picked-item {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0.5rem 0.625rem;
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  background: var(--gray-50);
  counter-increment: rep;
}
.rep-picked-item::before { content: counter(rep); min-width: 1.25rem; font-size: 0.75rem; font-weight: 600; color: var(--gray-400); text-align: center; }
.rep-picked-item.is-dragging { background: var(--navy-50); border-color: var(--navy-300); }
.rep-picked-handle { cursor: grab; touch-action: none; }
.rep-picked-title { flex: 1 1 auto; min-width: 0; font-size: 0.875rem; font-weight: 500; color: var(--gray-800); }
.rep-picked-remove { flex: none; }
.rep-picked-empty { margin: 0.25rem 0 0; font-size: 0.8125rem; color: var(--gray-500); }
.rep-save { margin: 0; }
.rep-save-title { font-weight: 600; color: var(--gray-800); padding: 0 0.25rem; }
.rep-save-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: 0.75rem; margin-bottom: 0.75rem; }
.rep-composer.is-enhanced .rep-save { display: none; }
.rep-save-dialog .rep-save { display: block !important; border: 0; padding: 0; box-shadow: none; }
.rep-save-dialog .rep-save-actions { justify-content: flex-end; }
.rep-composer-actions {
  position: sticky;
  bottom: 0;
  z-index: 5;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem 1rem;
  padding: 0.75rem 0;
  background: linear-gradient(to top, #fff 70%, rgba(255, 255, 255, 0));
}
.rep-composer-status { font-size: 0.8125rem; color: var(--gray-500); }

/* Sending the report: a folded card under the actions. */
.rep-send { margin-bottom: 1.25rem; }
.rep-send-summary { cursor: pointer; font-weight: 600; color: var(--navy-800); }
.rep-send-form { margin-top: 0.875rem; }
.rep-send-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr)); gap: 0.75rem; }
.rep-send-wide { grid-column: 1 / -1; }
.rep-send-actions { margin-top: 0.75rem; }
/* A saved report's schedules: one row each, what and when first, the buttons under it. */
.rep-schedule-lead { margin: 0 0 0.75rem; max-width: 48rem; color: var(--gray-600); }
.rep-schedules { list-style: none; margin: 0; padding: 0; }
.rep-schedule { display: flex; flex-direction: column; gap: 0.5rem; padding: 0.875rem 0; border-top: 1px solid var(--gray-200); }
.rep-schedule:first-child { border-top: 0; padding-top: 0.25rem; }
.rep-schedule.is-off .rep-schedule-main { opacity: 0.6; }
.rep-schedule-when, .rep-schedule-who, .rep-schedule-last {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 1rem; margin: 0;
}
.rep-schedule-who, .rep-schedule-next { font-size: 0.8125rem; color: var(--gray-600); }
.rep-schedule-who span { overflow-wrap: anywhere; }
.rep-schedule-last { font-size: 0.8125rem; gap: 0.25rem 0.5rem; }
.rep-schedule-last.is-ok { color: var(--green-600); }
.rep-schedule-last.is-bad { color: #b45309; }
.rep-schedule-when-fields { border: 0; margin: 0 0 0.75rem; padding: 0; min-width: 0; }
.rep-schedule-choice { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 0.75rem; margin-bottom: 0.5rem; }
.rep-schedule-choice .rep-option { min-width: 11rem; }
.rep-schedule-day { width: auto; min-width: 6rem; max-width: 12rem; }
.rep-schedule-hint { margin: 0.25rem 0 0; font-size: 0.75rem; color: var(--gray-500); }

/* ── Marks: a name wears its ValkOS picture (fragments/mark.html, config/Mark) ──────────────
   The drawings are one file per family (fragments/measure-marks.html…), the category marks'
   family (the logo's navy steps and one gold element) but INLINE, so a surface can recolour
   them: every facet carries its palette fill AND a tone class, and the classes read --mk-*
   properties that fall back to the palette; a one-tone family (states, glyphs) is mk-c, the
   colour of the words beside it. This block is the ONE list of surfaces that change a mark — a
   mark put on a new dark or gold surface adds its selector here. */
.vk-mark { flex: none; width: 16px; height: 16px; }
.vk-mark .mk-n5 { fill: var(--mk-n5, var(--navy-500)); }
.vk-mark .mk-n6 { fill: var(--mk-n6, var(--navy-600)); }
.vk-mark .mk-n7 { fill: var(--mk-n7, var(--navy-700)); }
.vk-mark .mk-n8 { fill: var(--mk-n8, var(--navy-800)); }
.vk-mark .mk-n9 { fill: var(--mk-n9, var(--navy-900)); }
.vk-mark .mk-g  { fill: var(--mk-g, var(--gold-400)); }
.vk-mark .mk-c  { fill: currentColor; }
/* A directional glyph (config/Glyph) points along the reading direction: turned round where the page
   reads right to left — by `scale`, not `transform`, so a control's own rotation (the sidebar's
   chevron, a fold's caret) composes with it instead of replacing it. */
[dir="rtl"] .vk-mark.glyph-flip { scale: -1 1; }
/* On navy (the payslip's table head and foot): logo-light's idea — the facets in light steps,
   the thick half brightest, the gold kept. */
.payroll-table th, .payroll-table tfoot td {
  --mk-n5: var(--white); --mk-n6: var(--navy-50); --mk-n7: var(--navy-50);
  --mk-n8: var(--navy-100); --mk-n9: var(--navy-100);
}
/* On navy too: the owner's dark badge (.bg-owner). */
.bg-owner {
  --mk-n5: var(--white); --mk-n6: var(--navy-50); --mk-n7: var(--navy-50);
  --mk-n8: var(--navy-100); --mk-n9: var(--navy-100);
}
/* On a filled button (navy, slate, red, green): the same light steps, the gold kept. An outline
   button's ground stays light, hover included, so its marks keep the palette. */
.btn-primary, .btn-secondary, .btn-danger, .btn-success {
  --mk-n5: var(--white); --mk-n6: var(--navy-50); --mk-n7: var(--navy-50);
  --mk-n8: var(--navy-100); --mk-n9: var(--navy-100);
}
/* On the side rail (navy-800), the navbar (navy-900) with every menu that drops from it (navy-800:
   the bell's, the account's, the mail's), the push banner's outline button
   (on navy-500), the preview strip's own words while the strip is red and its outline button then:
   the light steps, the gold kept. */
.app-sidebar, .navbar, #push-permission-banner .btn-outline,
#vk-preview-banner.preview-bar--writing > .preview-bar-msg, #vk-preview-banner.preview-bar--writing .btn-outline {
  --mk-n5: var(--white); --mk-n6: var(--navy-50); --mk-n7: var(--navy-50);
  --mk-n8: var(--navy-100); --mk-n9: var(--navy-100);
}
#vk-preview-banner.preview-bar--writing > .preview-bar-msg, #vk-preview-banner.preview-bar--writing .btn-outline { --mk-g: initial; }
/* On white (the red preview strip's exit button): the palette again. */
#vk-preview-banner.preview-bar--writing .btn-primary {
  --mk-n5: initial; --mk-n6: initial; --mk-n7: initial; --mk-n8: initial; --mk-n9: initial;
}
/* The bell's "mark all as read" underlines its words, which the inline-flex label keeps from
   the button. */
.notif-readall-btn:hover .vk-mark-label > span { text-decoration: underline; }
/* On gold (the gross salary's column and band, the warning button, the rail's lit entry, the push
   banner's gold button, the gold preview strip's own words and its outline button): the navy stays,
   the accent turns white — a navy accent would vanish into the navy body it usually sits on. */
.payroll-table th.payroll-col-payout, .payroll-table tfoot td.payroll-col-payout, .totals-row.final, .btn-warning,
.app-sidebar-nav a.active, #push-permission-banner .btn-primary, #vk-preview-banner > .preview-bar-msg,
#vk-preview-banner .btn-outline {
  --mk-n5: initial; --mk-n6: initial; --mk-n7: initial; --mk-n8: initial; --mk-n9: initial;
  --mk-g: var(--white);
}
/* The mark and the words: one flex line, so they never part and a long name wraps inside its
   own span; the svg is centred and out of the baseline, so the words' baseline is the label's
   and a .totals-row or a dl lines up as before. 16 px is the floor — a category mark at 13 px
   read as a speck. */
.vk-mark-label { display: inline-flex; align-items: baseline; gap: max(4px, 0.35em); max-width: 100%; }
.vk-mark-label > span { min-width: 0; }
/* A link's underline does not reach into an inline-flex box, so a marked label takes over whatever
   decoration its link has — at rest and on hover (the dashboard's card links, the bell's action). */
a > .vk-mark-label, a > .vk-mark-label > span { text-decoration: inherit; }
/* The bell's "go to" arrow is as tall as its small words. */
.notif-item-action .vk-mark-label > .vk-mark { width: 1em; height: 1em; }
.vk-mark-label > .vk-mark { align-self: center; width: max(16px, 1.25em); height: max(16px, 1.25em); }
.choice-menu-item > .vk-mark, .choice-menu-icon > .vk-mark { width: 16px; height: 16px; }
/* A button's verb: 16 px whatever the button's font, on the pixel grid. */
.btn .vk-mark-label > .vk-mark, .btn > .vk-mark { width: 16px; height: 16px; }
/* An icon-only control that cannot act now (a plan entry already confirmed, a code not loaded yet, a
   download in a preview): the mark is all it shows, so the mark fades and loses its colour. */
.btn-icon:disabled .vk-mark, .btn-icon-ghost:disabled .vk-mark,
.share-bundle-one-btn:disabled .vk-mark { opacity: 0.4; filter: grayscale(1); }
/* Glyphs (config/Glyph) keep the size their control drew before they became marks. */
.app-sidebar-back > .vk-mark { width: 13px; height: 13px; }
.company-select-arrow > .vk-mark { width: 20px; height: 20px; }
.btn.print-lightbox-nav > .vk-mark { width: 18px; height: 18px; }
.print-thumb-check > .vk-mark { width: 12px; height: 12px; }
.print-thumb-zoom > .vk-mark { width: 14px; height: 14px; }
.vk-datepicker-nav-btn > .vk-mark { width: 18px; height: 18px; }
/* A rail entry's mark rests a little dimmed and lights with its entry; a navbar module link's the same. */
.app-sidebar-nav a > .vk-mark { opacity: 0.75; transition: opacity 0.15s; }
.app-sidebar-nav a:hover > .vk-mark, .app-sidebar-nav a.active > .vk-mark { opacity: 1; }
.navbar-nav .nav-link > .vk-mark-label > .vk-mark { opacity: 0.75; transition: opacity 0.15s; }
.navbar-nav .nav-link:hover > .vk-mark-label > .vk-mark { opacity: 1; }
/* The navbar's icon-only toggles (mail, the bell) keep the 20 px their line icons had. */
.nav-notifications-mobile > .vk-mark, .nav-mail-toggle > .vk-mark,
.nav-notifications-toggle > .vk-mark { width: 20px; height: 20px; }
/* A notification's kind in its 2.25rem disc, and the bell's sound toggle. */
.notif-card-icon > .vk-mark, .notif-detail-icon > .vk-mark { width: 1.125rem; height: 1.125rem; }
.notif-sound-btn > .vk-mark { width: 16px; height: 16px; }
/* Between the phone layout and a wide desk the module links show their marks ALONE — with the
   words the bar overflowed under the right-hand cluster (decided with the user, 2026-10-02: 769 to
   1100 px; measured again on 2026-10-07 after tasks and the gear moved into the account menu; and
   on 2026-10-10 once the bar became a strip in a fixed place with the company's name always in it).
   The thresholds are CONTAINER queries on the strip (.navbar-inner), so they are about the room it
   has — its content box, 1152 px at most — measured in headless Chromium with Inter on a
   SUPERADMIN's seven links, the company switcher and the account (the widest bar): the words with
   the whole name "Test Company" need 1136 px, the words with the name shrunk to its floor 1095, the
   marks alone 765, the marks without the brand's name 696. So the links keep their words from
   1106 px of strip content (the name shrinks first — it never goes), and below 776 the brand's name
   gives way too (the logo stays, named for a screen reader); ~8 px of room each for Firefox, which
   sets the same words a few pixels wider. One number each, true on every page and in every state of
   the rail. The word stays for a screen reader (clipped, not removed) and in the link's title. A
   phone switches the container off, so none of this fires there. */
@container navbar (max-width: 775px) {
  .nav-brand > span {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
  }
}
@container navbar (max-width: 1105px) {
  .navbar-nav .nav-link { padding-left: 0.5rem; padding-right: 0.5rem; }
  .navbar-nav .nav-link > .vk-mark-label { gap: 0; }
  .navbar-nav .nav-link > .vk-mark-label > .vk-mark { width: 20px; height: 20px; }
  .navbar-nav .nav-link > .vk-mark-label > span {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden;
    clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
  }
}
/* A page heading's subject: one size whether named or set beside its words (.page-title-row). */
.page-title .vk-mark { width: 1.25em; height: 1.25em; }
/* A settings card's header: its screen's mark in the place of the old 2rem line icon. */
.settings-card-header > .vk-mark { width: 2rem; height: 2rem; }
/* A ring's legend name that wears a mark: the ellipsis moves to the words. */
.dash-donut-name > .vk-mark-label { display: flex; }
.dash-donut-name > .vk-mark-label > span { overflow: hidden; text-overflow: ellipsis; }
/* A name's qualifier ("excl. tips", "(45 %)" — fragments/mark :: namedQualified) is quieter than the
   name, and never wraps inside itself. */
.vk-mark-qualifier { font-size: 0.8em; font-weight: 400; white-space: nowrap; }
/* A payroll table's head on a phone (decided with the user, 2026-10-03): one line does not fit, so the
   WHOLE head stacks — a bigger mark on the start side and the words beside it at its height: the name,
   and under it the smaller qualifier. Every head of the table takes the same mark, so it reads as one
   row; the figures' columns stay right-aligned, the first column left. */
@media (max-width: 768px) {
  .payroll-table th { vertical-align: middle; line-height: 1.2; }
  .payroll-table th .vk-mark-label { align-items: center; }
  .payroll-table th .vk-mark-label > .vk-mark { width: 2.2em; height: 2.2em; }
  .payroll-table th .vk-mark-label > span { text-align: start; }
  .payroll-table th .vk-mark-words > span { display: block; }
  .payroll-table th .vk-mark-qualifier { font-size: 0.85em; }
}

/* ── Motion: how a figure, a bar and a block ARRIVE (static/js/reveal.js) ───────────────────
   Opt-in by markup, one attribute: data-motion="reveal" (a fade with an 8 px rise when scrolled
   into view), "grow-x" / "grow-y" (a bar from nothing, by transform — the int size Java wrote
   is never touched), "grow-arc" (on a ring's <svg>: every arc from its start, pathLength="1000"),
   "count" (a whole number the script counts up); data-stagger[="ms"] on a container lets the
   members that appear together follow one another. The script adds .vk-pending when it ARMS an
   element and swaps it for .vk-in when the element comes into view; both are gone when the
   animation ends, so the element is again what the server sent. It arrives ONCE per page: a
   reader who scrolls away and back finds it standing. The one exception is an element the reader
   saw arrive that content inserted above it pushed wholly off the screen; it arrives once more
   when reached (reveal.js's reclaimDisplaced), and its before state comes back only while it is
   off the screen, so nothing is ever seen to un-arrive. Nothing here applies without
   the script: a browser without JavaScript, a crawler and ?render=full never see a pending
   state (RevealConventionTest holds both classes inside the media block below).

   Keyframes rather than transitions, on purpose: the podium card, the crew face, the vehicle
   card and .progress-fill carry a transition of their own, and a transition shorthand here
   would replace it for the duration. An animation sits above them and lets go when it ends.
   Only the `from` keyframe is written — the `to` is the element's own value, so a bar's inline
   width and an arc's dasharray are never restated.

   One media wrapper is both guards: on paper, and for a reader who asked for less motion, no
   rule below exists and an armed element is simply there — the house rule for reduced motion
   (jump to the final state) with nothing to jump. The script arms nothing under that
   preference either, and finishes everything on beforeprint. */
@media screen and (prefers-reduced-motion: no-preference) {
  .vk-pending[data-motion~="reveal"]   { opacity: 0; }
  .vk-pending[data-motion~="grow-x"]   { transform: scaleX(0); }
  .vk-pending[data-motion~="grow-y"]   { transform: scaleY(0); }
  /* A class rule outranks a presentation attribute, and [stroke-dasharray] takes the hatched
     overlay along with its solid twin, so a slice sweeps in one piece. */
  .vk-pending[data-motion~="grow-arc"] [stroke-dasharray] { stroke-dasharray: 0 1000; }

  .vk-in[data-motion~="reveal"] {
    animation: vk-reveal 0.5s cubic-bezier(0.2, 0.7, 0.2, 1) both;
    animation-delay: var(--vk-delay, 0ms);
  }
  .vk-in[data-motion~="grow-x"] {
    animation: vk-grow-x 0.7s cubic-bezier(0.2, 0.8, 0.2, 1) both;
    animation-delay: var(--vk-delay, 0ms);
  }
  .vk-in[data-motion~="grow-y"] {
    animation: vk-grow-y 0.7s cubic-bezier(0.2, 0.8, 0.2, 1) both;
    animation-delay: var(--vk-delay, 0ms);
  }
  .vk-in[data-motion~="grow-arc"] [stroke-dasharray] {
    animation: vk-grow-arc 0.8s cubic-bezier(0.2, 0.8, 0.2, 1) both;
    animation-delay: var(--vk-delay, 0ms);
  }

  @keyframes vk-reveal   { from { opacity: 0; transform: translateY(8px); } }
  @keyframes vk-grow-x   { from { transform: scaleX(0); } }
  @keyframes vk-grow-y   { from { transform: scaleY(0); } }
  @keyframes vk-grow-arc { from { stroke-dasharray: 0 1000; } }
}
/* Inert without a transform, so written for every screen alike. */
[data-motion~="grow-x"] { transform-origin: left center; }
[data-motion~="grow-y"] { transform-origin: center bottom; }
/* A COUNTER NEVER MOVES THE LAYOUT. Every counted figure has tabular digits, in its final
   state as much as while it counts (so nothing snaps on the last frame): with every digit the
   same advance, a value on the way — no more digits and no more separators than the final one —
   is never wider than where it lands. Proportional digits break exactly that ("598 000" is
   wider than "612 400"), and on the money card the currency beside the hero figure wrapped under
   it and back, shaking the chart and the rings below by a whole line. */
[data-motion~="count"] { font-variant-numeric: tabular-nums; }
/* While a figure counts, the script fixes its width at the final text's, so an inline span needs
   a box (on a flex item this computes to block, which it already was), and nowrap keeps it one
   line inside that fixed width whatever it says on the way. */
.vk-counting { display: inline-block; white-space: nowrap; }

/* ── The history with one person: one folding block per topic (V102) ─────────────────────── */

/* One topic of a correspondence: a block that folds, the cards inside it unchanged. The heading
   is quiet — uppercase grey over a rule, the milestone board's look — because on these screens
   gold and navy already mean which way a message went, and a third colour would be noise. */
.mail-topic { min-width: 0; }
.mail-topic-head {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.625rem;
  /* A comfortable target: on a phone this is the only way into a topic. */
  min-height: 2.5rem;
  padding: 0.625rem 0.875rem 0.625rem 1.75rem;
  border-bottom: 1px solid var(--gray-200);
  cursor: pointer;
  /* Both rules are needed — WebKit ignores list-style and needs its own pseudo-element
     switched off (the .filter-checklist-summary lesson). */
  list-style: none;
}
.mail-topic-head::-webkit-details-marker { display: none; }
.mail-topic-head::after {
  content: "";
  position: absolute;
  left: 0.625rem;
  top: 1.05rem;
  border: 0.3rem solid transparent;
  border-top-color: var(--gray-500);
}
.mail-topic[open] > .mail-topic-head::after {
  top: 0.75rem;
  border-top-color: transparent;
  border-bottom-color: var(--gray-500);
}
.mail-topic-head:hover { background: var(--gray-50, #f8fafc); }
.mail-topic-head:focus-visible { outline: 2px solid var(--navy-400); outline-offset: -2px; }
.mail-topic-subject { flex: 1 1 14rem; min-width: 0; font-weight: 600; color: var(--gray-900); }
.mail-topic-count,
.mail-topic-head .mail-date { color: var(--gray-500); font-size: 0.8125rem; }
.mail-topic-unread { flex-shrink: 0; }
/* .mail-conversation is a flex column whose gap separates the TOPICS; the cards inside one are
   ordinary blocks and space themselves. */
.mail-topic > .mail-card { margin-top: 0.5rem; }
.mail-topic > .mail-card:last-child { margin-bottom: 0.75rem; }

/* ── Tasks: the board as a JSON file (/tasks/import and its preview) ─────────────────────
   The reset strips a list's indent and marker; these lists are prose (the rules of the import,
   what a file would change), so they get both back. A change list is one task per row: the
   title, then small chips naming what changes. */
.task-transfer-list { list-style: disc; padding-left: 1.25rem; }
.task-transfer-list li + li { margin-top: 0.25rem; }
.task-transfer-summary { display: flex; flex-wrap: wrap; gap: 1rem 2rem; }
.task-transfer-summary .figure { font-size: 1.25rem; font-weight: 700; color: var(--gray-900); }
.task-transfer-section > summary { cursor: pointer; font-weight: 600; }
.task-transfer-section[open] > summary { margin-bottom: 0.5rem; }
.task-transfer-changes { list-style: none; display: flex; flex-direction: column; gap: 0.375rem; }
.task-transfer-changes li { display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.25rem 0.5rem; }
.task-transfer-changes .was { color: var(--gray-500); font-size: 0.8125rem; }
/* "becomes": the arrow glyph stands in the line of text, as tall as the words around it (centred when
   it is an item of the flex line itself). Only the arrow: a named measure in the same list keeps its size. */
.task-transfer-changes .vk-mark[data-mark="glyph:arrow-end"],
.plan-history-change .vk-mark[data-mark="glyph:arrow-end"] { width: 1em; height: 1em; vertical-align: -0.125em; align-self: center; }
.task-transfer-chip {
  display: inline-block;
  padding: 0 0.4rem;
  border: 1px solid var(--gray-300);
  border-radius: 999px;
  font-size: 0.75rem;
  color: var(--gray-600);
  white-space: nowrap;
}
.task-transfer-problems { list-style: none; display: flex; flex-direction: column; gap: 0.375rem; }
.task-transfer-problems .where { color: var(--gray-500); font-size: 0.8125rem; display: block; }

/* ---------------------------------------------------------------------------
   Route planning (planning/). A board row is tinted at its left edge by the worst of its
   checks — red for a check the write would refuse now, yellow for a missing resource — the
   same colours as the week's cells and the issue chips; never colour alone, the chips say
   which. The quick-add and the entry form share one grid; the edit form under a row is the
   row-preview's padding. The week's cells are links, one per entry of the day. */
.plan-row-error > td:first-child { box-shadow: inset 3px 0 0 var(--red-600); }
.plan-row-warning > td:first-child { box-shadow: inset 3px 0 0 var(--yellow-500); }
.plan-issue { margin: 0 0.25rem 0.25rem 0; }
.plan-time { white-space: nowrap; }
.plan-kpi-alert .dash-kpi-figure { color: var(--red-600); }
.plan-form-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
  gap: 0.75rem 1rem;
  align-items: end;
}
.plan-form-note { grid-column: 1 / -1; }
.plan-entry-preview { padding: 0.75rem 1rem 1rem; }
/* Two cards (2026-10-22): Vehicles — the free ones in rows of plates, then those in a workshop — and the
   wider Drivers — the free ones in columns, a size smaller, then the absent; each part counts itself. */
.plan-cards {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 3fr);
  gap: 1rem;
  align-items: start;
}
@media (max-width: 900px) {
  .plan-cards { grid-template-columns: minmax(0, 1fr); }
}
.plan-card-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.plan-count {
  display: inline-block;
  min-width: 1.5rem;
  padding: 0 0.4rem;
  border-radius: 999px;
  background: var(--gray-100);
  color: var(--gray-700);
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.25rem;
  text-align: center;
  font-variant-numeric: tabular-nums;
}
.plan-card-section { margin-top: 1rem; padding-top: 0.75rem; border-top: 1px solid var(--gray-200); }
.plan-card-subtitle {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin: 0 0 0.5rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--gray-800);
}
.plan-card-subtitle.is-empty { margin-bottom: 0; color: var(--gray-500); font-weight: 500; }
.plan-card-subtitle.is-empty .vk-mark { opacity: 0.6; }
.plan-chip-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.375rem; }
.plan-chip-list li { display: flex; flex-wrap: wrap; align-items: center; gap: 0.375rem; }
/* The free cars: plates side by side, a partly busy one with its hours beside it. */
.plan-chip-list--plates { flex-direction: row; flex-wrap: wrap; gap: 0.5rem 0.875rem; }
.plan-chip-list--plates li.plan-card-group { flex-basis: 100%; }
/* The free drivers: as many columns as fit, a size smaller (the longest list of the board). */
.plan-chip-list--people {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(12rem, 1fr));
  gap: 0.375rem 1rem;
  font-size: 0.8125rem;
}
.plan-chip-list--people li { flex-direction: column; align-items: flex-start; gap: 0.125rem; min-width: 0; }
.plan-chip-list--people li.plan-card-group { grid-column: 1 / -1; flex-direction: row; }
.plan-chip-list--people .avatar-sm { width: 24px; height: 24px; }
.plan-chip-list--people .plan-busy { padding-inline-start: calc(24px + 0.375rem); }
/* What is away — a car in the workshop, a driver sick or on leave — in the drivers' size. */
.plan-chip-list--away { font-size: 0.8125rem; }
/* A driver chip: the face and the name never part — the unit may shrink to the card's width
   (min-width 0 in the wrapping row), so a long name breaks between its words beside the photo. */
.plan-person { display: inline-flex; align-items: center; gap: 0.375rem; min-width: 0; max-width: 100%; }
.plan-person > span { min-width: 0; overflow-wrap: break-word; }
/* The cards' groups: the company said once, as a small heading with its dot (the .milestone-group look),
   never beside each chip — the dot and the name on ONE line, a long name wrapping beside its dot; a chip
   busy for part of the day is muted and says its hours. */
.plan-chip-list li.plan-card-group {
  flex-wrap: nowrap;
  align-items: flex-start;
  gap: 0.375rem;
  margin: 0.5rem 0 0.125rem;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--gray-500);
}
.plan-card-group > .area-dot { margin-top: 0.25em; }
.plan-card-group > span:last-child { min-width: 0; overflow-wrap: break-word; }
.plan-chip-list li.plan-card-group:first-child { margin-top: 0; }
.plan-chip-list li.is-busy { color: var(--gray-500); }
/* The attention tile is a link to the flagged rows alone. */
a.plan-kpi-link { color: inherit; text-decoration: none; }
a.plan-kpi-link:hover .dash-kpi-figure, a.plan-kpi-link:focus-visible .dash-kpi-figure { text-decoration: underline; }

/* The board's figures (2026-10-23, decided with the user): ONE card of cells parted by hairlines — the row's
   1px gaps over a grey ground, so the lines are right however the cells fall. Two columns (four from 32rem) with
   the attention tile across the bottom while the card is narrow, five in a row once it holds them; the label on top and the
   figure at the cell's foot, so the figures stand on one line however the labels wrap. */
.plan-kpis { padding: 0; overflow: hidden; container: plan-kpis / inline-size; }
.plan-kpis .dash-kpi-row {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: stretch;
  gap: 1px;
  background: var(--gray-200);
}
.plan-kpis .dash-kpi {
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.875rem 1rem;
  background: var(--white);
}
.plan-kpis .dash-kpi-label { font-size: 0.75rem; }
.plan-kpis .dash-kpi-figure { font-size: 1.5rem; font-variant-numeric: tabular-nums; }
.plan-kpis .plan-kpi-link { grid-column: 1 / -1; transition: background-color 0.15s; }
.plan-kpis a.plan-kpi-link:hover { background: var(--gray-50); }
.plan-kpis a.plan-kpi-link:focus-visible { outline: 2px solid var(--navy-500); outline-offset: -2px; }
.plan-kpis .plan-kpi-alert, .plan-kpis a.plan-kpi-alert:hover { background: var(--red-50); }
@container plan-kpis (min-width: 32rem) {
  .plan-kpis .dash-kpi-row { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}
@container plan-kpis (min-width: 44rem) {
  .plan-kpis .dash-kpi-row { grid-template-columns: repeat(5, minmax(0, 1fr)); }
  .plan-kpis .plan-kpi-link { grid-column: auto; }
  .plan-kpis .dash-kpi { padding: 1rem 1.25rem; }
  .plan-kpis .dash-kpi-label { font-size: 0.8125rem; }
  .plan-kpis .dash-kpi-figure { font-size: 1.75rem; }
}

/* The board's toolbar (2026-10-23, decided with the user): every control ONE height. The day is one joined
   control — previous · date · next — then Today; the search carries its Filter button, so the button never
   stands alone on a line; the selects wear their labels beside them and "only rows needing attention" is a
   pill. On a desk two rows: the day and the search, then the filters (#planFilters is display: contents there,
   so its parts are the grid's own); on a phone (≤ 640 px, below) a column, the filters folded. */
.plan-toolbar { display: flex; flex-direction: column; gap: 0.5rem; }
/* The day's tools under the figures follow the same rules: one height, the day to copy from joined to its
   button, its label beside it; the planner's tools on the left, the exports on the right. */
:is(.plan-toolbar, .plan-tools) { --plan-ctl-h: 2.5rem; }
:is(.plan-toolbar, .plan-tools) :is(.btn, .form-input), .plan-tools .export-language-select,
.plan-tools-toggle { height: var(--plan-ctl-h, 2.5rem); }
:is(.plan-toolbar, .plan-tools) .form-input { padding-block: 0; font-size: 0.875rem; }
.plan-daynav { display: flex; align-items: center; gap: 0.5rem; }
.plan-daystep { display: flex; align-items: stretch; }
.plan-daystep > .btn { flex: none; width: var(--plan-ctl-h); padding: 0; border-color: var(--gray-300); }
.plan-daystep > .btn:first-child { border-start-end-radius: 0; border-end-end-radius: 0; }
.plan-daystep > .btn:last-child { border-start-start-radius: 0; border-end-start-radius: 0; }
/* The date: a native field, or the wrapper js/datepicker.js puts in its place. */
.plan-daystep > input[type="date"], .plan-daystep > .vk-datepicker { flex: none; width: 9.5rem; margin-inline: -1px; }
.plan-daystep .form-input { width: 100%; border-radius: 0; }
.plan-daystep > :is(:focus-visible, :focus-within) { position: relative; z-index: 1; }
.plan-search { display: flex; min-width: 0; }
.plan-search .form-input { flex: 1; min-width: 0; border-start-end-radius: 0; border-end-end-radius: 0; }
.plan-search .btn { flex: none; margin-inline-start: -1px; border-start-start-radius: 0; border-end-start-radius: 0; }
.plan-search .form-input:focus { position: relative; z-index: 1; }
.plan-filter-set { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem 1.5rem; }
.plan-filter { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
.plan-filter .form-label, .plan-copy .form-label { margin: 0; font-size: 0.8125rem; color: var(--gray-600); white-space: nowrap; }
.plan-filter .form-input { width: auto; min-width: 8rem; max-width: 16rem; }
.plan-filter-problem {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  min-height: var(--plan-ctl-h);
  padding: 0 0.875rem;
  border: 1px solid var(--gray-300);
  border-radius: var(--radius-full);
  background: var(--white);
  font-size: 0.875rem;
  color: var(--gray-700);
  cursor: pointer;
  white-space: nowrap;
  transition: background-color 0.15s, border-color 0.15s;
}
.plan-filter-problem:hover { border-color: var(--gray-400); }
.plan-filter-problem:has(input:checked) { border-color: var(--navy-500); background: var(--navy-50); color: var(--navy-700); }
.plan-tools { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem 1.5rem; }
.plan-tools form { margin: 0; }
.plan-tools-write { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem 1rem; }
.plan-tools-export { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; }
.plan-tools-write + .plan-tools-export { margin-inline-start: auto; }
.plan-copy { display: flex; align-items: center; gap: 0.5rem; min-width: 0; }
.plan-copy-pick { display: flex; min-width: 0; }
.plan-copy-pick > input[type="date"], .plan-copy-pick > .vk-datepicker { flex: none; width: 9.5rem; }
.plan-copy-pick .form-input { width: 100%; border-start-end-radius: 0; border-end-end-radius: 0; }
.plan-copy-pick > .btn {
  flex: none;
  margin-inline-start: -1px;
  border-color: var(--gray-300);
  border-start-start-radius: 0;
  border-end-start-radius: 0;
}
.plan-copy-pick > :is(:focus-visible, :focus-within) { position: relative; z-index: 1; }
@media (min-width: 641px) {
  .plan-toolbar {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr);
    grid-template-areas: "day search" "filters filters";
    align-items: center;
    gap: 0.75rem 1.5rem;
  }
  .plan-daynav { grid-area: day; }
  .plan-filters { display: contents; }
  .plan-search { grid-area: search; justify-self: end; width: min(100%, 28rem); }
  .plan-filter-set { grid-area: filters; }
}
/* A route missing its customer or area: the warning state before its status badge. */
.plan-route-incomplete { color: #b45309; display: inline-flex; align-items: center; margin-inline-end: 0.375rem; }
.plan-route-incomplete .vk-mark { width: 16px; height: 16px; }
.plan-cal-edit { vertical-align: middle; margin-inline-start: 0.25rem; }
.plan-bulk-select { max-width: 14rem; }
.plan-import-kept { font-style: italic; }
.plan-history summary { cursor: pointer; }
.plan-history-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.375rem; font-size: 0.875rem; }
.plan-history-change { display: inline-block; margin-left: 0.5rem; }
.plan-weekdays { white-space: nowrap; }
.plan-weekday { display: inline-block; min-width: 1.75rem; font-size: 0.75rem; text-align: center; }
.plan-weekday.is-on { font-weight: 600; }
.plan-weekday.is-off { color: var(--gray-400); text-decoration: line-through; }
/* The day board's route code stays whole on its line, the customer under it. */
.plan-table .plan-cell-route > .font-semibold { white-space: nowrap; }
/* The route register (planning/routes/list.html) fits its column instead of scrolling sideways: its
   cells a notch tighter (the .invoice-table sides), the checkbox tight against the route, and what
   would hold the table wide giving way only when it is squeezed — the week's days onto a second line
   (never more: four days are the strip's minimum, and a day never splits) and the hours after their
   dash. A phone reads every route as a card (the day board's, below). */
.plan-routes-table th,
.plan-routes-table td { padding-left: 0.5rem; padding-right: 0.5rem; }
.plan-routes-table th.row-col-check,
.plan-routes-table td.row-cell-check { width: auto; padding-left: 0.75rem; padding-right: 0; }
.plan-routes-table .plan-weekdays { display: inline-flex; flex-wrap: wrap; min-width: 7rem; max-width: 100%; white-space: normal; }
.plan-routes-table .plan-time { white-space: normal; }
/* A route both incomplete and archived: the warning stands over its badge rather than widening the
   column; the badge itself never breaks. */
.plan-routes-table td.row-cell-when { white-space: normal; }
.plan-routes-table .row-when { display: inline-flex; flex-wrap: wrap; justify-content: flex-end; align-items: center; row-gap: 0.25rem; }
.plan-routes-table .row-when .badge { white-space: nowrap; }
/* A long company name wraps inside its chip; the corners are the one-line pill's, so a chip that fits
   looks as everywhere else. */
.plan-routes-table .plan-company { white-space: normal; border-radius: 0.6875rem; }
/* What gives way does so only where the table is narrow (measured in Inter, Polish with several
   companies, the widest case): from 62.5rem the company stays on one line, from 70rem the week too. */
.plan-routes-wrapper { container: plan-routes / inline-size; }
@container plan-routes (min-width: 62.5rem) {
  .plan-routes-table .plan-company { white-space: nowrap; }
}
@container plan-routes (min-width: 70rem) {
  .plan-routes-table .plan-weekdays { min-width: 12.25rem; }
}
/* A count's name on a card; the desk has the column's head. */
.plan-card-label { display: none; }
/* ---------------------------------------------------------------------------
   The plan's calendar (templates/planning/calendar*.html, mine.html; 2026-10-21).
   The OVERVIEW is a week table, a row per route / driver / vehicle: its first column stays in view
   while the week scrolls sideways (the wrapper is the scroll container). A ONE-SUBJECT calendar is a
   list of days laid out as weeks; on a phone the same list is an agenda — only the days that say
   something, and today. Never colour alone: every state wears its mark and its words. */
.plan-cal-toolbar { row-gap: 0.75rem; }
.plan-cal-pick { min-width: 12rem; max-width: 18rem; }
.plan-cal-period { font-variant-numeric: tabular-nums; }
.plan-cal-wrapper { margin-bottom: 0.75rem; }
.plan-cal { min-width: 61rem; table-layout: fixed; }
.plan-cal th, .plan-cal td { vertical-align: top; }
.plan-cal thead th:first-child, .plan-cal th[scope="row"] {
  position: sticky;
  inset-inline-start: 0;
  z-index: 2;
  width: 11rem;
  background: var(--white);
  box-shadow: inset -1px 0 0 var(--gray-200);
}
.plan-cal-corner { vertical-align: bottom; }
.plan-cal-dayhead { text-align: start; }
.plan-cal-dayhead.is-today { box-shadow: inset 0 3px 0 var(--gold-400); }
.plan-week-day { color: inherit; text-decoration: none; display: block; }
.plan-week-day:hover span:first-child, .plan-week-day:focus-visible span:first-child { text-decoration: underline; }
.plan-cal td.plan-cal-day { position: relative; min-height: 4rem; padding: 0.3rem; }
.plan-cal .is-weekend { background: var(--gray-50); }
.plan-cal .is-today { background: var(--gold-50); }
.plan-cal-group th {
  background: var(--gray-50);
  padding-top: 0.5rem;
  padding-bottom: 0.375rem;
  font-weight: 600;
}
.plan-cal th[scope="row"] {
  text-transform: none;
  letter-spacing: normal;
  font-size: 0.875rem;
  color: var(--gray-800);
}
.plan-cal-head { font-weight: 600; }
.plan-cal-subject-link {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  min-width: 0;
  color: inherit;
  text-decoration: none;
  vertical-align: middle;
}
.plan-cal-subject-link:hover .plan-cal-label, .plan-cal-subject-link:focus-visible .plan-cal-label { text-decoration: underline; }
.plan-cal-label { overflow-wrap: break-word; }
.plan-cal-detail { display: block; font-weight: 400; font-size: 0.75rem; color: var(--gray-500); }
.plan-cal-sums {
  display: block;
  margin-top: 0.125rem;
  font-weight: 400;
  font-size: 0.75rem;
  color: var(--gray-500);
  font-variant-numeric: tabular-nums;
}
.plan-cal-untimed { display: block; font-size: 0.75rem; color: var(--gray-500); }
.plan-cal-extra th[scope="row"] { font-weight: 400; color: var(--gray-600); font-style: italic; }

/* One entry: a card with a coloured edge — red when it cannot run, amber when it misses a car or a
   driver — the state's mark at its top end, its words in the title and for a screen reader. */
.plan-cal-entry {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.125rem;
  padding: 0.25rem 0.3rem;
  margin-bottom: 0.25rem;
  border-radius: 0.375rem;
  border-inline-start: 3px solid var(--navy-300);
  background: var(--white);
  box-shadow: 0 0 0 1px var(--gray-200);
  color: inherit;
  text-decoration: none;
  font-size: 0.8125rem;
  line-height: 1.3;
}
a.plan-cal-entry:hover, a.plan-cal-entry:focus-visible { background: var(--navy-50); }
.plan-cal-entry.plan-cell-error { border-inline-start-color: var(--red-600); background: rgba(220, 38, 38, 0.06); }
.plan-cal-entry.plan-cell-warning { border-inline-start-color: var(--yellow-500); background: rgba(234, 179, 8, 0.08); }
.plan-cal-entry > .vk-mark { position: absolute; top: 0.3rem; inset-inline-end: 0.3rem; width: 14px; height: 14px; }
.plan-cal-entry.plan-cell-error > .vk-mark { color: var(--red-600); }
.plan-cal-entry.plan-cell-warning > .vk-mark { color: #b45309; }
/* Room for the state's mark on the first line only — the lines under it keep the card's width. */
:is(.plan-cal-entry.plan-cell-error, .plan-cal-entry.plan-cell-warning) > .plan-cal-entry-main { padding-inline-end: 1.125rem; }
.plan-cal-entry-main { font-weight: 600; overflow-wrap: break-word; min-width: 0; }
.plan-cal-entry-line { overflow-wrap: break-word; min-width: 0; }
.plan-cal-entry-note { color: var(--gray-600); font-style: italic; }

/* Away: sick, on leave, in the workshop — a chip over the day, the day itself faintly hatched. */
.plan-cal-away {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.25rem;
  padding: 0.125rem 0.375rem;
  margin-bottom: 0.25rem;
  border-radius: 0.375rem;
  font-size: 0.75rem;
  font-weight: 600;
}
.plan-cal-away .vk-mark { width: 14px; height: 14px; }
.plan-cal-away--sick { background: var(--red-50); color: var(--red-600); }
.plan-cal-away--leave { background: var(--navy-50); color: var(--navy-700); }
.plan-cal-away--workshop { background: var(--gray-100); color: var(--gray-700); }
.plan-cal-away-detail { font-weight: 400; }
.plan-cal td.is-away, .plan-day.is-away {
  background-image: repeating-linear-gradient(135deg, transparent 0 6px, rgba(100, 116, 139, 0.07) 6px 8px);
}

/* A route's running day without an entry: a dashed gold chip — a link to plan it, for a planner. */
.plan-cal-unplanned {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.125rem 0.375rem;
  border: 1px dashed var(--gold-400);
  border-radius: 0.375rem;
  color: var(--gold-700);
  font-size: 0.75rem;
  font-weight: 600;
  text-decoration: none;
  background: var(--white);
}
.plan-cal-unplanned .vk-mark { width: 14px; height: 14px; }
a.plan-cal-unplanned:hover, a.plan-cal-unplanned:focus-visible { background: var(--gold-50); }

/* The "+" over an empty day: invisible at rest (opacity — a keyboard still reaches it), shown under the
   pointer and on focus; a touch screen always shows it, faintly. */
.plan-cal-add {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  opacity: 0;
  color: var(--navy-500);
  border-radius: 0.375rem;
  transition: opacity 0.12s ease;
}
.plan-cal-add .vk-mark { width: 20px; height: 20px; }
td.plan-cal-day:hover > .plan-cal-add, .plan-day:hover .plan-cal-add, .plan-cal-add:focus-visible {
  opacity: 1;
  background: var(--navy-50);
  box-shadow: inset 0 0 0 1px var(--navy-200);
}
@media (hover: none) {
  .plan-cal-add { opacity: 0.45; }
}
@media (prefers-reduced-motion: reduce) {
  .plan-cal-add { transition: none; }
}

.plan-cal-legend { display: flex; flex-wrap: wrap; gap: 0.5rem 1.25rem; }
.plan-cal-legend-unplanned { color: var(--gold-700); }

/* One subject: who or what, then what the period adds up to. */
.plan-cal-back {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  margin-bottom: 0.75rem;
  font-size: 0.875rem;
  color: var(--navy-600);
  text-decoration: none;
}
.plan-cal-back:hover, .plan-cal-back:focus-visible { text-decoration: underline; }
.plan-cal-back .vk-mark { width: 14px; height: 14px; }
.plan-cal-subject-head { display: flex; align-items: center; gap: 0.875rem; margin-bottom: 1rem; }
.plan-cal-subject-head > .avatar { width: 3.5rem; height: 3.5rem; }
.plan-cal-subject-id { display: flex; flex-direction: column; align-items: flex-start; gap: 0.25rem; min-width: 0; flex: 1; }
.plan-cal-subject-name { margin: 0; font-size: 1.25rem; font-weight: 700; overflow-wrap: break-word; }
.plan-cal-subject-line { display: flex; flex-wrap: wrap; align-items: center; gap: 0.375rem; font-size: 0.875rem; color: var(--gray-600); }
.plan-cal-defaults { gap: 0.5rem; }
.plan-cal-kpis .dash-kpi-figure { font-variant-numeric: tabular-nums; }

/* The month: seven columns of days, the weekday names over them. */
.plan-month-head, .plan-month { display: grid; grid-template-columns: repeat(7, minmax(0, 1fr)); }
.plan-month-head {
  gap: 1px;
  margin-bottom: 0.25rem;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--gray-500);
  text-transform: capitalize;
}
.plan-month-head > span { padding: 0 0.375rem; }
.plan-month-head > .is-weekend { color: var(--gray-400); }
.plan-month {
  gap: 1px;
  margin: 0 0 0.75rem;
  padding: 0;
  list-style: none;
  background: var(--gray-200);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius);
  overflow: hidden;
}
.plan-day {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
  min-height: 7.5rem;
  padding: 0.375rem;
  background: var(--white);
  scroll-margin-top: 5rem;
}
.plan-day.is-weekend { background: var(--gray-50); }
.plan-day.is-today { background: var(--gold-50); }
.plan-day.is-outside { background: var(--gray-50); }
.plan-day.is-outside > * { opacity: 0.5; }
.plan-day:target { box-shadow: inset 0 0 0 2px var(--gold-400); }
.plan-month--week .plan-day { min-height: 14rem; }
.plan-day-head { display: block; }
.plan-day-num {
  position: relative;
  z-index: 1;
  display: inline-grid;
  place-items: center;
  min-width: 1.75rem;
  height: 1.75rem;
  padding: 0 0.25rem;
  border-radius: 999px;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--gray-700);
  text-decoration: none;
  font-variant-numeric: tabular-nums;
}
a.plan-day-num:hover, a.plan-day-num:focus-visible { background: var(--navy-50); }
.plan-day.is-today .plan-day-num { background: var(--navy-700); color: var(--white); }
/* The whole date for a screen reader; on a phone it is the agenda's heading. */
.plan-day-long {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0;
}
.plan-day-body { display: flex; flex-direction: column; gap: 0.125rem; min-width: 0; }
.plan-day-body .plan-cal-entry { margin-bottom: 0; }
.plan-day-nothing { display: none; font-size: 0.8125rem; color: var(--gray-500); }

/* The board's quick-add opened from the calendar: a gold ring says where the work continues. */
.plan-quick-add--prefilled { box-shadow: 0 0 0 2px var(--gold-300); }

/* ---------------------------------------------------------------------------
   Phone folds (static/js/phone-fold.js; 2026-10-22): a panel a desk always shows and a phone folds behind
   its button. Without the script nothing is folded and the buttons stay hidden. */
.phone-fold-toggle { display: none !important; }
.phone-fold-caret { display: inline-flex; transition: transform 0.15s ease; }
.phone-fold-toggle.is-open .phone-fold-caret { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) {
  .phone-fold-caret { transition: none; }
}
@media (max-width: 640px) {
  .phone-folds .phone-fold-toggle { display: inline-flex !important; align-items: center; gap: 0.375rem; }
  .phone-folds [data-phone-fold]:not(.is-open) { display: none !important; }
}

/* The day board on a phone (≤ 640 px, 2026-10-22, decided with the user): the day's navigation in one
   row, the filters, the day's tools and the quick-add folded (phone folds), the figures two columns,
   and every entry a CARD — route and time, the area, the car, the driver, the checks, then the status
   beside the row's actions, both always visible; an opened card's edit form under it. */
@media (max-width: 640px) {
  .phone-folds .plan-toolbar > .phone-fold-toggle { align-self: flex-start; }
  .plan-daystep { flex: 1; min-width: 0; }
  .plan-daystep > input[type="date"], .plan-daystep > .vk-datepicker { flex: 1; width: auto; min-width: 0; }
  /* "Today" is its mark alone (the word stays for a screen reader), or Firefox's date field is cut. */
  .plan-today .vk-mark-label > span {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .plan-filters { display: flex; flex-direction: column; gap: 0.75rem; }
  .plan-filter-set { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 0.75rem; }
  .plan-filter { flex-direction: column; align-items: stretch; gap: 0.25rem; }
  .plan-filter .form-input { width: 100%; min-width: 0; max-width: none; }
  .plan-filter--company, .plan-filter-problem { grid-column: 1 / -1; }
  .plan-filter-problem { white-space: normal; }
  .plan-tools, .plan-tools-write { flex-direction: column; align-items: stretch; gap: 0.75rem; }
  .plan-tools-write > form:first-child > .btn { width: 100%; }
  .plan-copy { flex-direction: column; align-items: stretch; gap: 0.25rem; }
  .plan-copy-pick > input[type="date"], .plan-copy-pick > .vk-datepicker { flex: 1; width: auto; min-width: 0; }
  .plan-tools-write + .plan-tools-export { margin-inline-start: 0; }
  .plan-tools-export .btn { flex: 1; }
  .plan-add-toggle { width: 100%; justify-content: center; }

  .plan-table-wrapper { overflow: visible; border: 0; border-radius: 0; box-shadow: none; background: none; }
  /* The card, shared by the day board's entries and the route register's routes; each table says
     only which cell goes where. */
  .plan-table, .plan-table tbody,
  .plan-routes-table, .plan-routes-table tbody { display: block; }
  .plan-table thead, .plan-routes-table thead { display: none; }
  .plan-table tbody tr.plan-entry-row,
  .plan-routes-table tbody tr[data-row-item] {
    display: grid;
    grid-template-columns: auto minmax(0, 1fr) auto;
    column-gap: 0.625rem;
    padding: 0.75rem;
    margin-bottom: 0.5rem;
    border: 1px solid var(--gray-200);
    border-radius: var(--radius);
    background: var(--white);
    box-shadow: var(--shadow);
  }
  .plan-table tbody tr.plan-entry-row {
    grid-template-areas:
      "check route time"
      "check company company"
      "check area area"
      "check vehicle vehicle"
      "check driver driver"
      "check checks checks"
      "check when when";
  }
  .plan-routes-table tbody tr[data-row-item] {
    grid-template-areas:
      "check route time"
      "check company company"
      "check area area"
      "check days days"
      "check vehicle vehicle"
      "check driver driver"
      "check entries entries"
      "check when when";
  }
  .plan-table tbody tr.plan-entry-row.is-selected,
  .plan-routes-table tbody tr[data-row-item].is-selected { background-color: color-mix(in srgb, var(--navy-50) 80%, white); }
  .plan-table tbody tr.plan-entry-row.is-expanded,
  .plan-routes-table tbody tr[data-row-item].is-expanded { border-color: var(--gold-300); background-color: var(--gold-50); }
  .plan-table tbody tr.plan-row-error { border-inline-start: 3px solid var(--red-600); }
  .plan-table tbody tr.plan-row-warning { border-inline-start: 3px solid var(--yellow-500); }
  .plan-table tbody tr.plan-entry-row > td,
  .plan-routes-table tbody tr[data-row-item] > td {
    display: block;
    width: auto;
    min-width: 0;
    padding: 0;
    border: 0;
    background: transparent;
    box-shadow: none;
  }
  /* The card's own edge says opened, refused or warned — never the first cell's inset bar. */
  .plan-table tbody tr.plan-entry-row > td:first-child,
  .plan-routes-table tbody tr[data-row-item] > td:first-child { box-shadow: none; }
  .plan-table tbody tr.plan-entry-row > td.row-cell-check,
  .plan-routes-table tbody tr[data-row-item] > td.row-cell-check {
    grid-area: check;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 0.375rem;
  }
  .plan-table .plan-cell-route, .plan-routes-table .plan-cell-route { grid-area: route; }
  .plan-table .plan-cell-time, .plan-routes-table .plan-cell-time { grid-area: time; text-align: end; font-weight: 600; }
  .plan-table .plan-cell-company, .plan-routes-table .plan-cell-company { grid-area: company; margin-top: 0.25rem; }
  .plan-table .plan-cell-area, .plan-routes-table .plan-cell-area { grid-area: area; font-size: 0.75rem; color: var(--gray-500); }
  .plan-table .plan-cell-vehicle, .plan-routes-table .plan-cell-vehicle { grid-area: vehicle; margin-top: 0.5rem; }
  .plan-table .plan-cell-driver, .plan-routes-table .plan-cell-driver { grid-area: driver; margin-top: 0.375rem; }
  .plan-table .plan-cell-checks { grid-area: checks; margin-top: 0.375rem; }
  .plan-routes-table .plan-cell-days { grid-area: days; margin-top: 0.375rem; }
  .plan-routes-table .plan-cell-entries { grid-area: entries; margin-top: 0.375rem; font-size: 0.8125rem; color: var(--gray-600); }
  .plan-routes-table .plan-card-label { display: inline-flex; margin-inline-end: 0.375rem; }
  .plan-table .plan-none, .plan-routes-table .plan-none { display: none; }
  .plan-table tbody tr.plan-entry-row > td.row-cell-when,
  .plan-routes-table tbody tr[data-row-item] > td.row-cell-when {
    grid-area: when;
    position: static;
    margin-top: 0.5rem;
    padding-top: 0.5rem;
    border-top: 1px solid var(--gray-100);
    text-align: start;
    background: transparent;
    box-shadow: none;
  }
  .plan-table .row-when-stack,
  .plan-routes-table .row-when-stack { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; width: 100%; }
  /* A long status beside a route's actions goes under them rather than past the card's edge. */
  .plan-routes-table .row-when-stack { flex-wrap: wrap; }
  .plan-table .row-when-stack > *, .plan-routes-table .row-when-stack > * { grid-area: auto; }
  /* The status always; the actions beside it only on an opened card (2026-10-23), and a closed card
     keeps no empty room for them. */
  .plan-table .row-when, .plan-routes-table .row-when { opacity: 1 !important; pointer-events: auto !important; }
  .plan-table tbody tr:not(.is-expanded) .row-actions,
  .plan-routes-table tbody tr:not(.is-expanded) .row-actions { display: none; }
  .plan-table .row-actions, .plan-routes-table .row-actions { gap: 0.25rem; }
  .plan-table .row-actions .btn-icon-ghost,
  .plan-routes-table .row-actions .btn-icon-ghost { min-width: 2.5rem; min-height: 2.5rem; }
  .plan-table tbody tr.row-preview-row,
  .plan-routes-table tbody tr.row-preview-row { display: block; margin: -0.25rem 0 0.75rem; }
  .plan-table tbody tr.row-preview-row > td,
  .plan-routes-table tbody tr.row-preview-row > td { display: block; padding: 0; border: 0; }
  .plan-table tbody tr.row-preview-row .row-preview,
  .plan-routes-table tbody tr.row-preview-row .row-preview { position: static; width: auto; }
}

/* A phone: the month is an agenda — the days with something to say, and today — each headed by its
   whole date; the "+" becomes a plain row under the day. */
@media (max-width: 640px) {
  .plan-month-head { display: none; }
  .plan-month {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    background: none;
    border: 0;
    border-radius: 0;
    overflow: visible;
  }
  .plan-day {
    min-height: 0;
    border: 1px solid var(--gray-200);
    border-radius: var(--radius);
  }
  .plan-month--week .plan-day { min-height: 0; }
  .plan-day.is-outside, .plan-day.is-empty:not(.is-today) { display: none; }
  .plan-day-num {
    display: inline-flex;
    height: auto;
    padding: 0.125rem 0.375rem;
    border-radius: 0.375rem;
  }
  .plan-day-num > [aria-hidden="true"] { display: none; }
  .plan-day-long {
    position: static;
    width: auto;
    height: auto;
    margin: 0;
    overflow: visible;
    clip: auto;
    white-space: normal;
    text-transform: capitalize;
  }
  .plan-day-nothing { display: block; }
  .plan-day .plan-cal-add {
    position: static;
    opacity: 1;
    min-height: 2.5rem;
    background: var(--navy-50);
  }
  .plan-cal-pick { min-width: 0; max-width: 100%; }
  .plan-cal { min-width: 57rem; }
  .plan-cal thead th:first-child, .plan-cal th[scope="row"] { width: 8.5rem; padding-inline: 0.5rem; }
  .plan-cal-detail { display: none; }
}
.plan-import-text { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.8125rem; white-space: pre; }
.plan-import-warnings { margin: 0; padding-left: 1rem; font-size: 0.75rem; color: var(--gray-600); min-width: 12rem; }

/* ---------------------------------------------------------------------------
   Invoice list: the amount column and the Status column
   (templates/invoice/status-fragments.html). Amounts line up on the right with
   tabular digits; a status is a row of small icons whose meaning is in their
   title and aria-label, coloured only as a second signal.
   --------------------------------------------------------------------------- */
.cell-amount { text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums; }
/* The invoice list's columns have FIXED widths (the <colgroup> in invoice/list.html): a column
   is as wide on every row and under every filter, and nothing a row holds can widen the table.
   The description takes what is left and ends in "…" (its whole text in the title). Below the
   sum of the columns the TABLE keeps its width and its wrapper scrolls — never the page. The
   cells are a notch tighter than .table's (0.5rem sides, 0.8125rem text). */
.invoice-table { table-layout: fixed; min-width: 50rem; }
.invoice-table th,
.invoice-table td { padding-left: 0.5rem; padding-right: 0.5rem; }
.invoice-table .invoice-col-check { width: 3.5rem; }
.invoice-table .invoice-col-platform { width: 5.25rem; }
.invoice-table .invoice-col-amount { width: 8.5rem; }
.invoice-table .invoice-col-status { width: 12.25rem; }
.invoice-table .invoice-col-date { width: 9.25rem; }
/* The checkbox and the chevron sit tight against the logo beside them. */
.invoice-table th.row-col-check,
.invoice-table td.row-cell-check { width: auto; padding-left: 0.75rem; padding-right: 0; }
.invoice-table td.row-cell-check .row-expand { margin-left: 0.125rem; }
.invoice-table .col-platform { text-align: center; padding-left: 0.5rem; }
/* The Platform head is its mark alone (decided with the user, S13b): a column of logos 5.25rem wide.
   The words stay for a screen reader (the sr-only shape) and in the sort link's title. */
.invoice-table th.col-platform .vk-mark-label > span {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}
/* One slot for every logo: at most 13 px tall and 3.25rem wide. A wordmark wider than
   that (Tesla 7.7:1, Mapon 5.5:1) is scaled down whole — never cropped — by contain,
   and sits in the middle of the cell like the narrow ones. */
.invoice-table .col-platform .platform-logo { height: 13px; max-width: 3.25rem; object-fit: contain; object-position: center; }
.invoice-table .col-platform .sender-initials { min-width: 1.125rem; height: 1.125rem; font-size: 0.625rem; }
/* The category mark is square: at the wordmarks' 13 px it read as a speck beside them. */
.invoice-table .col-platform .category-mark { height: 16px; }
.invoice-table td.invoice-cell-description {
  font-size: 0.8125rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.invoice-table td.cell-amount,
.invoice-table td.row-cell-when { font-size: 0.8125rem; }
.invoice-table td.row-cell-when { width: auto; }
.invoice-table .status-icon { width: 1.5rem; height: 1.5rem; }
.invoice-status { display: flex; align-items: center; gap: 0.125rem; white-space: nowrap; }
.invoice-status .plate { margin-left: 0.375rem; }
.status-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.75rem; height: 1.75rem; border-radius: 0.375rem;
  color: var(--gray-500); text-decoration: none;
}
a.status-icon:hover { background-color: rgba(15, 23, 42, 0.08); }
a.status-icon:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 1px; }
.status-icon--ok { color: #16a34a; }
.status-icon--muted { color: var(--gray-400); }
.status-icon--warn { color: #b45309; cursor: help; }
/* Cost or income (state:cost / state:income): the direction badge's orange and teal, at the area
   palette's strength so the mark holds 3:1 on white beside the green and amber states. */
.status-icon--cost { color: #ea580c; }
.status-icon--income { color: #0d9488; }
/* What the invoice is a cost of (V110): the category's ONE-TONE mark (config/CategoryMark, S16) in
   the category's own colour (cat-pie-<key>) — the ring's, the filter's, the row tint's; the title
   names it. */
.status-icon--category { color: var(--pie, var(--navy-400)); }
/* "No category yet" — the rings' rest grey (slate-300) vanishes as a 16 px mark on a tinted row. */
.status-icon--category.cat-pie-none { color: var(--gray-500); }
/* The list's row wears a pale tint of its category (data-category on the <tr>; none without
   one). A hint for the eye only — the Status column's icon, with the category in its title,
   says it in words. A ticked or opened row keeps its navy look, hence the :not()s; the :hover
   twin is a shade darker so the row still answers the pointer (the mail-row-* idiom). */
.invoice-table tbody tr[data-category="fuel"]           { --cat-tint: var(--cat-fuel); }
.invoice-table tbody tr[data-category="charging"]       { --cat-tint: var(--cat-charging); }
.invoice-table tbody tr[data-category="parking"]        { --cat-tint: var(--cat-parking); }
.invoice-table tbody tr[data-category="tolls"]          { --cat-tint: var(--cat-tolls); }
.invoice-table tbody tr[data-category="service"]        { --cat-tint: var(--cat-service); }
.invoice-table tbody tr[data-category="insurance"]      { --cat-tint: var(--cat-insurance); }
.invoice-table tbody tr[data-category="financing"]      { --cat-tint: var(--cat-financing); }
.invoice-table tbody tr[data-category="subscription"]   { --cat-tint: var(--cat-subscription); }
.invoice-table tbody tr[data-category="infrastructure"] { --cat-tint: var(--cat-infrastructure); }
.invoice-table tbody tr[data-category="accounting"]     { --cat-tint: var(--cat-accounting); }
.invoice-table tbody tr[data-category="taxes_fees"]     { --cat-tint: var(--cat-taxes_fees); }
.invoice-table tbody tr[data-category="other"]          { --cat-tint: var(--cat-other); }
.invoice-table tbody tr[data-category]:not(.is-selected):not(.is-expanded) {
  background-color: color-mix(in srgb, var(--cat-tint) 7%, white);
}
.invoice-table tbody tr[data-category]:not(.is-selected):not(.is-expanded):hover {
  background-color: color-mix(in srgb, var(--cat-tint) 13%, white);
}
/* Sortable column headers (invoice/list-sort.html): the header is a link that keeps the
   header's own look; the active column shows its arrow, the others a faint double arrow that
   darkens on hover and focus. */
.sortable-th .sort-link {
  display: inline-flex; align-items: center; gap: 0.25rem;
  color: inherit; text-decoration: none; white-space: nowrap;
  min-height: 1.75rem; border-radius: 0.25rem;
}
.sortable-th.cell-amount .sort-link { justify-content: flex-end; }
.sortable-th .sort-link:hover { color: var(--navy-700, #223b60); }
.sortable-th .sort-link:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 2px; }
.sortable-th .sort-icon { flex: none; display: inline-flex; }
.sortable-th .sort-icon > .vk-mark { width: 12px; height: 12px; }
.sortable-th .sort-icon--idle { opacity: 0.35; }
.sortable-th .sort-link:hover .sort-icon--idle,
.sortable-th .sort-link:focus-visible .sort-icon--idle { opacity: 0.8; }
.sortable-th.is-sorted { color: var(--navy-700, #223b60); }
/* A row a bank statement pays (data-paid) wears the same hue at a stronger strength, so the
   settled rows read apart from the open ones at a glance; one without a category takes the
   "other" grey rather than staying white. The bank-note icon still says it in words. */
.invoice-table tbody tr[data-paid="true"]:not([data-category]) { --cat-tint: var(--cat-other); }
.invoice-table tbody tr[data-paid="true"]:not(.is-selected):not(.is-expanded) {
  background-color: color-mix(in srgb, var(--cat-tint) 18%, white);
}
.invoice-table tbody tr[data-paid="true"]:not(.is-selected):not(.is-expanded):hover {
  background-color: color-mix(in srgb, var(--cat-tint) 24%, white);
}

/* ---- The list's category rings (invoice/category-charts.html) ----
   The dashboard's ring (fragments/charts :: donutSvg), smaller, its legend beside it. A slice's
   colour is its category's token at full strength — the very colour the rows above are a pale
   tint of — and the statement's money without an invoice is the dashboard's hatched grey. */
.cat-pie-fuel           { --pie: var(--cat-fuel); }
.cat-pie-charging       { --pie: var(--cat-charging); }
.cat-pie-parking        { --pie: var(--cat-parking); }
.cat-pie-tolls          { --pie: var(--cat-tolls); }
.cat-pie-service        { --pie: var(--cat-service); }
.cat-pie-insurance      { --pie: var(--cat-insurance); }
.cat-pie-financing      { --pie: var(--cat-financing); }
.cat-pie-subscription   { --pie: var(--cat-subscription); }
.cat-pie-infrastructure { --pie: var(--cat-infrastructure); }
.cat-pie-accounting     { --pie: var(--cat-accounting); }
.cat-pie-taxes_fees     { --pie: var(--cat-taxes_fees); }
.cat-pie-other          { --pie: var(--cat-other); }
.cat-pie-none           { --pie: var(--pie-rest); }
.cat-pie-bank           { --pie: var(--gray-500); }
.invoice-charts { padding: 0; margin-bottom: 1rem; }
/* Closed, the card is its summary: one line per ring — costs, then income under it — each a
   thin bar beside its total, and the toggle to their right. The whole summary is the button. */
.invoice-charts-strip {
  display: flex;
  align-items: center;
  gap: 0.5rem 1.5rem;
  padding: 0.625rem 1rem;
  cursor: pointer;
  list-style: none;
  border-radius: inherit;
}
.invoice-charts-strip::-webkit-details-marker { display: none; }
.invoice-charts-strip:hover { background: var(--gray-50); }
.invoice-charts-strip:focus-visible { outline: 2px solid var(--navy-400); outline-offset: -2px; }
/* One grid for both lines (a line is display: contents), so label, bar and total are columns
   and the two bars start and end at the same x whatever the labels' widths. */
.invoice-strip-rows {
  display: grid;
  grid-template-columns: auto minmax(4rem, 1fr) auto;
  align-items: center;
  gap: 0.375rem 0.625rem;
  flex: 1 1 auto;
  min-width: 0;
}
.invoice-strip-row { display: contents; }
.invoice-strip-label { font-size: 0.75rem; font-weight: 600; color: var(--gray-600); white-space: nowrap; }
.invoice-strip-bar {
  display: flex;
  gap: 2px;
  min-width: 0;
  height: 0.5rem;
  align-items: center;
}
/* A segment answers the pointer (js/donut-link.js marks the group): it thickens and the
   others fade, and the ring and its legend line follow when the card is open. The bar has no
   overflow: hidden — it would clip the grow and the tip — so its ends are rounded here. */
.invoice-strip-seg {
  position: relative;
  flex-basis: 0;
  min-width: 2px;
  height: 100%;
  background-color: var(--pie);
}
.invoice-strip-seg:first-child { border-radius: var(--radius-full) 0 0 var(--radius-full); }
.invoice-strip-seg:last-child { border-radius: 0 var(--radius-full) var(--radius-full) 0; }
.invoice-strip-seg:only-child { border-radius: var(--radius-full); }
/* A thin segment is hard to hit: the hit area reaches a little above and below it. */
.invoice-strip-seg::before { content: ""; position: absolute; inset: -0.3rem 0; }
.invoice-strip-bar.is-linked .invoice-strip-seg { opacity: 0.35; }
/* Grown by height, not a transform, so the tip inside is not stretched; the bar's own height
   stays, and a taller segment only overflows it. */
.invoice-strip-bar.is-linked .invoice-strip-seg.is-active { opacity: 1; height: 0.875rem; }
/* Pointed at directly, a segment shows its tip above it: the category's icon in its colour,
   the name, the amount and the share. A legend line lifts the segment but pops no tip, and a
   touch screen never shows one (the tap opens the card). */
.invoice-strip-tip {
  position: absolute;
  bottom: calc(100% + 0.5rem);
  left: 50%;
  z-index: 30;
  display: none;
  align-items: center;
  gap: 0.375rem;
  padding: 0.25rem 0.5rem;
  background: var(--white);
  border: 1px solid var(--gray-200);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-md);
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--gray-700);
  white-space: nowrap;
  pointer-events: none;
  transform: translateX(-50%);
}
.invoice-strip-tip svg { flex: none; color: var(--pie); }
.invoice-strip-tip .dash-swatch { flex: none; }
.invoice-strip-tip strong { color: var(--gray-900); font-variant-numeric: tabular-nums; }
.invoice-strip-tip-pct { color: var(--gray-500); font-variant-numeric: tabular-nums; }
@media (hover: hover) {
  .invoice-strip-seg:hover .invoice-strip-tip { display: inline-flex; }
}
@media (prefers-reduced-motion: no-preference) {
  .invoice-strip-seg { transition: height 0.15s ease, opacity 0.15s ease; }
  .invoice-strip-seg:hover .invoice-strip-tip { animation: invoice-tip-in 0.15s ease both; }
  @keyframes invoice-tip-in {
    from { opacity: 0; translate: 0 0.25rem; }
  }
}
.invoice-strip-total {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--gray-800);
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  text-align: right;
}
.invoice-charts-toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  margin-left: auto;
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--navy-600);
  white-space: nowrap;
}
.invoice-charts-toggle svg { width: 14px; height: 14px; transition: transform 0.15s ease; }
.invoice-charts[open] .invoice-charts-toggle svg { transform: rotate(180deg); }
.invoice-charts-toggle-close,
.invoice-charts[open] .invoice-charts-toggle-open { display: none; }
.invoice-charts[open] .invoice-charts-toggle-close { display: inline; }
.invoice-charts[open] > .invoice-charts-strip { border-bottom: 1px solid var(--gray-100); border-bottom-left-radius: 0; border-bottom-right-radius: 0; }
.invoice-charts-body { padding: 0.75rem 1rem 1rem; }
/* Each ring is a band across the card — costs, then income under it, a hairline between —
   the small ring on the left and its WHOLE legend beside it, flowing down in as many columns
   as the width holds (largest first down each column), so twelve categories stand about as
   tall as the ring instead of hanging below it. A phone gets one column under the ring. */
.invoice-charts-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem; }
.invoice-chart + .invoice-chart { border-top: 1px solid var(--gray-100); padding-top: 1rem; }
.invoice-chart { display: flex; flex-direction: column; gap: 0.375rem; min-width: 0; margin: 0; }
.invoice-chart-caption { font-size: 0.8125rem; font-weight: 600; color: var(--gray-600); }
.invoice-donut { display: grid; grid-template-columns: 8.5rem minmax(0, 1fr); gap: 0.375rem 0.875rem; align-items: start; }
.invoice-donut > .dash-donut-ring { width: 8.5rem; height: 8.5rem; margin: 0; }
.invoice-donut .dash-donut-centre strong { font-size: 1.125rem; }
.invoice-donut .dash-donut-centre span { font-size: 0.6875rem; }
.invoice-donut .dash-donut-legend { display: block; columns: 15rem; column-gap: 2rem; min-width: 0; }
.invoice-donut .dash-donut-legend li {
  grid-template-columns: 0.625rem minmax(0, 1fr) auto auto;
  break-inside: avoid;
  margin-bottom: 0.125rem;
  padding: 0.0625rem 0.25rem;
  border-radius: var(--radius-sm);
  transition: background-color 0.15s;
}
.invoice-donut .dash-donut-legend li.is-active { background: var(--gray-100); }
.invoice-donut .dash-donut-without { grid-column: 2 / -1; }
.invoice-donut a.dash-donut-name { text-decoration: none; border-radius: 2px; }
.invoice-donut a.dash-donut-name:hover { color: var(--navy-600); text-decoration: underline; }
.invoice-donut a.dash-donut-name:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 1px; }
.invoice-donut-pct { min-width: 2.5rem; text-align: right; font-size: 0.75rem; color: var(--gray-500); font-variant-numeric: tabular-nums; }
.invoice-donut-foot { grid-column: 1 / -1; display: flex; flex-direction: column; gap: 0.25rem; }
.invoice-donut-foot .dash-note { margin: 0; }
.invoice-charts-note { margin: 0.5rem 0 0; }

/* ---- The invoice list's filters (invoice/list.html) ----
   The text and the period in one bar; everything else in a panel under "Filters (n)" (a
   <details data-dropdown>, app.js closes it on a press outside and on Escape); what is active
   as chips under the bar. */
.invoice-filter-bar { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.75rem; margin-bottom: 0.75rem; }
/* The search takes the first line whole; the period, "Filters" and "Filter" share the second,
   which is why the period's dates are narrower here than a form field's full width. */
.invoice-filter-search { flex: 1 1 100%; min-width: 0; }
.invoice-filter-bar .period-picker input[type="date"],
.invoice-filter-bar .period-picker .vk-datepicker { width: 10rem; }
.invoice-filter-bar .period-picker .vk-datepicker .form-input { width: 100%; }
.invoice-filter-search .form-input { width: 100%; }
.filter-panel { position: relative; }
.filter-panel-toggle { display: inline-flex; align-items: center; gap: 0.375rem; list-style: none; }
.filter-panel-toggle::-webkit-details-marker { display: none; }
.filter-panel[open] > .filter-panel-toggle { border-color: var(--navy-500); background: var(--navy-50); }
.filter-panel-body {
  position: absolute;
  z-index: 30;
  top: calc(100% + 0.375rem);
  right: 0;
  width: min(40rem, calc(100vw - 2rem));
  display: flex;
  flex-direction: column;
  gap: 0.875rem;
  padding: 1rem;
  box-shadow: var(--shadow-lg);
}
.filter-panel-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr)); gap: 0.75rem; }
.filter-panel-grid .form-input,
.filter-panel-grid .filter-checklist-summary { width: 100%; min-width: 0; }
.filter-panel-actions { justify-content: flex-end; }
.invoice-filter-chips { margin: -0.25rem 0 1rem; }
a.mail-chip { text-decoration: none; }
@media (max-width: 640px) {
  /* On a phone the panel is part of the page, under the bar, not a floating box. */
  .filter-panel { flex: 1 1 100%; order: 1; }
  .filter-panel-body { position: static; width: auto; margin-top: 0.375rem; box-shadow: var(--shadow-md); }
}
@media (max-width: 480px) {
  .invoice-charts-strip { flex-wrap: wrap; }
  .invoice-charts-toggle { flex-basis: 100%; justify-content: flex-end; }
  .invoice-donut { grid-template-columns: minmax(0, 1fr); }
  .invoice-donut > .dash-donut-ring { margin: 0 auto; }
}
.invoice-category-value { display: flex; align-items: center; gap: 0.375rem; flex-wrap: wrap; }
.invoice-category-select { width: auto; max-width: 100%; }

/* The list's bulk "set a category" menu: a <details> whose panel is a column of submit buttons,
   one per category, each an icon and its name. */
.invoice-category-menu,
.bulk-menu { position: relative; display: inline-block; }
.invoice-category-menu > summary,
.bulk-menu > summary { list-style: none; cursor: pointer; }
.invoice-category-menu > summary::-webkit-details-marker,
.bulk-menu > summary::-webkit-details-marker { display: none; }
.invoice-category-menu-panel,
.bulk-menu-panel {
  position: absolute; z-index: 20; top: calc(100% + 0.25rem); left: 0;
  display: flex; flex-direction: column; min-width: 13rem;
  max-width: min(18rem, calc(100vw - 2rem)); max-height: 20rem; overflow-y: auto;
  padding: 0.25rem; background: #fff; border: 1px solid var(--gray-200);
  border-radius: var(--radius); box-shadow: 0 8px 24px rgba(15, 23, 42, 0.12);
}
.invoice-category-menu-item,
.bulk-menu-item {
  display: flex; align-items: center; gap: 0.5rem; width: 100%; min-height: 2.25rem;
  padding: 0.375rem 0.625rem; border: 0; border-radius: 0.375rem; background: none;
  color: var(--gray-700); font: inherit; font-size: 0.875rem; text-align: left; cursor: pointer;
}
.invoice-category-menu-item svg,
.bulk-menu-item svg { color: var(--navy-400); flex: none; }
.invoice-category-menu-item:hover,
.bulk-menu-item:hover { background: var(--navy-50); }
.invoice-category-menu-item:focus-visible,
.bulk-menu-item:focus-visible { outline: 2px solid var(--navy-400); outline-offset: -2px; }

/* ---------------------------------------------------------------------------
   One invoice (templates/invoice/panel.html): the same sections in a row opened on
   /invoices (compact, separated by rules) and on the detail screen (cards, beside the
   PDF that stays in view while they scroll).
   --------------------------------------------------------------------------- */
.doc-preview--invoice { grid-template-columns: minmax(19rem, 1fr) minmax(0, 1.2fr); gap: 1.25rem; }
.invoice-panel .invoice-section + .invoice-section { border-top: 1px solid var(--gray-200); padding-top: 0.75rem; margin-top: 0.75rem; }
.invoice-section-title { margin: 0 0 0.5rem; font-size: 0.875rem; font-weight: 600; color: var(--gray-700); }
details.invoice-section > summary.invoice-section-title { cursor: pointer; margin-bottom: 0; }
details.invoice-section[open] > summary.invoice-section-title { margin-bottom: 0.5rem; }
.invoice-panel form { margin: 0; }
.invoice-summary-head { display: flex; align-items: flex-start; gap: 0.75rem; margin-bottom: 0.75rem; }
.invoice-summary-mark { flex: none; display: inline-flex; align-items: center; min-height: 1.75rem; }
.invoice-summary-title { flex: 1 1 auto; min-width: 0; }
.invoice-summary-name { font-weight: 600; color: var(--gray-900); overflow-wrap: anywhere; }
.invoice-summary-amount {
  flex: none; font-size: 1.125rem; font-weight: 700; white-space: nowrap;
  font-variant-numeric: tabular-nums; color: var(--gray-900);
}
/* The head's facts as ONE list (invoice/panel :: head): an icon, a label, the value, a hairline
   between rows — a data sheet, which takes a badge, a sentence or a photo alike without the rows
   losing their line. The icons are the Status column's. Where a value was decided by something,
   that is its title (.has-source: the help cursor). A narrow panel puts the label over the value. */
.invoice-props { container-type: inline-size; display: flex; flex-direction: column; }
.invoice-prop {
  display: grid;
  grid-template-columns: 1.25rem 8.5rem minmax(0, 1fr);
  column-gap: 0.625rem;
  align-items: center;
  min-height: 2.5rem;
  padding: 0.375rem 0;
  border-top: 1px solid var(--gray-100);
}
.invoice-prop:first-child { border-top: 0; }
.invoice-prop-icon { display: inline-flex; align-items: center; justify-content: center; color: var(--navy-400); }
.invoice-prop-icon svg { width: 16px; height: 16px; }
.invoice-prop-icon.is-ok { color: #16a34a; }
.invoice-prop-icon.is-warn { color: #b45309; }
.invoice-prop-icon.is-muted { color: var(--gray-400); }
.invoice-prop-label { font-size: 0.75rem; color: var(--gray-500); }
.invoice-prop-value {
  display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.5rem;
  min-width: 0; font-size: 0.8125rem; color: var(--gray-900); overflow-wrap: anywhere;
}
.invoice-prop-value.has-source, .invoice-prop-value[title] { cursor: help; }
.invoice-prop-value a[title] { cursor: pointer; }
.invoice-prop-note { font-size: 0.75rem; color: var(--gray-500); }
.invoice-prop-muted { color: var(--gray-500); }
.invoice-props-caption {
  margin-top: 0.5rem; padding: 0.5rem 0 0.125rem; border-top: 1px solid var(--gray-200);
  font-size: 0.6875rem; font-weight: 600; letter-spacing: 0.03em; text-transform: uppercase; color: var(--gray-500);
}
.invoice-props-caption + .invoice-prop { border-top: 0; }
/* A collective invoice's split per car (V141) sits in the panel's narrow column (~361 px): a plate
   and three figures, so its cells pad less and its heads are written as the words are, without
   capitals or letter-spacing. At the ordinary head the fee share — the column a Riverty invoice is
   read for — hid behind the wrapper's scroll; measured in Chromium, the Norwegian heads
   ("Parkeringer", "Andel av gebyrene") now need ~339 px. */
.table.collective-split th, .table.collective-split td { padding: 0.5rem 0.375rem; }
.table.collective-split th { text-transform: none; letter-spacing: 0; }
@container (max-width: 22rem) {
  .invoice-prop { grid-template-columns: 1.25rem minmax(0, 1fr); row-gap: 0.125rem; align-items: start; }
  .invoice-prop-icon { grid-row: 1 / span 2; padding-top: 0.125rem; }
  .invoice-prop-label, .invoice-prop-value { grid-column: 2; }
}
.invoice-vehicle { flex-wrap: nowrap; gap: 0.625rem; }
.invoice-vehicle-photo { flex: none; display: block; line-height: 0; border-radius: 0.375rem; }
.invoice-vehicle-photo img {
  width: 3.5rem; height: 2.5rem; object-fit: contain;
  border-radius: 0.375rem; border: 1px solid var(--gray-200); background: var(--white);
}
.invoice-vehicle-text { display: flex; flex-wrap: wrap; align-items: center; gap: 0.125rem 0.5rem; min-width: 0; }
.invoice-vehicle-model { color: var(--gray-600); }

/* A section folded under one heading — the source e-mail, the settings, the technical details
   (invoice/panel.html): a quiet bar with an icon, the title, what is inside (cut with "…") and a
   chevron; opened, the sections follow one another divided by rules. */
.invoice-fold > summary { list-style: none; }
.invoice-fold > summary::-webkit-details-marker { display: none; }
.invoice-fold-summary {
  display: flex; align-items: center; gap: 0.5rem; min-height: 2.5rem;
  padding: 0.375rem 0.625rem; margin: 0 -0.625rem;
  border-radius: var(--radius); cursor: pointer; color: var(--gray-700);
}
.invoice-fold-summary > svg:first-child { flex: none; color: var(--navy-400); }
.invoice-fold-summary:hover { background: var(--gray-50); }
.invoice-fold-summary:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 1px; }
.invoice-fold-title { flex: none; font-size: 0.875rem; font-weight: 600; }
.invoice-fold-hint { flex: 1 1 auto; min-width: 0; font-size: 0.75rem; color: var(--gray-500); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.invoice-fold-chevron { flex: none; display: inline-flex; margin-left: auto; transition: transform 0.2s ease; }
.invoice-fold[open] .invoice-fold-chevron { transform: rotate(180deg); }
.invoice-fold[open] .invoice-fold-hint { visibility: hidden; }
.invoice-fold-body { padding-bottom: 0.25rem; }
.invoice-fold-body > .invoice-section { padding-top: 0.75rem; margin-top: 0.75rem; border-top: 1px solid var(--gray-200); }
.invoice-fold-body > .invoice-section:first-child { margin-top: 0.25rem; }
.invoice-panel .invoice-fold + .invoice-fold { padding-top: 0.25rem; margin-top: 0.25rem; }
/* On the detail screen every section is a card; the sections inside a folded card are not. */
.invoice-panel--page .invoice-fold-body > .invoice-section {
  background: none; border: 0; border-top: 1px solid var(--gray-200); border-radius: 0;
  padding: 1rem 0 0; margin: 1rem 0 0;
}
.invoice-panel--page .invoice-fold-summary { margin: -0.375rem -0.625rem; }
/* How the data was read (invoice/panel :: technical). */
.invoice-reading-title { margin: 1rem 0 0.5rem; font-size: 0.8125rem; font-weight: 600; color: var(--gray-700); }
.invoice-reading-note { margin-top: 0.125rem; font-size: 0.75rem; color: var(--gray-500); }

/* The opened row's document in the proportions of an A4 page (210 × 297), as tall as the screen
   allows, its width following the height — not a 75vh slab whatever the page. */
.doc-preview--invoice .doc-preview-document { min-width: 0; }
.doc-preview--invoice .pdf-frame-view {
  /* Width first — the column's, but never wider than a page 85vh tall would be — and the
     height from the page's proportions, so the whole page fits on the screen at once. */
  width: min(100%, calc(85vh * 210 / 297));
  height: auto;
  aspect-ratio: 210 / 297;
  margin: 0 auto;
}
.invoice-vehicle-field { flex: 1 1 12rem; min-width: 0; }
.invoice-vehicle-field .form-input { width: 100%; }
.invoice-edit-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(9rem, 1fr)); gap: 0.75rem; }
.invoice-edit-wide { grid-column: 1 / -1; }
.invoice-last-error { white-space: pre-wrap; overflow-wrap: anywhere; }
.btn.row-action-danger { color: var(--red-600); }

/* The detail screen: facts and actions, the PDF beside them held under the navbar, the rest
   below the facts. One column below 1100 px, the PDF after the head. */
.invoice-detail {
  display: grid; gap: 0 1.25rem; align-items: start;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1.2fr);
  grid-template-areas: "main doc" "more doc";
  /* 1fr on the second row: a track the PDF spans that is flexible, so the tall frame is not
     spread over the first row and does not open a gap between the head and the rest. */
  grid-template-rows: auto 1fr;
}
.invoice-detail-main { grid-area: main; }
.invoice-detail-more { grid-area: more; }
.invoice-detail-document { grid-area: doc; position: sticky; top: 5rem; }
.invoice-detail-document .pdf-frame-view { height: calc(100vh - 9rem); min-height: 24rem; }
.invoice-panel--page .invoice-section {
  background: var(--white); border: 1px solid var(--gray-200); border-radius: var(--radius-lg);
  padding: 1rem 1.25rem; margin-bottom: 1rem;
}
.invoice-panel--page .invoice-section + .invoice-section { border-top: 1px solid var(--gray-200); margin-top: 0; padding-top: 1rem; }
.invoice-panel--page .invoice-summary-amount { font-size: 1.375rem; }
/* The head and the actions are one card: what the invoice is, and what to do with it. */
.invoice-detail-main {
  background: var(--white); border: 1px solid var(--gray-200); border-radius: var(--radius-lg);
  padding: 1rem 1.25rem; margin-bottom: 1rem;
}
.invoice-panel--page .invoice-detail-main .invoice-section { background: none; border: 0; border-radius: 0; padding: 0; margin: 0; }
.invoice-panel--page .invoice-detail-main .invoice-section + .invoice-section {
  border-top: 1px solid var(--gray-200); padding-top: 0.75rem; margin-top: 0.75rem;
}
@media (max-width: 1100px) {
  .invoice-detail { grid-template-columns: minmax(0, 1fr); grid-template-areas: "main" "doc" "more"; grid-template-rows: none; }
  .invoice-detail-document { position: static; margin-bottom: 1rem; }
  .invoice-detail-document .pdf-frame-view { height: min(80vh, 56rem); }
}
.invoice-suggestions { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.invoice-suggestion { border: 1px solid var(--gray-200); border-radius: var(--radius); padding: 0.5rem 0.75rem; font-size: 0.8125rem; }
.invoice-suggestion-head { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.75rem; }
.invoice-suggestion-title { flex: 1 1 8rem; min-width: 0; overflow-wrap: anywhere; color: var(--navy-600); text-decoration: underline; text-underline-offset: 2px; }
.invoice-suggestion-amount { white-space: nowrap; font-variant-numeric: tabular-nums; text-align: right; }

/* The bank statement's Status column (invoice/bank-fragments :: statusIcons): a linked row is
   its invoices' category icons and the linked tile's mark (the warning state when the invoices do
   not add up to the payment); the potential invoices' mark carries its count. */
/* One line, however many invoices a payment covers: the cell grows, the table scrolls on a phone. */
.bank-status { flex-wrap: nowrap; gap: 0.25rem; }
.bank-linked-doc {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  min-width: 1.75rem; height: 1.75rem; padding: 0 0.125rem; border-radius: 0.375rem; text-decoration: none;
}
.bank-linked-doc:hover { background-color: rgba(15, 23, 42, 0.08); }
.bank-linked-doc:focus-visible { outline: 2px solid var(--navy-400); outline-offset: 1px; }
/* V110: the invoice's category's one-tone mark (config/CategoryMark), in navy — the Status column
   colours it by the category, the statement does not. */
.bank-linked-doc > svg { color: var(--navy-400); }
/* The hover belongs to the link: its title names the invoice, the logo's own only the platform. */
.bank-linked-doc > * { pointer-events: none; }
.status-icon--proposals { position: relative; }
.status-icon-count {
  position: absolute; top: -0.25rem; right: -0.3rem;
  min-width: 1rem; height: 1rem; padding: 0 0.25rem; border-radius: 9999px;
  background: var(--navy-600); color: var(--white); box-shadow: 0 0 0 1.5px var(--white);
  font-size: 0.625rem; font-weight: 700; line-height: 1rem; text-align: center;
  font-variant-numeric: tabular-nums;
}

/* An invoice opened inside a row of the statement: sized by the room the row gives it, not
   by the window — below ~48rem of it, the facts above the document. */
.row-preview { container-type: inline-size; }
@container (max-width: 48rem) {
  .doc-preview--invoice { grid-template-columns: minmax(0, 1fr); }
}

/* Payslip: the amounts listed under the platform summary (entered by hand, advance, parking) —
   aligned under the table's figures rather than stretched across the card. */
.payroll-summary-extras { max-width: 26rem; margin-inline-start: auto; }

/* /payroll's "Export PDF" dialog: which drivers the file lists. Each row is one big label (the
   whole row toggles its box), the name on the reading side and the gross salary on the other, so
   the operator sees what a driver adds before leaving them out. The list scrolls inside the
   dialog, never the page. */
.payroll-export-dialog { max-width: 32rem; }
.payroll-export-dialog .app-dialog-actions { margin-top: 1.25rem; }
.payroll-export-bar { display: flex; align-items: center; justify-content: space-between; gap: 0.5rem; flex-wrap: wrap; margin-bottom: 0.5rem; }
.payroll-export-list { list-style: none; margin: 0; padding: 0; max-height: min(22rem, 50vh); overflow-y: auto; border: 1px solid #e5e7eb; border-radius: 6px; }
.payroll-export-list li + li { border-top: 1px solid #f0f1f3; }
.payroll-export-item { display: flex; align-items: center; gap: 0.75rem; padding: 0.6rem 0.75rem; min-height: 2.5rem; cursor: pointer; }
.payroll-export-item:hover { background: #f8f9fa; }
.payroll-export-item input { width: 1.1rem; height: 1.1rem; flex: none; accent-color: #223b60; }
.payroll-export-item:has(input:focus-visible) { outline: 2px solid #d4a843; outline-offset: -2px; }
.payroll-export-name { flex: 1 1 auto; min-width: 0; overflow-wrap: anywhere; }
.payroll-export-amount { flex: none; font-variant-numeric: tabular-nums; color: #6b7280; font-size: 0.875rem; }
.payroll-export-item:has(input:not(:checked)) .payroll-export-name { color: #9ca3af; }
/* What the file holds: the summary, the calculations, and (under them, only meaningful with
   them) a new sheet per document for double-sided printing. */
.payroll-export-contents { border: 0; margin: 0 0 0.75rem; padding: 0; display: flex; flex-direction: column; gap: 0.25rem; }
.payroll-export-contents legend { padding: 0; margin-bottom: 0.25rem; }
.payroll-export-option { display: flex; align-items: flex-start; gap: 0.6rem; min-height: 2.25rem; padding: 0.35rem 0; cursor: pointer; }
.payroll-export-option input { width: 1.1rem; height: 1.1rem; flex: none; margin-top: 0.15rem; accent-color: #223b60; }
.payroll-export-option--sub { padding-inline-start: 1.7rem; font-size: 0.875rem; }
.payroll-export-option:has(input:disabled) { color: #9ca3af; cursor: default; }
/* "No calculation — …" beside a driver whose calculation the file leaves out: said only while
   calculations are ticked, since the summary lists that driver either way. */
.payroll-export-note { display: none; font-size: 0.8125rem; color: #92400e; }
.payroll-export-form:has([data-payroll-export-payslips]:checked) .payroll-export-note { display: block; }
.export-language--inline { display: flex; flex-direction: column; align-items: stretch; gap: 0.25rem; max-width: 16rem; text-align: start; }

/* A driver below the company's expectations (templates/fragments/expectation-flag.html): the
   drivers-below-expectations mark in its own palette, the words in the title. */
.expectation-flag {
  display: inline-flex; align-items: center; justify-content: center;
  flex-shrink: 0; cursor: help; vertical-align: middle;
}

/* ── Timeline: a person's or a car's working time on one axis (fragments/timeline.html,
   js/timeline.js) ──────────────────────────────────────────────────────────────────────────
   One hue per platform (validated with the dataviz palette check in lane order: Bolt, Uber,
   Mapon; the taximeter's blue added after, apart from the three in hue), waiting the same hue lighter, an ESTIMATE the waiting tint hatched — texture, never a
   second colour — and the stretches that are not work (workshop, leave, sick) hatched in their
   own hue. Text wears text tokens, never a series colour. */
:root {
  --tl-bolt: #2a9d63;
  --tl-bolt-wait: #a9dbc1;
  --tl-uber: #7a4bc4;
  --tl-uber-wait: #d3c4ef;
  --tl-akarnet: #2f6fb5;
  --tl-drive: #c27c1a;
  --tl-stop: #f1dfc3;
  --tl-hold: #dbe4f0;
  --tl-plan: #f3f6fb;
  --tl-label-w: 11rem;
}
.tl { position: relative; }
.tl-head { display: flex; flex-wrap: wrap; align-items: baseline; justify-content: space-between; gap: 0.25rem 1rem; }
.tl-range { margin: 0; color: var(--gray-600); font-size: 0.875rem; font-variant-numeric: tabular-nums; }
.tl-toolbar { margin: 0.75rem 0; }
.tl-presets { display: inline-flex; border: 1px solid var(--gray-300); border-radius: 8px; overflow: hidden; background: var(--white); }
.tl-preset {
  border: 0; background: transparent; padding: 0.375rem 0.8rem; min-height: 2.25rem;
  font: inherit; font-size: 0.8125rem; color: var(--gray-700); cursor: pointer; white-space: nowrap;
}
.tl-preset + .tl-preset { border-inline-start: 1px solid var(--gray-200); }
.tl-preset:hover { background: var(--gray-50); }
.tl-preset.is-active { background: var(--navy-500); color: var(--white); }
.tl-preset:focus-visible { outline: 2px solid var(--gold-400); outline-offset: -2px; }

.tl-summary { display: grid; grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr)); gap: 0.5rem; margin: 0 0 0.25rem; }
.tl-stat { margin: 0; padding: 0.55rem 0.75rem; border-radius: 8px; background: var(--gray-50); min-width: 0; }
.tl-stat dt { font-size: 0.75rem; color: var(--gray-600); }
.tl-stat dd { margin: 0; }
.tl-stat-value { font-size: 1.25rem; font-weight: 600; color: var(--navy-700); font-variant-numeric: tabular-nums; white-space: nowrap; }
.tl-stat-note { font-size: 0.75rem; color: var(--gray-500); }
.tl-summary-scope { margin: 0 0 0.25rem; font-size: 0.75rem; color: var(--gray-500); }

.tl-legend { display: flex; flex-wrap: wrap; gap: 0.25rem 1rem; list-style: none; padding: 0; margin: 0.5rem 0; font-size: 0.8125rem; color: var(--gray-600); }
.tl-legend li { display: inline-flex; align-items: center; }
.tl-swatch { display: inline-block; width: 1.25rem; height: 0.75rem; border-radius: 3px; margin-inline-end: 0.4rem; flex-shrink: 0; }
.tl-sw-wait { background: linear-gradient(90deg, var(--tl-bolt-wait) 50%, var(--tl-uber-wait) 50%); }
.tl-sw-ride { background: linear-gradient(90deg, var(--tl-bolt) 50%, var(--tl-uber) 50%); }
.tl-sw-est { background: repeating-linear-gradient(45deg, var(--tl-uber-wait) 0 3px, var(--white) 3px 6px); border: 1px solid var(--tl-uber-wait); }
.tl-sw-akarnet { background: var(--tl-akarnet); }
.tl-sw-busy { background: repeating-linear-gradient(45deg, #9aa3af 0 2px, #eef0f3 2px 5px); }
.tl-sw-plan { background: var(--tl-plan); border: 1.5px dashed var(--navy-400); }
.tl-sw-nodata { background: repeating-linear-gradient(45deg, #e5e7eb 0 2px, transparent 2px 6px); border: 1px solid var(--gray-200); }

.tl-body {
  display: grid; grid-template-columns: var(--tl-label-w) minmax(0, 1fr);
  border: 1px solid var(--gray-200); border-radius: 8px; overflow: hidden; background: var(--white);
}
.tl-labels { background: var(--gray-50); border-inline-end: 1px solid var(--gray-200); min-width: 0; }
.tl-labels-axis { height: 46px; border-bottom: 1px solid var(--gray-200); }
.tl-lane-label {
  display: flex; align-items: center; gap: 0.5rem; width: 100%; box-sizing: border-box;
  border: 0; border-bottom: 1px solid var(--gray-100); background: transparent; padding: 0 0.625rem;
  font: inherit; font-size: 0.8125rem; color: var(--gray-800); text-align: start; cursor: pointer; overflow: hidden;
}
.tl-lane-label:hover { background: var(--gray-100); }
.tl-lane-label:focus-visible { outline: 2px solid var(--gold-400); outline-offset: -2px; }
.tl-lane-label.is-folded { color: var(--gray-400); }
.tl-lane-label.is-folded .tl-lane-mark { opacity: 0.45; }
.tl-lane-label.is-folded .tl-lane-note,
.tl-lane-label.is-folded .tl-lane-name { font-size: 0.6875rem; }
.tl-lane-mark { flex: 0 0 1.25rem; display: inline-flex; align-items: center; justify-content: center; }
.tl-lane-mark img { width: 1.15rem; height: 1.15rem; object-fit: contain; }
.tl-lane-mark .vk-mark { width: 1.1rem; height: 1.1rem; }
.tl-lane-text { display: flex; flex-direction: column; min-width: 0; line-height: 1.2; }
.tl-lane-name { font-weight: 500; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-lane-note { font-size: 0.6875rem; color: #92400e; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tl-lane-note:empty { display: none; }

.tl-plot { position: relative; min-width: 0; touch-action: pan-y; cursor: grab; outline: none; user-select: none; -webkit-user-select: none; direction: ltr; }
.tl-plot.is-dragging { cursor: grabbing; }
.tl-plot:focus-visible { box-shadow: inset 0 0 0 2px var(--gold-400); }
.tl-svg { display: block; width: 100%; }
.tl-axis-bg { fill: var(--gray-50); }
.tl-axis-rule { stroke: var(--gray-200); }
.tl-axis-major { font-size: 12px; font-weight: 600; fill: var(--gray-700); }
.tl-axis-minor { font-size: 11px; fill: var(--gray-500); font-variant-numeric: tabular-nums; }
.tl-grid { stroke: var(--gray-100); }
.tl-grid-strong { stroke: var(--gray-200); }
.tl-grid-major { stroke: var(--gray-300); }
.tl-lane-rule { stroke: var(--gray-100); }
.tl-night { fill: rgba(34, 59, 96, 0.05); }
.tl-weekend { fill: rgba(34, 59, 96, 0.035); }
.tl-future { fill: rgba(148, 163, 184, 0.10); }
.tl-now { stroke: var(--gold-400); stroke-width: 2; }
.tl-now-head { fill: var(--gold-400); }
.tl-highlight { fill: rgba(212, 168, 67, 0.13); }
.tl-cursor { fill: none; stroke: var(--gold-500); stroke-width: 2; }

.tl-it { transition: none; }
.tl-it:hover, .tl-it.is-selected { stroke: var(--navy-800); stroke-width: 1.5; }
.tl-it.is-selected { stroke: var(--gold-500); stroke-width: 2.5; }
.tl-k-WAIT.tl-l-bolt { fill: var(--tl-bolt-wait); }
.tl-k-RIDE.tl-l-bolt { fill: var(--tl-bolt); }
.tl-k-WAIT.tl-l-uber { fill: var(--tl-uber-wait); }
.tl-k-RIDE.tl-l-uber { fill: var(--tl-uber); }
.tl-k-RIDE.tl-l-akarnet { fill: var(--tl-akarnet); }
.tl-k-DRIVE { fill: var(--tl-drive); }
.tl-k-STOP { fill: var(--tl-stop); }
.tl-k-HELD { fill: var(--tl-hold); stroke: var(--navy-200); stroke-width: 1; }
.tl-k-PLAN { fill: var(--tl-plan); stroke: var(--navy-400); stroke-width: 1.25; stroke-dasharray: 4 3; }
.tl-k-WORKSHOP { stroke: #ea580c; stroke-width: 1; }
.tl-k-LEAVE { stroke: var(--gold-500); stroke-width: 1; }
.tl-k-SICK { stroke: #dc2626; stroke-width: 1; }
.tl-bar-label { font-size: 11px; font-weight: 600; fill: var(--navy-800); pointer-events: none; }
.tl-k-PLAN-label { font-weight: 500; fill: var(--navy-700); }
.tl-k-WORKSHOP-label { fill: #7c2d12; }
.tl-tick.tl-l-bolt { fill: var(--tl-bolt); }
.tl-tick.tl-l-uber { fill: var(--tl-uber); }
.tl-tick.tl-l-akarnet { fill: var(--tl-akarnet); }
.tl-tick.tl-k-CANCELLED { fill: var(--gray-400); }
.tl-open-dot.tl-l-bolt { fill: var(--tl-bolt); }
.tl-open-dot.tl-l-uber { fill: var(--tl-uber); }
.tl-open-dot.tl-l-akarnet { fill: var(--tl-akarnet); }
.tl-point { cursor: pointer; }
.tl-point-disc { fill: var(--white); stroke: var(--gray-300); stroke-width: 1.5; }
.tl-point:hover .tl-point-disc { stroke: var(--navy-500); }
.tl-point.is-selected .tl-point-disc { stroke: var(--gold-500); stroke-width: 2.5; }
.tl-point.is-day .tl-point-disc { stroke-dasharray: 2 2; }
.tl-cluster { cursor: zoom-in; }
.tl-cluster-span { fill: var(--navy-100); }
.tl-cluster-disc { fill: var(--navy-500); stroke: var(--white); stroke-width: 2; }
.tl-cluster-count { fill: var(--white); font-size: 11px; font-weight: 700; pointer-events: none; }
.tl-daycol { cursor: zoom-in; }
.tl-daycol-hit { fill: transparent; }
.tl-daycol:hover .tl-daycol-hit { fill: rgba(34, 59, 96, 0.06); }
.tl-pat-bg-est-bolt { fill: var(--tl-bolt-wait); }
.tl-pat-bg-est-uber, .tl-pat-bg-est-day { fill: var(--tl-uber-wait); }
.tl-pat-ink-est-bolt, .tl-pat-ink-est-uber, .tl-pat-ink-est-day { fill: var(--white); opacity: 0.8; }
.tl-pat-bg-busy { fill: #eef0f3; }
.tl-pat-ink-busy { fill: #9aa3af; }
.tl-pat-bg-nodata { fill: transparent; }
.tl-pat-ink-nodata { fill: #e5e7eb; }
.tl-pat-bg-workshop { fill: #fff1e6; }
.tl-pat-ink-workshop { fill: #fdba74; }
.tl-pat-bg-leave { fill: var(--gold-50); }
.tl-pat-ink-leave { fill: var(--gold-200); }
.tl-pat-bg-sick { fill: var(--red-50); }
.tl-pat-ink-sick { fill: #fca5a5; }
.tl-plot.is-loading .tl-g-items, .tl-g-items.is-stale { opacity: 0.55; }

.tl-busy { position: absolute; inset: 0 0 auto 0; height: 3px; overflow: hidden; background: var(--gold-100); pointer-events: none; }
.tl-busy::after { content: ""; position: absolute; inset: 0; width: 35%; background: var(--gold-400); }
.tl-hint {
  position: absolute; left: 50%; top: 55%; transform: translate(-50%, -50%); z-index: 3;
  background: rgba(20, 31, 56, 0.9); color: var(--white); padding: 0.45rem 0.8rem; border-radius: 6px;
  font-size: 0.8125rem; pointer-events: none; white-space: nowrap;
}
.tl-empty {
  position: absolute; inset: 46px 0 0 0; display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
  gap: 0.5rem 0.75rem; padding: 0 1rem; color: var(--gray-500); font-size: 0.875rem; text-align: center; pointer-events: none;
}
.tl-empty .btn { pointer-events: auto; }

.tl-overview { margin: 0.5rem 0 0; padding-inline-start: var(--tl-label-w); }
.tl-overview-label { display: block; font-size: 0.6875rem; color: var(--gray-500); margin-bottom: 0.15rem; }
.tl-overview-svg { display: block; width: 100%; cursor: pointer; touch-action: none; }
.tl-ov-bg { fill: var(--gray-50); }
.tl-ov-col { fill: var(--navy-200); }
.tl-ov-brush { fill: rgba(212, 168, 67, 0.18); stroke: var(--gold-500); stroke-width: 1.5; }
.tl-message { margin-top: 0.75rem; display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem 1rem; }
/* Whatever the script hides stays hidden — .btn and .flex would otherwise win over [hidden]. */
.tl [hidden] { display: none !important; }

.tl-tip {
  position: absolute; z-index: 30; max-width: 20rem; padding: 0.5rem 0.7rem; border-radius: 8px;
  background: var(--navy-900); color: var(--white); font-size: 0.8125rem; line-height: 1.35;
  pointer-events: none; box-shadow: 0 8px 24px rgba(20, 31, 56, 0.25);
}
.tl-tip-title { font-weight: 600; }
.tl-tip-when { color: var(--navy-100); font-variant-numeric: tabular-nums; }
.tl-tip-line { color: #e5e7eb; overflow-wrap: anywhere; }
.tl-tip-badge { display: inline-block; padding: 0 0.3rem; border-radius: 4px; background: var(--gold-400); color: var(--navy-900); font-size: 0.6875rem; font-weight: 600; }

.tl-panel {
  margin-top: 0.75rem; padding: 0.75rem 1rem; border: 1px solid var(--gray-200); border-inline-start: 3px solid var(--gold-400);
  border-radius: 8px; background: var(--white);
}
.tl-panel-head { display: flex; align-items: flex-start; gap: 0.75rem; }
.tl-panel-mark { flex: 0 0 auto; display: inline-flex; }
.tl-panel-mark svg, .tl-panel-mark img { width: 1.5rem; height: 1.5rem; }
.tl-panel-heading { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.tl-panel-when { font-size: 0.8125rem; color: var(--gray-600); font-variant-numeric: tabular-nums; }
.tl-panel-lines { list-style: none; padding: 0; margin: 0.5rem 0 0.75rem; font-size: 0.875rem; color: var(--gray-700); display: grid; gap: 0.2rem; }
.tl-panel-money { font-weight: 600; color: var(--navy-700); }
.tl-panel-badge { justify-self: start; padding: 0 0.4rem; border-radius: 4px; background: var(--gold-100); color: var(--gold-700); font-size: 0.75rem; font-weight: 600; }

.tl-list { margin-top: 0.75rem; max-height: 26rem; overflow: auto; }
.tl-list-go { border: 0; background: none; padding: 0.25rem; font: inherit; font-size: 0.8125rem; color: var(--navy-500); text-decoration: underline; cursor: pointer; white-space: nowrap; }
.tl-help { margin-top: 0.5rem; font-size: 0.8125rem; color: var(--gray-600); }
.tl-help summary { cursor: pointer; width: max-content; }
.tl-help ul { margin: 0.25rem 0 0; padding-inline-start: 1.25rem; }
.tl-fallback { margin-top: 0.75rem; }

@media screen and (prefers-reduced-motion: no-preference) {
  .tl-busy::after { animation: tl-busy 1.1s ease-in-out infinite; }
  .tl-open-dot { animation: tl-pulse 1.8s ease-in-out infinite; }
}
@keyframes tl-busy { from { transform: translateX(-100%); } to { transform: translateX(290%); } }
@keyframes tl-pulse { 0%, 100% { opacity: 1; } 50% { opacity: 0.35; } }

@media (max-width: 640px) {
  :root { --tl-label-w: 2.75rem; }
  .tl-lane-label { justify-content: center; padding: 0; }
  .tl-lane-text { display: none; }
  .tl-summary { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tl-stat-value { font-size: 1.1rem; }
  .tl-tip { max-width: 16rem; }
}

/* ── My files (files/, V165) ────────────────────────────────────────────────────────────────
   The person's own file space: the breadcrumbs under the heading, the uploads' chips, the
   folder menus' small forms, the opened row's panel (facts on the left, a preview on the right
   where there is room), and the list's drop state. */
.files-crumbs { display: flex; flex-wrap: wrap; align-items: center; gap: 0.25rem 0.5rem; color: var(--gray-600); }
.files-crumbs a { color: inherit; text-decoration: none; }
.files-crumbs a:hover { text-decoration: underline; color: var(--gray-900); }
.files-crumbs .is-current { color: var(--gray-900); font-weight: 600; }
.files-crumbs-sep { color: var(--gray-400); }
.files-menu-panel { min-width: 18rem; padding: 0.75rem; }
.files-menu-form { display: flex; flex-direction: column; margin: 0; }
.files-cell-name .files-name { display: inline-flex; align-items: center; gap: 0.5rem; min-width: 0; }
.files-cell-name .files-name .vk-mark { flex: none; }
.files-table { min-width: 36rem; }
.files-move-select { max-width: 16rem; }
[data-files-list].is-dropping .table-wrapper { outline: 2px dashed var(--gold-400); outline-offset: -2px; background: var(--navy-50); }
.files-uploads { list-style: none; margin: 0 0 1rem; padding: 0; display: flex; flex-direction: column; gap: 0.375rem; }
.files-upload-chip { position: relative; display: grid; grid-template-columns: minmax(0, 1fr) auto; gap: 0 0.75rem; align-items: center;
  padding: 0.5rem 0.75rem 0.625rem; border: 1px solid var(--gray-200); border-radius: 0.5rem; background: #fff; font-size: 0.875rem; overflow: hidden; }
.files-upload-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-weight: 600; color: var(--gray-800); }
.files-upload-size { color: var(--gray-500); }
.files-upload-status { grid-column: 1 / -1; color: var(--gray-500); font-size: 0.75rem; min-height: 1em; }
.files-upload-bar { position: absolute; left: 0; bottom: 0; height: 3px; width: 0; background: var(--navy-500); transition: width 0.15s ease; }
.files-upload-chip.is-hashing .files-upload-bar { background: var(--gold-400); }
.files-upload-chip.is-done .files-upload-bar { background: var(--green-600, #2a9d63); }
.files-upload-chip.is-failed { border-color: var(--red-300, #fca5a5); }
.files-upload-chip.is-failed .files-upload-bar { background: var(--red-600, #dc2626); }
.files-panel { padding: 0.25rem 0 0.5rem; }
.files-panel-grid { display: grid; grid-template-columns: minmax(0, 1fr); gap: 1rem; }
@media (min-width: 900px) {
  .files-panel-grid:has(.files-panel-preview) { grid-template-columns: minmax(18rem, 1fr) minmax(0, 2fr); }
}
.files-facts { display: grid; grid-template-columns: auto minmax(0, 1fr); gap: 0.25rem 1rem; margin: 0; font-size: 0.875rem; }
.files-facts > div { display: contents; }
.files-facts dt { color: var(--gray-500); white-space: nowrap; }
.files-facts dd { margin: 0; color: var(--gray-800); }
.files-panel-forms { display: flex; flex-direction: column; gap: 0.75rem; margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--gray-200); }
.files-panel-forms form { margin: 0; }
.files-preview-image { display: block; max-width: 100%; max-height: 60vh; border: 1px solid var(--gray-200); border-radius: 0.5rem; background: #fff; }

/* Shared with colleagues (V166): the faces beside a row's name, the owner's "Shared with" list. */
.files-shared-faces { display: inline-flex; align-items: center; margin-left: 0.5rem; vertical-align: middle; }
.files-shared-faces .avatar + .avatar { margin-left: -0.4rem; }
.files-shared-faces .avatar { border: 2px solid #fff; border-radius: 50%; }
.files-shared-more { margin-left: 0.25rem; font-size: 0.75rem; color: var(--gray-600); }
.files-owner { display: inline-flex; align-items: center; gap: 0.375rem; }
.files-share-title { font-size: 0.875rem; font-weight: 600; margin: 0 0 0.5rem; color: var(--gray-700); }
.files-recipients { list-style: none; margin: 0 0 0.75rem; padding: 0; display: flex; flex-direction: column; gap: 0.25rem; }
.files-recipients li { display: flex; align-items: center; gap: 0.5rem; font-size: 0.875rem; }
.files-recipient-name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; }
.files-share-form { margin: 0; }
.files-share-form .filter-checklist-item .avatar { margin-right: 0.25rem; }
.files-panel-share { padding-top: 0.75rem; border-top: 1px solid var(--gray-200); }

/* Public links (V167): the owner's list in a panel, the address in small type, the public folder page. */
.files-links-list { list-style: none; margin: 0 0 0.75rem; padding: 0; display: flex; flex-direction: column; gap: 0.5rem; }
.files-links-list li { padding: 0.375rem 0; border-bottom: 1px solid var(--gray-100); }
.files-links-list li.is-gone { opacity: 0.6; }
.files-link-line { display: flex; flex-wrap: wrap; align-items: center; gap: 0.375rem 0.625rem; }
.files-link-url { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.75rem; color: var(--gray-600); flex: 1; min-width: 0; }
.files-link-form { margin: 0; }
.files-link-form-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr)); gap: 0.5rem; }
.files-links-table .files-link-url { margin-top: 0.125rem; }
.share-card--wide { max-width: 44rem; }
.share-folder-wrapper { margin-bottom: 0.75rem; max-height: 60vh; overflow: auto; }
.share-folder-table td, .share-folder-table th { padding: 0.375rem 0.5rem; font-size: 0.875rem; }
.share-folder-col-get { width: 3rem; text-align: end; }
.share-folder-size { white-space: nowrap; }

/* Attach from my files (V168): the picker over the task page, the task card's table. */
.files-picker { width: min(40rem, calc(100vw - 2rem)); }
.files-picker-host { min-height: 12rem; max-height: 60vh; overflow: auto; margin: 0.75rem 0; }
.files-picker-tabs { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-bottom: 0.5rem; }
.files-picker-crumbs { margin-bottom: 0.5rem; }
.files-picker-crumb { background: none; border: 0; padding: 0; color: var(--navy-500); cursor: pointer; font: inherit; }
.files-picker-crumb:hover { text-decoration: underline; }
.files-picker-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.125rem; }
.files-picker-folder, .files-picker-file { display: flex; align-items: center; gap: 0.5rem; width: 100%; padding: 0.375rem 0.5rem; border: 0; border-radius: 0.375rem; background: none; font: inherit; text-align: start; cursor: pointer; }
.files-picker-folder:hover, .files-picker-file:hover { background: var(--gray-100); }
.files-picker-name { flex: 1; min-width: 0; }
.files-used-in { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 0.25rem; font-size: 0.875rem; }
.task-files-table .row-cell-when { min-width: 14rem; }
