/* latin-ext */
@font-face {
  font-family: 'DM Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/dm-mono-latin-ext.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;
}
/* latin */
@font-face {
  font-family: 'DM Mono';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/dm-mono-latin.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;
}
/* latin-ext */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../fonts/dm-sans-latin-ext.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;
}
/* latin */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url('../fonts/dm-sans-latin.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;
}

/* Fallbacks for older/stricter font weight requests */
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url('../fonts/dm-sans-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url('../fonts/dm-sans-latin.woff2') format('woff2');
}
@font-face {
  font-family: 'DM Sans';
  font-style: normal;
  font-weight: 700;
  font-display: swap;
  src: url('../fonts/dm-sans-latin.woff2') format('woff2');
}

:root {
  --rt-navy: #0f182b;
  --rt-red: #e63946;
  --rt-warn: #d97706;  /* amber — "high" (magas) priority accent; reads on both themes */
  --rt-oat: #eeede9;
  --rt-light: #f9f7f4;
  --rt-navy-70: rgba(15, 24, 43, 0.70);
  --rt-border: rgba(15, 24, 43, 0.14);
  --rt-hover: rgba(15, 24, 43, 0.06);
  --rt-font-sans: "DM Sans", system-ui, sans-serif;
  --rt-font-mono: "DM Mono", ui-monospace, monospace;
  --rt-radius: 0.5rem;
  --rt-transition: 200ms ease;
}

/* Native Dark Mode Support for Raintel Tokens */
html.dark {
  --rt-light: #1b1b1c; /* Neutral near-black card surface (was #111827 blue-tinted,
                          which blended into the warm-dark page bg and muddied the
                          navy utilization bars). Matches the sidebar dark tone. */
  --rt-oat: #1f2937;   /* Maps to bg-gray-800 */
  --rt-navy: #f9f7f4;  /* Inverts navy text to light */
  --rt-navy-70: rgba(249, 247, 244, 0.70);
  --rt-border: rgba(249, 247, 244, 0.16);
  --rt-hover: rgba(249, 247, 244, 0.08);
}

/* ===================================================================
   SEMANTIC COLOUR LAYER — the ONLY sanctioned way to colour text,
   surfaces, and borders in custom core/templates.

   Why this exists: unfold's compiled Tailwind ships bg-white / text-white
   and its own base-* and primary-* palettes, but NOT Tailwind's default
   gray-*, slate-*, and amber-* utilities. Those classes silently no-op, which
   is what produced white-on-white numbers in dark mode and washed-out
   text in light mode. These token-backed classes invert correctly via
   html.dark and are contrast-audited (WCAG 2.1 AA+) in BOTH modes:

     .rt-text        navy on light  ~17:1 / light on dark  ~15:1  (AAA)
     .rt-text-muted  navy-70        ~6.3:1 /                ~7.7:1 (AA)
     Ratios verified by core/tests/test_template_contrast.py, which also
     FAILS the build on any raw gray-*, slate-*, zinc-*, or amber-* utility left
     in a template. Do not reach for Tailwind neutrals here — use these.
   =================================================================== */
/* Specificity note: unfold applies its base font-colour to admin content with
   per-element-type !important rules (headings, form controls, bold text). To win
   uniformly and regardless of source order, these selectors carry ID-level
   specificity via :not(#_) (a no-op that matches everything). This is the
   terminal colour decision for token-styled text. Verified navy in both modes. */
