/* SiteVerified Ghana — Lightweight stylesheet. No framework, no build step. */
:root {
  --sv-green: #0d4d3c;
  --sv-green-dark: #083528;
  --sv-gold: #c8952b;
  --sv-bg: #f7f6f2;
  --sv-text: #1e2422;
  --sv-muted: #5c6b66;
  --sv-border: #dfe3e0;
  --sv-white: #ffffff;
  --sv-danger: #a3312a;
  --sv-success: #1e6b47;
  --radius: 8px;
  /* Sampled from the logo mark — used on the public marketing page for a brand-matched look */
  --sv-navy: #0b2544;
  --sv-navy-dark: #071a30;
  --sv-logo-green: #3f9130;
  --sv-steel: #78838c;
  /* Dashboard-only surface tokens (cards, tables, inputs, nav highlight).
     Light values here match what every consumer used to hard-code, so this
     introduction changes nothing visually — only [data-theme="dark"] below
     actually repaints anything. */
  --sv-surface: #ffffff;
  --sv-surface-alt: #f0efe9;
  --sv-accent-tint: #eef4f0;
  --sv-input-bg: #ffffff;
  font-size: 16px;
}

/* =====================================================================
   DARK THEME — the dashboard's default look.
   Scoped to [data-theme="dark"] on <html>, which header.php only ever sets
   for a signed-in user (see Auth::theme()); the public marketing site and
   the auth screens never carry this attribute, so they keep their existing
   fixed styling untouched regardless of a user's dashboard preference.
   Because almost every dashboard rule above already reads its colour from
   one of these custom properties, redefining the handful below recolours
   the sidebar, tables, forms, cards, tabs, links and buttons in one pass;
   the few components that use a literal one-off colour (badges, alerts,
   chart marks) get their own explicit override further down.
   ===================================================================== */
:root[data-theme="dark"] {
  color-scheme: dark;
  --sv-green: #34d399;
  --sv-green-dark: #2aa374;
  --sv-gold: #e0a83c;
  --sv-bg: #0f1613;
  --sv-text: #e8ece9;
  --sv-muted: #93a19a;
  --sv-border: #29352f;
  --sv-white: var(--sv-surface);
  --sv-danger: #f2685f;
  --sv-success: #33c281;
  --sv-surface: #17211d;
  --sv-surface-alt: #1e2a24;
  --sv-accent-tint: rgba(52, 211, 153, .14);
  --sv-input-bg: #121b17;
}

* {
  box-sizing: border-box
}

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  background: var(--sv-bg);
  color: var(--sv-text);
  line-height: 1.55;
}

a {
  color: var(--sv-green);
  text-decoration: none
}

a:hover {
  text-decoration: underline
}

img {
  max-width: 100%;
  display: block
}

.container {
  max-width: 1100px;
  margin: 0 auto;
  padding: 0 20px
}

/* Navbar */
.navbar {
  background: var(--sv-surface);
  color: var(--sv-green);
  padding: 14px 0;
  border-bottom: 1px solid var(--sv-border)
}

/* Dashboard shell: the header stays fixed at the top of the viewport so it
   never scrolls away, matching the fixed sidebar below (see .app-dashboard
   .sidebar). Only pages that render the sidebar get this treatment — the
   public marketing site and auth screens keep the header in normal flow. */
body.app-dashboard .navbar {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: 1000
}

body.app-dashboard .app-shell {
  margin-top: 65px
}

.navbar .container {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px
}

.navbar .brand {
  display: flex;
  align-items: center;
  color: var(--sv-green);
  font-weight: 700;
  font-size: 1.2rem;
  letter-spacing: .2px
}

.navbar .brand span {
  color: var(--sv-gold)
}

.brand-logo {
  height: 34px;
  width: auto;
  display: block
}

.auth-logo {
  display: block;
  height: 64px;
  width: auto;
  margin: 0 auto 18px
}

.navbar nav a {
  color: var(--sv-green);
  margin-left: 18px;
  font-size: .95rem
}

.navbar nav a.btn {
  background: var(--sv-gold);
  color: #241a04;
  padding: 4px 6px;
  border-radius: var(--radius);
  margin-left: 18px
}

/* Hero — navy-to-green diagonal, matching the logo's shield (navy) + roof (green) */
.hero {
  position: relative;
  overflow: hidden;
  padding: 60px 0 50px;
  background: linear-gradient(135deg, var(--sv-navy) 0%, var(--sv-navy-dark) 55%, #0a2e22 100%);
  color: #fff
}

.hero::before {
  content: '';
  position: absolute;
  width: 620px;
  height: 620px;
  background: radial-gradient(circle, rgba(63, 145, 48, .35) 0%, transparent 70%);
  top: -220px;
  left: -160px;
  z-index: 0;
  pointer-events: none
}

.hero h1 {
  font-size: 2rem;
  line-height: 1.25;
  margin: 0 0 16px
}

.hero p {
  color: #d8e0ea;
  font-size: 1.05rem
}

.hero .cta {
  margin-top: 24px;
  display: flex;
  gap: 12px;
  flex-wrap: wrap
}

.hero-grid {
  display: flex;
  align-items: center;
  gap: 44px;
  position: relative;
  z-index: 1
}

.hero-copy {
  flex: 1 1 480px;
  min-width: 0
}

.hero-media {
  flex: 1 1 420px;
  min-width: 0
}

.hero-media img {
  width: 100%;
  height: auto;
  border-radius: 12px;
  box-shadow: 0 20px 50px rgba(0, 0, 0, .45)
}

@media(max-width:860px) {
  .hero-grid {
    flex-direction: column-reverse
  }

  /* flex-basis is a main-axis measurement, and column-reverse just rotated
     the main axis from horizontal to vertical — without this reset, the
     480px/420px basis meant for desktop COLUMN WIDTHS gets reused as a
     minimum HEIGHT for each stacked block, reserving far more vertical
     space than the actual image/copy content needs and leaving a large
     blank gap between them. */
  .hero-copy,
  .hero-media {
    flex: 1 1 auto
  }

  .hero h1 {
    font-size: 1.6rem
  }
}

@media(max-width:560px) {
  .hero {
    padding: 32px 0 36px
  }

  .hero-grid {
    gap: 20px
  }

  .hero::before {
    width: 320px;
    height: 320px;
    top: -110px;
    left: -100px
  }

  .hero h1 {
    font-size: 1.35rem;
    margin-bottom: 12px
  }

  .hero p {
    font-size: .96rem
  }

  .hero .cta {
    margin-top: 20px
  }

  .hero-media img {
    border-radius: 10px
  }
}

.hero-watermark {
  position: absolute;
  top: 50%;
  right: -60px;
  transform: translateY(-50%);
  width: 520px;
  max-width: 60vw;
  height: auto;
  opacity: .08;
  filter: brightness(0) invert(1);
  pointer-events: none;
  z-index: 0
}

/* Section heading accent — public marketing sections only */
#problem h2,
#solution h2,
#pricing h2,
#where h2,
#cta-final h2 {
  position: relative;
  padding-bottom: 14px
}

#problem h2::after,
#solution h2::after,
#pricing h2::after,
#where h2::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  width: 56px;
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--sv-navy), var(--sv-logo-green))
}

#cta-final h2::after {
  content: '';
  position: absolute;
  left: 50%;
  bottom: 0;
  transform: translateX(-50%);
  width: 56px;
  height: 4px;
  border-radius: 2px;
  background: linear-gradient(90deg, var(--sv-logo-green), var(--sv-gold))
}

/* Center all body copy in these sections/cards — hero is intentionally left out */
#problem,
#solution,
#pricing,
#where {
  text-align: center
}

#problem .section-lead,
#solution .section-lead,
#pricing .section-lead,
#where .section-lead {
  margin-left: auto;
  margin-right: auto
}

.location-chips {
  justify-content: center
}

/* Card icons (problem / solution / pricing sections) */
.card-icon {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 46px;
  height: 46px;
  border-radius: 12px;
  background: rgba(13, 77, 60, .08);
  margin: 0 auto 14px
}

.card-icon svg {
  width: 24px;
  height: 24px;
  color: var(--sv-green)
}

.card-icon img {
  width: 24px;
  height: 24px;
  object-fit: contain
}

/* Buttons attached to a body section (any layout — not just the CTA banner) */
.section-buttons {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 12px;
  margin-top: 20px
}

#problem .card-icon {
  background: rgba(11, 37, 68, .08)
}

#problem .card-icon svg {
  color: var(--sv-navy)
}

#solution .card-icon,
#pricing .card-icon {
  background: rgba(63, 145, 48, .10)
}

#solution .card-icon svg,
#pricing .card-icon svg {
  color: var(--sv-logo-green)
}

/* Featured pricing tier */
.card-featured {
  border: 2px solid var(--sv-logo-green);
  position: relative;
  box-shadow: 0 12px 30px rgba(11, 37, 68, .14)
}

.card-featured::before {
  content: 'Most popular';
  position: absolute;
  top: -13px;
  left: 22px;
  background: var(--sv-logo-green);
  color: #fff;
  font-size: .7rem;
  font-weight: 700;
  padding: 4px 10px;
  border-radius: 999px;
  letter-spacing: .3px
}

/* Location chips — "Where we operate" */
.location-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 10px
}

.location-chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 8px 14px;
  border-radius: 999px;
  font-size: .88rem;
  font-weight: 600
}

.location-chip svg {
  width: 15px;
  height: 15px;
  flex-shrink: 0
}

.location-chip.active {
  background: rgba(11, 37, 68, .08);
  color: var(--sv-navy)
}

.location-chip.upcoming {
  background: #f0efe9;
  color: var(--sv-steel)
}

.location-chip small {
  font-weight: 500;
  opacity: .75
}

.btn {
  display: inline-block;
  padding: 11px 22px;
  border-radius: var(--radius);
  font-weight: 600;
  border: 2px solid transparent;
  cursor: pointer;
  font-size: .95rem
}

