/* ─── CorLink — Design System ────────────────────────────────
   Primary: #8A6D21 (Olive Gold)   Secondary: #3B82F6 (Blue)
   Font: Inter, system-sans  Icons: Tabler Icons
   ─────────────────────────────────────────────────────────── */

/* ── Variables ─────────────────────────────────────────────── */
:root {
  --color-primary:        #8A6D21;
  --color-primary-dark:   #6E5719;
  --color-primary-light:  #F2EBD9;
  --color-secondary:      #3B82F6;
  --color-secondary-dark: #2563EB;
  --color-secondary-light:#EAF2FF;

  /* Each status color has a vivid tone (icons, decorative accents,
     large-scale UI) plus a "-dark" tone reserved for text/small-icon use
     on top of that color's own "-bg" tint — the vivid tones alone don't
     clear WCAG AA (4.5:1) against their pastel backgrounds or against
     white, same reasoning as --color-primary/--color-primary-dark
     already established. */
  --color-success:       #10B981;
  --color-success-dark:  #047857;
  --color-success-bg:    #D1FAE5;
  --color-warning:       #F59E0B;
  --color-warning-dark:  #B45309;
  --color-warning-bg:    #FEF3C7;
  --color-error:         #EF4444;
  --color-error-dark:    #B91C1C;
  --color-error-bg:      #FEE2E2;
  --color-info:          #0EA5E9;
  --color-info-dark:     #0369A1;
  --color-info-bg:       #E0F2FE;

  /* --color-text-muted is a mid-slate rather than the reference's
     literal #94A3B8 — that value is ~2.3:1 against a white surface,
     failing WCAG AA for the small captions/labels it's used on here.
     The reference's literal value lives on as --color-text-light,
     already reserved for lower-stakes decorative text (chevrons etc). */
  --color-text:          #0F172A;
  --color-text-muted:    #64748B;
  --color-text-light:    #94A3B8;
  --color-border:        #E5E7EB;
  --color-border-focus:  #8A6D21;
  --color-bg:            #F8FAFC;
  --color-surface:       #FFFFFF;
  --color-overlay:       rgba(15, 23, 42, 0.5);

  --radius-sm:   4px;
  --radius-md:   8px;
  --radius-lg:   12px;
  --radius-full: 9999px;

  --shadow-sm:   0 1px 2px rgba(0,0,0,.05);
  --shadow-md:   0 4px 16px rgba(0,0,0,.1);
  --shadow-lg:   0 8px 32px rgba(0,0,0,.12);

  --font-sans:   'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  --font-divehi: 'Faruma', serif;

  --transition: 150ms ease;
}

/* Dark mode — OS-level default for anyone who hasn't picked a theme
   explicitly (js/lib/theme.js hasn't written to localStorage yet, so
   it re-derives from this media query on every load).

   Besides the neutrals, dark mode also swaps every status tint pair:
   the light theme's pastel -bg + deep -dark text inverts to a
   translucent tint of the vivid tone + the vivid tone itself as text
   (all ≥4.5:1 on the dark surfaces) — solid pastel chips read as glare
   on a dark UI. Anything added here must also be mirrored in BOTH
   data-theme blocks below, since an explicit choice must fully undo
   the OS-level values in either direction. */
@media (prefers-color-scheme: dark) {
  :root {
    --color-text:       #E5E7EB;
    --color-text-muted: #9CA3AF;
    --color-border:     #1F2937;
    --color-bg:         #0F172A;
    --color-surface:    #111827;

    --color-primary-dark:   #D4A94A;
    --color-primary-light:  rgba(138, 109, 33, .18);
    --color-secondary-dark: #60A5FA;
    --color-secondary-light:rgba(59, 130, 246, .16);
    --color-success-dark:   #10B981;
    --color-success-bg:     rgba(16, 185, 129, .14);
    --color-warning-dark:   #F59E0B;
    --color-warning-bg:     rgba(245, 158, 11, .14);
    --color-error-dark:     #F87171;
    --color-error-bg:       rgba(239, 68, 68, .15);
    --color-info-dark:      #38BDF8;
    --color-info-bg:        rgba(14, 165, 233, .15);
  }

  /* The logo's navy wordmark/tagline pixels are close in value to the
     dark surface color behind them now that the logo's own white
     background has been keyed out to transparent — a faint light
     halo keeps them legible without touching the source asset. */
  .login-logo-img, .topbar-logo-crop img {
    filter: drop-shadow(0 0 1px rgba(255,255,255,.5)) drop-shadow(0 0 5px rgba(255,255,255,.15));
  }

  /* RichEditor's text-color tool (foreColor) writes an explicit inline
     color: onto whatever the author picked — often near-black, since
     that's the tool's own default swatch. Composed in light mode, that
     reads fine; redisplayed later in dark mode against a dark surface,
     it's invisible. Overriding to the theme's own text color in every
     read-only rich-text render site (thread bodies, review comments)
     trades away custom text colors for guaranteed readability — the
     same tradeoff most apps make rather than trying to contrast-check
     arbitrary author-chosen colors per element. */
  .thread-message-body, .thread-message-body *,
  .review-comment-text, .review-comment-text *,
  .rich-editor-body, .rich-editor-body * {
    color: var(--color-text) !important;
  }
}

/* Explicit theme choice via the toggle button (js/lib/theme.js sets
   data-theme on <html>, persisted in localStorage). An attribute
   selector is more specific than the plain `:root` inside the media
   query above, so these win either direction regardless of source
   order — picking Light while the OS is set to dark works correctly,
   and vice versa. */
:root[data-theme="dark"] {
  --color-text:       #E5E7EB;
  --color-text-muted: #9CA3AF;
  --color-border:     #1F2937;
  --color-bg:         #0F172A;
  --color-surface:    #111827;

  --color-primary-dark:   #D4A94A;
  --color-primary-light:  rgba(138, 109, 33, .18);
  --color-secondary-dark: #60A5FA;
  --color-secondary-light:rgba(59, 130, 246, .16);
  --color-success-dark:   #10B981;
  --color-success-bg:     rgba(16, 185, 129, .14);
  --color-warning-dark:   #F59E0B;
  --color-warning-bg:     rgba(245, 158, 11, .14);
  --color-error-dark:     #F87171;
  --color-error-bg:       rgba(239, 68, 68, .15);
  --color-info-dark:      #38BDF8;
  --color-info-bg:        rgba(14, 165, 233, .15);
}
:root[data-theme="dark"] .login-logo-img,
:root[data-theme="dark"] .topbar-logo-crop img {
  filter: drop-shadow(0 0 1px rgba(255,255,255,.5)) drop-shadow(0 0 5px rgba(255,255,255,.15));
}
/* Same rich-text color override as the prefers-color-scheme block
   above, needed again here for an explicit Dark choice while the OS
   is set to Light (same contract as the variable resets elsewhere in
   this file). */
:root[data-theme="dark"] .thread-message-body, :root[data-theme="dark"] .thread-message-body *,
:root[data-theme="dark"] .review-comment-text, :root[data-theme="dark"] .review-comment-text *,
:root[data-theme="dark"] .rich-editor-body, :root[data-theme="dark"] .rich-editor-body * {
  color: var(--color-text) !important;
}
/* The logo halo lives inside the prefers-color-scheme media block above
   as a plain rule, not a variable — so an explicit Light choice while
   the OS is dark needs this to undo it, same contract as the variable
   resets below. */
:root[data-theme="light"] .login-logo-img,
:root[data-theme="light"] .topbar-logo-crop img {
  filter: none;
}
:root[data-theme="light"] {
  --color-text:          #0F172A;
  --color-text-muted:    #64748B;
  --color-border:        #E5E7EB;
  --color-bg:            #F8FAFC;
  --color-surface:       #FFFFFF;

  --color-primary-dark:   #6E5719;
  --color-primary-light:  #F2EBD9;
  --color-secondary-dark: #2563EB;
  --color-secondary-light:#EAF2FF;
  --color-success-dark:   #047857;
  --color-success-bg:     #D1FAE5;
  --color-warning-dark:   #B45309;
  --color-warning-bg:     #FEF3C7;
  --color-error-dark:     #B91C1C;
  --color-error-bg:       #FEE2E2;
  --color-info-dark:      #0369A1;
  --color-info-bg:        #E0F2FE;
}

/* ── Reset ─────────────────────────────────────────────────── */
*, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
html { font-size: 16px; height: 100%; }
body {
  font-family: var(--font-sans);
  font-size: 0.9375rem;
  color: var(--color-text);
  background: var(--color-bg);
  line-height: 1.5;
  min-height: 100vh;
  -webkit-font-smoothing: antialiased;
}
/* Safety net: no single element (a wide table, a select auto-sized to its
   longest option, etc.) should be able to push the whole page into
   horizontal scroll — that clips the sticky topbar/content off-screen on
   mobile instead of just wrapping/scrolling the one element that's wide. */
html, body { overflow-x: hidden; max-width: 100%; }
/* Text links use the theme-switched -dark tone, not the raw primary:
   #8A6D21 is under 4.5:1 against white — fine for fills/icons, thin for
   body-size link text. -dark resolves to a deeper olive in light mode and
   the brighter #D4A94A in dark mode (where the raw tone would be fine but
   the brighter one reads better against the dark surface). */
a { color: var(--color-primary-dark); text-decoration: none; }
a:hover { text-decoration: underline; }
img, svg { display: block; }
button { cursor: pointer; border: none; background: none; font: inherit; }

/* ── Typography ────────────────────────────────────────────── */
h1 { font-size: 1.75rem; font-weight: 700; line-height: 1.2; }
h2 { font-size: 1.375rem; font-weight: 600; line-height: 1.3; }
h3 { font-size: 1.125rem; font-weight: 600; }
p  { line-height: 1.6; }

/* ── Utilities ─────────────────────────────────────────────── */
.hidden { display: none !important; }
.sr-only {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0,0,0,0); border: 0;
}

/* ── Spinner ────────────────────────────────────────────────── */
@keyframes spin { to { transform: rotate(360deg); } }
.spinner {
  display: inline-block;
  width: 16px; height: 16px;
  border: 2px solid rgba(255,255,255,.35);
  border-top-color: #fff;
  border-radius: 50%;
  animation: spin .65s linear infinite;
  vertical-align: middle;
  margin-left: 6px;
}

