:root {
  --bg: #f6f7f9;
  --surface: #ffffff;
  --border: #e5e7eb;
  --text: #111827;
  --text-muted: #6b7280;
  --primary: #0f766e;
  --primary-dark: #0d5f59;
  --primary-light: #ccfbf1;
  --danger: #dc2626;
  --danger-light: #fee2e2;
  --warning: #d97706;
  --warning-light: #fef3c7;
  --success: #16a34a;
  --success-light: #dcfce7;
  --info: #2563eb;
  --info-light: #dbeafe;
  /* Two extra colors purely for the Dashboard's Executive Summary charts
     (lib/charts.js) — everywhere else in the app already has a semantic
     color (success/danger/warning/info) to reach for, but a breakdown
     donut over a company's Chart of Accounts can have more distinct GL
     accounts than that palette covers, so these two round it out to 8
     without inventing new "meanings" that would clash with how
     success/danger/etc. are used elsewhere (paid/overdue/etc.). */
  --chart-violet: #7c3aed;
  --chart-slate: #64748b;
  --radius: 8px;
  --sidebar-w: 220px;
}

* { box-sizing: border-box; }
html, body { height: 100%; }
body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
  font-size: 14px;
}
a { color: var(--primary); text-decoration: none; }
a:hover { text-decoration: underline; }

.app-shell { display: flex; height: 100vh; }
.sidebar {
  width: var(--sidebar-w);
  background: #111827;
  color: #e5e7eb;
  flex-shrink: 0;
  padding: 16px 0;
  display: flex;
  flex-direction: column;
  height: 100vh;
  overflow: hidden;
}
.sidebar .brand {
  font-weight: 700;
  font-size: 16px;
  color: #fff;
  padding: 0 20px 16px;
  border-bottom: 1px solid #1f2937;
  margin-bottom: 8px;
  flex-shrink: 0;
}
/* Only the link list scrolls — the company name above it and the user
   box below it (see .user-box) stay fixed in place, always visible,
   exactly like "Retufu" in the topbar never scrolls out of view. */