.btn-primary {
  background: var(--sv-gold);
  color: #241a04
}

/* Outline button.
   Was hard-coded to white, which is correct on the hero and invisible
   everywhere else the app uses it -- in light theme that meant white text on
   a white card, so "Export CSV", "Open", "Edit" and every settings action
   simply vanished. --sv-text resolves to near-white in dark and near-black in
   light, so this keeps the dark app looking exactly as it did while making
   the light one legible. The hero is dark whatever the theme, so it keeps
   white explicitly below. */
.btn-outline {
  background: transparent;
  color: var(--sv-text);
  border-color: var(--sv-text)
}

.btn-outline:hover {
  color: var(--sv-green);
  border-color: var(--sv-green);
  text-decoration: none
}

.hero .btn-outline {
  color: #fff;
  border-color: #fff
}

.hero .btn-outline:hover {
  color: #fff;
  border-color: #fff;
  opacity: .85
}

.btn-solid {
  background: var(--sv-green);
  color: #fff
}

.btn-solid:hover {
  background: var(--sv-green-dark)
}

.btn-danger {
  background: var(--sv-danger);
  color: #fff
}

/* Sections */
section {
  padding: 48px 0
}

h2 {
  font-size: 1.5rem;
  margin-bottom: 8px
}

.section-lead {
  color: var(--sv-muted);
  max-width: 640px;
  margin-bottom: 28px
}

#cta-final {
  background: linear-gradient(135deg, var(--sv-navy) 0%, var(--sv-navy-dark) 100%);
  color: #fff
}

#cta-final h2 {
  color: #fff
}

#cta-final .section-lead {
  color: #cfd8e3
}

#cta-final+.footer {
  margin-top: 0
}

/* Same reasoning as the CTA-section case above, applied to every dashboard/
   portal page: .main already carries its own bottom padding, so the footer's
   generic 50px margin-top on top of that was the real source of the
   "gap before the footer" — not .app-shell's height, which is already
   content-driven (see .app-shell above).
   General-sibling (~), not adjacent-sibling (+): some pages put a <script>
   tag between the closing </div> of .app-shell and the footer include, which
   silently defeats a "+" selector — this still matches regardless of what
   else sits in between. */
.app-shell~.footer {
  margin-top: 0
}

/* Dark green section background needs light text instead of the default dark/muted colors */
#solution h2 {
  color: #fff
}

#solution .section-lead {
  color: #fff
}

#solution h2::after {
  background: linear-gradient(90deg, var(--sv-gold), #fff)
}

.grid {
  display: grid;
  gap: 20px
}

.grid-3 {
  grid-template-columns: repeat(3, 1fr)
}

.grid-2 {
  grid-template-columns: repeat(2, 1fr)
}

@media(max-width:800px) {

  .grid-3,
  .grid-2 {
    grid-template-columns: 1fr
  }
}

.card {
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-radius: var(--radius);
  padding: 22px
}

.card h3 {
  margin-top: 0;
  font-size: 1.05rem
}

.table-wrap {
  overflow-x: auto
}

table {
  width: 100%;
  border-collapse: collapse;
  background: var(--sv-surface)
}

th,
td {
  padding: 10px 12px;
  border-bottom: 1px solid var(--sv-border);
  text-align: left;
  font-size: .92rem
}

th {
  background: var(--sv-surface-alt);
  font-weight: 600
}

/* A row that navigates on click in place of a removed "View"/"Open" cell —
   see assets/js/row-link.js. Controls inside it (buttons, forms, links to
   somewhere else) stay independently clickable. */
tbody tr.row-link {
  cursor: pointer
}

tbody tr.row-link:hover {
  background: var(--sv-surface-alt)
}

tbody tr.row-link:focus-visible {
  outline: 2px solid var(--sv-green);
  outline-offset: -2px
}

.badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 999px;
  font-size: .78rem;
  font-weight: 600
}

/* Square variant: same colour tokens as .badge, but a label/tag shape
   rather than a status pill (used where the text reads as a category,
   e.g. the audit log's "What happened" column). */
.badge-square {
  border-radius: 4px
}

.badge-pending {
  background: #f0e6cf;
  color: #7a5a10
}

.badge-scheduled {
  background: #dde8f5;
  color: #274d7a
}

.badge-in_progress {
  background: #e4e0f7;
  color: #4a3a8c
}

.badge-met {
  background: #dcf1e4;
  color: var(--sv-success)
}

.badge-not_met {
  background: #f6dcd9;
  color: var(--sv-danger)
}

.badge-cancelled {
  background: #eee;
  color: #666
}

body.auth-page {
  background: var(--sv-green-dark)
}

.form-card {
  max-width: 460px;
  margin: 40px auto;
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-radius: var(--radius);
  padding: 32px
}

.form-card h1 {
  font-size: 1.4rem;
  margin-top: 0
}

label {
  display: block;
  font-weight: 600;
  font-size: .88rem;
  margin: 14px 0 6px
}

input[type=text],
input[type=email],
input[type=password],
input[type=tel],
input[type=date],
select,
textarea {
  width: 100%;
  padding: 10px 12px;
  border: 1px solid var(--sv-border);
  border-radius: 6px;
  font-size: .95rem;
  background: var(--sv-input-bg);
  color: var(--sv-text);
}

textarea {
  resize: vertical;
  min-height: 90px
}

.help-text {
  font-size: .8rem;
  color: var(--sv-muted);
  margin-top: 4px
}

/* First name + last name. To the person filling the form they read as one
   thing, so they sit on one row rather than eating two. Column gap only —
   the labels already carry the vertical rhythm. */
.field-pair {
  display: grid;
  grid-template-columns: 1fr 1fr;
  column-gap: 16px
}

@media(max-width:600px) {
  .field-pair {
    grid-template-columns: 1fr
  }
}

.alert {
  padding: 12px 14px;
  border-radius: 6px;
  margin-bottom: 16px;
  font-size: .9rem
}

.alert-error {
  background: #fbe4e1;
  color: var(--sv-danger);
  border: 1px solid #f0bdb6
}

.alert-success {
  background: #e1f3e7;
  color: var(--sv-success);
  border: 1px solid #b8e0c5
}

.alert-warning {
  background: #f8ecd4;
  color: #7a5a10;
  border: 1px solid #edd8a3
}

.alert-warning.alert-critical {
  background: #fbe4e1;
  color: var(--sv-danger);
  border-color: #f0bdb6
}

/* Storage quota bar (Google Drive status) */
.quota-bar {
  background: #eee;
  border-radius: 999px;
  height: 10px;
  overflow: hidden;
  margin: 8px 0
}

.quota-bar-fill {
  height: 100%;
  background: var(--sv-success);
  border-radius: 999px
}

.quota-bar-fill.level-warning {
  background: var(--sv-gold)
}

.quota-bar-fill.level-critical {
  background: var(--sv-danger)
}

/* First-run Google Drive setup modal */
.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(15, 25, 20, .55);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 20px;
  z-index: 1000
}

.modal-box {
  background: var(--sv-surface);
  border-radius: var(--radius);
  max-width: 520px;
  width: 100%;
  padding: 30px;
  box-shadow: 0 20px 50px rgba(0, 0, 0, .25)
}

.modal-box h2 {
  margin-top: 0
}

.modal-actions {
  display: flex;
  gap: 12px;
  margin-top: 22px;
  flex-wrap: wrap
}

/* No forced min-height here: the shell should size to its own content so the
   footer sits right after it. Short-content pages just have a shorter page —
   stretching this to fill the viewport was leaving a large blank gap before
   the footer on every page that wasn't already viewport-tall. */
.app-shell {
  display: flex;
}

.sidebar {
  width: 230px;
  background: var(--sv-surface);
  border-right: 1px solid var(--sv-border);
  flex-shrink: 0;
  padding: 22px 0;
  display: flex;
  flex-direction: column;
  transition: width .18s ease
}

/* Fixed in place under the fixed header, so it stays put while .main
   scrolls; .main and the footer are offset by its width below so they never
   render underneath it (see .main and .app-shell + .footer). */
body.app-dashboard .sidebar {
  position: fixed;
  top: 65px;
  left: 0;
  bottom: 0;
  overflow-y: auto;
  z-index: 900
}

.sidebar-toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  width: 100%;
  background: none;
  border: none;
  cursor: pointer;
  padding: 0 22px 14px;
  color: var(--sv-muted);
  font: inherit;
  font-size: .8rem;
  font-weight: 600
}

.sidebar-toggle:hover {
  color: var(--sv-green)
}

.sidebar-toggle-icon {
  flex-shrink: 0;
  width: 20px;
  height: 20px;
  border: 1px solid var(--sv-border);
  border-radius: 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: .7rem;
  line-height: 1;
  transition: transform .18s ease
}

.sidebar-links {
  flex: 1
}

.sidebar a {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 22px;
  color: var(--sv-text);
  font-size: .92rem
}

.sidebar a:hover,
.sidebar a.active {
  background: var(--sv-accent-tint);
  color: var(--sv-green);
  text-decoration: none;
  font-weight: 600
}

/* Hidden by default — expanded rail is text-only, same as before icons were
   added. It only appears once the rail collapses to icon-only (below). */
.sidebar-icon {
  display: none;
  flex-shrink: 0;
  width: 20px;
  height: 20px
}

.sidebar-icon svg {
  width: 100%;
  height: 100%
}

.sidebar-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap
}

.sidebar a.sidebar-sub .sidebar-icon {
  width: 17px;
  height: 17px
}

.sidebar-user {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 22px;
  padding: 14px 22px;
  border-top: 1px solid var(--sv-border);
  text-decoration: none;
  color: inherit
}

.sidebar-user:hover {
  background: var(--sv-surface-alt)
}

/* Collapsed: icon-only rail — only the labels (and the tier heading, which is
   text-only) hide; each link keeps its icon, so the rail still reads as a
   real menu instead of going blank. Links/avatar stay clickable and
   title-tipped either way. */
