@import url('https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@300;400;500;600;700;800&display=swap');

:root {
  --primary: #1A7A3C;
  --primary-light: #E8F5E9;
  --primary-dark: #0F4D25;
  --bg: #F5F6FA;
  --card: #FFFFFF;
  --border: #E5E7EB;
  --text: #1C1C1E;
  --text-secondary: #6B7280;
  /* Between --text and --text-secondary: for running text that is not a
     heading but still has to be read, not skimmed past. */
  --text-body: #4B5563;
  --text-disabled: #9CA3AF;

  --status-review: #7C3AED;         --status-review-bg: #F5F3FF;
  --status-progress: #D4760A;       --status-progress-bg: #FFF7ED;
  --status-waiting: #DC2626;        --status-waiting-bg: #FEF2F2;
  --status-resolved: #1A7A3C;       --status-resolved-bg: #E8F5E9;

  --priority-1: #DC2626;
  --priority-2: #D4760A;
  --priority-3: #1A56DB;
  --priority-4: #6B7280;

  --escalation: #7C3AED;
  --escalation-bg: #F5F3FF;

  --shadow-card: 0 1px 4px rgba(0,0,0,0.06), 0 4px 16px rgba(0,0,0,0.04);
  --shadow-modal: 0 8px 32px rgba(0,0,0,0.12);
  --shadow-panel: -4px 0 24px rgba(0,0,0,0.08);
}

* { box-sizing: border-box; font-family: 'Plus Jakarta Sans', system-ui, sans-serif; }

/* One family, app and dashboard alike. The selectors below are the things
   that name something — page and card titles, labels, badges, numbers —
   and they differ from body copy by weight rather than by typeface.
   Declared once here so individual views do not each have to remember. */
h1, h2, h3, h4, h5, h6,
.page-title, .stat-value, .stat-label, .badge, .nav-item-label, .nav-label,
.logo-text, .chart-card-title, .field-label, .sidebar-user-name,
table.data th, .btn {
  font-family: 'Plus Jakarta Sans', system-ui, sans-serif;
}

body { margin: 0; background: var(--bg); color: var(--text); font-size: 14px; line-height: 1.5; }
a { color: inherit; text-decoration: none; }

::-webkit-scrollbar { width: 4px; height: 4px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 99px; }
::-webkit-scrollbar-thumb:hover { background: var(--text-disabled); }

/* ── Shell layout ─────────────────────────────────────── */

.shell { display: flex; height: 100vh; width: 100%; overflow: hidden; }

