/* ==========================================================================
   AFENET HRIS — design system
   Brand tokens derived from afenet.net, applied to an application layout.
   Structure: tokens → app shell (sidebar + topbar) → components.
   ========================================================================== */

:root{
  /* ---- Brand ---- */
  --afenet-blue:#21409a;        /* primary: sidebar, primary action */
  --afenet-blue-dark:#152a66;   /* sidebar gradient base, pressed */
  --afenet-blue-mid:#2f56c4;    /* hover, gradients */
  --afenet-blue-light:#eef2fd;  /* tinted surface (legacy name, kept) */
  --afenet-blue-100:#dbe3fb;
  --afenet-red:#e8453c;         /* CTA / urgent. Never small text on white. */
  --afenet-red-50:#fdecea;
  --afenet-gold:#f5c518;        /* attention. Fill only, never a text colour. */
  --afenet-gold-50:#fdf6e0;
  --afenet-teal:#1a7f96;
  --afenet-teal-50:#e4f2f5;
  --afenet-green:#1f8a3d;
  --afenet-green-50:#e4f5e8;

  /* ---- Neutrals ---- */
  --bg:#f5f7fc;
  --canvas:#f5f7fc;
  --card:#ffffff;
  --border:#e3e7f2;
  --border-soft:#eef1f8;
  --text:#111a2e;
  --text-2:#38415a;
  --muted:#6b7590;
  --warn:#8a6a00;
  --danger:#b3261e;

  /* ---- Type ---- */
  --font-display:'Poppins','Segoe UI',-apple-system,Roboto,Helvetica,Arial,sans-serif;
  --font-body:'Inter','Segoe UI',-apple-system,Roboto,Helvetica,Arial,sans-serif;

  /* ---- Shape + depth ---- */
  --radius-sm:8px;
  --radius-md:12px;
  --radius-lg:16px;
  --radius-pill:999px;
  --shadow-sm:0 1px 2px rgba(17,26,46,.06),0 1px 3px rgba(17,26,46,.04);
  --shadow-md:0 4px 14px rgba(17,26,46,.07);
  --shadow-lg:0 14px 40px rgba(17,26,46,.14);

  /* ---- Layout ---- */
  --sidebar-w:250px;
  --topbar-h:60px;
}

*{box-sizing:border-box}
html,body{margin:0;padding:0}
body{
  font-family:var(--font-body);
  background:var(--canvas);
  color:var(--text);
  font-size:14px;line-height:1.5;
  -webkit-font-smoothing:antialiased;
}
h1,h2,h3,h4{font-family:var(--font-display);letter-spacing:-.01em}
button{font-family:inherit}

/* ==========================================================================
   APP SHELL — sidebar + topbar + content
   ========================================================================== */
.app-shell{display:flex;min-height:100vh;background:var(--canvas)}