.app-shell.sidebar-collapsed .sidebar {
  width: 64px
}

.app-shell.sidebar-collapsed .sidebar-toggle {
  justify-content: center;
  padding: 0 0 14px
}

.app-shell.sidebar-collapsed .sidebar-toggle-icon {
  transform: rotate(180deg)
}

.app-shell.sidebar-collapsed .sidebar-toggle-label,
.app-shell.sidebar-collapsed .sidebar-label,
.app-shell.sidebar-collapsed .sidebar-tier,
.app-shell.sidebar-collapsed .sidebar-user-meta {
  display: none
}

.app-shell.sidebar-collapsed .sidebar a {
  justify-content: center;
  padding: 10px 0
}

.app-shell.sidebar-collapsed .sidebar-icon {
  display: block
}

.app-shell.sidebar-collapsed .sidebar-user {
  justify-content: center;
  padding: 14px 0
}

.sidebar-user-avatar {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  border-radius: 50%;
  background: var(--sv-green);
  color: #fff;
  font-size: .8rem;
  font-weight: 700;
  letter-spacing: .3px;
  display: flex;
  align-items: center;
  justify-content: center
}

.sidebar-user-meta {
  min-width: 0;
  display: flex;
  flex-direction: column;
  line-height: 1.3
}

.sidebar-user-name {
  font-size: .88rem;
  font-weight: 600;
  color: var(--sv-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis
}

.sidebar-user-role {
  font-size: .76rem;
  color: var(--sv-muted)
}

.profile-fields {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 16px 24px;
  margin: 0
}

.profile-field dt {
  font-size: .78rem;
  color: var(--sv-muted);
  margin-bottom: 3px
}

.profile-field dd {
  margin: 0;
  font-weight: 600
}

.profile-field-wide {
  grid-column: 1 / -1
}

@media(max-width:600px) {
  .profile-fields {
    grid-template-columns: 1fr
  }
}

.main {
  flex: 1;
  padding: 28px;
  min-width: 0
}

body.app-dashboard .main {
  margin-left: 230px;
  transition: margin-left .18s ease
}

body.app-dashboard .app-shell.sidebar-collapsed .main {
  margin-left: 64px
}

/* The footer sits after .app-shell in normal flow (not inside .main), so it
   needs the same offset or the fixed sidebar would sit on top of it.
   Targets .footer directly rather than ".app-shell + .footer": several
   pages emit a page-specific <script> between the app-shell's closing tag
   and the footer include, which breaks the adjacent-sibling match and
   silently drops this offset (the sidebar then overlapped the footer
   text on those pages). A body.app-dashboard page always renders the
   fixed sidebar, so scoping on the body class alone is enough. */
body.app-dashboard .footer {
  margin-left: 230px;
  transition: margin-left .18s ease
}

body.app-dashboard .app-shell.sidebar-collapsed ~ .footer {
  margin-left: 64px
}

@media(max-width:760px) {
  /* The sidebar stays exactly what it is on desktop -- a vertical icon rail,
     not a different horizontal-bar widget -- so no .app-shell/.sidebar
     structural overrides are needed for the default (collapsed) look at all;
     the same .sidebar-collapsed rules used on desktop already apply here.
     sidebar-toggle.js defaults a never-toggled sidebar to collapsed at this
     width specifically, which is what keeps it out of the way on first
     visit. The one thing that DOES need to change for a narrow screen is
     what happens when it's expanded: on desktop that widens the rail in
     normal flow (there's room to spare); here it instead becomes a fixed
     overlay so .main is never pushed, resized, or repositioned. */
  .app-shell:not(.sidebar-collapsed) .sidebar {
    width: 78%;
    max-width: 300px;
    box-shadow: 6px 0 28px rgba(0, 0, 0, .35);
    z-index: 1000
  }

  .app-shell:not(.sidebar-collapsed) .sidebar a {
    justify-content: flex-start;
    white-space: normal
  }

  /* On a narrow screen the expanded sidebar is a full overlay (above) — it
     must never push or resize .main/.footer the way the desktop expanded
     rail does, so both stay pinned at the collapsed rail's width regardless
     of expanded/collapsed state. */
  body.app-dashboard .app-shell .main {
    margin-left: 64px
  }

  body.app-dashboard .footer {
    margin-left: 64px
  }
}

.footer {
  background: var(--sv-surface);
  color: var(--sv-green-dark);
  border-top: 1px solid var(--sv-border);
  padding: 28px 0;
  margin-top: 50px;
  font-size: .85rem;
  text-align: center
}

.footer a {
  color: var(--sv-green-dark)
}

.footer-brand {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 10px
}

.footer-logo {
  height: 22px;
  width: auto;
  flex-shrink: 0
}

.muted {
  color: var(--sv-muted)
}

.stat {
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-radius: var(--radius);
  padding: 18px
}

.stat .num {
  font-size: 1.7rem;
  font-weight: 700;
  color: var(--sv-green)
}

.stat .label {
  font-size: .82rem;
  color: var(--sv-muted)
}

/* =====================================================================
   CLIENT PORTAL
   Everything below is used only by the client-facing portal pages
   (portal*.php). Same tokens and flat, framework-free style as above.
   ===================================================================== */

.grid-4 {
  grid-template-columns: repeat(4, 1fr)
}

@media(max-width:900px) {
  .grid-4 {
    grid-template-columns: repeat(2, 1fr)
  }
}

@media(max-width:520px) {
  .grid-4 {
    grid-template-columns: 1fr
  }
}

.portal-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 18px
}

.section-heading {
  font-size: 1.05rem;
  margin: 30px 0 14px
}

/* "New" is the one badge meant to catch the eye across a busy page. */
.badge-new {
  background: var(--sv-gold);
  color: #fff;
  box-shadow: 0 0 0 3px rgba(200, 149, 43, .16)
}

/* --- Attention panel (unread reports / flagged issues) --- */
.attention-panel {
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-left: 4px solid var(--sv-gold);
  border-radius: var(--radius);
  padding: 16px 20px;
  margin-bottom: 8px
}

.attention-panel h2 {
  font-size: 1rem;
  margin: 0 0 10px
}

.attention-panel ul {
  margin: 0;
  padding-left: 0;
  list-style: none
}

.attention-panel li {
  padding: 7px 0;
  font-size: .93rem;
  line-height: 1.5
}

.attention-panel li+li {
  border-top: 1px solid var(--sv-border)
}

.attention-panel .badge {
  margin-right: 8px
}

/* --- Project cards --- */
.project-list {
  display: grid;
  gap: 16px;
  grid-template-columns: repeat(auto-fill, minmax(min(330px, 100%), 1fr))
}

@media(max-width:520px) {
  .project-list {
    grid-template-columns: 1fr
  }
}

.project-card {
  display: block;
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-radius: var(--radius);
  padding: 18px 20px;
  color: inherit;
  transition: border-color .15s, box-shadow .15s
}

.project-card:hover {
  text-decoration: none;
  border-color: var(--sv-green);
  box-shadow: 0 2px 10px rgba(13, 77, 60, .08)
}

.project-card-top {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 12px
}

.project-card h3 {
  margin: 0;
  font-size: 1.05rem;
  color: var(--sv-green)
}

.project-card p {
  margin: 3px 0 0;
  font-size: .86rem
}

.project-card-flags {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
  justify-content: flex-end
}

/* --- Progress bar --- */
.progress {
  background: #e8ebe9;
  border-radius: 999px;
  height: 8px;
  overflow: hidden;
  margin: 16px 0 0
}

.progress-lg {
  height: 12px
}

.progress-bar {
  background: var(--sv-green);
  height: 100%;
  border-radius: 999px;
  transition: width .3s
}

.progress-caption {
  font-size: .82rem;
  color: var(--sv-muted);
  margin: 7px 0 0
}

/* --- Key/value detail rows --- */
.kv {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 10px 16px;
  margin: 16px 0 0
}

.kv>div {
  min-width: 0
}

.kv dt {
  font-size: .74rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--sv-muted)
}

.kv dd {
  margin: 2px 0 0;
  font-size: .88rem;
  font-weight: 600;
  overflow-wrap: anywhere
}

/* --- Tabs (server-rendered links; no JS, per the app CSP) --- */
.tabs {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--sv-border);
  margin: 0 0 22px;
  overflow-x: auto
}

.tabs a {
  padding: 10px 16px;
  font-size: .92rem;
  font-weight: 600;
  color: var(--sv-muted);
  border-bottom: 2px solid transparent;
  white-space: nowrap
}

.tabs a:hover {
  text-decoration: none;
  color: var(--sv-green)
}

.tabs a.active {
  color: var(--sv-green);
  border-bottom-color: var(--sv-green)
}

.tabs .badge {
  margin-left: 5px
}

.tab-count {
  margin-left: 5px;
  font-weight: 400;
  opacity: .65
}

/* --- Milestone tracker --- */
.milestone-list {
  list-style: none;
  margin: 0;
  padding: 0
}

.milestone {
  position: relative;
  display: flex;
  gap: 16px;
  padding: 0 0 22px 0
}

/* The connecting line, drawn behind the markers; the last item stops it. */
.milestone::before {
  content: "";
  position: absolute;
  left: 7px;
  top: 18px;
  bottom: 0;
  width: 2px;
  background: var(--sv-border)
}

.milestone:last-child::before {
  display: none
}

.milestone-marker {
  flex: 0 0 16px;
  width: 16px;
  height: 16px;
  margin-top: 4px;
  border-radius: 50%;
  background: var(--sv-surface);
  border: 3px solid var(--sv-border);
  position: relative;
  z-index: 1
}

.milestone.state-met .milestone-marker {
  border-color: var(--sv-success);
  background: var(--sv-success)
}

.milestone.state-not_met .milestone-marker {
  border-color: var(--sv-danger);
  background: var(--sv-danger)
}