html body .rt-text:not(#_):not(#__)        { color: var(--rt-navy) !important; }
html body .rt-text-muted:not(#_):not(#__)  { color: var(--rt-navy-70) !important; }
html body .rt-text-onnavy:not(#_):not(#__) { color: #f9f7f4 !important; }  /* text on fixed navy chrome */

.rt-surface        { background-color: var(--rt-light); }  /* raised card/panel */
.rt-surface-sunken { background-color: var(--rt-oat); }    /* inset / subtle fill */
.rt-border         { border-color: var(--rt-border); }
.rt-hover:hover    { background-color: var(--rt-hover); }

/* Manual-override / "edited" chip — replaces the dead amber-* utilities. */
.rt-badge--edited {
  background: #f6e0b8;
  color: #7a4a00;                 /* 6.2:1 on the light amber */
  border: 1px solid #e2be79;
}
html.dark .rt-badge--edited {
  background: #4a3410;
  color: #fde7bf;                 /* 10:1 on the dark amber */
  border-color: #6b4e18;
}

/* ---- Semantic ACCENT layer (risk / success / info) ----
   Unfold ships default red/green/blue utilities only PARTIALLY and
   inconsistently (e.g. text-red-600 exists but text-green-600 / text-blue-600
   / the dark: variants do not), so accents were silently unreliable too.
   These token classes are contrast-audited AA+ as text on both the page
   surface and their own tint, in light and dark. */
:root {
  --rt-risk-text: #c1121f;    --rt-risk-bg: #fdecee;    --rt-risk-border: rgba(230, 57, 70, 0.35);
  --rt-success-text: #0f5c37; --rt-success-bg: #e7f3ec; --rt-success-border: rgba(15, 92, 55, 0.35);
  --rt-info-text: #2c46a6;    --rt-info-bg: #eaf0fb;    --rt-info-border: rgba(44, 70, 166, 0.30);
}
html.dark {
  --rt-risk-text: #ff9ba1;    --rt-risk-bg: #3b1418;    --rt-risk-border: rgba(255, 120, 130, 0.35);
  --rt-success-text: #7fe0a8; --rt-success-bg: #10281c; --rt-success-border: rgba(127, 224, 168, 0.30);
  --rt-info-text: #b9c9f2;    --rt-info-bg: #16203a;    --rt-info-border: rgba(150, 175, 240, 0.30);
}
html body .rt-text-risk:not(#_):not(#__)    { color: var(--rt-risk-text) !important; }
html body .rt-text-success:not(#_):not(#__) { color: var(--rt-success-text) !important; }
html body .rt-text-info:not(#_):not(#__)    { color: var(--rt-info-text) !important; }
.rt-surface-risk    { background-color: var(--rt-risk-bg); }
.rt-surface-success { background-color: var(--rt-success-bg); }
.rt-surface-info    { background-color: var(--rt-info-bg); }
.rt-border-risk    { border-color: var(--rt-risk-border); }
.rt-border-success { border-color: var(--rt-success-border); }
.rt-border-info    { border-color: var(--rt-info-border); }
.rt-bg-risk-solid  { background-color: var(--rt-red); color: #fff; }  /* solid risk chip/dot */

/* Typography styles override */
body, .unfold {
  font-family: var(--rt-font-sans) !important;
}

/* Data/programmatic values only: IDs, hours, money, log output */
.rt-mono {
  font-family: var(--rt-font-mono) !important;
}

/* Status badge semantics — red reserved for risk ("salt, not sauce") */
.rt-badge {
  display: inline-block;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  font-size: 0.75rem;
  font-weight: 500;
}
.rt-badge--info {
  background: var(--rt-navy);
  color: #fff;
}
.rt-badge--neutral {
  background: var(--rt-oat);
  color: var(--rt-navy);
}
.rt-badge--done {
  background: #e7f3ec;
  color: #1c7a4a;
}
.rt-badge--risk {
  background: var(--rt-red);
  color: #fff;
}

/* Dark-mode badge fixes (4P.F).
   html.dark inverts --rt-navy to near-white, which made --info render as
   white-on-white. Pin explicit dark values instead of relying on tokens.
   --neutral and --risk already read correctly in dark mode and are untouched. */
html.dark .rt-badge--info {
  background: #374151; /* gray-700 — visible chip on a gray-900 page */
  color: #f9f7f4;
}
html.dark .rt-badge--done {
  background: #14532d; /* green-900 */
  color: #bbf7d0;      /* green-200 */
}

/* Solid navy action button — the Add Task control on the Triage Queue.
   Literal values, NOT tokens: html.dark inverts --rt-navy to near-white, so a
   token-driven navy button would paint white text on a white ground. That is
   the same trap already documented for .rt-badge--info above, and the same
   remedy: pin explicit dark values.

   The `a.rt-btn.rt-btn--navy` selector is required, not belt-and-braces:
   `a.rt-btn` (defined below for link-buttons) sets `color: inherit` at
   specificity 0-1-1 and would otherwise beat a bare `.rt-btn--navy` at 0-1-0,
   leaving the label navy-on-navy. */
.rt-btn--navy,
a.rt-btn.rt-btn--navy {
  background: #0f182b;
  color: #fff;
  border: 1px solid transparent;
}
.rt-btn--navy:hover,
a.rt-btn.rt-btn--navy:hover {
  background: #1c2b4a;
  color: #fff;
}
html.dark .rt-btn--navy,
html.dark a.rt-btn.rt-btn--navy {
  /* Lifted off pure navy so the button still reads as a raised control
     against the near-black page rather than dissolving into it. */
  background: #2b3a5c;
  color: #fff;
}
html.dark .rt-btn--navy:hover,
html.dark a.rt-btn.rt-btn--navy:hover {
  background: #374971;
  color: #fff;
}

/* Card / section surface. NO border by default — surface + shadow + radius.
   Border-stroke convention (standardised 2026-07-23):
     * KPI STAT TILES (the metric row at the top of a dashboard) use the
       homescreen tile design: `rt-surface p-5 rounded-lg border rt-border
       shadow-sm` — a thin NEUTRAL stroke + drop shadow, no colour. This is
       the canonical stat-tile look across /admin/, Billable, and Timesheets.
     * A COLOURED accent stroke (`border-l-4` + a brand colour) is reserved
       for per-item STATE signalling — the per-client cards whose left edge
       goes red at/over budget, the "Current Month Hours" card. It encodes a
       status, not decoration.
     * Content cards and inner sections use plain `.rt-card` (this rule) or a
       NEUTRAL `border rt-border` outline for structure. Interactive controls
       (buttons, inputs, filter chips) may use primary-* borders for their
       active/focus state; those are not cards. */
.rt-card {
  background: var(--rt-light);
  border-radius: var(--rt-radius);
  box-shadow: 0 1px 2px rgba(15, 24, 43, 0.06);
  padding: 1.25rem;
}

.rt-btn {
  transition: transform var(--rt-transition), background var(--rt-transition),
              box-shadow var(--rt-transition);
  border-radius: var(--rt-radius);
  /* Explicit, not inherited from a reset: unfold's precompiled bundle does not
     carry Tailwind preflight's `button { cursor: pointer }`, so every <button>
     on a task card showed the default arrow while the one <a> beside them
     showed a hand — the row looked half-clickable. */
  cursor: pointer;
}
.rt-btn:hover {
  transform: scale(1.01);
  box-shadow: 0 1px 3px rgba(15, 24, 43, 0.12);
}
.rt-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

/* Hover FILLS for token-coloured buttons.
   `hover:rt-surface-sunken` and friends are written in templates as if they
   were Tailwind variants of our own classes — but Tailwind only generates a
   `hover:` variant for classes IT knows, and unfold's bundle has never heard
   of `rt-*`. So all eight of these tokens were inert: the buttons had a
   documented hover fill that never once painted. The guard did not catch it
   because it only inspects tokens STARTING with `rt-` (test_css_utilities now
   strips `hover:` / `dark:` prefixes before that check).
   The tokens themselves invert under html.dark, so one definition is correct
   in both modes; the `dark:` spellings resolve to the same rule rather than a
   second palette. */
.hover\:rt-surface-sunken:hover,
html.dark .dark\:hover\:rt-surface-sunken:hover  { background-color: var(--rt-hover); }
.hover\:rt-surface-success:hover,
html.dark .dark\:hover\:rt-surface-success:hover { background-color: var(--rt-success-bg); }
.hover\:rt-surface-risk:hover,
html.dark .dark\:hover\:rt-surface-risk:hover    { background-color: var(--rt-risk-bg); }
html body .hover\:rt-text:hover:not(#_):not(#__)      { color: var(--rt-navy) !important; }
html body .hover\:rt-text-risk:hover:not(#_):not(#__) { color: var(--rt-risk-text) !important; }
/* A link styled as a button. Tailwind's `no-underline` is NOT in unfold's
   precompiled bundle (see test_css_utilities KNOWN_MISSING), so an <a> given
   .rt-btn would keep the admin's underline and link colour and read as a link
   sitting between two buttons. Written here rather than baselined as another
   silent no-op. */
a.rt-btn {
  display: inline-flex;
  align-items: center;
  text-decoration: none;
  color: inherit;
}
a.rt-btn:hover {
  text-decoration: none;
}

/* Timesheet & Calendar Dashboard Styles */
.ts-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 2rem;
}
.month-nav {
  display: flex;
  align-items: center;
  background: var(--rt-light);
  border-radius: var(--rt-radius);
  padding: 0.5rem 1rem;
  border: 1px solid rgba(15, 24, 43, 0.1);
  gap: 1.25rem;
}
.month-nav-btn {
  background: transparent;
  border: none;
  color: var(--rt-navy);
  font-size: 1.2rem;
  cursor: pointer;
  padding: 0.25rem 0.5rem;
  border-radius: var(--rt-radius);
  transition: all var(--rt-transition);
}
.month-nav-btn:hover {
  background: rgba(15, 24, 43, 0.05);
  transform: scale(1.1);
}
.month-label {
  font-size: 1.1rem;
  font-weight: 600;
  min-width: 140px;
  text-align: center;
  color: var(--rt-navy);
}

/* Timesheets column toggle (4P.C2).
   Visual-only hiding: the cells stay in the DOM so filterTable()'s td indices
   remain valid. nth-child is 1-based; the column index is 0-based (col N -> N+1).
   Only optional columns are toggleable — Name(0), Department(1) and Details(6)
   are never hidden. */
.hide-col-2 th:nth-child(3), .hide-col-2 td:nth-child(3) { display: none; }
.hide-col-3 th:nth-child(4), .hide-col-3 td:nth-child(4) { display: none; }
.hide-col-4 th:nth-child(5), .hide-col-4 td:nth-child(5) { display: none; }
.hide-col-5 th:nth-child(6), .hide-col-5 td:nth-child(6) { display: none; }

/* Alpine: prevent the dropdown flashing before Alpine initialises. */
[x-cloak] { display: none !important; }

/* Timesheets: two-panel split (calendar left, department summary right).
   Single column on small screens, 2:1 split from lg up. */
.ts-split {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1.5rem;
}
@media (min-width: 1024px) {
  .ts-split {
    grid-template-columns: 2fr 1fr;
  }
}

/* Calendar Grid */
.calendar-grid-header {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  text-align: center;
  font-weight: 600;
  color: var(--rt-navy);
  font-size: 0.9rem;
  margin-bottom: 0.75rem;
  min-width: 700px;
}
.calendar-grid {
  display: grid;
  grid-template-columns: repeat(7, 1fr);
  gap: 6px;
  min-width: 700px; /* Force minimum width to enable horizontal scroll on mobile */
}
.calendar-cell {
  background: var(--rt-light);
  border-radius: var(--rt-radius);
  min-height: 80px;
  padding: 0.5rem;
  border: 1px solid rgba(15, 24, 43, 0.05);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
}
.calendar-cell.empty {
  background: transparent;
  border: none;
}
.calendar-cell.weekend {
  background: var(--rt-oat);
  opacity: 0.8;
}
/* Decreed exceptions to the ordinary week. Colours are literal rather than
   token-driven on purpose: --rt-navy inverts in dark mode, which would put
   these swatches white-on-white. Left borders rather than fills so the leave
   badges inside the cell stay readable. */
.calendar-cell.public_holiday {
  background: rgba(230, 57, 70, 0.08);
  border-left: 3px solid #e63946;
}
.calendar-cell.rest_day {
  background: rgba(230, 57, 70, 0.04);
  border-left: 3px solid #f4a261;
}
.calendar-cell.work_saturday {
  background: rgba(42, 157, 143, 0.08);
  border-left: 3px solid #2a9d8f;
}
.calendar-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.25rem;
  margin-top: 0.75rem;
  font-size: 0.75rem;
  color: var(--rt-navy-70);
}
.calendar-legend span {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
}
.legend-swatch {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  display: inline-block;
}
.legend-swatch.weekend { background: var(--rt-oat); border: 1px solid rgba(15, 24, 43, 0.15); }
.legend-swatch.public_holiday { background: #e63946; }
.legend-swatch.rest_day { background: #f4a261; }
.legend-swatch.work_saturday { background: #2a9d8f; }
.calendar-decree-warning {
  margin-top: 0.75rem;
  padding: 0.6rem 0.75rem;
  border-radius: var(--rt-radius);
  background: rgba(230, 57, 70, 0.08);
  border-left: 3px solid #e63946;
  color: #9b2c36;
  font-size: 0.8rem;
  line-height: 1.4;
}
.calendar-decree-note {
  margin-top: 0.75rem;
  font-size: 0.72rem;
  color: var(--rt-navy-70);
}
.day-kind-tag {
  font-size: 0.65rem;
  padding: 1px 6px;
  border-radius: 999px;
  margin-left: 0.35rem;
  white-space: nowrap;
  color: #ffffff;
}
.day-kind-tag.weekend { background: #8d99ae; }
.day-kind-tag.public_holiday { background: #e63946; }
.day-kind-tag.rest_day { background: #f4a261; color: #3d2a12; }
.day-kind-tag.work_saturday { background: #2a9d8f; }

/* Work-calendar changelist panel. Literal colours, same reason as above: the
   navy token inverts in dark mode, and this panel must stay readable in both. */
.wc-panel {
  background: var(--rt-light);
  border: 1px solid rgba(15, 24, 43, 0.08);
  border-radius: var(--rt-radius);
  padding: 1rem 1.25rem;
  margin-bottom: 1rem;
  font-size: 0.82rem;
  color: #3c3830;
}
.wc-panel__head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.75rem;
  margin-bottom: 0.5rem;
}
.wc-panel__year { font-size: 1.1rem; font-weight: 700; color: #1b2661; }
.wc-panel__decree { font-weight: 600; color: #2c46a6; }
.wc-panel__source { font-size: 0.7rem; opacity: 0.65; margin-left: auto; }
.wc-panel__lede { margin: 0 0 0.9rem; line-height: 1.5; max-width: 70ch; }
.wc-panel__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  gap: 1rem;
  margin-bottom: 0.9rem;
}
.wc-panel__grid h4 {
  margin: 0 0 0.4rem;
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: #5a554a;
}
.wc-panel__grid ul { margin: 0; padding: 0; list-style: none; }
.wc-panel__grid li {
  padding: 1px 0;
  font-variant-numeric: tabular-nums;
}
.wc-panel__grid li span { opacity: 0.6; margin-left: 0.4rem; }
.wc-panel__none { margin: 0; opacity: 0.6; }
.wc-panel__warn {
  margin: 0 0 0.9rem;
  padding: 0.6rem 0.75rem;
  border-radius: var(--rt-radius);
  background: rgba(230, 57, 70, 0.08);
  border-left: 3px solid #e63946;
  color: #9b2c36;
  line-height: 1.45;
}
.wc-panel__note { margin: 0; font-size: 0.72rem; opacity: 0.75; line-height: 1.5; }
.wc-panel__note a { color: #2c46a6; text-decoration: underline; }
.day-num {
  font-weight: bold;
  font-size: 0.9rem;
  color: var(--rt-navy-70);
}
.holiday-badge {
  background: rgba(230, 57, 70, 0.1);
  color: var(--rt-red);
  font-size: 0.75rem;
  padding: 2px 4px;
  border-radius: 4px;
  margin-top: 2px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  text-align: center;
  font-weight: 600;
}

/* Department aggregates */
.dept-item {
  background: var(--rt-light);
  border-radius: var(--rt-radius);
  padding: 1rem;
  margin-bottom: 0.75rem;
  border-left: 4px solid var(--rt-navy);
  border-top: 1px solid rgba(15, 24, 43, 0.05);
  border-bottom: 1px solid rgba(15, 24, 43, 0.05);
  border-right: 1px solid rgba(15, 24, 43, 0.05);
}
.dept-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 0.5rem;
}
.dept-name {
  font-weight: 700;
  color: var(--rt-navy);
}
.dept-stats {
  font-size: 0.85rem;
  color: var(--rt-navy-70);
}

/* Table styling */
.ts-table {
  width: 100%;
  border-collapse: collapse;
  text-align: left;
}
.ts-table th {
  padding: 0.75rem 1rem;
  /* Fixed brand navy — NOT var(--rt-navy), which inverts to near-white under
     html.dark and left this header white-on-white. The header stays a navy
     bar with white text in both modes. */
  background: #0f182b;
  color: #fff;
  font-weight: 600;
  font-size: 0.85rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}
.ts-table td {
  padding: 0.75rem 1rem;
  border-bottom: 1px solid rgba(15, 24, 43, 0.05);
  color: var(--rt-navy);
  font-size: 0.9rem;
}
.ts-table tbody tr:hover {
  background: var(--rt-light);
}

/* Modal styling */
.ts-modal {
  display: none;
  position: fixed;
  z-index: 1050;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: auto;
  background-color: rgba(15, 24, 43, 0.5);
  backdrop-filter: blur(4px);
}
.ts-modal-content {
  /* Token, not #fff — --rt-light inverts to the dark card surface under html.dark. */
  background-color: var(--rt-light);
  margin: 5% auto;
  padding: 2rem;
  border: 1px solid rgba(15, 24, 43, 0.1);
  border-radius: var(--rt-radius);
  box-shadow: 0 10px 25px -5px rgba(15, 24, 43, 0.1), 0 8px 10px -6px rgba(15, 24, 43, 0.1);
}
.close-modal {
  color: var(--rt-navy-70);
  float: right;
  font-size: 28px;
  font-weight: bold;
  cursor: pointer;
}
.close-modal:hover {
  color: var(--rt-red);
}

.ts-kpi-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: 1rem;
}
@media (min-width: 640px) {
  .ts-kpi-grid {
    grid-template-columns: repeat(3, 1fr);
  }
}


/* ---- Modal shell (Backups console destructive-restore confirmation) ----
   unfold ships no bg-black/60 and no max-w-lg, so the overlay silently had no
   backdrop and the panel no width cap — the CSS guard test caught both. */
.rt-modal-backdrop { background-color: rgba(0, 0, 0, 0.6); }
/* The edit dialog grew a variable-length section: PPC/Kampányok contributes
   eight more controls on top of the five fixed ones, so on a laptop viewport
   the panel ran past the bottom of the screen and took the Save button with
   it — the dialog rendered fine and could not be submitted. Bounded and
   scrolled instead. Tailwind's `max-h-*` and `overflow-y-auto` are not in
   unfold's precompiled bundle, so this is written here. */
.rt-modal-panel    { max-width: 32rem; max-height: 88vh; overflow-y: auto; }


/* ---- Wrapped preformatted text ----
   `whitespace-pre-wrap` is NOT in unfold's precompiled Tailwind bundle, so
   every `<pre class="whitespace-pre-wrap">` in this project silently fell back
   to the UA default `white-space: pre` and never wrapped. In the Triage Queue
   that made one long URL set the pane's intrinsic width, and
   `.split-main { overflow-x: auto }` turned it into a sideways scroll of the
   whole split view.

   `pre-wrap` alone does not break an *unbroken* token — `overflow-wrap:
   anywhere` is what actually breaks a 200-character URL. Both are required. */
.rt-prewrap {
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  word-break: normal;
  max-width: 100%;
}

/* ---- Form controls ----
   `.rt-input` was used in the Triage Queue filter bar from the day it shipped
   and was never defined anywhere — the search box and all three selects have
   been unstyled ever since. The CSS guard could not catch it:
   `_is_tailwind_utility` skips any class containing `rt-`, on the assumption
   that an `rt-` class is ours and therefore defined. Now it is. */
.rt-input {
  width: 100%;
  padding: 0.4rem 0.6rem;
  background-color: var(--rt-light);
  color: var(--rt-navy);
  border: 1px solid var(--rt-border);
  border-radius: var(--rt-radius);
  font-family: var(--rt-font-sans);
  font-size: 0.875rem;
  line-height: 1.4;
  transition: border-color var(--rt-transition), box-shadow var(--rt-transition);
}
.rt-input:focus {
  outline: none;
  border-color: var(--rt-navy-70);
  box-shadow: 0 0 0 3px var(--rt-hover);
}
.rt-input:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

/* `flex-1` is also absent from unfold's build (it is in the guard's
   KNOWN_MISSING baseline), and it sits beside `.rt-input` on the queue's
   search box and client picker. Same fix, our own namespace. */
.rt-flex-1 { flex: 1 1 0%; }


/* ---- Navigation rail (D5) ----
   The Triage Queue is a split view; unfold's 288px sidebar left it fighting
   for width, and unfold's own toggle only has two states — full or gone.
   This adds the missing third: an icon rail that keeps navigation reachable,
   expanding on hover.

   Why this works without touching unfold's templates: #nav-sidebar is
   position:fixed, so widening it on hover OVERLAYS the page instead of
   reflowing it. Only the wrapper (.rt-nav-wrapper, applied via the
   element_classes hook) reserves layout width, and that stays narrow.

   Everything is scoped to >=1280px (Tailwind's xl), the breakpoint at which
   unfold switches from its mobile drawer to a docked sidebar. A rail on a
   phone would be worse than the drawer.

   Depends on four things unfold renders: the #nav-sidebar id, the
   #nav-sidebar-inner id, a wrapper carrying w-[288px], and the body-level
   `sidebarDesktopOpen` Alpine variable. test_unfold_sidebar_contract.py
   asserts all four against the INSTALLED package, so an upgrade that changes
   them fails the build instead of silently breaking this. */
:root {
  --rt-rail-width: 4.25rem;
  --rt-nav-width: 288px;
}

@media (min-width: 1280px) {
  body.rt-rail .rt-nav-wrapper {
    /* Class-vs-class against Tailwind's w-[288px], hence !important. */
    width: var(--rt-rail-width) !important;
  }

  body.rt-rail #nav-sidebar {
    /* id + class outspecifies the utility, so no !important needed here. */
    width: var(--rt-rail-width);
    /* overflow-X only — `overflow: hidden` here would also clip vertical
       scrolling, and a long navigation would become unreachable in the rail. */
    overflow-x: hidden;
    transition: width 140ms ease;
  }

  body.rt-rail #nav-sidebar:hover,
  body.rt-rail #nav-sidebar:focus-within {
    width: var(--rt-nav-width);
    z-index: 60;
    box-shadow: 0 10px 25px -5px rgba(15, 24, 43, 0.18);
  }

  /* Collapse the labels.
     Clipping the container was the first attempt and it was wrong: the label
     spans start at x=62 and the section headings at x=12, both INSIDE a 68px
     rail, so a clip can only ever cut them mid-word — which is exactly the
     sloppy half-word bleed that shipped.

     font-size:0 collapses every label, heading and bare text node without
     needing to know unfold's markup — the labels are unclassed <span>s, so
     there is nothing stable to select anyway. It has to be applied to the
     DESCENDANTS as well as the container: the <h2> section headings carry
     Tailwind's `text-sm`, so they set their own size and would not inherit.

     Scoped with :not(:hover) rather than being undone on hover, so hovering
     simply stops applying it and Tailwind's own sizes take over — no need to
     guess what to restore them to. */
  body.rt-rail #nav-sidebar:not(:hover):not(:focus-within) #nav-sidebar-inner,
  body.rt-rail #nav-sidebar:not(:hover):not(:focus-within) #nav-sidebar-inner * {
    font-size: 0 !important;
    letter-spacing: 0 !important;
  }

  /* Material Symbols glyphs ARE text (ligatures), so they collapse with
     everything else unless given their size back. */
  body.rt-rail #nav-sidebar:not(:hover):not(:focus-within) #nav-sidebar-inner .material-symbols-outlined {
    font-size: 18px !important;
  }

  /* Centre the icons in the rail. Their `mr-3` exists to separate them from a
     label that is no longer there, and without this the column sits visibly
     left of centre. */
  body.rt-rail #nav-sidebar:not(:hover):not(:focus-within) #nav-sidebar-inner a,
  body.rt-rail #nav-sidebar:not(:hover):not(:focus-within) #nav-sidebar-inner h2 {
    justify-content: center;
  }
  body.rt-rail #nav-sidebar:not(:hover):not(:focus-within) #nav-sidebar-inner .material-symbols-outlined {
    margin-right: 0 !important;
  }

  /* ---- Keep the RAIL's vertical rhythm identical to the open menu ----
     Measured in a browser (1600x1000, tools/nav_layout_check.js), the rail
     used to shift every item upward: the group headings collapsed 32px -> 12px
     and the search box 38px -> 0, so by the bottom of the navigation the icons
     sat 118px above their open-menu positions. Toggling the rail therefore
     re-flowed the whole column instead of just narrowing it.

     The cause is `font-size: 0` above. unfold ships Tailwind v4, whose
     `text-sm` line-height is a UNITLESS ratio — it multiplies the font size, so
     zeroing the font zeroes the line box and the row loses its height. Rows
     that carry an explicit height (`h-[38px]` on every link) were never
     affected, which is why only the headings and the search box drifted.

     The fix is to give those two an ABSOLUTE line-height/height, which
     font-size cannot scale away. The numbers are the open menu's own measured
     values; `tools/nav_layout_check.js` re-measures both states in a real
     browser and fails on any drift, because no unit test can see this. */
  body.rt-rail #nav-sidebar:not(:hover):not(:focus-within) #nav-sidebar-inner h2 {
    line-height: 1.25rem !important;   /* = text-sm's 20px line box, unscaled */
  }

  /* The search box keeps its footprint and its magnifier, centred like every
     other icon in the rail. It used to be `display: none` — which is what put
     a 38px hole in the column — with the note that it would "otherwise render
     as an empty bordered rectangle". It is not empty: the icon stays, and the
     input is what gets dropped. */
  body.rt-rail #nav-sidebar:not(:hover):not(:focus-within) #nav-sidebar-inner :has(> input) {
    min-height: 38px;
    justify-content: center;
  }
  body.rt-rail #nav-sidebar:not(:hover):not(:focus-within) #nav-sidebar-inner input {
    display: none !important;
  }
  /* The keyboard-shortcut chip beside it keeps its border and padding at
     font-size 0, so it renders as a stray 4px dash next to the magnifier and
     pushes the icon off-centre. */
  body.rt-rail #nav-sidebar:not(:hover):not(:focus-within) #nav-sidebar-inner kbd {
    display: none !important;
  }

/* The rail toggle is injected next to unfold's own. Below xl it is hidden:
   a media query rather than a Tailwind `xl:` class, because the CSS guard
   would otherwise have to vouch for a utility unfold may not ship. */
.rt-rail-toggle {
  display: none;
}
@media (min-width: 1280px) {
  .rt-rail-toggle {
    display: inline-flex;
    cursor: pointer;
    align-items: center;
  }
}


/* ---- Vertical stack spacing ----
   `space-y-1`, `space-y-1.5`, `space-y-4` and `space-y-8` are all ABSENT from
   unfold's precompiled Tailwind bundle — they sat in the CSS guard's
   KNOWN_MISSING baseline, doing nothing, on the Triage Queue's task list, the
   Client 360 columns, the ClickUp Ops drawer and the batch-add group list.

   The Triage Queue is where it was finally visible: task cards touched each
   other bottom-to-top with no gap at all. The other three screens have the
   same defect and nobody had noticed.

   Same owl selector Tailwind generates, so behaviour matches what the class
   names imply. `space-y-2` is NOT here — that one does exist in the bundle. */
.rt-stack-1   > * + * { margin-top: 0.25rem; }
.rt-stack-1\.5 > * + * { margin-top: 0.375rem; }
.rt-stack-4   > * + * { margin-top: 1rem; }
.rt-stack-8   > * + * { margin-top: 2rem; }