/* ---------- sidebar ---------- */
.side{
  width:var(--sidebar-w);flex-shrink:0;
  background:linear-gradient(180deg,var(--afenet-blue) 0%,#1c3688 62%,var(--afenet-blue-dark) 100%);
  color:#fff;display:flex;flex-direction:column;position:sticky;top:0;height:100vh;z-index:40;
}
.side::after{
  content:'';position:absolute;inset:0;pointer-events:none;
  background:radial-gradient(420px 220px at 110% -5%,rgba(255,255,255,.10),transparent 70%);
}
.side-brand{display:flex;align-items:center;gap:11px;padding:16px 18px 15px;position:relative;z-index:1}
.side-brand img{height:30px;width:auto;background:#fff;padding:4px 6px;border-radius:7px;box-shadow:0 1px 3px rgba(0,0,0,.18)}
.side-brand .bt{line-height:1.15;min-width:0}
.side-brand .bt b{font-family:var(--font-display);font-size:14.5px;font-weight:600;display:block}
.side-brand .bt span{font-size:10.5px;opacity:.72;letter-spacing:.03em;display:block}

.side-nav{flex:1;overflow-y:auto;padding:4px 10px 14px;position:relative;z-index:1}
.side-nav::-webkit-scrollbar{width:5px}
.side-nav::-webkit-scrollbar-thumb{background:rgba(255,255,255,.2);border-radius:4px}
.nav-group{
  font-size:10px;letter-spacing:.14em;text-transform:uppercase;opacity:.55;font-weight:600;
  padding:16px 12px 7px;
}
.side-nav button,.nav-item{
  display:flex;align-items:center;gap:10px;width:100%;text-align:left;
  background:none;border:none;color:rgba(255,255,255,.86);cursor:pointer;
  padding:9px 12px;border-radius:var(--radius-sm);font-size:13.2px;font-weight:500;
  transition:background .14s ease,color .14s ease;position:relative;margin-bottom:1px;
  font-family:inherit;
}
.side-nav button svg{flex-shrink:0;opacity:.82}
.side-nav button:hover{background:rgba(255,255,255,.10);color:#fff}
.side-nav button.active{background:rgba(255,255,255,.15);color:#fff;font-weight:600}
.side-nav button.active svg{opacity:1}
.side-nav button.active::before{
  content:'';position:absolute;left:-10px;top:7px;bottom:7px;width:3px;
  background:var(--afenet-gold);border-radius:0 3px 3px 0;
}
.side-nav button.planned{opacity:.45;cursor:default}
.side-nav button.planned:hover{background:none;color:rgba(255,255,255,.86)}
.nav-soon{
  margin-left:auto;font-size:9px;letter-spacing:.06em;text-transform:uppercase;font-weight:600;
  background:rgba(255,255,255,.14);padding:2px 6px;border-radius:4px;
  white-space:nowrap;flex-shrink:0;
}
.side-nav button.planned span:first-of-type{line-height:1.25}
.nav-count{
  margin-left:auto;background:var(--afenet-red);color:#fff;font-size:10.5px;font-weight:700;
  min-width:19px;height:19px;border-radius:var(--radius-pill);display:flex;align-items:center;
  justify-content:center;padding:0 5px;
}
.side-foot{
  padding:12px 16px;border-top:1px solid rgba(255,255,255,.13);position:relative;z-index:1;
  font-size:11px;opacity:.65;
}

/* ---------- main column ---------- */
.main-col{flex:1;min-width:0;display:flex;flex-direction:column}

header{
  height:var(--topbar-h);background:var(--card);border-bottom:1px solid var(--border);
  display:flex;align-items:center;gap:12px;padding:0 20px;position:sticky;top:0;z-index:30;
}
header .brand{display:none}
header .crumb{min-width:0}
header .crumb span{font-size:12px;color:var(--muted);display:block;line-height:1.2}
header .crumb b{
  font-family:var(--font-display);font-size:14px;font-weight:600;color:var(--text);
  display:block;line-height:1.3;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;
}
header .tagline{display:none}
header .spacer{flex:1}
header .who-wrap{display:flex;align-items:center;gap:9px}
header .who{font-size:12.5px;text-align:right;line-height:1.25}
header .who b{display:block;font-size:12.5px;font-weight:600;color:var(--text)}
header .who span{display:block;font-size:10.5px;color:var(--muted);text-transform:capitalize}
.avatar{
  width:34px;height:34px;border-radius:var(--radius-pill);background:var(--afenet-blue);color:#fff;
  display:flex;align-items:center;justify-content:center;font-weight:700;font-size:12.5px;
  flex-shrink:0;font-family:var(--font-display);letter-spacing:.3px;
}
header button{
  background:var(--card);border:1px solid var(--border);color:var(--text-2);
  padding:7px 12px;border-radius:var(--radius-sm);cursor:pointer;font-size:12.5px;font-weight:500;
  display:flex;align-items:center;gap:6px;transition:.15s;
}
header button:hover{background:var(--afenet-blue-light);border-color:var(--afenet-blue-100);color:var(--afenet-blue)}
.sidebar-toggle{display:none}

main{padding:22px 24px 40px;max-width:1280px;width:100%;margin:0 auto}

/* ---------- mobile bottom tab bar ---------- */
.mobile-tabs{display:none}
.more-sheet{display:none}

/* ==========================================================================
   NOTIFICATIONS
   ========================================================================== */
.notif-wrap{position:relative}
.bell-btn{
  background:var(--card);border:1px solid var(--border);color:var(--text-2);
  width:34px;height:34px;border-radius:var(--radius-pill);cursor:pointer;
  display:flex;align-items:center;justify-content:center;position:relative;transition:.15s;flex-shrink:0;
}
.bell-btn:hover{background:var(--afenet-blue-light);border-color:var(--afenet-blue-100);color:var(--afenet-blue)}
.notif-badge{
  position:absolute;top:-3px;right:-3px;background:var(--afenet-red);color:#fff;font-size:9.5px;font-weight:700;
  min-width:17px;height:17px;border-radius:var(--radius-pill);display:flex;align-items:center;
  justify-content:center;padding:0 4px;border:2px solid var(--card);
}
.notif-panel{
  position:absolute;top:44px;right:0;width:340px;max-height:420px;overflow-y:auto;
  background:var(--card);color:var(--text);border:1px solid var(--border);
  border-radius:var(--radius-md);box-shadow:var(--shadow-lg);z-index:60;
}
.notif-panel-head{display:flex;justify-content:space-between;align-items:center;gap:10px;padding:12px 14px;border-bottom:1px solid var(--border-soft)}
.notif-panel-head b{font-size:13.5px}
.notif-list{display:flex;flex-direction:column}
.notif-item{display:flex;gap:10px;padding:11px 14px;border-bottom:1px solid var(--border-soft);cursor:pointer;transition:background .12s ease}
.notif-item:last-child{border-bottom:none}
.notif-item:hover{background:var(--afenet-blue-light)}
.notif-item.unread{background:#f7f9ff}
.notif-item .notif-dot{width:8px;height:8px;border-radius:50%;background:var(--border);margin-top:5px;flex-shrink:0}
.notif-item.unread .notif-dot{background:var(--afenet-blue)}
.notif-msg{font-size:12.5px;line-height:1.4}
.notif-time{font-size:11px;color:var(--muted);margin-top:2px}

/* ==========================================================================
   CARDS / HERO / STATS
   ========================================================================== */
.card{
  background:var(--card);border:1px solid var(--border);border-radius:var(--radius-md);
  padding:20px;margin-bottom:16px;box-shadow:var(--shadow-sm);transition:box-shadow .18s ease;
}
.card:hover{box-shadow:var(--shadow-md)}
.card h2{margin:0 0 12px;font-size:16px;font-weight:600;display:flex;align-items:center;gap:8px}
.card h3{font-size:13.5px;color:var(--text-2);margin:0 0 8px;font-weight:600}

.hero{
  background:linear-gradient(115deg,var(--afenet-blue) 0%,#2b4bad 55%,#3a63d6 100%);
  color:#fff;border:none;border-radius:var(--radius-lg);padding:24px 26px;
  box-shadow:var(--shadow-md);position:relative;overflow:hidden;margin-bottom:18px;
}
.hero::after{
  content:'';position:absolute;right:-70px;top:-80px;width:260px;height:260px;border-radius:50%;
  background:radial-gradient(circle,rgba(255,255,255,.16) 0%,transparent 70%);
}
.hero::before{
  content:'';position:absolute;left:-40px;bottom:-90px;width:200px;height:200px;border-radius:50%;
  border:1px solid rgba(255,255,255,.16);
}
.hero h2{margin:0 0 5px;font-size:20px;font-weight:600;color:#fff;position:relative}
.hero p{margin:0;font-size:13px;opacity:.9;position:relative}
.hero .hero-meta{display:flex;gap:24px;margin-top:15px;flex-wrap:wrap;font-size:12.5px;opacity:.92;position:relative}
.hero .hero-meta b{font-weight:600}

.stat-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(168px,1fr));gap:13px;margin-bottom:18px}
.stat-card{
  background:var(--card);border:1px solid var(--border);border-radius:var(--radius-md);
  padding:16px 17px;box-shadow:var(--shadow-sm);display:flex;flex-direction:column;gap:7px;
  transition:transform .15s ease,box-shadow .15s ease;position:relative;overflow:hidden;
}
.stat-card:hover{transform:translateY(-2px);box-shadow:var(--shadow-md)}
.stat-card .stat-icon{
  width:34px;height:34px;border-radius:9px;display:flex;align-items:center;justify-content:center;
  background:var(--afenet-blue-light);color:var(--afenet-blue);flex-shrink:0;
}
.stat-card.green .stat-icon{background:var(--afenet-green-50);color:var(--afenet-green)}
.stat-card.gold .stat-icon{background:var(--afenet-gold-50);color:#8a6a00}
.stat-card.danger .stat-icon{background:var(--afenet-red-50);color:var(--afenet-red)}
.stat-card .stat-value{
  font-family:var(--font-display);font-size:25px;font-weight:600;color:var(--text);line-height:1.1;
  font-variant-numeric:tabular-nums;
}
.stat-card .stat-label{font-size:11.5px;color:var(--muted);font-weight:500;letter-spacing:.02em;text-transform:none}

/* ==========================================================================
   TABLES
   ========================================================================== */
table{width:100%;border-collapse:collapse;font-size:13px}
thead th{
  position:sticky;top:0;background:#f7f9fe;text-align:left;padding:10px 12px;color:var(--muted);
  font-weight:600;font-size:11px;text-transform:uppercase;letter-spacing:.07em;
  border-bottom:1px solid var(--border);z-index:2;white-space:nowrap;
}
td{
  text-align:left;padding:11px 12px;border-bottom:1px solid var(--border-soft);vertical-align:middle;
  /* An email address is one unbreakable token, so a column holding
     "akiggundu@afenet.net" cannot be narrower than that string - which is how a
     ten-column table ends up wider than the card holding it. Letting long tokens
     break removes the forced minimum, and normal text is unaffected because it
     already has spaces to break at. */
  overflow-wrap:anywhere;
}
tbody tr{transition:background .12s ease}
tbody tr:nth-child(even){background:transparent}
tbody tr:hover{background:#fafbff}
tbody tr:last-child td{border-bottom:none}

/* ==========================================================================
   BADGES — one status vocabulary across every module
   ========================================================================== */
.badge{
  display:inline-flex;align-items:center;gap:5px;padding:3px 9px;border-radius:var(--radius-pill);
  font-size:11.5px;font-weight:600;line-height:1.5;white-space:nowrap;
}
.badge::before{content:'';width:6px;height:6px;border-radius:50%;background:currentColor;flex-shrink:0}
.badge.draft{background:#eef1f8;color:#5b6377}
.badge.submitted{background:var(--afenet-blue-light);color:var(--afenet-blue)}
.badge.approved,.badge.locked,.badge.signed,.badge.hq_approved,.badge.paid{background:var(--afenet-green-50);color:#166b2f}
.badge.returned,.badge.returned_to_country,.badge.declined,.badge.rejected{background:var(--afenet-red-50);color:#b8302a}
.badge.country_validated,.badge.submitted_to_hq,.badge.sent,.badge.viewed,.badge.finance_reviewed{background:var(--afenet-teal-50);color:#136374}
.badge.pending,.badge.expiring{background:var(--afenet-gold-50);color:#8a6a00}
.badge.withdrawn{background:#f0f1f5;color:#6b7590}
/* LIFECYCLE STATUSES, added with the badge() case fix in batch 56.
   Lower-casing the class revealed a second, quieter gap: `active` and `ended`
   reach badge() from the project roster and the staff record and had no rule
   at all, so they fell through to the bare base badge even once the case was
   right. They are not workflow states - nothing is waiting on them - so they
   are deliberately quieter than `approved` and `declined`: a fact about a
   record, not a decision about one. */
.badge.active{background:var(--afenet-green-50);color:#166b2f}
.badge.ended,.badge.closed,.badge.inactive,.badge.cancelled{background:#f0f1f5;color:#6b7590}
.badge.suspended{background:var(--afenet-gold-50);color:#8a6a00}
.badge.open{background:var(--afenet-blue-light);color:var(--afenet-blue)}
.badge.unpaid{background:var(--afenet-red-50);color:#b8302a}

/* ==========================================================================
   FORMS
   ========================================================================== */
form.inline{display:flex;flex-wrap:wrap;gap:12px;align-items:end}
form.inline label,.field-grid label,.login-form-inner label{
  display:flex;flex-direction:column;gap:5px;
  font-size:11.5px;font-weight:600;color:var(--text-2);letter-spacing:.01em;
}
input,select,textarea{
  padding:9px 11px;border:1px solid var(--border);border-radius:var(--radius-sm);
  font-size:13px;font-family:inherit;background:var(--card);color:var(--text);
  transition:border-color .15s ease,box-shadow .15s ease;
}
input:focus,select:focus,textarea:focus{
  outline:none;border-color:var(--afenet-blue);box-shadow:0 0 0 3px rgba(33,64,154,.13);
}
input:disabled,select:disabled,textarea:disabled{background:#f7f9fd;color:var(--muted);cursor:not-allowed}
textarea{min-width:240px}

/* ==========================================================================
   BUTTONS
   One primary action per screen. Red is reserved for destructive or
   time-critical actions, so it stays rare and keeps its meaning.
   ========================================================================== */
button.primary{
  background:var(--afenet-blue);color:#fff;border:1px solid var(--afenet-blue);
  padding:9px 16px;border-radius:var(--radius-sm);cursor:pointer;font-size:13px;font-weight:600;
  transition:background .15s ease,border-color .15s ease;
}
button.primary:hover{background:var(--afenet-blue-mid);border-color:var(--afenet-blue-mid)}
button.primary:active{background:var(--afenet-blue-dark);border-color:var(--afenet-blue-dark)}
button.primary:disabled{background:#aab4d0;border-color:#aab4d0;cursor:not-allowed}
button.secondary{
  background:var(--card);color:var(--text-2);border:1px solid var(--border);
  padding:8px 14px;border-radius:var(--radius-sm);cursor:pointer;font-size:13px;font-weight:600;
  transition:.15s;
}
button.secondary:hover{background:var(--afenet-blue-light);border-color:var(--afenet-blue-100);color:var(--afenet-blue)}
button.danger{
  background:var(--afenet-red-50);color:#b8302a;border:1px solid #f6c6c2;
  padding:7px 12px;border-radius:var(--radius-sm);cursor:pointer;font-size:12.5px;font-weight:600;
  transition:.15s;
}
button.danger:hover{background:#fbdad7}
button.small{padding:6px 11px;font-size:12px}
.row-actions{display:flex;gap:6px;flex-wrap:wrap}

/* Anchors that behave as buttons. Keyboard-reachable via role="button" +
   tabindex in the markup; focus ring matches form controls. */
.link-btn{
  cursor:pointer;color:var(--afenet-blue);font-size:12px;font-weight:600;
  text-decoration:none;border-radius:4px;
}
.link-btn:hover{text-decoration:underline}
a.secondary.small.link-download{
  display:inline-flex;align-items:center;text-decoration:none;
  padding:6px 11px;border-radius:var(--radius-sm);
}
a:focus-visible,button:focus-visible,[role="button"]:focus-visible{
  outline:2px solid var(--afenet-blue);outline-offset:2px;
}

.grid2{display:grid;grid-template-columns:1fr 1fr;gap:16px;align-items:start}

/* ==========================================================================
   MESSAGES
   ========================================================================== */
.msg{
  padding:11px 14px;border-radius:var(--radius-sm);margin-bottom:14px;font-size:13px;
  font-weight:500;border-left:4px solid transparent;
}
.msg.ok{background:var(--afenet-green-50);color:#166b2f;border-left-color:var(--afenet-green)}
.msg.err{background:var(--afenet-red-50);color:#b8302a;border-left-color:var(--afenet-red)}
.msg.info{background:var(--afenet-teal-50);color:#136374;border-left-color:var(--afenet-teal)}
.msg.warn{background:var(--afenet-gold-50);color:#7a5e00;border-left-color:var(--afenet-gold)}

/* ---------- confirmations ----------
   The flash banner used to sit in the document flow at the top of <main>. On a
   long screen - Projects & % LOE runs to one card per person - pressing Save
   1,500px down the page put the confirmation somewhere the user could not see,
   and a save that worked perfectly read as a no-op. It now floats over the
   top-right corner wherever the page is scrolled to, and occupies nothing when
   there is nothing to say. */
#flash-slot{
  position:fixed;top:calc(var(--topbar-h) + 12px);right:22px;z-index:70;
  width:min(420px,calc(100vw - 44px));pointer-events:none;
}
#flash-slot .msg{
  margin:0 0 10px;box-shadow:var(--shadow-lg);border:1px solid var(--border);
  border-left-width:4px;pointer-events:auto;animation:flash-in .18s ease-out;
}
@keyframes flash-in{from{opacity:0;transform:translateY(-6px)}to{opacity:1;transform:none}}
@media (max-width:720px){#flash-slot{left:12px;right:12px;width:auto}}

/* ---------- collapsible person rows ----------
   Projects & LOE, the %LOE grid and the accounts list are all one row per
   person. AFENET runs 700-1,500 people, so a card per person that is always
   open is metres of scrolling to reach one name — and a page that takes
   seconds to paint. Each row is a single line until it is opened. */
.acc-list{border:1px solid var(--border);border-radius:var(--radius-sm);overflow:hidden}
.acc-row + .acc-row{border-top:1px solid var(--border)}
.acc-head{
  display:flex;align-items:center;gap:10px;padding:9px 12px;cursor:pointer;
  background:var(--card);transition:background .12s;min-width:0;
}
.acc-head:hover,.acc-head[aria-expanded="true"]{background:var(--afenet-blue-light)}
.acc-caret{
  color:var(--muted);font-size:10px;width:11px;flex:none;display:inline-block;
  transition:transform .12s;
}
.acc-head[aria-expanded="true"] .acc-caret,
tr.acc-summary[aria-expanded="true"] .acc-caret{transform:rotate(90deg)}
.acc-name{font-weight:600;font-size:13.5px;color:var(--text);flex:none}
.acc-meta{
  font-size:12px;color:var(--muted);min-width:0;overflow:hidden;
  text-overflow:ellipsis;white-space:nowrap;
}
.acc-spacer{flex:1;min-width:8px}
.acc-body{padding:2px 12px 12px;background:var(--canvas)}
.acc-body table{margin-top:8px}
.acc-empty{padding:13px 12px;color:var(--muted);font-size:13px}
.acc-label{
  font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;
  letter-spacing:.05em;margin-bottom:5px;
}
tr.acc-summary{cursor:pointer}
tr.acc-summary:hover>td{background:var(--afenet-blue-light)}
tr.acc-detail>td{background:var(--canvas)}
@media (max-width:720px){
  .acc-meta{display:none}
}

/* A confirmation that stays beside the control that was just used, so the
   answer is where the eye already is. */
.save-note{font-size:12px;font-weight:600;margin-left:8px;white-space:nowrap}
.save-note.ok{color:#166b2f}
.save-note.err{color:#b8302a}
.save-note.warn{color:#7a5e00}
.save-note.info{color:var(--muted)}

/* ==========================================================================
   BIO / KEY-VALUE
   ========================================================================== */
.bio-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(230px,1fr));gap:12px 16px}
.bio-field{display:flex;flex-direction:column;gap:3px;margin:0}
.bio-label{font-size:11px;font-weight:600;color:var(--muted);text-transform:uppercase;letter-spacing:.05em}
.bio-val{font-size:13.5px;color:var(--text);min-height:18px;word-break:break-word}
.bio-field input,.bio-field select{width:100%}
.kv{display:grid;grid-template-columns:180px 1fr;gap:7px 12px;font-size:13px}
.kv div:nth-child(odd){color:var(--muted);font-weight:600}
.stack{display:flex;flex-direction:column;gap:10px}
.small-note{font-size:12px;color:var(--muted);line-height:1.5}
.muted{color:var(--muted)}
.sub{font-size:12px;color:var(--muted)}
code{background:#eef1f8;padding:1px 5px;border-radius:4px;font-size:12px;font-family:ui-monospace,Menlo,monospace}
.demo-banner{background:var(--afenet-gold-50);color:#7a5e00;padding:8px 20px;font-size:12px;border-bottom:1px solid #ecd89a}

/* ==========================================================================
   COUNTRY SELECTOR
   ========================================================================== */
.country-chip{
  display:inline-flex;align-items:center;gap:6px;background:var(--card);
  border:1px solid var(--border);color:var(--text-2);padding:6px 12px;
  border-radius:var(--radius-pill);font-weight:500;font-size:12.5px;cursor:pointer;transition:.15s;
}
.country-chip:hover{background:var(--afenet-blue-light);border-color:var(--afenet-blue-100);color:var(--afenet-blue)}
.country-screen{max-width:1000px;margin:0 auto;padding:32px 24px}
.country-head{display:flex;justify-content:space-between;align-items:center;gap:14px;margin-bottom:22px;flex-wrap:wrap}
.country-head .brand{display:flex;align-items:center;gap:12px}
.country-head .brand img{height:42px}
.country-head h1{margin:0;font-size:20px;font-weight:600;color:var(--afenet-blue)}
.country-intro{margin:8px 0 18px}
.country-intro h2{margin:0 0 6px;font-size:19px;font-weight:600}
.country-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:12px}
.country-tile{
  display:flex;flex-direction:column;gap:6px;align-items:flex-start;padding:16px;
  border-radius:var(--radius-md);border:1px solid var(--border);background:var(--card);
  text-align:left;cursor:pointer;transition:transform .12s ease,box-shadow .12s ease;
  box-shadow:var(--shadow-sm);font-family:inherit;
}
.country-tile.active{border-color:#b9e2c4}
.country-tile.active:hover{transform:translateY(-2px);box-shadow:var(--shadow-md)}
.country-tile.locked{opacity:.55;background:#f8fafd;cursor:not-allowed}
.country-tile .country-name{font-weight:600;font-size:14.5px;color:var(--text);font-family:var(--font-display)}
.country-tile .country-status{font-size:12px;color:var(--muted)}
.country-tile.active .country-status{color:#166b2f;font-weight:600}
.country-foot{margin-top:20px;color:var(--muted);font-size:12px;text-align:center}

/* ==========================================================================
   LOGIN
   ========================================================================== */
.login-wrap{
  display:flex;align-items:center;justify-content:center;min-height:100vh;
  background:radial-gradient(900px 500px at 15% 10%,#dbe3fb 0%,transparent 55%),var(--bg);
}
.login-card{width:380px;box-shadow:var(--shadow-lg)}
.login-card .logo-wrap{display:flex;justify-content:center;margin-bottom:10px}
.login-card .logo-wrap img{height:60px;width:auto}

.login-split{display:grid;grid-template-columns:1.15fr 1fr;min-height:100vh}
.login-brand{
  background:linear-gradient(150deg,#1b3a8f 0%,var(--afenet-blue) 45%,var(--afenet-blue-dark) 100%);
  color:#fff;position:relative;overflow:hidden;display:flex;align-items:center;
}
.login-brand::before{
  content:'';position:absolute;right:-140px;top:50%;transform:translateY(-50%);
  width:520px;height:520px;border-radius:50%;
  background:radial-gradient(circle,rgba(255,255,255,.10) 0%,rgba(255,255,255,.04) 45%,transparent 70%);
}
.login-brand::after{
  content:'';position:absolute;left:-90px;bottom:-90px;width:300px;height:300px;border-radius:50%;
  background:radial-gradient(circle,rgba(255,255,255,.07) 0%,transparent 70%);
}
.login-brand-inner{position:relative;z-index:1;padding:56px 8% 40px;width:100%;max-width:620px}
.login-logo{display:flex;align-items:center;gap:12px;margin-bottom:12px}
.login-logo img{height:40px;width:auto;background:#fff;border-radius:9px;padding:5px 7px}
.login-logo span{font-family:var(--font-display);font-size:20px;font-weight:600}
.login-pill{
  display:inline-block;background:rgba(255,255,255,.16);border:1px solid rgba(255,255,255,.28);
  border-radius:var(--radius-pill);padding:4px 13px;font-size:10.5px;font-weight:600;
  letter-spacing:.11em;margin-bottom:26px;
}
.login-brand h1{font-family:var(--font-display);font-size:38px;line-height:1.18;margin:0 0 18px;font-weight:600;letter-spacing:-.02em}
.login-brand p{font-size:14px;line-height:1.6;opacity:.9;margin:0 0 28px;max-width:430px}
.login-features{display:flex;flex-direction:column;gap:10px;max-width:430px}
.login-feature{
  display:flex;align-items:center;gap:11px;background:rgba(255,255,255,.11);
  border:1px solid rgba(255,255,255,.16);border-radius:var(--radius-md);padding:13px 16px;
  font-size:13.5px;font-weight:500;transition:background .18s ease,transform .18s ease;
}
.login-feature:hover{background:rgba(255,255,255,.19);transform:translateX(3px)}
.login-feature svg{flex-shrink:0;opacity:.9}
.login-foot{margin-top:34px;font-size:11.5px;opacity:.7}
.login-form-side{background:var(--card);display:flex;align-items:center;justify-content:center;padding:40px 6%}
.login-form-inner{width:100%;max-width:360px}
.login-form-inner h2{font-size:24px;margin:0 0 6px;font-weight:600}
.login-form-inner input{padding:10px 12px;font-size:13.5px}
.login-form-foot{margin-top:28px;font-size:11px;color:var(--muted);text-align:center}

/* ==========================================================================
   MODAL
   ========================================================================== */
.modal-backdrop{
  position:fixed;inset:0;background:rgba(17,26,46,.48);z-index:80;
  display:flex;align-items:flex-start;justify-content:center;padding:28px 16px;overflow-y:auto;
}
.modal{
  background:var(--card);border-radius:var(--radius-lg);box-shadow:var(--shadow-lg);
  width:100%;max-width:940px;margin:auto;overflow:hidden;
}
.modal-head{
  background:linear-gradient(115deg,var(--afenet-blue),var(--afenet-blue-mid));color:#fff;
  padding:16px 22px;display:flex;justify-content:space-between;align-items:center;gap:12px;
}
.modal-head h2{margin:0;font-size:17px;font-weight:600;color:#fff}
.modal-head .sub{font-size:12px;opacity:.9;margin-top:2px;color:#fff}
.modal-close{
  background:rgba(255,255,255,.16);border:1px solid rgba(255,255,255,.5);color:#fff;
  width:30px;height:30px;border-radius:50%;cursor:pointer;font-size:16px;line-height:1;flex-shrink:0;
}
.modal-close:hover{background:rgba(255,255,255,.3)}
.modal-body{padding:20px 22px;max-height:70vh;overflow-y:auto}
.modal-foot{
  padding:14px 22px;border-top:1px solid var(--border);background:#f9fbff;
  display:flex;justify-content:space-between;align-items:center;gap:12px;flex-wrap:wrap;
}
.modal-section{margin-bottom:20px}
.modal-section > h3{
  font-size:11px;text-transform:uppercase;letter-spacing:.09em;color:var(--afenet-blue);
  font-weight:600;margin:0 0 10px;padding-bottom:5px;border-bottom:2px solid var(--afenet-blue-light);
}
.field-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:12px}
.calc-preview{background:var(--afenet-blue-light);border-radius:var(--radius-sm);padding:14px 16px}
.calc-preview .calc-row{display:flex;justify-content:space-between;gap:12px;font-size:13px;padding:3px 0}
.calc-preview .calc-row.total{border-top:1px solid var(--afenet-blue-100);margin-top:6px;padding-top:7px;font-weight:700}
.calc-preview .calc-row.muted{color:var(--muted);font-size:12px}
.ded-row{display:flex;gap:8px;align-items:center;margin-bottom:6px}
.ded-row select{flex:1;min-width:0}
.ded-row input{width:110px}

/* ==========================================================================
   MODULE MAP
   ========================================================================== */
.module-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:10px}
.module-card{
  display:flex;align-items:center;gap:11px;padding:12px 14px;border:1px solid var(--border);
  border-radius:var(--radius-sm);background:var(--card);position:relative;
}
.module-card.planned{opacity:.62;background:#fafbfe}
.module-icon{
  width:32px;height:32px;border-radius:8px;display:flex;align-items:center;justify-content:center;
  background:var(--afenet-blue-light);color:var(--afenet-blue);flex-shrink:0;
}
.module-card.planned .module-icon{background:#eef1f8;color:var(--muted)}
.module-name{font-size:13px;font-weight:600}
.module-note{font-size:11.5px;color:var(--muted);margin-top:1px}
.module-status{
  margin-left:auto;font-size:9.5px;font-weight:700;text-transform:uppercase;letter-spacing:.05em;
  padding:3px 8px;border-radius:var(--radius-pill);flex-shrink:0;
}
.module-status.built{background:var(--afenet-green-50);color:#166b2f}
.module-status.partial{background:var(--afenet-gold-50);color:#8a6a00}
.module-status.planned{background:#eef1f8;color:#6b7590}

/* ==========================================================================
   EMPTY STATES
   ========================================================================== */
.empty-state{
  display:flex;flex-direction:column;align-items:center;gap:8px;padding:34px 12px;
  color:var(--muted);text-align:center;
}
.empty-state svg{opacity:.35}
.empty-state span{font-size:13px}

/* ==========================================================================
   RESPONSIVE
   Below 980px the sidebar collapses to a bottom tab bar. Country office staff
   file timesheets and supervisors approve from phones, so this is a designed
   layout rather than a squeezed desktop.
   ========================================================================== */
@media (max-width:980px){
  .side{
    position:fixed;left:0;top:0;bottom:0;height:100vh;
    transform:translateX(-100%);transition:transform .22s ease;box-shadow:var(--shadow-lg);
  }
  body.nav-open .side{transform:translateX(0)}
  body.nav-open::after{
    content:'';position:fixed;inset:0;background:rgba(17,26,46,.45);z-index:39;
  }
  .sidebar-toggle{
    display:flex;align-items:center;justify-content:center;
    width:34px;height:34px;padding:0;flex-shrink:0;
  }
  main{padding:16px 14px 88px}
  header{padding:0 14px;gap:9px}
  header .who{display:none}
  .grid2{grid-template-columns:1fr}
  .kv{grid-template-columns:130px 1fr}
  .notif-panel{width:min(340px,calc(100vw - 28px));right:-56px}

  .mobile-tabs{
    display:flex;position:fixed;left:0;right:0;bottom:0;z-index:45;
    background:var(--card);border-top:1px solid var(--border);
    box-shadow:0 -2px 12px rgba(17,26,46,.07);
    padding-bottom:env(safe-area-inset-bottom,0);
  }
  .mobile-tabs button{
    flex:1;min-width:0;background:none;border:none;cursor:pointer;
    display:flex;flex-direction:column;align-items:center;gap:3px;
    padding:9px 2px 10px;font-size:9.5px;font-weight:500;color:var(--muted);
  }
  .mobile-tabs button span{
    max-width:100%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;
  }
  .mobile-tabs button.active{color:var(--afenet-blue);font-weight:700}
  .mobile-tabs button .nav-count{
    position:absolute;transform:translate(14px,-4px);margin:0;
    min-width:16px;height:16px;font-size:9.5px;border:2px solid var(--card);
  }
}

@media (max-width:640px){
  /* Reclaim topbar width: the page title alone is enough context on a phone,
     and Sign out reads fine as an icon with an aria-label. */
  header .crumb span{display:none}
  header .crumb b{font-size:15px}
  .signout-btn span{display:none}
  .signout-btn{padding:7px 9px}
  .country-chip{padding:6px 10px;max-width:110px}
  .country-chip span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}

  .country-screen{padding:20px 14px}
  .login-brand{display:none}
  .login-split{grid-template-columns:1fr}
  .login-form-side{min-height:100vh}
  .modal-backdrop{padding:0}
  .modal{border-radius:0;min-height:100vh}
  .stat-grid{grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px}
  form.inline{gap:10px}
  form.inline label,.field-grid label{width:100%}
  form.inline input,form.inline select,form.inline textarea{width:100%}
  textarea{min-width:0}
  table{font-size:12.5px}
  thead th,td{padding:9px 10px}
}

@media (max-width:900px){
  .login-split{grid-template-columns:1fr}
  .login-brand{display:none}
  .login-form-side{min-height:100vh}
}

/* Horizontal scroll containment. Applied only on narrow screens: an
   overflow container on desktop would clip the sticky table headers. */
@media (max-width:980px){
  .card{overflow-x:auto}
  .modal-body{overflow-x:auto}
}

/* ==========================================================================
   WIDE TABLES
   Reported 2026-09-21: the staff directory was spilling past the right edge of
   its card - the View buttons were sitting on the page background.

   WHY IT ONLY SHOWED ON DESKTOP. The rule above contains any card on a screen
   of 980px or less. The directory is ten columns wide and needs roughly 1,100px,
   so between 981px and about 1,200px it was too wide to fit and too wide a
   screen to be contained: a dead zone that covers most laptops.

   A table that opts in here scrolls sideways inside its card instead. The cost
   is the sticky header: a horizontal scroll container is a scroll container on
   BOTH axes, so `position:sticky; top:0` inside it pins to the top of the table
   rather than the viewport. That is a real loss on a long directory, and it is
   why the rule above was written for narrow screens only. It is the lesser of
   the two - a header that stops following you is a nuisance, a table lying
   outside its own card is a bug everybody can see.

   Only put this on tables that genuinely need it. Most do not.
   ========================================================================== */
.table-wrap{
  overflow-x:auto;
  /* Room for the scrollbar so it does not sit on the last row's border. */
  padding-bottom:2px;
  -webkit-overflow-scrolling:touch;
}
.table-wrap thead th{
  /* Sticky cannot work in here (see above). Saying so explicitly beats a header
     that half-sticks and looks broken. */
  position:static;
}

/* ==========================================================================
   PERSONNEL FILE NUMBER
   Issued by the server, permanent, never editable — so it reads as an identity
   badge rather than a form field.
   ========================================================================== */
.pf-chip{
  display:inline-flex;align-items:center;gap:9px;
  background:var(--afenet-blue-light);border:1px solid var(--afenet-blue-100);
  border-radius:var(--radius-pill);padding:6px 15px;flex-shrink:0;
}
.pf-chip span{
  font-size:10px;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  color:var(--afenet-blue);opacity:.8;
}
.pf-chip b{
  font-family:var(--font-display);font-size:17px;font-weight:600;
  color:var(--afenet-blue);letter-spacing:.04em;font-variant-numeric:tabular-nums;
}
.pf-chip-pending{background:var(--afenet-gold-50);border-color:#ecd89a}
.pf-chip-pending span,.pf-chip-pending b{color:#8a6a00}
.pf-chip-pending b{font-size:13px;letter-spacing:0}

/* Internal staff record id — shown so it can be quoted when repairing a
   mislinked account, but visually quiet since it is not what people read by. */
.staff-id{color:var(--muted);font-variant-numeric:tabular-nums;font-size:12px}

/* Password box with a Show/Hide control. People paste passwords in here from
   an email or a manager, and without being able to read the field back a
   double-filled box looks identical to a correct one. */
.pw-wrap{position:relative;display:block}
.pw-wrap input{width:100%;padding-right:64px}
.pw-toggle{position:absolute;right:6px;top:50%;transform:translateY(-50%);
  background:none;border:0;padding:4px 8px;cursor:pointer;
  font:600 12px/1 var(--font-body,inherit);color:var(--afenet-blue);border-radius:4px}
.pw-toggle:hover{background:var(--afenet-blue-50,#eef1f8)}
.pw-toggle:focus-visible{outline:2px solid var(--afenet-blue);outline-offset:1px}
/* Two buttons in the box: Clear sits inside Show. */
.pw-wrap-2 input{padding-right:112px}
.pw-clear{right:56px;color:var(--muted,#6b7590)}
.pw-clear:hover{background:#f1f3f8;color:var(--text-2,#38415a)}
/* A password far longer than any we issue is a mistake, not a strong password. */
.pw-warn{color:#b3261e;font-weight:600;display:block;margin-top:4px;line-height:1.35}

/* ---------- Approvals sub-tabs ----------
   One tab, four queues (leave, timesheet, overtime, deliverables). The strip
   is horizontally scrollable rather than wrapping: on a laptop it never needs
   to, and on a phone a wrapped strip pushes the first queue off the fold. */
.sub-tabs{
  display:flex;gap:6px;margin-top:12px;overflow-x:auto;padding-bottom:2px;
  -webkit-overflow-scrolling:touch;scrollbar-width:none;
}
.sub-tabs::-webkit-scrollbar{display:none}
.sub-tab{
  display:inline-flex;align-items:center;gap:7px;white-space:nowrap;flex-shrink:0;
  background:var(--afenet-blue-light);color:var(--afenet-blue);
  border:1px solid transparent;border-radius:var(--radius-pill,999px);
  padding:7px 14px;font:inherit;font-size:13.5px;font-weight:600;cursor:pointer;
  transition:background .12s ease,color .12s ease,border-color .12s ease;
}
.sub-tab:hover{background:var(--afenet-blue-100);border-color:var(--afenet-blue-100)}
.sub-tab.active{background:var(--afenet-blue);color:#fff;border-color:var(--afenet-blue)}
.sub-tab svg{flex-shrink:0}
.sub-tab-count{
  background:var(--afenet-red);color:#fff;font-size:10.5px;font-weight:700;
  min-width:18px;height:18px;border-radius:var(--radius-pill,999px);
  display:inline-flex;align-items:center;justify-content:center;padding:0 5px;
}
.sub-tab.active .sub-tab-count{background:#fff;color:var(--afenet-blue)}

/* ---------- Reporting line: two supervisor slots side by side ----------
   `.form-row` carries no CSS of its own, so a second labelled control wrapped
   between its own label and its own select — the backup's caption ended up
   sitting beside the FIRST select, reading as if it belonged to it. Caught on a
   screenshot; it is invisible to any assertion about the DOM. */
.sup-row{display:flex;flex-wrap:wrap;gap:14px;align-items:flex-end}
.sup-row label{display:flex;flex-direction:column;gap:5px;font-size:12.5px;font-weight:600;color:var(--muted,#6b7590)}
.sup-row label>span{white-space:nowrap}
.sup-hint{font-style:normal;font-weight:500;opacity:.75}
.sup-row select{min-width:280px}
.sup-row button{flex-shrink:0}
@media (max-width:720px){
  .sup-row label,.sup-row select,.sup-row button{width:100%}
}

/* ---------------------------------------------------------------------------
   A CARD THAT FOLDS.  Batch 48.

   Peter: "can we allow the draft list and approved list to be collapsable - it
   makes the page look neat. We can always open it to view the list?"

   Built on <details>/<summary> rather than a click handler and a state flag,
   because the browser already does this: keyboard, screen readers, Find-in-page
   (Chrome opens a closed <details> to show a match) and the disclosure triangle
   all come free and all work the same way as the smaller <details> blocks
   already on this screen.

   The heading stays a real <h3> so the page outline is unchanged; the summary
   is what carries the click. `display:flex` replaces the default list-item
   marker - which is why the caret is drawn by hand, matching .acc-caret.
--------------------------------------------------------------------------- */
.fold{margin:0}
.fold>summary{
  display:flex;align-items:center;gap:8px;cursor:pointer;list-style:none;
  margin:-4px -6px;padding:4px 6px;border-radius:var(--radius-sm,6px);
  transition:background .12s;
}
.fold>summary::-webkit-details-marker{display:none}
.fold>summary:hover{background:var(--afenet-blue-light)}
.fold>summary:focus-visible{outline:2px solid var(--afenet-blue);outline-offset:2px}
.fold>summary h3{margin:0}
.fold-caret{
  color:var(--muted);font-size:10px;width:11px;flex:none;display:inline-block;
  transition:transform .12s;
}
.fold[open]>summary .fold-caret{transform:rotate(90deg)}
.fold-count{
  font-size:12px;color:var(--muted);margin-left:auto;flex:none;white-space:nowrap;
}
.fold-body{margin-top:12px}

/* ==========================================================================
   PAYROLL LINES TABLE — the screen a preparer spends the month in
   --------------------------------------------------------------------------
   THE BUG THIS BLOCK FIXES. `td{overflow-wrap:anywhere}` was added so an email
   address could not force a ten-column directory wider than its card. On the
   payroll run that rule does real damage: it breaks INSIDE a figure, so a net
   pay of 6,338,950 prints as "6,338,9 / 50" and a button reads "Ded ucti ons".
   A preparer reconciling a control total cannot afford to read a number twice.

   So money, codes and button labels opt OUT of that rule here, and the table
   is allowed to scroll sideways inside its card instead of being crushed into
   it. Text columns (Name, position) keep the wrapping — they are prose.

   THE COLOUR HAS A JOB. Three bands, in brand colours, answer the only three
   questions asked of this table: what was earned (blue), what was taken
   (amber/red), what leaves the bank (green). Tints only — the numerals stay
   near-black, so nothing here depends on colour to be read.
   ========================================================================== */
/*
 * WHY THIS ONE SCROLLS BOTH WAYS, when .table-wrap deliberately does not.
 *
 * A horizontal scroll container is a scroll container on BOTH axes, so a
 * sticky header inside it pins to the container rather than the viewport —
 * which is why the directory's .table-wrap gives sticky up as a loss. On a
 * 56-line payroll that loss is not affordable: the reader is matching a figure
 * to a column heading and a control total, and both must stay on screen.
 *
 * So this wrapper takes the other side of the trade. Capping the height makes
 * it a real scrollport, which makes BOTH the header and the totals row stick
 * where they are useful. A short run never reaches the cap and looks unchanged.
 */
.payroll-table-wrap{
  overflow:auto;max-height:72vh;
  border:1px solid var(--border);border-radius:var(--radius-md);
  background:var(--card);
  -webkit-overflow-scrolling:touch;
}
.payroll-table{width:100%;border-collapse:separate;border-spacing:0;font-size:13px}

/* ---- header: two-tone, so the bands are announced before the first row ---- */
.payroll-table thead th{
  position:sticky;top:0;z-index:3;
  background:#f2f5fd;color:var(--text-2);
  padding:11px 12px;font-size:10.5px;font-weight:700;
  text-transform:uppercase;letter-spacing:.07em;white-space:nowrap;
  border-bottom:2px solid var(--afenet-blue-100);
}
.payroll-table thead th.col-gross{background:var(--afenet-blue-light);color:var(--afenet-blue)}
.payroll-table thead th.col-ded{background:var(--afenet-gold-50);color:#7a5e00}
.payroll-table thead th.col-net{background:var(--afenet-green-50);color:#166b2f}
.payroll-table thead th.col-actions{background:#f2f5fd;text-align:right}

/* ---- cells ---- */
.payroll-table td{
  padding:10px 12px;border-bottom:1px solid var(--border-soft);
  vertical-align:middle;overflow-wrap:normal;
}
/* Every numeric cell: same digit width, never broken across lines. */
.payroll-table td.col-num,
.payroll-table td.col-gross,
.payroll-table td.col-ded,
.payroll-table td.col-net,
.payroll-table td.col-code{
  text-align:right;white-space:nowrap;
  font-variant-numeric:tabular-nums;font-feature-settings:'tnum' 1;
}
.payroll-table td.col-code{text-align:left;font-weight:600;color:var(--text-2);letter-spacing:.02em}
.payroll-table td.col-gross{background:rgba(33,64,154,.035)}
.payroll-table td.col-ded{background:rgba(245,197,24,.07)}
.payroll-table td.col-net{background:rgba(31,138,61,.055);font-weight:600}
.payroll-table td.col-net strong{color:#14682e;font-size:13.5px}
.payroll-table td.col-name{min-width:210px;line-height:1.35}
.payroll-table td.col-name .nm{font-weight:600;color:var(--text)}

/* ---- rows: a readable stripe, and a hover that follows the eye across ---- */
.payroll-table tbody tr:nth-child(even) td{background-image:linear-gradient(rgba(17,26,46,.018),rgba(17,26,46,.018))}
.payroll-table tbody tr:hover td{background-image:linear-gradient(rgba(33,64,154,.055),rgba(33,64,154,.055))}
.payroll-table tbody tr.row-blocked td:first-child{box-shadow:inset 3px 0 0 var(--afenet-red)}
.payroll-table tbody tr.row-ready td:first-child{box-shadow:inset 3px 0 0 var(--afenet-green)}
.payroll-table tbody tr.row-notmine{opacity:.6}

/* ---- totals: the control total, pinned to the bottom of the scroller ---- */
.payroll-table tr.row-total td{
  position:sticky;bottom:0;z-index:2;
  background:#eef2fd;border-top:2px solid var(--afenet-blue-100);border-bottom:none;
  font-weight:700;color:var(--afenet-blue);
  font-variant-numeric:tabular-nums;white-space:nowrap;
}
.payroll-table tr.row-total td.col-net{background:#e2f2e6;color:#14682e}
/* The control total is not a row you act on, so it does not light up under the
   pointer the way a person's line does. */
.payroll-table tbody tr.row-total:hover td{background-image:none}

/* ==========================================================================
   ROW ACTIONS — icon + label, colour coded, never wrapping
   Three verbs with three different consequences should not look identical.
   ========================================================================== */
.payroll-table td.col-actions{text-align:right;white-space:nowrap;padding:8px 12px}
.row-actions{display:flex;gap:6px;flex-wrap:wrap;justify-content:flex-end}
.btn-act{
  display:inline-flex;align-items:center;gap:6px;
  padding:6px 11px;border-radius:var(--radius-pill);
  font-family:inherit;font-size:12px;font-weight:600;line-height:1.35;
  white-space:nowrap;               /* the fix for "Ded ucti ons" */
  cursor:pointer;border:1px solid transparent;
  transition:background .14s ease,border-color .14s ease,box-shadow .14s ease,transform .1s ease;
}
.btn-act svg{width:14px;height:14px;flex:none}
.btn-act:hover{transform:translateY(-1px)}
.btn-act:active{transform:none}
.btn-act:focus-visible{outline:2px solid var(--afenet-blue);outline-offset:2px}
.btn-act.act-edit{background:var(--afenet-blue-light);color:var(--afenet-blue);border-color:var(--afenet-blue-100)}
.btn-act.act-edit:hover{background:var(--afenet-blue-100)}
.btn-act.act-warn{background:var(--afenet-gold-50);color:#7a5e00;border-color:#ecd89a}
.btn-act.act-warn:hover{background:#fbeec2}
.btn-act.act-undo{background:var(--afenet-teal-50);color:#136374;border-color:#bfe0e7}
.btn-act.act-undo:hover{background:#d3eaef}
.btn-act.act-danger{background:var(--afenet-red-50);color:#b8302a;border-color:#f6c6c2}
.btn-act.act-danger:hover{background:#fbdad7}

/* ==========================================================================
   RUN PROGRESS STRIP — what is left to do, said kindly
   --------------------------------------------------------------------------
   Payroll is counted work under a deadline, and the run card previously opened
   with whatever was WRONG. The same facts read differently as a count of what
   is done: "48 of 56 ready" is the same information as eight red banners, and
   it tells the preparer they are nearly there. The warnings below it are
   unchanged — this strip does not replace a single one of them.
   ========================================================================== */
.run-progress{
  display:flex;align-items:center;gap:16px;flex-wrap:wrap;
  background:linear-gradient(135deg,#f4f7ff 0%,#eef2fd 100%);
  border:1px solid var(--afenet-blue-100);border-radius:var(--radius-md);
  padding:13px 16px;margin:12px 0 14px;
}
.run-progress.is-done{background:linear-gradient(135deg,#f0faf2 0%,#e4f5e8 100%);border-color:#b9e2c4}
.run-progress .rp-main{flex:1 1 260px;min-width:220px}
.run-progress .rp-title{
  font-family:var(--font-display);font-size:13.5px;font-weight:600;color:var(--text);
  display:flex;align-items:center;gap:7px;margin-bottom:7px;
}
.run-progress.is-done .rp-title{color:#14682e}
.run-progress .rp-bar{height:8px;border-radius:var(--radius-pill);background:#dbe3fb;overflow:hidden}
.run-progress .rp-fill{
  height:100%;border-radius:var(--radius-pill);
  background:linear-gradient(90deg,var(--afenet-blue) 0%,var(--afenet-blue-mid) 100%);
  transition:width .4s ease;
}
.run-progress.is-done .rp-bar{background:#c8e8d2}
.run-progress.is-done .rp-fill{background:linear-gradient(90deg,var(--afenet-green) 0%,#2aa84f 100%)}
.run-progress .rp-note{font-size:12px;color:var(--text-2);margin-top:7px}
.rp-stats{display:flex;gap:10px;flex-wrap:wrap}
.rp-stat{
  background:var(--card);border:1px solid var(--border);border-radius:var(--radius-sm);
  padding:7px 13px;text-align:center;min-width:74px;
}
.rp-stat b{
  display:block;font-family:var(--font-display);font-size:19px;font-weight:600;
  line-height:1.15;font-variant-numeric:tabular-nums;color:var(--text);
}
.rp-stat span{font-size:10.5px;color:var(--muted);font-weight:600;letter-spacing:.04em;text-transform:uppercase}
.rp-stat.is-ready b{color:#14682e}
.rp-stat.is-blocked b{color:#b8302a}
.rp-stat.is-money b{color:var(--afenet-blue);font-size:15px;letter-spacing:-.01em}

@media (max-width:760px){
  .payroll-table td.col-name{min-width:170px}
  .run-progress{gap:12px;padding:12px}
  .rp-stat{min-width:64px;padding:6px 10px}
}
@media (prefers-reduced-motion:reduce){
  .btn-act:hover{transform:none}
  .run-progress .rp-fill{transition:none}
}

/* ==========================================================================
   STAFF DIRECTORY — the register, made legible and worth opening
   --------------------------------------------------------------------------
   Peter, 2026-09-22, after the payroll table: "can we improve the employee
   interphase and make it colourful. We can make the staff list hideable, and
   the staff codes have different colours."

   THE SAME BUG AS THE PAYROLL RUN, in the place it was born. `td{overflow-wrap:
   anywhere}` exists because "akiggundu@afenet.net" is one unbreakable token and
   a ten-column table cannot be narrower than it. That rule is right for the
   e-mail column and wrong for every other: it printed a mobile number as
   "07830156 / 26" and the View button as "Vi / e / w". So here the e-mail column
   KEEPS the rule — it is the only reason the rule exists — and the phone, the
   code, the badges and the buttons opt out of it.

   WHAT THE COLOUR IS FOR. Not decoration: a staff code is the identifier HR
   types, says on the phone and matches against a payslip, and it is four
   characters of grey text among nine other columns. Hashing the code to a fixed
   hue gives every person a constant colour — AL04 is always the same green, on
   this screen and on every screen that reuses .code-chip — so the eye finds a
   row before it has finished reading it. The hue carries NO meaning of its own:
   it is derived from the string, not from grade, department or status, and
   every chip states its code in text, so nothing is lost to a colour-blind
   reader or a black-and-white print.
   ========================================================================== */

/* ---- a chip palette: 10 tints, each with its own AA-legible ink ---- */
.code-chip,.avatar,.dept-pill{
  --chip-bg:#eef1f8;--chip-ink:#5b6377;--chip-line:#dfe3ee;
}
.cc-0{--chip-bg:#e8edfd;--chip-ink:#21409a;--chip-line:#c9d5f8}  /* brand blue   */
.cc-1{--chip-bg:#e2f2f5;--chip-ink:#136374;--chip-line:#bfe0e7}  /* brand teal   */
.cc-2{--chip-bg:#e4f5e8;--chip-ink:#166b2f;--chip-line:#bfe3c9}  /* brand green  */
.cc-3{--chip-bg:#fdf0d6;--chip-ink:#7a5e00;--chip-line:#eeda9f}  /* brand gold   */
.cc-4{--chip-bg:#fdeae8;--chip-ink:#b8302a;--chip-line:#f6c6c2}  /* brand red    */
.cc-5{--chip-bg:#ede8fa;--chip-ink:#53359c;--chip-line:#d6cbf3}  /* violet       */
.cc-6{--chip-bg:#fce9f1;--chip-ink:#9c2a62;--chip-line:#f5c9dd}  /* magenta      */
.cc-7{--chip-bg:#fdeede;--chip-ink:#96500f;--chip-line:#f3d5b4}  /* amber-orange */
.cc-8{--chip-bg:#e0f4ee;--chip-ink:#0f6b53;--chip-line:#b7e4d6}  /* emerald      */
.cc-9{--chip-bg:#e6eef6;--chip-ink:#2d5473;--chip-line:#c4d7e6}  /* slate blue   */

/* ---- the staff code, as an identity chip ---- */
.code-chip{
  display:inline-block;padding:3px 9px;border-radius:var(--radius-sm);
  background:var(--chip-bg);color:var(--chip-ink);
  border:1px solid var(--chip-line);
  font-family:var(--font-display);font-size:11.5px;font-weight:600;
  letter-spacing:.06em;white-space:nowrap;      /* never "AL / 04" */
  font-variant-numeric:tabular-nums;
}

/* ---- initials, same hue as that person's code, so the row reads as one ---- */
.avatar{
  display:inline-flex;align-items:center;justify-content:center;flex:none;
  width:30px;height:30px;border-radius:50%;
  background:var(--chip-bg);color:var(--chip-ink);border:1px solid var(--chip-line);
  font-family:var(--font-display);font-size:11.5px;font-weight:600;
  letter-spacing:.02em;text-transform:uppercase;user-select:none;
}
.avatar-lg{width:38px;height:38px;font-size:14px}

/* ---- department, hued off the department name so a team clusters ---- */
.dept-pill{
  display:inline-block;padding:3px 10px;border-radius:var(--radius-pill);
  background:var(--chip-bg);color:var(--chip-ink);
  font-size:11.5px;font-weight:600;line-height:1.5;
}
.dept-pill.is-none{background:#f4f5f9;color:var(--muted);font-weight:500;font-style:italic}

/* ---- staff vs consultant: two contract shapes, told apart at a glance ---- */
.type-pill{
  display:inline-flex;align-items:center;gap:5px;
  padding:3px 9px;border-radius:var(--radius-pill);
  font-size:11px;font-weight:600;white-space:nowrap;
}
.type-pill::before{content:'';width:6px;height:6px;border-radius:2px;background:currentColor}
.type-pill.is-staff{background:var(--afenet-blue-light);color:var(--afenet-blue)}
.type-pill.is-consultant{background:var(--afenet-teal-50);color:#136374}

/* ==========================================================================
   THE DIRECTORY TABLE
   Same trade as the payroll run: cap the height so the wrapper is a real
   scrollport, which is what makes the header stick. .table-wrap gives sticky
   up because it scrolls sideways only; a 61-row register cannot afford that.
   ========================================================================== */
.staff-table-wrap{
  overflow:auto;max-height:70vh;
  border:1px solid var(--border);border-radius:var(--radius-md);
  background:var(--card);-webkit-overflow-scrolling:touch;
}
.staff-table{width:100%;border-collapse:separate;border-spacing:0;font-size:13px}
.staff-table thead th{
  position:sticky;top:0;z-index:3;
  background:linear-gradient(180deg,#f4f7ff 0%,#eef2fd 100%);
  color:var(--afenet-blue);text-align:left;
  padding:11px 12px;font-size:10.5px;font-weight:700;
  text-transform:uppercase;letter-spacing:.07em;white-space:nowrap;
  border-bottom:2px solid var(--afenet-blue-100);
}
.staff-table thead th.col-actions{text-align:right}
.staff-table td{
  padding:9px 12px;border-bottom:1px solid var(--border-soft);
  vertical-align:middle;overflow-wrap:normal;
}
.staff-table tbody tr{cursor:pointer;transition:background .12s ease}
.staff-table tbody tr:nth-child(even) td{background:rgba(17,26,46,.016)}
.staff-table tbody tr:hover td{background:rgba(33,64,154,.055)}
.staff-table tbody tr:last-child td{border-bottom:none}
/* A left edge in the person's own colour: the row and its chip agree. */
.staff-table tbody tr td.col-id{box-shadow:inset 3px 0 0 var(--row-hue,transparent)}

.staff-table td.col-id{
  color:var(--muted);font-size:11.5px;font-variant-numeric:tabular-nums;
  white-space:nowrap;width:1%;
}
.staff-table td.col-code{white-space:nowrap;width:1%}
.staff-table td.col-name{min-width:200px}
.staff-table td.col-name .nm-cell{display:flex;align-items:center;gap:9px}
.staff-table td.col-name .nm{font-weight:600;color:var(--text);line-height:1.3}
.staff-table td.col-type,
.staff-table td.col-status{white-space:nowrap;width:1%}
.staff-table td.col-dept{min-width:120px}
.staff-table td.col-sup{color:var(--text-2);min-width:130px}
/* Phone numbers are figures: "0783015626" must never break. */
.staff-table td.col-tel{
  white-space:nowrap;font-variant-numeric:tabular-nums;color:var(--text-2);
}
/* The one column that keeps the wrapping — it is why the rule exists. */
.staff-table td.col-email{overflow-wrap:anywhere;color:var(--text-2);min-width:150px}
.staff-table td.col-actions{text-align:right;white-space:nowrap;width:1%}
.staff-table .empty-row td{
  text-align:center;color:var(--muted);padding:26px 12px;font-style:italic;
}

/* ==========================================================================
   DIRECTORY HEADCOUNT STRIP — the register said as a count of people
   The screen used to open with a search box and a warning. The same facts read
   differently as "61 people, 8 departments": a register somebody maintains,
   rather than a list of things wrong with it. Every warning below is unchanged.
   ========================================================================== */
.dir-stats{
  display:flex;gap:10px;flex-wrap:wrap;margin:12px 0 2px;
}
.dir-stat{
  flex:1 1 118px;min-width:108px;
  border:1px solid var(--chip-line,var(--border));border-radius:var(--radius-md);
  background:var(--chip-bg,var(--card));
  padding:11px 14px;
}
.dir-stat b{
  display:block;font-family:var(--font-display);font-size:21px;font-weight:600;
  line-height:1.15;font-variant-numeric:tabular-nums;color:var(--chip-ink,var(--text));
}
.dir-stat span{
  display:block;font-size:10.5px;font-weight:600;letter-spacing:.05em;
  text-transform:uppercase;color:var(--chip-ink,var(--muted));opacity:.85;margin-top:2px;
}
/* An alert count is a button, because there is something to do about it. */
.dir-stat.is-action{cursor:pointer;transition:transform .12s ease,box-shadow .12s ease}
.dir-stat.is-action:hover{transform:translateY(-1px);box-shadow:var(--shadow-sm)}
.dir-stat.is-action:focus-visible{outline:2px solid var(--afenet-blue);outline-offset:2px}

/* ==========================================================================
   FOLDING THE DIRECTORY
   Built on the .fold <details> pattern from batch 48, with one addition: the
   directory summary carries a live count and a colour bar, so a folded card
   still answers "how many?" without being opened.
   ========================================================================== */
.fold-dir>summary{
  padding:2px 6px;margin:-2px -6px;
}
.fold-dir>summary h2{margin:0;display:flex;align-items:center;gap:8px}
.fold-hint{
  font-size:11px;font-weight:600;color:var(--afenet-blue);
  background:var(--afenet-blue-light);border-radius:var(--radius-pill);
  padding:2px 10px;margin-left:auto;flex:none;white-space:nowrap;
}
.fold-dir[open]>summary .fold-hint::after{content:'hide'}
.fold-dir:not([open])>summary .fold-hint::after{content:'show'}

@media (max-width:760px){
  .staff-table td.col-name{min-width:160px}
  .dir-stat{flex:1 1 96px;min-width:92px;padding:9px 11px}
  .dir-stat b{font-size:18px}
}
@media (prefers-reduced-motion:reduce){
  .dir-stat.is-action:hover{transform:none}
}

/* ===========================================================================
 * MANAGEMENT ANALYTICS (batch 54)
 *
 * No charting library. The app loads none and this screen does not justify
 * adding one: every chart here is a horizontal bar, and a horizontal bar is a
 * div with a width. That keeps the screen printable, theme-able and legible
 * without JavaScript running a layout pass.
 *
 * THE WITHHELD PILL IS THE POINT OF THE WHOLE STYLESHEET SECTION. A suppressed
 * figure must look DELIBERATE, not broken — a blank cell reads as a bug and
 * gets "fixed" by the next person. It says the word, and its bar is not drawn
 * at all, because a bar whose length encodes a withheld number hands it back.
 * ======================================================================== */

.an-note{background:var(--afenet-blue-light);border-color:var(--afenet-blue-100)}

.an-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(280px,1fr));gap:20px 28px;margin-bottom:6px}
.an-grid > div{min-width:0}

.an-bars{display:flex;flex-direction:column;gap:6px;margin-bottom:10px}
.an-bar-row{display:grid;grid-template-columns:minmax(90px,1.15fr) minmax(60px,2fr) minmax(64px,auto);
  align-items:center;gap:10px;font-size:13px}
.an-bar-label{color:var(--text-2);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.an-bar-track{background:var(--border-soft);border-radius:var(--radius-pill);height:10px;overflow:hidden}
.an-bar-fill{background:linear-gradient(90deg,var(--afenet-blue),var(--afenet-blue-mid));
  height:100%;border-radius:var(--radius-pill);min-width:2px}
.an-bar-value{text-align:right;font-variant-numeric:tabular-nums;font-weight:600;color:var(--text)}
.an-bar-row.an-urgent .an-bar-fill{background:linear-gradient(90deg,var(--afenet-red),#f2705f)}
.an-bar-row.an-urgent .an-bar-value{color:var(--danger)}

/* A withheld figure. Says so, in words, and never draws a bar. */
.an-withheld{display:inline-block;padding:1px 8px;border-radius:var(--radius-pill);
  background:var(--border-soft);color:var(--muted);font-size:11.5px;font-weight:600;
  letter-spacing:.02em;white-space:nowrap;cursor:help;border:1px dashed var(--border)}

.an-figure{font-family:var(--font-display);font-size:22px;font-weight:600;color:var(--text);
  line-height:1.25;margin-bottom:6px}
.an-figure span{display:block;font-family:var(--font-body);font-size:12px;font-weight:500;color:var(--muted)}

@media (max-width:700px){
  .an-bar-row{grid-template-columns:minmax(70px,1.2fr) minmax(40px,1.4fr) minmax(56px,auto);gap:8px;font-size:12.5px}
  .an-figure{font-size:19px}
}

/* ==========================================================================
   SYSTEM SETUP — eleven cards, folded, colour-coded and aligned
   --------------------------------------------------------------------------
   Peter, 2026-09-22, on the System Setup screen after the payroll run and the
   staff directory: "can we improve the interphase like we did for employee page
   and payroll page? Make it more colourful and let it align properly. Let us
   also allow the Account pane to fold, so it opens when we need it to open
   including the rest on that page too, so it looks neat on arrival."

   WHAT WAS WRONG. This is the longest screen in the app — eleven cards, one of
   them a table of every login AFENET has — and all eleven were drawn open, one
   under another, in a single scroll. An administrator who came to confirm one
   public holiday scrolled past the country matrix, the job table, the caseload
   assigner, the create-login form and the bulk-login picker to reach it. Being
   able to SEE everything is not the same as being able to FIND anything.

   THE FOLD RULE, and it is a rule rather than a per-card opinion: a card opens
   on arrival when something in it is waiting on you — a login with no employee
   record, a payroll with nobody to prepare it, a holiday date nobody has
   confirmed, a paying account left blank. A card that only records a settled
   state arrives shut. Every folded heading keeps its count, so folding can
   never hide the fact that there is something to do.

   WHY THE ACCOUNTS LIST ARRIVES SHUT. It is the one card Peter named, and the
   one with sixty-seven rows. Its heading carries the total and, in red, the
   number of accounts with a fault — so a shut card still answers the only two
   questions you would have opened it to ask.

   THE COLOUR IS THE SAME SYSTEM AS THE DIRECTORY, not a second one. The chip
   palette (.cc-0 … .cc-9) already defined above is reused unchanged: a person's
   hue comes from their staff code, so the same human being is the same colour
   on Employee Management and here. Roles are the one new colour axis, and they
   are grouped by what the role DOES — administer, prepare, review, approve —
   because "Accountant (Reviewer 1)" and "Head of Finance (Reviewer 4)" are the
   same kind of thing and used to look as different as any two strings.
   ========================================================================== */

/* ---- the card, with a bar in its own colour down the left edge ---- */
/* The bar is a ::before with its own left radius rather than the card being
   given overflow:hidden. overflow:hidden would clip the focus ring on the
   summary - and it makes the card a scroll container, which is a trap to leave
   out of a card that already holds a sticky-header scrollport. */
.setup-card{position:relative}
.setup-card::before{
  content:'';position:absolute;left:0;top:0;bottom:0;width:5px;
  border-radius:var(--radius-md) 0 0 var(--radius-md);
  background:var(--chip-ink,var(--border));
}
.fold-setup>summary{padding-left:4px}
.fold-setup>summary h2{
  margin:0;font-size:15.5px;display:flex;align-items:center;gap:8px;min-width:0;
}
.fold-setup>summary h2 svg{color:var(--chip-ink,var(--afenet-blue));flex:none}
/* The summary is a flex row: heading, then badges, then the show/hide pill
   pushed to the far edge. Without the min-width:0 above, a long heading
   refuses to shrink and pushes the pill off the card on a narrow window. */
.fold-setup>summary .sc-tail{
  margin-left:auto;display:flex;align-items:center;gap:8px;flex:none;
}
.sc-count{
  font-family:var(--font-display);font-size:11.5px;font-weight:600;
  padding:2px 10px;border-radius:var(--radius-pill);white-space:nowrap;
  background:var(--chip-bg,var(--afenet-blue-light));color:var(--chip-ink,var(--afenet-blue));
  font-variant-numeric:tabular-nums;
}
.sc-alert{
  font-size:11.5px;font-weight:600;padding:2px 10px;border-radius:var(--radius-pill);
  background:var(--afenet-red-50);color:#b8302a;white-space:nowrap;
  font-variant-numeric:tabular-nums;
}
.sc-ok{
  font-size:11.5px;font-weight:600;padding:2px 10px;border-radius:var(--radius-pill);
  background:var(--afenet-green-50);color:#166b2f;white-space:nowrap;
}
/* Reuses .fold-hint from the directory: reads "hide" open, "show" shut. */
.fold-setup>summary .fold-hint{margin-left:0}
/* A card that wants you arrives with a tinted summary, so the eye lands on it
   before it has read a word.

   THE TINT IS THE CARD'S OWN HUE, NOT RED. A first cut made every wanting card
   red, and on a normal Monday four of the eleven want you - which turns the
   screen into four alarms and teaches the reader to ignore all of them. Red is
   reserved for the .sc-alert pill, which names an actual fault and a count.
   The tint says "start here"; the pill says "this is wrong". */
.setup-card.is-wanted>.fold-setup>summary{background:var(--chip-bg,var(--afenet-blue-light))}
.setup-card.is-wanted{border-color:var(--chip-line,var(--border))}

/* ==========================================================================
   SETUP STATS STRIP — the login register said as a count of accounts
   Built on .dir-stats from the directory rather than beside it: one strip
   component, two screens. The two tiles that name a fault are buttons, because
   there is something to do about each of them.
   ========================================================================== */
.setup-stats{display:flex;gap:10px;flex-wrap:wrap;margin:0 0 14px}
.setup-stats .dir-stat{flex:1 1 118px;min-width:104px}
.dir-stat.is-on{box-shadow:inset 0 0 0 2px var(--chip-ink,var(--afenet-blue))}

/* ==========================================================================
   THE ACCOUNTS TABLE
   Same trade as the payroll run and the directory: cap the height so the
   wrapper is a real scrollport, which is what makes the header stick.

   AND THE SAME WRAPPING BUG, for the third and last time. `td{overflow-wrap:
   anywhere}` is why this screen printed "hrisadmin@afenet.n / et" and the open
   / close affordance as "clo / se". The username column KEEPS the rule — an
   e-mail address is one unbreakable token and is the only reason the rule was
   ever written — and every other column opts out of it.
   ========================================================================== */
.acc-table-wrap{
  overflow:auto;max-height:70vh;
  border:1px solid var(--border);border-radius:var(--radius-md);
  background:var(--card);-webkit-overflow-scrolling:touch;
}
.acc-table{width:100%;border-collapse:separate;border-spacing:0;font-size:13px}
.acc-table thead th{
  position:sticky;top:0;z-index:3;
  background:linear-gradient(180deg,#f4f7ff 0%,#eef2fd 100%);
  color:var(--afenet-blue);text-align:left;
  padding:11px 12px;font-size:10.5px;font-weight:700;
  text-transform:uppercase;letter-spacing:.07em;white-space:nowrap;
  border-bottom:2px solid var(--afenet-blue-100);
}
.acc-table thead th.col-open{text-align:right}
.acc-table td{
  padding:9px 12px;border-bottom:1px solid var(--border-soft);
  vertical-align:middle;overflow-wrap:normal;
}
.acc-table tbody tr.acc-summary{cursor:pointer;transition:background .12s ease}
/* The stripe is a class, not :nth-child — an opened account adds a second <tr>
   to the same tbody, and a counted stripe would flip every row below it. */
.acc-table tbody tr.acc-alt td{background:rgba(17,26,46,.016)}
.acc-table tbody tr.acc-summary:hover td{background:rgba(33,64,154,.055)}
.acc-table tbody tr.acc-summary[aria-expanded="true"] td{
  background:var(--afenet-blue-light);border-bottom-color:var(--afenet-blue-100);
}

/* The open/close affordance, now a real <button> so the keyboard can reach it.
   It was the bare word "open" in a cell, which printed as "clo / se" under
   overflow-wrap:anywhere and could not be tabbed to at all. */
.acc-toggle{
  font-family:inherit;font-size:10.5px;font-weight:700;letter-spacing:.06em;
  text-transform:uppercase;white-space:nowrap;
  padding:4px 10px;border-radius:var(--radius-pill);cursor:pointer;
  background:var(--afenet-blue-light);color:var(--afenet-blue);
  border:1px solid var(--afenet-blue-100);
  transition:background .12s ease;
}
.acc-toggle:hover{background:var(--afenet-blue-100)}
.acc-toggle:focus-visible{outline:2px solid var(--afenet-blue);outline-offset:2px}
/* The left edge in the person's own colour, exactly as on the directory row. */
.acc-table td.col-user{box-shadow:inset 3px 0 0 var(--row-hue,transparent)}

/* The one column that keeps the wrapping — it is why the rule exists. */
.acc-table td.col-user{min-width:210px}
.acc-table td.col-user .u-cell{display:flex;align-items:center;gap:9px;min-width:0}
.acc-table td.col-user .u-mail{overflow-wrap:anywhere;color:var(--text-2);font-size:12.5px;line-height:1.35}
.acc-table td.col-person{min-width:150px}
.acc-table td.col-person .nm{font-weight:600;color:var(--text);line-height:1.3}
.acc-table td.col-roles{min-width:200px}
.acc-table td.col-record{min-width:150px}
.acc-table td.col-status,
.acc-table td.col-open{white-space:nowrap;width:1%}
.acc-table td.col-open{text-align:right;color:var(--muted);font-size:11px;font-weight:600}
.acc-table td.col-status .badge,
.acc-table td.col-record .badge{margin:1px 0}
.acc-table .empty-row td{
  text-align:center;color:var(--muted);padding:26px 12px;font-style:italic;
}
/* The caret already used by the old row markup, kept so nothing else moves. */
.acc-table .acc-caret{
  color:var(--muted);font-size:9px;flex:none;display:inline-block;transition:transform .12s;
}
.acc-table tr[aria-expanded="true"] .acc-caret{transform:rotate(90deg)}

/* ---- the opened row ---- */
.acc-table tr.acc-detail>td{
  background:linear-gradient(180deg,#f7f9ff 0%,#f2f5fd 100%);
  border-bottom:2px solid var(--afenet-blue-100);padding:14px 16px 16px;
}
.acc-label{
  font-size:10.5px;font-weight:700;letter-spacing:.07em;text-transform:uppercase;
  color:var(--afenet-blue);margin-bottom:7px;
}
.acc-table tr.acc-detail .row-actions{justify-content:flex-start}

/* ==========================================================================
   THE ROLE EDITOR — fourteen checkboxes, grouped and aligned
   --------------------------------------------------------------------------
   It was an inline-flex run of fourteen labels of wildly different widths, so
   the boxes landed in a different place on every line and the grid Peter was
   looking at had no columns at all. A grid of equal cells fixes the alignment
   outright, and grouping by what the role DOES turns a wall of names into four
   short lists you can check against how AFENET actually works.

   THE COLOUR ADDS NOTHING TO THE MEANING and is not allowed to: every box
   states its role in words, and a ticked box is marked by its checkbox and its
   filled background, never by hue alone.
   ========================================================================== */
.role-editor{max-width:820px}
.role-group{margin-bottom:12px}
.role-group-h{
  font-size:10.5px;font-weight:700;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);margin:0 0 6px;
}
.role-boxes{display:grid;grid-template-columns:repeat(auto-fill,minmax(212px,1fr));gap:6px}
.role-box{
  display:flex;align-items:center;gap:7px;min-width:0;
  padding:7px 10px;border-radius:var(--radius-sm);
  border:1px solid var(--border);background:var(--card);
  font-size:12px;line-height:1.3;cursor:pointer;
  transition:background .12s ease,border-color .12s ease;
}
.role-box:hover{border-color:var(--chip-line,var(--afenet-blue-100));background:#fbfcff}
.role-box input{flex:none;margin:0}
.role-box span{min-width:0;overflow-wrap:normal}
.role-box.is-on{
  background:var(--chip-bg,var(--afenet-blue-light));
  border-color:var(--chip-line,var(--afenet-blue-100));
  color:var(--chip-ink,var(--afenet-blue));font-weight:600;
}
.role-editor-foot{display:flex;align-items:center;gap:12px;flex-wrap:wrap;margin-top:4px}

/* ==========================================================================
   ROLE BADGES — coloured by what the role does, not by its name
   Four families and two edge cases. Legacy roles are dashed and grey: they
   still exist on old records and must be recognisable as something to retire,
   not as a job somebody holds.
   ========================================================================== */
/* The family sets the three chip variables and nothing else, so ONE palette
   serves the badge on the row, the box in the editor and the bar down the
   card. A family added here is coloured in all three places at once. */
.fam-admin  {--chip-bg:var(--afenet-blue-light);--chip-ink:var(--afenet-blue);--chip-line:var(--afenet-blue-100)}
.fam-hr     {--chip-bg:#ede8fa;--chip-ink:#53359c;--chip-line:#d6cbf3}
.fam-prep   {--chip-bg:var(--afenet-gold-50);--chip-ink:#7a5e00;--chip-line:#eeda9f}
.fam-review {--chip-bg:var(--afenet-green-50);--chip-ink:#166b2f;--chip-line:#bfe3c9}
.fam-approve{--chip-bg:var(--afenet-red-50);--chip-ink:#b8302a;--chip-line:#f6c6c2}
.fam-view   {--chip-bg:#e6eef6;--chip-ink:#2d5473;--chip-line:#c4d7e6}
.fam-base   {--chip-bg:#eef1f8;--chip-ink:#5b6377;--chip-line:#dfe3ee}
.fam-legacy {--chip-bg:transparent;--chip-ink:#6b7590;--chip-line:#e3e7f2}

.rb{
  display:inline-flex;align-items:center;gap:5px;padding:3px 9px;
  border-radius:var(--radius-pill);font-size:11px;font-weight:600;line-height:1.5;
  white-space:nowrap;margin:1px 0;
  background:var(--chip-bg,#eef1f8);color:var(--chip-ink,#5b6377);
}
.rb::before{content:'';width:6px;height:6px;border-radius:2px;background:currentColor;flex-shrink:0}
.rb.fam-legacy{border:1px dashed var(--chip-line);padding:2px 8px}

@media (max-width:760px){
  .setup-stats .dir-stat{flex:1 1 96px;min-width:92px;padding:9px 11px}
  .acc-table td.col-user{min-width:180px}
  .role-boxes{grid-template-columns:1fr}
  .fold-setup>summary{flex-wrap:wrap}
  .fold-setup>summary .sc-tail{margin-left:0;width:100%;padding-left:19px}
}

/* ==========================================================================
   BATCH 56 — TIMESHEET, LEAVE MANAGEMENT, PROJECTS & %LOE
   --------------------------------------------------------------------------
   Peter, 2026-09-22, after System Setup: "can we make the Timesheet, Leave
   management and Project % LOE pages colourful? Like we did for employee page?
   Also make things align correctly. Especially the timesheet page so staff will
   enjoy filling the timesheet."

   NO NEW PALETTE. Everything below sets or reads the same three chip variables
   (--chip-bg / --chip-ink / --chip-line) that .cc-0 … .cc-9 already define for
   the staff directory, so a project is the same colour here as it is on
   Employee Management, and a person is the same colour on all four screens.
   The tiles are .dir-stat, the folding card is .setup-card, the bar-and-stats
   strip is .run-progress. Four screens, one set of components.

   WHY THE TIMESHEET GETS THE MOST WORK. It is the only screen in this app that
   every member of staff has to use, every month, twenty-two times — and it was
   a wall of undifferentiated textareas with no way to see, without reading it
   line by line, how much of the month was done. Three things are added and they
   are all answers to "how far have I got?":

     1. A progress strip:  days accounted for, hours banked, whether the %LOE
        still fits. The same component the payroll preparer sees on a run.
     2. Week bands:        twenty-two rows read as five weeks. A person filling
        Friday afternoon looks for "this week", not for "day 18".
     3. Live day state:    the row answers for itself as you type — a pip and a
        count per row, green when every project on that day is described. No
        save, no re-render, no reading required.

   The point of all three is that nothing here waits for a round trip. The grid
   already saved a local draft on every keystroke (saveTsDraft); the same
   keystroke now also repaints the row's state, so encouragement is immediate
   and the server is not involved in it.

   ALIGNMENT, which is the other half of what was asked. Four separate faults:
     - `td{overflow-wrap:anywhere}` (line 262) was breaking "9h" across two
       lines and printing "Sav / e day". Same bug as the payroll run, the
       directory and System Setup. Every column here opts out; nothing in these
       three tables is an e-mail address, which is the only reason that rule
       exists.
     - The day column scrolled away sideways, so on a five-project month you
       could not tell which row you were typing into. It is sticky now.
     - The header scrolled away vertically for the same reason, so by week three
       the columns had no names. Sticky, on a capped-height scrollport — the
       same trade as the directory.
     - The hour figure, the badge and the button in the last column each had
       their own idea of vertical alignment. One grid, one baseline.
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. `.form-row` — 73 USES, AND NOT ONE RULE. THIS IS THE ALIGNMENT BUG.

   Found by ui-qa-classes.js while auditing this batch, and confirmed against
   the whole git history: `.form-row` has been the app's name for "a horizontal
   row of controls" since the beginning and has NEVER had a rule in this file.
   Seventy-three `<div class="form-row">` elements were therefore plain block
   divs: no flex, no gap, no wrapping, no shared baseline. Every control inside
   one sat on its own line, or jammed against its neighbour, depending only on
   whether it happened to be inline.

   That is most of what "make things align correctly" is about, and it is not
   confined to these three screens — it is every screen in the app. The rule is
   written deliberately low-specificity (one class, no element) so that the
   dozens of existing `style="align-items:center"` and `style="gap:14px"`
   attributes on individual rows keep winning, as they already do: this is the
   floor those overrides were compensating for, not a new opinion on top of
   them. Nothing that currently looks right can start looking different.
   -------------------------------------------------------------------------- */
.form-row{
  display:flex;flex-wrap:wrap;align-items:center;gap:10px;
}
/* A label inside one is a field, not a line of prose: it stacks its own caption
   over its control and refuses to stretch the row's height. */
.form-row label{
  display:inline-flex;align-items:center;gap:6px;min-width:0;
  font-size:12.5px;color:var(--text-2);
}
.form-row > .small-note{flex:1 1 auto;min-width:0}

/* --------------------------------------------------------------------------
   1. THE PROGRESS STRIP
   Reuses .run-progress / .rp-* from the payroll run unchanged. Only the
   traffic-light states for the stat tiles are new, because "18 of 22 days" is
   a figure that wants an opinion attached to it and a payout is not.
   -------------------------------------------------------------------------- */
.rp-stat.is-part b{color:#7a5e00}
.rp-stat.is-hours b{color:var(--afenet-blue);font-size:17px}
.run-progress.is-part .rp-bar{background:var(--afenet-gold-50)}
.run-progress.is-part .rp-fill{background:linear-gradient(90deg,#d9a400 0%,var(--afenet-gold) 100%)}

/* --------------------------------------------------------------------------
   2. THE GRID
   Capped height makes the wrapper a real scrollport, which is the only thing
   that makes position:sticky work on a table header. 68vh leaves the progress
   strip and the week band visible while the month scrolls under them.
   -------------------------------------------------------------------------- */
.ts-wrap{
  overflow:auto;max-height:68vh;
  border:1px solid var(--border);border-radius:var(--radius-md);
  background:var(--card);-webkit-overflow-scrolling:touch;
}
.ts-grid{width:100%;border-collapse:separate;border-spacing:0;font-size:13px}

/* ---- header: one band per project, in that project's own colour ---- */
.ts-grid thead th{
  position:sticky;top:0;z-index:4;
  background:linear-gradient(180deg,#f4f7ff 0%,#eef2fd 100%);
  color:var(--afenet-blue);text-align:left;
  padding:9px 11px;font-size:10.5px;font-weight:700;
  text-transform:uppercase;letter-spacing:.06em;
  border-bottom:2px solid var(--afenet-blue-100);
  vertical-align:bottom;overflow-wrap:normal;
}
/* A project column's header carries the project's hue as a filled band, so the
   column below it can stay white — a tinted textarea is harder to read and the
   eye only needs the colour at the top of the column to keep its place. */
.ts-grid thead th.ts-proj{
  background:var(--chip-bg);color:var(--chip-ink);
  border-bottom:2px solid var(--chip-ink);
  text-transform:none;letter-spacing:0;font-size:11.5px;
  min-width:186px;
}
.ts-grid thead th.ts-proj .tp-coag{
  display:block;font-family:var(--font-display);font-weight:600;font-size:12px;
  line-height:1.25;
}
.ts-grid thead th.ts-proj .tp-scope{
  display:block;font-weight:500;font-size:11px;opacity:.82;line-height:1.3;
  overflow-wrap:normal;
}
/* Batch 68: the Project Code, so two lines of one Scope are two readable columns. */
.ts-grid thead th.ts-proj .tp-code{
  display:block;font-weight:700;font-size:10.5px;letter-spacing:.03em;line-height:1.3;
}
.ts-grid thead th.ts-proj .tp-loe{
  display:inline-block;margin-top:4px;padding:1px 8px;border-radius:var(--radius-pill);
  background:rgba(255,255,255,.72);font-size:10.5px;font-weight:700;
  font-variant-numeric:tabular-nums;letter-spacing:.02em;
}
/* The day column is sticky in BOTH directions, so it needs the higher z-index
   where the two sticky axes cross. */
.ts-grid thead th.ts-day-h{left:0;z-index:6;min-width:96px}
.ts-grid thead th.ts-act{text-align:right;min-width:112px}

/* ---- cells ---- */
.ts-grid td{
  padding:8px 11px;border-bottom:1px solid var(--border-soft);
  vertical-align:top;overflow-wrap:normal;   /* never "Sav / e day" */
}

/* ---- the day cell: sticky, and the row's own status lives in it ---- */
.ts-grid td.ts-day{
  position:sticky;left:0;z-index:2;background:var(--card);
  min-width:96px;white-space:nowrap;
  box-shadow:inset 3px 0 0 var(--day-hue,transparent),1px 0 0 var(--border-soft);
}
.ts-day .td-date{
  font-family:var(--font-display);font-size:14px;font-weight:600;color:var(--text);
  font-variant-numeric:tabular-nums;line-height:1.2;
}
.ts-day .td-dow{font-size:11px;font-weight:600;color:var(--muted);letter-spacing:.04em;text-transform:uppercase}
.ts-day .td-hrs{
  display:inline-block;margin-top:3px;padding:1px 7px;border-radius:var(--radius-pill);
  background:var(--border-soft);color:var(--text-2);
  font-size:10.5px;font-weight:600;font-variant-numeric:tabular-nums;
}

/* ---- the week band ----
   The <th> spans every column, so it is WIDER than the scrollport and
   position:sticky on it has nowhere to move — the label would scroll off the
   left edge with the table. The band cell therefore stays put and its CONTENTS
   are the sticky thing, which is what keeps "Week 3" beside the day column on
   a five-project month. */
.ts-grid tr.ts-week>th{
  background:linear-gradient(90deg,var(--afenet-blue-light) 0%,#f6f8ff 55%,var(--card) 100%);
  text-align:left;padding:0;
  border-top:1px solid var(--afenet-blue-100);
  border-bottom:1px solid var(--afenet-blue-100);
  position:static;top:auto;   /* the band scrolls; only the column header is pinned */
}
.ts-grid tr.ts-week .tw-inner{
  position:sticky;left:0;z-index:3;
  display:inline-flex;align-items:center;flex-wrap:wrap;gap:0;
  padding:7px 12px;font-family:var(--font-display);font-size:11.5px;font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;color:var(--afenet-blue);
}
.ts-grid tr.ts-week .tw-span{
  margin-left:10px;font-family:var(--font-body);font-weight:500;
  letter-spacing:0;text-transform:none;color:var(--text-2);
}
.ts-grid tr.ts-week .tw-tally{
  margin-left:10px;font-family:var(--font-body);font-weight:600;letter-spacing:0;
  text-transform:none;font-variant-numeric:tabular-nums;
}
.ts-grid tr.ts-week .tw-tally.is-done{color:#166b2f}
.ts-grid tr.ts-week .tw-tally.is-part{color:#7a5e00}
.ts-grid tr.ts-week .tw-tally.is-empty{color:var(--muted)}

/* --------------------------------------------------------------------------
   3. LIVE DAY STATE
   The classes are set by refreshDayState() on every keystroke. They carry NO
   information that is not also written in words in the same row — a person who
   cannot see the difference between the green and the gold reads "3 of 3" and
   "1 of 3" instead, and the pip is a different SHAPE in each state, not only a
   different colour.
   -------------------------------------------------------------------------- */
.ts-row{transition:background .14s ease}
.ts-row:hover>td{background:rgba(33,64,154,.04)}
.ts-row:hover>td.ts-day{background:#f4f7ff}

.ts-row.is-done>td{background:rgba(31,138,61,.055)}
.ts-row.is-done>td.ts-day{background:#f0faf3;box-shadow:inset 3px 0 0 var(--afenet-green),1px 0 0 var(--border-soft)}
.ts-row.is-part>td{background:rgba(245,197,24,.07)}
.ts-row.is-part>td.ts-day{background:#fdfaef;box-shadow:inset 3px 0 0 var(--afenet-gold),1px 0 0 var(--border-soft)}

/* Today, and it wins over done/part on the day cell — "where am I" is a more
   urgent question than "is this row finished", and the row still shows its
   state through the pip and the tint on the project cells. */
.ts-row.is-today>td.ts-day{
  background:var(--afenet-blue-light);
  box-shadow:inset 3px 0 0 var(--afenet-blue),1px 0 0 var(--border-soft);
}
.ts-today-chip{
  display:inline-block;margin-left:5px;padding:0 6px;border-radius:var(--radius-pill);
  background:var(--afenet-blue);color:#fff;font-size:9.5px;font-weight:700;
  letter-spacing:.06em;text-transform:uppercase;vertical-align:1px;
}

/* ---- the pip: three states, three shapes ---- */
.ts-state{display:flex;align-items:center;gap:6px;justify-content:flex-end}
.ts-pip{
  width:15px;height:15px;border-radius:50%;flex:none;position:relative;
  border:2px dashed var(--border);background:var(--card);
}
.ts-pip.is-part{
  border:2px solid var(--afenet-gold);border-radius:50%;
  background:linear-gradient(90deg,var(--afenet-gold) 50%,transparent 50%);
}
.ts-pip.is-done{
  border:2px solid var(--afenet-green);background:var(--afenet-green);
}
.ts-pip.is-done::after{
  content:'';position:absolute;left:3px;top:0px;width:4px;height:7px;
  border:solid #fff;border-width:0 2px 2px 0;transform:rotate(40deg);
}
.ts-count{
  font-size:11px;font-weight:600;color:var(--muted);
  font-variant-numeric:tabular-nums;white-space:nowrap;
}
.ts-row.is-done .ts-count{color:#166b2f}
.ts-row.is-part .ts-count{color:#7a5e00}

/* ---- a project cell ---- */
.ts-cell{min-width:186px}
.ts-cell .tc-hrs{
  display:inline-block;margin-bottom:4px;padding:1px 8px;border-radius:var(--radius-pill);
  background:var(--chip-bg);color:var(--chip-ink);border:1px solid var(--chip-line);
  font-size:10.5px;font-weight:700;font-variant-numeric:tabular-nums;
  letter-spacing:.02em;white-space:nowrap;
}
.ts-cell textarea{
  width:100%;min-width:0;display:block;
  border:1px solid var(--border);border-radius:var(--radius-sm);
  padding:7px 9px;font:inherit;font-size:12.5px;line-height:1.45;
  resize:vertical;background:var(--card);color:var(--text);
  transition:border-color .12s ease,box-shadow .12s ease,background .12s ease;
}
.ts-cell textarea::placeholder{color:#a8b0c4;font-size:12px}
.ts-cell textarea:hover{border-color:var(--chip-line)}
.ts-cell textarea:focus{
  outline:none;border-color:var(--chip-ink);
  box-shadow:0 0 0 3px var(--chip-bg);background:#fff;
}
/* A filled cell keeps a quiet tint in its project's colour: the column reads as
   a column even where the header has scrolled out of a printed page. */
.ts-cell textarea.has-text{border-color:var(--chip-line);background:color-mix(in srgb,var(--chip-bg) 38%,#fff)}
@supports not (color:color-mix(in srgb,#fff 50%,#000)){
  .ts-cell textarea.has-text{background:#fbfcfe}
}
/* saveDay() paints this on a blank description when a save is refused. */
.ts-cell textarea.is-missing{border-color:var(--afenet-red);box-shadow:0 0 0 3px var(--afenet-red-50)}
.ts-cell.is-future textarea{background:#f4f6fb;color:#9aa3b8;border-style:dashed}

.ts-repeat{margin-top:4px}
.ts-repeat .link-btn{font-size:11px}

/* ---- the action cell ---- */
.ts-grid td.ts-act{text-align:right;white-space:nowrap;min-width:112px}
.ts-act .btn-day{
  display:inline-block;width:100%;max-width:104px;
  border:1px solid var(--border);border-radius:var(--radius-sm);
  background:var(--card);color:var(--text-2);
  padding:6px 10px;font-size:12px;font-weight:600;cursor:pointer;
  transition:background .12s ease,border-color .12s ease,color .12s ease,transform .12s ease;
}
.ts-act .btn-day:hover{transform:translateY(-1px)}
.ts-act .btn-day:focus-visible{outline:2px solid var(--afenet-blue);outline-offset:2px}
/* The button only looks like a primary action once the row can actually be
   saved. A blue Save beside a half-filled row is an invitation to an error. */
.ts-row.is-done .btn-day{
  background:linear-gradient(180deg,#25a04a 0%,var(--afenet-green) 100%);
  border-color:#1a7434;color:#fff;
}
.ts-row.is-saved .btn-day{background:var(--card);border-color:var(--border);color:var(--text-2)}
.ts-act .ts-saved{display:block;margin-top:5px}

/* ---- a locked (approved-leave) row ---- */
.ts-row.is-leave>td{background:var(--afenet-teal-50);color:#0f5563}
.ts-row.is-leave>td.ts-day{background:#e0eff2;box-shadow:inset 3px 0 0 var(--afenet-teal),1px 0 0 var(--border-soft)}
.ts-row.is-leave .tl-note{font-size:12px;color:#12606f}
.ts-row.is-future>td{background:#fafbfe}
.ts-row.is-future .ts-count{color:#a8b0c4}

/* The holiday/working selector, sized so the column does not jump when it
   changes and never wider than the day column it sits beside. */
.ts-grid td.ts-status{min-width:132px}
.ts-status select{
  width:100%;max-width:126px;font-size:12px;padding:5px 7px;
  border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--card);
}
.ts-row.is-holiday>td{background:var(--afenet-gold-50)}
.ts-row.is-holiday>td.ts-day{background:#fdf4d8;box-shadow:inset 3px 0 0 var(--afenet-gold),1px 0 0 var(--border-soft)}

/* ---- the footer, aligned on one row rather than three floating buttons ---- */
.ts-foot{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  margin-top:13px;padding-top:13px;border-top:1px solid var(--border-soft);
}
.ts-foot .ts-foot-spacer{flex:1;min-width:6px}

@media (max-width:760px){
  .ts-wrap{max-height:none}                 /* a phone has no room for a scrollport inside a scroll */
  .ts-grid td.ts-day,.ts-grid thead th.ts-day-h{position:static;box-shadow:inset 3px 0 0 var(--day-hue,transparent)}
  .ts-cell,.ts-grid thead th.ts-proj{min-width:150px}
  .ts-act .btn-day{max-width:none}
}
@media (prefers-reduced-motion:reduce){
  .ts-act .btn-day:hover{transform:none}
  .ts-cell textarea{transition:none}
}

/* --------------------------------------------------------------------------
   4. THE MONTHLY LOE CHECK TABLE
   Six columns of figures with one badge at the end. The figures get tabular
   numerals and right alignment so the column can be read down; the effective
   percentage gets a bar, because "60% assigned / 58.4% effective" is a
   comparison and a comparison wants a length, not two numbers.
   -------------------------------------------------------------------------- */
.loe-check{width:100%;border-collapse:separate;border-spacing:0;font-size:13px}
.loe-check thead th{
  background:linear-gradient(180deg,#f4f7ff 0%,#eef2fd 100%);
  color:var(--afenet-blue);text-align:left;
  padding:9px 11px;font-size:10.5px;font-weight:700;
  text-transform:uppercase;letter-spacing:.06em;white-space:nowrap;
  border-bottom:2px solid var(--afenet-blue-100);overflow-wrap:normal;
}
.loe-check thead th.num,.loe-check td.num{text-align:right;font-variant-numeric:tabular-nums}
.loe-check td{padding:8px 11px;border-bottom:1px solid var(--border-soft);overflow-wrap:normal;vertical-align:middle}
.loe-check tbody tr:hover>td{background:rgba(33,64,154,.04)}
.loe-check td.col-proj{box-shadow:inset 3px 0 0 var(--chip-ink,transparent)}
.loe-check .lc-coag{font-weight:600;color:var(--text)}
.loe-check .lc-scope{font-size:11.5px;color:var(--muted)}
.loe-check tr.lc-total>td{
  background:var(--afenet-blue-light);font-weight:700;color:var(--afenet-blue);
  border-top:2px solid var(--afenet-blue-100);border-bottom:none;
}
.loe-check tr.lc-total:hover>td{background:var(--afenet-blue-light)}
.loe-check tr.lc-bad>td{background:var(--afenet-red-50)}
.loe-check tr.lc-bad:hover>td{background:var(--afenet-red-50)}

/* Assigned vs effective, as a track with a marker at the assigned figure. */
.lc-fit{display:flex;align-items:center;gap:8px;justify-content:flex-end;min-width:128px}
.lc-track{
  position:relative;flex:1;min-width:64px;max-width:96px;height:8px;
  background:var(--border-soft);border-radius:var(--radius-pill);overflow:hidden;
}
.lc-fill{height:100%;border-radius:var(--radius-pill);min-width:2px;
  background:linear-gradient(90deg,var(--afenet-blue),var(--afenet-blue-mid))}
.lc-fit.is-over .lc-fill{background:linear-gradient(90deg,var(--afenet-red),#f2705f)}
.lc-fit.is-idle .lc-fill{background:var(--border)}
.lc-pct{font-variant-numeric:tabular-nums;font-weight:600;min-width:46px;text-align:right}

/* --------------------------------------------------------------------------
   5. LEAVE BALANCE TILES
   The five-column table said the same thing, and said it as arithmetic the
   reader had to do: entitlement minus used. The tile puts the answer — days
   left — in the largest type on the card and keeps the working underneath.
   An uncapped type draws NO bar: a bar with no ceiling encodes a length that
   means nothing, exactly as with .an-withheld on the analytics screen.
   -------------------------------------------------------------------------- */
.lv-tiles{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(196px,1fr));
  gap:12px;margin:4px 0 2px;
}
.lv-tile{
  position:relative;border:1px solid var(--chip-line,var(--border));
  border-radius:var(--radius-md);background:var(--card);
  padding:13px 15px 14px;min-width:0;overflow:hidden;
}
.lv-tile::before{
  content:'';position:absolute;left:0;top:0;bottom:0;width:4px;
  background:var(--chip-ink,var(--border));
}
.lv-type{
  font-family:var(--font-display);font-size:12px;font-weight:600;
  color:var(--chip-ink,var(--text-2));line-height:1.3;
  display:block;margin-bottom:6px;overflow-wrap:normal;
}
.lv-left{
  display:flex;align-items:baseline;gap:6px;
}
.lv-left b{
  font-family:var(--font-display);font-size:27px;font-weight:600;line-height:1;
  color:var(--text);font-variant-numeric:tabular-nums;letter-spacing:-.02em;
}
.lv-left span{font-size:11px;font-weight:600;color:var(--muted);letter-spacing:.04em;text-transform:uppercase}
.lv-bar{
  margin-top:10px;height:7px;border-radius:var(--radius-pill);
  background:var(--chip-bg,var(--border-soft));overflow:hidden;
}
.lv-fill{
  height:100%;border-radius:var(--radius-pill);min-width:2px;
  background:var(--chip-ink,var(--afenet-blue));opacity:.85;
}
.lv-used{
  display:block;margin-top:6px;font-size:11px;color:var(--muted);
  font-variant-numeric:tabular-nums;overflow-wrap:normal;
}
/* Running out. The tile keeps its own hue — only the figure and the bar turn,
   so one low balance among nine does not recolour the whole strip. */
.lv-tile.is-low .lv-left b{color:var(--danger)}
.lv-tile.is-low .lv-fill{background:var(--afenet-red);opacity:1}
.lv-tile.is-gone .lv-left b{color:var(--danger)}
.lv-tile.is-uncapped .lv-left b{font-size:19px;letter-spacing:0}
.lv-flag{
  display:inline-block;margin-top:8px;padding:1px 8px;border-radius:var(--radius-pill);
  font-size:10.5px;font-weight:700;letter-spacing:.03em;white-space:nowrap;
}
.lv-flag.is-low{background:var(--afenet-red-50);color:#b8302a}
.lv-flag.is-free{background:var(--afenet-green-50);color:#166b2f}
.lv-flag.is-note{background:var(--border-soft);color:var(--muted);font-weight:600}

/* ---- the entitlement reference list, once it is folded away ---- */
.lv-rules{
  display:grid;grid-template-columns:repeat(auto-fill,minmax(258px,1fr));
  gap:8px 20px;margin:2px 0 0;padding:0;list-style:none;
}
.lv-rules li{
  display:flex;gap:8px;align-items:flex-start;font-size:12.5px;color:var(--text-2);
  padding:5px 0;border-bottom:1px solid var(--border-soft);min-width:0;
}
.lv-rules li:last-child{border-bottom:none}
.lv-rules .lr-name{font-weight:600;color:var(--text);flex:none;min-width:0}
.lv-rules .lr-val{color:var(--muted);min-width:0}

/* ---- my requests / approved history ---- */
.lv-table{width:100%;border-collapse:separate;border-spacing:0;font-size:13px}
.lv-table thead th{
  position:sticky;top:0;z-index:3;
  background:linear-gradient(180deg,#f4f7ff 0%,#eef2fd 100%);
  color:var(--afenet-blue);text-align:left;
  padding:9px 11px;font-size:10.5px;font-weight:700;
  text-transform:uppercase;letter-spacing:.06em;white-space:nowrap;
  border-bottom:2px solid var(--afenet-blue-100);
}
.lv-table td{padding:9px 11px;border-bottom:1px solid var(--border-soft);vertical-align:middle;overflow-wrap:normal}
.lv-table tbody tr:nth-child(even)>td{background:rgba(17,26,46,.016)}
.lv-table tbody tr:hover>td{background:rgba(33,64,154,.045)}
.lv-table tbody tr:last-child>td{border-bottom:none}
.lv-table td.col-type{box-shadow:inset 3px 0 0 var(--chip-ink,transparent);font-weight:600}
.lv-table td.col-dates{white-space:nowrap;font-variant-numeric:tabular-nums}
.lv-table td.col-days{text-align:right;font-variant-numeric:tabular-nums}
.lv-table-wrap{
  overflow:auto;max-height:52vh;
  border:1px solid var(--border);border-radius:var(--radius-md);background:var(--card);
}

/* The request form, on a grid so the fields line up on one baseline instead of
   wrapping into a staircase. Each label is its own cell; the ones that are
   revealed conditionally (start time, medical report) occupy their cell and do
   not reflow the rest when they appear. */
.lv-form{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(196px,1fr));
  gap:12px 16px;align-items:end;
}
.lv-form label{display:flex;flex-direction:column;gap:5px;min-width:0;font-size:12px;font-weight:600;color:var(--text-2)}
.lv-form label input,.lv-form label select{width:100%;min-width:0}
.lv-form .lv-form-wide{grid-column:1/-1}
.lv-form .lv-form-go{grid-column:1/-1;display:flex;gap:10px;align-items:center;flex-wrap:wrap}
.lv-form label .small-note{font-weight:500}

/* --------------------------------------------------------------------------
   6. PROJECTS & %LOE
   The person rows already fold (.acc-*). What they lacked was the directory's
   identity: the avatar and code chip that make a name findable by colour on
   Employee Management were absent here, so the same person looked like a
   different kind of object on two screens. They are the same object now.
   -------------------------------------------------------------------------- */
.acc-head .acc-ident{display:flex;align-items:center;gap:8px;min-width:0;flex:none}
.acc-row.has-hue .acc-head{box-shadow:inset 3px 0 0 var(--row-hue,transparent)}

/* The split, as one bar of segments in the projects' own colours. A 60/40 is a
   shape before it is two numbers, and an 80/40 is visibly past its end. */
.loe-split{
  display:flex;height:9px;border-radius:var(--radius-pill);overflow:hidden;
  background:var(--border-soft);flex:none;width:112px;
}
.loe-split i{display:block;height:100%;background:var(--chip-ink,var(--afenet-blue))}
.loe-split.is-over{box-shadow:0 0 0 1.5px var(--afenet-red)}
.loe-split.is-under{opacity:.8}
.loe-split.is-empty{background:repeating-linear-gradient(135deg,var(--border-soft) 0 4px,#fff 4px 8px)}

/* The %LOE editor table inside an opened row. */
.loe-edit{width:100%;border-collapse:separate;border-spacing:0;font-size:13px}
.loe-edit thead th{
  background:var(--afenet-blue-light);color:var(--afenet-blue);text-align:left;
  padding:8px 11px;font-size:10.5px;font-weight:700;text-transform:uppercase;
  letter-spacing:.06em;white-space:nowrap;border-bottom:1px solid var(--afenet-blue-100);
}
.loe-edit thead th.num{text-align:right}
.loe-edit td{padding:8px 11px;border-bottom:1px solid var(--border-soft);vertical-align:middle;overflow-wrap:normal;background:var(--card)}
.loe-edit tbody tr:last-child td{border-bottom:none}
.loe-edit td.col-proj{box-shadow:inset 3px 0 0 var(--chip-ink,transparent)}
.loe-edit .le-coag{font-weight:600;color:var(--text)}
.loe-edit .le-scope{font-size:11.5px;color:var(--muted)}
/* Batch 68: the Project Code under the Scope, the edit/add forms, a line taken off. */
.loe-edit .le-code{display:inline-block;margin-top:3px;padding:0 7px;border-radius:var(--radius-pill);
  background:var(--afenet-blue-light);color:var(--afenet-blue);font-size:11px;font-weight:700;letter-spacing:.02em}
.loe-edit .le-none{font-weight:500;font-style:italic;color:var(--muted)}
.loe-edit tr.is-off td{opacity:.62}
.loe-edit tr.is-off td.col-act{opacity:1}
.loe-edit td.col-act .small+.small{margin-left:6px}
/* Save, Edit and Delete side by side; the "standing since" / "off from" note sits above them,
   so three buttons never push the table past its card on a laptop screen. */
.loe-edit td.col-act{white-space:normal}
.loe-edit td.col-act > .small-note{display:block;margin:0 0 4px}
.loe-edit td.col-act > button{white-space:nowrap;margin-top:2px}
.loe-edit tr.al-edit-row td{background:var(--afenet-blue-light);padding:12px 14px}
.al-inline{gap:10px 14px}
.al-inline .al-form-go{display:flex;align-items:center;gap:8px;flex-wrap:wrap}
.al-add{margin-top:10px}
.al-add.is-open{padding:12px 14px;border:1px dashed var(--afenet-blue-100);border-radius:var(--radius-sm);background:var(--card)}
.loe-hits{font-style:normal;text-align:left;padding:4px 2px 8px}
.person-search input[type="search"]{flex:1 1 320px;max-width:560px}
.loe-edit td.col-pct{text-align:right;white-space:nowrap}
.loe-edit td.col-pct input{
  width:78px;text-align:right;font-variant-numeric:tabular-nums;font-weight:600;
  border:1px solid var(--border);border-radius:var(--radius-sm);padding:6px 8px;
}
.loe-edit td.col-pct input:focus{outline:none;border-color:var(--afenet-blue);box-shadow:0 0 0 3px var(--afenet-blue-light)}
.loe-edit td.col-act{white-space:nowrap;text-align:right}
.loe-edit-foot{
  display:flex;align-items:center;gap:10px;flex-wrap:wrap;
  margin-top:9px;font-size:12px;color:var(--text-2);
}

/* The roster form, on the same grid as the leave form so the two screens do
   not present a row of inputs in two different shapes. */
.al-form{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(186px,1fr));
  gap:12px 16px;align-items:end;
}
.al-form label{display:flex;flex-direction:column;gap:5px;min-width:0;font-size:12px;font-weight:600;color:var(--text-2)}
.al-form label input,.al-form label select{width:100%;min-width:0}
/* The Staff picker holds "Nakiwala Christine Nabukenya - Senior Programme
   Officer, Surveillance" in a 186px grid cell. It gets the whole row. */
.al-form .al-form-wide{grid-column:1/-1}
.al-form .al-form-go{grid-column:1/-1}

@media (max-width:760px){
  .lv-tiles{grid-template-columns:repeat(auto-fill,minmax(150px,1fr));gap:10px}
  .lv-left b{font-size:23px}
  .lv-rules{grid-template-columns:1fr}
  .loe-split{width:78px}
  .lv-table-wrap{max-height:none}
}