.milestone.state-in_progress .milestone-marker {
  border-color: var(--sv-gold)
}

.milestone.state-scheduled .milestone-marker {
  border-color: #274d7a
}

.milestone-body {
  flex: 1;
  min-width: 0;
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-radius: var(--radius);
  padding: 14px 16px
}

.milestone-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap
}

.milestone-head h3 {
  margin: 0;
  font-size: .98rem
}

.milestone-body p {
  margin: 6px 0 0;
  font-size: .84rem
}

/* --- Visit history --- */
.visit-list {
  list-style: none;
  margin: 0;
  padding: 0
}

.visit-item {
  display: flex;
  gap: 16px;
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-radius: var(--radius);
  padding: 16px 18px;
  margin-bottom: 12px
}

.visit-item.is-unread {
  border-left: 4px solid var(--sv-gold)
}

.visit-date {
  flex: 0 0 58px;
  text-align: center;
  padding-top: 2px
}

.visit-day {
  display: block;
  font-size: 1.6rem;
  font-weight: 700;
  line-height: 1;
  color: var(--sv-green)
}

.visit-month {
  display: block;
  font-size: .72rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--sv-muted)
}

.visit-body {
  flex: 1;
  min-width: 0
}

.visit-head {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap
}

.visit-head h3 {
  margin: 0;
  font-size: 1rem
}

.visit-body p {
  margin: 6px 0 0;
  font-size: .86rem
}

.visit-excerpt {
  color: var(--sv-text);
  border-left: 2px solid var(--sv-border);
  padding-left: 12px
}

/* --- Document library --- */
.doc-grid {
  list-style: none;
  margin: 16px 0 0;
  padding: 0;
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(min(230px, 100%), 1fr))
}

.doc-card {
  position: relative;
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-radius: var(--radius);
  transition: border-color .15s
}

.doc-card:hover {
  border-color: var(--sv-green)
}

.doc-card.is-unread {
  border-left: 4px solid var(--sv-gold)
}

.doc-card>a {
  display: block;
  padding: 14px 16px;
  color: inherit
}

.doc-card>a:hover {
  text-decoration: none
}

.doc-kind {
  display: inline-block;
  font-size: .68rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  padding: 2px 8px;
  border-radius: 4px;
  margin-bottom: 8px
}

.doc-kind-photo {
  background: #dde8f5;
  color: #274d7a
}

.doc-kind-video {
  background: #e4e0f7;
  color: #4a3a8c
}

.doc-kind-report {
  background: #f0e6cf;
  color: #7a5a10
}

.doc-name {
  display: block;
  font-weight: 600;
  font-size: .92rem;
  color: var(--sv-green);
  overflow-wrap: anywhere
}

.doc-meta {
  display: block;
  font-size: .78rem;
  color: var(--sv-muted);
  margin-top: 3px
}

.doc-flag {
  position: absolute;
  top: 10px;
  right: 10px
}

.doc-note {
  margin: 0;
  padding: 0 16px 12px;
  font-size: .74rem
}

/* --- Activity timeline --- */
.timeline {
  list-style: none;
  margin: 0;
  padding: 0
}

.timeline-item {
  position: relative;
  display: flex;
  gap: 14px;
  padding-bottom: 20px
}

.timeline-item::before {
  content: "";
  position: absolute;
  left: 5px;
  top: 14px;
  bottom: 0;
  width: 2px;
  background: var(--sv-border)
}

.timeline-item:last-child::before {
  display: none
}

.timeline-dot {
  flex: 0 0 12px;
  width: 12px;
  height: 12px;
  margin-top: 4px;
  border-radius: 50%;
  background: var(--sv-border);
  position: relative;
  z-index: 1
}

.timeline-item.kind-status_change .timeline-dot {
  background: var(--sv-green)
}

.timeline-item.kind-attachment_added .timeline-dot {
  background: var(--sv-gold)
}

.timeline-body {
  flex: 1;
  min-width: 0
}

.timeline-when {
  margin: 0;
  font-size: .76rem;
  color: var(--sv-muted)
}

.timeline-what {
  margin: 3px 0 0;
  font-size: .9rem
}

.timeline-note {
  margin: 6px 0 0;
  font-size: .86rem;
  color: var(--sv-muted);
  border-left: 2px solid var(--sv-border);
  padding-left: 12px
}

/* --- Single report view --- */
.report-head {
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-left: 4px solid var(--sv-green);
  border-radius: var(--radius);
  padding: 18px 22px;
  margin-bottom: 18px
}

.report-head.verdict-not_met {
  border-left-color: var(--sv-danger)
}

.report-head h1 {
  margin: 4px 0 10px;
  font-size: 1.4rem
}

.report-eyebrow {
  margin: 0;
  font-size: .74rem;
  text-transform: uppercase;
  letter-spacing: .06em;
  color: var(--sv-muted);
  font-weight: 700
}

.report-verdict {
  margin: 0;
  display: flex;
  align-items: center;
  gap: 12px;
  flex-wrap: wrap;
  font-size: .88rem
}

/* --- Filters, preferences, empty states --- */
.filter-bar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-radius: var(--radius);
  padding: 12px 16px;
  margin-bottom: 18px
}

.filter-bar label {
  font-size: .8rem;
  color: var(--sv-muted);
  margin: 0
}

.filter-bar select {
  margin: 0;
  width: auto;
  min-width: 150px
}

.filter-bar .btn {
  margin: 0
}

.inline-form {
  display: inline-block;
  margin-bottom: 14px
}

.pref-row+.pref-row {
  border-top: 1px solid var(--sv-border);
  margin-top: 14px;
  padding-top: 14px
}

.pref-toggle {
  display: flex;
  align-items: flex-start;
  gap: 12px;
  cursor: pointer;
  font-weight: 400
}

.pref-toggle input {
  margin: 3px 0 0;
  width: auto;
  flex-shrink: 0
}

.pref-toggle strong {
  display: block;
  font-size: .95rem
}

.pref-toggle .muted {
  display: block;
  font-size: .84rem;
  margin-top: 2px
}

.empty-state {
  background: var(--sv-surface);
  border: 1px dashed var(--sv-border);
  border-radius: var(--radius);
  padding: 34px 26px;
  text-align: center
}

.empty-state h2 {
  margin-top: 0;
  font-size: 1.1rem
}

.empty-state p {
  max-width: 520px;
  margin: 0 auto 14px
}

@media(max-width:560px) {
  .kv {
    grid-template-columns: 1fr
  }

  .visit-item {
    flex-direction: column;
    gap: 8px
  }

  .visit-date {
    display: flex;
    align-items: baseline;
    gap: 6px;
    text-align: left
  }

  .visit-day {
    font-size: 1.1rem
  }
}

/* ---------------------------------------------------------------
   SETTINGS AREA
   Role-aware settings screens (hub, system, security, roles,
   integrations, notifications, operations) and the user-management
   table's row actions. Uses the existing tokens and .card/.badge
   vocabulary so the settings pages look like the rest of the app.
   --------------------------------------------------------------- */

.settings-layout {
  display: flex;
  gap: 26px;
  align-items: flex-start
}

.settings-body {
  flex: 1;
  min-width: 0
}

.settings-nav {
  width: 210px;
  flex-shrink: 0;
  background: var(--sv-white);
  border: 1px solid var(--sv-border);
  border-radius: var(--radius);
  padding: 10px 0;
  position: sticky;
  top: 16px
}

/* The dashboard header is fixed at 65px tall, so a plain "top: 16px" would
   let this stick half-hidden behind it — clear the header first. */
body.app-dashboard .settings-nav {
  top: 81px
}

.settings-nav a {
  display: block;
  padding: 7px 16px;
  font-size: .88rem;
  color: var(--sv-text)
}

.settings-nav a:hover,
.settings-nav a.active {
  background: var(--sv-accent-tint);
  color: var(--sv-green);
  text-decoration: none;
  font-weight: 600
}

.settings-nav-tier {
  margin: 12px 16px 4px;
  font-size: .7rem;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--sv-muted)
}

.settings-nav-tier:first-child {
  margin-top: 2px
}

.settings-cards {
  margin-bottom: 26px
}

a.settings-card {
  display: block;
  color: inherit;
  transition: border-color .12s ease
}

a.settings-card:hover {
  text-decoration: none;
  border-color: var(--sv-green)
}

a.settings-card h3 {
  margin: 0 0 6px;
  font-size: 1rem;
  color: var(--sv-green)
}

a.settings-card p {
  margin: 0;
  font-size: .88rem
}

.settings-section-heading {
  margin: 26px 0 10px;
  padding-bottom: 6px;
  border-bottom: 1px solid var(--sv-border);
  font-size: .78rem;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--sv-muted)
}

.settings-section-heading:first-child {
  margin-top: 0
}

.setting-row {
  margin-bottom: 18px
}

.setting-row label {
  display: block;
  margin-bottom: 4px;
  font-weight: 600;
  font-size: .92rem
}

.setting-row .pref-toggle label,
.setting-row label.pref-toggle {
  font-weight: 400
}

.setting-origin {
  margin: 5px 0 0;
  font-size: .76rem;
  color: var(--sv-muted)
}

.setting-origin code {
  font-size: .74rem
}

.settings-card-form {
  max-width: 640px
}

/* Sidebar: the Superadmin-only subsection */
.sidebar-tier {
  margin: 18px 22px 4px;
  font-size: .68rem;
  letter-spacing: .09em;
  text-transform: uppercase;
  color: var(--sv-muted);
  border-top: 1px solid var(--sv-border);
  padding-top: 14px
}

.sidebar a.sidebar-sub {
  padding-left: 32px;
  font-size: .86rem
}

/* Roles & permissions matrix */
.perm-toggle {
  padding: 5px 0
}

.perm-toggle strong {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: .84rem
}

.perm-list {
  margin: 12px 0 0;
  padding-left: 18px;
  font-size: .85rem
}

