@charset "UTF-8";
:root {
  /* action — brand blue, keep the existing hue */
  --app-accent: #1D4ED8; /* was #2563EB — one step darker for text contrast */
  --app-accent-dark: #1E40AF; /* hover / active */
  --app-accent-ink: #172554; /* headline on accent tint */
  --app-accent-tint: #EFF6FF; /* active nav, filter chip, callout bg, info badge */
  --app-accent-tint-2: #DBEAFE; /* border of the above */
  --app-accent-row: #F6F9FF; /* selected table row */
  --app-navy: #0F172A; /* logo tile, bulk-action bar (== theme-color meta) */
  /* neutral ramp — cool slate, replaces the pure greys */
  --app-ink: #0F172A;
  --app-ink-2: #1E293B;
  --app-ink-3: #334155;
  --app-ink-4: #475569;
  --app-muted: #64748B;
  --app-muted-2: #94A3B8;
  --app-line-strong: #CBD5E1; /* input + secondary button border */
  --app-line: #E2E8F0; /* card border, dividers */
  --app-line-soft: #EDF1F5; /* card header divider, grid hairline */
  --app-line-softest: #F1F5F9; /* table row divider, ghost hover */
  --app-panel: #FFFFFF;
  --app-bg: #F8FAFC; /* was #efefef */
  --app-bg-2: #FAFBFD; /* table head, row hover, card footer strip */
  /* sidebar — light now, see §6 */
  --sidebar-bg: #FFFFFF;
  --sidebar-ink: #334155;
  --sidebar-muted: #94A3B8;
  /* semantic */
  --app-info-fg: #1D4ED8;
  --app-info-bg: #EFF6FF;
  --app-warn-fg: #B45309;
  --app-warn-bg: #FFFBEB;
  --app-ok-fg: #15803D;
  --app-ok-bg: #F0FDF4;
  --app-danger-fg: #B91C1C;
  --app-danger-bg: #FEF2F2;
  --app-danger-line:#FECACA;
  --app-danger-field:#FCA5A5;
  --app-neutral-fg: #475569;
  --app-neutral-bg: #F1F5F9;
  /* elevation — much flatter than today's --shadow-soft */
  --shadow-card: 0 1px 2px rgba(15, 23, 42, .04);
  --shadow-accent:0 1px 2px rgba(29, 78, 216, .22);
  --shadow-float: 0 10px 30px rgba(15, 23, 42, .28);
  --shadow-pop: 0 8px 24px rgba(15, 23, 42, .12); /* dropdowns, suggestions */
  --focus-ring: 0 0 0 3px rgba(29, 78, 216, .14);
  /* radii + control heights */
  --r-card: 12px;
  --r-control: 8px;
  --r-control-sm: 7px;
  --r-badge: 6px;
  --h-control: 36px;
  --h-control-sm: 32px;
  --h-control-xs: 30px;
  --h-icon-btn: 28px;
  /* compatibility alias — keep until every consumer of --shadow-soft moves to --shadow-card */
  --shadow-soft: var(--shadow-card);
}

@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/ibm-plex-sans-latin-400-normal.77bc02670657.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/ibm-plex-sans-latin-ext-400-normal.1bc0d8e41d21.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("fonts/ibm-plex-sans-latin-500-normal.f7016cd446d2.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("fonts/ibm-plex-sans-latin-ext-500-normal.93a0df997e00.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("fonts/ibm-plex-sans-latin-600-normal.196e3cbc5fee.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Sans";
  font-style: normal;
  font-weight: 600;
  font-display: swap;
  src: url("fonts/ibm-plex-sans-latin-ext-600-normal.8fccbdf5bf67.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/ibm-plex-mono-latin-400-normal.79936b18df9f.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url("fonts/ibm-plex-mono-latin-ext-400-normal.8450d5944de2.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("fonts/ibm-plex-mono-latin-500-normal.b1c8a895f5fd.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
  font-family: "IBM Plex Mono";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("fonts/ibm-plex-mono-latin-ext-500-normal.fa8d8838b94a.woff2") format("woff2");
  unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
:root {
  --bs-font-sans-serif: 'IBM Plex Sans', system-ui, -apple-system, 'Segoe UI', sans-serif;
  --bs-font-monospace: 'IBM Plex Mono', ui-monospace, monospace;
  --bs-body-font-size: .875rem;
  --bs-body-color: var(--app-ink);
}

body {
  background: var(--app-bg);
  color: var(--app-ink);
  min-height: 100vh;
}

.app-layout {
  min-height: 100vh;
}

.text-muted {
  color: var(--app-muted) !important;
}

.num {
  font-family: var(--bs-font-monospace);
  font-variant-numeric: tabular-nums;
}

@media (max-width: 1100px) {
  body.sidebar-lock-scroll {
    overflow: hidden;
    touch-action: pinch-zoom;
  }
  body.sidebar-lock-scroll .app-sidebar {
    touch-action: pan-y pinch-zoom;
  }
}
.app-sidebar {
  position: fixed;
  inset: 0 auto 0 0;
  width: 236px;
  height: 100vh;
  display: flex;
  flex-direction: column;
  background: var(--sidebar-bg);
  color: var(--sidebar-ink);
  border-right: 1px solid var(--app-line);
  z-index: 1030;
  visibility: visible;
  transition: transform 220ms ease;
  overscroll-behavior: contain;
}

.sidebar-backdrop {
  position: fixed;
  inset: 0;
  background: rgba(15, 23, 42, 0.38);
  opacity: 0;
  pointer-events: none;
  z-index: 1025;
  transition: opacity 180ms ease;
}

.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 18px 18px 16px;
}

.sidebar-brand-tile {
  flex: 0 0 30px;
  width: 30px;
  height: 30px;
  border-radius: 8px;
  background: var(--app-navy);
  color: #ffffff;
  box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.08);
  display: grid;
  place-items: center;
  font-size: 14px;
  font-weight: 600;
  line-height: 1;
}

.sidebar-brand-text {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
}

.sidebar-title {
  font-size: 13.5px;
  font-weight: 600;
  letter-spacing: -0.01em;
  margin: 0;
}

.sidebar-subtitle {
  color: var(--sidebar-muted);
  font-size: 11px;
  font-weight: 400;
  margin: 0;
}

.sidebar-collapse-btn {
  margin-left: auto;
  flex: 0 0 auto;
  width: var(--h-icon-btn);
  height: var(--h-icon-btn);
  display: grid;
  place-items: center;
  border: 1px solid transparent;
  border-radius: var(--r-control-sm);
  background: transparent;
  color: var(--app-muted-2);
  font-size: 13px;
  padding: 0;
  cursor: pointer;
}

.sidebar-collapse-btn:hover {
  background: var(--app-line-softest);
  color: var(--app-ink-3);
}

.sidebar-collapse-btn:focus-visible {
  outline: 1px solid var(--app-accent);
  box-shadow: var(--focus-ring);
}

.sidebar-nav {
  flex: 1 1 auto;
  min-height: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 4px 10px 10px;
  overflow-y: auto;
}

.sidebar-group-label {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--app-muted-2);
  padding: 14px 8px 6px;
}

.sidebar-nav > .sidebar-group-label:first-child {
  padding-top: 8px;
}

.sidebar-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 7px 8px;
  border-radius: 8px;
  font-size: 13px;
  font-weight: 500;
  color: var(--sidebar-ink);
  background: transparent;
  text-decoration: none;
}

.sidebar-link i {
  flex: 0 0 17px;
  font-size: 17px;
  line-height: 1;
  opacity: 0.85;
}

.sidebar-link:hover {
  background: var(--app-line-softest);
  color: var(--sidebar-ink);
  text-decoration: none;
}

.sidebar-link.active {
  background: var(--app-accent-tint);
  color: var(--app-accent);
}

.sidebar-link:focus-visible {
  outline: 1px solid var(--app-accent);
  box-shadow: var(--focus-ring);
}

.sidebar-footer {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px calc(12px + env(safe-area-inset-bottom));
  border-top: 1px solid var(--app-line-soft);
}

.sidebar-avatar {
  flex: 0 0 28px;
  width: 28px;
  height: 28px;
  border-radius: 999px;
  background: var(--app-line-softest);
  border: 1px solid var(--app-line);
  display: grid;
  place-items: center;
  font-size: 11px;
  font-weight: 600;
  color: var(--app-ink-4);
}

.sidebar-user {
  display: flex;
  flex-direction: column;
  line-height: 1.25;
  min-width: 0;
}

