@import url('https://fonts.googleapis.com/css2?family=Cairo:wght@400;500;600;700;800;900&display=swap');

:root{
  --brand-green:#4A8E55; --brand-green-dark:#29624F; --brand-teal:#39836B; --brand-dark-teal:#1E4D58;
  --brand-gold:#C3A355; --brand-gold-dark:#a3843f; --brand-cream:#E5D9B4; --brand-charcoal:#353334; --brand-turquoise:#53AA8A;
  /* Aqua is the one hero-only accent — straight from the ministry's own landing-page materials
     (MOI Design Files/LandingPage), not used anywhere in the data screens below so it stays a
     distinct signal for "this is the entry moment," the same discipline the code-badge uses for
     "this is domain data." */
  --brand-aqua:#61DCD4;

  --bg:#f2f4f0; --bg2:#e9ede8; --panel:#ffffff; --panel2:#f6f8f5;
  --border:#e0e5db; --border-strong:#cdd6c7; --text:var(--brand-charcoal); --muted:#6b6f6a; --soft:#98a196;

  --brand:var(--brand-green); --brand-dark:var(--brand-green-dark); --accent:var(--brand-gold); --accent2:var(--brand-cream);
  --p1:#b5432e; --p2:#c3a355; --p3:#4A8E55; --p4:#29624F;
  /* --warn was previously byte-identical to --brand-gold (both #c3a355) purely by historical
     accident (design-system.md's "Button weight" note) — two differently-named tokens sharing one
     literal value is a token-hygiene problem even after the .btn.warn outline-vs-solid treatment
     patched the one collision it caused. Shifted to a distinct, still-amber hue (#B8860B, "dark
     goldenrod") so --warn and --brand-gold are genuinely separate tokens, not the same color twice. */
  --ok:#4A8E55; --warn:#B8860B; --danger:#b5432e; --info:#1E4D58; --escalation2:#7a1f3d;

  --focus-ring:var(--brand-green);
  --radius-sm:8px; --radius:14px; --radius-lg:20px;
  --shadow-sm:0 2px 8px rgba(41,68,50,.06);
  --shadow:0 10px 28px rgba(41,68,50,.09), 0 2px 6px rgba(41,68,50,.06);
  --shadow-lg:0 22px 50px rgba(30,50,36,.16), 0 4px 12px rgba(30,50,36,.08);
  --sidebar-w:268px; --sidebar-w-collapsed:76px; --topbar-h:70px;

  /* Elevation tiers (redesign pass) — named depth levels instead of one near-uniform shadow used
     everywhere. --elev-1/2 alias the existing resting/hover shadows so nothing already using
     --shadow-sm/--shadow breaks; --elev-3/4 are new, reserved for floating chrome (popovers, the
     notification panel, the command palette) and true modals respectively, so a user can read
     "what's on top of what" from shadow depth alone. */
  --elev-1:var(--shadow-sm);
  --elev-2:var(--shadow);
  --elev-3:0 18px 40px rgba(30,50,36,.16), 0 4px 10px rgba(30,50,36,.08);
  --elev-4:var(--shadow-lg);

  /* Wider type scale (redesign pass) — a true display size for hero numbers/headlines, distinct
     from the 1.05rem page title that previously doubled as the biggest text on the whole app. */
  --fs-display:2.75rem; --fs-h1:1.55rem; --fs-h2:1.15rem;
}
[data-theme="dark"]{
  --bg:#0e1510; --bg2:#111a13; --panel:#182119; --panel2:#1c2620;
  --border:#28362d; --border-strong:#374a3c; --text:#eef3ec; --muted:#a4b3a5; --soft:#7f9080;
  --shadow-sm:0 2px 8px rgba(0,0,0,.3);
  --shadow:0 10px 28px rgba(0,0,0,.4), 0 2px 6px rgba(0,0,0,.3);
  --shadow-lg:0 24px 55px rgba(0,0,0,.55), 0 4px 14px rgba(0,0,0,.35);
  --elev-1:var(--shadow-sm); --elev-2:var(--shadow);
  --elev-3:0 18px 40px rgba(0,0,0,.45), 0 4px 10px rgba(0,0,0,.3);
  --elev-4:var(--shadow-lg);
}

*{box-sizing:border-box}
html,body{height:100%}
body{
  margin:0; font-family:"Cairo","Segoe UI",Tahoma,system-ui,-apple-system,sans-serif;
  background:
    radial-gradient(1100px 480px at 100% -8%, color-mix(in srgb, var(--brand-gold) 7%, transparent), transparent 60%),
    radial-gradient(900px 420px at -10% 0%, color-mix(in srgb, var(--brand-green) 6%, transparent), transparent 55%),
    var(--bg);
  background-attachment:fixed;
  color:var(--text); direction:rtl; -webkit-font-smoothing:antialiased;
}
h1,h2,h3,h4{margin:0}
a{color:inherit}
button, input, select, textarea{font-family:inherit; font-size:1rem}
.dtin{direction:ltr; text-align:left; font-variant-numeric:lining-nums}
::-webkit-scrollbar{width:10px; height:10px}
::-webkit-scrollbar-thumb{background:var(--soft); border-radius:8px}
::selection{background:color-mix(in srgb, var(--brand) 30%, transparent)}

/* Global focus affordance — every interactive element gets a visible, on-brand ring; nothing
   relies on the browser default outline (which is easy to lose against a colored background). */
a:focus-visible, button:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible,
[tabindex]:focus-visible{
  outline:2px solid var(--focus-ring); outline-offset:2px; border-radius:6px;
}

/* Layout */
#shell{display:flex; min-height:100vh}
#sidebar{
  width:var(--sidebar-w); flex:0 0 var(--sidebar-w);
  background:linear-gradient(190deg, var(--brand-dark) 0%, #1f4a3d 45%, var(--brand-charcoal) 100%);
  color:#fff; display:flex; flex-direction:column; padding:20px 16px; gap:4px; position:sticky; top:0; height:100vh;
  box-shadow:2px 0 24px rgba(0,0,0,.12); z-index:30; transition:width .2s ease;
}
/* Desktop collapse-to-icons (redesign §7.8) — a real user preference, persisted in localStorage
   (see app.js toggleSidebarCollapse), unlike the mobile drawer below which is ephemeral per-view. */
#sidebar.collapsed{width:var(--sidebar-w-collapsed); flex-basis:var(--sidebar-w-collapsed); padding-inline:12px}
#sidebar.collapsed .brand .titles, #sidebar.collapsed .navlink span, #sidebar.collapsed .navlink .badge,
#sidebar.collapsed .userbox .who, #sidebar.collapsed .userbox .logout-icon-btn{display:none}
#sidebar.collapsed .navlink{justify-content:center}
#sidebar.collapsed .userbox{justify-content:center}
#sidebar .brand{display:flex; align-items:center; gap:11px; padding:6px 6px 20px; border-bottom:1px solid rgba(255,255,255,.14); margin-bottom:14px}
/* revision v8/v9 §7 — the seal is a real "go home" control now, not just decoration. */
#sidebar .brand[role="button"]{cursor:pointer; border-radius:10px; transition:background .15s}
#sidebar .brand[role="button"]:hover{background:rgba(255,255,255,.06)}
#sidebar .collapse-toggle{
  position:absolute; inset-inline-end:-13px; top:74px; width:26px; height:26px; border-radius:50%;
  background:var(--brand-dark); border:1px solid rgba(255,255,255,.25); color:#fff; cursor:pointer;
  display:flex; align-items:center; justify-content:center; box-shadow:var(--elev-2); transition:transform .2s;
}
#sidebar.collapsed .collapse-toggle{transform:rotate(180deg)}
.sidebar-backdrop{position:fixed; inset:0; background:rgba(10,18,12,.5); z-index:29; display:none}
@media(max-width:860px){
  #sidebar{position:fixed; inset-inline-start:0; z-index:50; box-shadow:var(--elev-4); transform:translateX(100%); transition:transform .22s ease-out; width:280px; flex-basis:280px}
  #sidebar.open{transform:translateX(0)}
  #sidebar.collapsed{width:280px; flex-basis:280px} /* collapse is a desktop-only concept; mobile drawer ignores it */
  #sidebar.collapsed .brand .titles, #sidebar.collapsed .navlink span, #sidebar.collapsed .navlink .badge,
  #sidebar.collapsed .userbox .who{display:block}
  #sidebar.collapsed .userbox{justify-content:flex-start}
  #sidebar .collapse-toggle{display:none}
  .sidebar-backdrop.show{display:block}
  #sidebar-toggle{display:flex !important}
}
@media (prefers-reduced-motion: reduce){ #sidebar{transition:none} }
/* The ministry seal (MOI Design Files/Logo), used untouched — a glass-teal chip behind it (the
   landing page's own icon-wrap treatment) rather than a flat color square, so the seal reads on
   the dark sidebar without any color/border being added to the mark itself. */
#sidebar .brand .mark{
  width:44px; height:44px; border-radius:12px; background:linear-gradient(150deg, rgba(255,255,255,.12), rgba(255,255,255,.03));
  border:1px solid rgba(255,255,255,.16);
  display:flex; align-items:center; justify-content:center;
  box-shadow:0 4px 12px rgba(0,0,0,.25); padding:6px;
}
#sidebar .brand .mark img{width:100%; height:100%; object-fit:contain}
#sidebar .brand .titles{line-height:1.3}
#sidebar .brand .t1{font-weight:800; font-size:.96rem}
#sidebar .brand .t2{font-size:.71rem; opacity:.7}
.navlink{
  display:flex; align-items:center; gap:11px; padding:11px 13px; border-radius:10px; color:#fff; text-decoration:none;
  opacity:.78; cursor:pointer; font-weight:600; font-size:.92rem; border-inline-start:3px solid transparent;
  transition:background .15s, opacity .15s, border-color .15s;
}
.navlink svg{flex:0 0 auto; opacity:.9}
.navlink:hover{background:rgba(255,255,255,.08); opacity:1}
.navlink.active{background:rgba(255,255,255,.13); opacity:1; border-inline-start-color:var(--accent); font-weight:800}
.navlink .badge{margin-inline-start:auto; background:var(--danger); color:#fff; border-radius:999px; font-size:.7rem; padding:1px 7px; font-weight:800}
#sidebar .spacer{flex:1}
#sidebar .userbox{border-top:1px solid rgba(255,255,255,.14); padding-top:14px; font-size:.82rem; display:flex; align-items:center; gap:10px}
#sidebar .userbox .av{width:34px; height:34px; flex:0 0 34px; border-radius:50%; background:rgba(255,255,255,.14); display:flex; align-items:center; justify-content:center; font-weight:800; font-size:.78rem}
#sidebar .userbox .who{min-width:0; flex:1}
#sidebar .userbox .name{font-weight:800; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
#sidebar .userbox .role{opacity:.7; font-size:.72rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
#sidebar .userbox .logout-icon-btn{
  flex:0 0 auto; width:32px; height:32px; border-radius:9px; border:1px solid rgba(255,255,255,.2); background:transparent;
  color:#fff; display:flex; align-items:center; justify-content:center; cursor:pointer; transition:background .15s;
}
#sidebar .userbox .logout-icon-btn:hover{background:rgba(255,255,255,.12)}

#main{flex:1; min-width:0; width:0; display:flex; flex-direction:column}
#topbar{
  display:flex; align-items:center; gap:14px; padding:15px 26px; background:color-mix(in srgb, var(--panel) 92%, transparent);
  backdrop-filter:saturate(1.4) blur(8px); border-bottom:1px solid var(--border);
  position:sticky; top:0; z-index:20;
}
#topbar h1{font-size:var(--fs-h1); margin:0; font-weight:800; letter-spacing:-.01em}
#topbar .iconbtn#sidebar-toggle{margin-inline-end:4px}
#topbar .spacer{flex:1}
/* overflow-x:clip, not hidden (revision v7 §6): "hidden" on only one axis forces the browser to
   promote the other axis's default 'visible' to 'auto' (CSS Overflow spec), which silently turned
   #content into a nested scroll container. .actions-rail's position:sticky descendant then
   miscalculated its offset against that unintended scrollport — even at zero scroll it rendered
   ~49-160px below its true static position (proportional to its own `top` value, verified live via
   getBoundingClientRect()), overlapping the "السجل الزمني للإجراءات" timeline card directly below
   it. clip gives the same overflow guard without the promotion (verified: overflow-y stays
   'visible', sticky offset returns to correct). */