.perm-list li {
  margin-bottom: 3px
}

/* Users & access */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-bottom: 16px
}

.filter-bar input,
.filter-bar select {
  margin: 0;
  width: auto;
  min-width: 180px
}

.filter-bar .btn {
  margin: 0
}

.row-actions {
  display: flex;
  flex-direction: column;
  gap: 6px;
  align-items: flex-start
}

.row-actions form {
  margin: 0
}

.row-actions .role-change {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap
}

.row-actions .role-change select {
  margin: 0;
  width: auto;
  padding: 4px 8px;
  font-size: .8rem
}

/* Landing page control — drag-and-drop reorder lists (sections, cards, locations) */
.landing-reorder-list {
  list-style: none;
  margin: 0;
  padding: 0
}

.landing-reorder-list .landing-reorder-list {
  margin-top: 10px;
  padding-left: 12px;
  border-left: 2px solid var(--sv-border)
}

.landing-item {
  margin-bottom: 10px
}

.landing-item.dragging {
  opacity: .4
}

.landing-item-disabled > details > summary {
  opacity: .55
}

.landing-item .drag-handle {
  cursor: grab;
  margin-right: 8px;
  color: var(--sv-muted)
}

.landing-subform {
  margin: 14px 0;
  max-width: 480px
}

.landing-subform label {
  display: block;
  margin: 10px 0 4px;
  font-weight: 600;
  font-size: .88rem
}

.landing-subform input[type="text"],
.landing-subform input[type="email"],
.landing-subform input[type="file"],
.landing-subform textarea,
.landing-subform select {
  width: 100%
}

.btn-tiny {
  padding: 4px 10px;
  font-size: .78rem;
  white-space: nowrap
}

.btn[disabled],
.btn-tiny[disabled] {
  opacity: .45;
  cursor: not-allowed
}

/* Visually hidden, still announced by screen readers */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  white-space: nowrap;
  border: 0
}

@media(max-width:860px) {
  .settings-layout {
    flex-direction: column
  }

  .settings-nav {
    width: 100%;
    position: static
  }
}

/* ---------------------------------------------------------------
   ANALYTICS / DATA VISUALISATION

   Colour lives in src/Chart.php (marks are coloured inline, because
   the values come from the data), so this file carries only layout,
   chrome and ink. The two rules that matter:

     - text never wears a series colour; the coloured mark beside it
       carries the identity
     - grid, axis and baseline stay one step off the surface, hairline
       and solid, so the data is the only loud thing on the page

   Palette validated against the white card surface: categorical pair
   worst CVD ΔE 11.3 / normal 23.5; status good↔critical sits in the
   6-8 CVD band, which is why every status mark also carries a glyph
   and a label.
   --------------------------------------------------------------- */

.viz-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  flex-wrap: wrap
}

.viz-head .btn {
  flex-shrink: 0
}

/* Tab strip across the dashboards a user may open */
.viz-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 4px 0 22px;
  border-bottom: 1px solid var(--sv-border)
}

.viz-tabs a {
  padding: 8px 14px 10px;
  border-bottom: 2px solid transparent;
  color: var(--sv-muted);
  line-height: 1.25
}

.viz-tabs a:hover {
  text-decoration: none;
  color: var(--sv-text)
}

.viz-tabs a.active {
  color: var(--sv-green);
  border-bottom-color: var(--sv-green);
  font-weight: 600
}

.viz-tab-title {
  display: block;
  font-size: .93rem
}

.viz-tab-audience {
  display: block;
  font-size: .72rem;
  letter-spacing: .04em;
  text-transform: uppercase;
  opacity: .75
}

/* Hero figure — exactly one per view */
.viz-hero {
  background: var(--sv-white);
  border: 1px solid var(--sv-border);
  border-radius: var(--radius);
  padding: 22px 24px;
  margin-bottom: 18px
}

.viz-hero-label {
  margin: 0;
  font-size: .85rem;
  color: var(--sv-muted)
}

.viz-hero-value {
  margin: 2px 0 0;
  font-size: 3rem;
  line-height: 1.05;
  font-weight: 600;
  letter-spacing: -.02em
}

.viz-hero-caption {
  margin: 10px 0 0;
  font-size: .84rem;
  color: var(--sv-muted);
  max-width: 68ch
}

/* KPI row of stat tiles */
.viz-kpis {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(min(230px, 100%), 1fr));
  gap: 12px;
  margin-bottom: 26px
}

.viz-stat {
  background: var(--sv-white);
  border: 1px solid var(--sv-border);
  border-radius: var(--radius);
  padding: 14px 16px;
  display: block;
  color: inherit
}

a.viz-stat-link:hover {
  text-decoration: none;
  border-color: var(--sv-green)
}

.viz-stat-label {
  margin: 0;
  font-size: .8rem;
  color: var(--sv-muted)
}

.viz-stat-value {
  margin: 3px 0 0;
  font-size: 1.65rem;
  font-weight: 600;
  line-height: 1.15
}

.viz-stat-caption {
  margin: 6px 0 0;
  font-size: .78rem;
  color: var(--sv-muted)
}

.viz-stat-more {
  display: inline-block;
  margin-top: 8px;
  font-size: .78rem;
  color: var(--sv-green);
  font-weight: 600
}

/* Signed change. Direction is carried by the arrow and the "vs …" text,
   never by the colour alone. */
.viz-delta {
  margin: 5px 0 0;
  font-size: .78rem;
  font-weight: 600
}

.viz-delta-good { color: #0a6c44 }
.viz-delta-bad  { color: #a3312a }
.viz-delta-flat { color: var(--sv-muted); font-weight: 400 }

.viz-delta-period {
  font-weight: 400;
  color: var(--sv-muted)
}

.viz-status-dot {
  font-weight: 700;
  margin-right: 5px
}

.viz-spark {
  display: block;
  margin-top: 8px;
  overflow: visible
}

/* Figures. minmax(min(340px, 100%), 1fr) — same shrink-to-container guard as
   .viz-kpis — keeps a chart card from being forced wider than the viewport on
   phones, where the fixed track minimum would otherwise force horizontal
   scroll while the narrower stat tiles above sit flush. */
.viz-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr));
  gap: 16px;
  margin-bottom: 8px
}

.viz-figure {
  margin: 0 0 6px
}

.viz-figure + .viz-figure {
  margin-top: 22px
}

.viz-title {
  font-size: .95rem;
  font-weight: 600;
  margin: 0 0 10px;
  color: var(--sv-text)
}

/* Charts render at a fixed native size and scroll horizontally rather than
   shrink — a narrow card (e.g. matching .viz-stat width on a phone) would
   otherwise scale the whole SVG coordinate system down, shrinking axis and
   value text to the point of being unreadable. Same trade-off as the
   "Show the numbers" table beside it: width is capped, not the content.

   The y-axis numbers live in their own frozen SVG outside the scrolling
   area (see .viz-axis-svg) so they stay in view — read the current value
   off a scrolled-away line without scrolling back to check the scale. */
.viz-svg-frame {
  display: flex;
  align-items: flex-start
}

.viz-axis-svg {
  flex-shrink: 0;
  display: block;
  width: 46px;
  height: 240px;
  overflow: visible
}

/* Chart::line/stackedColumns render two whole axis SVGs: a wide one, shown
   by default, with labels right-aligned against the plot; and a narrower
   one (see the phone breakpoint below) with labels left-aligned against the
   card edge, so the plot itself sits closer to the card's left corner, not
   just the numbers beside it. */
.viz-axis-svg-narrow {
  display: none;
  width: 32px
}

.viz-svg-wrap {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  flex: 1 1 auto;
  min-width: 0
}

.viz-svg {
  display: block;
  width: 674px;
  height: 240px;
  max-width: none;
  overflow: visible
}

.viz-axis-svg .viz-axis,
.viz-svg .viz-axis {
  font-size: 11px;
  fill: #6b736f
}


.viz-svg .viz-endlabel {
  font-size: 12px;
  font-weight: 600;
  fill: var(--sv-text)
}

.viz-empty {
  color: var(--sv-muted);
  font-size: .88rem;
  padding: 14px 0;
  margin: 0
}

/* Legend — always present for two or more series */
.viz-legend {
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  font-size: .84rem
}

.viz-legend li {
  display: flex;
  align-items: center;
  gap: 6px
}

.viz-key {
  width: 12px;
  height: 12px;
  border-radius: 3px;
  flex-shrink: 0
}

/* Horizontal bars */
.viz-bars {
  display: flex;
  flex-direction: column;
  gap: 8px
}

.viz-bar-row {
  display: grid;
  grid-template-columns: minmax(min(120px, 100%), 34%) 1fr auto;
  align-items: center;
  gap: 10px;
  font-size: .86rem
}

.viz-bar-label {
  color: var(--sv-text);
  overflow-wrap: anywhere
}

.viz-bar-track {
  background: #f0efec;
  border-radius: 4px;
  height: 16px;
  display: block;
  overflow: hidden
}

.viz-bar-fill {
  display: block;
  height: 100%;
  border-radius: 0 4px 4px 0;
  min-width: 2px
}

.viz-bar-value {
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  min-width: 3ch;
  text-align: right
}

/* Single stacked bar — 2px surface gaps do the separating, not strokes */
.viz-stack {
  display: flex;
  height: 22px;
  border-radius: 4px;
  overflow: hidden;
  background: #f0efec;
  margin-bottom: 12px
}

.viz-stack-seg {
  display: block;
  height: 100%;
  border-right: 2px solid var(--sv-white)
}

.viz-stack-seg:last-child {
  border-right: 0
}

/* Meter — a single ratio against a limit */
.viz-meter {
  margin-bottom: 14px
}

.viz-meter-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  font-size: .88rem;
  margin-bottom: 6px
}

.viz-meter-track {
  background: #e8f2ed;
  border-radius: 5px;
  height: 12px;
  overflow: hidden
}

