/* ===========================================================================
   Alliance IT Helpdesk - shared stylesheet
   ===========================================================================

   The same design system the printer dashboard runs on, so the two apps read
   as one product. Tokens, base elements, topbar, buttons, controls, panels,
   stats, chips, tables and motion are carried over unchanged; the printer
   pieces (fleet cards, toner bars, notification rail) are gone and the ticket
   pieces (badges, SLA row edges, the conversation thread) take their place.

   THEMES
   The bare :root block below is the complete light palette. Dark redefines
   only tokens, in two places: a prefers-color-scheme block (guarded so an
   explicit light choice still wins) and a [data-theme="dark"] block. No
   component rule may declare a colour inside either block - a colour whose
   only definition sits behind a media query never applies when the OS is in
   light mode, which is how you end up with one theme's text on the other
   theme's background. There is no theme toggle in the UI; dark follows the OS.

   CONTRAST
   Status and priority badges tint the surface with the hue but mix the hue
   into --ink for the label text. A flat --warn label is 4.1:1 on white, under
   AA for 11px text; mixed into --ink it clears AA in light mode and, because
   --ink flips to near-white in dark mode, in dark mode too. The hue still
   shows in the dot, the tint and the border, so no state is ever carried by
   colour alone - every badge, row edge and flag also says what it means in
   words.
   ========================================================================= */

:root {
  --ground:      #F4F7FA;
  --surface:     #FFFFFF;
  --surface-2:   #EDF2F7;
  --surface-3:   #E3EBF2;
  --border:      #DDE5EC;
  --border-firm: #C6D3DE;

  --ink:         #14212B;
  --ink-soft:    #5F707D;

  --accent:      #1F6FA8;
  --accent-deep: #17567F;
  --brand:       #4B9CD3;
  --brand-tint:  #E8F3FB;

  /* Text that sits ON --accent, which is a dark blue in light mode and a
     light blue in dark mode. White works for one and not the other: white on
     the dark-mode accent is 2.4:1. This token is the only thing that keeps
     the primary button legible in both. */
  --on-accent:   #FFFFFF;

  --ok:          #16855A;
  --warn:        #B87503;
  --crit:        #C0392B;
  --offline:     #7B8B96;

  --series-a:    #2A78D6;
  --series-b:    #EB6834;

  --shadow-sm:   0 1px 2px rgba(20, 33, 43, 0.06);
  --shadow-md:   0 4px 14px rgba(20, 33, 43, 0.08);
  --shadow-lg:   0 12px 32px rgba(20, 33, 43, 0.12);

  --sans: 'IBM Plex Sans', -apple-system, 'Segoe UI', Arial, sans-serif;
  --mono: 'IBM Plex Mono', 'SFMono-Regular', Consolas, 'Courier New', monospace;
  --ease: cubic-bezier(0.32, 0.72, 0, 1);

  --internal-tint: #FDF6E7;
  --sandbox-bg:   #FFF6E5;
  --sandbox-edge: #EFD9AC;
  --sandbox-ink:  #7A4E03;

  --r-sm: 6px;
  --r-md: 10px;
  --r-lg: 14px;
}

@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --internal-tint: #2A2413;
    --sandbox-bg:   #2E2413;
    --sandbox-edge: #4A3A1B;
    --sandbox-ink:  #F0C463;
    --ground:      #101820;
    --surface:     #18222B;
    --surface-2:   #1F2C37;
    --surface-3:   #263644;
    --border:      #2B3B48;
    --border-firm: #3A4C5B;

    --ink:         #E8EEF3;
    --ink-soft:    #9FB0BC;

    --accent:      #5FB0E8;
    --accent-deep: #8AC6F0;
    --brand:       #4B9CD3;
    --brand-tint:  #1B2E3D;
    --on-accent:   #0E1720;

    --ok:          #3FBE86;
    --warn:        #E0A02E;
    --crit:        #EF6E60;
    --offline:     #6E7F8B;

    --series-a:    #5AA2EA;
    --series-b:    #F28355;

    --shadow-sm:   0 1px 2px rgba(0, 0, 0, 0.4);
    --shadow-md:   0 4px 14px rgba(0, 0, 0, 0.45);
    --shadow-lg:   0 12px 32px rgba(0, 0, 0, 0.55);
  }
}

:root[data-theme="dark"] {
  --internal-tint: #2A2413;
  --sandbox-bg:   #2E2413;
  --sandbox-edge: #4A3A1B;
  --sandbox-ink:  #F0C463;
  --ground:      #101820;
  --surface:     #18222B;
  --surface-2:   #1F2C37;
  --surface-3:   #263644;
  --border:      #2B3B48;
  --border-firm: #3A4C5B;

  --ink:         #E8EEF3;
  --ink-soft:    #9FB0BC;

  --accent:      #5FB0E8;
  --accent-deep: #8AC6F0;
  --brand:       #4B9CD3;
  --brand-tint:  #1B2E3D;
  --on-accent:   #0E1720;

  --ok:          #3FBE86;
  --warn:        #E0A02E;
  --crit:        #EF6E60;
  --offline:     #6E7F8B;

  --series-a:    #5AA2EA;
  --series-b:    #F28355;

  --shadow-sm:   0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow-md:   0 4px 14px rgba(0, 0, 0, 0.45);
  --shadow-lg:   0 12px 32px rgba(0, 0, 0, 0.55);
}

/* ============================================================== base ===== */
* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
  padding-bottom: 60px;
}