.sidebar-user-name {
  font-size: 12px;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.sidebar-user-role {
  font-size: 11px;
  font-weight: 400;
  color: var(--sidebar-muted);
}

.sidebar-footer .dropup {
  margin-left: auto;
}

.sidebar-user-menu-btn {
  width: var(--h-icon-btn);
  height: var(--h-icon-btn);
  display: grid;
  place-items: center;
  border: 1px solid transparent;
  border-radius: var(--r-control-sm);
  background: transparent;
  color: var(--app-muted-2);
  font-size: 15px;
  padding: 0;
  cursor: pointer;
}

.sidebar-user-menu-btn:hover {
  background: var(--app-line-softest);
  color: var(--app-ink-3);
}

.sidebar-user-menu-btn:focus-visible {
  outline: 1px solid var(--app-accent);
  box-shadow: var(--focus-ring);
}

.sidebar-footer .dropdown-menu {
  box-shadow: var(--shadow-pop);
}

body.sidebar-collapsed .app-sidebar {
  transform: translateX(-100%);
  visibility: hidden;
  transition: transform 220ms ease, visibility 0s linear 220ms;
}

@media (min-width: 1101px) {
  .sidebar-backdrop {
    display: none;
  }
}
@media (max-width: 1100px) {
  .app-sidebar {
    transform: translateX(-100%);
    visibility: hidden;
    transition: transform 220ms ease, visibility 0s linear 220ms;
  }
  body.sidebar-mobile-open .app-sidebar {
    transform: translateX(0);
    visibility: visible;
    transition: transform 220ms ease;
    box-shadow: var(--shadow-float);
  }
  body.sidebar-mobile-open .sidebar-backdrop {
    opacity: 1;
    pointer-events: auto;
  }
  .sidebar-collapse-btn {
    display: none;
  }
  .sidebar-nav {
    padding-bottom: calc(10px + env(safe-area-inset-bottom));
  }
}
.app-main {
  margin-left: 236px;
  transition: margin-left 220ms ease;
}

.app-topbar {
  position: sticky;
  top: 0;
  z-index: 1020;
  height: 56px;
  background: rgba(255, 255, 255, 0.88);
  border-bottom: 1px solid var(--app-line);
  backdrop-filter: blur(8px);
  padding: 0 24px;
  display: flex;
  align-items: center;
}

.app-topbar-inner {
  max-width: 1400px;
  width: 100%;
  margin: 0 auto;
  display: flex;
  align-items: center;
  gap: 16px;
  justify-content: space-between;
}

/* Both topbar groups must be allowed to shrink below their content width,
   otherwise the default `min-width: auto` of a flex child turns a long page
   title or the search field into horizontal page overflow on narrow phones. */
.app-topbar-inner > * {
  min-width: 0;
}

.topbar-crumbs {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
  font-size: 12.5px;
  font-weight: 400;
  color: var(--app-muted-2);
}

.topbar-crumbs a {
  color: inherit;
  text-decoration: none;
}

.topbar-crumbs a:hover {
  color: var(--app-ink-3);
}

.topbar-crumb-sep {
  font-size: 13px;
  line-height: 1;
  flex: 0 0 auto;
}

.topbar-crumb-current {
  /* Rendered as the page <h1> on screens that have not adopted
     components/page_header.html, and as a plain <span> on those that have (see
     base.html). Reset the heading defaults either way so it reads as the last
     breadcrumb segment, not as a visible page title. */
  margin: 0;
  font-size: 12.5px;
  line-height: 1.4;
  color: var(--app-ink-2);
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar-search-form {
  position: relative;
  width: 280px;
  min-width: 200px;
}

.topbar-search-form .form-control {
  background: var(--app-panel);
  padding-right: 2rem;
}

.topbar-search-kbd {
  position: absolute;
  top: 50%;
  right: 8px;
  transform: translateY(-50%);
  pointer-events: none;
  font-family: var(--bs-font-monospace);
  font-size: 10.5px;
  font-weight: 500;
  color: var(--app-muted-2);
  background: var(--app-panel);
  border: 1px solid var(--app-line);
  border-radius: 4px;
  padding: 1px 5px;
}

.topbar-search-suggestions {
  position: absolute;
  top: calc(100% + 0.35rem);
  left: 0;
  right: 0;
  z-index: 1090;
  background: #fff;
  border: 1px solid var(--app-line);
  border-radius: var(--r-control);
  box-shadow: var(--shadow-pop);
  max-height: min(22rem, 55vh);
  overflow-y: auto;
}

.topbar-search-suggestion {
  display: flex;
  align-items: flex-start;
  gap: 0.55rem;
  padding: 0.6rem 0.7rem;
  text-decoration: none;
  color: inherit;
  border-bottom: 1px solid var(--app-line-soft);
}

.topbar-search-suggestion:last-child {
  border-bottom: 0;
}

.topbar-search-suggestion:hover,
.topbar-search-suggestion.is-active {
  background: var(--app-accent-tint);
}

.topbar-search-suggestion-main {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.topbar-search-suggestion-main strong {
  font-size: 0.88rem;
  line-height: 1.25;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar-search-suggestion-main small {
  color: var(--app-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topbar-search-suggestion-empty {
  padding: 0.65rem 0.75rem;
  color: var(--app-muted);
  font-size: 0.88rem;
}

/* Handoff §6.4 retires .quick-actions-panel (page actions belong in the page header).
   Removal is deferred to the screen PRs because customer_detail.html and
   project_detail.html still render this panel — dropping the styles here first would
   leave those two screens visibly broken. */
.quick-actions-panel {
  position: static;
}

.quick-actions-panel .card-body .btn {
  justify-content: flex-start;
}

@media (min-width: 1200px) {
  .quick-actions-panel {
    position: sticky;
    top: 5.6rem;
  }
}
.app-content {
  max-width: 1400px;
  margin: 0 auto;
  padding: 28px 32px 56px;
}

body.sidebar-collapsed .app-main {
  margin-left: 0;
}

@media (max-width: 1100px) {
  .app-main {
    margin-left: 0;
  }
}
@media (max-width: 992px) {
  .app-topbar {
    padding: 0 1rem;
  }
  .topbar-search-form {
    width: auto;
    min-width: 170px;
    max-width: 220px;
  }
  .topbar-search-kbd {
    display: none;
  }
  .app-content {
    padding: 20px 16px 40px;
  }
}
@media (max-width: 576px) {
  /* Narrow phones (down to 320px): the search field is the only elastic element
     left in the topbar, so it must be free to shrink to whatever space remains
     after the toggle and the current breadcrumb segment. Any min-width here
     forces the document wider than the viewport. */
  .topbar-search-form {
    width: auto;
    min-width: 0;
    max-width: none;
    flex: 1 1 120px;
  }
  .topbar-search-kbd {
    display: none;
  }
  /* Keep only the current page segment; the "Jericho" root crumb and its
     chevron are pure decoration at this width. */
  .topbar-crumb-root,
  .topbar-crumb-sep {
    display: none;
  }
}
.surface-card {
  background: var(--app-panel);
  border: 1px solid var(--app-line);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
}

.surface-card .card-header {
  background: #ffffff;
  border-bottom: 1px solid var(--app-line-soft);
  padding: 0.875rem 1.125rem;
  font-size: 0.84375rem;
  font-weight: 600;
}

.badge-soft {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  border-radius: var(--r-badge);
  padding: 0.23rem 0.58rem;
  font-size: 0.71875rem;
  font-weight: 500;
  border: none;
  background: var(--app-neutral-bg);
  color: var(--app-neutral-fg);
}

.badge-soft--info {
  background: var(--app-info-bg);
  color: var(--app-info-fg);
}

.badge-soft--warn {
  background: var(--app-warn-bg);
  color: var(--app-warn-fg);
}

.badge-soft--ok {
  background: var(--app-ok-bg);
  color: var(--app-ok-fg);
}

.badge-soft--danger {
  background: var(--app-danger-bg);
  color: var(--app-danger-fg);
}

.badge-soft--neutral {
  background: var(--app-neutral-bg);
  color: var(--app-neutral-fg);
}

.dot-status {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.8125rem;
}

.dot-status__dot {
  width: 6px;
  height: 6px;
  min-width: 6px;
  border-radius: 50%;
  display: inline-block;
  background: var(--app-line-strong);
}

.dot-status--ok {
  color: var(--app-ok-fg);
}

.dot-status--ok .dot-status__dot {
  background: var(--app-ok-fg);
}

.dot-status--neutral {
  color: var(--app-muted-2);
}

.btn {
  /* min-height, not height: a two-line label must be able to grow instead of clipping.
     The size goes through --bs-btn-font-size rather than a literal font-size so a
     variant (or a one-off override) can still redefine it. This sheet loads after
     Bootstrap, so the size variants below have to restate their own value — otherwise
     this default would override Bootstrap's .btn-sm / .btn-lg on equal specificity. */
  min-height: var(--h-control);
  border-radius: var(--r-control);
  --bs-btn-font-size: 0.8125rem;
  font-weight: 500;
}

.btn-sm {
  min-height: var(--h-control-xs);
  --bs-btn-font-size: 0.78125rem;
}

.btn-lg {
  min-height: 2.75rem;
  --bs-btn-font-size: 0.9375rem;
}

/* in-card controls are one step taller than a bare .btn-sm (handoff §6.3: 32px) */
.surface-card .btn-sm {
  min-height: var(--h-control-sm);
}

.btn:focus-visible {
  outline: 2px solid var(--app-accent);
  outline-offset: 1px;
  box-shadow: var(--focus-ring);
}

.btn-accent {
  --bs-btn-color: #fff;
  --bs-btn-bg: var(--app-accent);
  --bs-btn-border-color: var(--app-accent);
  --bs-btn-hover-color: #fff;
  --bs-btn-hover-bg: var(--app-accent-dark);
  --bs-btn-hover-border-color: var(--app-accent-dark);
  --bs-btn-focus-shadow-rgb: 29, 78, 216;
  --bs-btn-active-color: #fff;
  --bs-btn-active-bg: var(--app-accent-dark);
  --bs-btn-active-border-color: var(--app-accent-dark);
  box-shadow: var(--shadow-accent);
}

.btn-outline-ink {
  /* secondary action — no color inversion on hover, just a slightly stronger border */
  --bs-btn-color: var(--app-ink-2);
  --bs-btn-border-color: var(--app-line-strong);
  --bs-btn-hover-color: var(--app-ink-2);
  --bs-btn-hover-bg: var(--app-bg);
  --bs-btn-hover-border-color: var(--app-muted-2);
  --bs-btn-active-color: var(--app-ink-2);
  --bs-btn-active-bg: var(--app-bg);
  --bs-btn-active-border-color: var(--app-muted-2);
}

.btn-quiet {
  /* quiet action, menu inside a card */
  --bs-btn-color: var(--app-ink-4);
  --bs-btn-bg: transparent;
  --bs-btn-border-color: transparent;
  --bs-btn-hover-bg: var(--app-line-softest);
  --bs-btn-hover-color: var(--app-ink);
  --bs-btn-hover-border-color: transparent;
}

.btn-outline-danger-soft {
  /* destructive, not shouty */
  --bs-btn-color: var(--app-danger-fg);
  --bs-btn-bg: #fff;
  --bs-btn-border-color: var(--app-danger-line);
  --bs-btn-hover-bg: var(--app-danger-bg);
  --bs-btn-hover-color: var(--app-danger-fg);
  --bs-btn-hover-border-color: var(--app-danger-field);
}

.btn-row-icon {
  /* row-level icon button only, always paired with aria-label */
  width: var(--h-icon-btn);
  height: var(--h-icon-btn);
  display: inline-grid;
  place-items: center;
  border: 1px solid transparent;
  border-radius: var(--r-control-sm);
  background: transparent;
  color: var(--app-muted-2);
}
.btn-row-icon:hover {
  background: var(--app-line-softest);
  color: var(--app-accent);
  border-color: var(--app-line);
}

.btn.btn-icon-square {
  /* square icon-only control in a page header — the "⋯" overflow (§8.3).
     Always carries an aria-label; a bare icon is only allowed for overflow menus. */
  width: var(--h-control);
  padding: 0;
  display: inline-grid;
  place-items: center;
}

@media (max-width: 768px) {
  .btn.btn-sm {
    min-height: 2.75rem;
    min-width: 2.75rem;
    padding: 0.48rem 0.72rem;
  }
  .btn.btn-sm > i:only-child {
    font-size: 1.1rem;
  }
  .card-header .btn.btn-sm,
  .list-group-item .btn.btn-sm,
  .table .btn.btn-sm,
  .pwa-install-prompt .btn.btn-sm {
    min-height: 2.75rem;
    min-width: 2.75rem;
  }
  .d-flex.gap-2:has(> .btn),
  .d-inline-flex.gap-2:has(> .btn) {
    gap: 0.6rem !important;
  }
}
.table {
  --bs-table-bg: #ffffff;
  --bs-table-border-color: var(--app-line-softest);
  /* zebra striping is retired — keep the variable equal to the row bg so
     .table-striped stays visually neutral without touching templates */
  --bs-table-striped-bg: #ffffff;
  --bs-table-hover-bg: var(--app-bg-2);
  margin-bottom: 0;
}

.table > :not(caption) > * > * {
  padding: 0.6875rem 0.75rem;
}

.table > :not(caption) > * > *:first-child {
  padding-left: 1.125rem;
}

.table > tbody > tr.is-selected > * {
  --bs-table-accent-bg: var(--app-accent-row);
  background-color: var(--app-accent-row);
}

/* Bootstrap paints every cell with `background-color: var(--bs-table-bg)`, so a
   `background` on <thead> is covered up. Redefine the variable at thead scope instead —
   the head cells then inherit the tinted value while tbody keeps the white default. */
.table > thead {
  --bs-table-bg: var(--app-bg-2);
}

.table > tbody {
  font-size: 0.8125rem;
}

.table thead th {
  font-size: 0.65625rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--app-muted-2);
}

.table thead th.is-sorted {
  color: var(--app-muted);
}

.table a:not(.btn) {
  color: var(--app-accent);
  font-weight: 500;
  text-decoration: none;
}

.table a:not(.btn):hover {
  color: var(--app-accent-dark);
  text-decoration: underline;
}

/* Bootstrap's lg breakpoint, not md: seven-column lists (Zákazníci) squish unreadably
   through the 769–991px band, where a tablet in portrait is far better served by the
   card layout. Below this width every .table-mobile-cards table becomes stacked cards. */
@media (max-width: 991.98px) {
  /* Only the wrappers whose table actually turns into cards give up their scroller.
     A card table is a stack of full-width blocks, so it has nothing to scroll, while
     `overflow-x: auto` would clip the dropdowns and popovers that open out of a card.
     Tables that stay tabular (worker rentals, performance reports, the duplicate
     finder) keep Bootstrap's scroller — dropping it there would push their ten columns
     off-screen with no way to reach them. `:has` is the only way to key the wrapper on
     its own child; where it is unsupported the wrapper simply keeps scrolling, which
     costs a clipped dropdown rather than unreachable content. */
  .table-responsive:has(> .table-mobile-cards) {
    overflow: visible;
  }
  .table-mobile-cards {
    border-collapse: separate;
    border-spacing: 0;
  }
  .table-mobile-cards thead {
    display: none;
  }
  .table-mobile-cards tbody,
  .table-mobile-cards tr,
  .table-mobile-cards td {
    display: block;
    width: 100%;
  }
  .table-mobile-cards tr {
    margin-bottom: 0.7rem;
    padding: 0.82rem 0.95rem;
    border: 1px solid var(--app-line);
    border-radius: 0.72rem;
    background: #fff;
  }
  .table-mobile-cards.table-striped > tbody > tr:nth-of-type(odd) {
    background: var(--app-bg-2);
  }
  .table-mobile-cards td {
    border: 0;
    padding: 0.5rem 0.2rem;
    text-align: left !important;
    display: grid;
    grid-template-columns: minmax(5.8rem, 7.2rem) minmax(0, 1fr);
    align-items: start;
    gap: 0.65rem;
  }
  .table-mobile-cards td::before {
    content: attr(data-label);
    color: var(--app-muted);
    font-size: 0.75rem;
    font-weight: 650;
    letter-spacing: 0.01em;
    flex-shrink: 0;
    min-width: 6.5rem;
  }
  .table-mobile-cards td[data-label=""]::before {
    display: none;
  }
  .table-mobile-cards td[data-label=""] {
    display: flex;
    justify-content: flex-end;
    padding-top: 0.65rem;
  }
  .table-mobile-cards.table-striped > tbody > tr:nth-of-type(odd) > * {
    --bs-table-accent-bg: transparent;
    background-color: transparent;
  }
  /* The desktop ":first-child" rule above (`.table > :not(caption) > * > *:first-child`)
     out-specificities the plain `.table-mobile-cards td` padding shorthand and bleeds an
     extra 1.125rem indent onto whichever cell happens to render first in the card. That
     is invisible while the (label-less) select column leads every row, but a table that
     hides it for a subset of rows (e.g. the archived worker list, which drops bulk
     selection) exposes the next cell's label with a stray left indent. Reset it here with
     matching specificity so every card-mode label lines up regardless of which column
     leads. */
  .table.table-mobile-cards td:first-child {
    padding-left: 0.2rem;
  }
}
/* Identity cell of a list row (§8.2): link on the first line, IČO (Mono, muted) below. */
.cell-identity {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.cell-identity__title {
  display: flex;
  align-items: center;
  /* Wrap, don't clip: a status badge (e.g. "Archivovaný") next to a short name still
     overflows a 320px card without this — the link and badge are flex items with no
     natural shrink point, so they push past the card's right edge instead of dropping
     to a second line. */
  flex-wrap: wrap;
  gap: 0.4rem;
  min-width: 0;
}

.cell-identity__meta {
  font-size: 0.71875rem;
  color: var(--app-muted-2);
}

.surface-card a[href^="mailto:"],
.surface-card a[href^="tel:"] {
  color: var(--app-accent);
  font-weight: 600;
  text-decoration: none;
}

.surface-card a[href^="mailto:"]:hover,
.surface-card a[href^="tel:"]:hover {
  color: var(--app-accent-dark);
  text-decoration: underline;
}

.pagination {
  display: flex;
  justify-content: center;
  align-items: center;
  gap: 0.35rem;
  list-style: none;
  padding-left: 0;
  margin-top: 1rem;
  margin-bottom: 0;
}

.pagination .page-link,
.pagination > li > a,
.pagination > li > span {
  display: inline-block;
  border-radius: var(--r-control-sm);
  border: 1px solid var(--app-line);
  color: var(--app-ink-2);
  background: #ffffff;
  min-width: 2rem;
  height: var(--h-control-sm);
  line-height: calc(var(--h-control-sm) - 2px);
  text-align: center;
  text-decoration: none;
  padding: 0 0.7rem;
  font-family: var(--bs-font-monospace);
}

.pagination .page-link:hover,
.pagination > li > a:hover {
  color: var(--app-ink);
  background: var(--app-bg-2);
  border-color: var(--app-line);
  text-decoration: none;
}

.pagination .page-item.active .page-link,
.pagination > li.active > a,
.pagination > li.active > span {
  color: #ffffff;
  background: var(--app-accent);
  border-color: var(--app-accent);
}

.pagination .page-item.disabled .page-link {
  color: var(--app-muted-2);
  background: var(--app-bg-2);
  border-color: var(--app-line);
}

/* Card footer strip of a list table (§8.2): page size left, pager right.
   Rendered by templates/django_tables2/jericho_table.html on every list. */
.table-footer {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  padding: 12px 16px;
  border-top: 1px solid var(--app-line-soft);
  background: var(--app-bg-2);
}

.table-footer__label {
  font-size: 0.78125rem;
  color: var(--app-muted);
}

.table-footer__page-size {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.table-footer__size-btn {
  height: var(--h-control-xs);
  padding: 0 0.625rem;
  border: 1px solid var(--app-line);
  border-radius: var(--r-control-sm);
  background: #ffffff;
  color: var(--app-ink-2);
  font-size: 0.78125rem;
  font-weight: 500;
}

.table-footer__pager {
  margin-left: auto;
  min-width: 0;
}

.table-footer .pagination {
  margin-top: 0;
  justify-content: flex-end;
  flex-wrap: wrap;
}

.table-footer .page-link--word {
  font-family: var(--bs-font-sans-serif);
  font-size: 0.78125rem;
  font-weight: 500;
}

.table-footer .page-item.disabled .page-link--gap {
  border-color: transparent;
  background: transparent;
}

.app-form p {
  margin-bottom: 1rem;
}

.app-form label {
  font-size: 0.75rem;
  font-weight: 500;
  margin-bottom: 0.35rem;
  display: block;
}

.form-control,
.form-select {
  border-color: var(--app-line-strong);
  border-radius: var(--r-control);
  min-height: var(--h-control);
}

.form-control:focus,
.form-select:focus {
  border-color: var(--app-accent);
  box-shadow: var(--focus-ring);
}

.app-form .form-text {
  font-size: 0.71875rem;
  color: var(--app-muted-2);
}

/* Filtering: toolbar (always visible) + collapsible panel overlaying the table.
   Filter chips live here too — they are part of the same toolbar row. */
.filter-wrap {
  position: relative;
}

.filter-toolbar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.75rem 1rem;
  border-bottom: 1px solid var(--app-line-soft);
}

.filter-search {
  position: relative;
  width: 250px;
  max-width: 100%;
}

.filter-search__icon {
  position: absolute;
  top: 50%;
  left: 0.625rem;
  transform: translateY(-50%);
  font-size: 0.875rem;
  color: var(--app-muted-2);
  pointer-events: none;
}

.filter-search-input.form-control {
  height: var(--h-control-sm);
  min-height: var(--h-control-sm);
  padding-left: 2rem;
  border-radius: var(--r-control);
  background: var(--app-bg);
  font-size: 0.8125rem;
}

.filter-toggle.btn {
  height: var(--h-control-sm);
  min-height: var(--h-control-sm);
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  border-radius: var(--r-control);
}

.filter-count-pill {
  display: inline-grid;
  place-items: center;
  min-width: 1.125rem;
  height: 1.125rem;
  padding: 0 0.25rem;
  border-radius: var(--r-badge);
  background: var(--app-accent);
  color: #fff;
  font-size: 0.6875rem;
  line-height: 1;
}

.filter-chips {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem;
}

.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.375rem;
  height: var(--h-control-sm);
  padding: 0 0.375rem 0 0.625rem;
  border: 1px solid var(--app-accent-tint-2);
  border-radius: var(--r-control);
  background: var(--app-accent-tint);
  color: var(--app-accent);
  font-size: 0.75rem;
}

.filter-chip-remove {
  display: inline-grid;
  place-items: center;
  width: 1.125rem;
  height: 1.125rem;
  border-radius: var(--r-badge);
  color: inherit;
  text-decoration: none;
  line-height: 1;
}

.filter-chip-remove:hover {
  background: var(--app-accent-tint-2);
  color: var(--app-accent-dark);
}

.filter-toolbar__count {
  margin-left: auto;
  font-size: 0.75rem;
  color: var(--app-muted);
}

.filter-toolbar__unfiltered {
  color: var(--app-muted-2);
}

.filter-panel-wrap {
  position: absolute;
  z-index: 20;
  top: 100%;
  left: 0;
  width: 100%;
}

.filter-panel {
  border-radius: var(--r-card);
  border: 1px solid var(--app-line);
  background: var(--app-panel);
  box-shadow: var(--shadow-pop);
}

.filter-group + .filter-group {
  margin-top: 1rem;
}

.filter-group__label {
  margin-bottom: 0.5rem;
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--app-muted-2);
}

.filter-label {
  margin-bottom: 0.3rem;
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--app-ink-3);
}

.filter-panel .form-control,
.filter-panel .form-select {
  min-height: var(--h-control);
  border-radius: var(--r-control);
  font-size: 0.8125rem;
}

.filter-panel .form-control:focus,
.filter-panel .form-select:focus {
  border-color: var(--app-accent);
  box-shadow: var(--focus-ring);
}

.filter-panel__footer {
  display: flex;
  justify-content: flex-end;
  padding-top: 1rem;
}

.filter-reset-link {
  color: var(--app-accent);
  text-decoration: none;
  font-weight: 500;
  font-size: 0.8125rem;
}

.filter-reset-link:hover {
  color: var(--app-accent-dark);
  text-decoration: underline;
}

.app-form .app-field-invalid .form-control,
.app-form .app-field-invalid .form-select,
.app-form .app-field-invalid .form-check-input {
  border-color: var(--app-danger-field);
  background-color: var(--app-danger-bg);
}

.app-form .app-field-invalid .form-control:focus,
.app-form .app-field-invalid .form-select:focus,
.app-form .app-field-invalid .form-check-input:focus {
  border-color: var(--app-danger-field);
  box-shadow: 0 0 0 0.2rem rgba(185, 28, 28, 0.15);
}

.app-form .app-field-invalid .form-text,
.app-form .app-field-invalid .invalid-feedback {
  color: var(--app-danger-fg);
}

.app-form-errors {
  border-left: 0.25rem solid var(--app-danger-fg);
}

.sticky-board {
  position: relative;
  min-height: 680px;
  border: 1px dashed #b9b9b9;
  border-radius: 0.85rem;
  background-color: #fcfcfc;
  background-image: linear-gradient(to right, rgba(0, 0, 0, 0.04) 1px, transparent 1px), linear-gradient(to bottom, rgba(0, 0, 0, 0.04) 1px, transparent 1px);
  background-size: 24px 24px;
  overflow: auto;
  padding: 0.75rem;
  resize: vertical;
  max-height: 85vh;
}

.sticky-note {
  position: absolute;
  width: 260px;
  min-height: 170px;
  background: var(--note-bg, #ffe08b);
  border: 1px solid #d4b35f;
  border-radius: 0.6rem;
  box-shadow: 0 10px 18px rgba(0, 0, 0, 0.15);
  padding: 0.62rem 0.62rem 0.55rem;
  cursor: grab;
  user-select: none;
  transition: box-shadow 120ms ease;
}

.sticky-note:active {
  cursor: grabbing;
}

.sticky-note.is-dragging {
  box-shadow: 0 16px 30px rgba(0, 0, 0, 0.24);
  z-index: 5;
}

.sticky-note__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
  margin-bottom: 0.5rem;
  color: #5f4b22;
  font-size: 0.74rem;
  font-weight: 600;
}

.sticky-note__body {
  color: #252018;
  font-size: 0.92rem;
  line-height: 1.35;
  margin-bottom: 0.6rem;
  max-height: 4.1em;
  overflow: hidden;
  position: relative;
}

.sticky-note__body::after {
  content: "";
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 1.5em;
  background: linear-gradient(transparent, var(--note-bg, #ffe08b));
  pointer-events: none;
}

.sticky-note.is-expanded .sticky-note__body {
  max-height: none;
  overflow: visible;
}

.sticky-note.is-expanded .sticky-note__body::after {
  display: none;
}

.sticky-note__body p:last-child {
  margin-bottom: 0;
}

.sticky-note__body img {
  max-width: 100%;
  height: auto;
  border-radius: 0.35rem;
}

.sticky-note__actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.45rem;
}

.mobile-notes-list {
  display: grid;
  gap: 0.7rem;
}

.mobile-note-card {
  border: 1px solid #d4b35f;
  border-radius: 0.7rem;
  background: var(--note-bg, #ffe08b);
  padding: 0.7rem;
  box-shadow: 0 6px 12px rgba(0, 0, 0, 0.1);
}

.mobile-note-card__meta {
  display: flex;
  justify-content: space-between;
  gap: 0.6rem;
  margin-bottom: 0.5rem;
  color: #5f4b22;
  font-size: 0.76rem;
  font-weight: 600;
}

.mobile-note-card__body {
  color: #252018;
  font-size: 0.92rem;
  line-height: 1.4;
  margin-bottom: 0.6rem;
}

.mobile-note-card__body p:last-child {
  margin-bottom: 0;
}

.mobile-note-card__body img {
  max-width: 100%;
  height: auto;
  border-radius: 0.35rem;
}

.mobile-note-card__actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.45rem;
}

.notes-view-toggle {
  display: flex;
  gap: 0.2rem;
  margin-right: 0.5rem;
}
.notes-view-toggle .btn {
  padding: 0.15rem 0.42rem;
  font-size: 0.82rem;
  line-height: 1.4;
}

.notes-list-view table {
  width: 100%;
  border-collapse: collapse;
}
.notes-list-view th {
  font-size: 0.78rem;
  font-weight: 600;
  color: #6c757d;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 0.45rem 0.75rem;
  border-bottom: 2px solid #dee2e6;
  text-align: left;
  white-space: nowrap;
}
.notes-list-view td {
  padding: 0.5rem 0.75rem;
  border-bottom: 1px solid #eee;
  font-size: 0.88rem;
  vertical-align: middle;
}
.notes-list-view tr:hover {
  background-color: #f8f9fa;
}

.notes-list__color {
  width: 14px;
  height: 14px;
  border-radius: 50%;
  display: inline-block;
  border: 1px solid rgba(0, 0, 0, 0.15);
  vertical-align: middle;
}

.notes-list__text {
  color: #252018;
  max-width: 420px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Dashboard (handoff §8.1) — KPI row, two-column body, sticky interaction feed.
   Every rule here is dashboard-scoped; no other screen uses .dashboard-* classes. */
/* ---------- KPI row ---------- */
.dashboard-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 12px;
  padding-bottom: 16px;
}

.dashboard-kpi {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-width: 0;
  padding: 16px;
  background: var(--app-panel);
  border: 1px solid var(--app-line);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
  text-decoration: none;
}
.dashboard-kpi:hover {
  border-color: var(--app-line-strong);
  text-decoration: none;
}
.dashboard-kpi:focus-visible {
  outline: 2px solid var(--app-accent);
  outline-offset: 2px;
}

.dashboard-kpi__label {
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--app-muted-2);
}

.dashboard-kpi__value {
  font-size: 1.6875rem;
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--app-ink);
}

.dashboard-kpi__hint {
  font-size: 0.71875rem;
  font-weight: 400;
  color: var(--app-muted);
}

/* ---------- body grid ---------- */
.dashboard-body {
  display: grid;
  grid-template-columns: minmax(420px, 1.55fr) minmax(300px, 1fr);
  gap: 16px;
  align-items: start;
}

.dashboard-body__main {
  display: flex;
  flex-direction: column;
  gap: 16px;
  min-width: 0;
}

.dashboard-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
  gap: 16px;
}

/* ---------- card chrome ---------- */
.dashboard-card {
  overflow: hidden;
  min-width: 0;
}

.dashboard-card__header {
  display: flex;
  /* The header must wrap at every width, not only on phones: cards in .dashboard-pair
     get roughly 285px each around a 1280px viewport, which is narrower than
     title + subtitle + action laid out on one line. Without wrapping the action is
     clipped by .dashboard-card { overflow: hidden }. */
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  row-gap: 8px;
  padding: 14px 18px;
  border-bottom: 1px solid var(--app-line-soft);
}

.dashboard-card__title {
  margin: 0;
  font-size: 0.84375rem;
  font-weight: 600;
  color: var(--app-ink);
  min-width: 0;
}

.dashboard-card__count {
  font-size: 0.6875rem;
  font-weight: 500;
  color: var(--app-muted);
  background: var(--app-line-softest);
  border-radius: 99px;
  padding: 2px 8px;
}

.dashboard-card__subtitle {
  font-size: 0.71875rem;
  color: var(--app-muted-2);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dashboard-card__link {
  margin-left: auto;
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 0.78125rem;
  font-weight: 500;
  white-space: nowrap;
}

.dashboard-card__action {
  margin-left: auto;
  white-space: nowrap;
}

/* ---------- active projects table ---------- */
.dashboard-table-scroll {
  overflow-x: auto;
}

.dashboard-table {
  min-width: 440px;
  margin: 0;
}

.dashboard-table__action-col {
  width: 44px;
}

.dashboard-table__name {
  font-size: 0.8125rem;
  font-weight: 500;
}

.dashboard-table__muted {
  color: var(--app-ink-4);
}

.dashboard-table td.num {
  color: var(--app-muted);
}

/* ---------- generic row lists (watchdog, stalled, tasks) ---------- */
.dashboard-rows {
  display: flex;
  flex-direction: column;
}

.dashboard-row {
  display: flex;
  align-items: baseline;
  gap: 12px;
  padding: 10px 18px;
  border-bottom: 1px solid var(--app-line-softest);
  text-decoration: none;
}
.dashboard-row:last-child {
  border-bottom: 0;
}
.dashboard-row:hover {
  background: var(--app-bg-2);
  text-decoration: none;
}

.dashboard-row--stacked {
  flex-wrap: wrap;
  align-items: center;
  row-gap: 2px;
}

/* Two visual lines: "name … side" on the first, the meta line underneath. The meta
   span sits between them in source order (it belongs to the name for screen readers),
   so it is re-ordered here instead of being moved in the template. */
.dashboard-row--stacked .dashboard-row__meta {
  order: 1;
}

.dashboard-row--stacked .dashboard-row__side {
  margin-left: auto;
}

.dashboard-row__name {
  flex: 1;
  min-width: 0;
  font-size: 0.78125rem;
  font-weight: 500;
  color: var(--app-ink-2);
}

.dashboard-row__name--ellipsis {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dashboard-row__meta {
  flex: 1 0 100%;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font-size: 0.71875rem;
  color: var(--app-muted-2);
}

.dashboard-row__side {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  flex: 0 0 auto;
}

.dashboard-row__countdown {
  font-size: 0.71875rem;
  font-weight: 500;
  color: var(--app-accent);
}

.dashboard-row__date {
  flex: 0 0 auto;
  font-size: 0.71875rem;
  color: var(--app-muted);
}

.dashboard-row__date--warn {
  color: var(--app-warn-fg);
}

/* ---------- my tasks ---------- */
.dashboard-task {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--app-line-softest);
}
.dashboard-task:last-child {
  border-bottom: 0;
}
.dashboard-task:hover {
  background: var(--app-bg-2);
}

.dashboard-task__toggle {
  flex: 0 0 auto;
  display: flex;
  margin: 0;
}

/* Prototype §8.1: a 16px completion box. It is a submit <button> with
   role="checkbox" (see dashboard/home.html) so it needs no JavaScript; the
   check glyph only shows once the task is done, i.e. on hover/checked. */
.dashboard-task__check {
  width: 16px;
  height: 16px;
  padding: 0;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--app-line);
  border-radius: 4px;
  background: var(--app-panel);
  color: transparent;
  font-size: 0.6875rem;
  line-height: 1;
  cursor: pointer;
}
.dashboard-task__check:hover {
  border-color: var(--app-accent);
  color: var(--app-accent);
}
.dashboard-task__check[aria-checked=true] {
  background: var(--app-accent);
  border-color: var(--app-accent);
  color: #fff;
}

.dashboard-task__lead {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
}

.dashboard-task__lead .dashboard-row__name,
.dashboard-task__lead .dashboard-row__meta {
  flex: 0 1 auto;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dashboard-task__lead .dashboard-row__name {
  font-size: 0.8125rem;
}

.dashboard-task__due {
  flex: 0 0 auto;
  font-size: 0.75rem;
  color: var(--app-muted);
}

/* ---------- interaction feed ---------- */
.dashboard-feed-card {
  /* 72px = 56px fixed topbar (.app-topbar height in layout/_main.scss) + 16px gap.
     Keep in sync with that height. */
  position: sticky;
  top: 72px;
}

.dashboard-feed {
  display: flex;
  flex-direction: column;
  /* 640px is the design cap; on short viewports the scroll area must shrink instead,
     otherwise the sticky card runs past the bottom of the screen and its last rows
     can never be scrolled into view. Subtracted: 72px sticky offset + 48px card
     header + 24px breathing room below the card. */
  max-height: min(640px, 100vh - 144px);
  overflow-y: auto;
}

.dashboard-feed__item {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--app-line-softest);
  text-decoration: none;
  min-width: 0;
}
.dashboard-feed__item:last-child {
  border-bottom: 0;
}
.dashboard-feed__item:hover {
  background: var(--app-bg-2);
  text-decoration: none;
}

.dashboard-feed__top {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.dashboard-feed__kind {
  font-size: 0.6875rem;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: var(--app-ink-4);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dashboard-feed__time {
  margin-left: auto;
  flex: 0 0 auto;
  font-size: 0.6875rem;
  color: var(--app-muted-2);
}

.dashboard-feed__subject {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--app-ink);
}

.dashboard-feed__meta {
  font-size: 0.71875rem;
  color: var(--app-muted-2);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------- interaction type dots ---------- */
.dashboard-dot {
  width: 6px;
  height: 6px;
  flex: 0 0 6px;
  border-radius: 99px;
  background: var(--app-muted);
}

.dashboard-dot--accent {
  background: var(--app-accent);
}

.dashboard-dot--warn {
  background: var(--app-warn-fg);
}

.dashboard-dot--visit {
  /* violet has no semantic token — the palette in §4 carries no fifth accent, and this
     one literal only distinguishes "osobní návštěva" in the feed legend. */
  background: #7C3AED;
}

.dashboard-dot--muted {
  background: var(--app-muted);
}

/* ---------- responsive ---------- */
@media (max-width: 1200px) {
  .dashboard-body {
    grid-template-columns: minmax(0, 1fr);
  }
  .dashboard-feed-card {
    position: static;
  }
}
@media (max-width: 768px) {
  .dashboard-feed {
    max-height: none;
  }
}
.crm-calendar {
  min-height: 680px;
}

.calendar-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
}

.calendar-legend__item {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.86rem;
  color: #515151;
}

.calendar-legend__dot {
  width: 0.75rem;
  height: 0.75rem;
  border-radius: 50%;
  display: inline-block;
}

.calendar-legend__dot--google {
  background: #212529;
}

.calendar-legend__dot--crm {
  background: #2563EB;
}

.calendar-legend__dot--unsynced {
  background: #b98200;
}

.calendar-legend__dot--task {
  background: #0f766e;
}

.fc .calendar-event-google {
  background: #2f2f2f;
  border-color: #2f2f2f;
  color: #ffffff;
}

.fc .calendar-event-crm {
  background: #2563EB;
  border-color: #2563EB;
  color: #ffffff;
}

.fc .calendar-event-unsynced {
  background: #b98200;
  border-color: #b98200;
  color: #ffffff;
}

.fc .calendar-event-task {
  background: #0f766e;
  border-color: #0f766e;
  color: #ffffff;
}

.fc .calendar-event-google .fc-event-title,
.fc .calendar-event-crm .fc-event-title,
.fc .calendar-event-unsynced .fc-event-title,
.fc .calendar-event-task .fc-event-title,
.fc .calendar-event-google .fc-event-time,
.fc .calendar-event-crm .fc-event-time,
.fc .calendar-event-unsynced .fc-event-time,
.fc .calendar-event-task .fc-event-time {
  color: #ffffff;
}

.fc .calendar-event-google a,
.fc .calendar-event-crm a,
.fc .calendar-event-unsynced a,
.fc .calendar-event-task a {
  color: #ffffff !important;
  text-decoration: none;
}

.fc .calendar-event-google a:hover,
.fc .calendar-event-crm a:hover,
.fc .calendar-event-unsynced a:hover,
.fc .calendar-event-task a:hover {
  color: #f3f3f3 !important;
}

.fc .fc-event {
  min-height: 2rem;
}

.fc .fc-daygrid-event,
.fc .fc-timegrid-event,
.fc .fc-list-event {
  padding: 0.25rem 0.42rem;
}

@media (max-width: 991.98px) {
  .crm-calendar {
    min-height: auto;
  }
  .fc .fc-toolbar {
    gap: 0.45rem;
  }
  .fc .fc-toolbar-title {
    font-size: 1rem;
  }
  .fc .fc-button {
    min-height: 2.35rem;
    padding: 0.28rem 0.58rem;
  }
  .fc .fc-daygrid-event,
  .fc .fc-timegrid-event,
  .fc .fc-list-event {
    min-height: 2.5rem;
    padding: 0.45rem 0.62rem;
    line-height: 1.25;
  }
}
.pwa-install-prompt {
  position: fixed;
  right: 1rem;
  bottom: 1rem;
  z-index: 1100;
  width: min(27rem, 100vw - 2rem);
}

.pwa-install-prompt__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.9rem;
  border-radius: 0.85rem;
  border: 1px solid var(--app-line);
  box-shadow: var(--shadow-soft);
  background: #ffffff;
  padding: 0.85rem 0.9rem;
}

.pwa-install-prompt__title {
  font-size: 0.92rem;
  font-weight: 700;
}

.pwa-install-prompt__text {
  color: var(--app-muted);
  font-size: 0.82rem;
  line-height: 1.35;
}

.pwa-install-prompt__actions {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  flex-shrink: 0;
}

.pwa-network-banner {
  position: fixed;
  right: 1rem;
  left: 1rem;
  bottom: 1rem;
  z-index: 1110;
}

.pwa-network-banner__inner {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 0.9rem;
  border: 1px solid var(--app-line);
  border-radius: 0.85rem;
  box-shadow: var(--shadow-soft);
  background: rgba(255, 255, 255, 0.98);
  padding: 0.8rem 0.9rem;
}

.pwa-network-banner__title {
  font-size: 0.9rem;
  font-weight: 700;
}

.pwa-network-banner__text {
  color: var(--app-muted);
  font-size: 0.82rem;
  line-height: 1.35;
}

.pwa-network-banner__actions {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}

@media (max-width: 768px) {
  .pwa-install-prompt {
    right: 0.65rem;
    left: 0.65rem;
    width: auto;
    bottom: max(0.65rem, env(safe-area-inset-bottom));
  }
  .pwa-install-prompt__inner {
    flex-direction: column;
    align-items: stretch;
  }
  .pwa-install-prompt__actions {
    justify-content: flex-end;
  }
  .pwa-network-banner {
    right: 0.65rem;
    left: 0.65rem;
    bottom: calc(0.65rem + env(safe-area-inset-bottom));
  }
  .pwa-network-banner__inner {
    flex-direction: column;
    align-items: stretch;
  }
  .pwa-network-banner__actions {
    justify-content: flex-end;
  }
}
@media (max-width: 992px) {
  body.has-mobile-action-bar .pwa-network-banner {
    bottom: calc(5.3rem + env(safe-area-inset-bottom));
  }
}
.mobile-action-bar {
  position: fixed;
  right: 0.75rem;
  left: 0.75rem;
  bottom: max(0.65rem, env(safe-area-inset-bottom));
  z-index: 1090;
}

.mobile-action-bar__inner {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 0.55rem;
  border: 1px solid var(--app-line);
  border-radius: 0.88rem;
  background: rgba(255, 255, 255, 0.97);
  backdrop-filter: blur(6px);
  box-shadow: var(--shadow-soft);
  padding: 0.55rem;
}

.mobile-action-bar .btn {
  min-height: 2.75rem;
  width: 100%;
}

.mobile-action-bar form {
  margin: 0;
}

/* Breakpoint note: the shell (sidebar off-canvas) now switches at 1100px — see
   layout/_sidebar.scss. This bar deliberately stays on Bootstrap's lg boundary (992px),
   because its visibility is driven by `d-lg-none` in the templates; moving only the CSS
   guard would desync the two. Aligning both is a job for the screen PRs. */
@media (max-width: 992px) {
  body.has-mobile-action-bar .app-content {
    padding-bottom: calc(7rem + env(safe-area-inset-bottom));
  }
  body.has-mobile-action-bar .pwa-install-prompt {
    bottom: calc(5.3rem + env(safe-area-inset-bottom));
  }
  body.sidebar-mobile-open .mobile-action-bar {
    display: none;
  }
}
/* Bulk actions (§7.3, rule 6) — a slim always-visible select strip above the table
   plus the floating dark bar that appears only while the selection is non-empty.

   z-index note: `.mobile-action-bar` (1090) lives on DETAIL pages, this bar on LIST
   pages, so the two never co-occur; the bulk bar deliberately stays at 1030 (above
   page content, below the topbar layer). */
.bulk-select-strip {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  padding: 0 0.15rem 0.6rem;
  font-size: 0.8125rem;
  color: var(--app-muted);
}

.bulk-select-strip .form-check-label {
  color: var(--app-muted);
}

.bulk-bar {
  position: fixed;
  bottom: 24px;
  left: calc(236px + (100% - 236px) / 2);
  transform: translateX(-50%);
  z-index: 1030;
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  max-width: calc(100vw - 2rem);
  padding: 10px 12px 10px 16px;
  border-radius: var(--r-card);
  background: var(--app-navy);
  box-shadow: var(--shadow-float);
  /* 120ms fade is the only transition on this component (§9). */
  opacity: 0;
  visibility: hidden;
  transition: opacity 120ms ease;
}

.bulk-bar--visible {
  opacity: 1;
  visibility: visible;
}

/* While the bar is up, reserve room under the content so it never covers the
   last table row or the pagination — same body-class trick as `.mobile-action-bar`.
   `has-bulk-bar` is toggled by `initBulkActionForms` alongside `bulk-bar--visible`. */
body.has-bulk-bar .app-content {
  padding-bottom: 6rem;
}

/* The PWA install prompt sits at z-index 1100, above the bar; lift it clear. */
body.has-bulk-bar .pwa-install-prompt {
  bottom: 6.5rem;
}

.bulk-bar__main {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.bulk-bar__count {
  color: #fff;
  font-size: 0.8125rem;
  white-space: nowrap;
}

.bulk-bar__count strong {
  font-family: var(--bs-font-monospace);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}

.bulk-bar__divider {
  width: 1px;
  align-self: stretch;
  background: var(--app-ink-3);
}

.bulk-bar__control {
  height: var(--h-control-xs);
  padding: 0 0.55rem;
  border-radius: var(--r-control-sm);
  border: 1px solid var(--app-ink-3);
  background: transparent;
  color: #fff;
  font-size: 0.8125rem;
}

.bulk-bar__control option {
  color: var(--app-ink);
  background: var(--app-panel);
}

.bulk-bar__btn {
  height: var(--h-control-xs);
  padding: 0 0.7rem;
  border-radius: var(--r-control-sm);
  border: 1px solid var(--app-ink-3);
  background: transparent;
  color: #fff;
  font-size: 0.8125rem;
  font-weight: 500;
}

.bulk-bar__control:hover,
.bulk-bar__btn:hover:not(:disabled) {
  background: var(--app-ink-2);
}

.bulk-bar__btn:disabled {
  opacity: 0.5;
}

.bulk-bar__close {
  width: var(--h-icon-btn);
  height: var(--h-icon-btn);
  border: 0;
  border-radius: var(--r-control-sm);
  background: transparent;
  color: var(--app-muted-2);
  font-size: 1.1rem;
  line-height: 1;
}

.bulk-bar__close:hover {
  background: var(--app-ink-2);
  color: #fff;
}

.bulk-bar__banner {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-wrap: wrap;
  padding-top: 0.45rem;
  border-top: 1px solid var(--app-ink-3);
  color: var(--app-muted-2);
  font-size: 0.75rem;
}

.bulk-bar__banner-action {
  border: 0;
  background: transparent;
  padding: 0;
  color: #fff;
  font-size: 0.75rem;
  text-decoration: underline;
}

.bulk-bar :focus-visible {
  outline: 2px solid #fff;
  outline-offset: 1px;
}

/* Shell switches the sidebar off-canvas at 1100px, so the bar centres on the viewport. */
@media (max-width: 1100px) {
  .bulk-bar {
    left: 50%;
  }
}
@media (max-width: 768px) {
  .bulk-bar {
    left: 0;
    right: 0;
    bottom: 0;
    transform: none;
    max-width: none;
    border-radius: var(--r-card) var(--r-card) 0 0;
    padding: 0.7rem 0.75rem calc(0.7rem + env(safe-area-inset-bottom));
  }
  .bulk-bar__main {
    flex-wrap: wrap;
  }
  .bulk-bar__control,
  .bulk-bar__btn {
    min-height: 2.75rem;
  }
  .bulk-bar__divider {
    display: none;
  }
  /* Docked to the bottom edge here, so the reserved space follows the safe area. */
  body.has-bulk-bar .app-content {
    padding-bottom: calc(8rem + env(safe-area-inset-bottom));
  }
  body.has-bulk-bar .pwa-install-prompt {
    bottom: calc(8.5rem + env(safe-area-inset-bottom));
  }
}
/* Shared page header (§7.1) — title, subtitle or richer identity lead, right-aligned
   actions. */
.page-header {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  padding-bottom: 20px;
}

/* The lead must be allowed to shrink, or a long title pushes the actions off-screen
   instead of wrapping (flex items default to min-width: auto). */
.page-header__lead {
  flex: 1 1 auto;
  min-width: 0;
}

.page-header__back {
  display: inline-block;
  margin-bottom: 0.25rem;
  font-size: 0.78125rem;
  font-weight: 500;
  color: var(--app-muted);
  text-decoration: none;
}

.page-header__back:hover {
  color: var(--app-accent);
}

.page-header__title {
  margin: 0;
  font-size: 1.5rem;
  font-weight: 600;
  line-height: 1.15;
  letter-spacing: -0.02em;
  color: var(--app-ink);
}

.page-header__subtitle {
  margin: 0.25rem 0 0;
  font-size: 0.84375rem;
  font-weight: 400;
  color: var(--app-muted);
}

/* Richer lead (§8.3, `meta_template`): the title shares a row with badges and a status
   dot, and a one-line meta sits under it. Used by the customer detail identity block. */
.page-header__identity {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  min-width: 0;
}

.page-header__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin: 8px 0 0;
  font-size: 0.78125rem;
  color: var(--app-muted);
  min-width: 0;
}

.page-header__meta-sep {
  color: var(--app-line-strong);
}

.page-header__actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

/* An actions template whose every button is permission-gated renders an empty wrapper;
   it must not claim the flex gap. page_header.html wraps the include in {% spaceless %}
   so that the div really is :empty (whitespace would defeat the selector).

   !important is required here: a screen that also renders a mobile action bar (P2-1,
   see customer_detail.html) passes actions_class="d-none d-lg-flex" so the header copy
   is hidden below the breakpoint the mobile bar owns. Bootstrap's .d-lg-flex itself
   carries !important at >=lg, which would otherwise beat this plain `display: none` and
   force an empty, but visible, flex wrapper back open for a view-only user (no action
   perms at all -> nothing to show in either place). Matching !important here restores
   correct precedence: :empty (specificity 0,0,2,0) then wins over .d-lg-flex
   (0,0,1,0) regardless of source order, so the wrapper stays collapsed at every
   breakpoint whenever it truly has no content. */
.page-header__actions:empty {
  display: none !important;
}

/* Shared empty state (§7.4, rule 8) — icon tile, one sentence, one action. */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 0.35rem;
  padding: 1.75rem 1rem;
}

