/* ============================================================================
   Blazor-specific chrome: the boot splash and the unhandled-error bar.
   Everything else lives in the tms-*.css design system.
   ========================================================================== */

/* --- boot splash ------------------------------------------------------- */
.tms-boot {
  position: fixed; inset: 0; display: grid; place-items: center;
  background: var(--tms-bg); color: var(--tms-text-2); z-index: 9999;
}
.tms-boot-inner { display: flex; flex-direction: column; align-items: center; gap: 16px; }
.tms-boot-mark { width: 44px; height: 44px; display: block; }
.tms-boot-mark img { display: block; width: 100%; height: 100%; }
.tms-boot-bar { width: 168px; height: 3px; background: var(--tms-surface-3); border-radius: 999px; overflow: hidden; }
.tms-boot-bar i {
  display: block; height: 100%; width: 34%; border-radius: 999px;
  background: var(--tms-accent); animation: tms-boot-slide 1.1s ease-in-out infinite;
}
@keyframes tms-boot-slide { 0% { margin-left: -34%; } 100% { margin-left: 100%; } }
.tms-boot-text { font-size: var(--tms-fs-data); }

/* Blazor swaps this in if the runtime itself fails to start. */
.tms-boot-error .tms-boot-bar { display: none; }

/* --- unhandled error bar ----------------------------------------------- */
#blazor-error-ui {
  display: none; position: fixed; bottom: 0; left: 0; right: 0; z-index: 1000;
  align-items: center; gap: 12px; padding: 12px 20px;
  background: var(--tms-danger-tint); color: var(--tms-danger);
  border-top: 1px solid var(--tms-danger-line);
  font-size: var(--tms-fs-data); font-weight: 550;
  box-shadow: 0 -4px 14px rgba(0,0,0,.08);
}
#blazor-error-ui .dismiss { margin-left: auto; cursor: pointer; font-size: 18px; line-height: 1; }
#blazor-error-ui .reload { text-decoration: underline; cursor: pointer; }

/* --- validation -------------------------------------------------------- */
.validation-message, .tms-field .validation-message {
  display: block; font-size: var(--tms-fs-cap); color: var(--tms-danger); margin-top: 5px;
}
.tms-input.invalid, .invalid .tms-input { border-color: var(--tms-danger); }
.tms-input.modified.valid { border-color: var(--tms-border-strong); }

/* --- focus ring for Blazor-rendered anchors ---------------------------- */
.tms a.tms-rail-btn:focus-visible { outline-offset: -2px; }

/* ============================================================================
   LOGIN — full-bleed split panel, rendered without the rail.
   ========================================================================== */
.tms-login {
  display: grid;
  grid-template-columns: minmax(0, 7fr) minmax(0, 6fr);
  min-height: 100vh;
}
@media (max-width: 900px) { .tms-login { grid-template-columns: 1fr; } }

.tms-login-aside {
  background: var(--tms-rail); color: #fff; padding: 56px 52px;
  display: flex; flex-direction: column; justify-content: space-between;
  position: relative; overflow: hidden;
}
@media (max-width: 900px) { .tms-login-aside { display: none; } }

/* A quiet diagonal rule pattern — texture without decoration. */
.tms-login-aside::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: repeating-linear-gradient(122deg, transparent 0 26px, rgba(255,255,255,.028) 26px 27px);
}
.tms-login-aside > * { position: relative; z-index: 1; }

.tms-brandmark { display: flex; align-items: center; gap: 11px; }
.tms-brandmark .tms-bm-sq { width: 30px; height: 30px; display: block; flex: none; }
.tms-brandmark .tms-bm-sq img { display: block; width: 100%; height: 100%; }
.tms-brandmark .tms-bm-tx { font-weight: 650; letter-spacing: -.01em; font-size: 15px; }
.tms-brandmark .tms-bm-tx span { color: var(--tms-rail-text); font-weight: 450; }

.tms-login-lede {
  font-size: clamp(26px, 3.2vw, 38px); line-height: 1.16; font-weight: 650;
  letter-spacing: -.028em; text-wrap: balance; max-width: 15ch; margin: 0 0 18px;
}
.tms-login-lede em { font-style: normal; color: var(--tms-rail-accent); }
.tms-login-sub { color: var(--tms-rail-text); max-width: 38ch; font-size: 14.5px; margin: 0; }
.tms-login-foot { font-size: var(--tms-fs-cap); color: var(--tms-rail-text); margin: 0; }

.tms-login-main { background: var(--tms-surface); display: grid; place-items: center; padding: 40px 24px; }
.tms-login-form { width: 100%; max-width: 352px; }
.tms-login-form h2 { font-size: 22px; font-weight: 650; letter-spacing: -.02em; margin: 0 0 4px; }
.tms-login-form .tms-lf-sub { color: var(--tms-text-2); font-size: 13.5px; margin: 0 0 26px; }

.tms-login-error {
  display: flex; align-items: flex-start; gap: 8px; margin-bottom: 15px;
  padding: 10px 12px; border-radius: var(--tms-r);
  background: var(--tms-danger-tint); border: 1px solid var(--tms-danger-line);
  color: var(--tms-danger); font-size: var(--tms-fs-data); font-weight: 550;
}
.tms-login-error .tms-i { margin-top: 1px; flex: none; }

.tms-spinner-sm { width: 15px; height: 15px; border-width: 2px; }

/* --- grid column chooser (v2 addition) --------------------------------- */
.tms-colchooser { position: relative; }
.tms-colchooser-menu {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: var(--tms-z-dropdown);
  min-width: 210px;
  display: flex; flex-direction: column; gap: 2px; padding: 8px;
  background: var(--tms-surface); border: 1px solid var(--tms-border-strong);
  border-radius: var(--tms-r); box-shadow: var(--tms-shadow-3);
}
.tms-colchooser-menu .tms-check {
  padding: 5px 7px; border-radius: var(--tms-r-sm);
  font-size: var(--tms-fs-data); color: var(--tms-text);
}
.tms-colchooser-menu .tms-check:hover { background: var(--tms-surface-2); }