h1, h2, h3 { margin: 0; text-wrap: balance; }

h1 { font-size: 21px; font-weight: 600; letter-spacing: -0.02em; }

a { color: var(--accent); }

.num { font-family: var(--mono); font-variant-numeric: tabular-nums; }

:where(a, button, select, input, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* Off-screen but still read aloud, for icon-only controls and for headers
   that name a column the eye can read from its contents. */
.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;
}

/* One width for every page. .narrow is for pages that are a single form:
   a 1680px-wide description box helps nobody. */
.shell { width: min(1680px, 100% - 3rem); margin-inline: auto; }
.shell.narrow { width: min(760px, 100% - 2.5rem); }

@media (max-width: 640px) {
  .shell { width: calc(100% - 2rem); }
}

/* ============================================================ topbar ===== */
.topbar {
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 20;
}

.topbar-inner {
  display: flex;
  align-items: center;
  gap: 22px;
  padding-block: 14px;
  flex-wrap: wrap;
}

/* Brandmark, nav and Sign out each take their own row on a phone, and three
   rows of chrome pinned to the top is a fifth of the viewport. Below 640px
   the bar scrolls away with the page instead of following it down. */
@media (max-width: 640px) {
  .topbar { position: static; }
  .topbar-inner { gap: 12px; padding-block: 12px; }
}

.brandmark { display: flex; align-items: center; gap: 11px; margin-right: auto; min-width: 0; }

.pulse {
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--ok);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--ok) 22%, transparent);
  flex: none;
  transition: background 0.3s var(--ease), box-shadow 0.3s var(--ease);
}

.pulse.is-warn { background: var(--warn); box-shadow: 0 0 0 3px color-mix(in srgb, var(--warn) 22%, transparent); }
.pulse.is-crit { background: var(--crit); box-shadow: 0 0 0 3px color-mix(in srgb, var(--crit) 22%, transparent); }

.brandmark strong { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; white-space: nowrap; }
.brandmark span { color: var(--ink-soft); font-size: 13px; white-space: nowrap; }

.nav { display: flex; gap: 2px; flex-wrap: wrap; }

.nav a {
  display: block;
  padding: 7px 13px;
  border-radius: var(--r-sm);
  color: var(--ink-soft);
  font-size: 13.5px;
  font-weight: 500;
  text-decoration: none;
  transition: background 0.18s var(--ease), color 0.18s var(--ease);
}

.nav a:hover { background: var(--surface-2); color: var(--ink); }

.nav a[aria-current="page"] {
  background: var(--brand-tint);
  color: var(--accent);
  font-weight: 600;
}

/* Who you are signed in as. It matters more here than on the printer
   dashboard: an agent sees internal notes and every ticket, a requester sees
   only their own, and the two views look similar enough to be confused. */
.whoami {
  padding-left: 14px;
  border-left: 1px solid var(--border);
  line-height: 1.3;
}

.whoami b { display: block; font-size: 13px; font-weight: 600; }
.whoami span { font-size: 11.5px; color: var(--ink-soft); }

/* =========================================================== buttons ===== */
.btn {
  --btn-bg: var(--surface);
  --btn-ink: var(--ink);
  --btn-bd: var(--border-firm);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 7px;
  padding: 8px 15px;
  border: 1px solid var(--btn-bd);
  border-radius: var(--r-sm);
  background: var(--btn-bg);
  color: var(--btn-ink);
  font-family: var(--sans);
  font-size: 13.5px;
  font-weight: 500;
  text-decoration: none;
  cursor: pointer;
  transition: transform 0.12s var(--ease), box-shadow 0.18s var(--ease),
              background 0.18s var(--ease), border-color 0.18s var(--ease),
              color 0.18s var(--ease);
}

.btn:hover { box-shadow: var(--shadow-sm); border-color: var(--ink-soft); }
.btn:active { transform: translateY(1px); box-shadow: none; }
.btn:disabled { opacity: 0.55; cursor: default; transform: none; box-shadow: none; }

.btn-primary { --btn-bg: var(--accent); --btn-ink: var(--on-accent); --btn-bd: var(--accent); font-weight: 600; }
.btn-primary:hover:not(:disabled) { --btn-bg: var(--accent-deep); --btn-bd: var(--accent-deep); }

/* The quiet one: a secondary action standing next to a primary button. */
.btn-ghost { --btn-bg: transparent; --btn-ink: var(--ink-soft); }
.btn-ghost:hover:not(:disabled) { --btn-bg: var(--surface-2); --btn-ink: var(--ink); }

.btn-sm { padding: 6px 11px; font-size: 12.5px; }
.btn-block { width: 100%; }

.btn svg { width: 14px; height: 14px; flex: none; }

.btn .spin { animation: spin 0.7s linear infinite; }

/* ========================================================== controls ===== */
select,
input[type="text"],
input[type="password"],
input[type="search"],
input[type="email"],
textarea {
  font-family: var(--sans);
  font-size: 13.5px;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--border-firm);
  border-radius: var(--r-sm);
  padding: 8px 11px;
  transition: border-color 0.18s var(--ease), box-shadow 0.18s var(--ease);
}

select:hover, input:hover, textarea:hover { border-color: var(--ink-soft); }

input::placeholder, textarea::placeholder { color: var(--ink-soft); }

textarea {
  width: 100%;
  min-height: 130px;
  padding: 11px 12px;
  font-size: 14px;
  line-height: 1.55;
  resize: vertical;
}