.empty-state__tile {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  margin-bottom: 0.35rem;
  border-radius: 10px;
  background: var(--app-accent-tint);
  color: var(--app-accent);
  font-size: 1rem;
}

.empty-state--neutral .empty-state__tile {
  background: var(--app-line-softest);
  color: var(--app-muted-2);
}

.empty-state__title {
  margin: 0;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--app-ink);
}

.empty-state__text {
  margin: 0;
  max-width: 280px;
  font-size: 0.78125rem;
  line-height: 1.5;
  color: var(--app-muted);
}

.empty-state__action {
  margin-top: 0.5rem;
}

/* Same reasoning as .page-header__actions:empty — a fully permission-gated action
   template leaves an empty wrapper that would still contribute margin and flex gap.
   empty_state.html wraps the include in {% spaceless %} to keep the div truly empty. */
.empty-state__action:empty {
  display: none;
}

.empty-state__action .btn {
  min-height: 30px;
}

/* Shared period picker (§7.7) — year stepper, twelve months, quarters + whole year. */
.period-picker {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 10px 12px;
  border: 1px solid var(--app-line);
  border-radius: var(--r-card);
  background: var(--app-panel);
  box-shadow: var(--shadow-card);
}

.period-picker__year {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  padding: 2px;
  border: 1px solid var(--app-line);
  border-radius: var(--r-control);
  background: var(--app-bg);
}