/* ── Alerts ─────────────────────────────────────────────────── */
.alert {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 14px;
  border-radius: var(--radius-md);
  font-size: 0.875rem;
  line-height: 1.5;
}
.alert i { flex-shrink: 0; margin-top: 2px; }
.alert-error   { background: var(--color-error-bg);   color: var(--color-error-dark);   }
.alert-success { background: var(--color-success-bg); color: var(--color-success-dark); }
.alert-warning { background: var(--color-warning-bg); color: var(--color-warning-dark); }
.alert-info    { background: var(--color-info-bg);    color: var(--color-info-dark);    }

/* ── Buttons ────────────────────────────────────────────────── */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 10px 20px;
  border-radius: var(--radius-md);
  font-size: 0.9375rem; font-weight: 500;
  transition: background var(--transition), opacity var(--transition);
  white-space: nowrap;
}
.btn:disabled { opacity: .6; cursor: not-allowed; }

.btn-primary {
  background: var(--color-primary);
  color: #fff;
}
.btn-primary:hover:not(:disabled) { background: var(--color-primary-dark); }

.btn-secondary {
  background: var(--color-surface);
  color: var(--color-text);
  border: 1px solid var(--color-border);
}
.btn-secondary:hover:not(:disabled) { background: var(--color-bg); }

.btn-full { width: 100%; }

.icon-btn {
  display: flex; align-items: center; justify-content: center;
  width: 36px; height: 36px;
  border-radius: var(--radius-md);
  color: var(--color-text-muted);
  transition: background var(--transition), color var(--transition);
}
.icon-btn:hover:not(:disabled) {
  background: var(--color-primary-light);
  color: var(--color-primary);
}
.icon-btn:disabled { opacity: .4; cursor: not-allowed; }

/* Compact icon-only button for inline row actions (structure tree rename/
   toggle) where a labeled .btn would add more visual noise than the row
   needs — the icon + title tooltip carries enough meaning on its own. */
.icon-btn-xs {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px;
  flex-shrink: 0;
  border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  font-size: 0.875rem;
  transition: background var(--transition), color var(--transition);
}
.icon-btn-xs:hover { background: var(--color-primary-light); color: var(--color-primary); }

/* Realtime "this case changed" notice (request-detail.js) — a fixed
   toast the viewer dismisses or acts on, not an auto-reload (which
   would blow away an in-progress compose form the instant the other
   side does anything). Positioned above .bottom-nav's z-index so it
   isn't hidden behind the fixed mobile tab bar. */
.realtime-toast {
  position: fixed;
  left: 50%;
  bottom: calc(24px + env(safe-area-inset-bottom));
  transform: translateX(-50%);
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px 10px 16px;
  background: var(--color-text);
  color: #fff;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
  font-size: 0.875rem;
  max-width: calc(100vw - 32px);
}
.realtime-toast i:first-child { color: var(--color-primary-light); flex-shrink: 0; }
.realtime-toast span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.realtime-toast .icon-btn-xs { color: rgba(255,255,255,.7); }
.realtime-toast .icon-btn-xs:hover { background: rgba(255,255,255,.15); color: #fff; }
@media (max-width: 640px) {
  .realtime-toast { bottom: calc(76px + env(safe-area-inset-bottom)); }
}

/* ── Form Fields ────────────────────────────────────────────── */
.field-group { display: flex; flex-direction: column; gap: 6px; }

.field-label {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--color-text);
}

.field-input-wrap {
  position: relative;
}

.field-icon {
  position: absolute;
  left: 12px; top: 50%;
  transform: translateY(-50%);
  color: var(--color-text-muted);
  pointer-events: none;
  font-size: 1rem;
}

.field-input {
  width: 100%;
  padding: 10px 42px 10px 38px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: 0.9375rem;
  font-family: var(--font-sans);
  color: var(--color-text);
  background: var(--color-surface);
  transition: border-color var(--transition), box-shadow var(--transition);
  outline: none;
}
.field-input:focus {
  border-color: var(--color-border-focus);
  box-shadow: 0 0 0 3px rgba(13, 148, 136, 0.15);
}
.field-input::placeholder { color: var(--color-text-light); }
.field-input.input-error {
  border-color: var(--color-error);
  box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.12);
}

.field-toggle-pw {
  position: absolute;
  right: 10px; top: 50%;
  transform: translateY(-50%);
  color: var(--color-text-muted);
  padding: 4px;
  border-radius: var(--radius-sm);
  transition: color var(--transition);
}
.field-toggle-pw:hover { color: var(--color-primary); }

.field-hint {
  font-size: 0.8125rem;
  color: var(--color-text-muted);
}

.checkbox-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.9375rem;
  color: var(--color-text);
}
.checkbox-row input[type="checkbox"] {
  width: 16px;
  height: 16px;
  accent-color: var(--color-primary);
  flex-shrink: 0;
}
.checkbox-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  max-height: 200px;
  overflow-y: auto;
  padding: 8px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm, 6px);
}

/* ── Password Requirements ───────────────────────────────────── */
.pw-requirements {
  display: flex; flex-direction: column; gap: 4px;
  padding: 10px 12px;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.pw-req {
  display: flex; align-items: center; gap: 6px;
  font-size: 0.8125rem;
  color: var(--color-text-muted);
  transition: color var(--transition);
}
.pw-req.req-met { color: var(--color-success-dark); }
.pw-req i { font-size: 0.875rem; }

/* ── Login Layout ────────────────────────────────────────────── */
.login-bg {
  position: relative;
  min-height: 100vh;
  background: linear-gradient(135deg, var(--color-primary) 0%, var(--color-secondary) 100%);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 16px;
}

/* Sits on the gradient, not the white card — .icon-btn's default
   muted-gray coloring would be nearly invisible there, so this
   overrides to a translucent white pill instead. A subtle border adds
   edge definition independent of the underlying gradient color, since
   the fill alone hovers close to WCAG's 3:1 floor for UI components
   at the teal end of the gradient. */
.login-theme-toggle {
  position: absolute;
  top: 16px;
  right: 16px;
  color: #fff;
  background: rgba(255, 255, 255, 0.22);
  border: 1px solid rgba(255, 255, 255, 0.4);
}
.login-theme-toggle:hover:not(:disabled) {
  background: rgba(255, 255, 255, 0.35);
  color: #fff;
}

.login-card {
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 40px 36px;
  width: 100%;
  max-width: 420px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}

.login-brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 8px;
  text-align: center;
}

/* Full CorLink lockup (icon + wordmark + tagline baked into the image) */
.login-logo-img {
  width: 180px;
  height: auto;
  display: block;
}

.login-form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.login-section-title {
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text);
  display: flex;
  align-items: center;
  gap: 6px;
}

.login-footer {
  text-align: center;
  font-size: 0.8125rem;
  color: var(--color-text-muted);
  padding-top: 4px;
  border-top: 1px solid var(--color-border);
  display: flex;
  flex-direction: column;
  gap: 2px;
}
.login-footer-sub { color: var(--color-text-light); }

/* ── App Layout ──────────────────────────────────────────────── */
/* Three navigation tiers by viewport, all rendered from the same
   AppShell.topbarHtml() markup (sidebar + topbar are siblings inside
   .app-layout, so pure CSS decides which shows):
     ≥ 900px  — persistent left sidebar; the topbar keeps only the
                 theme/notification/user actions (its own brand + nav
                 links hide, the sidebar carries them instead)
     641–899  — original topbar with inline nav links
     ≤ 640px  — topbar + fixed bottom tab bar */
.app-layout {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
}

.sidebar { display: none; }

@media (min-width: 900px) {
  /* Fixed (not min-) height so the grid's 1fr row is an actual size
     constraint, not just a floor — .main-content below becomes the
     ONE scrolling container instead of the whole page. Without this,
     .topbar/.sidebar's position: sticky never had anything to stick
     WITHIN: html/body's `overflow-x: hidden` (see the reset section
     above) forces body's other axis to auto per the CSS overflow spec,
     making BODY the actual scrolling container instead of the
     viewport — and a sticky element whose own grid row can grow past
     the viewport (the old min-height:100vh) just scrolls away with
     everything else. Confirmed broken (topbar/sidebar both scrolled
     fully off-screen) and confirmed fixed with this change, via a
     real browser scroll test.
  */
  .app-layout {
    display: grid;
    grid-template-columns: 250px minmax(0, 1fr);
    grid-template-rows: auto 1fr;
    grid-template-areas:
      "sidebar topbar"
      "sidebar main";
    height: 100vh;
  }
  .topbar { grid-area: topbar; }
  .main-content { grid-area: main; overflow-y: auto; }
  /* Extra .topbar qualifier so this outranks the base .topbar-nav rule
     that appears LATER in the file — equal specificity would let source
     order re-show it. The brand (logo + organisation name) stays
     visible at every width; only the nav links move into the sidebar. */
  .topbar .topbar-nav { display: none; }

  .sidebar {
    grid-area: sidebar;
    display: flex;
    flex-direction: column;
    gap: 2px;
    height: 100vh;
    overflow-y: auto;
    padding: 20px 14px;
    background: var(--color-surface);
    border-right: 1px solid var(--color-border);
  }
}