/* Only the button half of a file input can be styled, so style that and let
   the browser draw the filename text beside it. */
input[type="file"] {
  font-family: var(--sans);
  font-size: 13px;
  color: var(--ink-soft);
  max-width: 100%;
}

input[type="file"]::file-selector-button {
  font-family: var(--sans);
  font-size: 12.5px;
  font-weight: 500;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--border-firm);
  border-radius: var(--r-sm);
  padding: 6px 11px;
  margin-right: 10px;
  cursor: pointer;
  transition: background 0.18s var(--ease), border-color 0.18s var(--ease);
}

input[type="file"]::file-selector-button:hover { background: var(--surface-2); border-color: var(--ink-soft); }

input[type="checkbox"] { width: 15px; height: 15px; accent-color: var(--accent); cursor: pointer; }

.segmented {
  display: inline-flex;
  background: var(--surface-2);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  padding: 2px;
  gap: 2px;
}

.segmented button {
  border: 0;
  background: transparent;
  color: var(--ink-soft);
  font-family: var(--sans);
  font-size: 12.5px;
  font-weight: 500;
  padding: 5px 12px;
  border-radius: 4px;
  cursor: pointer;
  transition: background 0.18s var(--ease), color 0.18s var(--ease);
}

.segmented button:hover { color: var(--ink); }

.segmented button.active {
  background: var(--surface);
  color: var(--ink);
  font-weight: 600;
  box-shadow: var(--shadow-sm);
}

.controls { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.controls-left { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }

/* ============================================================= forms ===== */
.field { display: grid; gap: 6px; margin-bottom: 16px; }

.field > label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

/* Everything except the checkbox, which is 15px wide and stays that way. */
.field input:not([type="checkbox"]), .field select, .field textarea { width: 100%; }

.field-row { display: flex; gap: 14px; flex-wrap: wrap; }
.field-row .field { flex: 1 1 220px; }
.field.grow { flex: 1 1 240px; }

.hint { font-size: 12px; color: var(--ink-soft); line-height: 1.45; }

/* A checkbox and its wording are one control, so the label wraps both and
   drops the uppercase treatment - this is a sentence, not a field name.
   Selected as .field > .check as well as .check so it outweighs the
   .field > label rule above rather than tying with it. */
.check,
.field > .check {
  display: flex;
  align-items: center;
  gap: 9px;
  font-size: 13.5px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--ink);
  cursor: pointer;
}

.check input[type="checkbox"] { width: 15px; height: 15px; margin: 0; flex: none; }

/* The queue filter bar. Its selects submit on change; the Apply button is
   there for anyone whose browser is not running the onchange handler. */
.filters {
  display: flex;
  gap: 12px;
  flex-wrap: wrap;
  align-items: flex-end;
  margin-bottom: 16px;
}

.filters .field { margin-bottom: 0; min-width: 150px; }
.filter-actions { display: flex; align-items: center; gap: 8px; }

/* ============================================================ layout ===== */
.layout { display: grid; grid-template-columns: minmax(0, 1fr) 344px; gap: 22px; align-items: start; }

@media (max-width: 1180px) { .layout { grid-template-columns: 1fr; } }

.page-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
  flex-wrap: wrap;
  margin: 26px 0 18px;
}

.page-sub { margin: 4px 0 0; font-size: 13px; color: var(--ink-soft); max-width: 70ch; }

.sec-head { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin: 30px 0 14px; }
.sec-head h2 { font-size: 16px; font-weight: 600; letter-spacing: -0.01em; }
.sec-head p { margin: 0; font-size: 13px; color: var(--ink-soft); }
.sec-head .spacer { margin-left: auto; }

.panel {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 22px 24px 24px;
}

.panel > h2 {
  font-size: 13px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-soft);
  margin-bottom: 16px;
}

.panel + .panel { margin-top: 16px; }

/* =========================================================== summary ===== */
.summary {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  margin-block: 24px 20px;
}

.stat { background: var(--surface); padding: 17px 20px; }

.stat-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.stat-value { font-size: 27px; font-weight: 600; letter-spacing: -0.02em; margin-top: 5px; line-height: 1.1; }
.stat-value.sm { font-size: 19px; }
.stat-value .qual { font-size: 16px; font-weight: 500; color: var(--ink-soft); }

/* A figure that is bad news. Always paired with a note saying why, so the
   colour reinforces the number rather than being the only thing carrying it. */
.stat-value.is-crit { color: var(--crit); }
.stat-value.is-warn { color: var(--warn); }
.stat-value.is-ok   { color: var(--ok); }

.stat-note { font-size: 12.5px; color: var(--ink-soft); margin-top: 3px; }
.stat-note b { color: var(--ink); font-weight: 600; }

/* =========================================================== badges ===== */
/* Status and priority. The hue lives in --badge; the dot, the tint and the
   border take it neat, the label takes it mixed into --ink so small text
   still clears AA in both themes. */
.badge {
  --badge: var(--offline);
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 10px 3px 8px;
  border-radius: 999px;
  border: 1px solid color-mix(in srgb, var(--badge) 30%, var(--border));
  background: color-mix(in srgb, var(--badge) 12%, var(--surface));
  color: color-mix(in srgb, var(--badge) 55%, var(--ink));
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.02em;
  line-height: 1.5;
  white-space: nowrap;
}

.badge::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--badge);
  flex: none;
}