.period-picker__step {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: var(--r-control-sm);
  color: var(--app-ink-4);
  text-decoration: none;
}

.period-picker__step:hover {
  background: var(--app-line-softest);
  color: var(--app-accent);
}

.period-picker__year-value {
  min-width: 3.25rem;
  text-align: center;
  font-family: var(--bs-font-monospace);
  font-variant-numeric: tabular-nums;
  font-size: 0.8125rem;
  color: var(--app-ink);
}

.period-picker__months {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
}

.period-picker__month {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 42px;
  height: var(--h-control-xs);
  padding: 0 0.5rem;
  border: 1px solid var(--app-line);
  border-radius: var(--r-control-sm);
  background: var(--app-panel);
  color: var(--app-ink-4);
  font-size: 0.78125rem;
  text-decoration: none;
}

.period-picker__month:hover {
  background: var(--app-bg);
  color: var(--app-accent);
}

.period-picker__month.is-selected,
.period-picker__month.is-selected:hover {
  border-color: var(--app-accent);
  background: var(--app-accent);
  color: #fff;
}

.period-picker__divider {
  width: 1px;
  align-self: stretch;
  min-height: var(--h-control-xs);
  background: var(--app-line);
}

.period-picker__ranges {
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
}

.period-picker__range {
  display: inline-flex;
  align-items: center;
  height: var(--h-control-xs);
  padding: 0 0.6rem;
  border: 1px solid var(--app-line-strong);
  border-radius: var(--r-control-sm);
  background: var(--app-panel);
  color: var(--app-ink-4);
  font-size: 0.78125rem;
  text-decoration: none;
}