.sidebar-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 0 10px 18px;
}
.sidebar-appname {
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--color-primary);
  line-height: 1.2;
}
.sidebar-tagline {
  font-size: 0.6563rem;
  color: var(--color-text-muted);
  line-height: 1.3;
}
.sidebar-nav { display: flex; flex-direction: column; gap: 2px; }
.sidebar-link {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 10px 12px;
  border-radius: var(--radius-md);
  color: var(--color-text-muted);
  font-size: 0.9063rem;
  font-weight: 500;
  transition: background var(--transition), color var(--transition);
}
.sidebar-link i { font-size: 1.125rem; flex-shrink: 0; }
.sidebar-link:hover {
  background: var(--color-bg);
  color: var(--color-text);
  text-decoration: none;
}
.sidebar-link--active {
  background: var(--color-primary-light);
  color: var(--color-primary-dark);
  font-weight: 600;
}
.sidebar-footer {
  margin-top: auto;
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 10px;
  color: var(--color-text-muted);
}
.sidebar-footer i { font-size: 1.25rem; flex-shrink: 0; }
.sidebar-footer-meta { min-width: 0; }
.sidebar-footer-title {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.sidebar-footer-sub {
  font-size: 0.75rem;
  color: var(--color-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* ── Topbar ──────────────────────────────────────────────────── */
.topbar {
  position: sticky; top: 0; z-index: 100;
  display: flex;
  align-items: center;
  gap: 16px;
  height: 60px;
  padding: 0 24px;
  background: var(--color-surface);
  border-bottom: 1px solid var(--color-border);
  box-shadow: var(--shadow-sm);
}

.topbar-brand {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-shrink: 0;
}

/* Crops the square logo lockup down to just the icon mark for the
   compact topbar — the wordmark/tagline in the source image only fit
   comfortably in the larger login screen version. */
.topbar-logo-crop {
  width: 32px;
  height: 32px;
  overflow: hidden;
  border-radius: var(--radius-sm);
  flex-shrink: 0;
}
.topbar-logo-crop img {
  width: 145%;
  max-width: none;
  transform: translate(-15%, -8%);
}
/* An org's own uploaded logo (Admin → Organization Settings) can be any
   aspect ratio — the zoom/shift above is hand-tuned to the CorLink mark's
   own padding specifically and would badly crop an arbitrary image, so
   this variant swaps to a plain, non-distorting fit instead. */
.topbar-logo-crop--org img {
  width: 100%;
  height: 100%;
  max-width: 100%;
  object-fit: contain;
  transform: none;
}

.topbar-appname {
  font-size: 1.125rem;
  font-weight: 700;
  color: var(--color-primary);
}

.topbar-nav {
  display: flex;
  align-items: center;
  gap: 4px;
  flex: 1;
}

.topbar-actions {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
  /* Pushes actions flush right on its own — .topbar-nav supplies the
     same push via flex: 1 on desktop, but that stops working the
     moment .topbar-nav is display: none at the mobile breakpoint. */
  margin-left: auto;
}

/* ── Bottom Tab Bar (mobile) ─────────────────────────────────── */
/* Hidden by default (desktop uses .topbar-nav instead) — the
   max-width: 640px block below flips both. */
.bottom-nav {
  display: none;
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 150;
  background: var(--color-surface);
  border-top: 1px solid var(--color-border);
  padding: 6px 4px calc(6px + env(safe-area-inset-bottom));
  box-shadow: 0 -2px 8px rgba(0,0,0,.04);
}
.bottom-nav-item {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 2px;
  padding: 6px 4px;
  border-radius: var(--radius-md);
  color: var(--color-text-muted);
  font-size: 0.6875rem;
  font-weight: 500;
  text-align: center;
  transition: color var(--transition), background var(--transition);
}
.bottom-nav-item:hover { text-decoration: none; background: var(--color-bg); }
.bottom-nav-item i { font-size: 1.25rem; }
.bottom-nav-item--active { color: var(--color-primary-dark); }

/* ── User Menu ───────────────────────────────────────────────── */
.user-menu-wrap { position: relative; }

.user-menu-btn {
  display: flex; align-items: center; gap: 8px;
  padding: 5px 12px 5px 5px;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border);
  background: var(--color-bg);
  color: var(--color-text);
  transition: background var(--transition), border-color var(--transition);
}
.user-menu-btn:hover { background: var(--color-primary-light); border-color: var(--color-primary); }

.avatar {
  width: 32px; height: 32px;
  border-radius: var(--radius-full);
  background: var(--color-primary);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 600;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}
.avatar-lg { width: 40px; height: 40px; font-size: 0.875rem; }

.user-name-short {
  font-size: 0.875rem;
  font-weight: 500;
  max-width: 120px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

.user-menu-dropdown {
  position: absolute; right: 0; top: calc(100% + 6px);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  min-width: 220px;
  overflow: hidden;
  z-index: 200;
}

.user-menu-header {
  display: flex; align-items: center; gap: 12px;
  padding: 16px;
}

.user-menu-name { font-weight: 600; font-size: 0.9375rem; }
.user-menu-role { font-size: 0.8125rem; color: var(--color-text-muted); }

.menu-divider { border: none; border-top: 1px solid var(--color-border); }

.menu-item {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px;
  width: 100%;
  font-size: 0.9375rem;
  color: var(--color-text);
  text-align: left;
  transition: background var(--transition);
}
.menu-item:hover { background: var(--color-bg); }
.menu-item.danger { color: var(--color-error-dark); }

.menu-item-link {
  font-size: 0.8125rem;
  color: var(--color-primary-dark);
  font-weight: 500;
}
.menu-item-link:hover { text-decoration: underline; }

/* ── Notifications ───────────────────────────────────────────── */
.notif-wrap { position: relative; }
.notif-btn { position: relative; }
.notif-badge {
  position: absolute;
  top: 2px; right: 2px;
  min-width: 16px; height: 16px;
  padding: 0 4px;
  border-radius: var(--radius-full);
  /* Fixed deep red, NOT var(--color-error-dark): that var flips to the
     vivid #EF4444 in dark mode where white-on-it is only ~3.8:1. */
  background: #B91C1C;
  color: #fff;
  font-size: 0.625rem;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
}

.notif-dropdown {
  position: absolute; right: 0; top: calc(100% + 6px);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  width: 340px;
  max-width: calc(100vw - 32px);
  overflow: hidden;
  z-index: 200;
}
.notif-dropdown-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--color-border);
  font-weight: 600;
  font-size: 0.875rem;
}
.notif-list {
  max-height: 360px;
  overflow-y: auto;
}
.notif-empty {
  padding: 24px 16px;
  text-align: center;
  color: var(--color-text-light);
  font-size: 0.875rem;
}
.notif-item {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  padding: 12px 16px;
  text-align: left;
  border-bottom: 1px solid var(--color-border);
  transition: background var(--transition);
}
.notif-item:last-child { border-bottom: none; }
.notif-item:hover { background: var(--color-bg); }
.notif-item-dot {
  width: 8px; height: 8px;
  border-radius: var(--radius-full);
  margin-top: 6px;
  flex-shrink: 0;
  background: transparent;
}
.notif-item--unread .notif-item-dot { background: var(--color-primary); }
.notif-item-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.notif-item-message {
  font-size: 0.875rem;
  color: var(--color-text);
  white-space: normal;
}
.notif-item--unread .notif-item-message { font-weight: 600; }
.notif-item-time {
  font-size: 0.75rem;
  color: var(--color-text-light);
}

/* ── Global Search (topbar) ──────────────────────────────────── */
.global-search-wrap { position: relative; }

.global-search-panel {
  position: absolute; right: 0; top: calc(100% + 6px);
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  width: 360px;
  max-width: calc(100vw - 32px);
  overflow: hidden;
  z-index: 200;
}
.global-search-input-wrap {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 14px;
  border-bottom: 1px solid var(--color-border);
  color: var(--color-text-light);
}
.global-search-input-wrap input {
  flex: 1;
  border: none;
  outline: none;
  background: transparent;
  font-size: 0.875rem;
  color: var(--color-text);
}
.global-search-results {
  max-height: 360px;
  overflow-y: auto;
}
.global-search-empty {
  padding: 24px 16px;
  text-align: center;
  color: var(--color-text-light);
  font-size: 0.875rem;
}
.global-search-result {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  width: 100%;
  padding: 12px 14px;
  text-align: left;
  border-bottom: 1px solid var(--color-border);
  transition: background var(--transition);
  color: var(--color-text-light);
}
.global-search-result:last-child { border-bottom: none; }
.global-search-result:hover { background: var(--color-bg); }
.global-search-result-body {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.global-search-result-title {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--color-text);
  white-space: normal;
}
.global-search-result-meta {
  font-size: 0.75rem;
  color: var(--color-text-light);
}

/* ── Main Content ────────────────────────────────────────────── */
.main-content {
  flex: 1;
  padding: 32px 32px;
  /* 1200px was tuned for the old full-width topbar layout — with the
     250px sidebar now consuming part of the viewport, the same cap
     left the content floating in a sea of empty margin on wide
     desktops. */
  max-width: 1440px;
  width: 100%;
  margin: 0 auto;
}

.page-header { margin-bottom: 24px; }
.page-title { color: var(--color-text); }
.page-subtitle { color: var(--color-text-muted); margin-top: 4px; }
.page-header-row {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}

/* ── Phase Notice ────────────────────────────────────────────── */
.phase-notice {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 16px 20px;
  background: var(--color-primary-light);
  border: 1px solid rgba(13,148,136,.25);
  border-radius: var(--radius-lg);
  color: var(--color-primary-dark);
  margin-bottom: 24px;
}
.phase-notice i { font-size: 1.25rem; flex-shrink: 0; margin-top: 2px; }
.phase-notice p { margin-top: 4px; font-size: 0.875rem; color: var(--color-text-muted); }

/* ── Stat Grid ───────────────────────────────────────────────── */
/* auto-FIT, not auto-fill: fill would keep empty phantom tracks on
   wide screens so 4 cards huddle in the left two-thirds of the row —
   fit collapses the unused tracks and stretches the cards evenly
   across the full content width. */
.stat-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 16px;
}

.stat-card {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 20px;
  display: flex;
  align-items: flex-start;
  gap: 14px;
  transition: border-color var(--transition), box-shadow var(--transition);
}
.stat-card:hover { border-color: var(--color-primary); box-shadow: var(--shadow-sm); }

.stat-card--disabled { opacity: .55; }
.stat-card--disabled:hover { border-color: var(--color-border); box-shadow: none; }

/* Colored icon square identifying each stat card's theme — the number
   itself stays neutral text; color lives in the icon box only, matching
   the reference layout instead of tinting the whole big digit. */
.stat-icon-box {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 1.25rem;
  flex-shrink: 0;
}
.stat-icon-box--primary   { background: var(--color-primary-light);   color: var(--color-primary-dark); }
.stat-icon-box--secondary { background: var(--color-secondary-light); color: var(--color-secondary-dark); }
.stat-icon-box--warning   { background: var(--color-warning-bg);      color: var(--color-warning-dark); }
.stat-icon-box--error     { background: var(--color-error-bg);        color: var(--color-error-dark); }

.stat-card-body { display: flex; flex-direction: column; gap: 4px; min-width: 0; }
.stat-value {
  font-size: 1.75rem;
  font-weight: 700;
  line-height: 1.2;
  color: var(--color-text);
}
.stat-label {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--color-text-muted);
}
.stat-note { font-size: 0.75rem; color: var(--color-text-light); }

/* ── Action List (dashboard "Action Needed") ────────────────── */
/* A denser row-per-item list rather than another grid of big square
   stat-card tiles — reads better once there are 5-6 targeted counts
   (Not Assigned, Response Not Started, Response Not Received, etc.)
   than the stat-grid's tile format would at that quantity. */