/* --- ticket list cells -------------------------------------------------- */
.tms-subj {
  font-weight: 550; display: block; max-width: 100%;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tms-subj-meta {
  display: flex; align-items: center; gap: 8px; margin-top: 3px;
  font-size: 11.5px; color: var(--tms-text-3);
}
.tms-bolt { color: var(--tms-danger); font-size: 13px; display: inline-flex; }
.tms-age { font-family: var(--tms-font-mono); font-variant-numeric: tabular-nums; white-space: nowrap; }
.tms-age.hot  { color: var(--tms-danger); font-weight: 600; }
.tms-age.warm { color: var(--tms-warn); }

/* An urgent row gets a faint wash so it reads before you reach the badge. */
.tms-grid-table tbody tr.is-urgent td:first-child + td { position: relative; }
.tms-grid-table tbody tr.is-urgent { background: color-mix(in srgb, var(--tms-danger-tint) 45%, transparent); }
.tms-grid-table tbody tr.is-urgent:hover { background: var(--tms-danger-tint); }

/* Flagged for senior review. The ribbon is the whole point — solid red and slightly wider
   than the priority stripe so it reads as a different KIND of signal, not a hotter one.
   The row itself stays neutral: flagging asks for attention, it does not mean the ticket
   is on fire, and tinting whole rows red would make a flagged queue unreadable. */
.tms-stripe-bar.tms-stripe-flag {
  background: var(--tms-danger);
  box-shadow: 0 0 0 1px color-mix(in srgb, var(--tms-danger) 40%, transparent);
}
.tms-grid-table tbody tr.is-flagged td:first-child + td { font-weight: 600; }

/* --- create-ticket form layout ----------------------------------------- */
.tms-form-grid {
  display: grid; grid-template-columns: minmax(0, 1fr) 288px;
  gap: 18px; align-items: start;
}
@media (max-width: 1080px) { .tms-form-grid { grid-template-columns: 1fr; } }

.tms-form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
@media (max-width: 620px) { .tms-form-row { grid-template-columns: 1fr; } }

.tms-sticky-actions {
  position: sticky; bottom: 0; display: flex; gap: 8px; align-items: center;
  padding: 12px 0; margin-top: 16px;
  background: linear-gradient(to top, var(--tms-surface) 62%, transparent);
}

.tms-checklist { list-style: none; margin: 0; padding: 0; font-size: var(--tms-fs-data); }
.tms-checklist li {
  display: flex; gap: 8px; padding: 6px 0;
  color: var(--tms-text-2); align-items: flex-start;
}
.tms-checklist li .tms-i { font-size: 14px; margin-top: 2px; flex: none; }
.tms-checklist li.ok { color: var(--tms-text); }
.tms-checklist li.ok .tms-i { color: var(--tms-ok); }
.tms-checklist li.todo .tms-i { color: var(--tms-text-3); }

/* --- Power BI embed ----------------------------------------------------- */
.tms-report-frame {
  width: 100%; height: calc(100vh - var(--tms-topbar-h) - 120px); min-height: 460px;
  border: 1px solid var(--tms-border); border-radius: var(--tms-r-lg);
  background: var(--tms-surface); overflow: hidden;
}
.tms-report-frame iframe { width: 100%; height: 100%; border: 0; display: block; }

/* --- mention picker on the composer ------------------------------------ */
/* One row that scrolls, not a block that grows.
   With a dozen candidates this wrapped onto three lines and pushed the composer's footer —
   Attach, the visibility warning, Save note — down the card. Scrolling sideways costs a
   fixed height however many people are in the list. */
.tms-mention-row {
  display: flex; align-items: center; gap: 6px;
  flex-wrap: nowrap; overflow-x: auto; overflow-y: hidden;
  padding: 9px 12px; border-top: 1px solid var(--tms-border);
  background: var(--tms-surface-2);
  scrollbar-width: none;
}
.tms-mention-row::-webkit-scrollbar { height: 0; }

/* Chips keep their size instead of being squeezed to fit the row. */
.tms-mention-row > * { flex: 0 0 auto; }

/* The label stays put while the names scroll under it — it names the row rather than
   being one of its items. */
.tms-mention-row .tms-eyebrow {
  margin-right: 2px;
  position: sticky; left: 0; z-index: 1;
  background: var(--tms-surface-2); padding-right: 6px;
}
.tms-mention-row .tms-chip { height: 25px; font-size: var(--tms-fs-cap); }

/* --- notification bell -------------------------------------------------- */
.tms-bell { position: relative; }
.tms-bell-menu {
  position: absolute; right: 0; top: calc(100% + 8px); z-index: var(--tms-z-dropdown);
  width: min(360px, calc(100vw - 32px));
  background: var(--tms-surface); border: 1px solid var(--tms-border-strong);
  border-radius: var(--tms-r-lg); box-shadow: var(--tms-shadow-3); overflow: hidden;
}
.tms-bell-h {
  display: flex; align-items: center; gap: 8px;
  padding: 11px 14px; border-bottom: 1px solid var(--tms-border);
}
.tms-bell-h .tms-pill { margin-left: auto; }
.tms-bell-list { max-height: 420px; overflow-y: auto; }

.tms-bell-item {
  display: flex; align-items: flex-start; gap: 10px; width: 100%; text-align: left;
  padding: 11px 14px; border-bottom: 1px solid var(--tms-border);
  transition: background-color var(--tms-fast);
}
.tms-bell-item:last-child { border-bottom: none; }
.tms-bell-item:hover { background: var(--tms-surface-2); }

/* Unread gets an accent rail, not just bold text — it survives a quick scan. */
.tms-bell-item.is-unread { box-shadow: inset 3px 0 0 var(--tms-accent); }

.tms-bell-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tms-bell-text {
  font-size: var(--tms-fs-data); line-height: 1.45; color: var(--tms-text);
  overflow-wrap: anywhere;
}
.tms-bell-item.is-unread .tms-bell-text { font-weight: 600; }
.tms-bell-when {
  font-size: 11.5px; color: var(--tms-text-3); font-variant-numeric: tabular-nums;
}

/* ============================================================================
   Grid column resize + reorder
   ========================================================================== */
.tms-grid-table thead th { position: sticky; }
.tms-grid-table thead th .tms-th-label {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* The grab handle sits in the header's right gutter. */
.tms-col-resize {
  position: absolute; top: 0; right: 0; bottom: 0; width: 9px;
  cursor: col-resize; touch-action: none; z-index: 3;
}
.tms-col-resize::after {
  content: ""; position: absolute; top: 6px; bottom: 6px; right: 4px; width: 1px;
  background: var(--tms-border-strong); opacity: 0;
  transition: opacity var(--tms-fast);
}
.tms-grid-table thead th:hover .tms-col-resize::after { opacity: 1; }
.tms-col-resize:hover::after { opacity: 1; background: var(--tms-accent); width: 2px; }

/* Reorder feedback: the dragged header fades, the drop target shows an insert rail. */
.tms-grid-table thead th[draggable="true"] { cursor: grab; }
.tms-grid-table thead th.is-dragging { opacity: .4; cursor: grabbing; }
.tms-grid-table thead th.is-droptarget { box-shadow: inset 2px 0 0 var(--tms-accent); }

/* Cells must clip, or a narrowed column is overflowed by its content. */
.tms-grid-table td { overflow: hidden; text-overflow: ellipsis; }
.tms-grid-table td.tms-stripe { overflow: visible; }

/* ============================================================================
   Full-bleed pages — the ticket grids use the whole viewport width
   ========================================================================== */
.tms-page-full { max-width: none; }

/* ============================================================================
   DASHBOARD
   ========================================================================== */
.tms-kpis {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px; margin-bottom: 16px;
}
.tms-kpi {
  position: relative; overflow: hidden; text-decoration: none;
  background: var(--tms-surface); border: 1px solid var(--tms-border);
  border-radius: var(--tms-r-lg); padding: 14px 15px; box-shadow: var(--tms-shadow-1);
  display: block; color: inherit;
  transition: border-color var(--tms-fast), box-shadow var(--tms-fast);
}
.tms-kpi:hover { border-color: var(--tms-border-strong); box-shadow: var(--tms-shadow-2); text-decoration: none; }

/* The rail encodes which measure this is — colour is doing real work here. */
.tms-kpi::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px;
  background: var(--tms-border-strong);
}
.tms-kpi.k-info::before { background: var(--tms-info); }
.tms-kpi.k-prog::before { background: var(--tms-progress); }
.tms-kpi.k-warn::before { background: var(--tms-warn); }
.tms-kpi.k-bad::before  { background: var(--tms-danger); }
.tms-kpi.k-ok::before   { background: var(--tms-ok); }

.tms-kpi .k-l {
  display: block; font-size: var(--tms-fs-micro); font-weight: 700;
  letter-spacing: .085em; text-transform: uppercase; color: var(--tms-text-3);
}
.tms-kpi .k-v {
  display: block; font-family: var(--tms-font-mono); font-size: 29px; font-weight: 600;
  letter-spacing: -.03em; font-variant-numeric: tabular-nums; line-height: 1.1;
  margin-top: 7px; color: var(--tms-text);
}
.tms-kpi .k-d { display: block; font-size: var(--tms-fs-cap); color: var(--tms-text-2); margin-top: 5px; }

.tms-dash-grid {
  display: grid; grid-template-columns: minmax(0, 1.65fr) minmax(0, 1fr);
  gap: 14px; align-items: start;
}
/* minmax(0, 1fr), NOT 1fr.
   "1fr" is shorthand for minmax(auto, 1fr), and that auto floor is the width of the
   widest thing inside — here the 420px flow chart. The column therefore refused to go
   narrower than the chart, pushed past the viewport, and took the whole page sideways
   with it. An explicit 0 floor lets the column shrink and the chart scroll inside its
   own box, which is what it was given overflow-x for. */
@media (max-width: 1080px) { .tms-dash-grid { grid-template-columns: minmax(0, 1fr); } }

/* --- my queue ----------------------------------------------------------- */
.tms-queue { display: flex; flex-direction: column; }
.tms-queue-row {
  display: flex; align-items: center; gap: 11px; width: 100%; text-align: left;
  padding: 10px 16px 10px 0; border-bottom: 1px solid var(--tms-border);
  transition: background-color var(--tms-fast);
}
.tms-queue-row:last-child { border-bottom: none; }
.tms-queue-row:hover { background: var(--tms-surface-2); }
.tms-queue-row .tms-stripe-bar { height: 34px; flex: none; }
.tms-queue-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.tms-queue-title {
  font-size: var(--tms-fs-data); font-weight: 550; color: var(--tms-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tms-queue-meta {
  display: flex; align-items: center; gap: 8px;
  font-size: 11.5px; color: var(--tms-text-3);
}

/* --- flow chart --------------------------------------------------------- */
.tms-spark-wrap { padding-bottom: 24px; }
.tms-spark { display: flex; align-items: flex-end; gap: 5px; height: 104px; padding-top: 8px; }
.tms-bar {
  flex: 1; border-radius: 3px 3px 0 0; position: relative;
  background: var(--tms-accent-tint); border-top: 2px solid var(--tms-accent);
  transition: opacity var(--tms-fast);
}
/* A bar is a <button> now, so the browser's button defaults have to be undone —
   otherwise it arrives with its own padding, border and centred text and the chart
   loses its shape. */
.tms-bar { padding: 0; font: inherit; cursor: pointer; }
.tms-bar:hover { opacity: .7; }
.tms-bar:focus-visible { outline: 2px solid var(--tms-accent); outline-offset: 2px; }

/* A day with nothing on it has nothing to open. Left clickable it would present an
   empty dialog as though something had gone wrong. */
.tms-bar:disabled { cursor: default; }
.tms-bar:disabled:hover { opacity: 1; }
.tms-bar span {
  position: absolute; bottom: -19px; left: 0; right: 0; text-align: center;
  font-size: 10.5px; color: var(--tms-text-3); font-variant-numeric: tabular-nums;
}

/* Closed flow reads green against the accent used for opened — two views of opposite
   directions of the same queue, so they must not share a colour. */
.tms-spark-closed .tms-bar {
  background: var(--tms-ok-tint); border-top-color: var(--tms-ok);
}

/* The per-day count, sitting above its bar.

   This is what the charts were missing. Each is scaled to its own busiest day, so two
   series with similar peaks draw near-identical shapes however different the numbers are —
   the value has to be readable without hovering. */
.tms-bar b {
  position: absolute; top: -15px; left: 0; right: 0; text-align: center;
  font-size: 10px; font-weight: 600; color: var(--tms-text-2);
  font-variant-numeric: tabular-nums; line-height: 1;
}
.tms-spark-closed .tms-bar b { color: var(--tms-ok); }

/* Headroom so the topmost label is not clipped by the card. */
.tms-spark { padding-top: 20px; }

/* Fourteen bars in a narrow card used to collide, and the counts above them were hidden
   to cope. That was the wrong trade twice over: the counts are the reason the two charts
   are readable apart at all, and the tooltip they fell back on does not exist on a touch
   screen. The chart scrolls instead, with each bar guaranteed enough width to hold its
   number. */
@media (max-width: 900px) {
  .tms-spark-wrap { overflow-x: auto; -webkit-overflow-scrolling: touch; }
  .tms-spark { min-width: 460px; }
}

.tms-spark-total {
  margin-left: 10px; font-family: var(--tms-font-mono);
  font-size: var(--tms-fs-cap); color: var(--tms-text-2);
}

/* --- developer load ----------------------------------------------------- */
.tms-load-row {
  display: flex; align-items: center; gap: 11px;
  padding: 9px 0; border-bottom: 1px solid var(--tms-border);
}
.tms-load-row:last-of-type { border-bottom: none; }
.tms-load-name {
  font-size: var(--tms-fs-data); font-weight: 550; min-width: 104px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tms-load-meter {
  flex: 1; height: 7px; background: var(--tms-surface-2);
  border-radius: 999px; overflow: hidden; display: flex;
}
.tms-load-meter i { display: block; height: 100%; }
.lm-prog { background: var(--tms-progress); }
.lm-crit { background: var(--tms-danger); }
.tms-load-count {
  font-family: var(--tms-font-mono); font-size: var(--tms-fs-cap); color: var(--tms-text-2);
  font-variant-numeric: tabular-nums; min-width: 22px; text-align: right;
}
.tms-swatch {
  display: inline-block; width: 9px; height: 9px; border-radius: 2px;
  vertical-align: middle; margin-right: 4px;
}

/* --- recent feed -------------------------------------------------------- */
.tms-feed { display: flex; flex-direction: column; }
.tms-feed-item {
  display: flex; align-items: flex-start; gap: 10px; width: 100%; text-align: left;
  padding: 10px 0; border-bottom: 1px solid var(--tms-border);
  transition: background-color var(--tms-fast);
}
.tms-feed-item:last-child { border-bottom: none; }
.tms-feed-item:hover { background: var(--tms-surface-2); }
.tms-feed-body { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tms-feed-text {
  font-size: var(--tms-fs-data); line-height: 1.45; color: var(--tms-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tms-feed-when { font-size: 11.5px; color: var(--tms-text-3); }

/* ============================================================================
   Existing-portal cell styles, carried over so the grid reads the same way
   ========================================================================== */

/* --- Ticket age (variant C): four fixed slots ---------------------------- */
/* Position, not content, decides the column, so every row aligns and a young
   ticket naturally indents to the right. Missing units leave an empty slot. */
.tms-agechips {
  display: inline-grid;
  grid-template-columns: repeat(4, var(--tms-ageslot-w, 36px));
  gap: 3px;
  align-items: center;
}
.tms-ageslot { display: flex; min-width: 0; }

.tms-agechip {
  display: inline-flex; align-items: center; justify-content: flex-end;
  width: 100%; height: 22px; padding: 0 5px; border-radius: var(--tms-r-sm);
  font-family: var(--tms-font-mono); font-size: 11.5px; font-weight: 600;
  /* tabular-nums + right-align puts the digits of 4 h and 23 h in the same place */
  font-variant-numeric: tabular-nums; white-space: nowrap;
  color: var(--c); background: transparent; border: 1px solid var(--c);
}

/* Colour is fixed PER UNIT: a ticket only hours old shows green, not red. The hues are
   unchanged — only the treatment is, so a row of age chips no longer reads like a traffic
   light bolted to the grid. Months stay the loudest thing in the group, which is the whole
   point of the column. */
.tms-agechip-month  { --c: #E24B4A; }
.tms-agechip-day    { --c: #D98324; }
.tms-agechip-hour   { --c: #B59A16; }
.tms-agechip-minute { --c: #4E9A3E; }

/* A zero unit: shown, not hidden, so the four slots line up and read across. Dotted and
   muted like unassigned and untagged — the shared language for "nothing here". */
.tms-agechip.is-zero {
  background: transparent; color: var(--tms-text-3, var(--tms-text-2));
  border: 1px dotted var(--tms-border-strong); font-weight: 500; opacity: .65;
}

@supports (background: color-mix(in srgb, red 10%, transparent)) {
  .tms-agechip {
    background: color-mix(in srgb, var(--c) 14%, transparent);
    border-color: color-mix(in srgb, var(--c) 42%, transparent);
    color: color-mix(in srgb, var(--c) 82%, #10151C);
  }

  [data-theme="dark"] .tms-agechip {
    background: color-mix(in srgb, var(--c) 24%, transparent);
    border-color: color-mix(in srgb, var(--c) 55%, transparent);
    color: color-mix(in srgb, var(--c) 60%, #FFFFFF);
  }
}

/* --- Assignee age (variant D): right-aligned monospace ------------------- */
/* Same four slots, but colour on the glyphs instead of a filled box — quieter,
   because assignee age is secondary to ticket age on the same row. */
.tms-agemono {
  display: inline-grid;
  grid-template-columns: repeat(4, var(--tms-agemono-w, 34px));
  gap: 0; align-items: center;
  font-family: var(--tms-font-mono); font-size: 12px; font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.tms-agemono i {
  font-style: normal; text-align: right; padding-right: 4px; white-space: nowrap;
}
/* The zero-slot placeholder. This rule was missing, so "·" inherited the unit ink at
   weight 600 and the dots competed with the real numbers for attention — a row of
   mostly-zero units read as busier than a row of real ones. It is alignment filler
   that happens to mean "none": quiet, and never coloured. */
.tms-agemono i.is-off {
  color: var(--tms-text-3);
  font-weight: 400;
}
/* Ink, NOT fill. The chip colours above are backgrounds carrying dark text, so
   reusing them as text colours fails on a light ground — the yellow measured
   2.2:1 on white. These are the same four hues taken down to AA-legible ink,
   then lifted again for the dark surface. */
.tms-agemono-month  { color: var(--tms-age-ink-month); }
.tms-agemono-day    { color: var(--tms-age-ink-day); }
.tms-agemono-hour   { color: var(--tms-age-ink-hour); }
.tms-agemono-minute { color: var(--tms-age-ink-minute); }

/* Measured against every background these glyphs actually sit on — plain row, hover
   row, urgent row and urgent+hover — in both themes. The first pass cleared AA but
   only just (4.71–5.51 light), which at 12px in a dense grid reads washed out; these
   target AAA (7:1) so the column holds up at a glance. */
:root, :root[data-theme="light"] {
  --tms-age-ink-month:  #9B1C1C;
  --tms-age-ink-day:    #7C3D00;
  --tms-age-ink-hour:   #5C4B08;
  --tms-age-ink-minute: #1A5B31;
}
:root[data-theme="dark"] {
  --tms-age-ink-month:  #F5A8A8;
  --tms-age-ink-day:    #F0BE7A;
  --tms-age-ink-hour:   #E4D470;
  --tms-age-ink-minute: #A0D986;
}

/* --- round priority badges ----------------------------------------------- */
.tms-badges { display: inline-flex; gap: 3px; align-items: center; }
/* Priority badge. The hue is the v1 red-to-green scale, unchanged — only the weight is
   graduated, so the top of the scale still shouts while the bottom stops competing with
   everything else in the row. */
.tms-badge {
  display: inline-flex; align-items: center; justify-content: center;
  width: 24px; height: 24px; border-radius: 50%; flex: none;
  font-size: 11px; font-weight: 700; letter-spacing: .01em;
  font-variant-numeric: tabular-nums;

  background: var(--c); color: #fff; border: 1px solid transparent;
}

@supports (background: color-mix(in srgb, red 10%, transparent)) {
  /* 1–3: solid, as before. The urgent end has to survive a glance across fifty rows. */
  .tms-badge.is-hot { background: var(--c); color: #FFFFFF; border-color: var(--c); }

  /* 4–7: tinted, like the other chips. */
  .tms-badge.is-warm {
    background: color-mix(in srgb, var(--c) 24%, transparent);
    border-color: color-mix(in srgb, var(--c) 65%, transparent);
    color: color-mix(in srgb, var(--c) 88%, #0B0F14);
  }

  /* 8–10 and NP: outline only — present, deliberately quiet. */
  .tms-badge.is-cool {
    background: transparent;
    border-color: color-mix(in srgb, var(--c) 55%, transparent);
    color: color-mix(in srgb, var(--c) 88%, #0B0F14);
  }

  [data-theme="dark"] .tms-badge.is-warm {
    background: color-mix(in srgb, var(--c) 30%, transparent);
    border-color: color-mix(in srgb, var(--c) 70%, transparent);
    color: color-mix(in srgb, var(--c) 72%, #EAF1FA);
  }

  [data-theme="dark"] .tms-badge.is-cool {
    border-color: color-mix(in srgb, var(--c) 60%, transparent);
    color: color-mix(in srgb, var(--c) 72%, #EAF1FA);
  }
}

/* --- assignee / requester name chip -------------------------------------- */
/* A person, in their own colour — tinted like the type chips rather than a solid block.
   Four solid blocks per row (status, type, person, priority) turned the grid into a
   colour chart; tinted fills keep the identity and give the row somewhere to breathe.

   The person's colour arrives as --c and everything else is derived from it, so one
   attribute per chip drives background, border and text. Plain colour first as the
   fallback: a browser without color-mix keeps a readable chip instead of an invisible
   one. */
/* The team glyph sits inside the chip and inherits its colour, so one --c still drives
   everything. Flex rather than inline-block: the icon and the name have to sit on one
   baseline and the name has to be the part that truncates. */
.tms-chip-i { width: 13px; height: 13px; flex: none; margin-right: 5px; }

/* The NAME truncates, not the chip. On a flex container text-overflow has nothing to act
   on — the ellipsis needs an element of its own, or a long name is simply cut mid-letter
   with no sign that anything is missing. */
.tms-chip-t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }

/* In the grid every chip fills the cell, so the rectangles line up into a column instead
   of ragging to whatever length each name happens to be. Elsewhere — cards, the employee
   form — a chip still hugs its text, because there is no column to align to. */
.tms-chipcol { display: flex; flex-direction: column; gap: 3px; align-items: stretch; }
.tms-chipcol .tms-namechip { width: 100%; max-width: none; }

.tms-namechip {
  display: inline-flex; align-items: center; max-width: 100%;
  height: 22px; padding: 0 8px; border-radius: var(--tms-r-sm);
  font-size: 11.5px; font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;

  color: var(--c, var(--tms-text));
  background: transparent;
  border: 1px solid var(--c, var(--tms-border-strong));
}

@supports (background: color-mix(in srgb, red 10%, transparent)) {
  .tms-namechip {
    background: color-mix(in srgb, var(--c) 13%, transparent);
    border-color: color-mix(in srgb, var(--c) 42%, transparent);
    color: color-mix(in srgb, var(--c) 82%, #0B0F14);
  }

  /* Backed off from the earlier "as bright as possible" pass. A near-full-strength border
     around every chip turned a page of forty rows into forty outlines all shouting at
     once — the colour has to identify a person at a glance, not compete with the row it
     sits in. The BORDER is what was doing the shouting, so that is what came down most;
     the text keeps enough of the hue to stay identifiable. */
  [data-theme="dark"] .tms-namechip {
    background: color-mix(in srgb, var(--c) 17%, transparent);
    border-color: color-mix(in srgb, var(--c) 46%, transparent);
    color: color-mix(in srgb, var(--c) 74%, #EAF1FA);
  }

  .tms-namechip .tms-chip-i { color: color-mix(in srgb, var(--c) 88%, #0B0F14); }
  [data-theme="dark"] .tms-namechip .tms-chip-i {
    color: color-mix(in srgb, var(--c) 78%, #EAF1FA);
  }
}

/* --- status pill driven by the TicketType.Color the API returns ----------- */
/* Status, tinted like the person and type chips. The colour still comes from
   TicketType.Color — the Ticket Statuses screen keeps deciding it — but it now arrives as
   --c and the fill, border and text are derived, so a row reads as one family of chips
   instead of four solid blocks competing. */
/* A FLOOR on the width, deliberately not a fixed width. Statuses run from "QA" at two
   characters to "Awaiting Confirmation" at twenty-one, so one shared width would have to
   fit the longest and would leave "Open" as a small word adrift in a box five times the
   colour area it needs. The floor squares up the stubby chips and leaves the long ones to
   size themselves, which also keeps chip length usable as a cue — "Awaiting Confirmation"
   is recognisable by silhouette before it is read. */
.tms-status-chip {
  display: inline-block; padding: 0 9px; border-radius: var(--tms-r-sm);
  height: 22px; line-height: 20px;
  font-size: 11.5px; font-weight: 600; white-space: nowrap;
  width: 120px; text-align: center;
  overflow: hidden; text-overflow: ellipsis;

  color: var(--c, var(--tms-text));
  background: transparent;
  border: 1px solid var(--c, var(--tms-border-strong));
}

@supports (background: color-mix(in srgb, red 10%, transparent)) {
  .tms-status-chip {
    background: color-mix(in srgb, var(--c) 14%, transparent);
    border-color: color-mix(in srgb, var(--c) 45%, transparent);
    color: color-mix(in srgb, var(--c) 78%, #10151C);
  }

  [data-theme="dark"] .tms-status-chip {
    background: color-mix(in srgb, var(--c) 22%, transparent);
    border-color: color-mix(in srgb, var(--c) 55%, transparent);
    color: color-mix(in srgb, var(--c) 55%, #FFFFFF);
  }
}

/* The age and badge columns are dense — let them breathe rather than clip. */
.tms-grid-table td.tms-nowrap { white-space: nowrap; overflow: visible; }

/* ============================================================================
   Grid filter row
   ========================================================================== */
.tms-grid-table thead tr.tms-filter-row th {
  position: sticky; top: var(--tms-th-h, 38px); z-index: 2;
  padding: 5px 6px; background: var(--tms-surface-2);
  border-bottom: 1px solid var(--tms-border);
}
.tms-filter-input {
  width: 100%; height: 27px; padding: 0 7px;
  background: var(--tms-surface); color: var(--tms-text);
  border: 1px solid var(--tms-border); border-radius: var(--tms-r-sm);
  font-size: var(--tms-fs-cap); font-weight: 450;
}
.tms-filter-input:focus {
  outline: none; border-color: var(--tms-accent);
  box-shadow: 0 0 0 2px var(--tms-accent-tint);
}
select.tms-filter-input { cursor: pointer; }

/* --- header gesture feedback (JS-driven) -------------------------------- */
.tms-grid-table thead th.is-resizing { background: var(--tms-accent-tint); }
.tms-grid-table thead th.is-dragging { opacity: .45; }
.tms-grid-table thead th.is-droptarget-before { box-shadow: inset 3px 0 0 var(--tms-accent); }
.tms-grid-table thead th.is-droptarget-after  { box-shadow: inset -3px 0 0 var(--tms-accent); }

/* Headers are the drag handle, so show the grab affordance — except on the
   resize gutter, which has its own cursor. */
.tms-grid-table thead tr:first-child th[data-colkey] { cursor: grab; }
.tms-grid-table thead tr:first-child th[data-colkey]:active { cursor: grabbing; }
.tms-grid-table thead tr.tms-filter-row th { cursor: default; }

/* ============================================================================
   Scrollbars — the grid's horizontal bar was rendering in the browser's default
   light chrome, which reads as a white slab across a dark page.
   ========================================================================== */
.tms-grid-scroll {
  scrollbar-width: thin;                                   /* Firefox */
  scrollbar-color: var(--tms-border-strong) transparent;
}
.tms-grid-scroll::-webkit-scrollbar { height: 11px; width: 11px; }
.tms-grid-scroll::-webkit-scrollbar-track {
  background: var(--tms-surface-2);
  border-top: 1px solid var(--tms-border);
}
.tms-grid-scroll::-webkit-scrollbar-thumb {
  background: var(--tms-border-strong); border-radius: 999px;
  border: 2px solid var(--tms-surface-2);                   /* inset the thumb */
}
.tms-grid-scroll::-webkit-scrollbar-thumb:hover { background: var(--tms-text-3); }
.tms-grid-scroll::-webkit-scrollbar-corner { background: var(--tms-surface-2); }

/* Same treatment for the other scrolling panels. */
.tms-bell-list, .tms-colchooser-list, .tms-select-list, .tms-fmenu-list, .tms-modal-b {
  scrollbar-width: thin;
  scrollbar-color: var(--tms-border-strong) transparent;
}
.tms-bell-list::-webkit-scrollbar,
.tms-colchooser-list::-webkit-scrollbar,
.tms-select-list::-webkit-scrollbar,
.tms-fmenu-list::-webkit-scrollbar,
.tms-modal-b::-webkit-scrollbar { width: 10px; height: 10px; }
.tms-bell-list::-webkit-scrollbar-thumb,
.tms-colchooser-list::-webkit-scrollbar-thumb,
.tms-select-list::-webkit-scrollbar-thumb,
.tms-fmenu-list::-webkit-scrollbar-thumb,
.tms-modal-b::-webkit-scrollbar-thumb {
  background: var(--tms-border-strong); border-radius: 999px;
  border: 2px solid var(--tms-surface);
}

/* The scroll container owns the bottom corners, so round them with the card. */
.tms-grid-scroll { border-radius: var(--tms-r-lg); }

/* ============================================================================
   Multi-select column filter
   ========================================================================== */
.tms-fmenu { position: relative; }
.tms-fmenu-btn {
  display: flex; align-items: center; gap: 4px; width: 100%; height: 27px;
  padding: 0 6px 0 7px; background: var(--tms-surface); color: var(--tms-text-2);
  border: 1px solid var(--tms-border); border-radius: var(--tms-r-sm);
  font-size: var(--tms-fs-cap); font-weight: 450; text-align: left;
  transition: border-color var(--tms-fast), color var(--tms-fast), background-color var(--tms-fast);
}
.tms-fmenu-btn:hover { border-color: var(--tms-border-strong); color: var(--tms-text); }
.tms-fmenu-btn.is-on {
  background: var(--tms-accent-tint); border-color: var(--tms-accent-line);
  color: var(--tms-accent-dark); font-weight: 600;
}
.tms-fmenu-btn .tms-i { margin-left: auto; font-size: 13px; flex: none; }
.tms-fmenu.is-open .tms-fmenu-btn .tms-i { transform: rotate(180deg); }
.tms-fmenu-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.tms-fmenu-pop {
  position: absolute; left: 0; top: calc(100% + 4px); z-index: var(--tms-z-dropdown);
  min-width: max(190px, 100%); max-width: 300px;
  background: var(--tms-surface); border: 1px solid var(--tms-border-strong);
  border-radius: var(--tms-r); box-shadow: var(--tms-shadow-3); overflow: hidden;
  display: flex; flex-direction: column;
}
.tms-fmenu-search, .tms-fmenu-actions, .tms-fmenu-pop .tms-pop-h, .tms-fmenu-pop .tms-pop-f { flex: none; }
.tms-fmenu-search { padding: 7px; border-bottom: 1px solid var(--tms-border); }
.tms-fmenu-search input {
  width: 100%; height: 28px; padding: 0 8px;
  background: var(--tms-surface-2); color: var(--tms-text);
  border: 1px solid transparent; border-radius: var(--tms-r-sm); font-size: var(--tms-fs-cap);
}
.tms-fmenu-search input:focus { outline: none; border-color: var(--tms-accent); }

.tms-fmenu-actions {
  display: flex; gap: 4px; padding: 5px 6px;
  border-bottom: 1px solid var(--tms-border); background: var(--tms-surface-2);
}
.tms-fmenu-actions .tms-btn { flex: 1; }

/* The popover is pinned to the viewport and given a max-height in JS, so it has to be a
   flex column: the list absorbs whatever is left after the header, actions and footer, and
   scrolls inside itself. With a fixed max-height here the footer was pushed out of the box
   on a short grid, taking the Done button with it. */
.tms-fmenu-list {
  list-style: none; margin: 0; padding: 4px;
  flex: 1 1 auto; min-height: 0; overflow-y: auto;
}
.tms-fmenu-list .tms-check {
  width: 100%; padding: 5px 7px; border-radius: var(--tms-r-sm);
  font-size: var(--tms-fs-data); color: var(--tms-text);
}
.tms-fmenu-list .tms-check:hover { background: var(--tms-surface-2); }
.tms-fmenu-list .tms-check span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tms-fmenu-none { padding: 14px 10px; text-align: center; color: var(--tms-text-3); font-size: var(--tms-fs-data); }

/* --- multi-column sort indicator ---------------------------------------- */
.tms-grid-table thead th .tms-sort { display: inline-flex; align-items: center; gap: 1px; }
.tms-sort-ord {
  font-size: 9px; font-weight: 700; line-height: 1;
  color: var(--tms-accent); font-variant-numeric: tabular-nums;
}

/* ============================================================================
   Shared popover chrome — header with a close button, and a footer.
   Every lingering popover (column chooser, column filter, multi-select) gets one,
   so none of them can trap you: the column chooser used to cover the only button
   that closed it.
   ========================================================================== */
.tms-pop-h {
  display: flex; align-items: center; gap: 8px;
  padding: 7px 8px 7px 10px; border-bottom: 1px solid var(--tms-border);
  background: var(--tms-surface-2);
}
.tms-pop-h .tms-eyebrow {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tms-pop-x {
  margin-left: auto; flex: none;
  width: 22px; height: 22px; display: grid; place-items: center;
  border-radius: var(--tms-r-sm); color: var(--tms-text-3); font-size: 13px;
  transition: background-color var(--tms-fast), color var(--tms-fast);
}
.tms-pop-x:hover { background: var(--tms-surface-3); color: var(--tms-text); }

.tms-pop-f {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 8px; border-top: 1px solid var(--tms-border);
  background: var(--tms-surface-2);
}
.tms-pop-f .tms-btn:last-child { margin-left: auto; }
.tms-pop-count { font-size: 11.5px; color: var(--tms-text-3); }

/* The chooser's checkbox list scrolls; header and footer stay put. */
.tms-colchooser-list {
  display: flex; flex-direction: column; gap: 2px;
  padding: 6px; flex: 1; min-height: 0; overflow-y: auto;
}

/* The menu no longer pads itself — header, list and footer own their spacing.
   It is also the height cap and the ONLY non-scrolling shell: a flex column that clips,
   so the checkbox list inside it is the single scroller. Declared here rather than in the
   base rule above because this rule comes later and would otherwise reset display back
   to block, which is exactly what left the popup unbounded with no scrollbar at all. */
.tms-colchooser-menu {
  padding: 0;
  display: flex; flex-direction: column;
  max-height: min(340px, 70vh); overflow: hidden;
}

/* Header and footer keep their size; only the list gives. */
.tms-colchooser-menu .tms-pop-h,
.tms-colchooser-menu .tms-pop-f { flex: none; }

/* ============================================================================
   CREATE TICKET

   Form left, capacity right. The rail is sticky so the quota and the free
   priority ranks stay visible while the description is being written — being
   told you are at the limit after typing 400 words is the thing to avoid.
   ========================================================================== */
.tms-create {
  display: grid;
  grid-template-columns: minmax(0, 1fr) 320px;
  gap: 16px;
  align-items: start;
}
@media (max-width: 1080px) {
  .tms-create { grid-template-columns: minmax(0, 1fr); }
}

/* Sticky ONLY where there is a second column for it to stick beside.
   This used to be declared unconditionally with a media query above it turning it
   static — but the unconditional rule came later in the file and won, so the panel
   stayed sticky once stacked and floated over the form underneath it. Scoped by
   min-width instead, so the narrow case cannot be undone by source order. */
@media (min-width: 1081px) {
  .tms-caprail { position: sticky; top: calc(var(--tms-topbar-h) + 16px); }
}

.tms-group-h {
  display: flex; align-items: center; gap: 9px;
  font-size: var(--tms-fs-micro); font-weight: 700; letter-spacing: .06em;
  text-transform: uppercase; color: var(--tms-text-3);
  margin: 20px 0 10px;
}
.tms-group-h:first-child { margin-top: 0; }
.tms-group-h i { flex: 1; height: 1px; background: var(--tms-border); display: block; }

.tms-two { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 620px) { .tms-two { grid-template-columns: 1fr; } }

/* Read-only stand-in for a select the user has no choice about — a customer
   cannot raise a ticket for anyone but their own organisation. */
.tms-input-static {
  display: flex; align-items: center;
  background: var(--tms-surface-2); color: var(--tms-text-2); cursor: default;
}

.tms-internal {
  margin-top: 18px; padding: 14px;
  border: 1px dashed var(--tms-progress-line); border-radius: var(--tms-r);
  background: var(--tms-progress-tint);
}
.tms-internal .tms-group-h { margin-top: 0; color: var(--tms-progress); }
.tms-internal .tms-group-h i { background: var(--tms-progress-line); }

/* --- capacity ------------------------------------------------------------- */
.tms-cap-top { display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap; margin-bottom: 9px; }
.tms-cap-top b { font-size: var(--tms-fs-title); font-family: var(--tms-font-mono); font-weight: 650; }
.tms-cap-top span { font-size: var(--tms-fs-cap); color: var(--tms-text-2); }
.tms-cap-free {
  margin-left: auto; background: var(--tms-accent-tint); color: var(--tms-accent-dark);
  border: 1px solid var(--tms-accent-line); border-radius: 999px;
  padding: 1px 9px; white-space: nowrap;
}

.tms-cap-bar {
  display: flex; height: 22px; border-radius: var(--tms-r);
  overflow: hidden; background: var(--tms-surface-3);
}
.tms-cap-bar i { display: block; height: 100%; }
.tms-cap-bar i + i { box-shadow: inset 1px 0 0 var(--tms-surface); }

.is-dev { background: var(--tms-progress); }
.is-bug { background: var(--tms-danger); }
.is-act { background: var(--tms-ok); }
.is-un  { background: var(--tms-border-strong); }

.tms-cap-leg {
  display: flex; flex-wrap: wrap; gap: 9px;
  margin-top: 8px; font-size: var(--tms-fs-micro); color: var(--tms-text-2);
}
.tms-cap-leg span { display: inline-flex; align-items: center; gap: 5px; }
.tms-sw { width: 9px; height: 9px; border-radius: 2px; display: inline-block; flex: none; }

.tms-cap-warn, .tms-cap-stop {
  margin-top: 10px; padding: 8px 11px; border-radius: var(--tms-r);
  font-size: var(--tms-fs-cap); line-height: 1.5;
}
.tms-cap-warn { background: var(--tms-warn-tint); color: var(--tms-warn); border: 1px solid var(--tms-warn-line); }
.tms-cap-stop { background: var(--tms-danger-tint); color: var(--tms-danger); border: 1px solid var(--tms-danger-line); }

/* The way out of a full quota: the client's own tickets waiting on them. */
.tms-cap-await {
  margin-top: 9px; padding: 9px 11px; border-radius: var(--tms-r);
  background: var(--tms-info-tint); border: 1px solid var(--tms-info-line);
}
.tms-cap-await > b { font-size: var(--tms-fs-cap); color: var(--tms-info); display: block; margin-bottom: 5px; }
.tms-cap-awrow {
  display: flex; align-items: center; gap: 7px;
  font-size: var(--tms-fs-micro); color: var(--tms-info);
  padding: 3px 0; text-decoration: none;
}
.tms-cap-awrow:hover { text-decoration: underline; }
.tms-cap-awttl { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tms-cap-awday { flex: none; font-family: var(--tms-font-mono); opacity: .8; }

/* --- priority slots ------------------------------------------------------- */
.tms-slotgrid { display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); gap: 5px; }
.tms-slot {
  border: 1px solid var(--tms-border); border-radius: var(--tms-r);
  background: var(--tms-surface-2); padding: 5px 6px; min-height: 44px;
  text-align: left; transition: border-color var(--tms-fast), background-color var(--tms-fast);
}
.tms-slot-n { display: block; font-family: var(--tms-font-mono); font-size: var(--tms-fs-data); font-weight: 650; line-height: 1.1; }
.tms-slot-s { display: block; font-size: 9.5px; color: var(--tms-text-3); margin-top: 2px;
              white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.tms-slot.is-free { border-style: dashed; }
.tms-slot.is-free:hover:not(:disabled) { border-color: var(--tms-accent); background: var(--tms-surface); }
.tms-slot.is-free .tms-slot-s { color: var(--tms-accent-ink-2); }

/* Occupied ranks stay readable rather than being greyed into illegibility —
   the ticket number is the reason the rank is unavailable. */
.tms-slot.is-taken { opacity: .75; cursor: not-allowed; }

.tms-slot.is-sel {
  background: var(--tms-accent-tint); border: 2px solid var(--tms-accent); border-style: solid;
}
.tms-slot.is-sel .tms-slot-n { color: var(--tms-accent-dark); }
.tms-slot.is-sel .tms-slot-s { color: var(--tms-accent); }

.tms-slot-none {
  display: block; width: 100%; margin-top: 6px;
  border: 1px dashed var(--tms-border); border-radius: var(--tms-r);
  background: var(--tms-surface-2); color: var(--tms-text-2);
  padding: 7px 10px; font-size: var(--tms-fs-cap); text-align: left;
}
.tms-slot-none.is-sel {
  background: var(--tms-accent-tint); border-color: var(--tms-accent);
  border-style: solid; color: var(--tms-accent-dark); font-weight: 600;
}

.tms-caprail .tms-btn-block { margin-top: 12px; }

/* Organisation option: name left, tier right — the tier is what people scan for
   when several clients share a similar name. */
.tms-orgrow { display: flex; align-items: baseline; gap: 8px; width: 100%; }
.tms-orgname { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tms-orgtier {
  flex: none; font-family: var(--tms-font-mono); font-size: var(--tms-fs-micro);
  color: var(--tms-text-3); background: var(--tms-surface-3);
  border-radius: var(--tms-r-sm); padding: 1px 6px;
}

.tms-hint-inline { font-weight: 400; font-size: var(--tms-fs-micro); color: var(--tms-text-3); text-transform: none; }

/* Window picker on the solved list. Sized to its content and to match the toolbar
   buttons beside it, rather than the full-width form inputs .tms-input assumes. */
.tms-window-pick {
  width: auto;
  padding: 5px 8px;
  font-size: 13px;
}

/* ---- Star rating -------------------------------------------------------- */
/* Rendered 5..1 in the DOM so ~ can reach the stars that should also light up;
   row-reverse puts 1..5 back in reading order on screen. */
.tms-stars {
  display: inline-flex;
  flex-direction: row-reverse;
  justify-content: flex-end;
  gap: 2px;
}
.tms-star-in {
  position: absolute;
  width: 1px; height: 1px;
  opacity: 0;
  pointer-events: none;
}
.tms-star {
  cursor: pointer;
  font-size: 24px;
  line-height: 1;
  color: var(--tms-border-strong);
  transition: color .12s ease, transform .12s ease;
  user-select: none;
}
.tms-star:hover { transform: scale(1.12); }

/* The chosen star and everything before it (later in the DOM). */
.tms-star-in:checked + .tms-star,
.tms-star-in:checked + .tms-star ~ .tms-star { color: var(--tms-warn); }

/* Preview on hover: the row reverses, so :hover on a label lights its own star
   and every sibling after it, which are the LOWER numbers. */
.tms-stars:hover .tms-star { color: var(--tms-border-strong); }
.tms-stars .tms-star:hover,
.tms-stars .tms-star:hover ~ .tms-star { color: var(--tms-warn); }

.tms-star-in:focus-visible + .tms-star {
  outline: 2px solid var(--tms-accent);
  outline-offset: 2px;
  border-radius: 3px;
}

/* ---- Close feedback dialog ---------------------------------------------- */
.tms-fb-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 12px 0;
  border-bottom: 1px solid var(--tms-border);
}
.tms-fb-row:last-of-type { border-bottom: 0; }
.tms-fb-who { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.tms-fb-who b { font-size: 14px; }
.tms-fb-who span { font-size: 12px; }

.tms-fb-comment { padding-top: 12px; }
.tms-fb-comment label {
  display: flex;
  align-items: baseline;
  gap: 8px;
  font-size: 13px;
  font-weight: 600;
  margin-bottom: 6px;
}
.tms-fb-comment label span { font-weight: 400; font-size: 12px; }
.tms-fb-comment textarea { resize: vertical; }

/* Required only because a low score was given — amber, not red: nothing is wrong
   yet, we are asking for one more thing. */
.tms-req { color: var(--tms-warn); }

/* Feedback shown back on a closed ticket. */
.tms-fb-panel {
  border: 1px solid var(--tms-border);
  border-radius: 8px;
  padding: 12px 14px;
  margin-bottom: 16px;
  background: var(--tms-surface-2);
}
.tms-fb-panel h3 { margin: 0 0 8px; font-size: 13px; }
.tms-fb-panel .tms-fb-score { color: var(--tms-warn); letter-spacing: 1px; }
.tms-fb-panel .tms-fb-line {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  font-size: 13px;
  padding: 3px 0;
}
.tms-fb-panel blockquote {
  margin: 8px 0 0;
  padding-left: 10px;
  border-left: 3px solid var(--tms-border-strong);
  font-size: 13px;
  color: var(--tms-text-2);
}
.tms-fb-panel.is-auto { border-color: var(--tms-warn-line); background: var(--tms-warn-tint); }

/* Standing feedback score beside a name in the trail. Deliberately quiet — it is
   context, not the subject of the row. */
.tms-trail-rate {
  font-size: 11px;
  font-weight: 600;
  color: var(--tms-warn);
  white-space: nowrap;
}

/* ============================================================================
   Employee performance
   ========================================================================== */
.tms-perf-note {
  display: flex; align-items: flex-start; gap: 9px;
  background: var(--tms-surface-2); border: 1px solid var(--tms-border);
  border-radius: var(--tms-r); padding: 10px 12px; margin-bottom: 14px;
  font-size: 12.5px; color: var(--tms-text-2); line-height: 1.5;
}
.tms-perf-note svg { flex: none; margin-top: 2px; color: var(--tms-text-3); }

.tms-perf-table th.tms-num, .tms-perf-table td.tms-num { text-align: right; }
.tms-perf-table td b { display: block; font-size: 13px; }
.tms-perf-sub {
  display: block; font-size: 11px; color: var(--tms-text-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 340px;
}

/* Too few tickets to derive rates from. Dimmed, never hidden — a person with three
   tickets should still appear, just not be judged. */
.tms-perf-table tbody tr.is-thin { opacity: .55; }

.tms-perf-table tbody tr.is-median {
  background: var(--tms-surface-2); border-top: 2px solid var(--tms-border-strong);
  cursor: default;
}
.tms-perf-table tbody tr.is-median:hover { background: var(--tms-surface-2); }

.tms-perf-cards {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  gap: 10px; margin-bottom: 18px;
}
.tms-perf-stat {
  background: var(--tms-surface); border: 1px solid var(--tms-border);
  border-radius: var(--tms-r); padding: 12px 14px;
  display: flex; flex-direction: column; gap: 3px;
}
.tms-perf-stat-l { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; color: var(--tms-text-3); }
.tms-perf-stat-v { font-family: var(--tms-font-head); font-size: 22px; font-weight: 700; line-height: 1.1; }
.tms-perf-stat-m { font-size: 11px; color: var(--tms-text-3); }

.tms-perf-block { margin-bottom: 22px; }
.tms-perf-block h2 { font-size: 14px; margin: 0 0 8px; }

.tms-perf-bars { display: flex; align-items: flex-end; gap: 6px; height: 130px; }
.tms-perf-bar { flex: 1; display: flex; flex-direction: column; justify-content: flex-end; height: 100%; gap: 4px; }
.tms-perf-bar-v { background: var(--tms-accent); border-radius: 3px 3px 0 0; min-height: 3px; }
.tms-perf-bar-l { font-size: 10px; color: var(--tms-text-3); text-align: center; white-space: nowrap; }

.tms-perf-mix { display: flex; flex-direction: column; gap: 6px; max-width: 520px; }
.tms-perf-mix-row { display: grid; grid-template-columns: 120px 1fr 40px; align-items: center; gap: 10px; font-size: 13px; }
.tms-perf-mix-t { background: var(--tms-surface-3); border-radius: 999px; height: 8px; overflow: hidden; }
.tms-perf-mix-t i { display: block; height: 100%; background: var(--tms-accent); border-radius: 999px; }

.tms-perf-quote {
  margin: 0 0 10px; padding: 10px 12px;
  border-left: 3px solid var(--tms-border-strong); background: var(--tms-surface-2);
  border-radius: 0 var(--tms-r) var(--tms-r) 0;
}
.tms-perf-quote p { margin: 4px 0; font-size: 13px; }
.tms-perf-quote cite { font-size: 11px; color: var(--tms-text-3); font-style: normal; }

/* ============================================================================
   Activity report
   ========================================================================== */
.tms-act-block { margin-bottom: 22px; }
.tms-act-block h2 { font-size: 14px; margin: 0 0 8px; }

.tms-act-table th.tms-num, .tms-act-table td.tms-num { text-align: right; }
.tms-act-table td b { display: block; font-size: 13px; }

.tms-act-sub {
  display: block; font-size: 11px; color: var(--tms-text-3);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 320px;
}

/* The expanded row, so it is obvious which month or person the sections below belong to. */
.tms-act-table tbody tr.is-open-row {
  background: var(--tms-accent-tint);
  box-shadow: inset 3px 0 0 var(--tms-accent);
}
.tms-act-table tbody tr.is-open-row:hover { background: var(--tms-accent-tint); }

/* Weekends dimmed. Activity on a Sunday is worth noticing, not worth reading as normal. */
.tms-act-table tbody tr.is-weekend td { color: var(--tms-text-3); }

.tms-act-did { font-size: 12px; white-space: nowrap; }

/* Status filter above the activity ticket table. */
.tms-act-filter {
  display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  margin-bottom: 8px;
}
.tms-act-filter-n { font-size: 11px; color: var(--tms-text-3); margin-left: 4px; }

/* Finished-per-day heatmap. The name column is pinned so a full month can scroll past it. */
.tms-heat-table th.tms-heat-who, .tms-heat-table td.tms-heat-who {
  position: sticky; left: 0; z-index: 1;
  background: var(--tms-surface); min-width: 150px;
}
.tms-heat-table td.tms-heat-who b { display: block; font-size: 13px; }

.tms-heat-table th.tms-heat-d {
  width: 26px; padding: 6px 0; text-align: center;
  font-size: 10px; color: var(--tms-text-3);
}
.tms-heat-table td.tms-heat-c {
  width: 26px; padding: 4px 0; text-align: center;
  font-family: var(--tms-font-mono); font-size: 11px;
  border-left: 1px solid var(--tms-surface);
}
.tms-heat-table .is-weekend { background: var(--tms-surface-2); }

/* Four bands rather than a gradient: a 4 is not meaningfully better than a 3 here, and a
   continuous scale would claim it was. */
.tms-heat-c.h0 { color: var(--tms-text-3); }
.tms-heat-c.h1 { background: color-mix(in srgb, var(--tms-accent) 18%, transparent); }
.tms-heat-c.h2 { background: color-mix(in srgb, var(--tms-accent) 42%, transparent); }
.tms-heat-c.h3 { background: var(--tms-accent); color: var(--tms-on-accent); font-weight: 700; }

/* Date range on the solved list. */
.tms-daterange { display: flex; align-items: center; gap: 8px; }
.tms-daterange label { display: flex; align-items: center; gap: 5px; font-size: 12px; color: var(--tms-text-3); }
.tms-daterange input { width: auto; padding: 5px 8px; font-size: 12.5px; }

/* Ticket id as a link. Inherits the cell colour so the grid does not turn into a page of
   blue text, but behaves as a link for the browser. */
.tms-id-link { color: inherit; text-decoration: none; }
.tms-id-link:hover { color: var(--tms-accent); text-decoration: underline; }

/* Group-by picker in the grid toolbar.
   .tms-input is a full-width, 40px-tall FORM control; the toolbar holds 29px chips and
   buttons and is a wrapping flex row. Left to inherit, the select stretched into whatever
   space was going and stood a head taller than everything beside it. Sized explicitly and
   flex:none so it neither grows nor collapses as the toolbar wraps. */
.tms-groupby { display: inline-flex; align-items: center; gap: 6px; flex: none; }
.tms-groupby > span { font-size: 11px; color: var(--tms-text-3); white-space: nowrap; }
.tms-groupby select {
  flex: none;
  width: 132px;          /* fits the longest option without sizing to it */
  height: 29px;          /* matches .tms-chip and .tms-btn-sm in the same row */
  padding: 0 6px;
  font-size: 12.5px;
  border-radius: var(--tms-r-sm);
}

/* Organisation filter in the flow-chart header. TmsMultiSelect is built for a form
   column, so it is given a width here and its control shrunk to sit beside the eyebrow
   text rather than tower over it. */
.tms-flow-org { width: 190px; flex: none; }

/* The control must never grow past its column, whatever is selected. */
.tms-flow-org .tms-select-control {
  flex-wrap: nowrap; overflow: hidden;
}
.tms-flow-org .tms-select-chip { max-width: 100%; }

/* The filter label on the closed chart. Truncated rather than wrapped: a card header is
   one line, and two organisation names are already at the limit of what belongs there. */
.tms-flow-tag {
  max-width: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  display: inline-block; line-height: 27px;
}
.tms-flow-org .tms-select-control {
  min-height: 28px; height: 28px; padding: 0 8px; font-size: 12px;
}

/* Select all / Clear row inside a multi-select. flex:none so it keeps its height while
   the option list below takes the remaining space and scrolls. */
.tms-select-actions {
  display: flex; gap: 6px; flex: none;
  padding: 6px 8px; border-bottom: 1px solid var(--tms-border);
}
.tms-select-actions button { flex: 1; }

/* A field hint shown ABOVE its control, for cases where an open dropdown would cover it. */
.tms-hint-top { margin: 0 0 4px; }

/* A cell held to one line, clipped with an ellipsis.
   td already sets overflow:hidden and text-overflow:ellipsis, but neither does anything
   until the text is stopped from wrapping — which is why these cells grew to two lines
   instead of truncating. The inner span carries it too, so a templated cell truncates the
   same as a plain one. */
/* display:block was on the CELL, which is what pushed the text to the top and stopped
   the column resizing: a td that is not a table-cell no longer takes part in the table
   layout, so vertical-align does nothing and the column-width algorithm ignores it.
   Only the inner span becomes a block; the cell stays a cell. */
.tms-grid-table td.tms-1line {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
.tms-grid-table td.tms-1line > span {
  display: block;
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ============================================================================
   Compact rows
   Roughly 40% more rows per screen. Padding and line-height do the work; the
   font drops only slightly, because shrinking text is what makes a "compact"
   mode unreadable rather than dense.
   ========================================================================== */
/* Ticket rows only — :not(.tms-grouprow) throughout.
   Squeezing the group headings clipped their text, and a heading is the one row on
   screen that has to stay readable: it is what tells you which block you are looking at.
   Compacting is about fitting more TICKETS, not more furniture. */
.tms-grid-scroll.is-dense .tms-grid-table tbody tr:not(.tms-grouprow) { height: 22px; }
.tms-grid-scroll.is-dense .tms-grid-table tbody tr:not(.tms-grouprow) > td {
  padding: 0 8px; line-height: 1.2;
}
/* The filter row pins itself at --tms-th-h, the height of the header row above it.
   Compact shrank that header without telling the variable, so the filter row stayed at
   38px — 14px below where the header now ends — and sat on top of the first body row.
   That is what was cutting the group heading in half; the heading was never the problem.
   Declared on the scroller so it inherits down to the sticky cell. */
.tms-grid-scroll.is-dense { --tms-th-h: 24px; }
.tms-grid-scroll.is-dense .tms-grid-table thead th { padding: 3px 8px; }
.tms-grid-scroll.is-dense .tms-grid-table { font-size: 12.5px; }

/* Padding stops mattering once something inside the row is taller than the row wants
   to be. These are what actually set the floor. */
.tms-grid-scroll.is-dense tbody tr:not(.tms-grouprow) .tms-avatar {
  width: 16px; height: 16px; font-size: 9px;
}
.tms-grid-scroll.is-dense tbody tr:not(.tms-grouprow) .tms-pill {
  padding: 0 5px; font-size: 10px; line-height: 14px; height: 14px;
}
.tms-grid-scroll.is-dense tbody tr:not(.tms-grouprow) .tms-i { font-size: 11px; }
.tms-grid-scroll.is-dense .tms-bd-est { padding: 0 4px; }

/* A cell that wraps to two lines is two rows tall however little padding it has, so in
   compact every ticket cell is held to one line. Group headings are exempt. */
.tms-grid-scroll.is-dense tbody tr:not(.tms-grouprow) > td,
.tms-grid-scroll.is-dense tbody tr:not(.tms-grouprow) > td > span {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* Group headings keep their room and their full text.
   overflow stays HIDDEN: with it visible the heading rendered outside its own row and
   slid under the sticky filter row above, which is what clipped "UNDEFINED". A heading
   that escapes its row is a heading that ends up behind something. */
.tms-grid-scroll.is-dense .tms-grouprow > td {
  padding: 6px 12px;
  line-height: 1.5;
  white-space: nowrap;
  overflow: hidden;
}

/* The priority ribbon.
   Two things moved it. The blanket "padding: 0 8px" above outranks the base rule that
   zeroes the stripe cell, so the ribbon was pushed 8px inward; and .tms-stripe-bar has a
   fixed height of 38px, which in a 22px row overflowed the cell — the cell is
   overflow:visible by design, so the bar simply hung out of its row.
   Both are pinned back here rather than left to inheritance. */
.tms-grid-scroll.is-dense .tms-grid-table tbody tr:not(.tms-grouprow) > td.tms-stripe {
  padding: 0;
  width: 3px;
}
.tms-grid-scroll.is-dense tbody tr:not(.tms-grouprow) .tms-stripe-bar { height: 22px; }

/* ============================================================================
   Card list — the grid's phone layout
   ========================================================================== */
.tms-cards { display: flex; flex-direction: column; gap: 8px; }

.tms-cards-group {
  margin: 10px 0 2px; font-size: 11px; font-weight: 700;
  letter-spacing: .075em; text-transform: uppercase; color: var(--tms-text-3);
}

.tms-card-row {
  background: var(--tms-surface); border: 1px solid var(--tms-border);
  border-radius: var(--tms-r); padding: 10px 12px;
  box-shadow: var(--tms-shadow-1); cursor: pointer;
}
.tms-card-row:active { background: var(--tms-surface-2); }

/* The urgent and flagged row treatments carry over as a left edge, which is the one
   part of the desktop stripe that still reads at this size. */
.tms-card-row.is-urgent  { border-left: 3px solid var(--tms-danger); }
.tms-card-row.is-flagged { border-left: 3px solid var(--tms-danger); }

.tms-tcard-top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.tms-tcard-id { font-size: 12px; font-weight: 700; }

.tms-tcard-subject {
  margin: 6px 0 4px; font-size: 14px; line-height: 1.35;
  /* Two lines, then clipped. A subject is a sentence, and one that runs to five lines
     turns a scannable list back into a wall of text. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden;
}

.tms-tcard-meta {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  font-size: 11.5px; color: var(--tms-text-3);
}
.tms-tcard-dot { opacity: .5; }
.tms-tcard-age { margin-left: auto; white-space: nowrap; }

/* Sort control, shown only in card mode. */
.tms-card-sort { width: auto; max-width: 150px; height: 38px; padding: 0 8px; font-size: 12.5px; }

/* ============================================================================
   PHONE AND TABLET — Phase 4: reports
   ============================================================================

   These stay wide and scroll. A month of days across, or eight measures per
   person, is what the data IS — a reflowed version would show less, not more.
   What they need is to scroll WITHOUT losing which row you are on, so the
   first column is pinned in every one of them.
   ========================================================================== */
@media (max-width: 1024px) {

  /* The label column stays put while the numbers scroll under it. Without this a
     report on a phone is a screen of figures with nothing to attach them to. */
  .tms-act-table tbody td:first-child,
  .tms-act-table thead th:first-child,
  .tms-perf-table tbody td:first-child,
  .tms-perf-table thead th:first-child {
    position: sticky; left: 0; z-index: 1;
    background: var(--tms-surface);
    /* A shadow rather than a border: it shows the column is floating over content
       that continues underneath, which a line does not. */
    box-shadow: 1px 0 0 var(--tms-border);
  }

  /* Header cells outrank body cells where they meet, or the corner flickers between
     the two as it scrolls. */
  .tms-act-table thead th:first-child,
  .tms-perf-table thead th:first-child,
  .tms-heat-table thead th.tms-heat-who { z-index: 3; }

  /* Group and median rows carry their own background, which a transparent sticky
     cell would let the scrolling content show through. */
  .tms-act-table tbody tr.is-open-row td:first-child { background: var(--tms-accent-tint); }
  .tms-perf-table tbody tr.is-median td:first-child { background: var(--tms-surface-2); }

  /* Names and subjects give up their width first — the figures are the point. */
  .tms-perf-sub, .tms-act-sub { max-width: 180px; }
}

@media (max-width: 720px) {
  /* Report rows are tappable and hold drill-through, so they need thumb-sized rows
     even though the report grids never enter card mode. */
  .tms-act-table tbody td, .tms-perf-table tbody td { padding: 10px 10px; }

  .tms-act-block h2 { font-size: 13px; }

  /* Stat tiles go two-up rather than one long column of single figures. */
  .tms-perf-cards { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .tms-perf-stat { padding: 10px; }
  .tms-perf-stat-v { font-size: 18px; }

  /* The work-mix rows lose their fixed label column, which at 120px left nothing
     for the bar. */
  .tms-perf-mix-row { grid-template-columns: 96px 1fr 34px; gap: 8px; font-size: 12px; }

  /* The heatmap keeps its pinned name column and simply scrolls; a month of days
     cannot be made to fit and should not pretend to. */
  .tms-heat-table th.tms-heat-who, .tms-heat-table td.tms-heat-who { min-width: 120px; }
}

@media (max-width: 720px) {
  /* KPI tiles two-up. auto-fit with a 190px floor gives ONE column at 390px, which turns
     four measures into four screens of scrolling — the point of a KPI row is seeing them
     together. Two-up keeps them comparable and still fits the numbers. */
  .tms-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; }
  .tms-kpi { padding: 10px 11px; }
}

/* ============================================================================
   PHONE — dashboard
   ========================================================================== */
@media (max-width: 720px) {

  /* Card headers hold a title AND a row of controls. On one line at 390px the title
     is squeezed to nothing and the controls spill past the card edge. Wrapping puts
     the controls on their own line, which is the only arrangement that fits. */
  .tms-card-h { flex-wrap: wrap; padding: 11px 12px; }
  .tms-card-h .tms-actions { margin-left: 0; width: 100%; }

  /* The organisation filter was sized for a desktop card header. */
  .tms-flow-org { width: 100%; }
  .tms-flow-tag { max-width: 100%; }

  /* Chart cards: the chart scrolls, so the card must not. */
  .tms-card-b { padding: 12px; }
  .tms-spark { min-width: 420px; }

  /* The eyebrow and the total repeat what the card title already says, and they are
     what pushes the header onto a third line. */
  .tms-card-h .tms-eyebrow { display: none; }

  /* Workload rows: a 104px name column left roughly 90px for the meter. */
  .tms-load-name { min-width: 0; flex: 1 1 auto; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .tms-load-row { gap: 8px; }

  /* Queue rows lose their right padding so the title gets the width back. */
  .tms-queue-row { padding-right: 8px; gap: 8px; }
}

/* The same trap, closed everywhere it can occur: a grid or flex child will not shrink
   below its content unless told it may. One wide chart or long word inside any card is
   otherwise enough to widen the page. */
.tms-dash-grid > *, .tms-kpis > *, .tms-perf-cards > *, .tms-create > *, .tms-detail > * {
  min-width: 0;
}

/* A page that scrolls sideways also breaks vertical scrolling on touch — the gesture
   picks the wrong axis. Nothing here is meant to be wider than the screen; the pieces
   that genuinely are scroll inside their own containers. */
@media (max-width: 1024px) {
  .tms-canvas { overflow-x: hidden; }
}

/* ============================================================================
   PHONE — dialogs that hold data
   ========================================================================== */
@media (max-width: 720px) {

  /* The list behind a chart bar. Five columns inside a sheet is four too many at
     390px: organisation and assignee are context, not identity, and dropping them
     leaves the ticket, its status and when it happened — which is what the bar was
     asked about. Both are still on the ticket itself, one tap away. */
  .tms-day-table th:nth-child(2), .tms-day-table td:nth-child(2),
  .tms-day-table th:nth-child(4), .tms-day-table td:nth-child(4) { display: none; }

  /* Anything else in a sheet scrolls inside its own box rather than widening the
     sheet — a dialog wider than the screen cannot be scrolled back into view. */
  .tms-modal-b .tms-grid-scroll { max-width: 100%; overflow-x: auto; }
  .tms-modal-b table { min-width: 0; }

  /* Priority manager: ten slots at five across is 60px each. Three across keeps the
     ticket number legible, which is the only thing that identifies the slot. */
  .tms-pm-slots { grid-template-columns: repeat(3, minmax(0, 1fr)) !important; }

  /* Ticket history: the trail lanes are a fixed label column plus a bar. */
  .tms-trail-row { grid-template-columns: minmax(0, 1fr) 26px 52px; }
  .tms-trail-lane { grid-template-columns: minmax(0, 78px) minmax(0, 1fr); }

  /* Feedback dialog: name and stars side by side leaves nothing for either. */
  .tms-fb-row { flex-wrap: wrap; gap: 6px; }
  .tms-fb-who { flex: 1 1 100%; }
}

@media (max-width: 720px) {
  /* Two-up form rows and stat pairs go single-column. "1fr 1fr" halves an already
     narrow screen and leaves neither field usable. */
  .tms-form-row, .tms-two { grid-template-columns: minmax(0, 1fr); }

  /* Age slot rows are four fixed columns wide; shrink the slot rather than let the
     row set the width of whatever contains it. */
  .tms-agechips { --tms-ageslot-w: 30px; }
  .tms-agemono { --tms-agemono-w: 28px; }
}

/* Save changes, the moment there is something to save. One pulse to catch the eye as it
   turns primary, then still — repetition would turn a nudge into an alarm. */
.tms-save-live { animation: tms-save-pop .35s var(--tms-ease); }

@keyframes tms-save-pop {
  0%   { transform: scale(1); }
  45%  { transform: scale(1.04); }
  100% { transform: scale(1); }
}

@media (prefers-reduced-motion: reduce) {
  .tms-save-live { animation: none; }
}

/* Jump-to-ticket box — leftmost in the ticket toolbar.
   Quiet at rest so it never competes with Search; accent ring once it holds a number. */
.tms-jump {
  display: inline-flex; align-items: center; gap: 2px; flex: none;
  height: var(--tms-h-control, 34px); padding: 0 8px;
  background: var(--tms-surface); border: 1px solid var(--tms-border);
  border-radius: var(--tms-r); transition: border-color var(--tms-fast), box-shadow var(--tms-fast);
}
.tms-jump:focus-within { border-color: var(--tms-accent); box-shadow: 0 0 0 3px var(--tms-accent-tint); }
.tms-jump-hash { font-family: var(--tms-mono, monospace); color: var(--tms-text-3, var(--tms-text-2)); font-weight: 600; }
.tms-jump:focus-within .tms-jump-hash { color: var(--tms-accent); }

.tms-jump-in {
  width: 76px; border: 0; outline: 0; background: transparent; color: var(--tms-text);
  font-family: var(--tms-mono, monospace); font-size: 13px; padding: 0 2px;
}
.tms-jump-in::placeholder { font-family: var(--tms-sans, inherit); color: var(--tms-text-3, var(--tms-text-2)); }

/* Appears only once digits are present — a permanent button on an empty box is a
   control that does nothing. */
.tms-jump-go {
  display: inline-flex; align-items: center; gap: 2px; flex: none;
  border: 0; cursor: pointer; border-radius: 99px; padding: 2px 8px 2px 6px;
  font-size: 11px; line-height: 1.4;
  background: var(--tms-accent-tint); color: var(--tms-accent-ink-2, var(--tms-accent));
}
.tms-jump-go:hover { background: var(--tms-accent); color: var(--tms-on-accent); }
.tms-jump-go .tms-i { width: 13px; height: 13px; transform: rotate(-90deg); }

/* Not found / not accessible. */
.tms-jump.is-bad { border-color: var(--tms-danger); box-shadow: 0 0 0 3px var(--tms-danger-tint); }
.tms-jump.is-bad .tms-jump-hash, .tms-jump.is-bad .tms-jump-in { color: var(--tms-danger); }

@media (max-width: 720px) { .tms-jump-in { width: 62px; } }

/* Organization with its client tier.
   T1 is deliberately the only loud one: eleven of forty-six clients, and the rows worth
   spotting without reading. T2 and T3 stay quiet — a chip that shouted on every row would
   be no signal at all. */
.tms-org { display: inline-flex; align-items: center; gap: 6px; min-width: 0; }
.tms-org-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Fixed width, not padding: the badge leads the row now, so anything that varied its
   width would push every name to a different x and lose the alignment that putting it
   first was for. Wide enough for T1/T2/T3 with no wrapping. */
.tms-tier {
  flex: none; font-size: 10px; line-height: 1.5; letter-spacing: .04em;
  width: 24px; text-align: center; box-sizing: content-box;
  padding: 0 4px; border-radius: 4px; font-family: var(--tms-mono, monospace);
}

/* The reserved slot for an organization with no tier — occupies the space, draws nothing.
   Its border is transparent rather than absent so it measures the same as a real badge. */
.tms-tier.is-blank { background: transparent; border: 1px solid transparent; }

/* T1 — prominent. */
.tms-tier.is-t1 {
  background: var(--tms-warn-tint); color: var(--tms-warn);
  border: 1px solid var(--tms-warn-line); font-weight: 600;
}

/* T2 — dim: present for anyone who looks, invisible to anyone scanning. */
.tms-tier.is-t2 {
  background: transparent; color: var(--tms-text-3, var(--tms-text-2));
  border: 1px solid var(--tms-border); opacity: .75;
}

/* T3 — quieter still. */
.tms-tier.is-t3 {
  background: transparent; color: var(--tms-text-3, var(--tms-text-2));
  border: 1px dashed var(--tms-border); opacity: .6;
}

/* Ticket type — the same colours the Insights charts use, so the grid and the reports
   tell one story: red defect, amber operational work, purple build, dashed grey for a
   ticket nobody classified.

   Tinted with a hairline border rather than a solid block: the row already carries a
   solid status chip and a solid person chip, and a third solid block fights both. */
.tms-type {
  display: inline-block; font-size: 11px; line-height: 1.6;
  padding: 1px 9px; border-radius: 6px; white-space: nowrap;
}
/* One width for the whole set. Unlike the statuses — which run from "QA" to "Awaiting
   Confirmation" and would strand a short label in a huge box — these four sit between
   three and eleven characters, so equalising them costs almost no wasted area and gives
   the column a single clean edge. */
/* Icon only. The column was 118px of text restating what the colour already said, while
   Subject — which has no fixed width and takes whatever is left — was truncating before a
   single useful word. This hands roughly 74px straight to Subject. Four values is a small
   enough vocabulary for shape to carry it; the word survives in the title. */
.tms-type.is-icon {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 22px; padding: 0; min-width: 0;
}
.tms-type.is-icon .tms-i { width: 14px; height: 14px; }

.tms-type.is-bug  { background: var(--tms-danger-tint);   color: var(--tms-danger);   border: 1px solid var(--tms-danger-line); font-weight: 600; }
.tms-type.is-act  { background: var(--tms-warn-tint);     color: var(--tms-warn);     border: 1px solid var(--tms-warn-line); }
.tms-type.is-dev  { background: var(--tms-progress-tint); color: var(--tms-progress); border: 1px solid var(--tms-progress-line); }

/* Untagged and unassigned are both ABSENCES, so they share one look: outlined, dashed,
   quiet. A filled chip would make a gap look like a value. */
.tms-type.is-none { background: transparent; color: var(--tms-text-2); border: 1px dashed var(--tms-border-strong); }

/* Age as a heat bar plus the largest unit. The bar carries the scan — long and red means
   stale — and the number carries the detail. Everything below the largest unit lives in
   the tooltip, where it belongs. */
.tms-agebar { display: inline-flex; align-items: center; gap: 7px; white-space: nowrap; }

.tms-agebar-t { height: 4px; border-radius: 2px; flex: none; background: var(--c); }

.tms-agebar-v {
  font-family: var(--tms-font-mono); font-size: 11.5px; font-weight: 600;
  font-variant-numeric: tabular-nums; color: var(--c);
  min-width: 34px;                       /* digits land in the same place row to row */
}

/* Same four hues the chips used, so nobody has to relearn what red means here. */
.tms-agebar-month  { --c: #E24B4A; }
.tms-agebar-day    { --c: #D98324; }
.tms-agebar-hour   { --c: #B59A16; }
.tms-agebar-minute { --c: #4E9A3E; }

[data-theme="dark"] .tms-agebar-month  { --c: #F98080; }
[data-theme="dark"] .tms-agebar-day    { --c: #F9B871; }
[data-theme="dark"] .tms-agebar-hour   { --c: #E3CD5C; }
[data-theme="dark"] .tms-agebar-minute { --c: #8FD070; }


/* ============================================================================
   REMINDERS — the band above the ticket list, and the ask on a ticket page.

   Colour tracks how long an answer has been outstanding, never how many times
   support asked. If volume drove the treatment, asking repeatedly would be the
   winning move and every ticket would carry five reminders within a month.
   ========================================================================== */
.tms-rmd-band {
  border: 1px solid var(--tms-warn-line); border-radius: var(--tms-r);
  background: var(--tms-surface); overflow: hidden; margin-bottom: 12px;
}
.tms-rmd-band.is-late { border-color: var(--tms-danger-line); }

/* The header wash is see-through — the series colour mixed with transparency rather than
   the fixed --tms-*-tint swatch — so with a page background on it lets the background
   through like the cards around it. The amounts match the old swatches on a plain page,
   so without a background nothing looks different. */
:root { --tms-rmd-wash-warn: 12%; --tms-rmd-wash-danger: 14%; }
:root[data-theme="light"] { --tms-rmd-wash-warn: 10%; --tms-rmd-wash-danger: 11%; }

.tms-rmd-h {
  display: flex; align-items: center; gap: 8px; padding: 8px 12px;
  background: color-mix(in srgb, var(--tms-warn) var(--tms-rmd-wash-warn), transparent);
  border-bottom: 1px solid var(--tms-warn-line);
  color: var(--tms-warn);
}
.tms-rmd-band.is-late .tms-rmd-h {
  background: color-mix(in srgb, var(--tms-danger) var(--tms-rmd-wash-danger), transparent);
  border-bottom-color: var(--tms-danger-line);
  color: var(--tms-danger);
}

.tms-rmd-t { font-size: var(--tms-fs-body); font-weight: 600; }
.tms-rmd-n {
  font-family: var(--tms-font-mono); font-size: var(--tms-fs-micro);
  border: 1px solid currentColor; border-radius: var(--tms-r-sm); padding: 0 6px;
}
.tms-rmd-hint { margin-left: auto; font-size: var(--tms-fs-cap); color: var(--tms-text-3); }

/* 3px stripe per row rather than a tinted fill: the band already carries the
   colour, and filling each row too would leave nothing quiet to read against. */
.tms-rmd { padding: 9px 12px; border-left: 3px solid var(--tms-border-strong); }
.tms-rmd + .tms-rmd { border-top: 1px solid var(--tms-border); }
.tms-rmd.is-new  { border-left-color: var(--tms-ok); }
.tms-rmd.is-due  { border-left-color: var(--tms-warn); }
.tms-rmd.is-late { border-left-color: var(--tms-danger); }

.tms-rmd-r { display: flex; align-items: center; gap: 10px; min-width: 0; }
.tms-rmd-id { font-family: var(--tms-font-mono); font-size: var(--tms-fs-cap); color: var(--tms-text-2); }
.tms-rmd-s {
  flex: 1; min-width: 0; font-size: var(--tms-fs-body); color: var(--tms-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tms-rmd-age {
  flex: none; font-family: var(--tms-font-mono); font-size: var(--tms-fs-micro);
  border-radius: var(--tms-r-sm); padding: 1px 7px; white-space: nowrap;
  border: 1px solid var(--tms-border-strong); color: var(--tms-text-2);
}
.tms-rmd.is-due .tms-rmd-age {
  color: var(--tms-warn); background: var(--tms-warn-tint); border-color: var(--tms-warn-line);
}
.tms-rmd.is-late .tms-rmd-age {
  color: var(--tms-danger); background: var(--tms-danger-tint); border-color: var(--tms-danger-line);
}

.tms-rmd-m { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; margin-top: 2px; }
.tms-rmd-who { font-size: var(--tms-fs-cap); color: var(--tms-text-3); }
.tms-rmd-why { font-size: var(--tms-fs-cap); color: var(--tms-text-2); font-style: italic; }

.tms-rmd-a { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 8px; }
.tms-rmd-eta { display: inline-flex; align-items: center; gap: 6px; font-size: var(--tms-fs-cap); color: var(--tms-text-2); }
.tms-rmd-date { width: 148px; height: 28px; padding: 2px 8px; font-size: var(--tms-fs-cap); }
.tms-rmd-note { flex: 1; min-width: 180px; height: 28px; padding: 2px 8px; font-size: var(--tms-fs-cap); }

/* The answered history on a ticket page — quiet, because it is a record rather
   than something anyone still has to act on. */
.tms-rmd-past { display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap; padding: 6px 0; font-size: var(--tms-fs-cap); }
.tms-rmd-past + .tms-rmd-past { border-top: 1px solid var(--tms-border); }
.tms-rmd-ans {
  font-size: var(--tms-fs-micro); border-radius: var(--tms-r-sm); padding: 0 7px;
  border: 1px solid var(--tms-border-strong); color: var(--tms-text-2);
}
.tms-rmd-ans.is-onit    { color: var(--tms-ok);     background: var(--tms-ok-tint);     border-color: var(--tms-ok-line); }
.tms-rmd-ans.is-blocked { color: var(--tms-warn);   background: var(--tms-warn-tint);   border-color: var(--tms-warn-line); }
.tms-rmd-ans.is-no      { color: var(--tms-danger); background: var(--tms-danger-tint); border-color: var(--tms-danger-line); }

/* The open reminder, mirrored into the ticket page's sticky header so it survives a long
   thread. Icon plus one number: the bar already carries number, status, organisation and
   title, and this must not push the title out of a bar that exists to keep it visible. */
.tms-t-stuck-rmd {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  height: 22px; padding: 0 8px; cursor: pointer;
  border-radius: var(--tms-r-sm); border: 1px solid var(--tms-border-strong);
  background: transparent; color: var(--tms-text-2);
  font-family: var(--tms-font-mono); font-size: var(--tms-fs-micro);
  font-variant-numeric: tabular-nums;
}
.tms-t-stuck-rmd .tms-i { width: 12px; height: 12px; }

.tms-t-stuck-rmd.is-due {
  color: var(--tms-warn); background: var(--tms-warn-tint); border-color: var(--tms-warn-line);
}
.tms-t-stuck-rmd.is-late {
  color: var(--tms-danger); background: var(--tms-danger-tint); border-color: var(--tms-danger-line);
}
.tms-t-stuck-rmd:hover { border-color: currentColor; }

/* ============================================================================
   CLIENT NUDGE — the customer's "I need an update", and support's reply.

   Two bands rather than the reminder's three: the client-facing window is four
   hours, not eight, because a client waiting is worse than a colleague waiting.
   ========================================================================== */
.tms-nudge { padding: 9px 0 0; border-left: 3px solid var(--tms-warn); padding-left: 9px; }
.tms-nudge.is-late { border-left-color: var(--tms-danger); }

.tms-nudge-why {
  font-size: var(--tms-fs-micro); border-radius: var(--tms-r-sm); padding: 1px 8px;
  color: var(--tms-warn); background: var(--tms-warn-tint); border: 1px solid var(--tms-warn-line);
}
.tms-nudge.is-late .tms-nudge-why {
  color: var(--tms-danger); background: var(--tms-danger-tint); border-color: var(--tms-danger-line);
}

.tms-nudge-ask { display: flex; flex-direction: column; gap: 10px; }
.tms-nudge-chips { display: flex; flex-wrap: wrap; gap: 6px; }

/* One click, not a text box. A required free-text field produces "please check",
   which is not something support can sort a queue by. */
.tms-nudge-chip {
  font-size: var(--tms-fs-cap); padding: 3px 11px; cursor: pointer;
  border-radius: var(--tms-r-sm); border: 1px solid var(--tms-border-strong);
  background: transparent; color: var(--tms-text-2);
}
.tms-nudge-chip:hover { border-color: var(--tms-accent-line); color: var(--tms-text); }
.tms-nudge-chip.is-on {
  color: var(--tms-accent-ink-2); background: var(--tms-accent-tint);
  border-color: var(--tms-accent-line); font-weight: 600;
}
[data-theme="dark"] .tms-nudge-chip.is-on { color: var(--tms-accent); }

/* Rows in the client-waiting band are links to the ticket, so they need the row
   treatment without the anchor's underline or inherited link colour. */
.tms-nudge-row { display: block; text-decoration: none; color: inherit; }
.tms-nudge-row:hover { background: var(--tms-surface-2); }
.tms-nudge-org { flex: none; font-size: var(--tms-fs-cap); color: var(--tms-text); }

/* Who owes the answer. Reads as a name rather than a status chip — it identifies a
   person, and dressing it as a badge would make it compete with the age pill that
   actually needs the attention. */
.tms-rmd-owner {
  font-size: var(--tms-fs-cap); font-weight: 600; color: var(--tms-text);
  margin-right: 6px;
}

/* Nobody holds it, so nobody can answer it — that is worse than a late reminder,
   not a blank. */
.tms-rmd-owner.is-none { color: var(--tms-danger); font-weight: 600; }

/* ============================================================================
   GROUPED TICKETS — one client request, one ticket per discipline.
   ========================================================================== */
.tms-grp-prog {
  font-family: var(--tms-font-mono); font-size: var(--tms-fs-micro);
  border-radius: var(--tms-r-sm); padding: 1px 8px;
  color: var(--tms-warn); background: var(--tms-warn-tint); border: 1px solid var(--tms-warn-line);
}
.tms-grp-prog.is-done {
  color: var(--tms-ok); background: var(--tms-ok-tint); border-color: var(--tms-ok-line);
}

.tms-grp-kid {
  display: flex; align-items: center; gap: 10px; padding: 8px 0;
  text-decoration: none; color: inherit; min-width: 0;
}
.tms-grp-kid + .tms-grp-kid { border-top: 1px solid var(--tms-border); }
.tms-grp-kid:hover .tms-grp-sub { color: var(--tms-accent); }

/* Finished parts stay legible but stop competing — the open ones are the question. */
.tms-grp-kid.is-done { opacity: .62; }

.tms-grp-id { font-family: var(--tms-font-mono); font-size: var(--tms-fs-cap); color: var(--tms-text-2); }

.tms-grp-role-chip {
  flex: none; min-width: 68px; text-align: center;
  font-size: var(--tms-fs-micro); border-radius: var(--tms-r-sm); padding: 1px 8px;
  color: var(--tms-text); background: var(--tms-surface-2); border: 1px solid var(--tms-border-strong);
}

.tms-grp-sub {
  flex: 1; min-width: 0; font-size: var(--tms-fs-data); color: var(--tms-text);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tms-grp-who-name { flex: none; font-size: var(--tms-fs-cap); color: var(--tms-text-2); }

.tms-grp-new { display: flex; flex-direction: column; gap: 8px; margin-bottom: 10px; }
.tms-grp-row { display: flex; align-items: center; gap: 8px; }

/* Both controls keep the standard 40px .tms-input height rather than a shrunken one.
   The role box was forced to 30px while the picker beside it renders a full-height
   control, so the two sat at different heights on the same row and neither lined up
   with the remove button. Matching the platform height is simpler than tuning two. */
.tms-grp-role { width: 160px; flex: none; }

/* A container now, not a control: it wraps TmsAssigneePicker, which draws its own input.
   Giving it a height and padding of its own boxed the picker inside a second, shorter
   frame. */
.tms-grp-who { flex: 1; min-width: 0; }

/* The shared person chip's "nobody" state. Dashed and quiet — the same language the type
   column uses for untagged, because both are absences rather than values. */
.tms-namechip-none {
  display: inline-block; font-size: 11px; line-height: 1.6; padding: 1px 9px;
  border-radius: 6px; white-space: nowrap;
  background: transparent; color: var(--tms-text-2);
  border: 1px dashed var(--tms-border-strong);
}

/* Several chips in a row — the reminder band and the group card both list people
   inline rather than in a column, so they need a gap the grid's column does not. */
.tms-chiprow { display: inline-flex; align-items: center; gap: 5px; flex-wrap: wrap; }

/* Link back to the client's ticket, shown only on a child. */
.tms-grp-up {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--tms-fs-cap); color: var(--tms-text-2); text-decoration: none;
  border: 1px solid var(--tms-border-strong); border-radius: var(--tms-r-sm);
  padding: 2px 9px;
}
.tms-grp-up:hover { color: var(--tms-accent); border-color: var(--tms-accent-line); }
.tms-grp-up .tms-i { width: 12px; height: 12px; }

/* The parent listed among the parts. Marked out with a left rule rather than a colour so
   it reads as the row the others hang from, without competing with their statuses. */
.tms-grp-kid.is-parent {
  border-left: 2px solid var(--tms-accent-line);
  padding-left: 9px; margin-bottom: 2px;
}
.tms-grp-kid.is-parent .tms-grp-role-chip {
  color: var(--tms-accent-ink-2); background: var(--tms-accent-tint);
  border-color: var(--tms-accent-line);
}
[data-theme="dark"] .tms-grp-kid.is-parent .tms-grp-role-chip { color: var(--tms-accent); }

/* Undo actions on your own recent comment. Quiet by default — they sit under every comment
   you just wrote, and a row of loud buttons there would compete with the conversation. */
.tms-msg-act { display: flex; align-items: center; gap: 6px; margin-top: 6px; }

.tms-msg-btn {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--tms-fs-cap); padding: 2px 9px; cursor: pointer;
  border-radius: var(--tms-r-sm); border: 1px solid var(--tms-border);
  background: transparent; color: var(--tms-text-3);
}
.tms-msg-btn:hover { color: var(--tms-text); border-color: var(--tms-border-strong); }
.tms-msg-btn:disabled { opacity: .5; cursor: default; }
.tms-msg-btn .tms-i { width: 12px; height: 12px; }

/* Delete is the one that cannot be taken back, so it is the one that reads as a risk. */
.tms-msg-btn.is-danger:hover {
  color: var(--tms-danger); border-color: var(--tms-danger-line); background: var(--tms-danger-tint);
}

/* Age in the bands: a bar and a number, not a second pill.
   Status is a state and age is a magnitude — two identically shaped chips made the row
   read as two equal badges, and neither won. The bar also gives a column of rows a ragged
   edge that can be read in one pass, which a column of text pills cannot. Same idiom as
   the grid's assignee-age column, so elapsed time looks the same across the portal. */
.tms-rmd-agebar {
  display: inline-flex; align-items: center; justify-content: flex-end; gap: 7px;
  flex: none; min-width: 74px;
}

.tms-rmd-agebar i {
  display: block; height: 4px; border-radius: 2px; flex: none;
  background: var(--tms-border-strong);
}

.tms-rmd-agebar b {
  font-family: var(--tms-font-mono); font-size: 11.5px; font-weight: 600;
  font-variant-numeric: tabular-nums; color: var(--tms-text-2);
  min-width: 30px; text-align: right;
}

/* Colour comes from the row's own heat class, so the bar, the number and the left stripe
   always agree about how bad it is. */
.tms-rmd.is-new  .tms-rmd-agebar i, .tms-nudge-row.is-new  .tms-rmd-agebar i { background: var(--tms-ok); }
.tms-rmd.is-due  .tms-rmd-agebar i, .tms-nudge-row.is-due  .tms-rmd-agebar i { background: var(--tms-warn); }
.tms-rmd.is-late .tms-rmd-agebar i, .tms-nudge-row.is-late .tms-rmd-agebar i { background: var(--tms-danger); }

.tms-rmd.is-new  .tms-rmd-agebar b, .tms-nudge-row.is-new  .tms-rmd-agebar b { color: var(--tms-ok); }
.tms-rmd.is-due  .tms-rmd-agebar b, .tms-nudge-row.is-due  .tms-rmd-agebar b { color: var(--tms-warn); }
.tms-rmd.is-late .tms-rmd-agebar b, .tms-nudge-row.is-late .tms-rmd-agebar b { color: var(--tms-danger); }

/* The client's reason drops its border. With the age no longer a pill, three equally
   weighted chips became two plus a stray; as tinted text it sits behind the status and
   ahead of nothing, which is the order it deserves. */
.tms-nudge-why {
  background: transparent; border: none; padding: 0;
  font-size: var(--tms-fs-cap); font-weight: 600;
}

/* ============================================================================
   DAY SHEET — one day's work per person, replacing a hand-typed standup table.
   ========================================================================== */
.tms-ds-head, .tms-ds-row {
  display: grid; grid-template-columns: 130px minmax(0, 1fr) 82px 150px;
  gap: 10px; align-items: center; padding: 8px 14px;
}

.tms-ds-head {
  background: var(--tms-surface-2); border-bottom: 1px solid var(--tms-border);
  font-size: var(--tms-fs-micro); letter-spacing: .08em; text-transform: uppercase;
  color: var(--tms-text-3);
}

.tms-ds-row { text-decoration: none; color: inherit; border-bottom: 1px solid var(--tms-border); }
.tms-ds-row:hover { background: var(--tms-surface-2); }
.tms-ds-row:hover .tms-ds-what { color: var(--tms-accent); }

.tms-ds-org  { font-size: var(--tms-fs-cap); color: var(--tms-text); }
.tms-ds-what { font-size: var(--tms-fs-data); color: var(--tms-text-2);
               overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tms-ds-id   { font-family: var(--tms-font-mono); font-size: var(--tms-fs-cap); color: var(--tms-text-3); }

/* The band that separates finished work from work that merely moved. Colour is the only
   thing distinguishing them, so it carries the same meaning as everywhere else: green
   done, amber in flight, grey talked about. */
.tms-ds-band {
  display: flex; align-items: center; gap: 7px;
  padding: 6px 14px; font-size: var(--tms-fs-cap);
  background: var(--tms-surface); border-bottom: 1px solid var(--tms-border);
}
.tms-ds-band i { width: 6px; height: 6px; border-radius: 2px; flex: none; }
.tms-ds-count { color: var(--tms-text-3); }

.tms-ds-band.is-done   { color: var(--tms-ok); }
.tms-ds-band.is-done i { background: var(--tms-ok); }
.tms-ds-band.is-moved  { color: var(--tms-warn); }
.tms-ds-band.is-moved i { background: var(--tms-warn); }
.tms-ds-band.is-talked  { color: var(--tms-text-3); }
.tms-ds-band.is-talked i { background: var(--tms-border-strong); }

@media (max-width: 720px) {
  .tms-ds-head { display: none; }
  .tms-ds-row { grid-template-columns: 1fr auto; row-gap: 4px; }
  .tms-ds-what { grid-column: 1 / -1; white-space: normal; }
}

/* ============================================================================
   PRINT — what Save as PDF produces.

   The browser's own print-to-PDF rather than a PDF library: it prints exactly what is on
   screen, needs no dependency inside a strict CSP, and every browser already offers
   Save as PDF as a destination. A generated PDF would be a second renderer to keep in
   step with this one, and the two would drift.
   ========================================================================== */
@media print {
  /* Chrome that belongs to the app, not to the report. */
  .tms-rail, .tms-topbar, .tms-actions, .tms-toast-host,
  .tms-rmd-band, .tms-jump, .tms-notifs { display: none !important; }

  /* The viewer's dark theme would print as a solid black page and drain a cartridge, so
     printing is always on white regardless of what is on screen. */
  :root, :root[data-theme="dark"] {
    --tms-bg: #FFFFFF; --tms-surface: #FFFFFF; --tms-surface-2: #FFFFFF;
    --tms-text: #000000; --tms-text-2: #222222; --tms-text-3: #444444;
    --tms-border: #CCCCCC; --tms-border-strong: #999999;
  }

  body, .tms-page, .tms-page-full { background: #FFFFFF !important; margin: 0; padding: 0; }

  .tms-card {
    box-shadow: none !important; border: 1px solid #CCCCCC !important;
    /* A person's sheet should not be split across two pages when it can be helped. */
    break-inside: avoid-page;
  }

  /* Status chips keep their meaning on paper: the tint prints as near-white on most
     printers, so the border and text carry it instead. */
  .tms-status-chip { border-color: #999999 !important; color: #000000 !important; }

  .tms-ds-row { break-inside: avoid; }

  a { text-decoration: none !important; color: inherit !important; }

  @page { margin: 14mm; }
}


/* =====================================================================================
   Chat import and chat findings.

   One colour per KIND of finding, taken from the existing semantic tokens so both themes
   follow without a single literal: a bug is danger, a request is info, a promise one of
   us made is the violet the portal already uses for work in progress.
   ===================================================================================== */

/* ---- import page ---- */
.tms-ci-step {
  display: inline-grid; place-items: center;
  width: 20px; height: 20px; margin-right: 6px;
  border-radius: 50%; font-size: 11px; font-weight: 700;
  background: var(--tms-accent-tint); color: var(--tms-accent);
  border: 1px solid var(--tms-accent-line);
  vertical-align: 1px;
}

.tms-ci-upload { display: grid; grid-template-columns: minmax(200px, 280px) 1fr; gap: 14px 18px; align-items: start; }
.tms-ci-upload .tms-ci-go { grid-column: 1 / -1; }
@media (max-width: 720px) { .tms-ci-upload { grid-template-columns: 1fr; } }

.tms-ci-go { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 12px; }
.tms-ci-go .tms-btn { display: inline-flex; align-items: center; gap: 8px; }

.tms-ci-stats { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
@media (max-width: 760px) { .tms-ci-stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.tms-ci-stat {
  display: flex; flex-direction: column; gap: 3px; min-width: 0;
  background: var(--tms-surface-2); border: 1px solid var(--tms-border);
  border-radius: 9px; padding: 10px 12px;
}
.tms-ci-stat .k {
  font-size: 10.5px; letter-spacing: .08em; text-transform: uppercase; color: var(--tms-text-3);
}
.tms-ci-stat .v {
  font-size: 19px; font-weight: 650; color: var(--tms-text);
  font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.tms-ci-stat .v small { font-size: 12px; font-weight: 500; color: var(--tms-text-3); }

.tms-ci-people { display: flex; flex-direction: column; gap: 6px; }

.tms-ci-person {
  display: grid; grid-template-columns: minmax(0, 1.2fr) auto minmax(0, 1fr);
  align-items: center; gap: 12px;
  border: 1px solid var(--tms-border); border-radius: 9px;
  background: var(--tms-surface-2); padding: 9px 12px;
}
.tms-ci-person.is-open { border-color: var(--tms-warn-line); background: var(--tms-warn-tint); }
@media (max-width: 760px) { .tms-ci-person { grid-template-columns: 1fr; } }

.tms-ci-who { display: flex; flex-direction: column; min-width: 0; }
.tms-ci-raw { font-weight: 600; color: var(--tms-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tms-ci-meta { font-size: 11.5px; color: var(--tms-text-3); }
.tms-ci-link { min-width: 0; }

.tms-ci-bar {
  height: 8px; border-radius: 999px; overflow: hidden;
  background: var(--tms-surface-3); border: 1px solid var(--tms-border);
}
.tms-ci-bar i {
  display: block; height: 100%; background: var(--tms-accent);
  border-radius: 999px; transition: width .4s ease;
}
.tms-ci-progress { margin: 8px 0 0; font-size: 12.5px; color: var(--tms-text-2); font-variant-numeric: tabular-nums; }

.tms-ci-row {
  display: grid; grid-template-columns: minmax(0, 1.2fr) minmax(0, 1.3fr) auto auto minmax(0, 1fr);
  align-items: center; gap: 12px;
  padding: 10px 16px; border-top: 1px solid var(--tms-border);
  color: var(--tms-text); text-decoration: none;
}
.tms-ci-row:first-child { border-top: 0; }
.tms-ci-row:hover { background: var(--tms-surface-2); }
.tms-ci-row.is-current { background: var(--tms-accent-tint); }
.tms-ci-row-org { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tms-ci-row-range, .tms-ci-row-n, .tms-ci-row-when { font-size: 12px; color: var(--tms-text-3); font-variant-numeric: tabular-nums; }
.tms-ci-row-when { text-align: right; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 860px) {
  .tms-ci-row { grid-template-columns: 1fr auto; }
  .tms-ci-row-range, .tms-ci-row-n, .tms-ci-row-when { display: none; }
}

/* ---- findings page ---- */
.tms-cf-band { display: flex; flex-direction: column; gap: 10px; margin-bottom: 22px; }

.tms-cf-band-h { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; }
.tms-cf-band-h i { width: 3px; height: 16px; border-radius: 2px; background: var(--band); flex: none; }
.tms-cf-band-h h2 { font-size: 15px; font-weight: 650; margin: 0; color: var(--tms-text); }
.tms-cf-count {
  font-size: 11px; font-variant-numeric: tabular-nums; color: var(--tms-text-2);
  border: 1px solid var(--tms-border); border-radius: 999px; padding: 0 8px;
}
.tms-cf-band-hint { margin-left: auto; font-size: 12px; color: var(--tms-text-3); }

.tms-cf-band.is-new      { --band: var(--tms-danger); }
.tms-cf-band.is-tracked  { --band: var(--tms-ok); }
.tms-cf-band.is-promised { --band: var(--tms-progress); }

.tms-cf-list { display: flex; flex-direction: column; gap: 10px; }

.tms-cf {
  --kind: var(--tms-text-3);
  --kind-tint: var(--tms-surface-2);
  --kind-line: var(--tms-border);

  position: relative;
  display: flex; flex-direction: column; gap: 10px;
  background: var(--tms-surface); border: 1px solid var(--tms-border);
  border-radius: 11px; padding: 14px 16px 14px 18px; overflow: hidden;
}
/* The kind, as a stripe down the leading edge — readable at a glance down a long list. */
.tms-cf::before {
  content: ""; position: absolute; left: 0; top: 0; bottom: 0; width: 3px; background: var(--kind);
}
.tms-cf.is-bug        { --kind: var(--tms-danger);   --kind-tint: var(--tms-danger-tint);   --kind-line: var(--tms-danger-line); }
.tms-cf.is-request    { --kind: var(--tms-info);     --kind-tint: var(--tms-info-tint);     --kind-line: var(--tms-info-line); }
.tms-cf.is-question   { --kind: var(--tms-accent);   --kind-tint: var(--tms-accent-tint);   --kind-line: var(--tms-accent-line); }
.tms-cf.is-commitment { --kind: var(--tms-progress); --kind-tint: var(--tms-progress-tint); --kind-line: var(--tms-progress-line); }
.tms-cf.is-complaint  { --kind: var(--tms-warn);     --kind-tint: var(--tms-warn-tint);     --kind-line: var(--tms-warn-line); }

.tms-cf-top { display: flex; align-items: flex-start; gap: 10px; }
.tms-cf-top h3 {
  flex: 1 1 auto; min-width: 0; margin: 0;
  font-size: 14.5px; font-weight: 600; line-height: 1.35; color: var(--tms-text);
  text-wrap: balance;
}
.tms-cf-kind {
  flex: none; font-size: 10.5px; font-weight: 600; letter-spacing: .07em; text-transform: uppercase;
  color: var(--kind); background: var(--kind-tint); border: 1px solid var(--kind-line);
  border-radius: 5px; padding: 2px 7px;
}

.tms-cf-summary { margin: 0; font-size: 13px; color: var(--tms-text-2); line-height: 1.5; }

/* The client's own words. Staff lines are set back so the request stands out. */
.tms-cf-evidence {
  display: flex; flex-direction: column; gap: 6px;
  border-left: 2px solid var(--kind-line); padding-left: 12px;
}
.tms-cf-quote { display: flex; flex-direction: column; gap: 1px; min-width: 0; }
.tms-cf-quote-h { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; font-size: 12px; }
.tms-cf-quote-h b { color: var(--tms-text); font-weight: 600; }
.tms-cf-quote-h span { color: var(--tms-text-3); font-variant-numeric: tabular-nums; }
.tms-cf-quote-b {
  font-size: 13px; color: var(--tms-text-2); line-height: 1.5;
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.tms-cf-quote.is-staff .tms-cf-quote-h b { color: var(--tms-accent); }
.tms-cf-quote.is-staff .tms-cf-quote-b { color: var(--tms-text-3); }

.tms-cf-meta { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.tms-cf-chip {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 11.5px; color: var(--tms-text-3);
  border: 1px solid var(--tms-border); border-radius: 6px; padding: 2px 8px;
  background: var(--tms-surface-2);
}
.tms-cf-chip b { color: var(--tms-text-2); font-weight: 600; }
.tms-cf-chip.is-link { text-decoration: none; }
.tms-cf-chip.is-link:hover { border-color: var(--tms-accent-line); }
.tms-cf-chip.is-late { color: var(--tms-danger); border-color: var(--tms-danger-line); background: var(--tms-danger-tint); }
.tms-cf-chip.is-late b { color: var(--tms-danger); }

.tms-cf-actions, .tms-cf-form { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.tms-cf-form .tms-cf-title  { flex: 1 1 320px; min-width: 0; }
.tms-cf-form .tms-cf-reason { flex: 1 1 260px; min-width: 0; }
.tms-cf-form .tms-cf-ticketno { width: 150px; }
.tms-cf-assignee { flex: 0 1 220px; min-width: 160px; }

.tms-cf-outcome {
  display: flex; flex-wrap: wrap; gap: 6px 14px; align-items: baseline;
  font-size: 12.5px; color: var(--tms-text-2);
  border-top: 1px solid var(--tms-border); padding-top: 9px;
}
.tms-cf-outcome a { color: var(--tms-accent); }
.tms-cf-by { color: var(--tms-text-3); font-size: 11.5px; }

@media (max-width: 640px) {
  .tms-cf-band-hint { margin-left: 0; flex-basis: 100%; }
}

/* ---- import page: the export drop zone ----
   Borrows .tms-upload-zone for the look. The difference is the input: it covers the zone,
   transparent, so a dropped file lands on a real file input. */
.tms-ci-drop {
  position: relative;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px;
  min-height: 112px;
}
.tms-ci-drop input[type="file"] {
  position: absolute; inset: 0; width: 100%; height: 100%;
  opacity: 0; cursor: pointer; z-index: 1;
}
.tms-ci-drop input[type="file"]:disabled { cursor: progress; }
.tms-ci-drop:focus-within { outline: 2px solid var(--tms-accent-line); outline-offset: 2px; }

.tms-ci-drop-sub { font-size: 11.5px; color: var(--tms-text-3); }

.tms-ci-drop.has-file { border-style: solid; border-color: var(--tms-accent-line); background: var(--tms-accent-tint); }

.tms-ci-file {
  display: inline-flex; align-items: center; gap: 8px; max-width: 100%;
  color: var(--tms-text); font-weight: 600;
}
.tms-ci-file .tms-i { display: inline-block; margin: 0; font-size: 15px; color: var(--tms-accent); }
.tms-ci-file-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tms-ci-file-size {
  font-family: var(--tms-font-mono); font-size: 11.5px; font-weight: 400; color: var(--tms-text-3); flex: none;
}

/* Cards on the import page are spaced by the page, not each other. .tms-card carries no
   margin of its own, so stacked without this they sit edge to edge and read as one box. */
.tms-ci-page { display: flex; flex-direction: column; gap: 14px; }
.tms-ci-page > .tms-page-head { margin-bottom: 4px; }

/* ---- import page: client contacts, bulk mapping, ticket comparison ---- */
.tms-ci-client { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.tms-ci-bulk { margin-left: auto; }

.tms-ci-compare {
  display: grid; grid-template-columns: 1fr auto; align-items: center; gap: 6px 12px;
  border: 1px solid var(--tms-border); border-radius: 9px;
  background: var(--tms-surface-2); padding: 10px 12px; margin: 0 0 12px;
}
.tms-ci-compare.is-on { border-color: var(--tms-accent-line); background: var(--tms-accent-tint); }
.tms-ci-compare .tms-hint { grid-column: 1 / -1; margin: 0; }
.tms-ci-check { display: inline-flex; align-items: center; gap: 8px; font-weight: 600; color: var(--tms-text); cursor: pointer; }
.tms-ci-check input { width: 16px; height: 16px; accent-color: var(--tms-accent); }
.tms-ci-days { width: auto; min-width: 150px; }
@media (max-width: 560px) { .tms-ci-compare { grid-template-columns: 1fr; } }

/* A ticket somebody named in the chat — a statement, not a guess. */
.tms-cf-chip.is-stated { color: var(--tms-ok); border-color: var(--tms-ok-line); background: var(--tms-ok-tint); }
.tms-cf-chip.is-stated b { color: var(--tms-ok); }

/* Reference names are held in capitals, like every name in the portal. The placeholder
   keeps its own case — it is an instruction, not a name. */
.tms-ci-caps { text-transform: uppercase; }
.tms-ci-caps::placeholder { text-transform: none; }

/* ---- findings: where each one stands ---- */
.tms-cf-band.is-disputed  { --band: var(--tms-danger); }
.tms-cf-band.is-waiting   { --band: var(--tms-warn); }
.tms-cf-band.is-confirmed { --band: var(--tms-ok); }

/* What a quoted message did: our fix claim, the client's confirmation or rejection. */
.tms-cf-did {
  font-style: normal; font-size: 10.5px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase;
  color: var(--tms-text-3); border: 1px solid var(--tms-border); border-radius: 4px; padding: 0 5px;
}
.tms-cf-quote.is-confirm .tms-cf-did { color: var(--tms-ok); border-color: var(--tms-ok-line); background: var(--tms-ok-tint); }
.tms-cf-quote.is-dispute .tms-cf-did { color: var(--tms-danger); border-color: var(--tms-danger-line); background: var(--tms-danger-tint); }
.tms-cf-quote.is-confirm .tms-cf-quote-b,
.tms-cf-quote.is-dispute .tms-cf-quote-b { color: var(--tms-text); }

.tms-cf-chip.is-waiting { color: var(--tms-warn); border-color: var(--tms-warn-line); background: var(--tms-warn-tint); }
.tms-cf-chip.is-waiting b { color: var(--tms-warn); }

/* The chat and the linked ticket disagree. */
.tms-cf-mismatch {
  margin: 0; font-size: 12.5px; padding: 6px 10px; border-radius: 7px; border: 1px solid var(--tms-border);
}
.tms-cf-mismatch.is-warn   { color: var(--tms-warn);   background: var(--tms-warn-tint);   border-color: var(--tms-warn-line); }
.tms-cf-mismatch.is-danger { color: var(--tms-danger); background: var(--tms-danger-tint); border-color: var(--tms-danger-line); }
.tms-cf-mismatch.is-info   { color: var(--tms-info);   background: var(--tms-info-tint);   border-color: var(--tms-info-line); }

/* ---- import: analyse again ---- */
.tms-ci-redo {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
  margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--tms-border);
}
.tms-ci-redo p { margin: 0; flex-basis: 100%; font-size: 13px; color: var(--tms-text-2); }

/* =====================================================================================
   Chat insights
   ===================================================================================== */
.tms-ci-asof { margin: 0 0 12px; font-size: 12.5px; color: var(--tms-text-3); }

.tms-in-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
  gap: 14px; align-items: start;
}

.tms-in-hero { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.tms-in-hero-n {
  font-size: 34px; font-weight: 700; letter-spacing: -.02em; line-height: 1;
  color: var(--tms-text); font-variant-numeric: tabular-nums;
}
.tms-in-hero-t { font-size: 13px; color: var(--tms-text-2); line-height: 1.45; }
.tms-in-hero-t b { color: var(--tms-text); }

.tms-in-rows { display: flex; flex-direction: column; gap: 7px; }
.tms-in-share, .tms-in-theme {
  display: grid; grid-template-columns: minmax(0, 1.5fr) minmax(48px, 1fr) auto;
  align-items: center; gap: 10px; font-size: 12.5px; color: var(--tms-text);
}
.tms-in-share-l, .tms-in-theme-name { min-width: 0; }
.tms-in-theme-name { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }

/* Magnitude bars: one hue, recessive track. */
.tms-in-theme-bar { height: 6px; border-radius: 999px; background: var(--tms-surface-3); overflow: hidden; }
.tms-in-theme-bar i { display: block; height: 100%; border-radius: 999px; background: var(--tms-accent); }

.tms-in-share-n, .tms-in-theme-n {
  font-weight: 650; text-align: right; font-variant-numeric: tabular-nums; color: var(--tms-text);
}
.tms-in-theme-n small { font-weight: 400; color: var(--tms-text-3); margin-left: 4px; }

/* Common / Rising — spelled out, so the flag never depends on colour. */
.tms-cf-flag {
  font-size: 10px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase;
  border: 1px solid; border-radius: 4px; padding: 0 5px; line-height: 16px;
}
.tms-cf-flag.is-common { color: var(--tms-info); border-color: var(--tms-info-line); background: var(--tms-info-tint); }
.tms-cf-flag.is-rising { color: var(--tms-warn); border-color: var(--tms-warn-line); background: var(--tms-warn-tint); }
.tms-cf-chip.is-theme { color: var(--tms-text-2); }

.tms-in-sub {
  margin: 16px 0 8px; font-size: 11px; font-weight: 650; letter-spacing: .06em;
  text-transform: uppercase; color: var(--tms-text-3);
}

.tms-in-repeat { display: flex; align-items: baseline; gap: 8px; font-size: 12.5px; }
.tms-in-repeat-t { flex: 1 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--tms-text); }
.tms-in-repeat-n { font-weight: 650; font-variant-numeric: tabular-nums; color: var(--tms-text); }

.tms-in-table { width: 100%; border-collapse: collapse; font-size: 12.5px; margin-top: 12px; }
.tms-in-table th {
  text-align: left; padding: 4px 6px; border-bottom: 1px solid var(--tms-border);
  font-size: 10.5px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--tms-text-3);
}
.tms-in-table td { padding: 6px; border-bottom: 1px solid var(--tms-border); color: var(--tms-text); }
.tms-in-table .n { text-align: right; font-variant-numeric: tabular-nums; }
.tms-in-table tbody tr { cursor: pointer; }
.tms-in-table tbody tr:hover { background: var(--tms-surface-2); }

/* Mood word: text stays in text colour; the hue is a dot beside it. */
.tms-in-mood {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 12px; font-weight: 600; color: var(--tms-text);
}
.tms-in-mood::before {
  content: ""; width: 8px; height: 8px; border-radius: 50%; flex: none;
  background: var(--tms-text-3);
}
.tms-in-mood.is-angry::before, .tms-in-mood.is-frustrated::before { background: var(--tms-mood-neg); }
.tms-in-mood.is-satisfied::before, .tms-in-mood.is-happy::before { background: var(--tms-mood-pos); }
.tms-in-mood.is-angry::before, .tms-in-mood.is-happy::before { width: 10px; height: 10px; }
.tms-in-mood.is-big { font-size: 22px; font-weight: 700; letter-spacing: -.01em; }
.tms-in-mood.is-big::before { width: 12px; height: 12px; }

/* ---- the mood chart: diverging bars per calendar day ---- */
.tms-mood { display: flex; flex-direction: column; gap: 6px; }

.tms-mood-plot { position: relative; display: flex; gap: 2px; height: 104px; }
/* Zero line: recessive, but the reference every bar is read against. */
.tms-mood-plot::before {
  content: ""; position: absolute; left: 0; right: 0; top: 50%; height: 1px;
  background: var(--tms-border-strong);
}

/* The hit target is the whole column, not the thin bar inside it. */
.tms-mood-col { position: relative; flex: 1 1 0; min-width: 0; height: 100%; border-radius: 3px; }
.tms-mood-col.is-hover { background: var(--tms-surface-2); }
.tms-mood-col.is-boundary::before {
  content: ""; position: absolute; left: -2px; top: 0; bottom: 0;
  border-left: 1px dashed var(--tms-border-strong);
}

.tms-mood-bar { position: absolute; left: 0; right: 0; display: block; }
.tms-mood-bar.is-pos { bottom: 50%; height: var(--h); min-height: 2px; background: var(--tms-mood-pos); border-radius: 4px 4px 0 0; }
.tms-mood-bar.is-neg { top: 50%; height: var(--h); min-height: 2px; background: var(--tms-mood-neg); border-radius: 0 0 4px 4px; }
/* Neutral is a different SHAPE — a tick on the line — so it never has to be told apart by hue. */
.tms-mood-bar.is-zero { top: calc(50% - 2px); height: 4px; left: 15%; right: 15%; background: var(--tms-text-3); border-radius: 2px; }

.tms-mood-axis {
  display: flex; justify-content: space-between;
  font-size: 11px; color: var(--tms-text-3); font-variant-numeric: tabular-nums;
}
.tms-mood-tip { min-height: 18px; font-size: 12px; color: var(--tms-text-2); }
.tms-mood-tip b { color: var(--tms-text); }

.tms-in-reasons { display: flex; flex-direction: column; gap: 10px; }
.tms-in-reason { display: flex; flex-direction: column; gap: 3px; padding-left: 10px; border-left: 2px solid var(--tms-mood-neg); }
.tms-in-reason-h { display: flex; align-items: center; gap: 8px; font-size: 12px; color: var(--tms-text-2); }
.tms-in-reason-n { font-size: 13px; color: var(--tms-text); }
.tms-in-reason-q { font-size: 12.5px; font-style: italic; color: var(--tms-text-2); overflow-wrap: anywhere; }

@media (prefers-reduced-motion: reduce) { .tms-in-theme-bar i, .tms-mood-bar { transition: none; } }

/* A theme row is two lines: the name with its flags, then the bar and the count. On one
   line, at card width, the flags wrapped beneath the name and read as belonging to the
   theme BELOW — "Common" appeared to label the next area. */
.tms-in-themes { display: flex; flex-direction: column; gap: 11px; }
.tms-in-theme {
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas: "name name" "bar n";
  row-gap: 4px;
}
.tms-in-theme .tms-in-theme-name { grid-area: name; }
.tms-in-theme .tms-in-theme-bar  { grid-area: bar; }
.tms-in-theme .tms-in-theme-n    { grid-area: n; }

/* A part being retried — visible, so a slow stretch never looks like a hang. */
.tms-ci-retry {
  display: flex; align-items: center; gap: 8px; margin: 6px 0 0;
  font-size: 12.5px; color: var(--tms-warn);
}

/* ===================================================================== AI: saved analyses */

/* "Saved" after a run, or "a snapshot from…" when reading an old one. */
.tms-ai-saved {
  display: flex; align-items: flex-start; gap: 8px; margin: 0 0 14px; padding: 8px 11px;
  border: 1px solid var(--tms-ok-line); background: var(--tms-ok-tint); color: var(--tms-text);
  border-radius: var(--tms-r-sm); font-size: var(--tms-fs-data);
}
.tms-ai-saved .tms-i { color: var(--tms-ok); flex: none; margin-top: 2px; }
.tms-ai-saved.is-old { border-color: var(--tms-info-line); background: var(--tms-info-tint); }
.tms-ai-saved.is-old .tms-i { color: var(--tms-info); }

/* The latest few, under the intro of the analysis modal. */
.tms-ai-recent { margin-top: 16px; border: 1px solid var(--tms-border); border-radius: var(--tms-r); overflow: hidden; }
.tms-ai-recent-h {
  display: flex; justify-content: space-between; align-items: baseline; padding: 8px 12px;
  background: var(--tms-surface-2); font-size: var(--tms-fs-cap); font-weight: 600;
  text-transform: uppercase; letter-spacing: .05em; color: var(--tms-text-2);
}
.tms-ai-recent-h a { text-transform: none; letter-spacing: 0; font-weight: 500; }
.tms-ai-recent-row {
  display: grid; grid-template-columns: auto minmax(0, 1fr); column-gap: 6px; width: 100%;
  padding: 8px 12px; border: 0; border-top: 1px solid var(--tms-border);
  background: var(--tms-surface); color: var(--tms-text); text-align: left; cursor: pointer; font: inherit;
}
.tms-ai-recent-row:hover { background: var(--tms-accent-tint); }
.tms-ai-recent-pin { grid-row: 1; color: var(--tms-accent); width: 13px; height: 13px; margin-top: 2px; }
.tms-ai-recent-t { grid-column: 2; font-weight: 600; font-size: var(--tms-fs-data); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tms-ai-recent-m { grid-column: 2; font-size: var(--tms-fs-cap); color: var(--tms-text-3); }

/* The saved-analyses page: list left, analysis right. */
.tms-aa { display: grid; grid-template-columns: minmax(240px, 320px) minmax(0, 1fr); gap: 16px; align-items: start; }
.tms-aa-list { position: sticky; top: 12px; display: flex; flex-direction: column; max-height: calc(100vh - 140px); overflow: hidden; }
.tms-aa-search { padding: 10px; border-bottom: 1px solid var(--tms-border); }
.tms-aa-search .tms-input { width: 100%; }
.tms-aa-rows { overflow-y: auto; }
.tms-aa-row {
  display: flex; flex-direction: column; gap: 2px; padding: 9px 12px;
  border-bottom: 1px solid var(--tms-border); border-left: 3px solid transparent;
  color: var(--tms-text); text-decoration: none;
}
/* Rows that are buttons (digest, releases, problems, WhatsApp) rather than links: a button
   shrinks to its text and brings its own border and background, so undo all three here —
   not inline, where it also wiped the selected row's tint. */
button.tms-aa-row {
  width: 100%; text-align: left; font: inherit; cursor: pointer;
  background: none; border-top: 0; border-right: 0;
}
.tms-aa-row:hover { background: var(--tms-surface-2); text-decoration: none; }
.tms-aa-row.is-on { background: var(--tms-accent-tint); border-left-color: var(--tms-accent); }
.tms-aa-row-t { font-weight: 600; font-size: var(--tms-fs-data); line-height: 1.35; }
.tms-aa-row-m { font-size: var(--tms-fs-cap); color: var(--tms-text-3); }
.tms-aa-pin { width: 13px; height: 13px; color: var(--tms-accent); vertical-align: -2px; margin-right: 3px; }
.tms-aa-none { padding: 12px; margin: 0; }

.tms-aa-view { padding: 18px 20px; min-width: 0; }
.tms-aa-head { display: flex; align-items: flex-start; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
.tms-aa-head h2 { flex: 1 1 280px; margin: 0; font-size: var(--tms-fs-sub); line-height: 1.35; text-wrap: balance; }
.tms-aa-tools { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.tms-aa-tools .tms-btn.is-on { color: var(--tms-accent); border-color: var(--tms-accent-line); background: var(--tms-accent-tint); }
.tms-aa-confirm { display: inline-flex; align-items: center; gap: 6px; font-size: var(--tms-fs-data); color: var(--tms-text-2); }
.tms-aa-rename { display: flex; gap: 6px; flex: 1 1 100%; }
.tms-aa-rename .tms-input { flex: 1; min-width: 0; }

.tms-aa-meta {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 10px 18px;
  margin: 0 0 14px; padding: 12px 14px; background: var(--tms-surface-2); border-radius: var(--tms-r-sm);
}
.tms-aa-meta > div { min-width: 0; }
.tms-aa-meta > div.is-wide { grid-column: 1 / -1; }
.tms-aa-meta dt { font-size: var(--tms-fs-micro); text-transform: uppercase; letter-spacing: .06em; color: var(--tms-text-3); }
.tms-aa-meta dd { margin: 2px 0 0; font-size: var(--tms-fs-data); overflow-wrap: anywhere; }

@media (max-width: 900px) {
  .tms-aa { grid-template-columns: minmax(0, 1fr); }
  .tms-aa-list { position: static; max-height: 280px; }
  .tms-aa-view { padding: 14px; }
}

/* ===================================================================== AI: usage */

.tms-au-month { display: inline-flex; align-items: center; gap: 8px; font-size: var(--tms-fs-data); color: var(--tms-text-2); }
.tms-au-month select { width: auto; min-width: 150px; }
.tms-au-table { min-width: 900px; }
.tms-au-table th.tms-num, .tms-au-table td.tms-num { text-align: right; }
.tms-au-table td b { font-size: 13px; }
.tms-au-features { display: flex; flex-wrap: wrap; gap: 4px; }
.tms-au-feature {
  display: inline-flex; align-items: center; gap: 5px; padding: 1px 7px;
  border: 1px solid var(--tms-border); border-radius: 999px;
  font-size: var(--tms-fs-cap); color: var(--tms-text-2); white-space: nowrap;
}
.tms-au-feature b { color: var(--tms-text); font-weight: 600; }
.tms-au-flag { display: block; font-size: 11px; color: var(--tms-warn); font-family: var(--tms-font); }
.tms-au-none { font-size: var(--tms-fs-cap); color: var(--tms-text-3); }

/* At the limit: the next request is refused. Tinted, and said in words in the cell. */
.tms-au-table tbody tr.is-full { background: var(--tms-warn-tint); }

.tms-au-limit { display: inline-flex; align-items: center; gap: 4px; }
.tms-au-limit .tms-input { width: 92px; height: 29px; text-align: right; }
.tms-au-reset { width: 26px; height: 26px; }
.tms-au-table td .tms-perf-sub { text-align: right; }

/* Setting a limit for someone not yet in the table. */
.tms-au-add { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; margin-bottom: 12px; }
.tms-au-add-who { flex: 1 1 260px; max-width: 360px; min-width: 0; }
.tms-au-add-lim { display: inline-flex; align-items: center; gap: 8px; font-size: var(--tms-fs-data); color: var(--tms-text-2); }
.tms-au-add-lim .tms-input { width: 92px; text-align: right; }
.tms-au-table tfoot td { border-top: 2px solid var(--tms-border-strong); background: var(--tms-surface-2); font-weight: 600; }
.tms-au-note { max-width: 78ch; margin-top: 10px; }

/* ===================================================================== chat findings: the exchange */

/* Our reply: readable, unlike the set-back staff lines around a report. */
.tms-cf-quote.is-reply .tms-cf-quote-b { color: var(--tms-text-2); }
.tms-cf-quote.is-reply .tms-cf-did { color: var(--tms-accent); border-color: var(--tms-accent-line); background: var(--tms-accent-tint); }

.tms-cf-chip.is-replied { color: var(--tms-accent); border-color: var(--tms-accent-line); background: var(--tms-accent-tint); }
.tms-cf-chip.is-replied b { color: var(--tms-accent); }

.tms-cf-thread { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
.tms-cf-thread-list {
  align-self: stretch; display: flex; flex-direction: column; gap: 8px;
  max-height: 460px; overflow-y: auto; padding: 10px 12px;
  border: 1px solid var(--tms-border); border-radius: var(--tms-r-sm); background: var(--tms-surface-2);
}
/* Cited lines stand out; the rest is there to be read, not to compete. */
.tms-cf-thread-list .tms-cf-quote { padding: 4px 8px; border-radius: 4px; border-left: 2px solid transparent; }
.tms-cf-thread-list .tms-cf-quote.is-cited { background: var(--tms-surface); border-left-color: var(--kind-line, var(--tms-accent)); }
.tms-cf-thread-list .tms-cf-quote.is-context .tms-cf-quote-b { color: var(--tms-text-2); }
.tms-cf-thread-list .tms-cf-quote.is-staff.is-context .tms-cf-quote-b { color: var(--tms-text-2); }
.tms-cf-role {
  font-style: normal; font-size: 10.5px; color: var(--tms-text-3);
  border: 1px solid var(--tms-border); border-radius: 4px; padding: 0 5px;
}
.tms-cf-gap { text-align: center; color: var(--tms-text-3); font-size: 12px; letter-spacing: .3em; }
.tms-cf-thread-note { margin: 0; }

/* ===================================================================== tinted charts */
/* Chart marks are drawn TINTED — a wash of the series colour with a crisp edge in the same
   colour — the way the tinted buttons and the selected rail item are, rather than as solid
   blocks. Same hues, so every legend and meaning is unchanged; a quieter page, and the
   edge keeps adjacent stacked segments distinct.

   Two numbers for the whole portal: how much colour fills a mark, and how strong its edge
   is. Light theme needs a little more fill to read on white. Progress bars (uploads,
   analysis), status pills and tiny indicators keep their solid colours — they are not
   charts. Insights draws its marks inline with the same two tokens. */
:root { --tms-tint-fill: 42%; --tms-tint-edge: 90%; }
:root[data-theme="light"] { --tms-tint-fill: 40%; --tms-tint-edge: 85%; }

:is(
  .tms-ai-bar-t > i,                 /* AI analysis dialog: per-module/assignee/age bars */
  .tms-ai-bar-t > i > u,             /* …and the overdue share inside them */
  .tms-perf-bar-v,                   /* Performance: monthly bars */
  .tms-perf-mix-t i,                 /* Performance: work mix */
  .tms-cap-bar .is-dev, .tms-cap-bar .is-bug, .tms-cap-bar .is-act, .tms-cap-bar .is-un,
  .tms-load-meter i,                 /* assignee load */
  .tms-in-theme-bar i,               /* Chat findings → Insights: themes */
  .tms-mood-bar.is-pos, .tms-mood-bar.is-neg
) {
  background: color-mix(in srgb, var(--tint) var(--tms-tint-fill), transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--tint) var(--tms-tint-edge), transparent);
}

.tms-ai-bar-t > i                   { --tint: var(--tms-accent); }
.tms-ai-bar-t > i > u               { --tint: var(--tms-danger); }
.tms-perf-bar-v, .tms-perf-mix-t i  { --tint: var(--tms-accent); }
.tms-cap-bar .is-dev                { --tint: var(--tms-progress); }
.tms-cap-bar .is-bug                { --tint: var(--tms-danger); }
.tms-cap-bar .is-act                { --tint: var(--tms-ok); }
.tms-cap-bar .is-un                 { --tint: var(--tms-border-strong); }
.tms-load-meter .lm-prog            { --tint: var(--tms-progress); }
.tms-load-meter .lm-crit            { --tint: var(--tms-danger); }
.tms-load-meter i:not(.lm-prog):not(.lm-crit) { --tint: var(--tms-accent); }
.tms-in-theme-bar i                 { --tint: var(--tms-accent); }
.tms-mood-bar.is-pos                { --tint: var(--tms-mood-pos); }
.tms-mood-bar.is-neg                { --tint: var(--tms-mood-neg); }

/* ===================================================================== Features page */
.tms-ft-tabs { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 6px; }
.tms-ft-tab {
  display: inline-flex; align-items: center; gap: 8px; height: 38px; padding: 0 14px;
  border: 1px solid var(--tms-border); border-radius: var(--tms-r); background: var(--tms-surface);
  color: var(--tms-text-2); font: inherit; font-size: 13.5px; font-weight: 600; cursor: pointer;
}
.tms-ft-tab:hover { border-color: var(--tms-border-strong); color: var(--tms-text); }
.tms-ft-tab.is-on { background: var(--tms-accent-tint); border-color: var(--tms-accent-line); color: var(--tms-accent-ink-2); }
.tms-ft-count { font-size: var(--tms-fs-cap); font-weight: 500; color: var(--tms-text-3); }
.tms-ft-group {
  margin: 22px 0 10px; font-size: var(--tms-fs-cap); font-weight: 650; letter-spacing: .07em;
  text-transform: uppercase; color: var(--tms-text-3);
}
.tms-ft-cards { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 12px; }
.tms-ft-card { display: flex; gap: 14px; padding: 16px; align-items: flex-start; }
.tms-ft-card.is-off { opacity: .72; }
.tms-ft-ico {
  flex: none; width: 42px; height: 42px; border-radius: 10px; display: grid; place-items: center;
  background: var(--tms-accent-tint); color: var(--tms-accent-ink-2); border: 1px solid var(--tms-accent-line);
  font-size: 20px;
}
.tms-ft-card.is-off .tms-ft-ico { background: var(--tms-surface-2); color: var(--tms-text-3); border-color: var(--tms-border); }
.tms-ft-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 6px; }
.tms-ft-top { display: flex; align-items: center; gap: 10px; }
.tms-ft-top b { flex: 1; font-size: 14px; }
.tms-ft-body p { margin: 0; font-size: var(--tms-fs-data); color: var(--tms-text-2); line-height: 1.5; }
.tms-ft-tags { display: flex; flex-wrap: wrap; gap: 6px; }
.tms-ft-tag {
  font-size: 11px; padding: 1px 8px; border-radius: 999px; border: 1px solid var(--tms-border);
  color: var(--tms-text-3);
}
.tms-ft-tag.is-need { color: var(--tms-warn); border-color: var(--tms-warn-line); background: color-mix(in srgb, var(--tms-warn) 10%, transparent); }
.tms-ft-state {
  font-size: 11.5px; font-weight: 600; padding: 1px 9px; border-radius: 999px;
  border: 1px solid var(--tms-border-strong); color: var(--tms-text-3);
}
.tms-ft-state.is-on { color: var(--tms-ok); border-color: var(--tms-ok-line); background: color-mix(in srgb, var(--tms-ok) 12%, transparent); }

/* The switch: a pill with a knob, accent when on. */
.tms-toggle {
  position: relative; flex: none; width: 40px; height: 22px; border-radius: 999px; padding: 0;
  border: 1px solid var(--tms-border-strong); background: var(--tms-surface-3); cursor: pointer;
  transition: background var(--tms-fast), border-color var(--tms-fast);
}
.tms-toggle-knob {
  position: absolute; top: 2px; left: 2px; width: 16px; height: 16px; border-radius: 50%;
  background: var(--tms-text-3); transition: transform var(--tms-fast), background var(--tms-fast);
}
.tms-toggle.is-on { background: color-mix(in srgb, var(--tms-accent) 30%, transparent); border-color: var(--tms-accent); }
.tms-toggle.is-on .tms-toggle-knob { transform: translateX(18px); background: var(--tms-accent); }
.tms-toggle:focus-visible { outline: 2px solid var(--tms-accent); outline-offset: 2px; }
.tms-toggle[disabled] { opacity: .5; cursor: default; }

/* ===================================================================== feature 1: triage */
.tms-triage {
  display: flex; flex-direction: column; gap: 10px; margin: 4px 0 14px; padding: 12px 14px;
  border: 1px dashed var(--tms-accent-line); border-radius: var(--tms-r);
  background: color-mix(in srgb, var(--tms-accent) 5%, transparent);
}
.tms-triage-h { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.tms-triage-out { display: flex; flex-direction: column; gap: 10px; }
.tms-triage-chips { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.tms-triage-chip {
  display: inline-flex; align-items: center; gap: 6px; height: 28px; padding: 0 10px;
  border: 1px solid var(--tms-accent-line); border-radius: 999px; cursor: pointer;
  background: var(--tms-surface); color: var(--tms-text-2); font: inherit; font-size: var(--tms-fs-data);
}
.tms-triage-chip b { color: var(--tms-text); font-weight: 600; }
.tms-triage-chip:hover { background: var(--tms-accent-tint); }
.tms-triage-chip.is-set { border-color: var(--tms-ok-line); color: var(--tms-ok); cursor: default; }
.tms-triage-dup {
  display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; border-radius: var(--tms-r-sm);
  border: 1px solid var(--tms-warn-line); background: color-mix(in srgb, var(--tms-warn) 10%, transparent);
}
.tms-triage-dup > b { display: flex; align-items: center; gap: 6px; color: var(--tms-warn); font-size: var(--tms-fs-data); }
.tms-triage-dup a { display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px; font-size: var(--tms-fs-data); color: var(--tms-text); }
.tms-triage-dup a i { font-style: normal; font-size: 11px; color: var(--tms-text-3); border: 1px solid var(--tms-border); border-radius: 4px; padding: 0 5px; }
.tms-triage-dup a small { flex-basis: 100%; color: var(--tms-text-3); font-size: var(--tms-fs-cap); }

/* ===================================================================== features 2-3: draft, summary */
/* A strip inside the composer, between the tabs and the editor toolbar: padded like the
   composer's other rows, one height for all three controls, nothing wider than the box. */
.tms-aidraft {
  display: flex; gap: 8px; align-items: center; flex-wrap: wrap;
  padding: 10px 12px; border-bottom: 1px solid var(--tms-border); background: var(--tms-surface);
}
.tms-aidraft > .tms-btn,
.tms-aidraft > .tms-input { height: 32px; min-height: 32px; font-size: var(--tms-fs-data); }
.tms-aidraft > .tms-btn { flex: none; }
.tms-aidraft-lang { flex: none; width: auto; min-width: 130px; padding: 0 10px; }
.tms-aidraft-intent { flex: 1 1 220px; width: auto; min-width: 0; }

.tms-aisum {
  margin: 12px 0 14px; padding: 12px 14px; border-radius: var(--tms-r);
  border: 1px solid var(--tms-accent-line); background: color-mix(in srgb, var(--tms-accent) 6%, var(--tms-surface));
  display: flex; flex-direction: column; gap: 6px;
}
.tms-aisum.is-empty { border-style: dashed; }
.tms-aisum-h { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; color: var(--tms-accent-ink-2); }
.tms-aisum-h b { color: var(--tms-text); font-size: var(--tms-fs-data); }
.tms-aisum-wait { font-size: 11px; font-weight: 600; padding: 1px 8px; border-radius: 999px; border: 1px solid var(--tms-border-strong); color: var(--tms-text-2); }
.tms-aisum-wait.is-us { color: var(--tms-danger); border-color: var(--tms-danger-line); background: color-mix(in srgb, var(--tms-danger) 10%, transparent); }
.tms-aisum-wait.is-them { color: var(--tms-warn); border-color: var(--tms-warn-line); background: color-mix(in srgb, var(--tms-warn) 10%, transparent); }
.tms-aisum-stale { font-size: 11px; color: var(--tms-warn); }
.tms-aisum-t { margin: 0; font-size: 13.5px; line-height: 1.55; color: var(--tms-text); }
.tms-aisum-next { margin: 0; font-size: var(--tms-fs-data); color: var(--tms-text-2); }
.tms-aisum-meta { font-size: 11px; color: var(--tms-text-3); }

/* ===================================================================== feature 4: knowledge base */
.tms-kb { display: grid; grid-template-columns: minmax(240px, 340px) minmax(0, 1fr); gap: 16px; align-items: start; }
.tms-kb-list { display: flex; flex-direction: column; max-height: calc(100vh - 150px); overflow: hidden; }
.tms-kb-search { padding: 10px; border-bottom: 1px solid var(--tms-border); }
.tms-kb-search .tms-input { width: 100%; }
.tms-kb-none { padding: 12px; margin: 0; }
.tms-kb-list > .tms-kb-row:last-child { border-bottom: 0; }
.tms-kb-list { overflow-y: auto; }
.tms-kb-row { display: flex; flex-direction: column; gap: 3px; padding: 10px 12px; border-bottom: 1px solid var(--tms-border); border-left: 3px solid transparent; color: var(--tms-text); text-decoration: none; }
.tms-kb-row:hover { background: var(--tms-surface-2); text-decoration: none; }
.tms-kb-row.is-on { background: var(--tms-accent-tint); border-left-color: var(--tms-accent); }
.tms-kb-row-t { font-weight: 600; font-size: var(--tms-fs-data); line-height: 1.35; }
.tms-kb-row-m { font-size: var(--tms-fs-cap); color: var(--tms-text-3); display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.tms-kb-badge { font-style: normal; font-size: 10.5px; padding: 0 6px; border-radius: 4px; border: 1px solid var(--tms-border-strong); color: var(--tms-text-2); }
.tms-kb-badge.is-draft { color: var(--tms-warn); border-color: var(--tms-warn-line); }
.tms-kb-view { padding: 18px 22px; min-width: 0; }
.tms-kb-head { display: flex; gap: 12px; align-items: flex-start; flex-wrap: wrap; }
.tms-kb-head h2 { flex: 1 1 280px; margin: 0; font-size: var(--tms-fs-sub); text-wrap: balance; }
.tms-kb-summary { margin: 10px 0 0; padding: 10px 12px; border-left: 3px solid var(--tms-accent); background: color-mix(in srgb, var(--tms-accent) 7%, transparent); font-size: 14px; }
.tms-kb-body { margin-top: 14px; font-size: 14px; line-height: 1.65; max-width: 75ch; }
.tms-kb-body ol, .tms-kb-body ul { padding-left: 22px; }
.tms-kb-meta { margin-top: 18px; display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.tms-kb-hits { margin-top: 8px; display: flex; flex-direction: column; gap: 6px; padding: 10px 12px; border-radius: var(--tms-r-sm); border: 1px solid var(--tms-info-line); background: color-mix(in srgb, var(--tms-info) 8%, transparent); }
.tms-kb-hits > b { display: flex; gap: 6px; align-items: center; font-size: var(--tms-fs-data); color: var(--tms-info); }
.tms-kb-hits a { font-size: var(--tms-fs-data); display: flex; flex-direction: column; }
.tms-kb-hits a small { color: var(--tms-text-3); font-size: var(--tms-fs-cap); }
@media (max-width: 900px) { .tms-kb { grid-template-columns: minmax(0, 1fr); } .tms-kb-list { max-height: 300px; } }

/* ===================================================================== feature 5: SLA */
.tms-sla-chip {
  display: inline-flex; align-items: center; gap: 5px; height: 22px; padding: 0 8px; border-radius: 999px;
  font-size: 11.5px; white-space: nowrap; border: 1px solid var(--c, var(--tms-border-strong));
  color: var(--c, var(--tms-text-2)); background: color-mix(in srgb, var(--c, transparent) 12%, transparent);
}
.tms-sla-chip b { font-weight: 600; color: var(--tms-text-2); }
.tms-sla-chip.is-breached, .tms-sla-chip.is-late { --c: var(--tms-danger); }
.tms-sla-chip.is-atrisk { --c: var(--tms-warn); }
.tms-sla-chip.is-met { --c: var(--tms-ok); }
.tms-sla-chip.is-paused { --c: var(--tms-info); }
.tms-sla-pair { display: inline-flex; gap: 6px; flex-wrap: wrap; align-items: center; }
/* Between the reminders card and the summary: the same 12px the cards keep between them. */
.tms-sla-ticket { display: flex; margin: 12px 0 0; }
.tms-sla-kpis { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 10px; margin-bottom: 14px; }
.tms-sla-kpis .tms-kpi { text-align: left; cursor: pointer; font: inherit; color: inherit; }
.tms-sla-kpis .tms-kpi.is-on { box-shadow: inset 0 0 0 1px var(--tms-accent); }
.tms-sla-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 14px; }
.tms-sla-table th.tms-num, .tms-sla-table td.tms-num { text-align: right; }
.tms-sla-table small { color: var(--tms-text-3); font-size: 11px; margin-left: 4px; }
.tms-sla-in { width: 76px; height: 28px; text-align: right; display: inline-block; }
.tms-sla-list { min-width: 860px; }

/* ===================================================================== feature 6: client health */
.tms-health-table { min-width: 900px; }
.tms-health-table th.tms-num, .tms-health-table td.tms-num { text-align: right; }
.tms-health-score {
  position: relative; display: inline-flex; align-items: center; width: 120px; height: 20px; border-radius: 999px;
  background: var(--tms-surface-3); overflow: hidden; vertical-align: middle;
}
.tms-health-score i {
  position: absolute; inset: 0 auto 0 0; border-radius: 999px;
  background: color-mix(in srgb, var(--c) var(--tms-tint-fill), transparent);
  box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--c) var(--tms-tint-edge), transparent);
}
.tms-health-score b { position: relative; margin-left: 8px; font-family: var(--tms-font-mono); font-size: 12px; }
.tms-health-score.is-healthy { --c: var(--tms-ok); }
.tms-health-score.is-watch { --c: var(--tms-warn); }
.tms-health-score.is-atrisk { --c: var(--tms-danger); }
.tms-health-change { margin-left: 8px; font-size: 11.5px; font-family: var(--tms-font-mono); }
.tms-health-change.is-up { color: var(--tms-ok); }
.tms-health-change.is-down { color: var(--tms-danger); }
.tms-health-parts { display: flex; flex-wrap: wrap; gap: 4px; }
.tms-health-part { font-size: 11px; padding: 1px 7px; border-radius: 999px; border: 1px solid var(--c, var(--tms-border)); color: var(--tms-text-2); white-space: nowrap; }
.tms-health-part b { color: var(--c, var(--tms-text)); font-weight: 600; }
.tms-health-part.is-good { --c: var(--tms-ok-line); }
.tms-health-part.is-mid { --c: var(--tms-warn-line); }
.tms-health-part.is-bad { --c: var(--tms-danger-line); }

/* ===================================================================== feature 7: survey page */
.tms-survey { min-height: 100vh; display: grid; place-items: center; padding: 24px 16px; background: var(--tms-bg); }
.tms-survey-card {
  width: min(460px, 100%); padding: 28px 26px; border-radius: var(--tms-r-lg); text-align: center;
  background: var(--tms-surface); border: 1px solid var(--tms-border); box-shadow: var(--tms-shadow-2);
  display: flex; flex-direction: column; gap: 12px; align-items: stretch;
}
.tms-survey-badge { width: 44px; height: 44px; align-self: center; }
.tms-survey-card h1 { margin: 0; font-size: 22px; }
.tms-survey-stars { display: flex; justify-content: center; gap: 8px; margin-top: 6px; }
.tms-survey-stars button {
  width: 48px; height: 48px; border-radius: 10px; font-size: 24px; cursor: pointer;
  border: 1px solid var(--tms-border-strong); background: var(--tms-surface-2); color: var(--tms-text-3);
}
.tms-survey-stars button.is-on { color: var(--tms-warn); border-color: var(--tms-warn-line); background: color-mix(in srgb, var(--tms-warn) 14%, transparent); }
.tms-survey-scale { display: flex; justify-content: space-between; margin: 0 6px; font-size: 11.5px; color: var(--tms-text-3); }
.tms-survey-l { text-align: left; font-size: var(--tms-fs-data); color: var(--tms-text-2); }
.tms-survey-thanks { margin: 0; color: var(--tms-ok); font-size: var(--tms-fs-data); }

/* ===================================================================== feature 8: digest */
/* The email exactly as sent, on its own white page; tall enough to read most of a digest
   without a second scrollbar inside the card. */
.tms-digest-frame { display: block; width: 100%; height: max(520px, calc(100vh - 340px)); border: 1px solid var(--tms-border); border-radius: var(--tms-r-sm); background: #fff; }
.tms-digest-subject { margin: 4px 0 8px; }

/* ===================================================================== feature 9: releases */
.tms-rel-badge { font-style: normal; font-size: 11px; font-weight: 600; padding: 1px 7px; border-radius: 999px; margin-left: 6px;
    color: var(--tms-accent); background: color-mix(in srgb, var(--tms-accent) 14%, transparent); border: 1px solid color-mix(in srgb, var(--tms-accent) 35%, transparent); }
.tms-rel-badge.is-done { color: var(--tms-ok); background: var(--tms-ok-tint); border-color: var(--tms-ok-line); }
.tms-rel-confirm { display: grid; gap: 8px; padding: 12px 14px; margin-bottom: 14px; border-radius: var(--tms-r-sm);
    background: color-mix(in srgb, var(--tms-accent) 9%, transparent); border: 1px solid color-mix(in srgb, var(--tms-accent) 30%, transparent); }
.tms-rel-confirm p { margin: 0; }
.tms-rel-lh { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-bottom: 6px; }
.tms-rel-lh label { margin: 0; }
.tms-rel-table td { vertical-align: top; padding: 8px 10px; }
.tms-rel-table td:first-child { width: 1%; white-space: nowrap; }

/* ===================================================================== feature 10: time */
.tms-time { display: grid; gap: 6px; }
.tms-time .tms-prop-l { display: flex; justify-content: space-between; align-items: baseline; }
.tms-time-total { font-family: var(--tms-font-mono, ui-monospace, monospace); font-size: 12px; color: var(--tms-accent); text-transform: none; letter-spacing: 0; }
.tms-time-row { display: flex; gap: 6px; align-items: center; }
.tms-time-row > .tms-input { min-width: 0; flex: 1; }
.tms-time-run { display: flex; gap: 8px; align-items: center; padding: 6px 8px; border-radius: var(--tms-r-sm);
    background: color-mix(in srgb, var(--tms-accent) 10%, transparent); border: 1px solid color-mix(in srgb, var(--tms-accent) 30%, transparent); }
.tms-time-run .tms-btn { margin-left: auto; }
.tms-time-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--tms-danger); animation: tms-time-pulse 1.4s ease-in-out infinite; }
@keyframes tms-time-pulse { 50% { opacity: .3; } }
.tms-time-list { list-style: none; margin: 4px 0 0; padding: 0; display: grid; gap: 4px; }
.tms-time-list li { display: grid; grid-template-columns: 52px 1fr auto; gap: 6px; align-items: start; font-size: 12px; }
.tms-time-who { color: var(--tms-text-2, var(--tms-muted)); min-width: 0; overflow-wrap: anywhere; }
.tms-time-range { flex-wrap: wrap; }
.tms-time-range .tms-input { width: auto; }
.tms-time-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); gap: 14px; margin-top: 14px; align-items: start; }
.tms-time-table td { padding: 8px 12px; vertical-align: top; }
.tms-time-table td.tms-num { width: 1%; white-space: nowrap; }
.tms-time-bar { display: block; height: 4px; margin-top: 6px; border-radius: 4px; width: var(--w); background: var(--tms-tint-fill, color-mix(in srgb, var(--tms-accent) 40%, transparent)); }
.tms-time-more { justify-self: start; padding: 0; border: 0; background: none; font: inherit; font-size: 12px; color: var(--tms-accent); cursor: pointer; }
.tms-time-more:hover { text-decoration: underline; }

/* ===================================================================== feature 11: problems */
.tms-prob { display: grid; gap: 6px; }
.tms-prob-chip { display: grid; grid-template-columns: auto 1fr; gap: 4px 8px; align-items: start; padding: 8px 10px; border-radius: var(--tms-r-sm);
    text-decoration: none; color: inherit; background: color-mix(in srgb, var(--tms-accent) 8%, transparent); border: 1px solid color-mix(in srgb, var(--tms-accent) 28%, transparent); }
.tms-prob-chip:hover { background: color-mix(in srgb, var(--tms-accent) 14%, transparent); }
.tms-prob-chip > span { display: grid; min-width: 0; overflow-wrap: anywhere; font-size: 13px; }
.tms-prob-chip small { color: var(--tms-text-2); font-size: 11px; }
.tms-prob-chip .tms-prob-st { grid-column: 2; justify-self: start; margin: 0; }
.tms-prob-work { margin: 0; }
.tms-prob-st { font-style: normal; font-size: 11px; font-weight: 600; padding: 1px 7px; border-radius: 999px; margin-left: 6px; white-space: nowrap; }
.tms-prob-st.is-info { color: var(--tms-accent); background: color-mix(in srgb, var(--tms-accent) 14%, transparent); border: 1px solid color-mix(in srgb, var(--tms-accent) 35%, transparent); }
.tms-prob-st.is-warn { color: var(--tms-warn); background: var(--tms-warn-tint, color-mix(in srgb, var(--tms-warn) 14%, transparent)); border: 1px solid var(--tms-warn-line, color-mix(in srgb, var(--tms-warn) 35%, transparent)); }
.tms-prob-st.is-ok { color: var(--tms-ok); background: var(--tms-ok-tint); border: 1px solid var(--tms-ok-line); }
.tms-prob-closed td { opacity: .6; }

/* ===================================================================== feature 12: whatsapp inbox */
.tms-wa-thread { display: grid; gap: 8px; max-height: 52vh; overflow-y: auto; padding: 4px 2px 10px; }
.tms-wa-msg { display: flex; gap: 8px; align-items: flex-start; }
.tms-wa-msg.is-out { justify-content: flex-end; }
.tms-wa-msg > input { margin-top: 10px; }
.tms-wa-bubble { display: grid; gap: 3px; max-width: min(560px, 82%); padding: 8px 11px; border-radius: 12px; border: 1px solid var(--tms-border); background: var(--tms-surface-2, var(--tms-surface)); min-width: 0; }
.tms-wa-msg.is-in .tms-wa-bubble { border-top-left-radius: 4px; }
.tms-wa-msg.is-out .tms-wa-bubble { border-top-right-radius: 4px; background: color-mix(in srgb, var(--tms-accent) 12%, transparent); border-color: color-mix(in srgb, var(--tms-accent) 30%, transparent); }
.tms-wa-msg.is-new .tms-wa-bubble { border-color: color-mix(in srgb, var(--tms-accent) 55%, transparent); }
.tms-wa-body { white-space: pre-wrap; overflow-wrap: anywhere; }
.tms-wa-bubble small { color: var(--tms-text-2); font-size: 11px; }
.tms-wa-type { font-style: normal; font-size: 11px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; color: var(--tms-text-2); }
.tms-wa-act { display: grid; gap: 10px; padding: 12px 0; border-top: 1px solid var(--tms-border); }
.tms-wa-reply { padding-top: 12px; border-top: 1px solid var(--tms-border); }
.tms-wa-reply textarea { flex: 1; min-width: 0; }

/* ===================================================================== feature 13: email replies */
.tms-er-table td { padding: 8px 12px; vertical-align: top; }
.tms-er-table td:first-child { white-space: nowrap; }
.tms-er-ex { display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; max-width: 520px; }

/* ===================================================================== feature 14: push */
.tms-push { display: grid; gap: 10px; }
.tms-push-row { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; }
.tms-push-keys { margin: 0; padding: 10px 12px; border-radius: var(--tms-r-sm); background: var(--tms-surface-2); border: 1px solid var(--tms-border);
    font-family: var(--tms-font-mono); font-size: 12px; overflow-x: auto; white-space: pre; user-select: all; }

/* ===================================================================== feature 16: audit log */
.tms-audit-filters { margin-bottom: 14px; }
.tms-audit-row { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; }
.tms-audit-row label { display: grid; gap: 4px; font-size: 12px; color: var(--tms-text-2); }
.tms-audit-row .tms-input { width: auto; min-width: 140px; }
.tms-audit-areas { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 10px; }
.tms-audit-areas .tms-chip { padding: 3px 10px; border-radius: 999px; border: 1px solid var(--tms-border); background: none; font: inherit; font-size: 12px; color: inherit; cursor: pointer; }
.tms-audit-areas .tms-chip.is-on { color: var(--tms-accent); background: color-mix(in srgb, var(--tms-accent) 12%, transparent); border-color: color-mix(in srgb, var(--tms-accent) 40%, transparent); }
.tms-audit-areas .tms-chip:disabled { opacity: .45; cursor: default; }

/* ===================================================================== AI usage: clients */
.tms-au-tabs { margin-bottom: 12px; }
.tms-au-org-h { display: flex; align-items: baseline; gap: 10px; margin: 18px 0 8px; }
.tms-au-org-h h2 { font-size: 15px; margin: 0; }
.tms-au-pool { font-size: 11px; color: var(--tms-text-3); }

/* Digest preview in the portal's colours. The email carries inline styles for mail clients
   (white page, dark text, #0E7C74 links, #777 notes); these win over them by !important and
   map each one to its theme token, so it reads right in light and dark. */
.tms-digest-bar { display: flex; align-items: center; justify-content: space-between; gap: 10px; flex-wrap: wrap; margin: 4px 0 10px; }
.tms-digest-bar .tms-hint { margin: 0; }
.tms-digest-view {
  padding: 4px 22px 18px; border: 1px solid var(--tms-border); border-radius: var(--tms-r-sm);
  background: var(--tms-surface-2); max-height: max(520px, calc(100vh - 340px)); overflow-y: auto;
}
.tms-digest-view > div { max-width: none !important; margin: 0 !important; color: var(--tms-text) !important; font-family: inherit !important; }
.tms-digest-view [style*="border-top"] { border-top-color: var(--tms-accent) !important; }
.tms-digest-view b, .tms-digest-view h3 { color: var(--tms-text) !important; }
.tms-digest-view h3 { font-family: inherit; }
.tms-digest-view a, .tms-digest-view [style*="#0E7C74"] { color: var(--tms-accent) !important; }
.tms-digest-view li > a { font-family: var(--tms-font-mono); font-size: .95em; }
.tms-digest-view li { color: var(--tms-text); }
.tms-digest-view [style*="#777"] { color: var(--tms-text-3) !important; }
.tms-digest-view li::marker { color: var(--tms-text-3); }