.st-new      { --badge: var(--accent); }
.st-open     { --badge: var(--warn); }
.st-waiting  { --badge: var(--offline); }
.st-resolved { --badge: var(--ok); }

/* Closed is the one state nobody needs to act on, so it is the quietest
   thing on the row: no tint, and a hollow dot rather than a filled one. */
.st-closed {
  --badge: var(--offline);
  background: var(--surface-2);
  border-color: var(--border);
  color: var(--ink-soft);
}

.st-closed::before { background: transparent; box-shadow: inset 0 0 0 1.5px var(--offline); }

.pr-critical { --badge: var(--crit); background: color-mix(in srgb, var(--crit) 17%, var(--surface)); font-weight: 700; }
.pr-high     { --badge: var(--warn); }
.pr-normal   { --badge: var(--accent); }

.pr-low {
  --badge: var(--offline);
  background: var(--surface-2);
  border-color: var(--border);
  color: var(--ink-soft);
}

.pr-low::before { background: transparent; box-shadow: inset 0 0 0 1.5px var(--offline); }

/* The cluster under a ticket title: status, priority, category. */
.badge-row { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }

/* ============================================================ chips ===== */
/* Neutral tags - a category, a filter in force. Modifiers colour the marker. */
.chip {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px;
  border-radius: var(--r-sm);
  background: var(--surface-2);
  border: 1px solid var(--border);
  font-size: 11.5px;
  color: var(--ink);
}

.chip::before { content: ""; width: 5px; height: 5px; border-radius: 1px; background: var(--chip, var(--ink-soft)); flex: none; }

.chip.c { --chip: var(--crit); }
.chip.w { --chip: var(--warn); }
.chip.i { --chip: var(--offline); }

/* ============================================================ tables ==== */
.table-wrap { overflow-x: auto; }

table { width: 100%; border-collapse: collapse; font-size: 13.5px; }

th {
  text-align: left;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
  padding: 9px 12px;
  border-bottom: 1px solid var(--border-firm);
  white-space: nowrap;
}

td { padding: 11px 12px; border-bottom: 1px solid var(--border); vertical-align: middle; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr { transition: background 0.15s var(--ease); }
tbody tr:hover { background: var(--surface-2); }

/* Row cell helpers. .ticket-link stays ink-coloured so a table of thirty
   tickets is not thirty lines of blue. */
.ticket-link { font-weight: 600; text-decoration: none; color: var(--ink); }
.ticket-link:hover { color: var(--accent); text-decoration: underline; }

.ref { font-family: var(--mono); font-size: 11.5px; color: var(--ink-soft); margin-top: 2px; }
.cell-dim { color: var(--ink-soft); font-size: 12.5px; }

/* Columns worth losing before the table starts scrolling sideways. */
@media (max-width: 720px) {
  .hide-sm { display: none; }
}

/* =============================================================== SLA ==== */
/* A coloured left edge on the row is the point of the queue: trouble should
   be findable without reading a single number. It is drawn as an inset
   shadow rather than a border so it does not shift the cell padding, and it
   is never the only signal - ticket_row prints an .sla-flag beside the
   reference saying "SLA breached" or "due soon" in words. */
tr.sla-breached td:first-child { box-shadow: inset 3px 0 0 var(--crit); }
tr.sla-due-soon td:first-child { box-shadow: inset 3px 0 0 var(--warn); }

tr.sla-breached { background: color-mix(in srgb, var(--crit) 6%, transparent); }
tr.sla-due-soon { background: color-mix(in srgb, var(--warn) 6%, transparent); }

.sla-flag {
  --sla: var(--offline);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-family: var(--sans);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: color-mix(in srgb, var(--sla) 55%, var(--ink));
  white-space: nowrap;
}

.sla-flag::before { content: ""; width: 5px; height: 5px; border-radius: 1px; background: var(--sla); flex: none; }

.sla-flag.is-breached { --sla: var(--crit); }
.sla-flag.is-due-soon { --sla: var(--warn); }
.sla-flag.is-met      { --sla: var(--ok); }
.sla-flag.is-paused   { --sla: var(--offline); }

/* The two target lines in the ticket sidebar. */
.sla-line { font-size: 12.5px; color: var(--ink-soft); margin-top: 5px; }
.sla-line b { color: var(--ink); font-weight: 600; }

/* ==================================================== ticket detail ===== */
/* Main column plus a sidebar of facts and agent controls. The sidebar drops
   below the conversation on narrow screens rather than squeezing both. */
.ticket-layout { display: grid; grid-template-columns: minmax(0, 1fr) 340px; gap: 20px; align-items: start; }

@media (max-width: 1020px) { .ticket-layout { grid-template-columns: 1fr; } }

.ticket-main { min-width: 0; }
.ticket-side { min-width: 0; }

.ticket-title { font-size: 21px; font-weight: 600; letter-spacing: -0.02em; line-height: 1.3; margin: 0 0 6px; }
.ticket-meta { font-family: var(--mono); font-size: 12px; color: var(--ink-soft); line-height: 1.6; }

.side-block { font-size: 13px; }
.side-block + .side-block { margin-top: 18px; padding-top: 18px; border-top: 1px solid var(--border); }

.side-label {
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
  margin-bottom: 5px;
}

.side-value { font-size: 13px; line-height: 1.5; word-break: break-word; }

/* ======================================================== the thread ==== */
.thread { min-width: 0; }

.thread-entry { padding: 16px 0; border-top: 1px solid var(--border); }
.thread-entry:first-child { border-top: 0; padding-top: 0; }

/* The original request. Marked so it does not read as just another reply
   halfway down a long ticket. */
.thread-entry.is-opening { border-top: 0; border-left: 3px solid var(--brand); padding: 2px 0 16px 14px; }

.thread-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 7px;
}