.action-list { display: flex; flex-direction: column; }
.action-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 14px 4px;
  border-bottom: 1px solid var(--color-border);
  color: var(--color-text);
  transition: background var(--transition);
}
.action-row:last-child { border-bottom: none; }
.action-row:hover { background: var(--color-bg); }
.action-row-icon {
  font-size: 1.125rem;
  color: var(--color-primary);
  flex-shrink: 0;
  width: 24px;
  text-align: center;
}
.action-row-label { flex: 1; font-size: 0.9375rem; font-weight: 500; min-width: 0; overflow-wrap: break-word; }
.action-row-count { flex-shrink: 0; }
.action-row-chevron { color: var(--color-text-light); flex-shrink: 0; }
.action-list-empty {
  padding: 20px 4px;
  color: var(--color-text-muted);
  font-size: 0.875rem;
  text-align: center;
}

/* ── Dashboard columns (Action Needed + Quick Actions/Deadlines) ──
   Right column stacks two panels (Quick Actions + Upcoming Deadlines)
   so its total height roughly matches the left column's single, taller
   Action Needed panel instead of leaving a lopsided gap underneath. */
.dashboard-columns {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr);
  gap: 24px;
  margin-top: 24px;
  align-items: start;
}
.dashboard-column-stack { display: flex; flex-direction: column; gap: 24px; }
@media (max-width: 900px) {
  /* minmax(0, 1fr), NOT a bare 1fr — a plain `1fr` track has an
     implicit `minmax(auto, 1fr)`, so CSS Grid won't shrink it below
     the un-wrapped intrinsic width of whatever's inside (here, a long
     Divehi deadline subject with white-space: nowrap for its ellipsis
     truncation). That ballooned this single mobile-collapsed column
     — shared by BOTH Action Needed and the Workload/Deadlines stack —
     wide enough to push the whole page into horizontal scroll, which
     is what made Action Needed's own count badges look like they'd
     vanished: they hadn't, they were just off past the right edge of
     a page that had silently grown wider than the viewport. */
  .dashboard-columns { grid-template-columns: minmax(0, 1fr); }
}

/* ── My Workload & Efficiency (dashboard) ─────────────────────── */
.workload-rings {
  display: flex;
  justify-content: space-around;
  gap: 12px;
  margin-bottom: 14px;
}
.eff-ring-wrap { text-align: center; }
.eff-ring {
  width: 86px;
  height: 86px;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  margin: 0 auto;
  background:
    radial-gradient(closest-side, var(--color-surface) 74%, transparent 75% 100%),
    conic-gradient(var(--color-primary) calc(var(--pct) * 1%), var(--color-border) 0);
  font-size: 1.0625rem;
  font-weight: 700;
}
.eff-ring--track {
  background:
    radial-gradient(closest-side, var(--color-surface) 74%, transparent 75% 100%),
    conic-gradient(var(--color-success) calc(var(--pct) * 1%), var(--color-border) 0);
}
.eff-ring-label {
  margin-top: 6px;
  font-size: 0.75rem;
  color: var(--color-text-muted);
}
.workload-row {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 5px 0;
  font-size: 0.8125rem;
}
.workload-label { width: 40%; min-width: 0; color: var(--color-text-muted); overflow-wrap: break-word; }
.workload-bar {
  flex: 1;
  height: 8px;
  border-radius: var(--radius-full);
  background: var(--color-bg);
  overflow: hidden;
}
.workload-bar-fill { height: 100%; border-radius: var(--radius-full); }
.workload-bar-fill--primary   { background: var(--color-primary); }
.workload-bar-fill--secondary { background: var(--color-secondary); }
.workload-bar-fill--warning   { background: var(--color-warning); }
.workload-bar-fill--success   { background: var(--color-success); }
.workload-count { width: 22px; text-align: right; font-weight: 600; }
.workload-note {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  font-size: 0.75rem;
  color: var(--color-error-dark);
}

/* ── List toolbar (search + org filter) ───────────────────────── */
.list-toolbar {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
}
.list-toolbar .search-box { flex: 1; min-width: 220px; }
.org-filter-select { max-width: 240px; }