.viz-meter-fill {
  height: 100%;
  border-radius: 0 5px 5px 0
}

.viz-meter-caption {
  margin: 6px 0 0;
  font-size: .78rem;
  color: var(--sv-muted)
}

/* "You vs the team" — two marks on one track, same hue, two steps */
.viz-compare {
  margin-bottom: 20px
}

.viz-compare-head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
  font-size: .88rem;
  margin-bottom: 6px
}

.viz-compare-track {
  position: relative;
  background: #f0efec;
  border-radius: 5px;
  height: 14px
}

.viz-compare-bar {
  display: block;
  height: 100%;
  background: #0d8a57;
  border-radius: 5px
}

/* The benchmark reads as a rule across the track, not as a second series */
.viz-compare-bench {
  position: absolute;
  top: -4px;
  bottom: -4px;
  width: 3px;
  background: #075132;
  border-radius: 2px
}

.viz-compare-caption {
  margin: 7px 0 0;
  font-size: .8rem;
  color: var(--sv-muted)
}

/* The table view — present under every plotted chart */
.viz-table {
  margin-top: 10px
}

.viz-table summary {
  cursor: pointer;
  font-size: .8rem;
  color: var(--sv-muted)
}

.viz-table summary:hover {
  color: var(--sv-green)
}

.viz-table .table-wrap {
  margin-top: 8px
}

.viz-table table {
  font-size: .82rem
}

.viz-table td {
  font-variant-numeric: tabular-nums
}

.viz-list {
  margin: 8px 0 0;
  padding-left: 18px;
  font-size: .86rem
}

.viz-list li {
  margin-bottom: 4px
}

@media(max-width:560px) {
  .viz-hero-value {
    font-size: 2.3rem
  }

  .viz-bar-row {
    grid-template-columns: 1fr auto;
    row-gap: 4px
  }

  .viz-bar-track {
    grid-column: 1 / -1;
    order: 3
  }

  .viz-axis-svg-wide {
    display: none
  }

  .viz-axis-svg-narrow {
    display: block
  }
}

/* =====================================================================
   DARK THEME — component overrides.
   Everything that reads its colour from a custom property already adapted
   via the :root[data-theme="dark"] block above. What's left here is every
   component with a one-off literal colour: status badges, alerts, document
   kind tags, progress/quota tracks, and the analytics (viz-*) chart marks.
   Each keeps the same shape and meaning as its light equivalent — pastel
   fill + saturated ink on a white card, low-opacity fill + brightened ink on
   a dark one — so a status reads the same regardless of theme.
   ===================================================================== */
[data-theme="dark"] .badge-pending {
  background: rgba(224, 168, 60, .16);
  color: #e0a83c
}

[data-theme="dark"] .badge-scheduled {
  background: rgba(96, 165, 250, .16);
  color: #8fbdfa
}

[data-theme="dark"] .badge-in_progress {
  background: rgba(167, 139, 250, .18);
  color: #c3b1fb
}

[data-theme="dark"] .badge-met {
  background: rgba(51, 194, 129, .16)
}

[data-theme="dark"] .badge-not_met {
  background: rgba(242, 104, 95, .16)
}

[data-theme="dark"] .badge-cancelled {
  background: rgba(255, 255, 255, .08);
  color: #aeb8b3
}

[data-theme="dark"] .doc-kind-photo {
  background: rgba(96, 165, 250, .16);
  color: #8fbdfa
}

[data-theme="dark"] .doc-kind-video {
  background: rgba(167, 139, 250, .18);
  color: #c3b1fb
}

[data-theme="dark"] .doc-kind-report {
  background: rgba(224, 168, 60, .16);
  color: #e0a83c
}

[data-theme="dark"] .alert-error {
  background: rgba(242, 104, 95, .12);
  border-color: rgba(242, 104, 95, .4)
}

[data-theme="dark"] .alert-success {
  background: rgba(51, 194, 129, .12);
  border-color: rgba(51, 194, 129, .4)
}

[data-theme="dark"] .alert-warning {
  background: rgba(224, 168, 60, .12);
  border-color: rgba(224, 168, 60, .4);
  color: #e0a83c
}

[data-theme="dark"] .alert-warning.alert-critical {
  background: rgba(242, 104, 95, .12);
  border-color: rgba(242, 104, 95, .4);
  color: var(--sv-danger)
}

[data-theme="dark"] .quota-bar,
[data-theme="dark"] .progress,
[data-theme="dark"] .viz-bar-track,
[data-theme="dark"] .viz-stack,
[data-theme="dark"] .viz-meter-track,
[data-theme="dark"] .viz-compare-track {
  background: var(--sv-surface-alt)
}

[data-theme="dark"] .viz-delta-good {
  color: var(--sv-success)
}

[data-theme="dark"] .viz-delta-bad {
  color: var(--sv-danger)
}

[data-theme="dark"] .viz-compare-bar {
  background: #2aa374
}

[data-theme="dark"] .viz-compare-bench {
  background: #34d399
}

[data-theme="dark"] .viz-axis-svg .viz-axis,
[data-theme="dark"] .viz-svg .viz-axis {
  fill: var(--sv-muted)
}

[data-theme="dark"] .project-card:hover {
  box-shadow: 0 2px 14px rgba(52, 211, 153, .14)
}

[data-theme="dark"] .modal-overlay {
  background: rgba(3, 7, 5, .65)
}

/* Appearance settings — theme picker */
.theme-options {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px
}

@media(max-width:520px) {
  .theme-options {
    grid-template-columns: 1fr
  }
}

.theme-option {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 16px;
  border: 2px solid var(--sv-border);
  border-radius: var(--radius);
  cursor: pointer;
  font-weight: 400
}

.theme-option input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px
}

.theme-option.is-selected {
  border-color: var(--sv-green)
}

.theme-option-label strong {
  display: block;
  font-size: .95rem
}

.theme-option-label .muted {
  display: block;
  font-size: .82rem;
  margin-top: 2px
}

.theme-swatch {
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
  width: 46px;
  height: 46px;
  border-radius: 8px;
  padding: 6px;
  border: 1px solid var(--sv-border)
}

.theme-swatch span {
  display: block;
  height: 4px;
  border-radius: 2px
}

.theme-swatch-light {
  background: #f7f6f2
}

.theme-swatch-light span {
  background: #dfe3e0
}

.theme-swatch-light span:first-child {
  background: #0d4d3c;
  width: 60%
}

.theme-swatch-dark {
  background: #0f1613
}

.theme-swatch-dark span {
  background: #29352f
}

.theme-swatch-dark span:first-child {
  background: #34d399;
  width: 60%
}

/* Print support, app-wide: the staff-only case-study export
   (report_case_study.php) and the client portal's own report view
   (portal_report.php, "Print / Save as PDF") both rely on the browser's
   native Print -> Save as PDF rather than a PDF library — no dependency,
   no extra route, and it honours whatever the browser already does with
   page size and margins. .sidebar/.navbar/.footer/.no-print never belong
   in the output; .main is widened to fill the page once the sidebar it
   normally shares space with is gone. */
@media print {
  .navbar,
  .sidebar,
  .footer,
  .no-print {
    display: none !important
  }

  .app-shell {
    display: block !important
  }

  .main {
    margin: 0 !important;
    width: 100% !important;
    max-width: 100% !important;
    padding: 0 !important
  }

  body,
  body.case-study-page {
    background: #fff
  }
}
/* =====================================================================
   TASK BOARD & CONVERSATIONS
   The staff-side working screens: the verification task board
   (tasks.php, task_view.php, task_new.php) and internal messaging
   (messages.php, message_thread.php, message_compose.php).

   Everything below is built from the same tokens as the rest of the app
   and reuses its existing idioms rather than inventing a second visual
   language: the left-rail-plus-body shape comes from .visit-item, the
   status dot from .milestone-marker, the pill from .badge, and the
   filter row from .filter-bar. Nothing here is framework markup — the
   app's CSP is script-src 'self', so these screens are server-rendered
   and the CSS carries the interaction states a JS widget would
   otherwise own.
   ===================================================================== */

/* --- Page header shared by both areas --------------------------------- */
.work-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  flex-wrap: wrap;
  margin-bottom: 4px
}

.work-head h1 {
  margin: 0;
  font-size: 1.45rem
}

.work-head-actions {
  display: flex;
  gap: 8px;
  align-items: center;
  flex-wrap: wrap
}

.work-sub {
  color: var(--sv-muted);
  font-size: .9rem;
  margin: 6px 0 20px;
  max-width: 70ch
}

/* --- Metric rail ------------------------------------------------------
   Deliberately flatter and quieter than the app's .stat cards: these are
   a reading of the queue, glanced at on the way to the list underneath,
   not the headline of the page. Each one is a link that applies its own
   filter, so the number and the way to act on it are one object. */
.metric-rail {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(min(140px, 100%), 1fr));
  margin: 0 0 20px
}

.metric {
  display: block;
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-left: 3px solid var(--sv-border);
  border-radius: var(--radius);
  padding: 12px 14px;
  color: inherit
}

a.metric:hover {
  text-decoration: none;
  border-color: var(--sv-green);
  border-left-color: var(--sv-green)
}

a.metric.is-active {
  border-left-color: var(--sv-green);
  background: var(--sv-accent-tint)
}

.metric-value {
  display: block;
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1.1;
  color: var(--sv-text)
}

.metric-label {
  display: block;
  font-size: .74rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--sv-muted);
  margin-top: 4px
}

/* Colour is reserved for the two states that mean "somebody must act". */
.metric.tone-alert {
  border-left-color: var(--sv-danger)
}

.metric.tone-alert .metric-value {
  color: var(--sv-danger)
}

.metric.tone-review {
  border-left-color: var(--sv-gold)
}

.metric.tone-review .metric-value {
  color: var(--sv-gold)
}