.sidebar {
  width: 240px; flex-shrink: 0; height: 100vh; background: #fff;
  border-right: 1px solid var(--border); display: flex; flex-direction: column;
  position: fixed; left: 0; top: 0; z-index: 100; overflow: hidden;
  transition: width 0.15s ease;
}
.logo-area { height: 64px; display: flex; align-items: center; padding: 0 20px; gap: 10px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
.logo-mark { width: 30px; height: 30px; object-fit: contain; flex-shrink: 0; }
.logo-text { font-size: 16px; font-weight: 700; color: var(--primary); white-space: nowrap; }

.sidebar-toggle {
  position: fixed; top: 20px; left: 240px; transform: translateX(-50%);
  width: 26px; height: 26px; border-radius: 50%; background: #fff; border: 1px solid var(--border);
  box-shadow: var(--shadow-card); display: flex; align-items: center; justify-content: center;
  color: var(--text-secondary); cursor: pointer; z-index: 101; padding: 0; transition: left 0.15s ease;
}
.sidebar-toggle:hover { color: var(--primary); border-color: var(--primary); }
.sidebar-toggle svg { transition: transform 0.15s ease; }

.nav-group { padding-bottom: 4px; }
.nav-label { padding: 20px 20px 8px; font-size: 10px; font-weight: 600; color: var(--text-disabled); letter-spacing: 0.08em; white-space: nowrap; overflow: hidden; }
.nav { flex: 1; overflow-y: auto; overflow-x: hidden; }
.nav-item {
  display: flex; align-items: center; gap: 12px; width: 100%; height: 44px;
  padding: 0 20px; background: transparent; border: none; border-left: 3px solid transparent;
  cursor: pointer; text-align: left; transition: all 0.15s;
}
.nav-item:hover { background: #F9FAFB; }
.nav-item.active { background: var(--primary-light); border-left-color: var(--primary); }
.nav-icon { font-size: 16px; width: 20px; text-align: center; color: var(--text-secondary); flex-shrink: 0; }
.nav-item.active .nav-icon { color: var(--primary); }
.nav-item-label { font-size: 14px; font-weight: 400; color: var(--text-secondary); white-space: nowrap; overflow: hidden; }
.nav-item.active .nav-item-label { font-weight: 600; color: var(--primary); }

.sidebar-user { height: 52px; display: flex; align-items: center; gap: 10px; padding: 0 16px; border-top: 1px solid var(--border); }
.sidebar-user-avatar { width: 36px; height: 36px; border-radius: 50%; background: var(--primary-light); display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 700; color: var(--primary); flex-shrink: 0; }
.sidebar-user-name { font-size: 13px; font-weight: 600; color: var(--text); line-height: 1.3; white-space: nowrap; }
.sidebar-user-role { font-size: 11px; color: var(--text-secondary); white-space: nowrap; }

/* Collapsed state: icon-only rail. Persisted in localStorage; the inline
   head script adds this class before first paint to avoid a flash. */
html.sidebar-collapsed .sidebar { width: 76px; }
html.sidebar-collapsed .sidebar-toggle { left: 76px; }
html.sidebar-collapsed .sidebar-toggle svg { transform: rotate(180deg); }
html.sidebar-collapsed .main { margin-left: 76px; }
html.sidebar-collapsed .logo-area { justify-content: center; padding: 0; }
html.sidebar-collapsed .logo-text,
html.sidebar-collapsed .nav-label,
html.sidebar-collapsed .nav-item-label,
html.sidebar-collapsed .sidebar-user-name,
html.sidebar-collapsed .sidebar-user-role { display: none; }
html.sidebar-collapsed .nav-item { justify-content: center; padding: 0; gap: 0; }
html.sidebar-collapsed .sidebar-user { justify-content: center; padding: 0; }

.main { margin-left: 240px; flex: 1; display: flex; flex-direction: column; height: 100vh; overflow: hidden; transition: margin-left 0.15s ease; }
.topbar { height: 64px; background: #fff; border-bottom: 1px solid var(--border); display: flex; align-items: center; padding: 0 28px; justify-content: space-between; flex-shrink: 0; }
.page-title { font-size: 22px; font-weight: 700; color: var(--text); }
.topbar-right { display: flex; align-items: center; gap: 12px; }
.topbar-search { height: 38px; padding-left: 36px; padding-right: 14px; border-radius: 10px; border: 1px solid var(--border); background: var(--bg); font-size: 13px; outline: none; width: 240px; color: var(--text); }
.topbar-search-wrap { position: relative; }
.topbar-search-icon { position: absolute; left: 12px; top: 50%; transform: translateY(-50%); color: var(--text-secondary); font-size: 14px; }
.bell-btn { position: relative; cursor: pointer; }
.bell-circle { width: 38px; height: 38px; border-radius: 50%; background: var(--primary-light); display: flex; align-items: center; justify-content: center; font-size: 16px; }
.bell-badge { position: absolute; top: -2px; right: -2px; width: 16px; height: 16px; border-radius: 50%; background: var(--status-waiting); color: #fff; font-size: 9px; font-weight: 700; display: flex; align-items: center; justify-content: center; }
.avatar-circle { width: 38px; height: 38px; border-radius: 50%; background: var(--primary-light); display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 700; color: var(--primary); cursor: pointer; }

.page-content { flex: 1; overflow-y: auto; padding: 28px; }
.page-sub { font-size: 13px; color: var(--text-secondary); margin-bottom: 24px; }

/* ── Cards ────────────────────────────────────────────── */

.card { background: #fff; border-radius: 20px; box-shadow: var(--shadow-card); }
.card-pad { padding: 20px; }

.stat-card { background: #fff; border-radius: 20px; padding: 18px; flex: 1; min-width: 0; box-shadow: var(--shadow-card); border-left: 4px solid; }
/* Two lines reserved so a wrapping label ("Waiting for Parts") does not
   make its card taller than the rest of the row. */
.stat-card .stat-label { min-height: 2.6em; }
.stat-label { font-size: 12.5px; color: var(--text-secondary); margin-bottom: 8px; line-height: 1.3; }
.stat-value { font-size: 36px; font-weight: 700; line-height: 1; }
.stat-trend { font-size: 12px; color: var(--text-secondary); margin-top: 8px; }

.stat-card-sm { flex: 1; background: #fff; border-radius: 18px; padding: 16px 20px; box-shadow: var(--shadow-card); }
.stat-card-sm .stat-label { font-size: 13px; margin-bottom: 6px; }
.stat-card-sm .stat-value { font-size: 28px; }

.flash { padding: 12px 16px; border-radius: 8px; font-size: 13px; font-weight: 500; margin-bottom: 16px; }
.flash-success { background: var(--primary-light); color: var(--primary); }
.flash-error { background: var(--status-waiting-bg); color: var(--status-waiting); }

/* ── Forms ────────────────────────────────────────────── */

.field-label { font-size: 12px; font-weight: 600; color: var(--text); margin-bottom: 6px; display: block; }
.input, .select, textarea.input {
  width: 100%; height: 48px; padding: 0 14px; border: 1px solid var(--border); border-radius: 12px;
  background: var(--bg); font-size: 14px; color: var(--text); font-family: inherit; outline: none; transition: border-color 0.15s;
}
textarea.input { height: auto; padding: 12px 14px; resize: none; }
.input:focus, .select:focus, textarea.input:focus { border-color: var(--primary); }

/* Scoped to real dropdowns. The Concern Queue's search box borrows
   .select-sm for its shape alone, and was being handed a chevron it has no
   menu behind, plus centred text a search field should not have. */
select.select, select.select-sm {
  appearance: none; -webkit-appearance: none; -moz-appearance: none;
  cursor: pointer;
  /* One line, clipped. Under appearance:base-select the control renders as
     a button that wraps its text, and a long unit name spilled out of the
     field; the generated button is in the UA shadow tree, so the rule has
     to live here. */
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%236B7280' stroke-width='1.8' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='5.5 8.5 12 15 18.5 8.5'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 12px center;
  background-size: 14px;
  padding-right: 36px;
}

/* Every dropdown sits its label centred between equal padding, with the
   chevron laid over the right of it. Centring only the small ones left the
   Reports pickers and the modal selects with their labels hard against a
   left edge while 36px of chevron space waited on the other side -- two
   kinds of dropdown on one screen. */
select.select,
select.select-sm {
  text-align: center;
  /* Both axes. appearance:base-select renders the control as a flex button
     whose content stretches to the top of the box, so the label sat high
     with the rest of the 48px below it -- centring it horizontally alone
     left that untouched. */
  display: flex;
  align-items: center;
  justify-content: center;
  padding-left: 36px;
  padding-right: 36px;
}
.select-sm {
  height: 38px; padding-left: 12px; border-radius: 10px; border: 1px solid var(--border);
  background-color: #fff; font-size: 13px; color: var(--text); font-family: inherit;
  /* A filter keeps one width whatever is chosen in it. Under
     appearance:base-select the control sizes to the selected option rather
     than the longest one, so picking "Resolved" made the box shrink and the
     row of filters shuffle along. */
  min-width: 150px;
  background-position: right 10px center; background-size: 13px; padding-right: 30px;
}
.select-sm:focus { outline: none; border-color: var(--primary); }

/* ── Pagination ───────────────────────────────────────── */
.pagination-nav { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; }
.pagination-info { font-size: 13px; color: var(--text-secondary); }
.pagination-btns { display: flex; align-items: center; gap: 4px; }
.page-btn {
  display: inline-flex; align-items: center; justify-content: center; min-width: 34px; height: 34px;
  padding: 0 8px; border-radius: 8px; border: 1px solid var(--border); background: #fff;
  font-size: 13px; font-weight: 600; color: var(--text); cursor: pointer; font-family: inherit;
}
.page-btn:hover:not(.disabled):not(.active) { background: var(--primary-light); border-color: var(--primary); color: var(--primary); }
.page-btn.active { background: var(--primary); border-color: var(--primary); color: #fff; cursor: default; }
.page-btn.disabled { color: var(--text-disabled); cursor: default; }
.page-btn-icon { width: 34px; padding: 0; }

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  border-radius: 14px; font-weight: 600; cursor: pointer; border: none; transition: all 0.15s;
  white-space: nowrap; font-size: 15px; padding: 0 20px; height: 50px; font-family: inherit;
}
.btn-primary { background: var(--primary); color: #fff; }
.btn-primary:hover { background: var(--primary-dark); }
.btn-outline { background: #fff; color: var(--primary); border: 1.5px solid var(--primary); }
.btn-danger { background: #fff; color: var(--status-waiting); border: 1.5px solid var(--status-waiting); }

/* Undoing a merge is neither the safe path nor a destructive one, so it
   should read as neither green nor red. Purple is already the colour of a
   concern under review, which is what a separated report goes back to
   being. */
.btn-violet { background: #fff; color: var(--status-review); border: 1.5px solid var(--status-review); }
.btn-violet:hover { background: var(--status-review-bg); }
.btn-ghost { background: transparent; color: var(--text-secondary); border: none; }
.btn-sm { font-size: 12px; padding: 4px 12px; height: 28px; border-radius: 8px; }
.btn:disabled { opacity: 0.55; cursor: not-allowed; }
.btn-block { width: 100%; }
.icon-btn { width: 28px; height: 28px; border-radius: 8px; border: 1px solid var(--border); background: #fff; cursor: pointer; font-size: 13px; display: inline-flex; align-items: center; justify-content: center; }

/* ── Table ────────────────────────────────────────────── */

table.data { width: 100%; border-collapse: collapse; }
table.data th {
  padding: 12px; font-size: 11px; font-weight: 600; color: var(--text-secondary); text-align: left;
  text-transform: uppercase; letter-spacing: 0.04em; white-space: nowrap; border-bottom: 2px solid var(--border);
}
table.data td { padding: 14px 12px; border-bottom: 1px solid var(--border); font-size: 13px; vertical-align: middle; }
table.data tbody tr:nth-child(even) { background: #FAFAFA; }
table.data tbody tr:hover { background: #F5F6FA; }
.mono { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 11px; color: var(--text-secondary); }
.truncate { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 220px; }

/* ── Badges ───────────────────────────────────────────── */

.badge { display: inline-flex; align-items: center; gap: 5px; padding: 4px 10px; border-radius: 999px; font-size: 11px; font-weight: 600; white-space: nowrap; }
.badge-dot { width: 6px; height: 6px; border-radius: 50%; flex-shrink: 0; }

.badge-under_review { background: var(--status-review-bg); color: var(--status-review); }
.badge-in_progress { background: var(--status-progress-bg); color: var(--status-progress); }
.badge-waiting_for_parts { background: var(--status-waiting-bg); color: var(--status-waiting); }
.badge-resolved { background: var(--status-resolved-bg); color: var(--status-resolved); }

/* Two states that are not workflow statuses but are shown in the same pill,
   because to a reader "where is my report" has one answer. Amber for a hold
   (it may still come back), red for a removal (it will not). */
.badge-flagged_for_review { background: #FEF3C7; color: #92400E; }
.badge-removed { background: var(--status-waiting-bg); color: var(--status-waiting); }

.badge-1_emergency { background: color-mix(in srgb, var(--priority-1) 12%, white); color: var(--priority-1); }
.badge-2_urgent { background: color-mix(in srgb, var(--priority-2) 12%, white); color: var(--priority-2); }
.badge-3_routine { background: color-mix(in srgb, var(--priority-3) 12%, white); color: var(--priority-3); }
.badge-4_low { background: color-mix(in srgb, var(--priority-4) 12%, white); color: var(--priority-4); }
.badge-unassigned { background: #F3F4F6; color: var(--text-disabled); }

/* Deadline state. Overdue borrows the "waiting" red because both mean
   "this needs a human now"; due_soon uses the in-progress amber. */
.badge-overdue  { background: var(--status-waiting-bg);  color: var(--status-waiting); }
.badge-due-soon { background: var(--status-progress-bg); color: var(--status-progress); }
.badge-on-track { background: #F3F4F6; color: var(--text-secondary); }

.badge-escalation { background: var(--escalation-bg); color: var(--escalation); }
.badge-flagged { background: var(--status-waiting-bg); color: var(--status-waiting); }

select.badge-select {
  border: none; padding: 4px 10px; border-radius: 999px; font-size: 11.5px; font-weight: 700;
  font-family: inherit; cursor: pointer;
}

/* ── Empty state ──────────────────────────────────────── */
.empty { text-align: center; padding: 48px 20px; color: var(--text-secondary); font-size: 13.5px; }

/* ── Slide-in panel (Concern Queue detail) ───────────────── */
.panel-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.15); z-index: 200; }
.slide-panel {
  position: fixed; top: 0; right: 0; width: 420px; height: 100vh; background: #fff;
  border-left: 1px solid var(--border); z-index: 300; overflow: hidden;
  display: flex; flex-direction: column; box-shadow: var(--shadow-panel);
}
.slide-panel-head { padding: 20px 20px 0; }
.slide-panel-section { padding: 16px 20px; border-bottom: 1px solid var(--border); }
.slide-panel-close { background: none; border: none; font-size: 20px; cursor: pointer; color: var(--text-secondary); flex-shrink: 0; }

/* The drawer's own three parts, matching the modals: the form grows with the
   role being edited -- a staff member adds a unit, a student a college and a
   year -- and Save used to slide off the bottom with it. */
.panel-head {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 22px 24px 16px;
  border-bottom: 1px solid var(--border);
}
.panel-title { font-family: 'Plus Jakarta Sans', system-ui, sans-serif; font-size: 17px; font-weight: 800; color: var(--text); }
.panel-body { padding: 20px 24px 0; overflow-y: auto; flex: 1; min-height: 0; }
.panel-foot {
  position: sticky;
  bottom: 0;
  z-index: 1;
  margin: 18px -24px 0;
  padding: 15px 24px;
  border-top: 1px solid var(--border);
  background: var(--bg);
  display: flex;
  gap: 10px;
}
.panel-foot .btn { flex: 1; height: 44px; }

/* ── Centered modal (Work Queue detail) ──────────────────── */
.modal-backdrop-center { position: fixed; inset: 0; background: rgba(0,0,0,0.4); z-index: 500; display: flex; align-items: center; justify-content: center; padding: 20px; }
.modal-center { background: #fff; border-radius: 20px; width: 800px; max-height: 90vh; overflow-y: auto; box-shadow: var(--shadow-modal); }

/* ── Priority level cards (routing panel) ─────────────────── */
.priority-card-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; }
.priority-card {
  position: relative; text-align: left; border-radius: 14px; padding: 12px 14px;
  cursor: pointer; background: #fff; border: 1.5px solid var(--pc);
  transition: transform .12s ease, box-shadow .12s ease, background-color .12s ease;
}
.priority-card:hover { transform: translateY(-2px); box-shadow: 0 6px 16px rgba(0,0,0,0.08); }
.priority-card.is-active { background: color-mix(in srgb, var(--pc) 8%, white); }
.priority-card-top { display: flex; align-items: center; gap: 9px; margin-bottom: 6px; }
.priority-card-icon {
  width: 26px; height: 26px; border-radius: 8px; flex-shrink: 0;
  background: color-mix(in srgb, var(--pc) 12%, white); color: var(--pc);
  display: flex; align-items: center; justify-content: center;
}
.priority-card-check {
  width: 18px; height: 18px; border-radius: 50%; flex-shrink: 0; background: var(--pc); color: #fff;
  display: flex; align-items: center; justify-content: center;
}
/* Takes the room between the glyph and the tick, so the tick stays right. */
.priority-card-title { flex: 1; min-width: 0; font-size: 13.5px; font-weight: 700; color: var(--pc); }
.priority-card-desc { font-size: 11px; line-height: 1.4; color: var(--text-secondary); }

/* ── Timeline ─────────────────────────────────────────── */

/* ── Photo / map placeholders ─────────────────────────── */
.photo-box { border-radius: 12px; background: var(--primary-light); display: flex; align-items: center; justify-content: center; color: var(--primary); }
.photo-thumb { width: 84px; height: 84px; object-fit: cover; border-radius: 8px; border: 1px solid var(--border); }
.map-box { border-radius: 12px; background: var(--primary-light); position: relative; overflow: hidden; }
.map-grid-line { position: absolute; background: rgba(26,122,60,0.12); }

/* ── Concern detail modal ─────────────────────────────── */

/* Two panes that scroll independently. The report is long and the review is
   short; letting one page scroll for both meant scrolling the verdict off
   the screen to read the thing you are giving a verdict on. */
.detail-modal { width: 880px; max-width: 100%; overflow: hidden; }
.detail-grid {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  /* Without an explicit row track a grid child sizes to its content and
     spills past max-height instead of scrolling inside it. */
  grid-template-rows: minmax(0, 1fr);
  max-height: 88vh;
}
/* Same reason, on the other axis: min-height:0 is what lets overflow-y
   actually bite on a grid child. */
.detail-main { padding: 26px 26px 30px; border-right: 1px solid var(--border); overflow-y: auto; min-height: 0; scrollbar-gutter: stable both-edges; }
.detail-side { padding: 26px; display: flex; flex-direction: column; overflow-y: auto; min-height: 0; background: var(--bg); scrollbar-gutter: stable both-edges; }

/* A flex child shrinks by default, so once the pane's contents ran past its
   height everything in it was squeezed -- a 48px select rendered at 24px and
   the buttons at 23. They keep their own height now and the pane scrolls,
   which is what the overflow above is for. */
.detail-side > * { flex-shrink: 0; }

/* The three controls of the status block, one height and one rhythm. */
.status-controls { display: flex; flex-direction: column; gap: 10px; }
.status-controls > .select,
.status-controls > .btn { height: 48px; margin: 0; }
.detail-side-title { font-family: 'Plus Jakarta Sans', system-ui, sans-serif; font-size: 16px; font-weight: 800; color: var(--text); }

/* One rhythm down the pane, so the eye can find the section it wants
   instead of reading a single unbroken column of facts. */
.detail-section { margin-top: 20px; }
.detail-label {
  margin: 0 0 9px;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--text-disabled);
}
.detail-desc { line-height: 1.6; color: var(--text); }
.detail-comments { max-height: 220px; overflow-y: auto; }
.detail-history { margin-bottom: 4px; }

.detail-photo { display: block; border-radius: 12px; overflow: hidden; border: 1px solid var(--border); }
.detail-photo img { display: block; width: 100%; height: 168px; object-fit: cover; }

/* The verdict stays put while the report scrolls past it.
   Sticky, not just pushed to the end: margin-top:auto only keeps it in view
   while the pane's contents fit, and once they overflow it goes below the
   fold with them. */
.detail-action {
  position: sticky;
  bottom: 0;
  margin-top: auto;
  padding-top: 14px;
  padding-bottom: 2px;
  background: linear-gradient(to bottom, transparent, var(--bg) 14px);
}
.detail-action .btn { height: 46px; }

.detail-head { display: flex; align-items: center; gap: 13px; }
.detail-head-text { min-width: 0; flex: 1; }
/* Priority always, plus the flagged pill when there is one. Stacked so a
   flagged concern does not widen the head and squeeze the title. */
.detail-head-badges { display: flex; flex-direction: column; align-items: flex-end; gap: 5px; flex-shrink: 0; }
.detail-tracking { font-size: 11px; color: var(--text-disabled); margin-bottom: 3px; }
.detail-title {
  margin: 0;
  font-family: 'Plus Jakarta Sans', system-ui, sans-serif;
  font-size: 18px;
  font-weight: 800;
  line-height: 1.22;
  letter-spacing: -0.01em;
  color: var(--text);
}
.detail-unit { margin: 3px 0 0; font-size: 12px; color: var(--text-secondary); }

/* Single-column modals -- routing, flagged reports, user forms.
   Same parts as the two-pane panel: a head that names the thing, a body
   that scrolls, and a foot that keeps the action reachable however long
   the body gets. Before this each one was its own arrangement of padding,
   and the action could scroll out of sight. */
.detail-modal.is-single { width: 560px; }
.detail-single { display: flex; flex-direction: column; max-height: 88vh; }
.detail-single-head {
  display: flex;
  align-items: flex-start;
  gap: 13px;
  padding: 22px 24px 16px;
  border-bottom: 1px solid var(--border);
}
/* The footer scrolls with the body and sticks to its bottom, rather than
   sitting outside as a sibling.
   A scrollbar narrows its scroller's content box, so a footer outside that
   box was inset by 24px while the fields inside it were inset by 24 plus a
   scrollbar -- 4px here, 15 or more on Windows, which is enough to read as
   a field that is not lined up with its own button. Inside, the two share
   one content box and cannot drift apart. */
.detail-single-body { padding: 4px 24px 0; overflow-y: auto; min-height: 0; }
.detail-single-foot {
  position: sticky;
  bottom: 0;
  z-index: 1;
  /* Negative margins let the bar's background reach the modal's edges while
     its button stays on the same line as the fields above it. */
  margin: 18px -24px 0;
  padding: 15px 24px;
  border-top: 1px solid var(--border);
  background: var(--bg);
}
.detail-single-foot .btn { height: 46px; }

/* One card per comment.
   The thread used to be rows divided by hairlines, which made a long comment
   and the name under it read as one run of text -- you could not see where
   one person stopped and the next began. A card each, and the reply keeps
   the same card set in from the left. */
.cmt-card {
  padding: 11px 13px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--card);
}
.cmt-card + .cmt-card { margin-top: 8px; }
.cmt-card.is-gone { background: var(--bg); border-style: dashed; }

/* The elbow joining a reply to the comment it answers: a line dropping out
   of the parent, turning right, and running into the reply's left edge. A
   straight rule down the card's side said "indented" but not "attached to
   that one". */
.cmt-card.is-reply {
  position: relative;
  margin-left: 30px;
}
.cmt-card.is-reply::before {
  content: '';
  position: absolute;
  left: -17px;
  /* Up through the 8px gap, so the drop appears to leave the card above. */
  top: -9px;
  width: 15px;
  height: 30px;
  border-left: 2px solid var(--border);
  border-bottom: 2px solid var(--border);
  border-bottom-left-radius: 9px;
}

/* When replies stack, the one above carries the line down to the next, so
   the branches read as one thread rather than a row of separate hooks. */
.cmt-card.is-reply:has(+ .cmt-card.is-reply)::after {
  content: '';
  position: absolute;
  left: -17px;
  top: 21px;
  bottom: -9px;
  width: 2px;
  background: var(--border);
}

.cmt-card-head { display: flex; align-items: center; gap: 9px; }
.cmt-who { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.cmt-name { font-size: 12.5px; font-weight: 700; color: var(--text); }
.cmt-when { font-size: 11px; color: var(--text-secondary); }

.cmt-body { margin: 8px 0 0; font-size: 13px; line-height: 1.5; color: var(--text); word-break: break-word; }
.cmt-body.is-deleted { font-style: italic; color: var(--text-disabled); }

.cmt-remove {
  flex-shrink: 0;
  border: 0;
  background: none;
  padding: 2px 4px;
  font-size: 11px;
  font-weight: 700;
  color: var(--text-secondary);
  cursor: pointer;
}
.cmt-remove:hover { color: var(--status-waiting); }

/* ── Review pane ──────────────────────────────────────────
   The pane opened on two lines of coloured text and then a lot of nothing
   above the verdict button. Its job is to answer three questions in order:
   where does this concern stand, did the checks pass, and what has happened
   to it -- so it is now laid out in that order. */
.review-state {
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--card);
  padding: 4px 14px;
  margin-bottom: 20px;
}
.review-state-row {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 9px 0;
}
.review-state-row + .review-state-row { border-top: 1px solid var(--border); }
.review-state-label { flex: 1; font-size: 12.5px; color: var(--text-secondary); }
.review-state-value { font-size: 13px; font-weight: 700; color: var(--text); }

/* A check that passed and one that failed should be distinguishable at a
   glance, not by reading which colour the sentence is. */
.check-list { display: flex; flex-direction: column; gap: 8px; margin-bottom: 20px; }
.check-row {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 11px 12px;
  border-radius: 12px;
  border: 1px solid var(--primary-light);
  background: var(--primary-light);
}
.check-row.is-fail { border-color: #FEE2E2; background: var(--status-waiting-bg); }

.check-mark {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 19px;
  height: 19px;
  margin-top: 1px;
  border-radius: 50%;
  background: var(--primary);
  color: #fff;
}
.check-row.is-fail .check-mark { background: var(--status-waiting); }

.check-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.check-name { font-size: 12.5px; font-weight: 700; color: var(--primary-dark); }
.check-row.is-fail .check-name { color: var(--status-waiting); }
.check-note { font-size: 11.5px; line-height: 1.45; color: var(--text-secondary); }

/* Newest first, with the current state marked. */

/* The app's progress rail, brought to the dashboard so a concern's journey
   reads the same to the crew as it does to the person who reported it: a
   filled check for each step taken, a ring on where it stands now, and a
   line joining them. Oldest first, because it is a journey and journeys are
   read forwards. */
.progress-rail { list-style: none; margin: 0; padding: 0; }
.progress-step { display: flex; gap: 12px; }
.progress-rail-col { display: flex; flex-direction: column; align-items: center; flex-shrink: 0; }

.progress-mark {
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: 50%;
  background: var(--primary);
  color: #fff;
  flex-shrink: 0;
}
.progress-step.is-current .progress-mark {
  background: var(--card);
  border: 2px solid var(--primary);
}

/* Still to come: numbered and grey, so what is left is visible without
   being mistaken for what has happened. */
.progress-step.is-upcoming .progress-mark {
  background: var(--bg);
  color: var(--text-disabled);
  font-size: 11px;
  font-weight: 700;
}
.progress-step.is-upcoming .progress-label { color: var(--text-disabled); }
.progress-step.is-upcoming .progress-line,
.progress-step.is-current .progress-line { background: var(--border); }
.progress-pip { width: 8px; height: 8px; border-radius: 50%; background: var(--primary); }

.progress-line { width: 2px; flex: 1; background: var(--primary); min-height: 10px; }

/* The end of the road. A removed report is the one outcome that is not
   progress, so it is the one step that is not green: a red cross, a bold
   label, and the line above it in red too, so the rail visibly stops
   rather than appearing to carry on somewhere. */
.progress-step.is-removed .progress-mark {
  background: var(--status-waiting);
  color: #fff;
}
.progress-step.is-removed .progress-label {
  font-weight: 700;
  color: var(--status-waiting);
}
.progress-step.is-removed .progress-note { color: var(--text); }

.progress-body { padding-bottom: 16px; min-width: 0; }
.progress-step:last-child .progress-body { padding-bottom: 0; }
.progress-label { font-size: 13.5px; color: var(--text); }
.progress-step.is-current .progress-label { font-weight: 700; }
.progress-time { margin-top: 1px; font-size: 11.5px; color: var(--text-secondary); }
.progress-note { margin-top: 3px; font-size: 11.5px; line-height: 1.45; color: var(--text-secondary); }

/* The concern card inside a Google info window.
   The window supplies its own frame and a close button, so the card drops
   its shadow and sits flush; the width is what the compact card is drawn
   for on the board. */
/* The map is 500px tall, so a pin near the middle has only ~250px of room
   above it for its window. Capping the card here means a long title or a
   location that wraps can never make the window taller than the map it
   sits in -- which is what was cutting the top off the card. Auto-pan
   handles the ordinary case; this is the floor it cannot fall through. */
/* The card's own width. maxWidth on the InfoWindow keeps Google from
   reserving its default ~650px box around it. */
.map-info { width: 252px; max-height: 232px; overflow-y: auto; overscroll-behavior: contain; }

/* A long report is clamped rather than allowed to set the window's height:
   the map is 500px tall, and a pin in the upper half has only a few
   hundred pixels above it to put a card in. */
.map-info .cc-desc {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}
.map-info .concern-card { border: 0; box-shadow: none; padding: 0; }

/* Room for the close button beside the title rather than above it. */
.map-info .cc-head { padding-right: 30px; }

/* No `position` here. Google places this bubble above its tail with an
   absolute position and a transform of its own; overriding it to relative
   dropped the bubble into normal flow and left it floating a couple of
   hundred pixels away from the pin it belonged to, tail still pointing at
   the pin. The close button below does not need it either -- the transform
   Google sets already makes this element a containing block for absolutely
   positioned children. */
.gm-style .gm-style-iw-c { border-radius: 16px; padding: 13px 15px; }
/* Google caps and scrolls this element itself; the cap on .map-info above
   replaces that, so the two do not fight over which one scrolls. */
.gm-style .gm-style-iw-d { overflow: hidden !important; max-height: none !important; }

/* Google gives the close button a header row of its own, which leaves a
   band of empty window above the card. Collapsing that row and floating the
   button in it puts the card back at the top -- and it anchors to the row
   rather than to the bubble, so the bubble keeps the positioning Google
   gives it and stays attached to its pin.

   The row is .gm-style-iw-chr and the button is inside it, not a direct
   child of .gm-style-iw-c: an earlier `>` selector here matched nothing,
   which is why the band was still there. */
.gm-style .gm-style-iw-chr {
  height: 0;
  min-height: 0;
  position: relative;
  z-index: 1;
}
.gm-style .gm-style-iw-chr button.gm-ui-hover-effect {
  position: absolute !important;
  top: 0 !important;
  right: 0 !important;
  width: 28px !important;
  height: 28px !important;
  min-width: 0 !important;
  min-height: 0 !important;
  padding: 0 !important;
  margin: 0 !important;
  opacity: 0.55;
}
.gm-style .gm-style-iw-chr button.gm-ui-hover-effect:hover { opacity: 1; }
.gm-style .gm-style-iw-chr button.gm-ui-hover-effect > span {
  margin: 0 !important;
  width: 15px !important;
  height: 15px !important;
}

/* ── Reports ──────────────────────────────────────────────
   One card, laid out in the order the job is done: choose a period, check
   the selection is not empty, see what the form will contain, download it.
   It used to be a run of ad-hoc inline styles with the download buttons
   last and unlabelled. */
.report-card { max-width: 640px; }

/* Segmented control for the three period modes. */
.seg { display: inline-flex; gap: 4px; padding: 4px; border-radius: 11px; background: var(--bg); }
.seg-btn {
  padding: 8px 18px;
  border: 0;
  border-radius: 8px;
  background: transparent;
  font-family: inherit;
  font-size: 13px;
  font-weight: 400;
  color: var(--text-secondary);
  white-space: nowrap;
  cursor: pointer;
  transition: background-color .12s ease, color .12s ease;
}
.seg-btn:hover { color: var(--text); }
.seg-btn.is-active {
  background: var(--card);
  color: var(--text);
  font-weight: 600;
  box-shadow: 0 1px 4px rgba(0,0,0,0.08);
}

/* The period fields size themselves rather than carrying inline widths. */
.report-fields { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 12px 14px; }
.report-field-wide { grid-column: 1 / -1; }

.report-summary { padding: 15px 17px; border-radius: 13px; background: var(--bg); }
.report-summary-head { font-size: 14px; font-weight: 700; color: var(--text); }
.report-summary-dates { margin-top: 2px; font-size: 12.5px; color: var(--text-secondary); }

.report-figures { display: flex; gap: 26px; margin: 13px 0 0; }
.report-figures dt { font-size: 11.5px; color: var(--text-secondary); }
.report-figures dd {
  margin: 2px 0 0;
  font-family: 'Plus Jakarta Sans', system-ui, sans-serif;
  font-size: 20px;
  font-weight: 800;
  color: var(--primary);
}


.report-note { margin: 0 0 12px; font-size: 12.5px; line-height: 1.5; color: var(--text-secondary); }
.report-download { height: 46px; display: inline-flex; align-items: center; justify-content: center; gap: 8px; }

/* Password field with a reveal toggle. The margin utilities callers pass
   (mb-4 and friends) land on the input, so the wrapper takes them off it and
   wears them itself -- otherwise the gap would sit inside the box. */
.pw-field { position: relative; display: block; }
.pw-field > .input { padding-right: 44px; }

.pw-toggle {
  position: absolute;
  top: 50%;
  right: 6px;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: 8px;
  background: none;
  color: var(--text-secondary);
  cursor: pointer;
}
.pw-toggle:hover { color: var(--text); background: var(--bg); }

/* ── Dropdown lists ───────────────────────────────────────
   The closed control has always been ours -- rounded, our border, our
   chevron -- but the list that opened underneath it was the operating
   system's: square corners, a blue highlight, a system font. It read as a
   piece of a different application dropped into the middle of ours.

   appearance:base-select hands the list to CSS. Guarded, because it is
   recent: a browser without it keeps the native list exactly as before,
   which is plain but never broken. */
@supports (appearance: base-select) {
  select.select, select.select-sm { appearance: base-select; }

  /* Our chevron is already painted as a background image; the built-in one
     would sit on top of it. */
  select.select::picker-icon, select.select-sm::picker-icon { display: none; }



  select.select::picker(select), select.select-sm::picker(select) {
    appearance: base-select;
    margin-top: 6px;
    padding: 6px;
    border: 1px solid var(--border);
    border-radius: 14px;
    background: var(--card);
    box-shadow: var(--shadow-modal);
    font-family: inherit;
  }

  select.select option, select.select-sm option {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 9px 11px;
    border-radius: 9px;
    font-size: 13.5px;
    color: var(--text);
    background: transparent;
    cursor: pointer;
  }

  select.select option:hover, select.select-sm option:hover,
  select.select option:focus, select.select-sm option:focus {
    background: var(--bg);
  }

  select.select option:checked, select.select-sm option:checked {
    background: var(--primary-light);
    color: var(--primary-dark);
    font-weight: 600;
  }

  select.select option::checkmark, select.select-sm option::checkmark {
    order: 1;
    margin-left: auto;
    color: var(--primary);
  }
}

/* ── Dashboard, fitted ────────────────────────────────────
   The counters and all five charts on one screen. The page used to run to
   about 1,460px against a 720px viewport, so the second row of charts sat
   below the fold and the first thing anyone did on opening the dashboard
   was scroll.

   Only this region is fitted. What follows it -- the recent concerns, the
   unit breakdown -- is meant to be scrolled to.

   Heights come down from .page-content, which is flex:1 of a 100vh column
   and so already definite. Nothing here takes its height from a canvas --
   that is what made the charts grow without stopping -- and min-height:0 at
   each step is what lets a flex child shrink below its own content. */
.dash-top {
  display: flex;
  flex-direction: column;
  gap: 16px;
  /* .page-content is 100vh minus the 64px topbar, less its own 28px padding
     top and bottom. */
  height: calc(100vh - 64px - 56px);
  min-height: 520px;
}
.dash-top > .dash-intro,
.dash-top > .dash-stats { flex: 0 0 auto; margin-bottom: 0; }

.dash-top > .charts-grid { margin-bottom: 0; min-height: 0; }
.dash-top > .charts-grid-main { flex: 1.15 1 0; }
.dash-top > .charts-grid-3 { flex: 1 1 0; }

.dash-top .chart-card { min-height: 0; padding: 16px 18px; }
.dash-top .chart-card-sub { margin-bottom: 8px; }
.dash-top .chart-card-canvas-wrap,
.dash-top .chart-card-canvas-wrap.h-sm { height: auto; flex: 1 1 0; min-height: 0; }

/* The meter keeps its circle but stops insisting on a square box, which
   would push the row past the screen on a short window. */
.dash-top .meter-wrap { aspect-ratio: auto; flex: 1 1 0; min-height: 0; width: 100%; max-width: none; }
.dash-top .meter-caption { flex: 0 0 auto; }

/* Below this the charts would be unreadable squeezed onto one screen, so
   the page goes back to scrolling. */
@media (max-height: 640px) {
  .dash-top { height: auto; }
  .dash-top > .charts-grid-main,
  .dash-top > .charts-grid-3 { flex: none; }
  .dash-top .chart-card-canvas-wrap { height: 220px; flex: none; }
  .dash-top .chart-card-canvas-wrap.h-sm { height: 180px; flex: none; }
  .dash-top .meter-wrap { aspect-ratio: 1 / 1; flex: none; max-width: 220px; }
}

/* ── Location block ───────────────────────────────────── */

.loc-map {
  position: relative;
  display: block;
  border-radius: 12px;
  overflow: hidden;
  border: 1px solid var(--border);
  background: var(--primary-light);
  transition: border-color 0.14s ease, box-shadow 0.14s ease;
}
.loc-map:hover { border-color: var(--primary); box-shadow: 0 2px 10px rgba(26,122,60,0.18); }
.loc-map img { display: block; width: 100%; height: 150px; object-fit: cover; }

.loc-map-schematic { position: relative; height: 150px; background: var(--primary-light); }

/* The same circle the Geospatial Map draws (.map-dot), so a concern looks
   like a concern on both screens. Sitting at the centre of an image that is
   itself centred on the coordinates, it marks the exact reported point. */
.loc-map-dot {
  position: absolute; left: 50%; top: 50%; transform: translate(-50%, -50%);
  width: 14px; height: 14px; border-radius: 50%;
  background: var(--primary); border: 2.5px solid #fff;
  box-shadow: 0 1px 4px rgba(0,0,0,0.35);
  pointer-events: none;
}

.loc-map-open {
  position: absolute;
  right: 9px;
  bottom: 9px;
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 5px 10px;
  border-radius: 999px;
  background: rgba(255,255,255,0.94);
  font-size: 11px;
  font-weight: 700;
  color: var(--primary);
  box-shadow: 0 1px 5px rgba(0,0,0,0.16);
}

/* The checking map on Flagged Reports. Deliberately not a link: it exists
   so a flag reason can be judged without leaving the review. */
.flagmap {
  border: 1px solid var(--border); border-radius: 12px; overflow: hidden; background: var(--bg);
}
.flagmap img { display: block; width: 100%; height: 200px; object-fit: cover; }
.flagmap-none {
  display: flex; align-items: center; gap: 8px; justify-content: center;
  height: 120px; padding: 0 18px; text-align: center;
  font-size: 12.5px; color: var(--text-secondary); background: var(--primary-light);
}
.flagmap-key {
  display: flex; flex-wrap: wrap; gap: 6px 16px; align-items: center;
  padding: 9px 12px; border-top: 1px solid var(--border); background: #fff;
  font-size: 11.5px; color: var(--text-secondary);
}
.flagmap-key span { display: inline-flex; align-items: center; gap: 6px; }
.flagmap-swatch {
  width: 10px; height: 10px; border-radius: 50%; flex-shrink: 0; display: inline-block;
}
.flagmap-swatch.is-ring {
  background: rgba(26,122,60,0.10); border: 2px solid rgba(26,122,60,0.6);
}
.flagmap-verdict { margin-top: 6px; font-size: 12px; font-weight: 600; }
.flagmap-verdict.is-out { color: var(--status-waiting); }
.flagmap-verdict.is-in  { color: var(--primary); }

.loc-meta { margin-top: 9px; }
.loc-place { font-size: 12.5px; font-weight: 600; color: var(--text); }
.loc-coords { margin-top: 2px; font-size: 11px; color: var(--text-secondary); }

.loc-empty {
  display: flex; align-items: center; gap: 8px;
  padding: 13px 14px; border-radius: 12px;
  border: 1px dashed var(--border);
  font-size: 12.5px; color: var(--text-secondary);
}

@media (max-width: 900px) {
  .detail-grid { grid-template-columns: 1fr; grid-template-rows: none; max-height: 88vh; overflow-y: auto; }
  .detail-main { border-right: 0; border-bottom: 1px solid var(--border); overflow: visible; }
  .detail-side { overflow: visible; }
}

/* ── Comments ─────────────────────────────────────────── */

.cmt-row { display: flex; align-items: flex-start; gap: 8px; margin-bottom: 8px; }

/* A reply is set in from the left, with a rule down the gap so the eye can
   follow it back to the comment it answers. One level only. */
.cmt-row.is-reply,
.flex-between.is-reply { margin-left: 26px; border-left: 2px solid var(--border); padding-left: 12px; }

/* Nobody uploads a photo, so the circle is the author's initials -- the same
   treatment the sidebar and the message threads already use. */
.cmt-avatar {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 28px;
  height: 28px;
  border-radius: 50%;
  background: var(--primary-light);
  color: var(--primary);
  font-family: 'Plus Jakarta Sans', system-ui, sans-serif;
  font-size: 11px;
  font-weight: 700;
}
.is-reply .cmt-avatar { width: 24px; height: 24px; font-size: 10px; }

.cmt-reply-btn {
  border: 0;
  background: none;
  padding: 0;
  font-size: 11px;
  font-weight: 700;
  color: var(--primary);
  cursor: pointer;
}
.cmt-reply-btn:hover { text-decoration: underline; }

.cmt-replying {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 8px;
  padding: 7px 11px;
  border-radius: 10px;
  background: var(--primary-light);
  font-size: 12px;
  color: var(--primary-dark);
}
.cmt-replying button {
  margin-left: auto;
  border: 0;
  background: none;
  font-size: 16px;
  line-height: 1;
  color: var(--primary);
  cursor: pointer;
}

/* ── Messages ─────────────────────────────────────────── */
.msg-bubble { max-width: 320px; padding: 10px 14px; font-size: 13px; color: var(--text); line-height: 1.5; }
.msg-bubble-mine { background: var(--primary-light); border-radius: 16px 4px 16px 16px; }
.msg-bubble-theirs { background: #F3F4F6; border-radius: 4px 16px 16px 16px; }
.msg-time { font-size: 11px; color: var(--text-disabled); margin-top: 4px; }
.thread-item { padding: 14px 16px; cursor: pointer; display: flex; gap: 12px; align-items: flex-start; border-bottom: 1px solid var(--border); border-left: 3px solid transparent; transition: all 0.1s; }
.thread-item:hover { background: #FAFAFA; }
.thread-item.active { background: var(--primary-light); border-left-color: var(--primary); }
.thread-avatar { width: 42px; height: 42px; border-radius: 50%; background: var(--primary-light); display: flex; align-items: center; justify-content: center; font-size: 16px; font-weight: 700; color: var(--primary); flex-shrink: 0; }
.unread-badge { flex-shrink: 0; min-width: 18px; height: 18px; padding: 0 5px; border-radius: 999px; background: var(--primary); color: #fff; font-size: 10px; font-weight: 700; display: flex; align-items: center; justify-content: center; }

/* Concern shared into a conversation as a card */
.msg-attach-card { display: flex; align-items: center; gap: 8px; margin-top: 8px; padding: 8px 10px; border: 1px solid var(--border); border-radius: 10px; background: #fff; text-decoration: none; color: inherit; }
.msg-attach-card:hover { border-color: var(--primary); }
.msg-attach-title { display: block; font-size: 12.5px; font-weight: 600; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.msg-attach-meta { display: block; font-size: 11px; color: var(--text-secondary); margin-top: 1px; }
.msg-bubble-mine .msg-attach-card { background: rgba(255,255,255,0.75); border-color: rgba(0,0,0,0.06); }

/* Comment action menu (⋮). Alpine ships with Livewire, so no new JS. */
/* ── Concern board (kanban) ───────────────────────────── */

.view-toggle { display: flex; gap: 2px; background: var(--bg); border-radius: 10px; padding: 3px; }
.view-toggle button {
  display: flex; align-items: center; justify-content: center; width: 34px; height: 30px;
  border: none; background: transparent; border-radius: 8px; cursor: pointer; color: var(--text-secondary);
}
.view-toggle button.is-active { background: #fff; color: var(--primary); box-shadow: 0 1px 3px rgba(0,0,0,0.10); }

.kanban {
  display: grid; grid-auto-flow: column; grid-auto-columns: minmax(268px, 1fr);
  gap: 16px; overflow-x: auto; padding-bottom: 8px; align-items: start;
}
.kanban-col { border-radius: 16px; padding: 12px; background: var(--bg); }

/* Each column is tinted by its own priority colour, so the board reads as
   urgency left-to-right even before the labels are read. */
.kanban-col.tone-p1         { background: var(--status-waiting-bg); }
.kanban-col.tone-p2         { background: var(--status-progress-bg); }
.kanban-col.tone-p3         { background: #EFF6FF; }
.kanban-col.tone-p4         { background: #F3F4F6; }

.kanban-head { display: flex; align-items: center; gap: 8px; padding: 2px 4px 12px; }
.kanban-title { font-size: 14px; font-weight: 800; margin: 0; }
.kanban-count {
  min-width: 22px; height: 22px; border-radius: 50%; background: #fff;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 11px; font-weight: 700;
}
.tone-p1 .kanban-title, .tone-p1 .kanban-count         { color: var(--priority-1); }
.tone-p2 .kanban-title, .tone-p2 .kanban-count         { color: var(--priority-2); }
.tone-p3 .kanban-title, .tone-p3 .kanban-count         { color: var(--priority-3); }
.tone-p4 .kanban-title, .tone-p4 .kanban-count         { color: var(--priority-4); }

/* No cap and no scrollbar of its own: a column runs to its last card and
   the page scrolls, so the board is as tall as its busiest column. Capping
   each one meant four little scroll areas inside a page that did not
   scroll, and cards below the fold that nothing indicated were there. */
.kanban-body { display: flex; flex-direction: column; gap: 10px; }

/* ── Concern card ─────────────────────────────────────────
   One card, every surface. See resources/views/components/concern-card.blade.php
   -- board columns, work queue, routing, flagged, archived and the detail
   modal all render this, so spacing and hierarchy stay in step.            */

.concern-card {
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 15px 17px;
  display: flex;
  flex-direction: column;
  /* Deliberately tight. The gaps that matter are set per group below, so
     that the card reads as three things -- what it is, where and when, and
     where it stands -- rather than five items floating equally apart. */
  gap: 7px;
  box-shadow: var(--shadow-card);
}

.concern-card.is-interactive { cursor: pointer; transition: transform .12s ease, box-shadow .12s ease, border-color .12s ease; }
.concern-card.is-interactive:hover { transform: translateY(-1px); border-color: color-mix(in srgb, var(--primary) 30%, var(--border)); box-shadow: 0 4px 16px rgba(16,40,26,0.10); }
.concern-card.is-interactive:focus-visible { outline: 2px solid var(--primary); outline-offset: 2px; }

/* head: unit glyph, headings, priority */
/* Centred, not top-aligned: the glyph is taller than a one-line title
   and its byline, so starting them together left it hanging below. */
.cc-head { display: grid; grid-template-columns: auto 1fr auto; gap: 11px; align-items: center; }
.cc-headings { min-width: 0; }

.cc-title {
  font-family: 'Plus Jakarta Sans', system-ui, sans-serif;
  font-size: 16.5px;
  font-weight: 800;
  /* Tighter than the body: a two-line title should read as one object. */
  line-height: 1.22;
  letter-spacing: -0.01em;
  color: var(--text);
  margin: 0;
  overflow-wrap: anywhere;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* Secondary, and sized to say so -- five points below the title rather than
   one, so the eye reaches the title first without having to choose. */
.cc-byline {
  font-size: 11.5px;
  font-weight: 400;
  line-height: 1.3;
  color: var(--text-secondary);
  margin: 3px 0 0;
}

.badge-esc-mark { font-weight: 800; margin-left: 1px; }

/* the rule under the head, as on the reference card */
.cc-desc {
  border-top: 1px solid var(--border);
  margin: 3px 0 0;
  padding-top: 10px;
  font-size: 12.5px;
  line-height: 1.5;
  /* Back at body weight. Anything heavier read as a second heading
     competing with the title. The description
     gets its presence from a darker ink instead, which is the part that was
     actually making it recede. */
  color: var(--text-body);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}

/* Where and when. Sits close to the description it qualifies. */
.cc-meta { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 1px; }

.cc-superseded {
  margin: 0;
  align-self: flex-start;
  font-size: 11px;
  font-weight: 600;
  color: var(--text-secondary);
  background: var(--bg);
  border-radius: 999px;
  padding: 3px 9px;
}

/* Where it stands. Given the most room, because it answers a different
   question from everything above it. */
.cc-foot { display: flex; align-items: center; gap: 10px; margin-top: 4px; }
.cc-stats { display: flex; align-items: center; gap: 13px; }
.cc-status { margin-left: auto; }

/* compact: narrow board columns */
.concern-card.is-compact { padding: 12px 13px; gap: 6px; border-radius: 14px; }
.concern-card.is-compact .cc-head { gap: 9px; }
.concern-card.is-compact .cc-title { font-size: 14px; line-height: 1.25; }
.concern-card.is-compact .cc-byline { font-size: 10.5px; margin-top: 2px; }
.concern-card.is-compact .cc-desc { font-size: 11.5px; line-height: 1.45; padding-top: 9px; margin-top: 2px; -webkit-line-clamp: 2; }
.concern-card.is-compact .cc-meta { gap: 4px; }
.concern-card.is-compact .cc-foot { gap: 8px; margin-top: 3px; }
.concern-card.is-compact .cc-stats { gap: 11px; }
.concern-card.is-compact .badge { font-size: 10px; padding: 3px 8px; }

/* ── Unit glyph ───────────────────────────────────────────
   Green throughout: the unit is identified by its shape, not its colour,
   so the palette stays one accent and priority keeps the colour coding.  */

.unit-icon {
  display: inline-grid;
  place-items: center;
  flex-shrink: 0;
  background: var(--primary-light);
  color: var(--primary);
  border-radius: 12px;
}

.unit-icon-sm { width: 32px; height: 32px; border-radius: 10px; }
.unit-icon-md { width: 42px; height: 42px; }
.unit-icon-lg { width: 52px; height: 52px; border-radius: 14px; }

/* ── Meta chip ────────────────────────────────────────────*/

.meta-chip {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  max-width: 100%;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--border);
  background: var(--bg);
  font-size: 10.5px;
  line-height: 1.5;
  letter-spacing: 0.005em;
  color: var(--text-secondary);
  white-space: nowrap;
}

.meta-chip-icon { flex-shrink: 0; opacity: .75; }
.meta-chip-text { overflow: hidden; text-overflow: ellipsis; }

.meta-chip-danger { background: var(--status-waiting-bg); border-color: color-mix(in srgb, var(--status-waiting) 25%, transparent); color: var(--status-waiting); font-weight: 600; }
.meta-chip-warn   { background: var(--status-progress-bg); border-color: color-mix(in srgb, var(--status-progress) 28%, transparent); color: var(--status-progress); font-weight: 600; }

/* ── Stat count ───────────────────────────────────────────*/

.stat-count { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; color: var(--text-secondary); }
.stat-count svg { opacity: .6; }
.stat-count b { font-family: 'Plus Jakarta Sans', system-ui, sans-serif; font-weight: 700; color: var(--text); }

.kanban-body > .concern-card { box-shadow: 0 1px 3px rgba(16,40,26,0.06); }
.kanban-empty { font-size: 12px; color: var(--text-disabled); text-align: center; padding: 14px 0; margin: 0; }

.cmt-menu { position: absolute; top: 4px; right: 4px; }
/* Was opacity .55 on a bare glyph, which testers read as "there is no
   edit or delete button". It is the only way to reach either, so it has to
   look like a control at rest, not only on hover. */
.cmt-menu-btn {
  background: var(--bg); border: 1px solid var(--border); cursor: pointer; line-height: 1;
  font-size: 15px; padding: 2px 6px; border-radius: 6px; color: var(--text-secondary); opacity: 1;
}
.cmt-menu-btn:hover { opacity: 1; background: rgba(0,0,0,0.06); }
.cmt-menu-list {
  position: absolute; right: 0; top: 100%; z-index: 20; min-width: 132px;
  background: #fff; border: 1px solid var(--border); border-radius: 10px;
  box-shadow: var(--shadow-modal); padding: 4px; margin-top: 2px;
}
.cmt-menu-list button {
  display: block; width: 100%; text-align: left; background: transparent; border: none;
  cursor: pointer; font-size: 13px; padding: 8px 10px; border-radius: 7px; color: var(--text);
}
.cmt-menu-list button:hover { background: var(--bg); }
.cmt-menu-list button.danger { color: var(--status-waiting); }
[x-cloak] { display: none !important; }

.msg-day-sep { align-self: center; font-size: 11px; color: var(--text-disabled); background: #F3F4F6; padding: 3px 10px; border-radius: 999px; }

/* ── Utility ──────────────────────────────────────────── */
.flex { display: flex; }
.flex-between { display: flex; align-items: center; justify-content: space-between; }
.flex-col { display: flex; flex-direction: column; }
.items-center { align-items: center; }
.gap-6 { gap: 6px; } .gap-8 { gap: 8px; } .gap-10 { gap: 10px; } .gap-12 { gap: 12px; } .gap-16 { gap: 16px; } .gap-20 { gap: 20px; }
.mt-4 { margin-top: 4px; } .mt-6 { margin-top: 6px; } .mt-8 { margin-top: 8px; } .mt-12 { margin-top: 12px; } .mt-16 { margin-top: 16px; } .mt-20 { margin-top: 20px; } .mt-24 { margin-top: 24px; }
.mb-8 { margin-bottom: 8px; } .mb-12 { margin-bottom: 12px; } .mb-16 { margin-bottom: 16px; } .mb-20 { margin-bottom: 20px; } .mb-24 { margin-bottom: 24px; }
.text-muted { color: var(--text-secondary); }
.text-disabled { color: var(--text-disabled); }
.text-sm { font-size: 12.5px; }
.text-xs { font-size: 11px; }
.font-semibold { font-weight: 600; }
.font-bold { font-weight: 700; }
.w-full { width: 100%; }
.filters-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.kv-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 20px; font-size: 13px; }
.kv-label { color: var(--text-secondary); }
.progress-track { height: 4px; width: 60px; background: var(--border); border-radius: 99px; }
.progress-fill { height: 4px; border-radius: 99px; }

/* ── Auth ─────────────────────────────────────────────── */
.auth-shell { min-height: 100vh; display: flex; align-items: center; justify-content: center; background: var(--bg); padding: 24px; }

/* Two panels in one card: a poster that says what the system is for, and
   the form. Below 860px the poster is dropped rather than stacked — on a
   laptop, stacking pushes the password field under the fold. */
.auth-card { background: #fff; border-radius: 26px; width: 100%; max-width: 940px;
             display: grid; grid-template-columns: 1fr 1fr; overflow: hidden;
             box-shadow: 0 1px 2px rgba(16,40,26,.05), 0 24px 70px rgba(16,40,26,.14); }

/* Layered radials rather than a linear ramp: a straight gradient reads as
   a background, and the point of this panel is that it reads as a surface
   with light falling on it. */
.auth-poster { position: relative; display: flex; flex-direction: column; justify-content: space-between;
               padding: 34px; min-height: 520px; color: #fff;
               background:
                 radial-gradient(68% 58% at 78% 14%, rgba(228,250,235,.92) 0%, rgba(228,250,235,0) 62%),
                 radial-gradient(62% 54% at 94% 76%, #45D97E 0%, rgba(69,217,126,0) 64%),
                 radial-gradient(58% 52% at 22% 18%, #1DAE52 0%, rgba(29,174,82,0) 66%),
                 radial-gradient(88% 74% at 4% 96%, #042A13 0%, rgba(4,42,19,0) 68%),
                 linear-gradient(152deg, #14833F 0%, #0F5C2C 58%, #0A3F20 100%); }

.auth-poster-mark { display: flex; align-items: center; gap: 13px; }
.auth-poster-seal { width: 44px; height: 44px; object-fit: contain; }
.auth-poster-rule { width: 1px; height: 26px; background: rgba(255,255,255,.35); flex-shrink: 0; }
.auth-poster-logo { height: 37px; width: auto; object-fit: contain; }

.auth-poster-copy { max-width: 330px; }
.auth-poster-eyebrow { font-size: 13px; font-weight: 500; color: rgba(255,255,255,.78); margin-bottom: 10px; }
.auth-poster-line { font-size: 26px; line-height: 1.28; font-weight: 700; letter-spacing: -.01em; }

.auth-panel { padding: 48px 46px; display: flex; flex-direction: column; justify-content: center; }
.auth-title { font-size: 42px; font-weight: 700; letter-spacing: -.026em; line-height: 1.08; margin: 0; }
.auth-sub { font-size: 13.5px; color: var(--text-secondary); margin: 6px 0 0; line-height: 1.5; max-width: 330px; }
.auth-panel form { margin-top: 26px; }

.auth-foot { text-align: center; margin-top: 18px; }
.auth-foot a { font-size: 13px; font-weight: 600; color: var(--primary); }
.auth-foot a:hover { text-decoration: underline; }

@media (max-width: 860px) {
    .auth-card { grid-template-columns: 1fr; max-width: 440px; }
    .auth-poster { display: none; }
    .auth-panel { padding: 38px 30px; }
}

/* ── Dashboard charts ─────────────────────────────────── */
.charts-grid { display: grid; gap: 20px; margin-bottom: 20px; }
.charts-grid-2 { grid-template-columns: 1fr 1fr; }
.charts-grid-3 { grid-template-columns: repeat(3, 1fr); }
.charts-grid-main { grid-template-columns: 2fr 1fr; }

.chart-card { background: #fff; border-radius: 20px; box-shadow: var(--shadow-card); padding: 20px; display: flex; flex-direction: column; }
.chart-card-title { font-size: 14px; font-weight: 600; color: var(--text); margin-bottom: 4px; }
.chart-card-sub { font-size: 12px; color: var(--text-secondary); margin-bottom: 12px; }
/* A definite height, not a minimum.
   Chart.js sizes a responsive canvas from its container, so a container that
   in turn takes its height from the canvas has nothing to settle on: every
   resize round re-measures a number it just wrote, and on a display whose
   device pixel ratio is not 1 the rounding creeps upward until the chart
   fills the page. min-height left exactly that hole. */
.chart-card-canvas-wrap { position: relative; flex: 0 0 auto; height: 240px; }
.chart-card-canvas-wrap.h-sm { height: 190px; }
.chart-card-canvas-wrap > canvas { display: block; }

.meter-wrap { position: relative; width: 100%; max-width: 220px; margin: 0 auto; aspect-ratio: 1 / 1; }
.meter-value {
  position: absolute; inset: 0; display: flex; align-items: center; justify-content: center;
  font-size: 26px; font-weight: 700; color: var(--text); pointer-events: none;
}
.meter-caption { text-align: center; font-size: 12px; color: var(--text-secondary); margin-top: 10px; }

@media (max-width: 1100px) {
  .charts-grid-2, .charts-grid-3, .charts-grid-main { grid-template-columns: 1fr; }
}

/* Unit glyph inline in a table cell, plus the small count pip beside it. */
.cell-unit { display: inline-flex; align-items: center; gap: 8px; color: var(--primary); font-weight: 500; }
.count-pip {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 22px; height: 22px; padding: 0 6px; border-radius: 999px;
  background: var(--escalation-bg); color: var(--escalation);
  font-size: 11px; font-weight: 700;
}

/* Result of confirming an email address from the link in the message. */
.verify-mark {
  width: 62px; height: 62px; border-radius: 50%;
  display: grid; place-items: center; margin: 4px auto 14px;
}
.verify-mark.is-ok  { background: var(--primary-light); color: var(--primary); }
.verify-mark.is-bad { background: var(--status-waiting-bg); color: var(--status-waiting); }

/* ── Sidebar attention counts ─────────────────────────────
   A number against a link means somebody has to do something there. Pages
   that only report on the past carry nothing, so the marks stay meaningful. */

.nav-item { position: relative; }

.nav-badge {
  margin-left: auto;
  min-width: 20px;
  height: 20px;
  padding: 0 6px;
  border-radius: 999px;
  background: var(--status-waiting);
  color: #fff;
  font-size: 11px;
  font-weight: 700;
  line-height: 20px;
  text-align: center;
  flex-shrink: 0;
}

.nav-item.active .nav-badge { background: var(--status-waiting); }

/* The dot rides the icon, so it survives the sidebar collapsing. */
.nav-icon { position: relative; }

.nav-dot {
  position: absolute;
  top: -1px; right: -2px;
  width: 8px; height: 8px;
  border-radius: 50%;
  background: var(--status-waiting);
  border: 2px solid var(--card);
  display: none;
}

html.sidebar-collapsed .nav-badge { display: none; }
html.sidebar-collapsed .nav-dot   { display: block; }

/* Pointer from the queue to the triage page, so a new report never reads as lost. */
.queue-hint {
  display: flex; align-items: center; gap: 10px;
  margin: 0 0 16px;
  padding: 11px 14px;
  border: 1px solid color-mix(in srgb, var(--escalation) 26%, transparent);
  background: var(--escalation-bg);
  border-radius: 12px;
  font-size: 13px;
  color: var(--text);
}
.queue-hint svg { color: var(--escalation); flex-shrink: 0; }
.queue-hint span { flex: 1; }


/* ── Possible-duplicate check on Priority & Routing ───────
   Shown before the priority controls, because whether this concern is a
   duplicate decides whether it needs a priority at all. */

.dupe-check {
  margin: 4px 24px 0;
  padding: 14px;
  border: 1px solid color-mix(in srgb, var(--escalation) 28%, transparent);
  background: var(--escalation-bg);
  border-radius: 14px;
  display: flex; flex-direction: column; gap: 10px;
}

.dupe-head {
  display: flex; align-items: flex-start; gap: 8px;
  font-size: 13px; font-weight: 600; color: var(--text); line-height: 1.45;
}
.dupe-head svg { color: var(--escalation); flex-shrink: 0; margin-top: 2px; }

.dupe-hint { margin: 0; font-size: 11.5px; line-height: 1.5; color: var(--text-secondary); }

/* Read once, then in the way every time — so it folds. */
.dupe-more { font-size: 11.5px; color: var(--text-secondary); }
.dupe-more > summary {
  cursor: pointer; list-style: none;
  font-weight: 600; color: var(--escalation);
  display: inline-flex; align-items: center; gap: 5px;
}
.dupe-more > summary::-webkit-details-marker { display: none; }
.dupe-more > summary::after {
  content: ''; width: 5px; height: 5px; flex-shrink: 0;
  border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(45deg) translate(-1px, -1px);
  transition: transform .15s ease;
}
.dupe-more[open] > summary::after { transform: rotate(-135deg) translate(-1px, -1px); }
.dupe-more > p { margin: 8px 0 0; line-height: 1.5; }

.dupe-row {
  /* The button sits under the report it is about, not beside it. Alongside,
     it squeezed the description into a narrow column and read as belonging
     to the whole list rather than to this one concern. */
  display: flex; flex-direction: column; align-items: stretch; gap: 5px;
  background: var(--card);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 12px;
}

.dupe-row-head { display: flex; align-items: flex-start; gap: 10px; }
.dupe-title { flex: 1; min-width: 0; font-size: 13.5px; font-weight: 600; color: var(--text); line-height: 1.35; }

/* Distance is the whole reason this card is on the screen, so it reads as
   a figure instead of the fourth item in a run of middot-separated meta. */
.dupe-dist {
  flex-shrink: 0;
  font-size: 12px; font-weight: 700;
  font-variant-numeric: tabular-nums;
  color: var(--escalation);
  background: color-mix(in srgb, var(--escalation) 12%, transparent);
  border-radius: 7px;
  padding: 2px 7px;
}

.dupe-desc  { font-size: 11.5px; color: var(--text-secondary); line-height: 1.45;
              display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.dupe-meta  { font-size: 11px; color: var(--text-secondary); }
.dupe-act   { margin-top: 5px; }

/* Reports folded into the open concern, with the way back. */
.merged-list { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }
.merged-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 10px 12px; background: var(--escalation-bg);
}
.merged-row + .merged-row { border-top: 1px solid var(--border); }

/* Building and floor on a duplicate candidate — the detail a pin cannot carry. */
.dupe-place {
  display: flex; align-items: center; gap: 5px;
  font-size: 12px; font-weight: 600; color: var(--text);
}
.dupe-place svg { color: var(--text-secondary); flex-shrink: 0; }

/* ── Removing a report ────────────────────────────────────────────────
   The panel that replaces the Remove button in the Flagged Reports modal.
   Asking for a reason is not ceremony: the sentence typed here is what the
   reporter receives, so the field is the notification's body, not a
   confirmation prompt. */
.removal-panel {
    display: flex;
    flex-direction: column;
    gap: 8px;
    padding: 14px;
    /* Same family as .btn-danger, which the panel's own button uses. */
    border: 1px solid var(--status-waiting);
    border-radius: 12px;
    background: var(--status-waiting-bg);
}

.removal-label {
    font-weight: 700;
    font-size: 13.5px;
    color: var(--ink);
}

.removal-hint,
.removal-note {
    font-size: 12px;
    line-height: 1.5;
    color: var(--muted);
}

.removal-note {
    margin-top: 2px;
}

.removal-input {
    width: 100%;
    resize: vertical;
    min-height: 68px;
    background: #fff;
}

.removal-error {
    font-size: 12px;
    font-weight: 600;
    color: var(--status-waiting);
}

/* The attachment is a button now, not a link. Strip the browser's button
   chrome so it still reads as the card it did before.
   Only the chrome: the border, background and hover come from
   .msg-attach-card above, and a `border: 0` here would beat them on
   specificity and flatten the card. */
button.msg-attach-card {
    width: 100%;
    font: inherit;
    text-align: left;
    cursor: pointer;
    -webkit-appearance: none;
    appearance: none;
}

/* ── Sort control ─────────────────────────────────────────────────────
   The glyph sits inside the same rounded box as the select rather than
   beside it, so the pair reads as one control and lines up with the
   filter selects next to it. */
.sort-select {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding-left: 10px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: #fff;
}
.sort-select:focus-within { border-color: var(--primary); }
.sort-select-label {
    display: flex;
    color: var(--text-secondary);
    flex-shrink: 0;
}
/* The select supplies the height and the caret; the wrapper supplies the
   frame, so the select drops its own. */
.sort-select .select-sm {
    border: 0;
    border-radius: 0 10px 10px 0;
    padding-left: 2px;
    background-color: transparent;
}

/* ── Photos in a conversation ─────────────────────────────────────────
   A picture is often the whole message ("this socket, not that one"), so
   it sits in the bubble at a size you can actually judge, and opens full
   size in a tab when that is not enough. */
.msg-photo {
    display: block;
    margin-top: 8px;
    border-radius: 10px;
    overflow: hidden;
    max-width: 260px;
}
.msg-photo img { display: block; width: 100%; height: auto; }

/* The composer grows to hold a pending photo; the thread above gives up
   the space rather than the composer being clipped. */
.msg-composer {
    flex-shrink: 0;
    padding: 8px 16px;
    border-top: 1px solid var(--border);
    display: flex;
    flex-direction: column;
    gap: 8px;
}
.msg-composer-row { display: flex; align-items: center; gap: 10px; }
.msg-composer-error { font-size: 12px; font-weight: 600; color: var(--status-waiting); }
.msg-composer-hint { font-size: 12px; color: var(--text-secondary); }

/* What is about to be sent, so nobody discovers they attached the wrong
   picture only after it has gone. */
.msg-pending {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg);
}
.msg-pending img { width: 40px; height: 40px; border-radius: 7px; object-fit: cover; flex-shrink: 0; }
.msg-pending-text { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.msg-pending-text strong { font-size: 12.5px; }
.msg-pending-text span {
    font-size: 11.5px; color: var(--text-secondary);
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.msg-pending-x {
    border: 0; background: transparent; cursor: pointer;
    color: var(--text-secondary); display: flex; flex-shrink: 0; padding: 4px;
}
.msg-pending-x:hover { color: var(--status-waiting); }

.msg-attach-btn {
    width: 38px; height: 38px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    border: 1px solid var(--border); border-radius: 10px;
    background: #fff; color: var(--text-secondary); cursor: pointer;
}
.msg-attach-btn:hover { border-color: var(--primary); color: var(--primary); }

.msg-send-btn {
    width: 38px; height: 38px; flex-shrink: 0;
    border-radius: 50%; background: var(--primary); border: none; color: #fff;
    cursor: pointer; display: flex; align-items: center; justify-content: center;
}
.msg-send-btn:disabled { opacity: 0.6; cursor: default; }

/* The Reopened column. Purple, the same colour escalation already uses
   elsewhere, because both mean "this one is not going normally" -- and
   deliberately not red, which on these boards means Emergency. */
.kanban-col.tone-reopen { background: var(--escalation-bg); }
.tone-reopen .kanban-title, .tone-reopen .kanban-count { color: var(--escalation); }

/* Finished work still owed a satisfaction form. Green, because nothing is
   wrong with it -- it is done, and only the asking is left. */
.kanban-col.tone-feedback { background: var(--primary-light, #EAF6EE); }
.tone-feedback .kanban-title, .tone-feedback .kanban-count { color: var(--primary); }

/* The reopened strip across the top of a card. Full-bleed so it reads as a
   condition of the whole card rather than another chip among the meta. */
.cc-reopened {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: -14px -14px 12px;
    padding: 7px 14px;
    border-radius: 14px 14px 0 0;
    background: var(--escalation-bg);
    color: var(--escalation);
    font-size: 11.5px;
    font-weight: 700;
}
.concern-card.is-compact .cc-reopened { margin: -12px -12px 10px; padding: 6px 12px; }

/* ── Notifications ────────────────────────────────────────────────────
   One row per thing the system said. Unread carries a dot and a tinted
   ground; read fades back so the list can be scanned for what is new
   without hiding what is not. */
.notif-row { display: flex; align-items: stretch; border-bottom: 1px solid var(--border); }
.notif-row:last-child { border-bottom: 0; }
.notif-row.is-unread { background: var(--primary-light); }
.notif-row:hover { background: var(--bg); }

.notif-main {
    display: flex;
    gap: 10px;
    flex: 1;
    min-width: 0;
    padding: 14px 16px;
    border: 0;
    background: transparent;
    text-align: left;
    cursor: pointer;
    font: inherit;
}
.notif-dot {
    width: 8px; height: 8px; border-radius: 50%;
    margin-top: 6px; flex-shrink: 0;
    background: transparent;
}
.notif-row.is-unread .notif-dot { background: var(--primary); }

.notif-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.notif-title { font-size: 13.5px; font-weight: 700; color: var(--text); }
.notif-row.is-unread .notif-title { color: var(--primary-dark, var(--text)); }
/* pre-line: some bodies are two blocks -- what happened, then what it means. */
.notif-text { font-size: 12.5px; line-height: 1.5; color: var(--text-secondary); white-space: pre-line; }
.notif-meta { margin-top: 2px; font-size: 11.5px; color: var(--text-disabled); }

.notif-toggle {
    flex-shrink: 0;
    width: 44px;
    border: 0;
    border-left: 1px solid var(--border);
    background: transparent;
    color: var(--text-disabled);
    cursor: pointer;
}
.notif-toggle:hover { color: var(--primary); background: var(--bg); }

.seg-count {
    margin-left: 6px;
    padding: 1px 6px;
    border-radius: 99px;
    background: var(--primary);
    color: #fff;
    font-size: 11px;
    font-weight: 700;
}

/* ── Asking for the satisfaction form in a conversation ───────────────
   The unit already has the reporter's attention here, which is why this
   exists: a form asked for in a thread gets answered far more often than
   one sitting in a notification list. */
.msg-feedback-ask {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    padding: 7px 10px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--bg);
    font-size: 12px;
    color: var(--text-secondary);
}
.msg-feedback-chip {
    border: 1px solid var(--primary);
    border-radius: 99px;
    padding: 2px 10px;
    background: #fff;
    color: var(--primary);
    font-family: var(--mono, ui-monospace, monospace);
    font-size: 11.5px;
    font-weight: 700;
    cursor: pointer;
}
.msg-feedback-chip:hover { background: var(--primary-light); }

.msg-feedback-card {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 8px;
    padding: 8px 10px;
    border: 1px solid var(--primary);
    border-radius: 10px;
    background: var(--primary-light);
    color: var(--primary);
}
.msg-feedback-card span { display: flex; flex-direction: column; min-width: 0; }
.msg-feedback-card strong { font-size: 12.5px; }
.msg-feedback-card span span { font-size: 11px; opacity: 0.85; }

/* Why the status control is absent. Shown in its place rather than as a
   disabled select: a greyed-out control invites clicking and says nothing
   about who to wait for. */
.status-locked {
    display: flex;
    gap: 9px;
    padding: 12px;
    border: 1px solid var(--border);
    border-radius: 12px;
    background: var(--bg);
    color: var(--text-secondary);
}
.status-locked > svg { flex-shrink: 0; margin-top: 1px; }
.status-locked span { display: flex; flex-direction: column; gap: 3px; font-size: 12px; line-height: 1.5; }
.status-locked strong { font-size: 13px; color: var(--text); }

/* A duplicate candidate filed under a different unit than the one being
   reviewed -- now possible since matching dropped the same-unit
   requirement. Amber, same family as the flagged-report and pending-check
   states: something for a person to notice, not an error. */
.dupe-unit-mismatch {
    display: flex;
    align-items: center;
    gap: 5px;
    margin-top: 6px;
    padding: 5px 8px;
    border-radius: 8px;
    background: #FEF3C7;
    color: #92400E;
    font-size: 11px;
    font-weight: 600;
}
.dupe-unit-mismatch svg { flex-shrink: 0; }

/* ── Supporting reports disclosure ───────────────────────────────────
   Who else reported a concern, on the admin and staff panels. Mirrors
   the app's equivalent so both sides read the same list. Native
   <details>, so expanding costs no round trip and cannot close the
   panel's other state with it. */
.support-disclosure { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; }

.support-summary {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 11px 13px; cursor: pointer; list-style: none;
    font-size: 13px; font-weight: 600; color: var(--brand-700, var(--text));
    background: var(--surface-2, transparent);
}
/* Safari/Chrome draw their own marker on top of the flex row. */
.support-summary::-webkit-details-marker { display: none; }
.support-summary:hover { background: var(--hover, rgba(0,0,0,.03)); }
.support-summary-text { display: inline-flex; align-items: center; gap: 7px; }

.support-chevron { transition: transform .18s ease; flex-shrink: 0; }
.support-disclosure[open] .support-chevron { transform: rotate(180deg); }

.support-list { border-top: 1px solid var(--border); max-height: 260px; overflow-y: auto; }
.support-item { padding: 11px 13px; }
.support-item + .support-item { border-top: 1px solid var(--border); }

.support-item-head { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.support-name { font-size: 13px; font-weight: 600; color: var(--text); }
.support-date { font-size: 11.5px; color: var(--text-muted); flex-shrink: 0; }
.support-note { margin: 4px 0 0; font-size: 12.5px; line-height: 1.5; color: var(--text-muted); }

.support-meta {
    display: flex; align-items: center; flex-wrap: wrap; gap: 8px;
    margin-top: 7px; font-size: 11.5px; color: var(--text-muted);
}

@media (prefers-reduced-motion: reduce) { .support-chevron { transition: none; } }

/* The one decision escalation is not allowed to make for itself.
   Loud on purpose: it sits among ordinary actions but is not one. */
.emergency-call {
    padding: 12px 14px;
    margin-bottom: 10px;
    border: 1px solid var(--priority-1);
    border-radius: 10px;
    background: var(--status-waiting-bg);
}
.emergency-call-head {
    margin: 0 0 4px;
    font-size: 13px;
    font-weight: 700;
    color: var(--priority-1);
    line-height: 1.45;
}
.emergency-call-body {
    margin: 0 0 10px;
    font-size: 12.5px;
    color: var(--text-muted, #64748B);
    line-height: 1.5;
}
.needs-review {
    display: inline-block;
    margin-left: 6px;
    padding: 2px 7px;
    border-radius: 20px;
    border: 1px solid var(--priority-1);
    background: var(--status-waiting-bg);
    color: var(--priority-1);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .02em;
    white-space: nowrap;
}