.thread-author { font-size: 13px; font-weight: 600; }
.thread-author .who-role { font-weight: 400; color: var(--ink-soft); margin-left: 6px; }
.thread-time { font-family: var(--mono); font-size: 11px; color: var(--ink-soft); }

/* pre-wrap because ticket text arrives from email and from a textarea, and
   both carry meaning in their line breaks. The measure is capped because a
   1300px line of body text is unreadable however wide the screen is. */
.thread-body {
  font-size: 14px;
  line-height: 1.6;
  white-space: pre-wrap;
  overflow-wrap: break-word;
  max-width: 78ch;
}

/* INTERNAL NOTES
   Agent-only. These must never reach a requester, so they are deliberately
   the most visually distinct thing in the thread: amber edge, tinted panel,
   and a label in words rather than a colour cue that could be missed. The
   template still has to keep them inside a current_user.is_agent guard -
   this styling is a reminder, not a permission check. */
.thread-entry.is-internal {
  border-top: 0;
  border-left: 3px solid var(--warn);
  border-radius: 0 var(--r-md) var(--r-md) 0;
  background: color-mix(in srgb, var(--warn) 9%, var(--surface));
  padding: 14px 16px;
  margin: 16px 0;
}

.internal-tag {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border-radius: 999px;
  background: color-mix(in srgb, var(--warn) 20%, var(--surface));
  border: 1px solid color-mix(in srgb, var(--warn) 38%, var(--border));
  color: color-mix(in srgb, var(--warn) 55%, var(--ink));
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  white-space: nowrap;
}

/* ====================================================== attachments ===== */
.attachment-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 7px; font-size: 13px; }

.attachment-list li { display: flex; align-items: baseline; gap: 8px; min-width: 0; }
.attachment-list a { text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.attachment-list a:hover { text-decoration: underline; }
.attachment-size { font-family: var(--mono); font-size: 11px; color: var(--ink-soft); flex: none; }

/* ==================================================== audit history ===== */
.audit { display: grid; gap: 7px; font-size: 12px; line-height: 1.5; color: var(--ink-soft); }
.audit-entry { overflow-wrap: break-word; }
.audit-time { font-family: var(--mono); font-size: 11px; color: var(--ink-soft); }
.audit-actor { color: var(--ink); font-weight: 600; }

/* A details/summary that reads as a control rather than a browser default.
   Used for the ticket history and for the show-as-a-table fallbacks under
   the metrics charts. */
.disclosure > summary {
  cursor: pointer;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--ink-soft);
  list-style: none;
  transition: color 0.18s var(--ease);
}

.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary::before { content: "\25B8  "; }
.disclosure[open] > summary::before { content: "\25BE  "; }
.disclosure[open] > summary { margin-bottom: 12px; }
.disclosure > summary:hover { color: var(--ink); }

/* ============================================================ flashes === */
.flashes { display: grid; gap: 8px; margin-top: 18px; }

.flash {
  --flash: var(--accent);
  display: flex;
  align-items: flex-start;
  gap: 9px;
  padding: 11px 14px;
  border: 1px solid color-mix(in srgb, var(--flash) 32%, var(--border));
  border-left: 3px solid var(--flash);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--flash) 10%, var(--surface));
  font-size: 13.5px;
  line-height: 1.5;
  animation: rise 0.3s var(--ease) both;
}

.flash-ok    { --flash: var(--ok); }
.flash-error { --flash: var(--crit); }
.flash-warn  { --flash: var(--warn); }

/* The same shape, for an error shown inline on a page with no flash queue -
   the sign-in form, mainly. */
.error-box {
  display: flex;
  gap: 9px;
  padding: 11px 13px;
  border: 1px solid color-mix(in srgb, var(--crit) 35%, var(--border));
  border-left: 3px solid var(--crit);
  border-radius: var(--r-sm);
  background: color-mix(in srgb, var(--crit) 10%, var(--surface));
  font-size: 13px;
  margin-bottom: 18px;
  animation: rise 0.3s var(--ease) both;
}

/* ========================================================= pagination === */
.pager { display: flex; align-items: center; justify-content: center; gap: 10px; margin-top: 18px; flex-wrap: wrap; }
.pager-info { font-family: var(--mono); font-size: 12px; color: var(--ink-soft); font-variant-numeric: tabular-nums; }

/* ============================================================ metrics === */
.chart-cols { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 16px; margin-top: 16px; }

/* Panels in a grid get their gap from the grid, not from the stacking rule. */
.chart-cols > .panel + .panel { margin-top: 0; }

.chart-title { font-size: 14px; font-weight: 600; letter-spacing: -0.01em; margin: 0 0 3px; }
.chart-sub { font-size: 12.5px; color: var(--ink-soft); margin: 0 0 16px; line-height: 1.45; }

.chart-wrap { position: relative; min-width: 0; }

canvas { max-height: 340px; }

.loading { text-align: center; padding: 30px 0; font-size: 13px; color: var(--ink-soft); }

/* Horizontal bar rows, for the breakdowns that read better as a ranked list
   than as a chart - backlog age, open tickets per person, top categories. */