.quick-actions-list { display: flex; flex-direction: column; gap: 8px; }
.quick-action-btn {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  color: var(--color-text);
  transition: background var(--transition), border-color var(--transition);
}
.quick-action-btn:hover { background: var(--color-bg); border-color: var(--color-primary); text-decoration: none; }
.quick-action-icon {
  width: 38px;
  height: 38px;
  border-radius: var(--radius-md);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  background: var(--color-primary);
  color: #fff;
  font-size: 1.0625rem;
}
.quick-action-text { flex: 1; min-width: 0; }
.quick-action-label { display: block; font-size: 0.875rem; font-weight: 600; }
.quick-action-sub {
  display: block;
  font-size: 0.75rem;
  color: var(--color-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.quick-action-btn > i { color: var(--color-text-light); flex-shrink: 0; }

/* ── Upcoming Deadlines ────────────────────────────────────────── */
.deadline-list { display: flex; flex-direction: column; }
.deadline-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 4px;
  border-bottom: 1px solid var(--color-border);
  color: var(--color-text);
  transition: background var(--transition);
}
.deadline-row:last-child { border-bottom: none; }
.deadline-row:hover { background: var(--color-bg); }
.deadline-date {
  flex-shrink: 0;
  width: 42px;
  border-radius: var(--radius-md);
  padding: 4px 0;
  text-align: center;
  line-height: 1.15;
}
.deadline-date-month { display: block; font-size: 0.625rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; }
.deadline-date-day { display: block; font-size: 1.0625rem; font-weight: 700; }
.deadline-date-time { display: block; font-size: 0.5625rem; font-weight: 600; opacity: .8; letter-spacing: .02em; }
.deadline-date--error     { background: var(--color-error-bg);     color: var(--color-error-dark); }
.deadline-date--warning   { background: var(--color-warning-bg);   color: var(--color-warning-dark); }
.deadline-date--secondary { background: var(--color-secondary-light); color: var(--color-secondary-dark); }
.deadline-row-body { flex: 1; min-width: 0; }
.deadline-row-ref { font-size: 0.8125rem; font-weight: 600; }
.deadline-row-subject {
  font-size: 0.75rem;
  color: var(--color-text-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.deadline-row-status { flex-shrink: 0; font-size: 0.75rem; font-weight: 600; }
.deadline-row-status--error     { color: var(--color-error-dark); }
.deadline-row-status--warning   { color: var(--color-warning-dark); }
.deadline-row-status--secondary { color: var(--color-secondary-dark); }

/* ── Responsive ─────────────────────────────────────────────── */
@media (max-width: 640px) {
  .login-card { padding: 28px 20px; }
  /* Extra bottom padding clears the fixed .bottom-nav bar so the last
     bit of page content doesn't render underneath it. */
  .main-content { padding: 20px 16px calc(76px + env(safe-area-inset-bottom)); }
  .topbar { padding: 0 16px; gap: 10px; }
  /* Organisation name stays visible on phones, just bounded — it can
     be long ("Maldives Correctional Service"). Was a flat 46vw, which
     on real phone widths (360-412px) left .topbar-actions (theme
     toggle + notification bell + user avatar/chevron — all three
     flex-shrink: 0, same as .topbar-brand, with .topbar-nav hidden
     below this breakpoint so NOTHING in the row can give way) pushed
     past the viewport edge entirely, silently clipped by html/body's
     overflow-x: hidden — the notification bell and user menu became
     untappable on an ordinary phone. calc() against the actions
     block's own fixed width (36px theme toggle + 36px notif bell +
     ~72px user-menu-btn with .user-name-short hidden, + three 8px
     topbar-actions gaps, + this rule's own 16px/16px topbar padding
     and 32px logo + 10px gap) makes the two guaranteed to fit at any
     viewport width, not just the ones this was eyeballed against.
     Confirmed via a real browser measurement at 360/390/412px. */
  .topbar-appname {
    font-size: 0.875rem;
    max-width: calc(100vw - 260px);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .user-name-short { display: none; }
  /* The topbar's own nav links get cramped/cut off at phone widths —
     .bottom-nav replaces them with a fixed tab bar instead. */
  .topbar-nav { display: none; }
  .bottom-nav { display: flex; }

  /* .notif-dropdown/.global-search-panel are `position: absolute;
     right: 0` against their OWN icon's wrapper — correct when that
     icon sits at the true right edge of the topbar, but the bell and
     search icons both sit to the LEFT of the user-menu (the actual
     rightmost item), so their fixed width (340-360px) extended left
     from that inner anchor and ran off the left edge of the viewport
     on phone widths, clipping the notification/search panel's text.
     Anchoring to the viewport instead of the icon's own wrapper fixes
     this regardless of where the icon sits or how long the org name
     pushes it — 66px = the topbar's own 60px height + the 6px gap
     these panels already use above this breakpoint. Confirmed via a
     real browser measurement at 360/390/412px. */
  .notif-dropdown, .global-search-panel {
    position: fixed;
    top: 66px;
    left: 16px;
    right: 16px;
    width: auto;
    max-width: none;
  }
  .stat-grid { grid-template-columns: 1fr 1fr; }
  .panel { padding: 16px; }
  .modal-box { padding: 18px; }

  .org-selector-row {
    flex-direction: column;
    align-items: stretch;
    gap: 6px;
  }
  .org-selector-row .field-select { width: 100%; }

  /* Data tables become stacked cards: header row is hidden (its labels
     move to each cell via data-label), each <tr> becomes a bordered
     card, and each <td> becomes a labeled block instead of a column —
     nothing has to fit side-by-side in a narrow viewport anymore. */
  .data-table thead { display: none; }
  .data-table, .data-table tbody, .data-table tr, .data-table td {
    display: block;
    width: 100%;
  }
  .data-table tr {
    border: 1px solid var(--color-border);
    border-radius: var(--radius-md);
    padding: 10px 12px;
    margin-bottom: 12px;
  }
  .data-table td {
    padding: 6px 0;
    border-bottom: 1px solid var(--color-border);
  }
  .data-table tr:last-child { margin-bottom: 0; }
  .data-table td:last-child { border-bottom: none; }
  .data-table td::before {
    content: attr(data-label);
    display: block;
    font-size: 0.6875rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.02em;
    color: var(--color-text-muted);
    margin-bottom: 3px;
  }

  .structure-node { padding: 12px; }
  .structure-children { margin-left: 4px; padding-left: 10px; }
}

/* ── Divehi / Faruma font support ────────────────────────────── */
@font-face {
  font-family: 'Faruma';
  src: url('../fonts/Faruma.woff2') format('woff2'),
       url('../fonts/Faruma.woff')  format('woff');
  font-weight: normal;
  font-style: normal;
  font-display: swap;
}

/* Divehi text fields use RTL + Faruma font */
.field-divehi {
  font-family: var(--font-divehi);
  direction: rtl;
  text-align: right;
}
/* Form controls need the element+class selector: .field-input/
   .field-input-plain declare font-family at the same specificity but
   LATER in this sheet, which silently beat the plain .field-divehi
   rule above on <input>/<textarea> — the Subject field stayed in the
   sans font when toggled to Divehi. (0,1,1) outranks (0,1,0)
   regardless of source order. */
input.field-divehi, textarea.field-divehi, select.field-divehi {
  font-family: var(--font-divehi);
}

/* ── Topbar Nav Links ────────────────────────────────────────── */
.topbar-link {
  padding: 8px 12px;
  border-radius: var(--radius-md);
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--color-text-muted);
  transition: background var(--transition), color var(--transition);
}
.topbar-link:hover { background: var(--color-bg); text-decoration: none; }
.topbar-link--active { color: var(--color-primary-dark); background: var(--color-primary-light); }

/* ── Tabs ────────────────────────────────────────────────────── */
.tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--color-border);
  margin-bottom: 20px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.tabs::-webkit-scrollbar { display: none; }
.tab-btn {
  padding: 10px 16px;
  font-size: 0.9375rem;
  font-weight: 500;
  color: var(--color-text-muted);
  border-bottom: 2px solid transparent;
  transition: color var(--transition), border-color var(--transition);
  flex-shrink: 0;
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}
.tab-btn:hover { color: var(--color-text); }
.tab-btn--active { color: var(--color-primary-dark); border-bottom-color: var(--color-primary); }

/* "Needs my action" count pill on a top-level tab (Inbox/Sent/Approvals/
   Info Requests). Hidden via the [hidden] attribute when the count is 0,
   so a tab with nothing outstanding stays clean. Reads as a quiet muted
   pill until its tab is active, when it picks up the primary accent to
   match the active underline. */
.tab-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: var(--radius-full);
  background: var(--color-bg);
  color: var(--color-text-muted);
  font-size: 0.6875rem;
  font-weight: 700;
}
.tab-btn--active .tab-count { background: var(--color-primary); color: #fff; }

/* "Needs my action" count on the Requests nav item, painted by
   AppShell.loadActionCount() on every page so pending work is visible
   from anywhere (the task equivalent of the notification bell). Primary
   accent, NOT the notif bell's red — this is "you have things to do,"
   not "something happened." Hidden via [hidden] when the count is 0. */
.nav-action-badge {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: var(--radius-full);
  background: var(--color-primary);
  color: #fff;
  font-size: 0.6875rem;
  font-weight: 700;
  line-height: 1;
}
.sidebar-link .nav-action-badge { margin-left: auto; }       /* push to the far right of the row */
.topbar-link .nav-action-badge { margin-left: 6px; vertical-align: middle; }
/* Bottom nav: small overlay on the corner of the icon. */
.bottom-nav-icon-wrap { position: relative; display: inline-flex; }
.nav-action-badge--corner {
  position: absolute;
  top: -6px; left: 100%;
  transform: translateX(-45%);
  min-width: 15px; height: 15px;
  padding: 0 4px;
  font-size: 0.5625rem;
}

/* Sub-tabs INSIDE a tab (Approvals: Requests | Responses) — same
   control, slightly smaller, sitting under the search box rather than
   at the top of the page. The count pill reuses .filter-chip-count. */
.tabs--sub { margin-bottom: 16px; }
.tabs--sub .tab-btn {
  padding: 8px 12px;
  font-size: 0.875rem;
  display: inline-flex;
  align-items: center;
  gap: 6px;
}

/* ── Deadline entry: days OR end date + time, kept in sync ─────── */
.deadline-input-row {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.deadline-days-input { max-width: 110px; }
.deadline-time-input { max-width: 130px; }
.deadline-input-or {
  color: var(--color-text-muted);
  font-size: 0.8125rem;
  flex-shrink: 0;
}
/* "5d left / due today / overdue 3d" chip next to a deadline date */
.deadline-remaining {
  display: inline-block;
  padding: 1px 8px;
  border-radius: var(--radius-full);
  font-size: 0.75rem;
  font-weight: 600;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  color: var(--color-text-muted);
  white-space: nowrap;
}
.deadline-remaining--soon {
  background: var(--color-warning-bg);
  border-color: transparent;
  color: var(--color-warning-dark);
}
.deadline-remaining--overdue {
  background: var(--color-error-bg);
  border-color: transparent;
  color: var(--color-error-dark);
}

.tab-loading {
  display: flex; align-items: center; gap: 8px;
  color: var(--color-text-muted);
  padding: 24px 0;
}

/* ── Empty States (Requests tabs) ────────────────────────────── */
/* Rendered inside a <td> spanning the table, so it needs to undo the
   cell's own text-align/vertical-align rather than relying on the
   table's default layout. */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: 6px;
  padding: 40px 20px;
  color: var(--color-text-muted);
}
.empty-state i {
  font-size: 2rem;
  color: var(--color-text-light);
  margin-bottom: 6px;
}
.empty-state-title {
  font-size: 0.9375rem;
  font-weight: 600;
  color: var(--color-text);
}
.empty-state-subtitle {
  font-size: 0.8125rem;
  color: var(--color-text-muted);
  max-width: 360px;
}
.empty-state .btn { margin-top: 8px; }

/* ── Search Box (Requests tabs) ──────────────────────────────── */
/* dir="auto" on the input itself (see js/views/requests.js) lets the
   browser flip to RTL text flow for Divehi search terms without any
   JS — same mechanism used for mixed English/Divehi typing elsewhere
   in this app, just without a language toggle since a search box
   needs to accept either script in the same field. */
.search-box {
  position: relative;
  margin-bottom: 12px;
}
.search-box-icon {
  position: absolute;
  left: 12px; top: 50%;
  transform: translateY(-50%);
  color: var(--color-text-muted);
  pointer-events: none;
  font-size: 1rem;
}
.search-box-input {
  width: 100%;
  padding: 9px 36px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: 0.9375rem;
  font-family: var(--font-sans);
  color: var(--color-text);
  background: var(--color-surface);
  transition: border-color var(--transition), box-shadow var(--transition);
  outline: none;
}
.search-box-input:focus {
  border-color: var(--color-border-focus);
  box-shadow: 0 0 0 3px rgba(13, 148, 136, 0.15);
}
.search-box-input::placeholder { color: var(--color-text-light); }
/* Native WebKit/Chromium "clear" (x) button on type="search" sits at
   the far right — the padding above already clears it, this just
   removes the odd default squared hit-area so it matches the
   rounded field visually. */
.search-box-input::-webkit-search-cancel-button { cursor: pointer; }

/* ── Filter Chips ────────────────────────────────────────────── */
/* Quick-filter row under a tab (e.g. Inbox: All / Unrouted / Not
   Assigned / Overdue…) — same horizontal-scroll treatment as .tabs
   since the full set won't always fit on mobile, but visually a step
   down (pill buttons, not underlined) so it reads as "narrowing the
   current tab" rather than a second row of tabs. */
.filter-chips {
  display: flex;
  gap: 8px;
  margin-bottom: 16px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
}
.filter-chips::-webkit-scrollbar { display: none; }
.filter-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 12px;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border);
  background: var(--color-surface);
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--color-text-muted);
  flex-shrink: 0;
  white-space: nowrap;
  transition: background var(--transition), color var(--transition), border-color var(--transition);
}
.filter-chip:hover { color: var(--color-text); border-color: var(--color-text-muted); }
.filter-chip--active {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: #fff;
}
.filter-chip-count {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 18px;
  height: 18px;
  padding: 0 5px;
  border-radius: var(--radius-full);
  background: var(--color-bg);
  color: var(--color-text-muted);
  font-size: 0.6875rem;
  font-weight: 600;
}
.filter-chip--active .filter-chip-count { background: rgba(255, 255, 255, 0.25); color: #fff; }

/* Two side-by-side queues within one tab (Approvals: Requests /
   Responses; Info Requests: Awaiting My Reply / Awaiting Their Reply) —
   stacks on mobile since two full-width tables side by side never fit. */
.queue-group { margin-bottom: 24px; }
.queue-group:last-child { margin-bottom: 0; }
.queue-group-title {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.9375rem;
  font-weight: 600;
  margin-bottom: 10px;
}

/* ── Org Selector ────────────────────────────────────────────── */
.org-selector-row {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
  max-width: 100%;
}
.org-selector-row .field-label { margin: 0; flex-shrink: 0; }
.org-selector-row .field-select { flex: 1; min-width: 0; }

.field-select {
  max-width: 100%;
  padding: 8px 32px 8px 12px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: 0.875rem;
  font-family: var(--font-sans);
  color: var(--color-text);
  background: var(--color-surface);
  outline: none;
  text-overflow: ellipsis;
}
.field-select:focus { border-color: var(--color-border-focus); }

/* Loop In Staff (CC) picker — search-and-add instead of a <select
   multiple> (unusable on a touchscreen, and a bare name list doesn't
   scale past a handful of staff). Selected people render as removable
   .attachment-chip pills (same component, different icon) above the
   search box; matches render as a scrollable list below it. */
.loop-in-chips:empty { display: none; }
.loop-in-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 8px; }
.loop-in-results:empty { display: none; }
.loop-in-results {
  max-height: 200px;
  overflow-y: auto;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  margin-top: 8px;
}
.loop-in-result-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 10px;
  border-bottom: 1px solid var(--color-border);
  font-size: 0.875rem;
}
.loop-in-result-row:last-child { border-bottom: none; }
.loop-in-result-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.loop-in-result-meta { color: var(--color-text-muted); font-size: 0.8125rem; }
.loop-in-result-empty {
  padding: 10px;
  color: var(--color-text-muted);
  font-size: 0.8125rem;
  text-align: center;
}

.field-input-plain {
  padding: 9px 12px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: 0.9375rem;
  font-family: var(--font-sans);
  color: var(--color-text);
  background: var(--color-surface);
  outline: none;
  width: 100%;
}
.field-input-plain:focus { border-color: var(--color-border-focus); box-shadow: 0 0 0 3px rgba(13,148,136,.15); }
/* Multi-line fields only — a single-line <input> sharing this class
   (e.g. Subject) has no meaningful way to resize. Vertical-only so
   dragging can't widen a field past its form and break the layout. */
textarea.field-input-plain { resize: vertical; min-height: 90px; }

/* Label + toggle on one row, e.g. "Language" ... EN | ދިވެހި — mirrors
   field-group's own gap so it drops into the same form layouts. */