#content{padding:26px; flex:1; min-width:0; overflow-x:clip}
#content canvas{max-width:100%}
@media(max-width:860px){ #content{padding:16px} #topbar{padding:12px 16px} }
#sidebar-toggle{display:none}

/* Revision v14 §3 — executive dashboard: a single-viewport briefing screen, no scroll at any axis
   *when the viewport actually has room* (a real 1920x1080 kiosk display). Revision v16 fix: the
   previous hard `height:100vh; overflow:hidden` + `flex:1; min-height:0` combination silently
   clipped/zeroed-out content the instant the real browser viewport was shorter than the design
   assumption — true on almost every real desktop (browser chrome, OS taskbar, a non-maximized
   window, or a monitor that simply isn't 1080px tall all eat into the usable height). Live-measured
   on this dev machine: .exec-dash-body collapsed to `clientHeight:0` — the entire priority/
   department/on-time-ring section was 100% invisible, not just cropped. Fixed by switching every
   hard `height`/`overflow:hidden` here to `min-height`/normal flow: when content fits, the result
   is pixel-identical to before (fills the screen, no visible scrollbar); when it doesn't, the page
   scrolls like every other screen in the app instead of hiding data. Toggled by
   ExecutiveDashboardView.render() and cleared on every route change (app.js). */
body.exec-dash-active{min-height:100vh}
body.exec-dash-active #shell{min-height:100vh}
body.exec-dash-active #content{padding:10px 16px; display:flex; flex-direction:column}
.exec-dash{min-height:calc(100vh - var(--topbar-h) - 32px); display:flex; flex-direction:column; gap:8px}
.exec-dash-head{display:flex; align-items:center; justify-content:space-between; flex:0 0 auto}
.exec-dash-head h1{font-size:1.3rem; margin:0; font-weight:900; display:flex; align-items:center; gap:10px}
.exec-dash-clock{font-weight:700; color:var(--muted); font-variant-numeric:tabular-nums}
/* QA round 2 §1/§3 — was 3 columns (priority bars / department / health ring); the priority-bars
   column and health ring both moved out (§3 deleted the former outright, §1 moved the latter into
   .exec-dash-kpis-top), leaving the department widget as the row's sole column. [2026-08-27] a
   first fix-up mistakenly re-added a priority-bars column here; corrected on further live feedback —
   the widget that was actually meant belongs in .exec-dash-kpis-top as a chip strip (see
   .exec-dash-priority-tile below), not here, so this stays single-column. */
.exec-dash-body{display:grid; grid-template-columns:1fr; gap:16px; flex:1}
.exec-dash-col{background:var(--panel); border:1px solid var(--border); border-radius:var(--radius); box-shadow:var(--elev-1); padding:9px 18px; display:flex; flex-direction:column}
.exec-dash-bars{display:flex; flex-direction:column; gap:12px; justify-content:center; flex:1}
.exec-dash-bar-row{display:flex; align-items:center; gap:10px; font-size:.82rem}
/* revision v16 — department names were hard-truncated with an ellipsis ("إدارة تقنية المعل...")
   in a column meant to be read at a glance on a briefing screen; wrapping to two lines instead of
   losing the tail of the name reads better than a mystery abbreviation. */
.exec-dash-bar-row .lbl{flex:0 0 128px; font-weight:700; white-space:normal; line-height:1.25}
.exec-dash-bar-row .track{flex:1; height:14px; border-radius:999px; background:var(--panel2); overflow:hidden}
.exec-dash-bar-row .track .fill{display:block; height:100%; border-radius:999px}
.exec-dash-bar-row .n{flex:0 0 28px; text-align:end; font-weight:800; font-variant-numeric:tabular-nums}
.exec-dash-esc-note{color:var(--muted); font-size:.82rem; font-weight:700; margin-top:6px}

/* Revision v15 §1 — restructure: freshness indicator, full clickability, the merged escalation
   tile, and the P1 spotlight strip. Reuses the same .kpi/.exec-dash-* tokens above — no new colors. */
.exec-dash-updated{font-size:.8rem; color:var(--muted); font-weight:700; font-variant-numeric:tabular-nums}
.exec-dash-clickable{cursor:pointer; transition:filter .12s, box-shadow .12s}
.exec-dash-clickable:hover{filter:brightness(.97)}
/* Revision v17 §7.1 — a third tile added to the top KPI row for the priority-distribution chip
   strip. QA round 2 §1 replaced that tile with the on-time/health tile instead of adding a 4th
   column; [2026-08-27] corrected after live feedback — the chip strip is a distinct widget from the
   health tile, not a replacement for it, so it's back as a 4th column here (health tile stays last/
   left-most, unaffected). */
.exec-dash-kpis-top{display:grid; grid-template-columns:.7fr 1.3fr 1.05fr 1.05fr; gap:12px; flex:0 0 auto}
.exec-dash-kpis-top .kpi{padding:10px; text-align:center; display:flex; flex-direction:column; justify-content:center}
.exec-dash-kpis-top .kpi .n{font-size:1.9rem; font-weight:900; font-variant-numeric:tabular-nums}
.exec-dash-kpis-top .kpi .l{color:var(--muted); font-size:.74rem; font-weight:700; margin-top:2px}
.exec-dash-escalation-tile{background:var(--panel); border:1px solid var(--border); border-radius:var(--radius); box-shadow:var(--shadow); padding:10px 18px; display:flex; flex-direction:column; justify-content:center; gap:6px}
.exec-dash-esc-head{display:flex; align-items:baseline; gap:10px; border-radius:8px}
.exec-dash-esc-head .n{font-size:1.6rem; font-weight:900; font-variant-numeric:tabular-nums}
.exec-dash-esc-head .l{color:var(--muted); font-size:.8rem; font-weight:700}
.exec-dash-esc-bar{display:flex; height:12px; border-radius:999px; overflow:hidden; background:var(--panel2)}
.exec-dash-esc-seg{display:block; height:100%; min-width:2px}
.exec-dash-esc-legend{display:flex; gap:16px; flex-wrap:wrap; font-size:.78rem; font-weight:700; color:var(--muted)}
.exec-dash-esc-legend span{display:flex; align-items:center; gap:6px}
.exec-dash-esc-legend i{width:9px; height:9px; border-radius:50%; display:inline-block}

/* Revision v17 §7.1 — priority-distribution tile: four separate clickable numbers, distinct from
   the bar-chart breakdown (removed, §3 above). [2026-08-27] restored a 2nd time after live feedback
   that the first restore brought back the wrong twin (a bar chart) instead of this chip strip. */
.exec-dash-priority-tile{background:var(--panel); border:1px solid var(--border); border-radius:var(--radius); box-shadow:var(--shadow); padding:12px 16px; display:flex; flex-direction:column; justify-content:center; gap:8px}
.exec-dash-priority-tile-title{font-size:.78rem; font-weight:800; color:var(--muted)}
.exec-dash-p-chips{display:flex; gap:8px}
.exec-dash-p-chip{flex:1; display:flex; flex-direction:column; align-items:center; gap:2px; padding:6px 4px; border-radius:10px; background:color-mix(in srgb, var(--chip-c) 12%, transparent); border:1px solid color-mix(in srgb, var(--chip-c) 30%, transparent)}
.exec-dash-p-chip .v{font-size:1.15rem; font-weight:900; color:var(--chip-c); font-variant-numeric:tabular-nums}
.exec-dash-p-chip .k{font-size:.7rem; font-weight:800; color:var(--text)}

/* QA round 2 §1/§2 — the on-time/health tile, now living in the top KPI row's last (visually
   left-most, RTL) slot instead of a full-height .exec-dash-body column. §2's clipping bug: the old
   design stacked a 150px ring above its label/note, which needed ~220px+ of vertical room — fine in
   a full body column, but cut off in this row's much shorter height. Laid out as a row (small ring
   beside its text) instead of a column fixes that without dropping any of the three original pieces
   of data (percentage, "ضمن الموعد" label, escalated/total note). */
.exec-dash-health-tile{background:var(--panel); border:1px solid var(--border); border-radius:var(--radius); box-shadow:var(--shadow); padding:10px 16px; display:flex; align-items:center; gap:14px}
.exec-dash-health-ring{position:relative; width:64px; height:64px; flex:0 0 auto}
.exec-dash-health-ring svg{width:100%; height:100%; transform:rotate(-90deg)}
.exec-dash-health-ring circle{fill:none; stroke-width:11}
.exec-dash-health-ring .track{stroke:var(--panel2)}
.exec-dash-health-ring .val{stroke:var(--brand); stroke-linecap:round; transition:stroke-dashoffset 1s ease}
.exec-dash-health-ring .center{position:absolute; inset:0; display:flex; align-items:center; justify-content:center}
.exec-dash-health-ring .pct{font-size:.9rem; font-weight:900}
.exec-dash-health-text{min-width:0; display:flex; flex-direction:column; gap:1px}
.exec-dash-health-title{font-size:.78rem; font-weight:800; color:var(--muted)}
.exec-dash-health-sub{font-size:.82rem; font-weight:800}
.exec-dash-health-text .exec-dash-esc-note{margin-top:2px}

/* QA round 1 §7.2 — the P1 spotlight card is distribution/counts only now: the old top-5
   individual-task listing (.exec-dash-p1-main/-rows/-text/-dept/-due/-empty) is gone; the header
   sits above the two aggregate breakdown columns instead of beside a task list. */
.exec-dash-p1{flex:0 0 auto; background:var(--panel); border:1px solid var(--border); border-radius:var(--radius); box-shadow:var(--shadow); padding:8px 18px; border-inline-start:4px solid var(--p1); display:flex; flex-direction:column; gap:6px}
.exec-dash-p1-head{display:flex; align-items:center; gap:10px; font-weight:800; font-size:.88rem}
.exec-dash-p1-count{margin-inline-start:auto; color:var(--p1); font-weight:900}
.exec-dash-p1-breakdown-cols{display:flex; gap:14px}
.exec-dash-p1-breakdown-col{flex:1; min-width:0; display:flex; flex-direction:column}
.exec-dash-p1-breakdown-title{font-size:.72rem; font-weight:800; color:var(--muted); margin-bottom:6px}
.exec-dash-p1-chip-list{display:flex; flex-direction:column; gap:3px; max-height:96px; overflow-y:auto}
.exec-dash-p1-chip{display:flex; align-items:center; justify-content:space-between; gap:8px; font-size:.76rem; padding:3px 7px; border-radius:7px}
.exec-dash-p1-chip:hover{background:var(--panel2)}
.exec-dash-p1-chip .k{min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.exec-dash-p1-chip .v{flex:0 0 auto; font-weight:900; font-variant-numeric:tabular-nums; color:var(--p1)}

/* QA round 1 §7.3 — the on-time-compliance percentage appended to each "المهام لكل جهة مالكة" row. */
.exec-dash-dept-ontime{flex:0 0 40px; text-align:end; font-weight:800; font-size:.74rem; font-variant-numeric:tabular-nums}

/* Revision v17 §7.6 — a dedicated inner scroll for the "لكل جهة مالكة" bars column only, so
   zero-filling every active department (always shown now, even at 0) can't push the whole page into
   scrolling. [2026-08-27] fix — two compounding bugs made rows render sliced in half: (1) a hardcoded
   max-height (112px) cut whichever row landed on that exact pixel boundary — rows aren't uniform
   height (a department name can wrap to one or two lines), so no fixed constant is safe; now set
   inline by ExecutiveDashboardView.sizeDepartmentList(), computed from the real rendered row heights
   so only whole rows ever show. (2) .exec-dash-bars' justify-content:center (correct for a short list
   that doesn't fill its box) fought that cap once content overflowed it: centering pushes rows both
   above *and* below the visible area, clipping symmetrically at both edges instead of cleanly at the
   bottom — overridden back to flex-start here so overflow only ever clips the untruncated remainder. */
.exec-dash-bars-scroll{overflow-y:auto; justify-content:flex-start}

@media(max-width:1200px){ .exec-dash-kpis-top{grid-template-columns:1fr} .exec-dash-p1-breakdown-cols{flex-direction:column} }

/* Buttons */
.btn{
  display:inline-flex; align-items:center; gap:8px; padding:9px 17px; border-radius:10px; border:1px solid var(--border-strong);
  background:var(--panel); color:var(--text); cursor:pointer; font-weight:700; font-size:.88rem;
  transition:filter .15s, transform .08s, box-shadow .15s, background .15s;
}
.btn:hover{filter:brightness(.97); box-shadow:var(--shadow-sm)}
.btn:active{transform:translateY(1px)}
.btn:disabled{opacity:.5; cursor:not-allowed; box-shadow:none; transform:none}
.btn.primary{background:linear-gradient(155deg, var(--brand), var(--brand-dark)); border-color:var(--brand-dark); color:#fff; box-shadow:0 3px 10px color-mix(in srgb, var(--brand) 35%, transparent)}
.btn.primary:hover{box-shadow:0 5px 16px color-mix(in srgb, var(--brand) 45%, transparent)}
.btn.gold{background:linear-gradient(155deg, var(--accent), var(--brand-gold-dark)); border-color:var(--brand-gold-dark); color:#2b2416; box-shadow:0 3px 10px color-mix(in srgb, var(--accent) 35%, transparent)}
.btn.danger{background:var(--danger); border-color:var(--danger); color:#fff}
/* Outlined rather than solid-filled — --warn and --brand-gold share a hue, so a solid .btn.warn
   would look identical to .btn.gold; this reads as "secondary, flagged" instead of a primary CTA,
   which suits a manual override of an otherwise-automatic process (revision v3 §8). */
.btn.warn{background:color-mix(in srgb, var(--warn) 10%, transparent); border-color:var(--warn); color:var(--warn)}
.btn.warn:hover{background:color-mix(in srgb, var(--warn) 18%, transparent)}
.btn.ghost{background:transparent; box-shadow:none}
.btn.ghost:hover{background:var(--panel2)}
.btn.sm{padding:6px 12px; font-size:.8rem; border-radius:9px}
.iconbtn{
  width:39px; height:39px; border-radius:11px; border:1px solid var(--border-strong); background:var(--panel);
  display:flex; align-items:center; justify-content:center; cursor:pointer; position:relative; color:var(--text);
  transition:background .15s, box-shadow .15s;
}
.iconbtn:hover{background:var(--panel2); box-shadow:var(--shadow-sm)}
.iconbtn .dot{position:absolute; top:6px; left:6px; width:9px; height:9px; border-radius:50%; background:var(--danger); border:2px solid var(--panel)}

/* Cards / panels */
.card{background:var(--panel); border:1px solid var(--border); border-radius:var(--radius); box-shadow:var(--shadow)}
.card .pad{padding:20px}
.kpis{display:grid; grid-template-columns:repeat(auto-fit,minmax(190px,1fr)); gap:14px; margin-bottom:20px}
.kpi{
  background:var(--panel); border:1px solid var(--border); border-radius:var(--radius); padding:18px; box-shadow:var(--shadow);
  position:relative; overflow:hidden;
}
.kpi .icn{
  position:absolute; top:14px; inset-inline-end:14px; width:38px; height:38px; border-radius:11px;
  display:flex; align-items:center; justify-content:center; background:var(--panel2); color:var(--brand);
  box-shadow:0 3px 8px rgba(0,0,0,.08);
}
.kpi .n{font-size:2rem; font-weight:900; letter-spacing:-.02em; font-variant-numeric:tabular-nums}
.kpi .l{color:var(--muted); font-size:.82rem; font-weight:700; margin-top:3px}
.kpi::after{
  content:""; position:absolute; inset-inline-end:-30px; bottom:-30px; width:100px; height:100px; border-radius:50%;
  background:radial-gradient(circle, color-mix(in srgb, var(--brand) 10%, transparent), transparent 70%); pointer-events:none;
}
.kpi.accent{border-inline-start:4px solid var(--brand); background:linear-gradient(165deg, color-mix(in srgb, var(--brand) 7%, var(--panel)), var(--panel) 55%)}
.kpi.accent .icn{color:#fff; background:linear-gradient(150deg, var(--brand), var(--brand-dark))}
.kpi.warn{border-inline-start:4px solid var(--warn); background:linear-gradient(165deg, color-mix(in srgb, var(--warn) 10%, var(--panel)), var(--panel) 55%)}
.kpi.warn .icn{color:#2b2416; background:linear-gradient(150deg, var(--accent), var(--brand-gold-dark))}
.kpi.warn::after{background:radial-gradient(circle, color-mix(in srgb, var(--warn) 14%, transparent), transparent 70%)}
.kpi.danger{border-inline-start:4px solid var(--danger); background:linear-gradient(165deg, color-mix(in srgb, var(--danger) 8%, var(--panel)), var(--panel) 55%)}
.kpi.danger .icn{color:#fff; background:linear-gradient(150deg, var(--danger), #7d2c1e)}
.kpi.danger::after{background:radial-gradient(circle, color-mix(in srgb, var(--danger) 14%, transparent), transparent 70%)}
.kpi-link{display:block; text-decoration:none; color:inherit; cursor:pointer; transition:transform .18s, box-shadow .18s; animation:kpi-in .35s ease-out backwards}
.kpi-link:hover{transform:translateY(-3px); box-shadow:var(--shadow-lg)}
.kpi-link:focus-visible{outline:2px solid var(--brand); outline-offset:2px}
.kpi-link:nth-child(1){animation-delay:0ms} .kpi-link:nth-child(2){animation-delay:60ms}
.kpi-link:nth-child(3){animation-delay:120ms} .kpi-link:nth-child(4){animation-delay:180ms}
.kpi-link:nth-child(5){animation-delay:240ms}
@keyframes kpi-in{from{opacity:0; transform:translateY(10px)} to{opacity:1; transform:translateY(0)}}
@media (prefers-reduced-motion: reduce){.kpi-link{transition:none; animation:none}.kpi-link:hover{transform:none}}
.grid2{display:grid; grid-template-columns:1fr 1fr; gap:16px; min-width:0}
.grid3{display:grid; grid-template-columns:repeat(3,1fr); gap:16px; min-width:0}
@media(max-width:980px){.grid2,.grid3{grid-template-columns:1fr}}

.section-title{font-size:1.02rem; font-weight:800; margin:0 0 14px; display:flex; align-items:center; gap:8px}
.charts-row{display:grid; grid-template-columns:1fr 1fr; gap:16px; margin-bottom:18px; min-width:0}
.chart-box{
  background:var(--panel); border:1px solid var(--border); border-radius:var(--radius); padding:18px; box-shadow:var(--shadow);
  height:308px; min-width:0; animation:kpi-in .4s ease-out backwards;
}
.chart-box:nth-child(1){animation-delay:120ms} .chart-box:nth-child(2){animation-delay:180ms}
@media(max-width:980px){.charts-row{grid-template-columns:1fr}}
@media (prefers-reduced-motion: reduce){.chart-box{animation:none}}

/* Dashboard overview (revision v7 §4) — one primary chart (the pipeline story) beside a compact
   secondary column, replacing four equal-weight chart cards stacked two-by-two. */
.dash-overview-title{margin-top:4px}
.dash-overview{display:grid; grid-template-columns:1.3fr 1fr; gap:16px; margin-bottom:18px; min-width:0}
.chart-box.primary{height:360px}
.dash-side{display:flex; flex-direction:column; gap:16px; min-width:0}
.chart-box.compact{height:auto; flex:1; min-height:220px}
@media(max-width:980px){.dash-overview{grid-template-columns:1fr}.chart-box.primary{height:320px}}
.priority-mix{background:var(--panel); border:1px solid var(--border); border-radius:var(--radius); padding:18px; box-shadow:var(--shadow); animation:kpi-in .4s ease-out backwards}
.priority-mix .bar{display:flex; height:14px; border-radius:999px; overflow:hidden; background:var(--panel2); margin-bottom:16px}
.priority-mix .bar span{height:100%; transition:width .5s ease-out}
.priority-mix .legend{display:flex; flex-direction:column; gap:8px}
.priority-mix .legend .item{display:flex; align-items:center; gap:8px; font-size:.84rem; font-weight:700; color:inherit; text-decoration:none; padding:4px 6px; border-radius:8px; transition:background .12s}
.priority-mix .legend .item:hover{background:var(--panel2)}
.priority-mix .legend .dot{width:9px; height:9px; border-radius:50%; flex:0 0 auto}

/* Dashboard hero — the "on-track" health gauge (revision v2 §10 "more visuality") */
.dash-hero{
  position:relative; overflow:hidden; border-radius:var(--radius-lg); margin-bottom:22px; padding:26px 30px;
  display:flex; align-items:center; justify-content:space-between; gap:24px; flex-wrap:wrap;
  color:#fff; background:linear-gradient(120deg, var(--brand-dark) 0%, var(--brand-teal) 60%, var(--brand-dark-teal) 100%);
  box-shadow:var(--shadow-lg); animation:kpi-in .4s ease-out backwards;
}
.dash-hero::before{
  content:""; position:absolute; inset-inline-end:-60px; top:-80px; width:280px; height:280px; border-radius:50%;
  background:radial-gradient(circle, color-mix(in srgb, var(--brand-gold) 24%, transparent), transparent 70%);
}
.dash-hero::after{
  content:""; position:absolute; inset-inline-start:-40px; bottom:-90px; width:220px; height:220px; border-radius:50%;
  background:radial-gradient(circle, rgba(255,255,255,.08), transparent 70%);
}
/* Ministry-headquarters watermark (MOI Design Files/Pictures) — the ghost/monochrome variant,
   multiply-blended so its flattened white background disappears into the hero gradient and only
   the building's silhouette reads, echoing the same restrained watermark treatment the ministry's
   own brand guide cover uses. Purely decorative, so it's excluded from the ring-gauge's live
   region and hidden from screen readers. */
.dash-hero .wm{
  position:absolute; inset-inline-end:6%; bottom:-18%; width:260px; height:auto; opacity:.16;
  mix-blend-mode:multiply; pointer-events:none; z-index:0;
}
[data-theme="dark"] .dash-hero .wm{opacity:.1; mix-blend-mode:soft-light}
.dash-hero .txt{position:relative; z-index:1; min-width:220px}
.dash-hero .txt h2{font-size:1.28rem; font-weight:800; margin:0 0 6px}
.dash-hero .txt p{margin:0; opacity:.85; font-size:.86rem; max-width:440px; line-height:1.6}
.dash-hero .txt .updated{margin-top:12px; font-size:.74rem; opacity:.65; display:flex; align-items:center; gap:6px}
.ring-gauge{position:relative; z-index:1; flex:0 0 auto; width:132px; height:132px}
.ring-gauge svg{width:100%; height:100%; transform:rotate(-90deg)}
.ring-gauge circle{fill:none; stroke-width:11}
.ring-gauge .track{stroke:rgba(255,255,255,.18)}
.ring-gauge .val{stroke:var(--brand-gold); stroke-linecap:round; transition:stroke-dashoffset 1s cubic-bezier(.2,.8,.3,1)}
.ring-gauge .center{position:absolute; inset:0; display:flex; flex-direction:column; align-items:center; justify-content:center}
.ring-gauge .center .pct{font-size:1.55rem; font-weight:900; font-variant-numeric:tabular-nums}
.ring-gauge .center .lbl{font-size:.62rem; opacity:.8; font-weight:700; margin-top:2px}
@media (prefers-reduced-motion: reduce){.dash-hero{animation:none} .ring-gauge .val{transition:none}}
@media(max-width:640px){.dash-hero{padding:20px}}

/* Tables (revision v7 §2) — one deliberately-tuned view replaces the old comfortable/compact
   toggle: rows are tall enough to read wrapped task text comfortably, tight enough that a daily
   operator sees a full page of tasks without scrolling on a normal laptop, and zebra-striped so a
   glance can track a row across eight columns without needing extra padding to do that job. */
table{width:100%; border-collapse:collapse; font-size:.88rem}
th,td{padding:13px 16px; text-align:right; border-bottom:1px solid var(--border); vertical-align:middle}
thead th{background:var(--panel2); color:var(--muted); font-weight:800; font-size:.75rem; text-transform:uppercase; letter-spacing:.03em; position:sticky; top:0; border-bottom:1px solid var(--border-strong)}
thead tr:first-child th:first-child{border-start-end-radius:var(--radius)}
thead tr:first-child th:last-child{border-start-start-radius:var(--radius)}
tbody tr{cursor:pointer; border-inline-start:3px solid transparent; transition:background .12s, border-color .12s}
tbody tr:nth-child(even){background:color-mix(in srgb, var(--panel2) 45%, transparent)}
tbody tr:hover{background:var(--panel2); border-inline-start-color:var(--brand)}
.table-wrap{overflow-x:auto; border-radius:var(--radius)}

/* revision v3 §4: past VIRTUALIZE_THRESHOLD rows (js/views/taskList.js), the browser skips
   layout/paint for offscreen rows entirely — a standards-based alternative to hand-rolled row
   recycling that keeps scrolling smooth with hundreds of loaded rows. */
.tbl-virtual tbody tr{content-visibility:auto; contain-intrinsic-size:auto 44px}

/* Column-header filter/sort (revision v2 §6) */
th.hf-th{white-space:nowrap}
.hf-label{cursor:pointer; user-select:none; background:none; border:none; padding:0; margin:0; color:inherit; font:inherit; text-transform:inherit; letter-spacing:inherit}
.hf-label:hover{color:var(--brand)}
.hf-label:focus-visible{outline:2px solid var(--brand); outline-offset:2px; border-radius:4px}
.hf-filter-btn:focus-visible{outline:2px solid var(--brand); outline-offset:2px}
.hf-filter-btn{
  display:inline-flex; align-items:center; gap:3px; margin-inline-start:6px; padding:2px 5px; border-radius:6px;
  border:1px solid transparent; background:transparent; color:var(--soft); cursor:pointer; vertical-align:middle;
}
.hf-filter-btn:hover{color:var(--text); background:var(--border)}
.hf-filter-btn.active{color:var(--brand); border-color:var(--brand)}
.hf-count{font-size:.68rem; font-weight:900; background:var(--brand); color:#fff; border-radius:999px; padding:0 5px}

/* Badges / tags */
.tag{display:inline-flex; align-items:center; gap:5px; padding:4px 11px; border-radius:999px; font-size:.74rem; font-weight:800; white-space:nowrap}
.tag.p1{background:color-mix(in srgb, var(--p1) 16%, transparent); color:var(--p1)}
.tag.p2{background:color-mix(in srgb, var(--p2) 24%, transparent); color:#7a6229}
/* redesign §1.5: text color was --p3 directly (#4A8E55) against its own 16%-tinted background —
   measured (WCAG relative-luminance formula, not eyeballed) at 3.11:1, below the 4.5:1 AA floor for
   this tag's ~12px text. --brand-green-dark clears 5.56:1 against the same background without
   touching the --p3 token itself (it's one of the ministry's official palette colors). */
.tag.p3{background:color-mix(in srgb, var(--p3) 16%, transparent); color:var(--brand-green-dark)}
.tag.p4{background:color-mix(in srgb, var(--p4) 16%, transparent); color:var(--p4)}
.tag.status{background:var(--panel2); color:var(--text); border:1px solid var(--border)}
.tag.status.Sent{color:var(--info)}
.tag.status.UnderReview{color:var(--brand-teal)}
.tag.status.ClarificationRequested{color:#8a6d1f}
.tag.status.Assigned{color:var(--brand)}
.tag.status.InProgress{color:var(--brand-dark)}
.tag.status.Done{color:var(--ok)}
.tag.status.Archived{color:var(--muted)}
.tag.overdue{background:color-mix(in srgb, var(--danger) 16%, transparent); color:var(--danger)}
/* Revision v17 §5 — clarification-request status tags. */
.tag.pending{background:color-mix(in srgb, var(--warn) 20%, transparent); color:#8a6d1f}
.tag.done{background:color-mix(in srgb, var(--ok) 18%, transparent); color:var(--ok)}
[data-theme="dark"] .tag.pending{color:var(--warn)}
.tag.escalation-1{background:color-mix(in srgb, var(--danger) 16%, transparent); color:var(--danger)}
.tag.escalation-2{background:color-mix(in srgb, var(--escalation2) 22%, transparent); color:var(--escalation2); font-weight:900}
/* revision v8/v9 §5 — clickable escalation badges (a reason is on record) get a visible affordance. */
.tag.escalation-1.clickable, .tag.escalation-2.clickable{cursor:pointer; text-decoration:underline dotted; text-underline-offset:3px}
.tag.escalation-1.clickable:hover, .tag.escalation-2.clickable:hover{filter:brightness(.92)}
[data-theme="dark"] .tag.status.ClarificationRequested{color:var(--warn)}

/* Signature element: the task-code badge (design system) */
.code-badge{
  display:inline-flex; align-items:center; font-family:"Cairo",monospace; font-variant-numeric:tabular-nums;
  font-weight:800; font-size:.86rem; letter-spacing:.02em; padding:4px 11px 4px 9px; border-radius:7px;
  background:var(--panel2); border:1px solid var(--border); border-inline-start:3px solid var(--soft);
  color:var(--text); direction:ltr; unicode-bidi:isolate;
}
.code-badge.p1{border-inline-start-color:var(--p1); color:var(--p1)}
.code-badge.p2{border-inline-start-color:var(--p2); color:#7a6229}
/* redesign §1.5: same measured contrast gap as .tag.p3 above (3.72:1 light / 3.93:1 dark, both
   below AA) — border keeps the priority-accent hue, text switches to the darker green. In dark
   mode --brand-green-dark itself drops to 2.19:1 against the dark panel (too dark-on-dark), so
   dark mode uses --brand-turquoise instead (measured 5.55:1 / 4.54:1 — see .tag.p3 override below). */
.code-badge.p3{border-inline-start-color:var(--p3); color:var(--brand-green-dark)}
[data-theme="dark"] .code-badge.p3{color:var(--brand-turquoise)}
[data-theme="dark"] .tag.p3{color:var(--brand-turquoise)}
.code-badge.p4{border-inline-start-color:var(--p4); color:var(--p4)}
[data-theme="dark"] .code-badge.p2{color:var(--p2)}
.progress{height:8px; border-radius:999px; background:var(--panel2); overflow:hidden; border:1px solid var(--border)}
.progress > i{display:block; height:100%; background:linear-gradient(90deg, var(--brand-dark), var(--brand)); transition:width .5s ease}
@media (prefers-reduced-motion: reduce){.progress > i{transition:none}}

/* Forms */
.field{margin-bottom:16px}
.field label{display:block; font-weight:700; font-size:.85rem; margin-bottom:6px}
.field .hint{color:var(--muted); font-size:.76rem; margin-top:5px}
input[type=text],input[type=search],input[type=date],input[type=number],input[type=email],textarea,select{
  width:100%; padding:10px 13px; border-radius:10px; border:1px solid var(--border-strong); background:var(--panel); color:var(--text);
  transition:border-color .15s, box-shadow .15s;
}
input:focus,select:focus,textarea:focus{border-color:var(--brand); box-shadow:0 0 0 3px color-mix(in srgb, var(--brand) 18%, transparent)}
textarea{min-height:100px; resize:vertical}
/* Revision v14 §2 — inline per-field validation for the single-page create-task form: a field
   marked .invalid (after blur, or on a blocked submit attempt) gets a visible red border and a
   small error line, instead of only a single bottom-of-form/toast error. */
.field.invalid input,.field.invalid select,.field.invalid textarea{border-color:var(--danger); box-shadow:0 0 0 3px color-mix(in srgb, var(--danger) 15%, transparent)}
.field .field-error{color:var(--danger); font-size:.76rem; margin-top:5px; font-weight:700}

/* Revision v17 §4 — employee picker: the active-task count renders as a separate colored badge,
   not appended to the name text (a plain <select><option> cannot hold styled child markup). */
.emp-picker{position:relative}
.emp-picker-btn{
  width:100%; display:flex; align-items:center; justify-content:space-between; gap:8px;
  padding:10px 13px; border-radius:10px; border:1px solid var(--border-strong); background:var(--panel);
  color:var(--text); cursor:pointer; font:inherit; text-align:start;
}
.emp-picker-btn:focus{border-color:var(--brand); box-shadow:0 0 0 3px color-mix(in srgb, var(--brand) 18%, transparent)}
.emp-picker-caret{color:var(--soft); font-size:.75rem}
.emp-picker-list{
  position:absolute; inset-inline:0; top:calc(100% + 4px); z-index:40; max-height:240px; overflow-y:auto;
  background:var(--panel); border:1px solid var(--border-strong); border-radius:10px; box-shadow:var(--shadow-lg,0 8px 24px rgba(0,0,0,.15));
}
.emp-picker-row{
  display:flex; align-items:center; justify-content:space-between; gap:10px; padding:9px 13px; cursor:pointer;
}
.emp-picker-row:hover{background:var(--panel2)}
.emp-picker-name{font-weight:600; font-size:.87rem}
.emp-count-badge{
  flex:0 0 auto; min-width:22px; height:22px; padding:0 7px; border-radius:999px; display:inline-flex;
  align-items:center; justify-content:center; font-size:.74rem; font-weight:800;
  background:var(--accent); color:var(--brand-charcoal);
}
.row{display:flex; gap:12px; flex-wrap:wrap}
.row > *{flex:1; min-width:180px}
.filters{display:flex; gap:10px; flex-wrap:wrap; margin-bottom:16px; align-items:center}
.filters select, .filters input{width:auto; min-width:140px}

/* Revision v15 §2.1 — create-task screen, no page scroll *when the viewport has room* (tightens
   revision v14 §2's "plain vertical scroll accepted" allowance). Revision v16 fix: the original hard
   `height:100vh; overflow:hidden` clipped the form silently the instant the real browser viewport was
   shorter than 1920x1080 minus chrome — live-measured on this dev machine, the مرفق (attachment)
   field was rendered but ~75% clipped off with no scrollbar and no way to reach it (exactly the "half
   showing, half not" symptom reported). Same fix as the executive dashboard: `height`/`overflow:hidden`
   → `min-height`/normal flow, so a viewport with room renders pixel-identical (no visible scroll), and
   a shorter one scrolls normally instead of hiding fields. A dense multi-column grid (required rows
   first, optional rows last, per §2.1) keeps every field on-screen without needing the aggressive
   font/spacing shrinkage a naive single-column form would require. */
body.create-task-active{min-height:100vh}
body.create-task-active #shell{min-height:100vh}
body.create-task-active #content{padding:16px; display:flex; flex-direction:column}
.ct-card{min-height:calc(100vh - var(--topbar-h) - 32px); display:flex; flex-direction:column}
.ct-pad{display:flex; flex-direction:column; padding:18px 22px}
.ct-title{margin-bottom:12px; font-size:1.05rem; flex:0 0 auto}
.ct-grid{display:grid; grid-template-columns:repeat(3,1fr); gap:10px 18px; align-content:start}
.ct-grid .field{margin-bottom:0}
.ct-span-2{grid-column:span 2}
.ct-span-full{grid-column:1/-1}
.ct-text-area{min-height:0; height:110px; resize:none}
.ct-notes-area{min-height:0; height:64px; resize:none}
.ct-tx-fields{display:grid; grid-template-columns:1fr 1fr; gap:10px 18px}
.ct-attach-field .file-drop{padding:10px 16px}
.ct-attach-field .fd-icon{margin-bottom:2px}
.ct-actions{display:flex; margin-top:14px; flex:0 0 auto}
@media(max-width:980px){
  body.create-task-active{height:auto; overflow:visible}
  body.create-task-active #content{overflow:visible}
  .ct-card{height:auto}
  .ct-grid{grid-template-columns:1fr}
  .ct-span-2{grid-column:1}
  .ct-tx-fields{grid-template-columns:1fr}
}

/* Modal */
/* will-change/translateZ(0): forces .modal-bg onto its own compositor layer. Without it, Chromium
   sometimes paints the sticky #sidebar's layer above this fixed backdrop despite the correct
   z-index (100 vs #sidebar's 30) and correct hit-testing — the backdrop dims #content but the
   sidebar renders fully bright/undimmed on top of it. Reproduced live: elementFromPoint() over the
   sidebar region correctly resolved to #active-modal (z-index math was never the bug), yet the
   pixels showed the sidebar undimmed — a layer paint-order bug, fixed by explicit layer promotion. */
.modal-bg{position:fixed; inset:0; background:rgba(13,20,15,.6); backdrop-filter:blur(2px); display:none; align-items:center; justify-content:center; z-index:100; padding:20px; will-change:opacity; transform:translateZ(0)}
.modal-bg.show{display:flex; animation:modal-bg-in .15s ease-out}
@keyframes modal-bg-in{from{opacity:0} to{opacity:1}}
.modal{background:var(--panel); border-radius:18px; width:100%; max-width:640px; max-height:88vh; overflow:auto; box-shadow:var(--shadow-lg); position:relative; animation:modal-in .18s cubic-bezier(.2,.8,.3,1)}
@keyframes modal-in{from{opacity:0; transform:translateY(10px) scale(.98)} to{opacity:1; transform:translateY(0) scale(1)}}
@media (prefers-reduced-motion: reduce){.modal-bg.show, .modal{animation:none}}
.modal .head{display:flex; align-items:center; padding:18px 22px; border-bottom:1px solid var(--border)}
.modal .head h3{margin:0; font-size:1.02rem; font-weight:800}
.modal .head .spacer{flex:1}
.modal .body{padding:22px}
.modal .foot{padding:15px 22px; border-top:1px solid var(--border); display:flex; gap:10px; justify-content:flex-start}
.modal-close{
  position:absolute; top:14px; inset-inline-end:16px; width:30px; height:30px; border-radius:9px; border:none;
  background:transparent; color:var(--muted); cursor:pointer; display:flex; align-items:center; justify-content:center;
  font-size:1.1rem; line-height:1; transition:background .15s, color .15s;
}
.modal-close:hover{background:var(--panel2); color:var(--text)}

/* Empty/loading */
.empty{padding:44px 20px; text-align:center; color:var(--muted)}
.empty .empty-icon{width:44px; height:44px; margin:0 auto 12px; color:var(--soft); opacity:.7}
.empty .empty-text{font-size:.9rem; font-weight:600}
.loading{padding:40px; text-align:center; color:var(--muted)}

/* Skeleton loading (design system) — used where a blank-then-pop render would otherwise flash */
.skeleton-row{display:flex; gap:14px; align-items:center; padding:14px 18px; border-bottom:1px solid var(--border)}
.skeleton-row:last-child{border-bottom:none}
.skel{border-radius:6px; background:linear-gradient(90deg, var(--panel2) 25%, var(--border) 50%, var(--panel2) 75%); background-size:200% 100%; animation:skel-shimmer 1.4s ease-in-out infinite}
.skel.h-14{height:14px} .skel.w-60{width:60px} .skel.w-90{width:90px} .skel.w-140{width:140px} .skel.flex1{flex:1}
@keyframes skel-shimmer{0%{background-position:200% 0} 100%{background-position:-200% 0}}
@media (prefers-reduced-motion: reduce){.skel{animation:none; background:var(--panel2)}}
.toast-host{position:fixed; bottom:22px; left:22px; z-index:200; display:flex; flex-direction:column; gap:9px; max-width:360px}
.toast{background:var(--brand-charcoal); color:#fff; padding:13px 17px; border-radius:11px; box-shadow:var(--shadow-lg); font-size:.85rem; font-weight:700; animation:toast-in .22s cubic-bezier(.2,.8,.3,1)}
.toast.err{background:var(--danger)}
.toast.ok{background:linear-gradient(155deg, var(--brand), var(--brand-dark))}
.toast.info{background:linear-gradient(155deg, var(--brand-teal), var(--brand-dark-teal))}
.toast.clickable{cursor:pointer}
.toast.clickable:hover{filter:brightness(1.12)}
@keyframes toast-in{from{opacity:0; transform:translateY(10px) scale(.97)} to{opacity:1; transform:translateY(0) scale(1)}}
@media (prefers-reduced-motion: reduce){.toast{animation:none}}

/* Timeline */
.timeline{list-style:none; margin:0; padding:0; border-inline-start:2px solid var(--border); padding-inline-start:20px}
.timeline li{position:relative; padding-bottom:18px}
.timeline li::before{content:""; position:absolute; inset-inline-start:-25px; top:3px; width:11px; height:11px; border-radius:50%; background:var(--brand); box-shadow:0 0 0 3px color-mix(in srgb, var(--brand) 20%, transparent)}
.timeline .t{font-weight:800; font-size:.85rem}
.timeline .m{color:var(--muted); font-size:.78rem; margin-top:2px}

/* revision v3 §6: escalation/resolution entries stay inside the single chronological timeline but
   read as visually distinct — colored dot + inline icon, reusing the existing badge tokens. */
.timeline li.tl-escalation-1::before{background:var(--warn); box-shadow:0 0 0 3px color-mix(in srgb, var(--warn) 20%, transparent)}
.timeline li.tl-escalation-2::before{background:var(--escalation2); box-shadow:0 0 0 3px color-mix(in srgb, var(--escalation2) 20%, transparent)}
.timeline li.tl-resolved::before{background:var(--ok); box-shadow:0 0 0 3px color-mix(in srgb, var(--ok) 20%, transparent)}
.timeline .tl-icon{display:inline-flex; vertical-align:-3px; margin-inline-end:4px}
.timeline li.tl-escalation-1 .tl-icon{color:var(--warn)}
.timeline li.tl-escalation-2 .tl-icon{color:var(--escalation2)}
.timeline li.tl-resolved .tl-icon{color:var(--ok)}

/* Notification panel */
.notif-panel-inner .head{display:flex; align-items:center; padding:14px 18px; border-bottom:1px solid var(--border); animation:modal-bg-in .15s ease-out}
.notif-panel-inner .list{max-height:420px; overflow:auto; animation:modal-bg-in .18s ease-out}
@media (prefers-reduced-motion: reduce){.notif-panel-inner .head, .notif-panel-inner .list{animation:none}}
.notif-item{padding:13px 18px; border-bottom:1px solid var(--border); cursor:pointer; transition:background .12s}
.notif-item:hover{background:var(--panel2)}
.notif-item .msg{font-weight:700; font-size:.86rem}
.notif-item .meta{font-size:.74rem; color:var(--muted); margin-top:3px}
.notif-item.unread{background:color-mix(in srgb, var(--brand) 6%, var(--panel))}
.notif-item.unread .msg::before{content:""; display:inline-block; width:7px; height:7px; border-radius:50%; background:var(--brand); margin-inline-end:6px}

/* Login screen — adapted from the ministry's own landing-page materials (MOI Design Files/
   LandingPage/moi-landing-v02.html): a real photo of the ministry headquarters as a full-bleed
   hero (not the generic globe/space graphics also included in the materials — see
   docs/design-materials-audit.md for why those were set aside), the same dark diagonal overlay
   gradient the landing page uses for legibility, and its aqua "status badge" + gradient-divider
   language above the functional login card. The card itself stays a solid, fully opaque panel
   (not the landing page's translucent glass treatment) because it hosts a real dev-user picker
   that has to stay legible and clickable, not a decorative nav panel. */
#login-screen{
  min-height:100vh; display:flex; flex-direction:column; align-items:center; justify-content:center; padding:40px 20px;
  background:
    linear-gradient(135deg, rgba(10,28,30,.78) 0%, rgba(12,38,38,.6) 55%, rgba(10,22,25,.82) 100%),
    url('../img/lp-bg.png') center/cover no-repeat;
  position:relative; overflow:hidden;
}
/* revision v7 §5: client-specified exact asset (MOI Design Files/LandingPage/assets/lp-bg.png),
   used as-is per instruction — supersedes the v6 §2.1 choice (moi-login-bg.png, the ministry-seal
   photo) and the original design-materials-audit's "deliberately not used" call on this same file
   (see the Update section appended to that audit). The same dark diagonal overlay gradient already
   used for legibility over any hero photo applies here unchanged, and reads even better against
   this image's own near-black base tone than it did over the photographic hero. */
.login-hero{position:relative; z-index:1; width:100%; max-width:460px; display:flex; flex-direction:column; align-items:center; gap:14px; margin-bottom:24px; text-align:center}
/* revision v8/v9 §7 — clickable seal, consistent with the sidebar's "go home" affordance. */
.login-mark{width:56px; height:56px; border-radius:16px; background:linear-gradient(150deg, rgba(255,255,255,.14), rgba(255,255,255,.04)); border:1px solid rgba(255,255,255,.18); display:flex; align-items:center; justify-content:center; padding:9px; cursor:pointer; transition:background .15s, transform .15s}
.login-mark:hover{background:rgba(255,255,255,.2); transform:translateY(-1px)}
.login-mark img{width:100%; height:100%; object-fit:contain}
.login-badge{
  display:inline-flex; align-items:center; gap:8px; background:rgba(97,220,212,.12); border:1px solid rgba(97,220,212,.32);
  border-radius:999px; padding:6px 18px; font-size:.78rem; font-weight:700; color:var(--brand-aqua); letter-spacing:.02em;
}
.login-badge .dot{width:7px; height:7px; border-radius:50%; background:var(--brand-aqua); box-shadow:0 0 8px var(--brand-aqua); animation:login-pulse 2s infinite}
@keyframes login-pulse{0%,100%{opacity:1; transform:scale(1)} 50%{opacity:.5; transform:scale(.8)}}
@media (prefers-reduced-motion: reduce){.login-badge .dot{animation:none}}
.login-hero h1{margin:0; font-size:1.7rem; font-weight:900; color:#fff; text-shadow:0 2px 20px rgba(0,0,0,.5)}
.login-hero .divider{width:52px; height:3px; border-radius:2px; background:linear-gradient(90deg, var(--brand-aqua), var(--brand-gold))}
.login-card{position:relative; z-index:1; background:var(--panel); border-radius:20px; width:100%; max-width:460px; box-shadow:0 40px 80px rgba(0,0,0,.5); overflow:hidden}
.login-card .top{background:linear-gradient(135deg,var(--brand),var(--brand-dark)); padding:16px 26px; color:#fff; position:relative; overflow:hidden}
.login-card .top::after{content:""; position:absolute; inset-inline-end:-40px; top:-40px; width:160px; height:160px; border-radius:50%; background:rgba(255,255,255,.07)}
.login-card .top p{margin:0; opacity:.9; font-size:.84rem; font-weight:700; position:relative}
.login-card .body{padding:22px 24px 28px}
.devuser{
  display:flex; align-items:center; gap:12px; padding:13px; border:1px solid var(--border); border-radius:13px; margin-bottom:10px; cursor:pointer;
  transition:border-color .15s, box-shadow .15s, transform .1s;
}
.devuser:hover{border-color:var(--brand); box-shadow:var(--shadow-sm); transform:translateY(-1px)}
.devuser .av{width:38px; height:38px; border-radius:50%; background:linear-gradient(150deg, var(--brand), var(--brand-dark)); color:#fff; display:flex; align-items:center; justify-content:center; font-weight:800}
.devuser .info{line-height:1.3; min-width:0}
.devuser .info .n{font-weight:800; font-size:.88rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.devuser .info .r{font-size:.74rem; color:var(--muted); overflow:hidden; text-overflow:ellipsis; white-space:nowrap}

/* Revision v15 §4 — dev-login redesign: grouped by role with a two-column grid (login-card-wide)
   instead of one long undifferentiated list, role-tinted avatars for at-a-glance scanning. Visual
   only — same click-to-login mechanism, same accounts, same roles (including مكتب المعالي). */
.login-card-wide{max-width:720px}
.devuser-body{max-height:min(560px, 62vh); overflow-y:auto}
.devuser-group{margin-bottom:16px}
.devuser-group:last-child{margin-bottom:0}
.devuser-group-title{font-size:.72rem; font-weight:800; text-transform:uppercase; letter-spacing:.04em; color:var(--soft); margin-bottom:8px; padding:0 2px}
.devuser-grid{display:grid; grid-template-columns:1fr 1fr; gap:10px}
@media(max-width:560px){ .devuser-grid{grid-template-columns:1fr} }
.devuser-grid .devuser{margin-bottom:0}
/* Revision v17 §1 — implicit grid items default to min-width:auto, so a long unbreakable name
   (e.g. "ضابط اتصال الجهة المالكة — إدارة الأمن السيبراني", the longest seeded LiaisonOfficer
   label) forced its 1fr column to grow to that text's full min-content width instead of the
   fair-share column width — 828px of content in a 662px card, live-measured. It never showed as a
   page-level scrollbar only because #login-screen's own overflow:hidden silently swallowed the
   162-166px overflow instead — invisible clipping, the same failure class as the v16 fix elsewhere
   in this file. min-width:0 lets the grid track stay at its fair 1fr share, which lets
   .devuser .info .n/.r's existing text-overflow:ellipsis actually activate instead of never
   triggering because the box was free to grow to fit the text. */
.devuser-grid .devuser{min-width:0}
.av.role-ExecutiveOffice{background:linear-gradient(150deg, var(--brand), var(--brand-dark))}
.av.role-TaskUnitManager{background:linear-gradient(150deg, var(--brand-teal), var(--brand-dark-teal))}
.av.role-TaskUnitEmployee{background:linear-gradient(150deg, var(--brand-turquoise), var(--brand-teal))}
.av.role-LiaisonOfficer{background:linear-gradient(150deg, var(--brand-gold), var(--brand-gold-dark))}
.av.role-OwningEntityManager{background:linear-gradient(150deg, var(--brand-gold-dark), #7a5f26)}
.av.role-Leadership{background:linear-gradient(150deg, var(--brand-dark-teal), var(--brand-charcoal))}
.av.role-SystemAdmin{background:linear-gradient(150deg, var(--brand-charcoal), #1c1a1b)}
.av.role-ExcellencyOffice{background:linear-gradient(150deg, var(--danger), #7d2c1e)}

.pill-check{display:flex; align-items:center; gap:8px; padding:8px 13px; border:1px solid var(--border-strong); border-radius:999px; cursor:pointer; font-size:.8rem; font-weight:700; transition:background .12s, color .12s, border-color .12s}
/* Admin forms (admin.js) use `<label class="pill-check">` as a standalone active/enabled toggle
   directly inside `.field` (department/priority/escalation/user modals) — `.field label`'s
   `display:block` (0,1,1 specificity) otherwise beats `.pill-check`'s `display:flex` (0,1,0),
   stretching what should be a small chip into a full-width bar. */
.field label.pill-check{display:inline-flex; width:fit-content}
.pill-check:hover{border-color:var(--brand)}
.pill-check.active{background:var(--brand); border-color:var(--brand); color:#fff}
.chiprow{display:flex; gap:8px; flex-wrap:wrap; align-items:center}
.chiprow select, .chiprow input{width:auto; min-width:170px}

/* Route transitions (app.js App.route) — a slightly quicker, softer crossfade than the browser's
   250ms default so drill-downs feel snappy rather than sluggish on a data-dense app. */
::view-transition-old(root), ::view-transition-new(root){ animation-duration:.16s; animation-timing-function:ease-out }

/* Task list row entrance (design system §"Motion") — a shorter, subtler version of the dashboard
   KPI stagger-in (kpi-in keyframe) so a freshly loaded/filtered table settles in instead of
   popping, without the more noticeable translateY used for the five KPI cards. */
@keyframes row-in{from{opacity:0} to{opacity:1}}
.tbl-row-in tbody tr{animation:row-in .22s ease-out backwards}
.tbl-row-in tbody tr:nth-child(1){animation-delay:0ms} .tbl-row-in tbody tr:nth-child(2){animation-delay:20ms}
.tbl-row-in tbody tr:nth-child(3){animation-delay:40ms} .tbl-row-in tbody tr:nth-child(4){animation-delay:60ms}
.tbl-row-in tbody tr:nth-child(5){animation-delay:80ms} .tbl-row-in tbody tr:nth-child(6){animation-delay:100ms}
.tbl-row-in tbody tr:nth-child(n+7){animation-delay:120ms}
@media (prefers-reduced-motion: reduce){.tbl-row-in tbody tr{animation:none}}

/* ============================================================
   Redesign pass — new components (additive to the token system
   above; nothing in this block replaces an existing rule).
   ============================================================ */

/* Subtle brand texture (redesign §9) — a restrained echo of the ministry's own concentric-arc
   motif (seen in the brand-guide cover / login materials), used sparingly on hero surfaces only
   (login screen, dashboard hero) rather than globally. Pure CSS, no image request. */
.brand-texture{position:relative}
.brand-texture::before{
  content:""; position:absolute; inset:0; pointer-events:none; z-index:0; opacity:.5;
  background-image:repeating-radial-gradient(circle at 100% 0%, rgba(255,255,255,.05) 0, rgba(255,255,255,.05) 1px, transparent 1px, transparent 34px);
}

/* Custom file drop-zone (redesign §1.1/§6) — replaces the raw native <input type=file> that broke
   the visual language and leaked English "Choose File"/"No file chosen" into a 100%-Arabic app. */
.file-drop{
  border:1.5px dashed var(--border-strong); border-radius:var(--radius); padding:22px; text-align:center;
  cursor:pointer; transition:border-color .15s, background .15s; background:var(--panel2);
}
.file-drop:hover, .file-drop.dragover{border-color:var(--brand); background:color-mix(in srgb, var(--brand) 6%, var(--panel2))}
.file-drop .fd-icon{color:var(--soft); margin-bottom:8px}
.file-drop .fd-main{font-weight:800; font-size:.9rem}
.file-drop .fd-sub{color:var(--muted); font-size:.78rem; margin-top:3px}
.file-drop input[type=file]{display:none}
.file-chip{
  display:flex; align-items:center; gap:10px; padding:10px 13px; border:1px solid var(--border-strong);
  border-radius:var(--radius-sm); background:var(--panel);
}
.file-chip .fc-icon{color:var(--brand); flex:0 0 auto}
.file-chip .fc-name{flex:1; min-width:0; font-weight:700; font-size:.85rem; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.file-chip .fc-size{color:var(--muted); font-size:.74rem}
.file-chip .fc-remove{flex:0 0 auto; border:none; background:transparent; color:var(--danger); cursor:pointer; padding:4px; border-radius:6px}
.file-chip .fc-remove:hover{background:color-mix(in srgb, var(--danger) 12%, transparent)}

/* KPI urgency tiering (redesign §7.1) — the escalation/overdue tile is visually dominant (bigger
   number, own row) instead of five uniformly-sized boxes competing for attention equally.
   revision v7 §3: the original .kpi-hero just added `grid-column:1/-1` to the plain narrow-card
   .kpi styling — that left the absolutely-positioned corner icon and the tight `.icn`/`.n`/`.l`
   stack floating with no visual anchor across the newly-wide banner, reading as an accidental
   stretch rather than a designed alert. Rebuilt as a deliberate horizontal banner: a static
   (non-absolute) icon chip, a text block, and a trailing note + arrow that makes the "this is
   clickable, go look" affordance explicit instead of implicit. */
/* revision v8/v9 §13: `grid-column:1/-1` stretched this tile across the *entire* KPI row — on a
   normal-width desktop that's 1300px+ of banner for one icon + two lines of text, reading as an
   empty, oversized bar rather than a designed alert. Spanning exactly 2 tracks (roughly double a
   plain KPI tile, same idea as a "double-wide" card) keeps it visually dominant without the dead
   space; `max-width` guards the case where the grid only ever lays out 1-2 columns anyway. */
.kpi-hero{grid-column:span 2; max-width:560px; display:flex; align-items:center; gap:18px; padding:20px 24px}
.kpi-hero::after{display:none}
.kpi-hero .icn{position:static; width:50px; height:50px; flex:0 0 auto; border-radius:14px}
.kpi-hero .txt{display:flex; flex-direction:column; gap:2px; min-width:0}
.kpi-hero .n{font-size:var(--fs-display); line-height:1}
.kpi-hero .l{margin-top:0; font-size:.92rem}
.kpi-hero .note{font-size:.78rem; color:var(--muted); font-weight:600}
.kpi-hero .cta{margin-inline-start:auto; flex:0 0 auto; display:flex; align-items:center; gap:6px; font-size:.82rem; font-weight:800; opacity:.8; white-space:nowrap}
@media(max-width:640px){.kpi-hero{grid-column:1/-1; max-width:none; flex-wrap:wrap; padding:18px}.kpi-hero .cta{margin-inline-start:0}}
.kpi.tier-info{box-shadow:var(--elev-1)}
.kpi.tier-info .n{font-size:1.55rem}
.kpi-pulse{animation:kpi-pulse-ring 2.2s ease-in-out infinite}
@keyframes kpi-pulse-ring{0%,100%{box-shadow:var(--elev-2), 0 0 0 0 color-mix(in srgb, var(--danger) 35%, transparent)} 50%{box-shadow:var(--elev-2), 0 0 0 8px color-mix(in srgb, var(--danger) 0%, transparent)}}
@media (prefers-reduced-motion: reduce){.kpi-pulse{animation:none}}

/* Designed empty/zero-data chart states (redesign §5/§7.1) — a bare legend with no visible arcs
   read as "broken," not "no activity"; this gives the zero-data case an intentional placeholder. */
.chart-empty{height:100%; display:flex; flex-direction:column; align-items:center; justify-content:center; color:var(--muted); gap:8px}
.chart-empty svg{opacity:.5}
.chart-empty .ce-title{font-weight:800; font-size:.85rem; color:var(--text); position:absolute; top:18px; inset-inline-start:18px}
.chart-empty .ce-text{font-size:.8rem; font-weight:600}

/* Active-filter chip row (redesign §3/§7.2) — makes current filter state legible and removable
   at a glance instead of requiring each column's popover to be reopened to recall what's active. */
.active-filters{display:flex; flex-wrap:wrap; gap:8px; align-items:center; margin-bottom:14px}
.active-filters:empty{display:none}
.afilter-chip{
  display:inline-flex; align-items:center; gap:6px; padding:5px 8px 5px 12px; border-radius:999px;
  background:color-mix(in srgb, var(--brand) 10%, var(--panel)); border:1px solid color-mix(in srgb, var(--brand) 30%, var(--border));
  font-size:.78rem; font-weight:700; color:var(--brand-dark);
}
[data-theme="dark"] .afilter-chip{color:var(--brand-turquoise)}
.afilter-chip button{border:none; background:rgba(0,0,0,.08); color:inherit; width:17px; height:17px; border-radius:50%; cursor:pointer; display:flex; align-items:center; justify-content:center; padding:0}
.afilter-chip button:hover{background:rgba(0,0,0,.16)}
.afilter-clear-all{font-size:.78rem; font-weight:700; color:var(--muted); background:none; border:none; cursor:pointer; text-decoration:underline; padding:4px}

/* Column visibility picker (redesign §7.2) */
/* inset-inline-end:0 (not left/right) anchors the popover's trailing edge to its trigger's
   wrapper — RTL's trailing edge is the visual right, so without this it fell back to the
   absolutely-positioned element's default static-position placement, which overflowed off the
   left edge of the viewport (the "الأعمدة" button sits near the left of the filters bar, after the
   flex spacer) and clipped its own checkbox labels. */
/* revision v7 §8 — a small popover appearing with a hard display:none→block cut felt abrupt next
   to the rest of the app's now-consistent motion language (modals, toasts); the same
   fade+settle used for .modal now covers this popover class of UI too. */
.col-picker-pop{position:absolute; inset-inline-end:0; top:100%; z-index:60; margin-top:6px; background:var(--panel); border:1px solid var(--border); border-radius:var(--radius); box-shadow:var(--elev-3); padding:10px; min-width:200px; max-width:calc(100vw - 32px); animation:modal-in .16s cubic-bezier(.2,.8,.3,1)}
@media (prefers-reduced-motion: reduce){.col-picker-pop{animation:none}}
.col-picker-pop label{display:flex; align-items:center; gap:8px; padding:6px 8px; border-radius:8px; font-size:.85rem; font-weight:600; cursor:pointer}
.col-picker-pop label:hover{background:var(--panel2)}

/* Date-range filter (revision v8/v9 §6) — a real, prominent picker (presets + custom from/to),
   not a bare pair of date inputs. Same anchored-popover pattern as .col-picker-pop above (RTL
   trailing-edge anchor, modal-in fade+settle). */
.daterange-pop{position:absolute; inset-inline-start:0; top:100%; z-index:60; margin-top:6px; background:var(--panel); border:1px solid var(--border); border-radius:var(--radius); box-shadow:var(--elev-3); padding:14px; min-width:280px; animation:modal-in .16s cubic-bezier(.2,.8,.3,1)}
@media (prefers-reduced-motion: reduce){.daterange-pop{animation:none}}
.dr-presets{display:flex; flex-wrap:wrap; gap:6px; margin-bottom:12px}
.dr-preset-btn{padding:6px 12px; border-radius:999px; border:1px solid var(--border-strong); background:var(--panel2); color:var(--text); font-size:.78rem; font-weight:700; cursor:pointer; transition:background .12s, border-color .12s, color .12s}
.dr-preset-btn:hover{border-color:var(--brand)}
.dr-preset-btn.active{background:var(--brand); border-color:var(--brand); color:#fff}
.dr-divider{height:1px; background:var(--border); margin-bottom:12px}
.dr-custom{display:flex; gap:10px}
.dr-custom .field{margin-bottom:0; flex:1; min-width:0}
.dr-actions{display:flex; gap:8px; margin-top:12px}
.dash-filter-bar{display:flex; align-items:center; gap:10px; margin-bottom:18px}
.dash-filter-bar .drl{font-size:.8rem; color:var(--muted); font-weight:700}

/* Persistent task-detail actions rail (redesign §7.3) — sticky so the one thing a working user
   needs (the next action) never scrolls out of view behind a long update/timeline history.
   Revision v13 — this used to have the timeline card stacked right below it as a plain-flow
   sibling, which overlapped it once scrolled (position:sticky reserves flow space from the
   element's *static* position, not from where it's actually painted once pinned — any flowing
   sibling directly beneath a sticky box of this shape will eventually overlap it; not a tunable
   offset bug). Fixed structurally in taskDetail.js by moving the timeline into the other column —
   .actions-rail is now the sole occupant of its column, so max-height/overflow:auto here only
   guard against the rail's own content ever exceeding the viewport, not against a sibling. */
@media(min-width:861px){
  .actions-rail{position:sticky; top:calc(var(--topbar-h) + 16px); max-height:calc(100vh - var(--topbar-h) - 32px); overflow:auto}
}
/* Revision v15 §5.2 — priority shown read-only in the approval modal until "تغيير الأولوية" is
   clicked, so an edit is a deliberate action rather than an always-open dropdown. */
.approve-priority-display{display:flex; align-items:center; justify-content:space-between; gap:10px; padding:9px 13px; border:1px solid var(--border-strong); border-radius:10px; background:var(--panel2); font-weight:800}

.detail-tabs{display:flex; gap:4px; border-bottom:1px solid var(--border); margin-bottom:16px}
.detail-tab{padding:10px 16px; border:none; background:none; font-weight:800; font-size:.86rem; color:var(--muted); cursor:pointer; border-bottom:2px solid transparent; margin-bottom:-1px; transition:color .15s, border-color .15s}
.detail-tab:hover{color:var(--text)}
.detail-tab.active{color:var(--brand); border-bottom-color:var(--brand)}
.detail-tab .cnt{background:var(--panel2); border-radius:999px; padding:0 7px; font-size:.72rem; margin-inline-start:5px}

/* Admin IA regroup — grouped left sub-nav (redesign §7.6) replacing the flat 7-button pill row */
.admin-shell{display:flex; gap:20px; align-items:flex-start}
.admin-subnav{flex:0 0 232px; position:sticky; top:calc(var(--topbar-h) + 16px)}
.admin-subnav .grp{margin-bottom:18px}
.admin-subnav .grp-title{font-size:.72rem; font-weight:800; text-transform:uppercase; letter-spacing:.04em; color:var(--soft); padding:0 10px; margin-bottom:6px}
.admin-subnav a{display:flex; align-items:center; gap:9px; padding:9px 11px; border-radius:9px; font-weight:700; font-size:.86rem; color:var(--text); cursor:pointer; opacity:.8; border-inline-start:3px solid transparent}
.admin-subnav a:hover{background:var(--panel2); opacity:1}
.admin-subnav a.active{background:color-mix(in srgb, var(--brand) 10%, var(--panel2)); opacity:1; border-inline-start-color:var(--brand); color:var(--brand-dark)}
[data-theme="dark"] .admin-subnav a.active{color:var(--brand-turquoise)}
.admin-content{flex:1; min-width:0}
@media(max-width:980px){.admin-shell{flex-direction:column} .admin-subnav{position:static; width:100%; display:flex; flex-wrap:wrap; gap:6px} .admin-subnav .grp{display:flex; align-items:center; gap:6px; margin-bottom:8px} .admin-subnav .grp-title{margin-bottom:0}}

/* Command palette (redesign §4/§7.6 discoverability — Ctrl/Cmd+K) */
.cmdk-bg{position:fixed; inset:0; background:rgba(13,20,15,.55); backdrop-filter:blur(2px); z-index:150; display:none; align-items:flex-start; justify-content:center; padding-top:12vh; will-change:opacity; transform:translateZ(0)}
.cmdk-bg.show{display:flex; animation:modal-bg-in .12s ease-out}
.cmdk{width:100%; max-width:560px; background:var(--panel); border-radius:16px; box-shadow:var(--elev-4); overflow:hidden; animation:modal-in .16s cubic-bezier(.2,.8,.3,1)}
.cmdk-input-row{display:flex; align-items:center; gap:10px; padding:14px 18px; border-bottom:1px solid var(--border)}
.cmdk-input-row svg{color:var(--muted); flex:0 0 auto}
.cmdk-input-row input{border:none; padding:0; background:none; font-size:1rem}
.cmdk-input-row input:focus{box-shadow:none}
.cmdk-hint{font-size:.68rem; color:var(--soft); border:1px solid var(--border); border-radius:5px; padding:1px 6px}
.cmdk-results{max-height:360px; overflow:auto; padding:8px}
.cmdk-group-title{font-size:.68rem; font-weight:800; text-transform:uppercase; letter-spacing:.04em; color:var(--soft); padding:8px 10px 4px}
.cmdk-item{display:flex; align-items:center; gap:10px; padding:10px 12px; border-radius:9px; cursor:pointer; font-size:.86rem; font-weight:600}
.cmdk-item .ci-icon{color:var(--brand); flex:0 0 auto}
.cmdk-item .ci-sub{color:var(--muted); font-size:.74rem; font-weight:500; margin-inline-start:auto}
.cmdk-item.sel, .cmdk-item:hover{background:color-mix(in srgb, var(--brand) 10%, var(--panel2))}
@media (prefers-reduced-motion: reduce){.cmdk-bg.show, .cmdk{animation:none}}

.notif-panel-inner{box-shadow:var(--elev-3)}
/* Notification panel icon-coding by type (redesign §1.6) — mirrors the timeline's existing
   tl-escalation-1/2/resolved treatment so the bell panel is scannable by kind, not just read state. */
.notif-item{display:flex; gap:11px; align-items:flex-start}
.notif-item .ni-icon{flex:0 0 auto; width:30px; height:30px; border-radius:9px; display:flex; align-items:center; justify-content:center; background:var(--panel2); color:var(--muted)}
.notif-item .ni-body{flex:1; min-width:0}
.notif-item.n-escalation-1 .ni-icon{background:color-mix(in srgb, var(--warn) 18%, transparent); color:var(--warn)}
.notif-item.n-escalation-2 .ni-icon{background:color-mix(in srgb, var(--escalation2) 18%, transparent); color:var(--escalation2)}
.notif-item.n-clarification .ni-icon{background:color-mix(in srgb, var(--info) 18%, transparent); color:var(--info)}
.notif-item.n-assignment .ni-icon{background:color-mix(in srgb, var(--brand) 16%, transparent); color:var(--brand)}
.notif-item.n-transfer .ni-icon{background:color-mix(in srgb, var(--brand-gold-dark) 18%, transparent); color:var(--brand-gold-dark)}
.notif-item.n-resolved .ni-icon{background:color-mix(in srgb, var(--ok) 16%, transparent); color:var(--ok)}

/* In-app toast alerts, upgraded (redesign §2.2) — icon-coded by type, matching the classification
   above, dismissible via an explicit close control in addition to the existing click-to-open. */
.toast{display:flex; align-items:flex-start; gap:10px}
.toast .tst-icon{flex:0 0 auto; margin-top:1px}
.toast .tst-msg{flex:1; min-width:0}
.toast .tst-close{flex:0 0 auto; background:rgba(255,255,255,.16); border:none; color:#fff; width:20px; height:20px; border-radius:50%; cursor:pointer; display:flex; align-items:center; justify-content:center; padding:0; opacity:.85}
.toast .tst-close:hover{opacity:1; background:rgba(255,255,255,.28)}

/* Motion extensions (redesign §1.7), all reduced-motion-gated with the rest of the app */
@keyframes value-flash{0%{background-color:color-mix(in srgb, var(--brand) 22%, transparent)} 100%{background-color:transparent}}
.kpi .n.flash{animation:value-flash 1.1s ease-out}
@keyframes escalation-attention{0%,100%{transform:scale(1)} 30%{transform:scale(1.12)} 55%{transform:scale(1)}}
.tag.escalation-1.attn, .tag.escalation-2.attn{animation:escalation-attention 900ms ease-in-out 2}
@media (prefers-reduced-motion: reduce){.kpi .n.flash, .tag.escalation-1.attn, .tag.escalation-2.attn{animation:none}}

.success-burst{position:fixed; inset:0; z-index:250; display:flex; align-items:center; justify-content:center; pointer-events:none}
.success-burst svg{width:96px; height:96px}
.success-burst circle{fill:none; stroke:var(--ok); stroke-width:4; stroke-linecap:round; stroke-dasharray:220; stroke-dashoffset:220; animation:sb-circle .5s ease-out forwards}
.success-burst path{fill:none; stroke:var(--ok); stroke-width:5; stroke-linecap:round; stroke-linejoin:round; stroke-dasharray:40; stroke-dashoffset:40; animation:sb-check .35s ease-out .45s forwards}
@keyframes sb-circle{to{stroke-dashoffset:0}}
@keyframes sb-check{to{stroke-dashoffset:0}}
@media (prefers-reduced-motion: reduce){.success-burst{display:none}}

/* Shared-element transition target (redesign §7/§8) — a stable view-transition-name is assigned
   inline (js/views/taskList.js + taskDetail.js) to the clicked row's code badge and the detail
   header's code badge so the two morph into each other, instead of the whole page just crossfading. */
::view-transition-group(vt-task-code){ animation-duration:.22s }

/* Revision v15 §3 — task-detail header redesign: explicit titled sections (بيانات أساسية / الحالة
   والتصعيد / مصدر المهمة) with a visible divider between them, instead of one flat undifferentiated
   grid — the reported "unreadable, looks like a blank page" finding. Reuses existing tokens only
   (.card/.section-title/--muted/--border), per docs/design-system.md — no new color system. */
.td-title-row{display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:12px}
.td-text{white-space:pre-wrap; line-height:1.8; font-size:1rem; margin:0 0 4px}
.td-section{margin-top:18px; padding-top:16px; border-top:1px solid var(--border)}
.td-section-title{font-size:.86rem; color:var(--muted); text-transform:uppercase; letter-spacing:.03em; margin-bottom:12px}
.td-meta-grid > div label{display:block; font-weight:700; font-size:.78rem; color:var(--muted); margin-bottom:3px}
.td-meta-grid > div > div{font-size:.95rem; font-weight:600}

.breadcrumb{display:flex; align-items:center; gap:6px; font-size:.82rem; color:var(--muted); margin-bottom:12px; font-weight:700}
.breadcrumb a{cursor:pointer; color:var(--muted)}
.breadcrumb a:hover{color:var(--brand); text-decoration:underline}
.breadcrumb .sep{opacity:.5}
.breadcrumb .cur{color:var(--text)}

@media print{ #sidebar,#topbar,.no-print{display:none !important} }

/* ═════════ Revision v18 (ملاحظات اجتماع 2026-08-25) ═════════
   Every rule below reuses the existing token set (--panel/--panel2/--border/--muted/--ok/--warn/
   --danger/--p1…--p4) per docs/design-system.md — no new palette, no new radius/shadow scale. */

/* §1 — the create-task screen's automatic-classification line. Sits under the priority dropdown; the
   "overridden" variant is used when the user has deliberately chosen a different priority than the
   rule suggests, which is always allowed. */
.ct-auto-class{
  display:flex; align-items:center; gap:6px; margin-top:6px; padding:6px 9px; border-radius:8px;
  font-size:.76rem; font-weight:700; line-height:1.6;
  background:color-mix(in srgb, var(--ok) 10%, transparent); color:var(--text);
  border:1px solid color-mix(in srgb, var(--ok) 28%, transparent);
}
.ct-auto-class.overridden{
  background:color-mix(in srgb, var(--warn) 12%, transparent);
  border-color:color-mix(in srgb, var(--warn) 32%, transparent);
}
.ct-auto-class svg{flex:0 0 auto}

/* §2 — the «افتراضي» tag on an automatically-filled expected end date. */
.tag.auto-default{background:color-mix(in srgb, var(--warn) 18%, transparent); color:#8a6d1f; font-weight:800}
[data-theme="dark"] .tag.auto-default{color:var(--warn)}

/* §4 — the four new statuses, each given its own colour in the same family the existing six use
   (.tag.status above): held = neutral/paused, returned-for-revision = warning, cancelled = muted
   danger, reopened = informational. */
.tag.status.OnHold{color:var(--brand-teal)}
.tag.status.ReturnedForRevision{color:#8a6d1f}
.tag.status.Cancelled{color:var(--danger)}
.tag.status.Reopened{color:var(--info)}
[data-theme="dark"] .tag.status.ReturnedForRevision{color:var(--warn)}

/* §4.1 — the hold panel on the task screen. */
.td-hold{background:color-mix(in srgb, var(--brand-teal) 7%, transparent); border-radius:var(--radius); padding:14px 16px}
.td-hold .td-section-title{margin-bottom:10px; display:flex; align-items:center; gap:8px}

/* §10 — the P3/P4 stage list. */
.stage-list{display:flex; flex-direction:column; gap:8px}
.stage-row{
  display:flex; align-items:center; gap:12px; padding:9px 12px; border-radius:10px;
  background:var(--panel2); border:1px solid var(--border); font-size:.84rem;
}
.stage-idx{
  flex:0 0 24px; height:24px; border-radius:50%; display:inline-flex; align-items:center; justify-content:center;
  background:var(--panel); border:1px solid var(--border); font-weight:800; font-size:.74rem;
}
.stage-title{flex:1; min-width:0; font-weight:700}
.stage-due{flex:0 0 auto; display:inline-flex; align-items:center; gap:5px; color:var(--muted); font-weight:700; font-variant-numeric:tabular-nums}
.stage-progress{flex:0 0 150px; display:flex; align-items:center; gap:8px}
.stage-progress .progress{flex:1}
.stage-progress b{font-variant-numeric:tabular-nums; font-size:.8rem}
.stage-actions{flex:0 0 auto; display:flex; gap:6px}
@media(max-width:860px){
  .stage-row{flex-wrap:wrap}
  .stage-progress{flex:1 0 100%}
}

/* §8/§9.2 — the shared seven-column board table. Denser than the main task table (seven columns must
   fit without horizontal page scroll) and with the two free-text columns clamped so one long update
   can't push the date columns off screen. */
.board-table{font-size:.82rem}
.board-table th, .board-table td{padding:9px 11px}
.board-table .board-title{max-width:280px; font-weight:700}
.board-table .board-update{max-width:320px; color:var(--text)}
.board-table .board-title, .board-table .board-update{
  display:table-cell; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;
}
.board-table .board-muted{color:var(--muted); font-weight:600}

/* §9.2 — each dashboard table scrolls inside itself; the PAGE keeps its no-vertical-scroll rule. The
   max-height here is the starting point, trimmed at runtime by fitBoardsToViewport(). */
.exec-dash-boards{display:grid; grid-template-columns:1fr 1fr; gap:16px; flex:0 0 auto; margin-top:8px}
.exec-board-col{min-width:0; padding:10px 14px}
.exec-board-col .section-title{display:flex; align-items:center; gap:8px; margin-bottom:8px}
.exec-board-scroll{max-height:260px; overflow-y:auto; overflow-x:auto}
.exec-board-scroll .table-wrap{overflow-x:visible}
@media(max-width:1200px){ .exec-dash-boards{grid-template-columns:1fr} }

/* §9.3 — the period filter in the dashboard header, and the two-indicator table per administration. */
.exec-period-filter{display:flex; gap:6px; align-items:center}
.exec-period-filter .pill-check{font-size:.74rem; padding:4px 10px}
.exec-metrics-scroll{overflow-y:auto; max-height:190px; flex:1}
.exec-metrics{display:flex; flex-direction:column; gap:4px}
.exec-metrics-head, .exec-metrics-row{display:grid; grid-template-columns:1fr 108px 108px; gap:8px; align-items:center}
.exec-metrics-head{font-size:.7rem; font-weight:800; color:var(--muted); padding:0 6px 4px; border-bottom:1px solid var(--border)}
.exec-metrics-head .v, .exec-metrics-row .v{text-align:end}
.exec-metrics-row{font-size:.8rem; padding:4px 6px; border-radius:7px}
.exec-metrics-row:hover{background:var(--panel2)}
.exec-metrics-row .lbl{font-weight:700; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap}
.exec-metrics-row .v{font-weight:900; font-variant-numeric:tabular-nums}
.exec-metrics-row .v small{display:block; font-size:.66rem; font-weight:700; color:var(--muted)}

/* §8 — the مكتب المعالي board screen. Ordinary page scrolling (deliberately NOT the dashboard's
   single-viewport lock, since this is a complete list) plus paging. */
.eb-head{display:flex; align-items:center; justify-content:space-between; gap:12px; margin-bottom:12px}
.eb-count{font-size:.78rem; font-weight:700; color:var(--muted); font-variant-numeric:tabular-nums}
.eb-filters{margin-bottom:12px}
.eb-filters select{max-width:220px}
.eb-more{display:flex; justify-content:center; margin-top:14px}

/* §9.2 layout — the page-level no-vertical-scroll rule, kept intact now that the two full tables share
   the screen with everything that was already on it. The dashboard column becomes a fixed-height flex
   stack: the KPI strip and the P1 card keep their natural heights, the department/indicator row is
   capped, and the two tables take whatever is left — each region scrolling inside itself, so no row is
   ever hidden, only reached by that region's own scrollbar. This OVERRIDES the two-rule pair above
   (.exec-dash's min-height and .exec-dash-body's single column); both are left in place as the record
   of what this screen looked like before §9. */
body.exec-dash-active #content{overflow:hidden}
.exec-dash{height:calc(100vh - var(--topbar-h) - 24px); min-height:0; overflow:hidden}
.exec-dash-body{grid-template-columns:1fr 1fr; flex:0 0 auto; max-height:176px; gap:12px}
.exec-dash-col{min-height:0}
.exec-dash-bars-scroll{max-height:124px}
.exec-metrics-scroll{max-height:124px}
.exec-dash-boards{flex:1 1 auto; min-height:0; gap:12px}
.exec-board-col{display:flex; flex-direction:column; min-height:0}
.exec-board-scroll{flex:1 1 auto; min-height:0; max-height:none}
/* The P1 spotlight card (untouched by §9) gives up a little height so the tables get a usable one. */
.exec-dash-p1-chip-list{max-height:74px}
@media(max-width:1200px){
  /* Below this width the app already drops to one column everywhere; the page is allowed to scroll
     again here, same escape hatch the create-task screen uses at its own breakpoint. */
  body.exec-dash-active #content{overflow:visible}
  .exec-dash{height:auto; overflow:visible}
  .exec-dash-body{max-height:none}
  .exec-board-scroll{max-height:300px}
}

/* §9.2 height budget, tuned against a real 1536×730 viewport: the fixed regions above the tables are
   trimmed so the two tables get a usable slice (roughly four rows each before their own scrollbar
   takes over) while the page itself still never scrolls. Measured live: document scrollHeight equals
   clientHeight at this budget. */
.exec-dash{height:calc(100vh - var(--topbar-h) - 36px)}
.exec-dash-body{max-height:132px}
.exec-dash-bars-scroll{max-height:92px}
.exec-metrics-scroll{max-height:92px}
.exec-dash-p1{padding:6px 18px; gap:4px}
.exec-dash-p1-chip-list{max-height:48px}

/* §9.2 — final height/density budget, measured live at 1536×730. Two adjustments, both confined to
   this screen: (1) the fixed regions above the tables give up more of their padding so the tables get
   ~5 rows before their own scrollbar takes over; (2) inside the dashboard the seven-column table runs
   at a denser scale with tighter text clamps, since each table only gets half the page width here —
   مكتب المعالي's full-width board (§8) keeps the roomier default. */
.exec-dash-kpis-top .kpi{padding:7px}
.exec-dash-kpis-top .kpi .n{font-size:1.6rem}
.exec-dash-escalation-tile, .exec-dash-health-tile{padding:7px 14px}
.exec-dash-p1-breakdown-title{margin-bottom:3px}
.exec-dash-body{max-height:112px}
.exec-dash-bars-scroll, .exec-metrics-scroll{max-height:74px}
.exec-dash-col{padding:7px 14px}
.exec-board-col .section-title{font-size:.8rem; margin-bottom:5px}
.exec-board-scroll .board-table{font-size:.72rem}
.exec-board-scroll .board-table th, .exec-board-scroll .board-table td{padding:5px 7px; white-space:nowrap}
.exec-board-scroll .board-table .board-title{max-width:150px}
.exec-board-scroll .board-table .board-update{max-width:150px}
.exec-board-scroll .code-badge{font-size:.72rem; padding:2px 7px}

/* §9.2 — column widths for the dashboard's half-width tables, measured column by column against the
   real rendered table (the seven columns came to 814px inside a 573px box before this): the three
   free-text columns are clamped, and the date headers are allowed to wrap onto two short lines
   instead of forcing a wide column. The container keeps overflow-x as a safety net, but at a standard
   1536px-wide screen the seven columns now fit without it. */
.exec-board-scroll .board-table th{white-space:normal; line-height:1.25}
.exec-board-scroll .board-table th, .exec-board-scroll .board-table td{padding:4px 6px}
.exec-board-scroll .board-table td:first-child{max-width:84px}
.exec-board-scroll .board-table .board-title{max-width:112px}
.exec-board-scroll .board-table td:nth-child(3){max-width:104px; overflow:hidden; text-overflow:ellipsis}
.exec-board-scroll .board-table .board-update{max-width:104px}
/* …the last 52px: the three free-text columns give up ~18px each, which brings the table (625px)
   inside the 573px box with a little slack, so no horizontal scrollbar is needed at this width. */
.exec-board-scroll .board-table td:first-child{max-width:72px}
.exec-board-scroll .board-table .board-title{max-width:96px}
.exec-board-scroll .board-table td:nth-child(3){max-width:88px}
.exec-board-scroll .board-table .board-update{max-width:88px}
/* §9.3 — the period chips are real <button>s (see executiveDashboard.js); reset the UA button chrome
   so they look identical to the app's existing .pill-check chips. */
button.pill-check{background:none; color:inherit; font-family:inherit; line-height:1.4}
button.pill-check.active{background:var(--brand); color:#fff}
button.pill-check:focus-visible{outline:2px solid var(--brand); outline-offset:2px}