.period-picker__range:hover {
  background: var(--app-bg);
  border-color: var(--app-muted-2);
}

.period-picker__range.is-selected,
.period-picker__range.is-selected:hover {
  border-color: var(--app-accent);
  background: var(--app-accent-tint);
  color: var(--app-accent-ink);
}

.modal-content {
  border-radius: var(--r-card);
  border: 1px solid var(--app-line);
  box-shadow: var(--shadow-pop);
}

.modal-header {
  border-bottom: 1px solid var(--app-line-soft);
}

.modal-title {
  font-size: 0.9375rem;
  font-weight: 600;
}

@media (max-width: 575.98px) {
  .modal .modal-dialog.modal-fullscreen-sm-down .modal-content {
    height: 100dvh;
    border-radius: 0;
  }
  .modal .modal-dialog.modal-fullscreen-sm-down .modal-header {
    position: sticky;
    top: 0;
    z-index: 2;
    background: #ffffff;
  }
  .modal .modal-dialog.modal-fullscreen-sm-down .modal-body {
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    padding-bottom: 1rem;
  }
  .modal .modal-dialog.modal-fullscreen-sm-down .modal-footer {
    position: sticky;
    bottom: 0;
    z-index: 2;
    background: #ffffff;
    border-top: 1px solid var(--app-line);
    padding-bottom: calc(0.75rem + env(safe-area-inset-bottom));
  }
}
.crm-tabs {
  gap: 0.35rem;
}