.bar-row { margin-bottom: 11px; }
.bar-head { display: flex; justify-content: space-between; gap: 10px; font-size: 12.5px; margin-bottom: 4px; }
.bar-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bar-value { font-family: var(--mono); font-size: 12px; color: var(--ink-soft); flex: none; font-variant-numeric: tabular-nums; }
.bar-track { height: 8px; border-radius: 999px; background: var(--surface-3); overflow: hidden; }

.bar-fill {
  /* display:block is load-bearing. .bar-track is not a flex or grid
     container, so without it the fill stays an inline span - and inline
     non-replaced elements ignore width, height and transform outright. You
     get the number beside an empty track and no bar at all. */
  display: block;
  height: 100%;
  border-radius: 999px;
  background: var(--series, var(--brand));
  width: var(--pct, 0%);
  transform-origin: left;
  animation: grow 0.9s var(--ease) both;
}

/* =========================================================== sign-in ===== */
/* The one page with no topbar and no shell: a sign-in form has nothing to
   fill a wide screen with. Put the class on <body>. */
body.signin { display: grid; place-items: center; min-height: 100vh; padding: 24px; }

.login-card {
  width: min(400px, 100%);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  padding: 32px 30px 30px;
  animation: rise 0.5s var(--ease) both;
}

.login-brand { display: flex; align-items: center; gap: 10px; margin-bottom: 26px; }
.login-brand strong { display: block; font-size: 14px; font-weight: 600; }
.login-brand span { display: block; font-size: 12px; color: var(--ink-soft); }

.login-card h1 { font-size: 21px; font-weight: 600; letter-spacing: -0.02em; }
.login-card .sub { margin: 5px 0 22px; font-size: 13.5px; color: var(--ink-soft); }
.login-card button[type="submit"] { width: 100%; margin-top: 6px; padding: 11px; font-size: 14px; }

.footnote {
  margin-top: 22px;
  padding-top: 16px;
  border-top: 1px solid var(--border);
  font-size: 12px;
  color: var(--ink-soft);
  line-height: 1.55;
}

/* ============================================================ misc ====== */
.note-line {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: -4px 0 16px;
  font-size: 12px;
  color: var(--ink-soft);
}

.note-line::before {
  content: "";
  width: 3px;
  align-self: stretch;
  background: var(--brand);
  border-radius: 2px;
  opacity: 0.5;
}

/* Empty states carry real weight here: the queue has three of them, each
   offering a different way out. */
.empty-msg { text-align: center; padding: 32px 16px; color: var(--ink-soft); font-size: 13.5px; line-height: 1.6; }
.empty-msg strong { display: block; color: var(--ink); font-size: 15px; font-weight: 600; margin-bottom: 5px; }
.empty-msg p { margin: 0 auto; max-width: 52ch; }
.empty-actions { display: flex; gap: 10px; justify-content: center; flex-wrap: wrap; margin-top: 16px; }

.caveat {
  font-size: 12px;
  line-height: 1.55;
  color: var(--ink-soft);
  border-left: 2px solid var(--border-firm);
  padding-left: 11px;
  margin-top: 18px;
}

.legend { display: flex; gap: 18px; flex-wrap: wrap; margin-bottom: 16px; }
.legend div { display: flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--ink-soft); }
.legend i { width: 10px; height: 10px; border-radius: 3px; flex: none; }

/* ========================================================== watchers ==== */
/* People copied onto a ticket. Sits in the sidebar next to Assigned to, so
   anyone reading the ticket can see who else is receiving it. */
.watcher-list { list-style: none; margin: 0; padding: 0; display: grid; gap: 6px; }

.watcher-list li {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  word-break: break-all;
}

.watcher-list form { margin-left: auto; }

.watcher-remove {
  border: 0;
  background: transparent;
  color: var(--ink-soft);
  font-family: var(--sans);
  font-size: 11.5px;
  padding: 2px 4px;
  border-radius: 4px;
  cursor: pointer;
  /* Visible at rest rather than on hover: a touch device never hovers, and a
     control you cannot reach is the same as one that is not there. */
  text-decoration: underline;
  text-underline-offset: 2px;
  transition: color 0.15s var(--ease), background 0.15s var(--ease);
}

.watcher-remove:hover { color: var(--crit); background: color-mix(in srgb, var(--crit) 10%, transparent); }
.watcher-remove:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

/* ====================================================== destructive ===== */
/* Deleting sits apart from the everyday controls, muted until opened, so it
   is never the thing you hit reaching for Save changes. */
.ticket-danger { margin-top: 18px; padding-top: 16px; border-top: 1px solid var(--border); }

.danger-toggle summary {
  cursor: pointer;
  font-size: 12.5px;
  color: var(--ink-soft);
  list-style: none;
}
.danger-toggle summary::-webkit-details-marker { display: none; }
.danger-toggle summary::before { content: "> "; font-family: var(--mono); }
.danger-toggle[open] summary::before { content: "v "; }
.danger-toggle summary:hover { color: var(--crit); }
.danger-toggle[open] summary { margin-bottom: 12px; color: var(--crit); font-weight: 600; }

.danger-note { font-size: 12.5px; line-height: 1.55; color: var(--ink-soft); margin: 0 0 12px; }