/* --- Personal performance panel --------------------------------------- */
.perf-panel {
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-radius: var(--radius);
  padding: 16px 18px;
  margin: 0 0 20px
}

.perf-panel h2 {
  margin: 0 0 12px;
  font-size: .8rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--sv-muted)
}

.perf-grid {
  display: grid;
  gap: 14px 22px;
  grid-template-columns: repeat(auto-fit, minmax(min(130px, 100%), 1fr))
}

.perf-item strong {
  display: block;
  font-size: 1.15rem;
  color: var(--sv-green)
}

.perf-item span {
  display: block;
  font-size: .76rem;
  color: var(--sv-muted);
  margin-top: 2px
}

/* --- Task list --------------------------------------------------------
   A left rail carrying the one thing that decides whether you read the
   row at all — when it is due, and whether that has passed — then the
   body. Same anatomy as the portal's .visit-item, so the staff board and
   the client portal read as one product. */
.task-list {
  list-style: none;
  margin: 0;
  padding: 0
}

.task-row {
  display: flex;
  gap: 14px;
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-left: 4px solid var(--sv-border);
  border-radius: var(--radius);
  padding: 15px 18px;
  margin-bottom: 10px
}

.task-row.row-link {
  cursor: pointer
}

.task-row:hover {
  border-color: var(--sv-green);
  border-left-color: var(--sv-green)
}

.task-row.row-link:focus-visible {
  outline: 2px solid var(--sv-green);
  outline-offset: 2px
}

.task-row.prio-3 {
  border-left-color: var(--sv-gold)
}

.task-row.is-overdue {
  border-left-color: var(--sv-danger)
}

.task-row.is-closed {
  opacity: .72
}

.task-rail {
  flex: 0 0 54px;
  text-align: center;
  padding-top: 2px
}

.task-rail-day {
  display: block;
  font-size: 1.5rem;
  font-weight: 700;
  line-height: 1;
  color: var(--sv-green)
}

.task-rail-month {
  display: block;
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--sv-muted);
  margin-top: 2px
}

.task-rail-none {
  display: block;
  font-size: .7rem;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--sv-muted);
  line-height: 1.3;
  padding-top: 6px
}

.task-row.is-overdue .task-rail-day {
  color: var(--sv-danger)
}

.task-body {
  flex: 1;
  min-width: 0
}

.task-title-row {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap
}

.task-title-row h3 {
  margin: 0;
  font-size: 1rem;
  font-weight: 600
}

.task-title-row h3 a {
  color: inherit
}

.task-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 14px;
  margin: 7px 0 0;
  font-size: .8rem;
  color: var(--sv-muted)
}

.task-meta strong {
  color: var(--sv-text);
  font-weight: 600
}

/* --- Progress readout -------------------------------------------------
   Two segments, not one: work signed off and work still waiting on a
   reviewer are different claims, and a single bar would let the second
   quietly pass for the first. */
.check-meter {
  margin: 10px 0 0;
  max-width: 420px
}

.check-track {
  display: flex;
  height: 8px;
  border-radius: 999px;
  overflow: hidden;
  background: #e8ebe9
}

.check-approved {
  background: var(--sv-green)
}

.check-awaiting {
  background: var(--sv-gold)
}

.check-sentback {
  background: var(--sv-danger)
}

.check-caption {
  font-size: .76rem;
  color: var(--sv-muted);
  margin: 6px 0 0
}

.check-key {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  margin-right: 12px
}

.check-key i {
  width: 9px;
  height: 9px;
  border-radius: 2px;
  display: inline-block;
  font-style: normal
}

/* --- Row actions ------------------------------------------------------ */
.task-actions {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin: 12px 0 0
}

.task-actions form {
  margin: 0
}

/* --- Avatar chips ----------------------------------------------------- */
.people {
  display: flex;
  align-items: center;
  gap: 5px;
  flex-wrap: wrap
}

.avatar-chip {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
  background: var(--sv-surface-alt);
  border: 1px solid var(--sv-border);
  color: var(--sv-text);
  font-size: .68rem;
  font-weight: 700;
  letter-spacing: .02em;
  flex: 0 0 auto
}

.avatar-chip.avatar-lg {
  width: 38px;
  height: 38px;
  font-size: .82rem
}

.avatar-chip.is-you {
  background: var(--sv-accent-tint);
  border-color: var(--sv-green);
  color: var(--sv-green)
}

/* --- Priority / relationship pills ------------------------------------
   The same shape as .badge, because the app already teaches that shape as
   "a small piece of status" — but a different palette, so priority can
   never be misread as a verification verdict. */
.pill {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 999px;
  font-size: .72rem;
  font-weight: 600;
  border: 1px solid var(--sv-border);
  color: var(--sv-muted);
  white-space: nowrap
}

.pill-prio-3 {
  border-color: var(--sv-gold);
  color: var(--sv-gold)
}

.pill-prio-1 {
  opacity: .8
}

.pill-overdue {
  border-color: var(--sv-danger);
  color: var(--sv-danger)
}

.pill-role {
  background: var(--sv-surface-alt);
  border-color: transparent
}

.badge-blocked {
  background: #f0e0d2;
  color: #8a4a12
}

/* --- Checklist -------------------------------------------------------- */
.checklist {
  list-style: none;
  margin: 0;
  padding: 0
}

.check-item {
  display: flex;
  gap: 12px;
  align-items: flex-start;
  padding: 11px 0;
  border-bottom: 1px solid var(--sv-border)
}

.check-item:last-child {
  border-bottom: 0
}

.check-state {
  flex: 0 0 16px;
  width: 16px;
  height: 16px;
  margin-top: 3px;
  border-radius: 50%;
  border: 3px solid var(--sv-border);
  background: var(--sv-surface)
}

.check-item.is-approved .check-state {
  border-color: var(--sv-success);
  background: var(--sv-success)
}

.check-item.is-awaiting .check-state {
  border-color: var(--sv-gold)
}

.check-item.is-sentback .check-state {
  border-color: var(--sv-danger);
  background: var(--sv-danger)
}

.check-main {
  flex: 1;
  min-width: 0
}

.check-text {
  font-size: .92rem;
  margin: 0
}

.check-item.is-approved .check-text {
  color: var(--sv-muted)
}

.check-note {
  font-size: .78rem;
  color: var(--sv-muted);
  margin: 4px 0 0
}

.check-note.is-sentback {
  color: var(--sv-danger)
}

.check-controls {
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 6px
}

.check-controls form {
  margin: 0;
  display: flex;
  gap: 6px;
  align-items: center;
  flex-wrap: wrap
}

.check-controls input[type=text] {
  width: auto;
  min-width: 180px;
  margin: 0;
  padding: 5px 9px;
  font-size: .82rem
}

/* --- Detail page layout ----------------------------------------------- */
.work-layout {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 300px;
  gap: 20px;
  align-items: start
}

@media(max-width:1000px) {
  .work-layout {
    grid-template-columns: 1fr
  }
}

.work-aside .card+.card {
  margin-top: 16px
}

.work-aside h3 {
  font-size: .8rem;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--sv-muted);
  margin: 0 0 12px
}

.aside-list {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: .86rem
}

.aside-list li {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  justify-content: space-between
}

/* The name block is what gives when the row is tight; the control on the
   right keeps its size, because a "Remove" button that has wrapped onto its
   own line under a name reads as belonging to the next row down. */
.aside-list li > :first-child {
  min-width: 0;
  flex: 1 1 auto
}

.aside-list li > :last-child {
  flex: 0 0 auto
}

.aside-list .people {
  flex-wrap: nowrap;
  align-items: flex-start
}

.aside-list .people > span:last-child {
  min-width: 0;
  overflow-wrap: anywhere
}

.aside-list li+li {
  border-top: 1px solid var(--sv-border)
}

/* --- Discussion (task comments and message threads share this) -------- */
.discussion {
  list-style: none;
  margin: 0;
  padding: 0
}

.discussion-item {
  display: flex;
  gap: 12px;
  padding: 14px 0;
  border-bottom: 1px solid var(--sv-border)
}

.discussion-item:last-child {
  border-bottom: 0
}

.discussion-body {
  flex: 1;
  min-width: 0
}

.discussion-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
  font-size: .84rem
}

.discussion-head strong {
  font-size: .9rem
}

.discussion-time {
  color: var(--sv-muted);
  font-size: .76rem
}

.discussion-text {
  margin: 5px 0 0;
  font-size: .9rem;
  overflow-wrap: anywhere
}

/* A message you sent reads as yours without being pushed to the right: a
   chat-bubble layout would make a threaded WORK record harder to scan,
   which is the opposite of what this screen is for. */
.discussion-item.is-mine .discussion-body {
  background: var(--sv-accent-tint);
  border-radius: var(--radius);
  padding: 10px 14px;
  margin: -4px 0
}

/* --- Activity timeline ------------------------------------------------ */
.timeline {
  list-style: none;
  margin: 0;
  padding: 0;
  font-size: .84rem
}

.timeline li {
  position: relative;
  padding: 0 0 14px 20px
}

.timeline li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 5px;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  background: var(--sv-border)
}

.timeline li::after {
  content: "";
  position: absolute;
  left: 3.5px;
  top: 15px;
  bottom: 0;
  width: 1px;
  background: var(--sv-border)
}

.timeline li:last-child::after {
  display: none
}

.timeline-detail {
  color: var(--sv-muted);
  display: block;
  margin-top: 2px;
  overflow-wrap: anywhere
}

.timeline-time {
  color: var(--sv-muted);
  font-size: .76rem
}

/* --- Conversation list ------------------------------------------------ */
.thread-list {
  list-style: none;
  margin: 0;
  padding: 0
}

.thread-row {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-left: 4px solid transparent;
  border-radius: var(--radius);
  padding: 14px 18px;
  margin-bottom: 8px
}

.thread-row.row-link {
  cursor: pointer
}

.thread-row:hover {
  border-color: var(--sv-green);
  border-left-color: var(--sv-green)
}