.crm-tabs .nav-link {
  white-space: nowrap;
  color: var(--app-ink-3);
  font-weight: 500;
}

.crm-tabs .nav-link.active {
  color: var(--app-accent);
  font-weight: 600;
  border-bottom: 2px solid var(--app-accent);
}

@media (max-width: 991.98px) {
  .crm-tabs {
    display: flex;
    flex-wrap: nowrap;
    overflow-x: auto;
    overflow-y: hidden;
    scroll-snap-type: x proximity;
    scrollbar-width: thin;
    padding-bottom: 0.2rem;
    margin-bottom: 0.2rem;
  }
  .crm-tabs .nav-item {
    flex: 0 0 auto;
    scroll-snap-align: start;
  }
  .crm-tabs .nav-link {
    border-radius: 999px;
    border: 1px solid var(--app-line);
    background: #ffffff;
    padding: 0.44rem 0.78rem;
    font-size: 0.86rem;
    line-height: 1.2;
    min-height: 2.25rem;
    color: var(--app-ink);
    font-weight: 500;
  }
  .crm-tabs .nav-link.active {
    background: var(--app-accent);
    border-color: var(--app-accent);
    color: #ffffff;
    font-weight: 500;
    border-bottom: 1px solid var(--app-accent);
    box-shadow: var(--shadow-accent);
  }
}
/* Screen tab strip (§8.2) — segments of one screen, under the page header.
   Distinct from `.crm-tabs` (Bootstrap nav-tabs inside a page): this one is a plain
   underlined row of links with Mono counts. */
.customer-tabs {
  display: flex;
  align-items: center;
  gap: 24px;
  border-bottom: 1px solid var(--app-line);
  margin-bottom: 16px;
  overflow-x: auto;
  scrollbar-width: thin;
}

.customer-tab {
  padding: 9px 2px;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--app-muted);
  text-decoration: none;
  white-space: nowrap;
  border-bottom: 2px solid transparent;
}

.customer-tab:hover {
  color: var(--app-ink-2);
  text-decoration: none;
}

.customer-tab.is-active {
  color: var(--app-accent);
  font-weight: 600;
  border-bottom-color: var(--app-accent);
}

/* Count next to a tab label — shared by both strips (§8.2 screen tabs above, §8.3
   detail tabs inside .crm-tabs). One class, so a count reads the same everywhere. */
.tab-count {
  margin-left: 0.35rem;
  font-size: 0.75rem;
  font-weight: 400;
  color: var(--app-muted-2);
}

.customer-tab.is-active .tab-count,
.crm-tabs .nav-link.active .tab-count {
  color: var(--app-muted);
}

/* Projects overview (§8.4) — one card per project (header row + metrics strip) and
   the sticky per-currency totals footer. Everything here is bound 1:1 to
   templates/projects/project_overview.html. */
.project-overview-list {
  display: flex;
  flex-direction: column;
  gap: 12px;
}

/* `.project-card` deliberately has no `overflow: hidden`: the row-action dropdown is a
   Popper popover that flips upwards near the viewport bottom (guaranteed by the sticky
   totals footer) and a clipping card would cut it off. The metrics strip below rounds
   the card's bottom corners itself instead. */
.project-card__head {
  display: flex;
  align-items: flex-start;
  gap: 20px;
  padding: 16px 18px;
}

/* Flex children default to min-width: auto, so a long project name or customer
   line would push the price and the row actions off-screen instead of wrapping. */
.project-card__lead {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex: 1 1 auto;
  min-width: 0;
}

.project-card__title-row {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}

.project-card__name {
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--app-ink);
  text-decoration: none;
}

.project-card__name:hover {
  color: var(--app-accent);
  text-decoration: none;
}

.project-card__meta {
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  font-size: 0.78125rem;
  color: var(--app-muted);
  min-width: 0;
}

.project-card__meta-sep {
  color: var(--app-line-strong);
}

.project-card__price {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 4px;
  flex: 0 0 auto;
}

.project-card__price-label {
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--app-muted-2);
}

.project-card__price-value {
  font-size: 1.125rem;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--app-ink);
  white-space: nowrap;
}

.project-card__actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 0 0 auto;
}

/* The card variant of `.btn-row-icon`: 30x30 and outlined, because it sits on the
   white card surface rather than inside a table row (§8.4). */
.project-card__icon-btn {
  width: 30px;
  height: 30px;
  border-color: var(--app-line);
  background: var(--app-panel);
  color: var(--app-ink-4);
}

.project-card__icon-btn:hover {
  background: var(--app-line-softest);
  color: var(--app-accent);
  border-color: var(--app-line);
}

/* Hairlines are the 1px grid gap showing the strip's own background through, not cell
   borders: an auto-fit grid wraps into several rows on narrow viewports, and borders
   would then leave a stray edge at each wrap point and no line between the rows.
   `overflow: hidden` here (and only here) rounds the cells into the card's bottom
   corners without clipping the dropdown that lives in the card head. */
.project-card__metrics {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  gap: 1px;
  border-top: 1px solid var(--app-line-soft);
  background: var(--app-line-soft);
  border-radius: 0 0 var(--r-card) var(--r-card);
  overflow: hidden;
}

.project-card__metric {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 12px 18px;
  background: var(--app-bg-2);
  min-width: 0;
}

.project-card__metric-label {
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--app-muted-2);
}

.project-card__metric-value {
  font-size: 0.78125rem;
  font-weight: 500;
  color: var(--app-ink-2);
  overflow-wrap: anywhere;
}

/* Absence is spelled out, never left blank (§8.4). */
.project-card__metric-value--absent {
  font-weight: 400;
  color: var(--app-muted-2);
}

/* ---------------------------------------------------------------- totals footer */
/* Full-bleed inside `.app-content`: the negative inline margins cancel the content
   padding so the strip reaches both edges, and its own padding restores the gutter. */