.btn-danger {
  --btn-bg: var(--crit);
  --btn-ink: #FFFFFF;
  --btn-bd: var(--crit);
  font-weight: 600;
}
.btn-danger:hover:not(:disabled) {
  --btn-bg: color-mix(in srgb, var(--crit) 82%, black);
  --btn-bd: color-mix(in srgb, var(--crit) 82%, black);
}

.btn-block { width: 100%; }

/* ======================================================= idle warning === */
/* Fixed to the corner rather than a modal: it must be noticeable without
   trapping focus or hiding what somebody was reading. */
#idle-warning {
  position: fixed;
  right: 18px;
  bottom: 18px;
  z-index: 60;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  max-width: min(420px, calc(100vw - 36px));
  padding: 13px 16px;
  background: var(--surface);
  border: 1px solid var(--border-firm);
  border-left: 3px solid var(--warn);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  font-size: 13px;
  animation: rise 0.25s var(--ease) both;
}

#idle-warning[hidden] { display: none; }
#idle-warning strong { font-size: 13.5px; }
#idle-warning b { font-family: var(--mono); }
#idle-warning button { margin-left: auto; }

/* ================================================== row selection ======= */
.pick-cell { width: 34px; padding-right: 0; text-align: center; }
.pick-cell input[type="checkbox"] { width: 15px; height: 15px; cursor: pointer; margin: 0; }

tbody tr.is-picked { background: var(--brand-tint); }
tbody tr.is-picked:hover { background: var(--brand-tint); }

/* The toolbar is sticky at the bottom: with fifty rows on screen the selection
   is made at the top and the action would otherwise be scrolled out of sight. */
.bulk-bar {
  position: sticky;
  bottom: 16px;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-top: 14px;
  padding: 12px 16px;
  background: var(--surface);
  border: 1px solid var(--border-firm);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
  animation: rise 0.2s var(--ease) both;
}

.bulk-bar[hidden] { display: none; }
.bulk-count { font-size: 13.5px; }
.bulk-count b { font-family: var(--mono); font-weight: 600; }
#bulk-delete { margin-left: auto; }

.bulk-confirm {
  flex-basis: 100%;
  margin-top: 6px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}
.bulk-confirm[hidden] { display: none; }
.bulk-confirm .field { margin-bottom: 12px; }
.bulk-actions { display: flex; gap: 8px; justify-content: flex-end; }

/* ====================================================== drawer nav ====== */
.menu-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 36px;
  height: 36px;
  flex: none;
  padding: 0;
  border: 1px solid transparent;
  border-radius: var(--r-sm);
  background: transparent;
  color: inherit;
  cursor: pointer;
  transition: background 0.15s var(--ease);
}

.menu-button:hover { background: var(--surface-2); }
.menu-button:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.menu-lines { display: grid; gap: 4px; width: 18px; }
.menu-lines i { display: block; height: 2px; border-radius: 1px; background: currentColor; }

.drawer-scrim {
  position: fixed;
  inset: 0;
  z-index: 70;
  background: rgba(20, 33, 43, 0.45);
  animation: fade 0.15s var(--ease) both;
}
.drawer-scrim[hidden] { display: none; }

.nav-drawer {
  position: fixed;
  top: 0;
  left: 0;
  bottom: 0;
  z-index: 80;
  width: min(290px, 84vw);
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border-right: 1px solid var(--border);
  box-shadow: var(--shadow-lg);
  /* Off-screen rather than display:none, so the links stay in the accessibility
     tree and the panel can animate in. */
  transform: translateX(-102%);
  transition: transform 0.22s var(--ease);
  visibility: hidden;
}

.nav-drawer.is-open { transform: translateX(0); visibility: visible; }

/* Without JavaScript there is no way to open it, so it stays in the flow of the
   page as a plain list instead of being stranded off-screen. */
.no-js .nav-drawer {
  position: static;
  transform: none;
  visibility: visible;
  width: 100%;
  box-shadow: none;
}

.drawer-head {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 18px 18px 14px;
  border-bottom: 1px solid var(--border);
}
.drawer-head b { display: block; font-size: 14px; }
.drawer-head span { font-size: 12px; color: var(--ink-soft); }

.drawer-close {
  margin-left: auto;
  border: 0;
  background: transparent;
  color: var(--ink-soft);
  font-size: 22px;
  line-height: 1;
  padding: 0 4px;
  cursor: pointer;
  border-radius: var(--r-sm);
}
.drawer-close:hover { color: var(--ink); }
.drawer-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.drawer-links { flex: 1; overflow-y: auto; padding: 10px 10px 16px; }

.drawer-links a {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 10px 12px;
  border-radius: var(--r-sm);
  color: var(--ink);
  text-decoration: none;
  font-size: 14px;
  transition: background 0.15s var(--ease);
}

.drawer-links a:hover { background: var(--surface-2); }

/* The current page is named by aria-current, and shown by more than colour:
   a solid left edge and heavier text, so it reads in greyscale too. */
.drawer-links a[aria-current="page"] {
  background: var(--brand-tint);
  font-weight: 600;
  box-shadow: inset 3px 0 0 var(--accent);
}

.drawer-links a:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

.drawer-icon {
  width: 20px;
  text-align: center;
  color: var(--ink-soft);
  font-size: 13px;
  flex: none;
}
.drawer-links a[aria-current="page"] .drawer-icon { color: var(--accent); }

.drawer-group {
  padding: 14px 12px 6px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-soft);
}

.drawer-foot { padding: 14px; border-top: 1px solid var(--border); }

body.drawer-open { overflow: hidden; }

@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