.field-group-row {
  display: flex;
  flex-direction: row;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
}
.field-group-row .field-label { margin: 0; }

/* Segmented pill toggle — replaces a <select> for the common two-option
   case (EN / Dhivehi). A hidden input keeps the selected value under
   the same `name` a plain <select> would have used, so existing
   FormData-based submit handlers don't need to change. */
.lang-toggle {
  display: inline-flex;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  padding: 3px;
  gap: 2px;
  flex-shrink: 0;
}
.lang-toggle-btn {
  padding: 5px 14px;
  border-radius: var(--radius-full);
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--color-text-muted);
  transition: background var(--transition), color var(--transition);
}
.lang-toggle-btn--active {
  background: var(--color-primary);
  color: #fff;
}

/* ── Panel ───────────────────────────────────────────────────── */
.panel {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 20px 24px;
}
.panel-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
}
.panel-header h3 { font-size: 1.0625rem; }

/* ── Buttons: small sizes ────────────────────────────────────── */
.btn-sm { padding: 7px 14px; font-size: 0.8125rem; }
.btn-xs { padding: 4px 10px; font-size: 0.75rem; }

/* ── Data Table ──────────────────────────────────────────────── */
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}
.data-table th {
  text-align: left;
  padding: 10px 12px;
  color: var(--color-text-muted);
  font-weight: 600;
  font-size: 0.8125rem;
  border-bottom: 1px solid var(--color-border);
}
.data-table td {
  padding: 12px;
  border-bottom: 1px solid var(--color-border);
  vertical-align: middle;
}
.data-table tr:last-child td { border-bottom: none; }

/* ── Organization Logos ─────────────────────────────────────────── */
.org-logo-thumb {
  width: 32px;
  height: 32px;
  object-fit: contain;
  border-radius: var(--radius-sm, 4px);
  border: 1px solid var(--color-border);
  background: #fff;
}
.org-logo-preview {
  display: block;
  max-width: 160px;
  max-height: 160px;
  object-fit: contain;
  border-radius: var(--radius-sm, 4px);
  border: 1px solid var(--color-border);
  background: #fff;
  padding: 8px;
  margin: 0 auto 16px;
}

/* ── Badges ──────────────────────────────────────────────────── */
.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: var(--radius-full);
  font-size: 0.75rem;
  font-weight: 500;
  background: var(--color-bg);
  color: var(--color-text-muted);
  border: 1px solid var(--color-border);
}
.badge-success { background: var(--color-success-bg); color: var(--color-success-dark); border-color: transparent; }
.badge-muted   { background: var(--color-bg); color: var(--color-text-light); border-color: transparent; }
.badge-primary { background: var(--color-primary-light); color: var(--color-primary-dark); border-color: transparent; }
.badge-warning { background: var(--color-warning-bg); color: var(--color-warning-dark); border-color: transparent; }
.badge-error   { background: var(--color-error-bg); color: var(--color-error-dark); border-color: transparent; }
.badge-outline { background: transparent; }

/* A pill radius (border-radius: 9999px) only looks right on a single line
   of text — the browser has to shrink each corner's radius to fit the
   box, and on a multi-line badge that warps the whole shape into a lens/
   oval. Badges whose text can wrap (e.g. "Supervisor — Legal Section")
   use this instead: normal rounded-rect corners, and stacked one per
   line rather than wrapping mid-badge. */
.badge-list {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
}
.badge-wrap {
  display: block;
  width: fit-content;
  max-width: 100%;
  white-space: normal;
  text-align: left;
  border-radius: var(--radius-sm);
}

/* Users tab Assignments column: unlike .badge-list above (forced
   one-per-line stack, kept as-is for the Designations panel's short
   chips), a user's assignments can be long ("Supervisor · Section:
   ..."), so each chip is a fixed non-pill box (same radius as
   .structure-section-chip below) that only ever wraps AS A UNIT
   between chips, never mid-text — sidesteps the pill-radius oval
   distortion .badge-wrap works around, without forcing every
   assignment onto its own line. */
.assignment-chip-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
}
.assignment-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  max-width: 100%;
  padding: 4px 10px;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: 0.75rem;
  color: var(--color-text-muted);
}
.assignment-chip-text {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  min-width: 0;
}
.assignment-chip--primary {
  background: var(--color-primary-light);
  border-color: transparent;
  color: var(--color-primary-dark);
  font-weight: 500;
}
.assignment-chip--primary i { font-size: 0.6875rem; }

/* ── Structure Tree (Commands/Departments/Divisions/Sections) ──── */
/* Each command/division is its own card rather than a row in a flat,
   divider-separated list — makes each top-level entity a clear visual
   unit instead of everything running together. */
.structure-node {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 14px 16px;
}
.structure-node + .structure-node { margin-top: 12px; }

.structure-node-header {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-size: 0.9375rem;
}
.structure-node-title {
  display: flex;
  align-items: center;
  gap: 8px;
}
.structure-node-title i { color: var(--color-primary); }
.structure-node-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 4px;
}

/* Indented, left-lined list of departments/divisions under a command,
   so the hierarchy reads at a glance instead of relying on a bare
   margin with no visual connection to its parent. */
.structure-children {
  list-style: none;
  margin: 12px 0 0 12px;
  padding-left: 16px;
  border-left: 2px solid var(--color-border);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.structure-child-row {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 8px 10px;
}
.structure-child-top {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  font-size: 0.875rem;
  color: var(--color-text);
}
.structure-child-name { font-weight: 500; }

/* Sections under a department/division: each one is a self-contained
   "chip" (code + status + its own actions) instead of loose badges and
   buttons all competing for space on the same line. */
.structure-sections {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 8px;
}
.structure-sections--top { margin-top: 12px; }
.structure-section-chip {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 3px 4px 3px 10px;
}
.structure-section-code {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--color-primary-dark);
  white-space: nowrap;
}
.structure-empty {
  color: var(--color-text-light);
  font-size: 0.8125rem;
  font-style: italic;
}

/* ── Modal ───────────────────────────────────────────────────── */
.modal-overlay {
  position: fixed; inset: 0;
  background: var(--color-overlay);
  display: flex; align-items: center; justify-content: center;
  z-index: 1000;
  padding: 20px;
}
.modal-box {
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-lg);
  padding: 24px;
  width: 100%;
  max-width: 440px;
  max-height: 90vh;
  overflow-y: auto;
}
/* Writing-heavy modals (compose request, draft/edit a response or an
   internal reply) get real room to work in — the 440px default is for
   small pick-one forms like route/assign. */
.modal-box--lg { max-width: 960px; width: 94vw; max-height: 94vh; }
.modal-box--lg .rich-editor-body { min-height: 360px; }
/* Between the two: grouped-section forms (e.g. Manage User) need more
   breathing room than a flat small pick-one form, but aren't a writing
   surface either. */
.modal-box--md { max-width: 600px; width: 94vw; }
.modal-box h3 { margin-bottom: 16px; }
/* Read-only key/value pairs in a details modal (Rooms booking detail) —
   two columns on wide modals, one on narrow, matching .field-row's own
   breakpoint so both patterns look consistent inside the same modal size. */
.detail-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 14px 20px;
}
.detail-grid > div > strong {
  display: block;
  font-size: 0.75rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  color: var(--color-text-muted);
  margin-bottom: 2px;
}
@media (max-width: 640px) {
  .detail-grid { grid-template-columns: 1fr; }
}
.modal-form {
  display: flex;
  flex-direction: column;
  gap: 14px;
}
/* Puts two field-groups side by side in a wide modal (e.g. compose's
   Deadline + Loop In Staff, Attachments + Approving Supervisor) — pairs
   collapse to a single column on narrow modals/screens. A hidden input
   sibling (e.g. the single-section "From Section" fallback) renders
   nothing and doesn't leave a gap, since [type=hidden] has no box. */
.field-row {
  display: flex;
  gap: 16px;
  align-items: flex-start;
}
.field-row > .field-group { flex: 1 1 0; min-width: 0; }
@media (max-width: 640px) {
  .field-row { flex-direction: column; gap: 14px; }
}
.modal-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 4px;
}

/* Groups a related cluster of fields/rows into a bordered card instead
   of one flat field-group sequence — mirrors .structure-node's card
   treatment, applied inside modals (e.g. Manage User's Profile / Access
   & Status / Section Assignments blocks). */
.modal-section {
  background: var(--color-bg);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 14px 16px;
}
.modal-section + .modal-section { margin-top: 14px; }
.modal-section-title {
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: 0.03em;
  margin-bottom: 10px;
}

/* Label-left / control-right row with a divider between rows, used
   inside .modal-section cards for a list of toggleable settings — same
   divider/:last-child shape as .action-row (dashboard Action Needed). */
.settings-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 0;
  border-bottom: 1px solid var(--color-border);
}
.settings-row:last-child { border-bottom: none; }
.settings-row-label {
  display: flex;
  align-items: center;
  gap: 8px;
  flex: 1 1 180px;
  min-width: 0;
  font-size: 0.875rem;
  color: var(--color-text);
}
.settings-row-control {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-shrink: 0;
}

.assignment-add-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
}
.assignment-add-row .field-select { flex: 1; min-width: 140px; }