.sidebar nav { flex: 1; overflow-y: auto; }
.sidebar a.nav-link {
  display: block;
  padding: 9px 20px;
  color: #d1d5db;
  font-size: 13.5px;
}
.sidebar a.nav-link:hover { background: #1f2937; text-decoration: none; }
.sidebar a.nav-link.active { background: var(--primary); color: #fff; font-weight: 600; }
.sidebar .nav-section {
  padding: 14px 20px 4px;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: #6b7280;
}
/* Collapsible sidebar groups (AR, AP, Cashbook, Inventory, Accounting,
   Reports, HR & Payroll, Retufu Console, etc) -- the live user's own
   complaint: every section used to render fully expanded all the time,
   making the sidebar too long, and asked for "a high level and corporate
   UI" grouping everything under its own heading. Modelled on Sage's own
   left-hand navigation (the closest real comparison: traditional
   double-entry SME accounting software, not a single-page app like
   QuickBooks Online, so its collapsible-section pattern is the one that
   fits Retufu's own server-rendered-page architecture without a rebuild).
   Each .nav-section is now a <button>, not a plain heading div, so it's
   keyboard-focusable and screen-reader-announced as a toggle -- restyled
   below to still look identical to the old plain heading text. Whichever
   group contains the page currently on screen is computed SERVER-SIDE
   (layout.ejs) and rendered already-open, so there's no flash of a
   collapsed-then-expanding section on page load and no dependency on JS
   running before the correct group is visible; a small script
   (public/js/app.js) only handles the click-to-toggle/accordion behaviour
   after that. */
.sidebar .nav-section-toggle {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  background: none;
  border: none;
  cursor: pointer;
  font-family: inherit;
  text-align: left;
}
.sidebar .nav-section-toggle:hover { color: #9ca3af; }
.nav-toggle-caret { font-size: 9px; transition: transform 0.15s ease; flex-shrink: 0; }
.nav-group.open .nav-toggle-caret { transform: rotate(180deg); }
.nav-group-body { display: none; }
.nav-group.open .nav-group-body { display: block; }
.sidebar .user-box { padding: 12px 20px; border-top: 1px solid #1f2937; font-size: 12.5px; color: #9ca3af; flex-shrink: 0; }
.sidebar .user-box strong { color: #e5e7eb; display: block; }
.sidebar form { margin-top: 6px; }
button.link-btn {
  background: none; border: none; color: var(--primary); padding: 0; font-size: 13px; cursor: pointer;
}
button.link-btn:hover { text-decoration: underline; }
.sidebar button.link-btn { color: #9ca3af; font-size: 12px; }
.sidebar button.link-btn:hover { color: #fff; }

.main { flex: 1; min-width: 0; height: 100vh; display: flex; flex-direction: column; overflow: hidden; }
.topbar {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  padding: 14px 28px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-shrink: 0;
}
.topbar-title { display: flex; align-items: center; gap: 12px; }
.app-brand-link, .topbar-page-link { text-decoration: none; }
.app-brand-link:hover .app-brand { color: var(--primary-dark); }
.topbar-page-link:hover h1 { color: var(--primary); text-decoration: underline; }
.app-brand { font-size: 21px; font-weight: 800; color: var(--primary); letter-spacing: -0.3px; }
.topbar-divider { color: var(--border); font-size: 18px; font-weight: 400; }

/* Hamburger toggle for the off-canvas sidebar drawer (see views/layout.ejs,
   public/js/app.js). Hidden above the breakpoint below — desktop is
   unaffected, this only ever appears on a narrow viewport. */
.sidebar-toggle-btn {
  display: none;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border: none;
  background: transparent;
  color: var(--text-muted, #6b7280);
  border-radius: 8px;
  cursor: pointer;
  flex-shrink: 0;
  padding: 0;
}
.sidebar-toggle-btn:hover { background: var(--bg); color: var(--text); }
/* Full-viewport click-to-close backdrop behind the drawer -- see the
   sidebar-open @media rules below, which are what actually reveal it. */
.sidebar-backdrop { display: none; }

.impersonation-banner {
  background: var(--warning);
  color: #fff;
  padding: 10px 28px;
  font-size: 13.5px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-shrink: 0;
}
.impersonation-banner form { margin: 0; }
.impersonation-banner button {
  background: rgba(255,255,255,0.2);
  border: 1px solid rgba(255,255,255,0.5);
  color: #fff;
  padding: 5px 12px;
  border-radius: 6px;
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
}
.impersonation-banner button:hover { background: rgba(255,255,255,0.3); }
/* Entity-switch banner reuses .impersonation-banner's layout/warning-color
   wholesale (see views/layout.ejs) — same "you're not in your default
   context, here's how to get back" shape, just a different message. */

/* Topbar entity switcher — see views/layout.ejs. Reuses .brand's exact box
   model (padding/border/margin) so the sidebar's layout doesn't shift
   between a single-entity company (plain .brand div) and a grouped one
   (this button), then layers on dropdown/button-reset styling. */
.entity-switcher-toggle {
  width: 100%; background: none; border: none; cursor: pointer;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  text-align: left; font-family: inherit;
}
.entity-switcher-toggle:hover .entity-switcher-name { color: var(--primary-light); }
.entity-switcher-toggle .dropdown-caret { color: #9ca3af; flex-shrink: 0; }
.entity-switcher-menu { left: 0; right: auto; min-width: 260px; }
.entity-switcher-item { width: 100%; background: none; border: none; font-family: inherit; cursor: pointer; text-align: left; }
.entity-switcher-item.current { cursor: default; }
.entity-switcher-item.current:hover { background: none; }
.entity-switcher-dot {
  width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; background: var(--text-muted);
}
.entity-switcher-dot.current { background: var(--primary); }
.entity-switcher-dot.admin { background: var(--info); }
.entity-switcher-dot.grant { background: var(--success); }
.entity-switcher-dot.home { background: var(--warning); }
/* Optional parent/child hierarchy within a group (see lib/entityAccess.js) —
   a child's row indents beneath the parent's, which renders un-indented and
   first in the list. A flat/symmetric group (no parent designated) never
   applies this class at all, so it looks exactly as it always has. */
.entity-switcher-item-child { padding-left: 28px; }
.topbar h1 { font-size: 16px; font-weight: 500; color: var(--text-muted, #6b7280); margin: 0; }
.content { padding: 24px 28px; flex: 1; overflow-y: auto; }

.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 20px; }
.card + .card { margin-top: 16px; }
/* Cards side by side in a grid must line up; the stacking margin is for stacked cards only. */
[class*="grid"] > .card + .card, .grid > .card + .card { margin-top: 0; }
/* minmax(0, 1fr) rather than a bare 1fr on every one of these — a plain
   1fr track's minimum width defaults to its content's min-content size
   (e.g. a stat-card's bold, nowrap-by-nature Rand value like "R 1 073
   792,00"), so a row with enough cards/long enough figures could never
   actually shrink to fit and instead overflowed sideways out of its
   parent .card, cutting the card's own border off mid-row (seen first on
   the Dashboard's Executive Summary, which nests two grid-3 rows side by
   side — six cards fighting for space). minmax(0, ...) removes that
   content-based floor so the track can shrink all the way down and the
   value text wraps onto a second line instead, exactly like every other
   number on this site already does when its column gets narrow. */
.grid { display: grid; gap: 16px; }
.grid-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
@media (max-width: 900px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
@media (max-width: 600px) { .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; } }

/* Off-canvas sidebar drawer for phone/tablet widths -- applies to BOTH the
   staff/admin nav and the employee self-service portal nav, since they
   share this exact .app-shell/.sidebar/.main markup (see views/layout.ejs).
   Below this breakpoint the sidebar no longer sits in the flex row
   alongside .main (which is what squeezed page content into a narrow
   sliver before this fix -- reported live on the /my/leave page on a
   phone) -- instead it's pulled out of flow (position: fixed), parked
   off-screen to the left, and only slides into view as an overlay when the
   hamburger button (.sidebar-toggle-btn, see above) is tapped, toggled via
   the "sidebar-open" class on <body> (see public/js/app.js). 860px was
   chosen over the existing 900px/600px grid breakpoints because the
   sidebar itself (220px) plus a usable content column needs more room to
   coexist than a card grid does -- most tablets in portrait (768px) and
   all phones fall under it, most tablets in landscape (1024px+) don't. */
@media (max-width: 860px) {
  .sidebar-toggle-btn { display: inline-flex; }
  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    bottom: 0;
    /* Overrides the base rule's height: 100vh (see .sidebar above) --
       100vh is measured against the LAYOUT viewport, which on a phone
       includes the space the browser's own address bar/toolbar occupies
       when visible, so a 100vh box is taller than what's actually on
       screen. top: 0 / bottom: 0 alone does NOT fix this for a
       position: fixed element -- fixed elements are anchored to the
       initial containing block, which browsers size to that same
       address-bar-hidden "worst case" height by default, so the box's
       bottom (the user-box: name, "Account & security", "Log out") still
       ends up clipped below the fold. Live bug report, confirmed on an
       actual phone: "Log out" not visible at all, no way to reach it.
       100dvh is the actual fix -- a "dynamic viewport height" unit that
       tracks the REAL visible viewport as the browser's address bar
       shows/hides, supported by every current mobile browser (Chrome,
       Safari, Samsung Internet). The 100vh line first is a fallback for
       any browser that doesn't understand dvh, which would otherwise
       ignore the whole declaration and end up with no height at all. */
    height: 100vh;
    height: 100dvh;
    /* Belt-and-braces on top of the dvh fix above: if the drawer's
       content is ever taller than the visible viewport for any reason
       (a very short/wide phone, an unusual browser chrome, a future nav
       item added), the drawer itself scrolls instead of silently
       clipping its bottom again -- so "Log out" is always reachable
       either way, exactly as the live user asked for ("scroll down and
       up"), never dependent on getting the height calculation exactly
       right. */
    overflow-y: auto;
    z-index: 1001;
    width: min(var(--sidebar-w), 82vw);
    transform: translateX(-100%);
    transition: transform 0.22s ease;
    box-shadow: 2px 0 16px rgba(0,0,0,0.25);
  }
  body.sidebar-open .sidebar { transform: translateX(0); }
  .sidebar-backdrop {
    display: block;
    position: fixed;
    inset: 0;
    background: rgba(15, 23, 42, 0.5);
    z-index: 1000;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.22s ease;
  }
  body.sidebar-open .sidebar-backdrop { opacity: 1; pointer-events: auto; }
  /* Prevent the page behind the open drawer from scrolling along with it. */
  body.sidebar-open { overflow: hidden; }
  .topbar { padding: 12px 16px; flex-wrap: wrap; row-gap: 8px; }
  .topbar-title { min-width: 0; }
  .topbar-title h1 {
    font-size: 15px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    max-width: 46vw;
  }
  .app-brand { font-size: 18px; }
  .content { padding: 14px 16px; }
}
@media (max-width: 480px) {
  .topbar-title h1 { max-width: 38vw; }
  .content { padding: 12px; }
}

/* min-width: 0 overrides the grid-item default of min-width: auto, which
   is otherwise content-based (see the .grid-2/3/4 comment above) — belt
   and braces alongside minmax(0, 1fr) on the grid tracks themselves, since
   a .stat-card can also appear outside a .grid (e.g. a lone card) where
   this alone wouldn't matter, but inside one it's what actually lets the
   card shrink instead of forcing its row wider than the page. */
.stat-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px; min-width: 0; }
.stat-card .label { font-size: 12px; color: var(--text-muted); text-transform: uppercase; letter-spacing: .04em; }
/* white-space: nowrap + overflow: hidden here, NOT overflow-wrap: anywhere
   — a value wrapping onto 2-3 lines (what overflow-wrap did before) reads
   worse than the same number just shown smaller. public/js/app.js's
   shrinkStatCardValues() measures each value against its actual card width
   on load and on resize and dials font-size down (see its own comment for
   the algorithm) until the whole figure fits on one line, so 24px here is
   only the ceiling for a short number like "R 500,00" — a long one like
   "R 1 073 792,00" shrinks automatically, exactly as long as it needs to,
   regardless of how many digits it runs to. */
.stat-card .value { font-size: 24px; font-weight: 700; margin-top: 6px; white-space: nowrap; overflow: hidden; }
.stat-card .sub { font-size: 12px; color: var(--text-muted); margin-top: 4px; }

/* Executive Summary charts (Dashboard) — the SVGs themselves come from
   lib/charts.js already sized/laid-out server-side; this is just the
   surrounding card chrome, matching the plain .card look used everywhere
   else on this page rather than inventing a new visual language. */
.chart-card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 18px; min-width: 0; }
.chart-card .chart-title { font-size: 13px; font-weight: 700; color: var(--text); margin-bottom: 12px; }
.chart-card svg { display: block; max-width: 100%; }

/* Donut charts sit beside a text legend rather than relying on the tiny
   in-ring <title> tooltip alone — the legend is the primary readable
   source of the breakdown, the ring is the at-a-glance shape of it. */
.donut-row { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; }
.donut-legend { flex: 1; min-width: 160px; display: flex; flex-direction: column; gap: 8px; }
.donut-legend .legend-item { display: flex; align-items: center; gap: 8px; font-size: 13px; min-width: 0; }
.donut-legend .legend-swatch { width: 10px; height: 10px; border-radius: 3px; flex-shrink: 0; }
.donut-legend .legend-name { color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.donut-legend .legend-value { margin-left: auto; color: var(--text-muted); font-variant-numeric: tabular-nums; white-space: nowrap; }

.btn {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--primary); color: #fff; border: none; border-radius: 6px;
  padding: 9px 16px; font-size: 13.5px; font-weight: 600; cursor: pointer; text-decoration: none;
}
.btn:hover { background: var(--primary-dark); text-decoration: none; }
.btn.secondary { background: var(--surface); color: var(--text); border: 1px solid var(--border); }
.btn.secondary:hover { background: #f3f4f6; }
.btn.danger { background: var(--danger); }
.btn.danger:hover { background: #b91c1c; }
.btn.small { padding: 5px 10px; font-size: 12px; }
.btn:disabled { opacity: .5; cursor: not-allowed; }

/* Export dropdown — a single "Export" button that reveals a small menu of
   format choices (PDF / CSV / Excel), used on every report under Reports.
   Plain CSS + a few lines of JS (see public/js/app.js) rather than a UI
   library — this is the only dropdown in the app, doesn't need one. */
.dropdown { position: relative; display: inline-block; }
.dropdown-toggle { display: inline-flex; align-items: center; gap: 6px; }
.dropdown-caret { font-size: 10px; transition: transform .15s ease; }
.dropdown.open .dropdown-caret { transform: rotate(180deg); }
.dropdown-menu {
  display: none; position: absolute; right: 0; top: calc(100% + 6px); z-index: 20;
  min-width: 210px; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: 0 8px 24px rgba(17, 24, 39, 0.12);
  padding: 6px; margin: 0; list-style: none;
}
.dropdown.open .dropdown-menu { display: block; }
.dropdown-item {
  display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: 6px;
  font-size: 13px; font-weight: 500; color: var(--text); text-decoration: none;
}
.dropdown-item:hover { background: var(--bg); text-decoration: none; }
.dropdown-item-badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 32px; height: 22px; border-radius: 4px; font-size: 10px; font-weight: 700;
  letter-spacing: 0.3px; color: #fff; flex-shrink: 0;
}
.dropdown-item-badge.pdf { background: var(--danger); }
.dropdown-item-badge.csv { background: var(--text-muted); }
.dropdown-item-badge.xlsx { background: var(--success); }
.dropdown-item-badge.docx { background: var(--primary); }
.dropdown-item-label { display: flex; flex-direction: column; }
.dropdown-item-desc { color: var(--text-muted); font-size: 11px; font-weight: 400; }

/* Topbar icon buttons (notification bell, Help & Support) — see
   views/layout.ejs. The bell reuses the .dropdown/.dropdown-toggle/
   .dropdown-menu component wholesale (same open/close JS in app.js), just
   with an icon button instead of a text+caret toggle. */
.topbar-actions { display: flex; align-items: center; gap: 4px; }
.topbar-icon-btn {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  width: 36px; height: 36px; border-radius: 8px; border: none; background: transparent;
  color: var(--text-muted); cursor: pointer;
}
.topbar-icon-btn:hover { background: var(--bg); color: var(--text); }
.badge-count {
  position: absolute; top: 3px; right: 3px; min-width: 16px; height: 16px; padding: 0 4px;
  border-radius: 999px; background: var(--danger); color: #fff; font-size: 10px; font-weight: 700;
  display: flex; align-items: center; justify-content: center; line-height: 1;
}
.notification-menu { min-width: 300px; max-height: 360px; overflow-y: auto; }
.dropdown-menu-header {
  padding: 6px 10px; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--text-muted);
}
.dropdown-empty { padding: 14px 10px; font-size: 13px; color: var(--text-muted); }
.dropdown-item-badge.count { background: var(--warning); }
.dropdown-item-badge.success { background: var(--success); }
.dropdown-item-badge.error { background: var(--danger); }
.dropdown-item-badge.info { background: var(--primary); }
/* Inline pending-count pill next to a sidebar nav link (e.g. "My Approvals"
   in the employee portal) — deliberately separate from .badge-count above,
   which is position:absolute and anchored to an icon button's corner; a
   nav-link needs an inline pill that sits in the text flow instead. */
.nav-link-count {
  display: inline-flex; align-items: center; justify-content: center; min-width: 16px; height: 16px;
  padding: 0 4px; margin-left: 6px; border-radius: 999px; background: var(--danger); color: #fff;
  font-size: 10px; font-weight: 700; line-height: 1; vertical-align: middle;
}

/* Help & Support panel — a small floating card under the topbar button,
   hidden by default (display:none, toggled by app.js). Deliberately NOT a
   full-height side sheet — it's a one-shot "send us a message" form, not a
   persistent page-taking-over chat UI. */
.support-panel {
  display: none; position: fixed; top: 70px; right: 28px; z-index: 50;
  width: 320px; max-width: calc(100vw - 40px);
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: 0 12px 32px rgba(17, 24, 39, 0.18);
}
.support-panel.open { display: block; }
.support-panel-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 16px; border-bottom: 1px solid var(--border);
}
.support-panel-close {
  border: none; background: transparent; color: var(--text-muted); font-size: 18px;
  line-height: 1; cursor: pointer; padding: 2px 6px; border-radius: 4px;
}
.support-panel-close:hover { background: var(--bg); color: var(--text); }
.support-panel-body { padding: 14px 16px; }
.support-bubble {
  background: var(--bg); border-radius: 10px; padding: 10px 12px; font-size: 13px;
  color: var(--text); margin-bottom: 12px; line-height: 1.4;
}
.support-panel textarea { font-size: 13.5px; }
.support-module-row {
  display: flex; align-items: center; justify-content: flex-end; gap: 8px; margin-bottom: 10px;
}
.support-module-row label { font-size: 12px; color: var(--text-muted); margin: 0; white-space: nowrap; }
.support-module-row select { width: auto; font-size: 12.5px; padding: 4px 8px; }

table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 10px 12px; border-bottom: 1px solid var(--border); font-size: 13.5px; }
th { font-size: 11.5px; text-transform: uppercase; letter-spacing: .03em; color: var(--text-muted); font-weight: 600; }
tr:last-child td { border-bottom: none; }
.table-wrap { overflow-x: auto; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }

.form-group { margin-bottom: 14px; }
label { display: block; font-size: 13px; font-weight: 600; margin-bottom: 5px; color: #374151; }
input[type=text], input[type=email], input[type=password], input[type=number], input[type=date], input[type=file],
select, textarea {
  width: 100%; padding: 9px 11px; border: 1px solid var(--border); border-radius: 6px; font-size: 13.5px;
  font-family: inherit; background: #fff; color: var(--text);
}
textarea { resize: vertical; min-height: 70px; }
/* Fields locked to a Company Settings default (invoice due date, quote
   expiry, PO expected date, and each document type's default notes) —
   visibly non-interactive rather than looking like an ordinary editable
   field that just happens to reject typing. */
input[readonly], textarea[readonly] { background: #f3f4f6; color: var(--muted); cursor: not-allowed; }
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--primary); box-shadow: 0 0 0 3px var(--primary-light); }
.checkbox-row { display: flex; align-items: center; gap: 8px; }
.checkbox-row input { width: auto; }
.form-actions { display: flex; gap: 10px; margin-top: 20px; }
.form-hint { font-size: 12px; color: var(--text-muted); margin-top: 4px; }
/* Page-level "how this feature works" explanatory paragraphs (as opposed
   to short field-level hints next to individual inputs, which keep using
   plain .form-hint and stay visible to everyone -- hiding those would
   make forms harder to fill in correctly for every user, not just
   support-avoidance text). Marked with this second class deliberately, on
   top of .form-hint, wherever a page has one -- visible only to the
   platform operator (is_superadmin on the users table; layout.ejs puts a
   "platform-admin" class on <body> for that account only). Client company
   users, including their own admins, no longer see the explanation of how
   a feature works, by design: the live user's own reasoning, "this
   explanation will tell the user everything then they won't call me for
   support, they need [to] call me for support to assist them and bill
   them." */
body:not(.platform-admin) .page-hint { display: none; }

/* Show/hide toggle injected by public/js/app.js around every password
   field — the extra right padding on the input makes room for the
   button so typed characters never run under it. */
.password-field { position: relative; }
.password-field input[type=password], .password-field input[type=text] { padding-right: 38px; }
.password-toggle {
  position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  width: 30px; height: 30px; display: flex; align-items: center; justify-content: center;
  background: none; border: none; border-radius: 6px; padding: 0; cursor: pointer; color: var(--text-muted);
}
.password-toggle:hover { color: var(--text); background: var(--bg); }
.password-toggle svg { width: 17px; height: 17px; }

/* Live strength meter injected by public/js/app.js on fields marked
   data-password-strength — hidden until the field has a value. */
.password-strength { display: none; align-items: center; gap: 8px; margin-top: 6px; }
.password-strength-track { flex: 1; height: 4px; border-radius: 2px; background: var(--border); overflow: hidden; }
.password-strength-bar { height: 100%; width: 0; border-radius: 2px; transition: width .15s ease, background-color .15s ease; }
.password-strength-bar.weak { background: var(--danger); width: 25%; }
.password-strength-bar.fair { background: var(--warning); width: 50%; }
.password-strength-bar.good { background: var(--info); width: 75%; }
.password-strength-bar.strong { background: var(--success); width: 100%; }
.password-strength-label { font-size: 12px; font-weight: 600; color: var(--text-muted); white-space: nowrap; }

.badge { display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 11.5px; font-weight: 600; text-transform: capitalize; }
.badge.draft { background: #f3f4f6; color: #4b5563; }
.badge.sent { background: var(--info-light); color: var(--info); }
.badge.paid { background: var(--success-light); color: var(--success); }
.badge.partially_paid { background: var(--warning-light); color: var(--warning); }
.badge.overdue { background: var(--danger-light); color: var(--danger); }
.badge.void { background: #f3f4f6; color: #9ca3af; }
.badge.issued { background: var(--success-light); color: var(--success); }
.badge.accepted { background: var(--success-light); color: var(--success); }
.badge.declined { background: var(--danger-light); color: var(--danger); }
.badge.expired { background: #f3f4f6; color: #9ca3af; }
.badge.recorded { background: #f3f4f6; color: #4b5563; }
.badge.reconciled { background: var(--success-light); color: var(--success); }
.badge.pending { background: var(--warning-light); color: var(--warning); }
.badge.approved { background: var(--success-light); color: var(--success); }
.badge.rejected { background: var(--danger-light); color: var(--danger); }
.badge.cancelled { background: #f3f4f6; color: #9ca3af; }
.badge.active { background: var(--success-light); color: var(--success); }
.badge.fully_depreciated { background: var(--info-light); color: var(--info); }
.badge.disposed { background: #f3f4f6; color: #9ca3af; }
.badge.completed { background: var(--success-light); color: var(--success); }
.badge.open { background: var(--info-light); color: var(--info); }
.badge.closed { background: #f3f4f6; color: #4b5563; }
.badge.terminated { background: #f3f4f6; color: #9ca3af; }
.badge.finalized { background: var(--info-light); color: var(--info); }

.flex { display: flex; align-items: center; }
.flex-between { display: flex; align-items: center; justify-content: space-between; }
.gap-8 { gap: 8px; } .gap-12 { gap: 12px; } .gap-16 { gap: 16px; }
.muted { color: var(--text-muted); }
.mt-0 { margin-top: 0; } .mt-8 { margin-top: 8px; } .mt-16 { margin-top: 16px; } .mt-24 { margin-top: 24px; }
.mb-8 { margin-bottom: 8px; } .mb-16 { margin-bottom: 16px; }
.text-right { text-align: right; }
.empty-state { text-align: center; padding: 48px 20px; color: var(--text-muted); }
.empty-state .btn { margin-top: 12px; }
.alert { padding: 12px 16px; border-radius: 6px; font-size: 13.5px; margin-bottom: 16px; }
.alert.error { background: var(--danger-light); color: #991b1b; }
.alert.success { background: var(--success-light); color: #14532d; }
.alert.info { background: var(--info-light); color: #1e3a8a; }
.alert.warning { background: var(--warning-light); color: #92400e; }

/* One-shot confirmation banner (see lib/flash.js + public/js/app.js) — shown
   for exactly 3 seconds after a create/update/delete/etc action, then fades
   out and removes itself. Ordinary .alert divs elsewhere (form validation
   errors, "past draft" warnings, and so on) are NOT auto-dismissed — only
   this specific banner, identified by id, so a person always has time to
   read something they actually need to act on. */
#flash-banner {
  transition: opacity 0.4s ease, transform 0.4s ease;
}
#flash-banner.flash-dismissing {
  opacity: 0;
  transform: translateY(-6px);
}
.filters { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; align-items: center; }
.filters a, .filters .filter-tag {
  padding: 6px 12px; border-radius: 6px; font-size: 12.5px; background: var(--surface); border: 1px solid var(--border); color: var(--text-muted);
}
.filters a.active { background: var(--primary); border-color: var(--primary); color: #fff; }
.line-items-table input { padding: 6px 8px; font-size: 13px; }
.line-items-table td { padding: 6px; vertical-align: top; }
.totals-box { max-width: 320px; margin-left: auto; }
.totals-box .row { display: flex; justify-content: space-between; padding: 6px 0; font-size: 13.5px; }
.totals-box .row.total { font-size: 16px; font-weight: 700; border-top: 1px solid var(--border); margin-top: 4px; padding-top: 10px; }
.auth-page { display: flex; align-items: center; justify-content: center; min-height: 100vh; background: var(--bg); }
.auth-card { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 36px; width: 100%; max-width: 380px; }
.auth-card h1 { font-size: 20px; margin: 0 0 4px; }
.auth-card p.sub { color: var(--text-muted); font-size: 13px; margin: 0 0 20px; }

/* Login page only — scoped so it doesn't affect the shared .auth-page
   background used by the closed-signup and suspended-workspace screens. */
.auth-page.login-page { background: #111827; }
.auth-shell { display: flex; flex-direction: column; align-items: center; gap: 22px; width: 100%; max-width: 380px; }
.login-brand { font-size: 34px; font-weight: 800; color: var(--primary); letter-spacing: -0.5px; }
.auth-divider { border: none; border-top: 1px solid var(--border); margin: 20px 0 16px; }
.auth-forgot { display: block; text-align: center; font-size: 13px; }
.receipt-thumb { max-width: 100%; border-radius: 6px; border: 1px solid var(--border); }

/* --- Employee self-service portal (views/my/*.ejs) ---------------------
   A dedicated, slightly more polished treatment for the one part of the
   app a non-admin, non-finance person actually spends time in: an avatar +
   name/role "hero" header reused across Workspace/Profile, small icon-led
   card headers for each section, and a consistent look for the
   clock-in/out and leave-balance widgets. Built entirely from the same
   tokens/components as the rest of the app (--primary, .card, .stat-card,
   .badge, .btn) — no new colors or one-off styling, just better
   composition, so it can never visually drift from the main product. */
.portal-hero {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
}
.portal-hero-main { display: flex; align-items: center; gap: 16px; min-width: 0; }
.portal-avatar {
  width: 56px; height: 56px; border-radius: 50%; flex-shrink: 0;
  background: linear-gradient(135deg, var(--primary), var(--primary-dark));
  color: #fff; display: flex; align-items: center; justify-content: center;
  font-size: 19px; font-weight: 700; letter-spacing: 0.5px;
}
.portal-hero-text { min-width: 0; }
.portal-hero-name { margin: 0; font-size: 19px; font-weight: 700; line-height: 1.3; }
.portal-hero-meta { color: var(--text-muted); font-size: 13px; margin-top: 3px; }
.portal-hero-meta .dot { margin: 0 6px; color: var(--border); }
.portal-hero-logo { max-height: 44px; max-width: 180px; object-fit: contain; flex-shrink: 0; }

/* Small icon-led section headers used throughout the portal cards — the
   icon is purely decorative (aria-hidden), so it never competes with the
   card's own heading for what a screen reader announces. */
.portal-card-header { display: flex; align-items: center; gap: 8px; margin-bottom: 14px; }
.portal-card-header .portal-icon { color: var(--primary); flex-shrink: 0; display: inline-flex; }
.portal-card-header strong { font-size: 14px; }
.portal-card-header .portal-card-header-sub { color: var(--text-muted); font-weight: 400; font-size: 12.5px; margin-left: 4px; }

/* Clock in/out — a single, unmistakable primary action rather than a form
   buried in a table row, with the current state read at a glance from the
   ring color (idle/in-progress/done) instead of only the text beside it. */
.portal-clock { display: flex; align-items: center; gap: 18px; flex-wrap: wrap; }
.portal-clock-ring {
  width: 64px; height: 64px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  border: 3px solid var(--border); color: var(--text-muted);
}
.portal-clock-ring.in-progress { border-color: var(--warning); color: var(--warning); }
.portal-clock-ring.done { border-color: var(--success); color: var(--success); }
.portal-clock-status { font-size: 14px; font-weight: 600; }
.portal-clock-sub { color: var(--text-muted); font-size: 12.5px; margin-top: 2px; }

/* Profile/contact "info list" — replaces a bare <table> of label/value
   rows with a slightly airier list that still reads perfectly in a screen
   reader (it's a definition list) and lines up cleanly at any width. */
.info-list { display: flex; flex-direction: column; }
.info-row { display: flex; justify-content: space-between; gap: 16px; padding: 10px 0; border-bottom: 1px solid var(--border); }
.info-row:last-child { border-bottom: none; padding-bottom: 0; }
.info-row dt { color: var(--text-muted); font-size: 13px; margin: 0; flex-shrink: 0; }
.info-row dd { margin: 0; font-size: 13.5px; font-weight: 600; text-align: right; }

/* Performance review — a simple vertical timeline (a rail + dot per
   entry) rather than a plain stacked list, so a history of several
   reviews reads as one continuous record rather than N unrelated cards. */
.portal-timeline { position: relative; padding-left: 22px; }
.portal-timeline::before { content: ''; position: absolute; left: 5px; top: 6px; bottom: 6px; width: 2px; background: var(--border); }
.portal-timeline-item { position: relative; padding-bottom: 20px; }
.portal-timeline-item:last-child { padding-bottom: 0; }
.portal-timeline-item::before {
  content: ''; position: absolute; left: -22px; top: 4px; width: 11px; height: 11px; border-radius: 50%;
  background: var(--primary); border: 2px solid var(--surface); box-shadow: 0 0 0 1px var(--primary);
}
.portal-timeline-item .card { padding: 16px; }
.rating-stars { color: var(--warning); letter-spacing: 1px; font-size: 13px; }
.rating-stars .off { color: var(--border); }

/* POS receipt (views/pos/sale_show.ejs) — printed straight from the
   browser onto an 80mm/58mm thermal roll (see docs/Retufu-POS-High-Level-Plan.docx
   section 6, "Hardware & offline"): everything outside .pos-receipt
   (sidebar, topbar, action buttons) is hidden, and the receipt itself is
   narrowed to roll width with no card chrome, so what prints is just the
   receipt content, not a wide-screen web page. */
@media print {
  .sidebar, .topbar, .sidebar-backdrop, .no-print { display: none !important; }
  .main { margin: 0 !important; padding: 0 !important; }
  .pos-receipt { max-width: 80mm; margin: 0; padding: 0; border: none; box-shadow: none; font-size: 11px; }
}
.pos-till-search { font-size: 16px; padding: 10px 12px; }
.pos-cash-summary { display: flex; gap: 24px; flex-wrap: wrap; }
.pos-cash-summary .stat { min-width: 140px; }
.pos-cash-summary .stat .label { color: var(--text-muted); font-size: 12px; }
.pos-cash-summary .stat .value { font-size: 20px; font-weight: 700; }

/* Organizational chart (routes/orgChart.js, views/org_chart/index.ejs) --
   plain CSS box-and-connector tree, no charting library, same "no build
   step" approach as the rest of this app's client side. Company at the
   top, one branch per department below it, employees as leaf boxes under
   their own department -- see orgChart.js's own header comment for why
   this is built from real Departments/Employees data rather than a
   freeform canvas. */
/* THIRD version of the connector lines, after two CSS-only attempts that
   each broke on real data:
     1. .org-chart-row/.org-chart-branch (a flexbox pair where each branch
        drew half its own connector line off its OWN box's percentage
        width) -- broke once a department's ranked position tree had
        siblings of very different widths, AND separately broke at the
        department-fan-out level itself once there were enough
        departments to flex-wrap onto a second row (the live user's own
        screenshot: 4 departments, one wrapped, and the connector line
        for it shot off sideways instead of dropping down to it, because
        the CSS math had no idea a wrap had happened).
     2. An HTML <table> for the ranked tree only -- fixed alignment, but
        let every box balloon to fill its table column since a block-level
        box has nothing capping its own width inside a <td>.
   Both failed because CSS was trying to PREDICT where a box would land
   before the browser had laid anything out. This version doesn't predict:
   every box on the page, from the CEO down through departments down
   through every rank inside a ranked department, is laid out with plain
   flexbox (.oc-node/.oc-children below -- align-items:center sizes each
   box to its own content and never stretches it; flex-wrap:nowrap so nothing
   can silently reflow the connector math doesn't know about, wide charts
   scroll horizontally via .org-chart's own overflow-x:auto instead of
   wrapping), and a small script (public/js/org-chart-lines.js) then
   measures the REAL rendered position of every box with
   getBoundingClientRect and draws every connector line in the whole chart
   -- CEO to departments, department head down through every rank -- from
   those actual coordinates, using the exact same algorithm at every
   level. See that script's own comment for the drawing algorithm, and
   _position_branch.ejs's for the markup contract. A second, unrelated bug
   fixed earlier and still true here -- one box rendered PER OPEN VACANCY
   SLOT used to produce a link nested inside another link (invalid HTML) --
   stays fixed in org_chart/index.ejs: vacancies collapse into one box per
   named position with two sibling (never nested) links. */
.org-chart { display: flex; flex-direction: column; align-items: center; padding: 24px 8px 40px; overflow-x: auto; }
.org-chart-box { border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); padding: 10px 14px; text-align: center; font-size: 13.5px; }
.org-chart-box-root { background: var(--primary); color: #fff; font-weight: 700; border-color: var(--primary); padding: 12px 28px; border-radius: 999px; }
.org-chart-box-dept { min-width: 160px; font-weight: 600; background: var(--primary-light); border-color: var(--primary); border-radius: 999px; }
.org-chart-box-header { display: flex; align-items: center; justify-content: center; gap: 6px; }
.org-chart-edit-link { color: var(--text-muted); text-decoration: none; font-size: 13px; }
.org-chart-edit-link:hover { color: var(--primary); }
.org-chart-box-unassigned { min-width: 160px; font-weight: 600; border-style: dashed; color: var(--text-muted); border-radius: 999px; }
.org-chart-box-person { display: block; min-width: 120px; text-decoration: none; color: var(--text); border-radius: 999px; }
.org-chart-box-person:hover { border-color: var(--primary); }
.org-chart-box-unassigned-person { border-style: dashed; }
/* Still used directly (not via the SVG script) for the one connector type
   that was never actually at risk of misalignment: a department dropping
   straight down to its own plain flat employee/vacancy list is always
   exactly one line to one target, no fan-out, nothing to get wrong. */
.org-chart-connector { width: 2px; height: 20px; background: var(--primary); }
.org-chart-connector-small { height: 14px; }
/* Back to a single vertical stack, matching the reference image (each
   department's own people run straight down in one column, connected by
   nothing more than the branch's own vertical spine) -- the earlier
   wrapping-grid version existed only to absorb one box PER OPEN VACANCY
   SLOT, which is gone now (routes/orgChart.js's vacancy count collapses
   into a single "N open positions" box -- see org_chart/index.ejs), so a
   real department's employee list is short enough that a plain column
   never gets out of hand the way a raw vacancy count could. */
.org-chart-leaves { display: flex; flex-direction: column; gap: 8px; align-items: stretch; }
.org-chart-hod { display: block; font-size: 12px; font-weight: 600; color: var(--primary); margin-top: 4px; }
.org-chart-hod-missing { color: var(--text-muted); font-weight: 400; }
.org-chart-box-vacancy { border-style: dashed; border-color: var(--danger); border-radius: 999px; background: var(--danger-light); color: var(--danger); min-width: 120px; }
.org-chart-box-vacancy a { color: inherit; text-decoration: underline; }

/* Multi-level "Reports to" position tree (org_chart/_position_branch.ejs)
   -- THIRD approach, after two CSS-only attempts. History, kept because it
   explains why this one is built the way it is:
     1. A flexbox pair matching .org-chart-row/.org-chart-branch above --
        broke on the live user's real data because that technique has each
        sibling draw half its own connector line off its OWN box width,
        which only lines up when every sibling renders about the same
        width. A real ranked tree doesn't: one sibling can have three more
        levels hanging off it, the one next to it can be a single
        childless box.
     2. An HTML <table>, whose cell edges always align regardless of
        content width -- fixed the line-alignment problem, but exposed a
        different one: .org-chart-box-person's `display: block` (below)
        had always relied on its flex container sizing it to its own
        content; inside a <td> there's no such thing, a table cell's width
        comes from the WIDEST content anywhere in that column, and a
        block box with no max-width obediently stretched to fill it --
        every box ballooned into a distorted capsule.
   Both attempts share a root cause: CSS trying to PREDICT where a box
   will land before the browser lays anything out. This version doesn't
   predict -- it lets the browser lay the boxes out with plain flexbox
   (align-items:center sizes each box to its own content and never
   stretches it, the same behaviour that already made the department-level
   fan-out above work correctly from day one), and a small script
   (public/js/org-chart-lines.js) then measures the REAL rendered position
   of every box with getBoundingClientRect and draws the connecting lines
   with SVG from those actual coordinates, after layout has already
   happened. It can't be wrong about where a box is, because it isn't
   guessing -- the same technique real org-chart libraries use under the
   hood. See that script's own comment for the drawing algorithm, and
   _position_branch.ejs's comment for the markup contract it depends on. */
.oc-tree { position: relative; }
.oc-lines-svg { position: absolute; top: 0; left: 0; z-index: 0; overflow: visible; pointer-events: none; }
.oc-children { display: flex; flex-direction: row; gap: 40px; align-items: flex-start; justify-content: center; flex-wrap: nowrap; padding-top: 40px; }
.oc-node { display: flex; flex-direction: column; align-items: center; position: relative; z-index: 1; }
.oc-node .org-chart-box { max-width: 210px; }
.oc-node .org-chart-box-root { max-width: 320px; }
.oc-child { display: flex; }

/* ==========================================================================
   Brand heading bands: every card's heading uses the system colour
   (--primary) so pages are no longer plain white. Applies globally to the
   first <strong>/<h2>/<h3> of a .card. Print stays plain.
   ========================================================================== */
.card > strong:first-child,
.card > h2:first-child,
.card > h3:first-child {
  display: block;
  margin: -20px -20px 16px;
  padding: 10px 20px;
  background: var(--primary);
  color: #fff;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: .01em;
  border-radius: calc(var(--radius) - 1px) calc(var(--radius) - 1px) 0 0;
}
.card > strong:first-child .form-hint,
.card > strong:first-child .muted { color: rgba(255, 255, 255, .85); }
.card > strong:first-child a { color: #fff; text-decoration: underline; }
.topbar h1 { color: var(--primary-dark); font-weight: 600; }
thead th { background: var(--primary-light); color: var(--primary-dark); }
tfoot th, tfoot td { background: var(--primary-light); color: var(--primary-dark); }
@media print {
  .card > strong:first-child, .card > h2:first-child, .card > h3:first-child { background: none; color: #000; margin: 0 0 8px; padding: 0; }
  thead th, tfoot th, tfoot td { background: none; color: #000; }
}

/* ==========================================================================
   Automatic device sizing. public JS (see the inline script in layout.ejs)
   puts one of device-phone / device-tablet / device-desktop on <html>, plus
   touch, short-screen and landscape/portrait, and re-checks on resize and
   rotation. Phones and tablets already get the drawer sidebar and stacked
   grids from the breakpoints above; this adds tablet/till sizing, touch
   targets and compact heights for small till screens.
   ========================================================================== */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }
html.touch a, html.touch button, html.touch select, html.touch input, html.touch label { touch-action: manipulation; -webkit-tap-highlight-color: transparent; }

/* Touch screens: finger-sized targets, and 16px inputs so iOS/Android do not zoom on focus */
html.touch .btn, html.touch button.btn { min-height: 44px; padding-left: 16px; padding-right: 16px; }
html.touch input[type=text], html.touch input[type=email], html.touch input[type=password], html.touch input[type=number],
html.touch input[type=date], html.touch select, html.touch textarea { min-height: 44px; font-size: 16px; }
html.touch .nav-link, html.touch .nav-section-toggle { padding-top: 12px; padding-bottom: 12px; }
html.touch .remove-row-btn { min-width: 44px; min-height: 44px; }
html.touch th, html.touch td { padding-top: 12px; padding-bottom: 12px; }

/* Tablets (either orientation): a slimmer fixed sidebar, a little less padding */
html.device-tablet { --sidebar-w: 200px; }
html.device-tablet .content { padding: 18px 20px; }
html.device-tablet .grid-4 { grid-template-columns: repeat(2, minmax(0, 1fr)); }

/* Phones: single column forms, full-width buttons */
html.device-phone .form-actions { display: flex; flex-direction: column; gap: 8px; }
html.device-phone .form-actions .btn { width: 100%; }
html.device-phone .pos-cash-summary { gap: 12px; }
html.device-phone .pos-cash-summary .stat { min-width: 0; flex: 1 1 45%; }

/* Small till screens (height 600px or less, e.g. 1024x600 or landscape phones): compact vertical rhythm */
html.short-screen .topbar { min-height: 44px; }
html.short-screen .content { padding-top: 10px; padding-bottom: 10px; }
html.short-screen .card { padding: 14px; }
html.short-screen .card > strong:first-child, html.short-screen .card > h2:first-child, html.short-screen .card > h3:first-child { margin: -14px -14px 12px; padding: 8px 14px; }
html.short-screen .form-group { margin-bottom: 8px; }
html.short-screen .pos-cash-summary .stat .value { font-size: 17px; }

/* Till on a touch screen: bigger search box and totals */
html.touch .pos-till-search { font-size: 18px; min-height: 52px; }
html.touch .pos-cash-summary .stat .value { font-size: 24px; }

/* Never let any page scroll sideways because of one wide element */
.content, .main { min-width: 0; }
img, svg, video, canvas { max-width: 100%; height: auto; }
.portal-timeline-item .card > strong:first-child { margin: -16px -16px 12px; padding: 8px 16px; }

/* ==========================================================================
   Org chart: boxes are rounded rectangles that grow with their text (the old
   fully-round pill cut off wrapped lines at the curved corners), long words
   wrap, the chart centres when it fits and scrolls from the LEFT edge when it
   is wider than the screen (centring used to push the left side off-screen),
   and gaps shrink on smaller screens.
   ========================================================================== */
/* The tree is exactly as wide as its content (never squeezed to the screen, which pushed the left
   branches off-screen and broke the connector coordinates); org-chart-lines.js then scales the whole
   tree down to fit the screen width, so there is normally no sideways scrolling. */
.org-chart { align-items: flex-start; overflow-x: auto; }
.oc-tree { width: max-content; transform-origin: 0 0; }
.org-chart-box, .org-chart-box-root, .org-chart-box-dept, .org-chart-box-unassigned,
.org-chart-box-person, .org-chart-box-vacancy { border-radius: 14px; }
.org-chart-box-root { padding: 12px 28px; }
.oc-node .org-chart-box { padding: 8px 14px; min-width: 130px; max-width: 200px; overflow-wrap: anywhere; word-break: normal; line-height: 1.35; box-sizing: border-box; }
.oc-node .org-chart-box > * { max-width: 100%; }
.oc-node .org-chart-box .form-hint, .oc-node .org-chart-box span { overflow-wrap: anywhere; }
.oc-node .org-chart-box-root { max-width: 320px; }
.oc-children { gap: clamp(10px, 1.6vw, 28px); padding-top: 32px; }
.oc-child { flex: 0 0 auto; }
@media (max-width: 900px) { .oc-node .org-chart-box { font-size: 12.5px; min-width: 130px; max-width: 190px; padding: 8px 12px; } }

/* Flash banner as a floating toast. It used to sit in the page flow and then remove itself after
   3 seconds, so everything under it jumped up. Floating means it takes no space, so nothing moves
   when it appears or disappears. */
#flash-banner {
  position: fixed;
  top: 64px;
  left: 50%;
  transform: translateX(-50%);
  z-index: 2000;
  width: max-content;
  max-width: min(720px, calc(100vw - 32px));
  margin: 0;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.18);
  pointer-events: none;
}
#flash-banner.flash-dismissing {
  opacity: 0;
  transform: translateX(-50%) translateY(-6px);
}

/* Sign-out warning shown one minute before an idle session ends (public/js/session-timeout.js). */
.session-warning {
  position: fixed;
  left: 50%;
  bottom: 24px;
  transform: translateX(-50%);
  z-index: 3000;
  display: flex;
  align-items: center;
  gap: 12px;
  max-width: min(560px, calc(100vw - 32px));
  padding: 12px 16px;
  border-radius: 8px;
  background: #92400e;
  color: #fff;
  font-size: 13.5px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.25);
}