.overview-totals-footer {
  position: sticky;
  bottom: 0;
  z-index: 30;
  margin: 16px -32px 0;
  background: rgba(255, 255, 255, 0.92);
  backdrop-filter: blur(8px);
  border-top: 1px solid var(--app-line);
}

.overview-totals-footer__inner {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 28px;
  padding: 12px 32px;
}

.overview-totals__group {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 28px;
  min-width: 0;
}

.overview-totals__cell {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

.overview-totals__label {
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--app-muted-2);
}

.overview-totals__value {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--app-ink);
}

.overview-totals__value--profit {
  color: var(--app-ok-fg);
}

/* Shown instead of the currency groups when the selection has nothing to sum. */
.overview-totals__empty {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--app-muted-2);
}

.overview-totals__note {
  margin: 0 0 0 auto;
  max-width: 280px;
  font-size: 0.75rem;
  color: var(--app-muted-2);
}

/* No bottom padding reservation on `.app-content`: the footer is in flow (it takes up
   its own space after the last card, hence the 16px margin-top above) rather than
   fixed, and a sticky box cannot leave its containing block — padding here would make
   it unstick that many pixels early and hover above blank space at full scroll.
   The PWA install prompt is a different matter: it is fixed at z-index 1100, above
   the strip, so lift it clear of the stuck footer. Scoped with :has() instead of a
   body class because the footer is plain server-rendered markup with no JS to toggle
   one — same approach as the table overflow rule in components/_tables.scss. */
body:has(.overview-totals-footer) .pwa-install-prompt {
  bottom: 5.5rem;
}

@media (max-width: 992px) {
  .project-card__head {
    flex-wrap: wrap;
  }
  .project-card__price {
    align-items: flex-start;
  }
  .overview-totals-footer {
    margin-inline: -16px;
  }
  .overview-totals-footer__inner {
    gap: 16px;
    padding: 12px 16px;
  }
  .overview-totals__group {
    gap: 16px;
  }
  .overview-totals__note {
    margin-left: 0;
    max-width: none;
  }
}
/* Rental project detail — per-month billing tab. */
.rental-monthly__chart {
  position: relative;
  height: 280px;
}

.rental-monthly__chevron {
  display: inline-block;
  transition: transform 0.15s ease;
}

.rental-monthly__toggle:not(.collapsed) .rental-monthly__chevron {
  transform: rotate(90deg);
}

.rental-monthly__label-empty {
  padding-left: 1.25rem; /* aligns with labels that carry a chevron */
}

.rental-monthly__detail > td {
  background: var(--app-bg-2);
}

/* Bootstrap's height transition cannot animate a <tr>; show/hide instantly. */
.rental-monthly__detail.collapsing {
  transition: none;
}

/* Customer detail (handoff §8.3) — two-column body with a sticky right rail, merged
   contact rows, hairline fact grid, "Co dál" callout, quiet add menu and the Souhrn
   card.

   Scope map: .customer-detail-body / -main / -side are screen-scoped, and the detail
   tab strip is only reached through `.customer-detail-main .crm-tabs` so the shared
   .crm-tabs component (project detail uses it too) keeps its own behaviour. .fact-grid,
   .contact-row*, .callout, .quiet-menu and .summary-card are written generically so the
   next detail screen can reuse them. The .page-header__* elements this screen introduced
   live in components/_page_header.scss with the rest of that block. */
/* --- body grid ---------------------------------------------------------------- */
.customer-detail-body {
  display: grid;
  grid-template-columns: minmax(400px, 1fr) minmax(280px, 320px);
  gap: 20px;
  align-items: start;
}

.customer-detail-main {
  min-width: 0;
}

.customer-detail-side {
  display: flex;
  flex-direction: column;
  gap: 16px;
  position: sticky;
  top: 72px;
  min-width: 0;
}

/* Below 1100px the rail has no room to sit beside the tab content; it stacks under it
   and stops being sticky (a sticky element in a single-column flow just scrolls). */
@media (max-width: 1100px) {
  .customer-detail-body {
    grid-template-columns: minmax(0, 1fr);
  }
  .customer-detail-side {
    position: static;
  }
}
/* --- tab strip ------------------------------------------------------------------ */
/* Seven tabs with counts can outgrow a narrow desktop column, so this one strip may
   scroll sideways. Scoped to the screen: .crm-tabs is shared with project detail, which
   must not inherit a scroll container. overflow-y is pinned to hidden so the horizontal
   overflow does not make the strip scrollable on the block axis as well. */
.customer-detail-main .crm-tabs {
  overflow-x: auto;
  overflow-y: hidden;
  scrollbar-width: thin;
}

/* --- contact rows (Kontaktní údaje, Adresy) ------------------------------------ */
.contact-rows {
  list-style: none;
  margin: 0;
  padding: 0;
}

.contact-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 12px 18px;
  border-bottom: 1px solid var(--app-line-softest);
}

.contact-row:last-child {
  border-bottom: 0;
}

.contact-row:hover {
  background: var(--app-bg-2);
}

.contact-row__kind {
  flex: 0 0 74px;
  width: 74px;
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--app-muted-2);
}

.contact-row__value {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
  min-width: 0;
  font-size: 0.8125rem;
  font-weight: 500;
}

.contact-row__text {
  color: var(--app-ink);
  overflow-wrap: anywhere;
}

.contact-row__meta {
  font-size: 0.71875rem;
  font-weight: 400;
  color: var(--app-muted-2);
  overflow-wrap: anywhere;
}

/* item.note is rendered rich text (TinyMCE, |safe) and can carry block markup (<p>,
   <ul>, …) — .contact-row__meta is a <div> (not <span>: block content inside an inline
   element is invalid HTML) but still needs to read as one compact meta line, not a
   paragraph with default vertical margins. */
.contact-row__meta p,
.contact-row__meta ul,
.contact-row__meta ol {
  margin: 0;
  line-height: inherit;
}

.contact-row__meta p:not(:last-child),
.contact-row__meta ul:not(:last-child),
.contact-row__meta ol:not(:last-child) {
  margin-bottom: 4px;
}

.contact-row__actions {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 0 0 auto;
}

.contact-row--empty {
  color: var(--app-muted);
  font-size: 0.8125rem;
}

/* --- fact grid ------------------------------------------------------------------ */
.fact-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  /* the 1px gap shows the container colour through as a hairline between cells */
  gap: 1px;
  background: var(--app-line-soft);
}

.fact-grid__cell {
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 13px 18px;
  background: var(--app-panel);
  min-width: 0;
}

.fact-grid__cell--action {
  align-items: flex-start;
  gap: 7px;
  padding: 16px 18px;
}

.fact-grid__label {
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--app-muted-2);
}

.fact-grid__value {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--app-ink);
  overflow-wrap: anywhere;
}

.fact-grid__value--empty {
  color: var(--app-muted-2);
}

.fact-grid__hint {
  font-size: 0.71875rem;
  line-height: 1.45;
  color: var(--app-muted-2);
}

/* --- "Co dál" callout ------------------------------------------------------------ */
.callout {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding: 16px 18px;
  border-radius: var(--r-card);
  background: var(--app-accent-tint);
  border: 1px solid var(--app-accent-tint-2);
}

.callout__label {
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--app-accent);
}

.callout__headline {
  font-size: 0.90625rem;
  font-weight: 600;
  color: var(--app-accent-ink);
}

.callout__text {
  font-size: 0.78125rem;
  line-height: 1.5;
  color: var(--app-accent-dark);
}

.callout__links {
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
}

.callout__link {
  font-size: 0.75rem;
  font-weight: 500;
  color: var(--app-accent-dark);
}

/* The "nothing urgent" state is not an alert — it drops the accent tint entirely. */
.callout--neutral {
  background: var(--app-bg-2);
  border-color: var(--app-line);
}

.callout--neutral .callout__label {
  color: var(--app-muted-2);
}

.callout--neutral .callout__headline {
  color: var(--app-ink-2);
}

.callout--neutral .callout__text,
.callout--neutral .callout__link {
  color: var(--app-muted);
}

/* --- quiet menu ------------------------------------------------------------------ */
.quiet-menu {
  display: flex;
  flex-direction: column;
  padding: 6px;
}

.quiet-menu__item {
  display: flex;
  align-items: center;
  gap: 10px;
  min-height: 34px;
  padding: 0 10px;
  border: 0;
  border-radius: var(--r-control);
  background: transparent;
  color: var(--app-ink-2);
  font-size: 0.78125rem;
  font-weight: 500;
  text-align: left;
  text-decoration: none;
}

.quiet-menu__item:hover {
  background: var(--app-line-softest);
  color: var(--app-ink);
  text-decoration: none;
}

.quiet-menu__item > i {
  color: var(--app-muted);
  font-size: 0.9375rem;
}

/* --- Souhrn ---------------------------------------------------------------------- */
.summary-card {
  display: flex;
  flex-direction: column;
  gap: 12px;
  padding: 16px 18px;
  background: var(--app-panel);
  border: 1px solid var(--app-line);
  border-radius: var(--r-card);
  box-shadow: var(--shadow-card);
}

.summary-card__label {
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--app-muted-2);
}

.summary-card__row {
  display: flex;
  align-items: baseline;
  gap: 10px;
}

.summary-card__key {
  flex: 1;
  min-width: 0;
  font-size: 0.78125rem;
  color: var(--app-muted);
}

.summary-card__value {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--app-ink);
  text-align: right;
}

.summary-card__row--tags {
  align-items: flex-start;
}

.summary-card__value--tags {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 4px;
  min-width: 0;
}

.summary-card__empty {
  color: var(--app-muted-2);
}

@media (max-width: 575.98px) {
  .contact-row {
    flex-wrap: wrap;
    gap: 8px;
  }
  .contact-row__kind {
    flex-basis: 100%;
    width: auto;
  }
}
/* Sales performance report (§8.5) — KPI row, the named card pair, the report
   sections below them and the collapsible "Jak report číst" card. */
.performance-period {
  margin: 0 0 0.75rem;
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--app-ink-2);
}

/* KPI row — auto-fit so the tiles reflow instead of being squeezed (§9). */
.performance-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 0.75rem;
  margin: 1rem 0;
}

.performance-kpi {
  display: flex;
  flex-direction: column;
  gap: 0.4375rem;
  min-width: 0;
  padding: 15px 16px;
  border: 1px solid var(--app-line);
  border-radius: var(--r-card);
  background: var(--app-panel);
  box-shadow: var(--shadow-card);
}

.performance-kpi__label {
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--app-muted-2);
}

.performance-kpi__value {
  font-size: 1.6875rem;
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.02em;
  color: var(--app-ink);
}

/* Missing figure, not zero — §8.5 forbids rendering None as 0 %. */
.performance-kpi__value--empty {
  color: var(--app-muted-2);
}

.performance-kpi__hint {
  font-size: 0.71875rem;
  color: var(--app-muted);
}

/* The two named cards sit side by side and reflow the same way.
   `min(300px, 100%)` instead of a bare 300px: at the 320px viewport the
   e2e suite guards, .app-content padding leaves a 288px content box, so a
   flat 300px minimum would force a track wider than the viewport (horizontal
   scroll). `min()` lets the track shrink to the available width instead. */
.performance-pair {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(300px, 100%), 1fr));
  gap: 1rem;
  margin-bottom: 1rem;
}

.report-card {
  display: flex;
  flex-direction: column;
  min-width: 0;
  margin-bottom: 1rem;
  border: 1px solid var(--app-line);
  border-radius: var(--r-card);
  background: var(--app-panel);
  box-shadow: var(--shadow-card);
  overflow: hidden;
}