/* ======================================================== settings ====== */
.settings-section + .settings-section { margin-top: 16px; }

.section-note {
  margin: -8px 0 18px;
  font-size: 13px;
  line-height: 1.55;
  color: var(--ink-soft);
  max-width: 68ch;
}

.settings-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 18px 22px;
}

.settings-grid .field { margin-bottom: 0; }
.settings-grid .field-wide { grid-column: 1 / -1; }
.settings-grid textarea { min-height: 120px; font-family: var(--mono); font-size: 13px; }

.settings-save {
  position: sticky;
  bottom: 16px;
  z-index: 20;
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-top: 18px;
  padding: 14px 18px;
  background: var(--surface);
  border: 1px solid var(--border-firm);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-lg);
}
.settings-save .hint { margin: 0; }

.ro-label { color: var(--ink-soft); width: 42%; }
.ro-value { font-family: var(--mono); font-size: 12.5px; word-break: break-all; }

code {
  font-family: var(--mono);
  font-size: 0.92em;
  background: var(--surface-2);
  padding: 1px 5px;
  border-radius: 4px;
}

/* ==================================================== test system ======= */
/* Amber, and stated in words as well as colour. Somebody who cannot tell amber
   from grey still reads "Test system", which is the whole job of this bar. */
.sandbox-bar {
  background: var(--sandbox-bg);
  border-bottom: 1px solid var(--sandbox-edge);
  color: var(--sandbox-ink);
  font-size: 13px;
  line-height: 1.5;
}

.sandbox-bar-inner {
  display: flex;
  align-items: center;
  gap: 10px 14px;
  flex-wrap: wrap;
  padding: 9px 0;
}

.sandbox-tag {
  flex: none;
  padding: 2px 9px;
  border-radius: 999px;
  background: var(--sandbox-ink);
  color: var(--sandbox-bg);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.sandbox-words { flex: 1 1 260px; }

.sandbox-link {
  flex: none;
  color: var(--sandbox-ink);
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 2px;
  white-space: nowrap;
}
.sandbox-link:hover { text-decoration-thickness: 2px; }
.sandbox-link:focus-visible { outline: 2px solid var(--sandbox-ink); outline-offset: 2px; }

/* The signpost - shown where somebody is about to do the wrong thing, or has
   just done it. Deliberately heavier than the bar: the bar is ambient, this
   is an interruption. */
.divert {
  margin: 0 0 18px;
  padding: 18px 20px;
  background: var(--sandbox-bg);
  border: 1px solid var(--sandbox-edge);
  border-left: 4px solid var(--sandbox-ink);
  border-radius: var(--r-md);
  color: var(--sandbox-ink);
}

.divert h2 {
  margin: 0 0 8px;
  font-size: 16px;
  font-weight: 600;
  color: var(--sandbox-ink);
}

.divert p { margin: 0 0 14px; font-size: 14px; line-height: 1.6; max-width: 62ch; }
.divert .btn-primary { background: var(--sandbox-ink); border-color: var(--sandbox-ink); }
.divert .btn-primary:hover { filter: brightness(1.1); }

.divert-foot { margin: 10px 0 0; font-size: 12.5px; opacity: 0.85; }

.divert-slim { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.divert-slim p { margin: 0; flex: 1 1 320px; }
.divert-slim .btn { flex: none; }

/* ============================================== ticket side panel ======= */
/* The controls an agent touches on every ticket - assign, status, priority,
   Save - used to scroll away the moment they started reading the thread. It
   sticks below the topbar instead, with its own scroll region so a long panel
   on a short screen is still reachable.

   Only above 1020px. Below that the layout is one column and the side panel
   sits under the conversation, where pinning it would cover the thread. */
@media (min-width: 1021px) {
  .ticket-side > .panel:first-child {
    position: sticky;
    top: 76px;
    max-height: calc(100vh - 96px);
    overflow-y: auto;
    /* Room for the focus ring on the last control, which sticky clipping
       would otherwise cut in half. */
    scrollbar-gutter: stable;
  }
}

/* ================================================ upload warning ======== */
.upload-warning {
  color: var(--crit);
  font-weight: 500;
}

input[type="file"][aria-invalid="true"] {
  outline: 2px solid var(--crit);
  outline-offset: 2px;
  border-radius: var(--r-sm);
}

/* ============================================== internal drafting ======= */
/* Public and internal replies are the same form, and which one you are writing
   is the only irreversible choice on the page. While the box is ticked the
   whole form takes the same tint the posted internal notes carry, so the two
   states cannot be confused mid-sentence. The button text changes too - this
   is never carried by colour alone. */
form.is-internal-draft {
  background: var(--internal-tint);
  border-left: 3px solid var(--warn);
  margin-left: -20px;
  padding-left: 17px;
  border-radius: 0 var(--r-sm) var(--r-sm) 0;
  transition: background 0.15s var(--ease);
}

form.is-internal-draft textarea {
  border-color: var(--warn);
}

/* =========================================================== motion ===== */
@keyframes rise    { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes grow    { from { transform: scaleX(0); }                    to { transform: scaleX(1); } }
@keyframes slidein { from { opacity: 0; transform: translateX(14px); } to { opacity: 1; transform: none; } }
@keyframes spin    { to { transform: rotate(360deg); } }
@keyframes beat    { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.45; transform: scale(0.8); } }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001s !important;
    animation-delay: 0s !important;
    transition-duration: 0.001s !important;
  }
}