.assignment-list {
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.assignment-list li {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  font-size: 0.875rem;
  padding: 6px 0;
}
.assignment-list li + li { border-top: 1px solid var(--color-border); }
/* Spacer between the assignments list and the Add Assignment sub-form
   that now sits in the same .modal-section card. */
.assignment-list + form.modal-form { margin-top: 12px; }

.temp-password-box {
  font-family: monospace;
  font-size: 1.0625rem;
  font-weight: 600;
  letter-spacing: 0.5px;
  background: var(--color-bg);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
  padding: 10px 14px;
  text-align: center;
  color: var(--color-primary-dark);
  user-select: all;
}

/* Spinner variant for use on light panel backgrounds (default spinner
   assumes a colored button background). */
.spinner--dark {
  border-color: rgba(0,0,0,.12);
  border-top-color: var(--color-primary);
}

/* ── Requests: Detail Header & Meta ─────────────────────────────── */
.detail-header {
  display: flex;
  align-items: center;
  gap: 16px;
  margin-bottom: 16px;
  flex-wrap: wrap;
}
.detail-header-title {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.detail-header-title .page-title { margin: 0; }

.detail-meta-panel { margin-bottom: 20px; }
.detail-meta {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: 14px 24px;
}
.detail-meta > div {
  display: flex;
  flex-direction: column;
  gap: 2px;
  font-size: 0.9375rem;
  color: var(--color-text);
}
.detail-meta-label {
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  color: var(--color-text-muted);
}

/* ── Requests: Thread ─────────────────────────────────────────── */
.thread {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-bottom: 20px;
}
.thread-message {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-left: 3px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: 16px 18px;
}
.thread-message--request  { border-left-color: var(--color-secondary); }
.thread-message--response { border-left-color: var(--color-primary); }
.thread-message-header {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
  font-size: 0.875rem;
}
/* Internal-request row header only: section-flow metadata (LTR, reads
   left-to-right) pinned to the start, subject pinned to the end — so a
   Divehi (RTL) subject reads from its own natural right edge instead
   of being glued directly after the metadata text. */
.thread-message-header--split { justify-content: space-between; }
.thread-message-header-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  min-width: 0;
}
.internal-request-subject {
  flex: 1 1 220px;
  min-width: 0;
  overflow-wrap: break-word;
  text-align: left;
}
.internal-request-subject.field-divehi { text-align: right; }
/* Bodies are sanitized rich HTML (RichEditor.sanitize()), not plain
   text — no white-space: pre-wrap here, real block elements (p/ul/
   table) provide their own line breaks. The global reset zeroes all
   margins, so paragraph/list/table spacing has to be restored
   explicitly or a multi-paragraph message renders as one solid block. */
.thread-message-body {
  line-height: 1.6;
  color: var(--color-text);
}
.thread-message-body p,
.thread-message-body ul,
.thread-message-body ol,
.thread-message-body table { margin-bottom: 10px; }
.thread-message-body p:last-child,
.thread-message-body ul:last-child,
.thread-message-body ol:last-child,
.thread-message-body table:last-child { margin-bottom: 0; }
/* Logical padding so markers flip correctly for RTL (Divehi) bodies —
   same fix as .rich-editor-body's lists. */
.thread-message-body ul, .thread-message-body ol { padding-inline-start: 1.5em; }
.thread-message-body blockquote {
  border-inline-start: 3px solid var(--color-border);
  padding-inline-start: 10px;
  margin-inline-start: 8px;
  margin-bottom: 10px;
}
.thread-message-body table { border-collapse: collapse; width: 100%; }
.thread-message-body td, .thread-message-body th {
  border: 1px solid var(--color-border);
  padding: 6px 10px;
}
/* Small kind label ("Request" / "Response") atop each thread message —
   makes the two windows unmistakable at a glance. */
.thread-message-kind {
  font-size: 0.6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--color-text-muted);
  margin-bottom: 6px;
}

/* ── Review comments (supervisor feedback on pending drafts) ──── */
.review-comments {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px dashed var(--color-border);
}
.review-comments-header {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 0.8125rem;
  font-weight: 600;
  margin-bottom: 8px;
}
.review-comments-header .btn { margin-left: auto; }
.review-comment {
  padding: 8px 10px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  margin-bottom: 8px;
  font-size: 0.8125rem;
}
.review-comment--resolved { opacity: .65; }
.review-comment-quote {
  border-inline-start: 3px solid var(--color-warning);
  padding: 2px 10px;
  margin-bottom: 6px;
  color: var(--color-text-muted);
  font-style: italic;
  background: var(--color-warning-bg);
  border-radius: var(--radius-sm);
}
/* Clickable variant: jumps to and highlights the matching passage in
   the reply text above (entry-detail.js's _highlightQuote). */
.review-comment-quote--clickable {
  cursor: pointer;
}
.review-comment-quote--clickable:hover {
  background: var(--color-warning);
  color: var(--color-warning-dark);
}
/* The highlighted passage itself, injected into the reply body as a
   <mark> — brief flash animation draws the eye, then settles into a
   persistent highlight until the next comment is clicked or the view
   reloads. */
mark.quote-highlight {
  background: var(--color-warning);
  color: inherit;
  border-radius: 2px;
  padding: 0 1px;
  animation: quote-highlight-flash 1s ease-out;
}
@keyframes quote-highlight-flash {
  0%, 30%  { background: var(--color-warning-dark); }
  100%     { background: var(--color-warning); }
}
/* Comment bodies are sanitized rich HTML now (RichEditor in the Add
   Comment modal) — restore block spacing the same way
   .thread-message-body does, and flip RTL via .field-divehi when the
   comment is Divehi (auto-detected at render). */
.review-comment-text { line-height: 1.5; }
.review-comment-text p,
.review-comment-text ul,
.review-comment-text ol { margin-bottom: 6px; }
.review-comment-text p:last-child,
.review-comment-text ul:last-child,
.review-comment-text ol:last-child { margin-bottom: 0; }
.review-comment-text ul, .review-comment-text ol { padding-inline-start: 1.5em; }
/* One-time "how to comment" note shown to the reviewing supervisor */
.review-comments-hint {
  display: flex;
  gap: 6px;
  align-items: flex-start;
  padding: 8px 10px;
  margin-bottom: 8px;
  background: var(--color-bg);
  border-radius: var(--radius-md);
  line-height: 1.5;
}
.review-comments-hint i { flex-shrink: 0; margin-top: 2px; }
.review-comment-meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 6px;
  font-size: 0.71875rem;
  color: var(--color-text-light);
}
.review-comment-meta .btn { margin-left: auto; }

.thread-message-actions {
  margin-top: 12px;
  padding-top: 12px;
  border-top: 1px solid var(--color-border);
}

.thread-receipt {
  display: flex;
  align-items: flex-start;
  gap: 6px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--color-border);
  font-size: 0.8125rem;
  color: var(--color-text-muted);
  line-height: 1.5;
}
.thread-receipt i { color: var(--color-success-dark); margin-top: 2px; flex-shrink: 0; }

/* Collapsed-by-default wrapper around a run of .thread-receipt lines
   (received/routed/assigned/sent-by) — same native <details>/<summary>
   pattern as .internal-collab-panel below, but styled to sit inline
   with the thread (no dashed box) since this IS part of the same
   conversation, just history that doesn't need to compete with the
   actual message for attention on every open. */
.activity-log { margin-top: 10px; padding-top: 10px; border-top: 1px dashed var(--color-border); }
.activity-log summary {
  display: flex;
  align-items: center;
  gap: 6px;
  cursor: pointer;
  font-size: 0.8125rem;
  color: var(--color-text-muted);
  list-style: none;
}
.activity-log summary::-webkit-details-marker { display: none; }
.activity-log summary::before {
  content: '';
  width: 0; height: 0;
  border-left: 4px solid var(--color-text-muted);
  border-top: 3px solid transparent;
  border-bottom: 3px solid transparent;
  transition: transform var(--transition);
  flex-shrink: 0;
}
.activity-log[open] summary::before { transform: rotate(90deg); }
/* Scoped to [open] — an unconditional `display: flex` here would beat
   the browser's own `details:not([open]) > *:not(summary){display:none}`
   UA rule (author styles always win over UA styles regardless of the
   open attribute), silently defeating the collapse. Confirmed via a
   real headless-Chromium render: getComputedStyle returned `flex` on a
   CLOSED <details>'s body without this guard. */
.activity-log[open] .activity-log-body { margin-top: 8px; display: flex; flex-direction: column; }
.activity-log-body .thread-receipt { margin-top: 0; padding-top: 0; border-top: none; }
.activity-log-body .thread-receipt + .thread-receipt { margin-top: 8px; }

.thread-approval {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  padding: 10px 14px;
  border-radius: var(--radius-md);
  font-size: 0.8125rem;
  color: var(--color-text-muted);
}
.thread-approval i { margin-top: 2px; flex-shrink: 0; }
.thread-approval--approved { background: var(--color-success-bg); color: var(--color-success-dark); }
.thread-approval--returned { background: var(--color-warning-bg); color: var(--color-warning-dark); }

.detail-actions-panel .panel { margin-bottom: 0; }
.detail-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
}
.detail-actions form.modal-form { width: 100%; }

/* ── Next Step banner (request detail) ───────────────────────── */
/* THE one place that names the current workflow step, whose move it
   is, and hosts the page's single primary action — replaces the old
   bottom-of-round "Actions" panel. minmax(0)/flex-wrap care: titles
   can be long Divehi strings and must wrap, never overflow (the
   dashboard grid lesson). */
.next-step-banner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 12px;
  padding: 14px 18px;
  margin-bottom: 20px;
  border-radius: var(--radius-lg);
  border: 1px solid var(--color-border);
  border-left-width: 4px;
  background: var(--color-surface);
}
.next-step-banner--action  { border-left-color: var(--color-primary); background: var(--color-primary-light); }
.next-step-banner--waiting { border-left-color: var(--color-border); background: var(--color-bg); }
.next-step-banner--done    { border-left-color: var(--color-success); background: var(--color-success-bg); }
/* Slimmer variant for the per-row banner inside an Internal Collaboration
   request (which can stack several rows) — less padding, tighter margin,
   smaller title than the one page-level banner up top. */
.next-step-banner--compact { padding: 9px 13px; margin-bottom: 12px; border-radius: var(--radius); }
.next-step-banner--compact .next-step-title { font-size: 0.875rem; }
.next-step-text { min-width: 0; flex: 1 1 240px; }
.next-step-title { font-weight: 600; overflow-wrap: break-word; }
.next-step-sub {
  font-size: 0.8125rem;
  color: var(--color-text-muted);
  margin-top: 2px;
  overflow-wrap: break-word;
}
.next-step-sub i { margin-right: 4px; }
.next-step-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
}

/* Status + reference + deadline for the case, shown ONCE in the meta
   panel instead of repeated in every round's header/meta rows. */
.case-header-status {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 12px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--color-border);
}
.case-ref { font-size: 0.8125rem; color: var(--color-text-muted); letter-spacing: 0.02em; }

/* ── Rich Text Editor ─────────────────────────────────────────── */
/* No overflow:hidden here — the table dropdown is position:absolute
   inside the toolbar and must escape the editor box (a short/empty
   body would otherwise clip most of the menu unreachably). The corner
   rounding that overflow used to provide moves to the toolbar/body
   radii below. */