.performance-pair .report-card {
  margin-bottom: 0;
}

.report-card__header {
  padding: 14px 18px;
  border-bottom: 1px solid var(--app-line-soft);
}

.report-card__title {
  margin: 0;
  font-size: 0.84375rem;
  font-weight: 600;
  color: var(--app-ink);
}

.report-card__body {
  padding: 16px 18px;
}

.report-card__note {
  margin: 0 0 0.75rem;
  font-size: 0.75rem;
  color: var(--app-muted);
}

.report-card__body > .report-card__note:last-child {
  margin: 0.75rem 0 0;
}

.report-card__empty {
  margin: 0;
  font-size: 0.8125rem;
  color: var(--app-muted);
}

/* Per-currency money blocks — never one summed figure (rule 9). */
.money-totals {
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
}

.money-total {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

.money-total__label {
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--app-muted-2);
}

.money-total__caption {
  margin-top: 0.25rem;
  font-size: 0.71875rem;
  color: var(--app-muted);
}

.money-total__value {
  font-size: 1.125rem;
  font-weight: 500;
  color: var(--app-ink);
}

.report-figures {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 1rem;
}

.report-figure {
  display: flex;
  flex-direction: column;
  gap: 0.25rem;
  min-width: 0;
}

.report-figure__label {
  font-size: 0.625rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--app-muted-2);
}

.report-figure__value {
  font-size: 1.25rem;
  font-weight: 500;
  line-height: 1.1;
  color: var(--app-ink);
}

/* "Jak report číst" — the whole header is the collapse toggle. */
.report-help__toggle {
  display: flex;
  align-items: center;
  gap: 0.625rem;
  width: 100%;
  padding: 13px 18px;
  border: 0;
  background: transparent;
  text-align: left;
}

.report-help__toggle:hover {
  background: var(--app-bg-2);
}

.report-help__icon {
  font-size: 0.9375rem;
  color: var(--app-muted);
}

.report-help__title {
  min-width: 0;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--app-ink);
}

/* Monospace + tabular-nums come from the shared .num class in the template
   (base/_base.scss) — only the layout/color that's specific to this toggle
   lives here. */
.report-help__count {
  margin-left: auto;
  font-size: 0.75rem;
  color: var(--app-muted-2);
}

.report-help__chevron {
  font-size: 0.875rem;
  color: var(--app-muted-2);
  transform: rotate(0deg);
  transition: transform 150ms ease;
}

.report-help__toggle[aria-expanded=true] .report-help__chevron {
  transform: rotate(90deg);
}

@media (prefers-reduced-motion: reduce) {
  .report-help__chevron {
    transition: none;
  }
}
.report-help__body {
  border-top: 1px solid var(--app-line-soft);
}

.report-help__rules {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
  padding: 2px 18px 16px 44px;
}

.report-help__rule {
  margin: 0;
  font-size: 0.78125rem;
  line-height: 1.55;
  color: var(--app-ink-4);
}

@media (max-width: 575.98px) {
  .report-help__rules {
    padding-left: 18px;
  }
}
.workspace {
  display: flex;
  height: calc(100vh - 56px);
  min-height: 0;
  overflow: hidden;
  border-top: 1px solid var(--bs-border-color);
}

.workspace-list-pane {
  flex: 0 0 clamp(260px, 18vw, 360px);
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--bs-border-color);
  background: var(--bs-body-bg);
  min-height: 0;
  min-width: 0;
}

.workspace-list-header {
  padding: 0.75rem;
  border-bottom: 1px solid var(--bs-border-color);
}

.workspace-list {
  list-style: none;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  flex: 1 1 auto;
  min-height: 0;
}

.workspace-list-sentinel {
  text-align: center;
  padding: 0.75rem;
  list-style: none;
}

.workspace-card {
  border-bottom: 1px solid var(--bs-border-color-translucent);
}

.workspace-card > a {
  display: block;
  padding: 0.75rem 1rem;
  color: inherit;
  text-decoration: none;
}

.workspace-card > a:hover {
  background: var(--bs-tertiary-bg);
}

.workspace-card.is-active > a {
  background: var(--bs-primary-bg-subtle);
  border-left: 3px solid var(--bs-primary);
}

.workspace-card-name {
  display: block;
  font-weight: 600;
  margin-bottom: 0.25rem;
}

.workspace-card-tags {
  display: block;
  margin-bottom: 0.25rem;
}

.workspace-card-tags .badge-soft {
  font-size: 0.7rem;
  margin-right: 0.25rem;
}

.workspace-card-excerpt {
  display: block;
  color: var(--bs-secondary-color);
  font-size: 0.85rem;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.workspace-detail-pane {
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  min-height: 0;
}

.workspace-detail-inner {
  width: 100%;
  max-width: 1100px;
  margin: 0 auto;
  padding: 1.5rem;
}

.workspace-empty {
  margin: auto;
  text-align: center;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
}

.workspace-detail-header {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 1rem;
  margin-bottom: 1rem;
}

.workspace-detail-title {
  margin: 0 0 0.25rem 0;
}

.workspace-detail-tags .badge-soft {
  margin-right: 0.25rem;
}

.workspace-detail-body {
  border-top: 1px solid var(--bs-border-color);
  padding-top: 1rem;
}

.workspace-back-bar {
  display: none;
}

@media (max-width: 768px) {
  .workspace {
    height: calc(100vh - 56px);
    height: calc(100dvh - 56px);
  }
  .workspace-list-pane {
    flex: 1 1 auto;
    border-right: none;
  }
  .workspace-detail-pane {
    display: none;
  }
  .workspace.is-detail-open .workspace-detail-pane {
    display: flex;
    position: fixed;
    inset: 0;
    z-index: 1035;
    background: var(--bs-body-bg);
  }
  .workspace-back-bar {
    display: block;
    position: sticky;
    top: 0;
    z-index: 1;
    padding: 0.625rem 1rem;
    background: var(--bs-body-bg);
    border-bottom: 1px solid var(--bs-border-color);
  }
  .workspace-detail-inner {
    padding: 1rem;
  }
}
/* "Vzory ovládání" — the design-system reference screen (handoff §8.6).

   The page documents the system, so it uses the tokens for everything it shows; the only
   literal values here are the type-ramp sizes, which mirror §4 of the handoff and must
   render AT the size they describe for the sample to be worth anything. */
.patterns {
  max-width: 1100px;
  display: flex;
  flex-direction: column;
  gap: 1rem; /* 16 */
}

.patterns-section {
  padding: 1.5rem; /* 24 */
  display: flex;
  flex-direction: column;
  gap: 1rem; /* 16 */
}

.patterns-section__head {
  display: flex;
  align-items: baseline;
  gap: 0.75rem; /* 12 */
  min-width: 0;
}

.patterns-section__num {
  flex: none;
  font-size: 0.625rem; /* 10 */
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--app-muted-2);
}

.patterns-section__title {
  min-width: 0;
  margin: 0;
  font-size: 0.9375rem; /* 15 */
  font-weight: 600;
  color: var(--app-ink);
}

.patterns-demo {
  border: 1px dashed var(--app-line);
  border-radius: var(--r-card);
  background: var(--app-bg);
  padding: 1rem; /* 16 */
}

/* the header example is a stand-in, so its title has to look like the real .page-header__title */
.patterns-demo .page-header__title {
  margin: 0;
}

.patterns-rules {
  margin: 0;
  padding-left: 1.25rem;
  display: flex;
  flex-direction: column;
  gap: 0.5rem; /* 8 */
  font-size: 0.8125rem; /* 13 */
  color: var(--app-ink-3);
}

.patterns-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem; /* 8 */
  min-width: 0;
}

.patterns-row--caption {
  gap: 0.75rem; /* 12 */
}

.patterns-caption {
  min-width: 0;
  font-size: 0.71875rem;
  color: var(--app-muted);
}

.patterns-verdicts {
  display: grid;
  /* min(260px, 100%) lets the track shrink below 260px inside the 238px
     content box at a 320px viewport (CLAUDE.md rule 6; same fix as PR #79). */
  grid-template-columns: repeat(auto-fit, minmax(min(260px, 100%), 1fr));
  gap: 0.75rem; /* 12 */
}

.patterns-verdict {
  min-width: 0;
  border: 1px solid var(--app-line);
  border-radius: var(--r-card);
  padding: 0.75rem 1rem; /* 12 / 16 */
  display: flex;
  flex-direction: column;
  gap: 0.25rem; /* 4 */
}

.patterns-verdict--yes {
  border-color: var(--app-ok-fg);
  background: var(--app-ok-bg);
}

.patterns-verdict--no {
  border-color: var(--app-danger-line);
  background: var(--app-danger-bg);
}

.patterns-verdict__label {
  font-size: 0.625rem; /* 10 */
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.patterns-verdict--yes .patterns-verdict__label {
  color: var(--app-ok-fg);
}

.patterns-verdict--no .patterns-verdict__label {
  color: var(--app-danger-fg);
}

.patterns-verdict__text {
  margin: 0;
  font-size: 0.8125rem; /* 13 */
  color: var(--app-ink-3);
}

.patterns-fields {
  max-width: 420px;
}

.patterns-fields .app-field:last-child {
  margin-bottom: 0;
}

.patterns-rule-text {
  margin: 0;
  font-size: 0.8125rem; /* 13 */
  color: var(--app-ink-3);
}

/* type ramp — the literal sizes mirror handoff §4 and are the point of the sample */
.patterns-ramp {
  display: flex;
  flex-direction: column;
  gap: 0.75rem; /* 12 */
}

.patterns-ramp__line {
  margin: 0;
  color: var(--app-ink);
}

.patterns-ramp__line--h1 {
  font-size: 1.5rem; /* 24 */
  font-weight: 600;
  letter-spacing: -0.01em;
}

.patterns-ramp__line--h2 {
  font-size: 0.9375rem; /* 15 */
  font-weight: 600;
}

.patterns-ramp__line--h3 {
  font-size: 0.84375rem; /* 13.5 */
  font-weight: 600;
}

.patterns-ramp__line--body {
  font-size: 0.8125rem; /* 13 */
  font-weight: 400;
}

.patterns-ramp__line--label {
  font-size: 0.625rem; /* 10 */
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--app-muted);
}

.patterns-ramp__line--num {
  font-size: 0.8125rem; /* 13 */
}

.patterns-swatches {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem; /* 16 */
}

.patterns-swatch {
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 0.25rem; /* 4 */
}

.patterns-swatch__chip {
  width: 44px;
  height: 44px;
  border-radius: var(--r-control);
  border: 1px solid var(--app-line);
}

.patterns-swatch__chip--accent {
  background: var(--app-accent);
}

.patterns-swatch__chip--ink {
  background: var(--app-ink);
}

.patterns-swatch__chip--muted {
  background: var(--app-muted);
}

.patterns-swatch__chip--line {
  background: var(--app-line);
}

.patterns-swatch__chip--bg {
  background: var(--app-bg);
}

.patterns-swatch__code {
  font-size: 0.71875rem;
  color: var(--app-ink-3);
}

.patterns-swatch__name {
  font-size: 0.71875rem;
  color: var(--app-muted);
}

.toast--success {
  background-color: var(--app-ok-fg);
  color: #fff;
}

.toast--error {
  background-color: var(--app-danger-fg);
  color: #fff;
}

.toast--warning {
  background-color: var(--app-warn-fg);
  color: #fff;
}

.toast--info {
  background-color: var(--app-navy);
  color: #fff;
}

#progress-container .step-icon {
  width: 24px;
  text-align: center;
  font-size: 1.1rem;
}
#progress-container .spinner-border-sm {
  width: 16px;
  height: 16px;
}

#result-container .card-footer {
  flex-wrap: wrap;
}