.thread-row.row-link:focus-visible {
  outline: 2px solid var(--sv-green);
  outline-offset: 2px
}

/* Unread is carried by the rail and by the weight of the subject, not by
   a coloured dot in a gutter: one signal, read at a glance, that survives
   both themes and greyscale. */
.thread-row.is-unread {
  border-left-color: var(--sv-gold)
}

.thread-row.is-unread .thread-subject {
  font-weight: 700
}

.thread-main {
  flex: 1;
  min-width: 0
}

.thread-subject {
  font-size: .96rem;
  font-weight: 600;
  margin: 0
}

.thread-subject a {
  color: inherit
}

.thread-people {
  font-size: .8rem;
  color: var(--sv-muted);
  margin: 3px 0 0
}

.thread-snippet {
  font-size: .85rem;
  color: var(--sv-muted);
  margin: 6px 0 0;
  overflow-wrap: anywhere
}

.thread-side {
  flex: 0 0 auto;
  text-align: right;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 6px
}

.thread-time {
  font-size: .76rem;
  color: var(--sv-muted);
  white-space: nowrap
}

.thread-count {
  font-size: .74rem;
  color: var(--sv-muted)
}

@media(max-width:620px) {

  .thread-row,
  .task-row {
    flex-direction: column;
    gap: 10px
  }

  .thread-side {
    flex-direction: row;
    flex-wrap: wrap;
    min-width: 0;
    align-items: center;
    text-align: left
  }

  .task-rail {
    flex: 0 0 auto;
    display: flex;
    align-items: baseline;
    gap: 6px;
    text-align: left
  }

  .task-rail-day {
    font-size: 1.05rem
  }
}

/* --- Recipient picker -------------------------------------------------
   Works with no JavaScript at all: the <select> is the real field, and
   assets/js/message-compose.js only narrows it as you type. */
.recipient-picker {
  position: relative
}

.recipient-picker select[multiple] {
  min-height: 190px
}

.picker-hint {
  font-size: .78rem;
  color: var(--sv-muted);
  margin: 6px 0 0
}

.chosen-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 0 0
}

/* --- Checklist builder (task_new.php) --------------------------------- */
.builder-row {
  display: flex;
  gap: 8px;
  align-items: center;
  margin-bottom: 8px
}

.builder-row input[type=text] {
  margin: 0
}

.builder-actions {
  margin-top: 4px
}

/* --- Pagination -------------------------------------------------------
   Server-rendered links, like .tabs — the app has no client router. */
.pager {
  display: flex;
  gap: 6px;
  align-items: center;
  justify-content: center;
  margin: 20px 0 0;
  flex-wrap: wrap
}

.pager a,
.pager span {
  padding: 6px 12px;
  border: 1px solid var(--sv-border);
  border-radius: 6px;
  font-size: .85rem;
  color: var(--sv-muted)
}

.pager a:hover {
  text-decoration: none;
  border-color: var(--sv-green);
  color: var(--sv-green)
}

.pager .is-current {
  background: var(--sv-green);
  border-color: var(--sv-green);
  color: #fff;
  font-weight: 600
}

.pager .is-disabled {
  opacity: .45
}

/* --- Dark theme corrections ------------------------------------------
   Only the few places that hard-code a light value; everything else is
   token-driven already and needs nothing here. */
[data-theme="dark"] .check-track {
  background: #202b26
}

[data-theme="dark"] .badge-blocked {
  background: rgba(224, 168, 60, .18);
  color: #e0a83c
}

[data-theme="dark"] .pager .is-current {
  color: #0f1613
}

/* --- Theme toggle (partials/header.php) --------------------------------
   Sun/moon button just left of the notification bell. Icon shown reflects
   the theme currently active (sun in light mode, moon in dark) -- clicking
   it flips to the other one via theme-toggle.js + theme_toggle.php, which
   persists through Auth::setTheme() exactly like settings_appearance.php. */
.theme-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  margin-left: 10px;
  border: 0;
  border-radius: 999px;
  background: none;
  color: var(--sv-green);
  cursor: pointer
}

.theme-toggle:hover {
  background: var(--sv-accent-tint)
}

.theme-toggle-icon {
  width: 20px;
  height: 20px
}

.theme-toggle-icon-moon {
  display: none
}

:root[data-theme="dark"] .theme-toggle-icon-sun {
  display: none
}

:root[data-theme="dark"] .theme-toggle-icon-moon {
  display: inline-flex
}

/* --- Notification bell + dropdown (partials/header.php) ---------------
   The <a> is the no-JS baseline (a plain link to /notifications.php);
   notification-bell.js turns it into a dropdown toggle. Everything here is
   token-driven, so dark mode needs no extra rules. */
.notif-bell-wrap {
  position: relative;
  display: inline-flex;
  margin-left: 5px
}

/* Tighter gap to the logout button than the default nav-link spacing
   (.navbar nav a.btn's 18px). */
.navbar .notif-bell-wrap + a.btn {
  margin-left: 8px
}

/* Overrides ".navbar nav a" (color/margin-left/font-size on every anchor in
   the nav), which would otherwise bleed into the bell itself and every link
   inside the dropdown. Two classes beats that rule's one, regardless of
   source order, so this does not need !important. */
.navbar .notif-bell,
.navbar .notif-dropdown a {
  margin-left: 0;
  font-size: inherit
}

.navbar .notif-dropdown a {
  color: inherit
}

.notif-bell {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  border-radius: 999px;
  color: var(--sv-green)
}

.notif-bell:hover {
  background: var(--sv-accent-tint);
  text-decoration: none
}

.notif-bell svg {
  width: 20px;
  height: 20px
}

.notif-bell-badge {
  position: absolute;
  top: 0;
  right: 0;
  min-width: 16px;
  height: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--sv-gold);
  color: #241a04;
  font-size: .66rem;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
  box-shadow: 0 0 0 2px var(--sv-surface)
}

.notif-dropdown {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  width: 340px;
  max-width: calc(100vw - 24px);
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-radius: var(--radius);
  box-shadow: 0 12px 32px rgba(0, 0, 0, .18);
  z-index: 1100;
  overflow: hidden
}

.notif-dropdown-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--sv-border);
  font-size: .92rem
}

.notif-dropdown-markall {
  background: none;
  border: none;
  color: var(--sv-green);
  font-size: .78rem;
  font-weight: 600;
  cursor: pointer;
  padding: 0
}

.notif-dropdown-markall:hover {
  text-decoration: underline
}

.notif-dropdown-empty {
  padding: 18px 14px;
  color: var(--sv-muted);
  font-size: .86rem;
  margin: 0
}

.notif-dropdown-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 360px;
  overflow-y: auto
}

.notif-dropdown-item {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 10px 14px;
  border-bottom: 1px solid var(--sv-border);
  font-size: .84rem
}

.notif-dropdown-item:last-child {
  border-bottom: none
}

.notif-dropdown-item.is-unread {
  background: var(--sv-accent-tint)
}

.notif-dropdown-text {
  min-width: 0;
  color: var(--sv-text)
}

.notif-dropdown-text a {
  color: inherit;
  font-weight: 600
}

.notif-dropdown-time {
  display: block;
  color: var(--sv-muted);
  font-size: .74rem;
  margin-top: 2px
}

.notif-dropdown-viewall {
  display: block;
  text-align: center;
  padding: 10px;
  font-size: .84rem;
  font-weight: 600;
  border-top: 1px solid var(--sv-border);
  background: var(--sv-surface-alt)
}

.notif-dropdown-viewall:hover {
  text-decoration: none;
  background: var(--sv-accent-tint)
}

/* Status dot vocabulary shared with Chart::STATUS — never colour alone, so
   every item still carries its title/body text and (in the dropdown/full
   list) is grouped under an explicit "Unread" tab, not just a coloured mark. */
.notif-dot {
  flex: none;
  width: 9px;
  height: 9px;
  border-radius: 999px;
  margin-top: 6px;
  background: var(--sv-steel)
}

.notif-dot-info {
  background: #2a78d6
}

.notif-dot-success {
  background: var(--sv-success)
}

.notif-dot-warning {
  background: var(--sv-gold)
}

.notif-dot-critical {
  background: var(--sv-danger)
}

/* --- Notifications page (notifications.php) ---------------------------- */
.notif-list {
  list-style: none;
  margin: 0;
  padding: 0
}

.notif-row {
  display: flex;
  gap: 14px;
  align-items: flex-start;
  background: var(--sv-surface);
  border: 1px solid var(--sv-border);
  border-left: 4px solid transparent;
  border-radius: var(--radius);
  padding: 14px 18px;
  margin-bottom: 8px
}

.notif-row.is-unread {
  border-left-color: var(--sv-gold)
}

.notif-row.is-unread .notif-title {
  font-weight: 700
}

.notif-row .notif-dot {
  margin-top: 4px
}

.notif-main {
  flex: 1;
  min-width: 0
}

.notif-title {
  margin: 0;
  font-size: .96rem
}

.notif-title a {
  color: inherit
}

.notif-body {
  margin: 4px 0 0;
  font-size: .86rem;
  color: var(--sv-muted)
}

.notif-time {
  margin: 6px 0 0;
  font-size: .78rem;
  color: var(--sv-muted)
}

.notif-actions {
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: none
}

@media(max-width:560px) {
  .notif-row {
    flex-wrap: wrap
  }

  .notif-actions {
    flex-direction: row;
    width: 100%
  }

  /* Anchoring to the bell (position:absolute, right:0) breaks on a narrow
     screen: the bell sits near the left of a wrapped navbar, so a
     340px-wide panel hanging off its right edge runs straight past the
     left edge of the viewport instead. Fixed to the viewport itself
     instead, so it always fits regardless of where the bell lands. */
  .notif-dropdown {
    position: fixed;
    top: 65px;
    left: 12px;
    right: 12px;
    width: auto;
    max-width: none
  }
}