.rich-editor {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
}
.rich-editor-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 2px;
  padding: 6px;
  background: var(--color-bg);
  border-bottom: 1px solid var(--color-border);
  border-radius: var(--radius-md) var(--radius-md) 0 0;
}
.rich-editor-toolbar button {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-sm);
  color: var(--color-text);
  transition: background var(--transition);
}
.rich-editor-toolbar button:hover { background: var(--color-primary-light); color: var(--color-primary); }
.rich-editor-toolbar select {
  height: 28px;
  padding: 0 6px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-size: 0.8125rem;
  background: var(--color-surface);
  color: var(--color-text);
}
.rich-editor-sep {
  width: 1px;
  height: 20px;
  background: var(--color-border);
  margin: 0 4px;
}
.rich-editor-color {
  display: flex;
  align-items: center;
  gap: 2px;
  padding: 0 4px;
  height: 28px;
  border-radius: var(--radius-sm);
  color: var(--color-text-muted);
  cursor: pointer;
}
.rich-editor-color:hover { background: var(--color-primary-light); }
.rich-editor-color input[type="color"] {
  width: 18px;
  height: 18px;
  padding: 0;
  border: none;
  background: none;
  cursor: pointer;
}

/* ── Table dropdown (grid picker + row/column operations) ─────── */
.rich-editor-table-wrap { position: relative; }
.rich-editor-table-menu {
  position: absolute;
  top: 32px;
  left: 0;
  z-index: 60;
  width: max-content;
  padding: 10px;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
}
.rich-editor-menu-label {
  font-size: 0.75rem;
  font-weight: 600;
  color: var(--color-text-muted);
  margin-bottom: 6px;
}
.rich-editor-grid {
  display: grid;
  grid-template-columns: repeat(8, 16px);
  gap: 3px;
}
.rich-editor-grid-cell {
  width: 16px;
  height: 16px;
  border: 1px solid var(--color-border);
  border-radius: 3px;
  cursor: pointer;
}
.rich-editor-grid-cell--on {
  background: var(--color-primary-light);
  border-color: var(--color-primary);
}
.rich-editor-grid-size {
  text-align: center;
  font-size: 0.75rem;
  color: var(--color-text-muted);
  margin-top: 6px;
}
.rich-editor-menu-divider {
  height: 1px;
  background: var(--color-border);
  margin: 8px -10px;
}
.rich-editor-table-menu button[data-table-op] {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  padding: 6px 8px;
  border-radius: var(--radius-sm);
  font-size: 0.8125rem;
  color: var(--color-text);
  text-align: left;
  transition: background var(--transition);
}
.rich-editor-table-menu button[data-table-op]:hover:not(:disabled) { background: var(--color-bg); }
.rich-editor-table-menu button[data-table-op]:disabled { opacity: .4; cursor: not-allowed; }
.rich-editor-table-menu button[data-table-op] i { font-size: 1rem; color: var(--color-text-muted); }
.rich-editor-body {
  min-height: 180px;
  max-height: 640px;
  overflow-y: auto;
  border-radius: 0 0 var(--radius-md) var(--radius-md);
  padding: 10px 12px;
  font-size: 0.9375rem;
  line-height: 1.6;
  color: var(--color-text);
  outline: none;
  /* `resize` isn't exclusive to <textarea> — any block box with
     overflow other than visible gets the native drag handle. */
  resize: vertical;
}
.rich-editor-body:focus { background: var(--color-surface); }
.rich-editor-body p, .rich-editor-body ul, .rich-editor-body ol, .rich-editor-body table { margin-bottom: 10px; }
/* padding-inline-start, not padding-left: the physical property put the
   list markers outside the box in RTL (Divehi) — bullets/numbers looked
   like they simply didn't work. The logical property flips with the
   direction set by .field-divehi. */
.rich-editor-body ul, .rich-editor-body ol { padding-inline-start: 1.5em; }
.rich-editor-body blockquote {
  border-inline-start: 3px solid var(--color-border);
  padding-inline-start: 10px;
  margin-inline-start: 8px;
  margin-bottom: 10px;
}
.rich-editor-body table { border-collapse: collapse; width: 100%; }
.rich-editor-body td, .rich-editor-body th {
  border: 1px solid var(--color-border);
  padding: 6px 10px;
  min-width: 40px;
}

/* ── Attachments ──────────────────────────────────────────────── */
.attachments-panel {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px dashed var(--color-border);
}
.attachments-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}
.attachment-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 10px;
  border-radius: var(--radius-full);
  border: 1px solid var(--color-border);
  background: var(--color-bg);
  font-size: 0.8125rem;
  cursor: pointer;
  transition: background var(--transition), border-color var(--transition);
}
.attachment-chip:hover { background: var(--color-primary-light); border-color: var(--color-primary); }

/* Replaces a plain upload button — the whole box is a native drop
   target (dragover/drop bound in js/views/request-detail.js) and,
   since it's a <label> wrapping the actual <input type="file" multiple>,
   also a click-to-browse target with no extra JS needed for that part. */
.attachment-dropzone {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  padding: 14px;
  border: 2px dashed var(--color-border);
  border-radius: var(--radius-md);
  color: var(--color-text-muted);
  font-size: 0.8125rem;
  cursor: pointer;
  text-align: center;
  transition: background var(--transition), border-color var(--transition), color var(--transition);
}
.attachment-dropzone:hover,
.attachment-dropzone--active {
  border-color: var(--color-primary);
  background: var(--color-primary-light);
  color: var(--color-primary);
}
.attachment-dropzone i { font-size: 1.125rem; }
.attachment-browse-link { text-decoration: underline; }

/* ── Prisoner picker (letter compose modal) ──────────────────── */
/* Searchable dropdown over the org's prisoner registry: type-ahead
   input, match list, then a summary card once one is chosen. Bound in
   js/views/prisoner-letters.js. */
.prisoner-picker { position: relative; }
.prisoner-picker-list {
  position: absolute;
  top: calc(100% + 4px);
  left: 0;
  right: 0;
  z-index: 20;
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-md);
  max-height: 260px;
  overflow-y: auto;
}
.prisoner-option {
  display: block;
  width: 100%;
  text-align: left;
  padding: 8px 12px;
  border: none;
  border-bottom: 1px solid var(--color-border);
  background: transparent;
  color: var(--color-text);
  font-size: 0.875rem;
  cursor: pointer;
  transition: background var(--transition);
}
.prisoner-option:last-child { border-bottom: none; }
.prisoner-option:hover { background: var(--color-primary-light); }
.prisoner-option strong { display: block; }
.prisoner-option span { color: var(--color-text-muted); font-size: 0.8125rem; }
.prisoner-selected {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px;
  padding: 10px 12px;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-bg);
  font-size: 0.875rem;
}
.new-prisoner-form {
  margin-top: 10px;
  padding: 12px;
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-bg);
  display: flex;
  flex-direction: column;
  gap: 10px;
}

/* ── Round sections (multi-round conversation grouping) ──────── */
/* Each request/response round-trip renders inside one bordered
   container so it's visually obvious where one round ends and the
   next begins, instead of every round's panels stacking flat and
   looking identical to each other. */
.round-section {
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  background: var(--color-bg);
  margin-bottom: 28px;
  overflow: hidden;
}
.round-section:last-child { margin-bottom: 20px; }

.round-header {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 10px 20px;
  background: var(--color-primary-light);
  border-bottom: 1px solid var(--color-border);
}
.round-badge {
  font-size: 0.6875rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-primary-dark);
  background: var(--color-surface);
  padding: 2px 8px;
  border-radius: var(--radius-full);
}
.round-meta-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  padding: 10px 20px;
  font-size: 0.8125rem;
  color: var(--color-text-muted);
  border-bottom: 1px solid var(--color-border);
}

.round-section > .thread,
.round-summary-body > .thread { margin: 16px 20px; }
.round-section > .internal-collab-panel,
.round-summary-body > .internal-collab-panel { margin: 0 20px 16px; }

/* Older rounds collapse to a one-line summary by default (only the
   latest round stays expanded) — same native <details> chevron recipe
   as .activity-log above. */
.round-section--collapsed > .round-summary {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  padding: 12px 20px;
  cursor: pointer;
  list-style: none;
  font-size: 0.8125rem;
}
.round-section--collapsed > .round-summary::-webkit-details-marker { display: none; }
.round-section--collapsed > .round-summary::before {
  content: '';
  width: 0; height: 0;
  border-left: 4px solid var(--color-text-muted);
  border-top: 3px solid transparent;
  border-bottom: 3px solid transparent;
  transition: transform var(--transition);
  flex-shrink: 0;
}
.round-section--collapsed[open] > .round-summary::before { transform: rotate(90deg); }
.round-section--collapsed[open] > .round-summary { border-bottom: 1px solid var(--color-border); }

/* ── Internal Collaboration (org-only) ───────────────────────── */
/* A native <details>/<summary> disclosure — deliberately styled to
   look nothing like the surrounding .thread-message/.panel boxes
   (dashed border, muted background, lock icon) so it can never be
   mistaken for part of the actual external request/response
   conversation above it. */
.internal-collab-panel {
  background: var(--color-surface);
  border: 1px dashed var(--color-border);
  border-radius: var(--radius-md);
}
.internal-collab-panel summary {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  padding: 10px 14px;
  cursor: pointer;
  font-weight: 600;
  font-size: 0.9375rem;
  color: var(--color-text-muted);
  list-style: none;
}
.internal-collab-panel summary::-webkit-details-marker { display: none; }
.internal-collab-panel summary::before {
  content: '';
  width: 0; height: 0;
  border-left: 5px solid var(--color-text-muted);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform var(--transition);
  flex-shrink: 0;
}
.internal-collab-panel[open] summary::before { transform: rotate(90deg); }
/* Scoped to [open] — an unconditional `display: flex` here beats the
   browser's own `details:not([open]) > *:not(summary){display:none}`
   UA rule (author styles always win over UA styles regardless of the
   open attribute), so a collapsed panel (entry.internalRequestDetails
   is empty and canStart is true — see _renderInternalCollab) was
   silently rendering its body anyway instead of staying closed.
   Confirmed via a real headless-Chromium render before this fix:
   getComputedStyle returned `block` on a CLOSED <details>'s body. */
.internal-collab-panel[open] .internal-collab-body {
  padding: 12px 14px 14px;
  border-top: 1px dashed var(--color-border);
  display: flex;
  flex-direction: column;
  gap: 10px;
}
.internal-request-row {
  background: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: 12px 14px;
  margin-bottom: 10px;
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.internal-request-row:last-child { margin-bottom: 0; }
/* Applied directly to each reply's .thread-message (in place of a
   .internal-request-replies wrapper div that existed only to carry
   this padding override) — .internal-request-row above is already a
   flex column with its own gap, so a flattened reply still gets the
   same spacing as a direct child. */
.thread-message--compact { padding: 10px 12px; margin-bottom: 8px; }
.thread-message--compact:last-child { margin-bottom: 0; }
