/* =====================================================================
   tibr-shell.css — the appv3 page shell + house components (2026-09).

   One stylesheet, two brands. Palette and wordmark switch on
   <body data-brand="tibr|vyntworks">; everything else (density, hairlines,
   the grid, buttons, inputs) is shared. Design: flat, hairline borders,
   5px radii, tracked uppercase labels, tabular numerals, hover-revealed
   row actions. Nunito throughout.

   Prefix: ts-  (shell + components).  Colours are RGB triplets so we can
   write rgb(var(--ts-primary-500) / 0.15).
   ===================================================================== */

:root {
  --ts-sidebar-w: 13.5rem;
  --ts-chrome-h: 40px;
  --ts-r: 5px;
  --ts-font: 'Nunito', ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
  --ts-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
}

/* ---- TIBR (default): wordmark orange + wordmark grey ------------------ */
body, body[data-brand="tibr"] {
  --ts-primary-50: 254 241 236;  --ts-primary-100: 253 218 204; --ts-primary-200: 251 181 155;
  --ts-primary-300: 248 142 103; --ts-primary-400: 244 115 63;  --ts-primary-500: 241 90 41;
  --ts-primary-600: 212 72 26;   --ts-primary-700: 173 57 19;   --ts-primary-800: 133 43 14;
  --ts-primary-900: 92 29 9;
  --ts-gray-50: 248 248 248;  --ts-gray-100: 241 241 242; --ts-gray-200: 220 221 222;
  --ts-gray-300: 190 191 193; --ts-gray-400: 152 153 156; --ts-gray-500: 115 116 119;
  --ts-gray-600: 88 89 91;    --ts-gray-700: 69 70 72;    --ts-gray-800: 51 52 54;
  --ts-gray-900: 34 35 36;    --ts-gray-950: 23 24 25;
  --ts-accent: 241 90 41;          /* active marker on the dark masthead */
  --ts-page-bg: #f6f6f6;
}

/* ---- Vyntworks: "Steel Navy & Chalk" ---------------------------------- */
body[data-brand="vyntworks"] {
  --ts-primary-50: 233 239 245;  --ts-primary-100: 217 227 238; --ts-primary-200: 183 201 220;
  --ts-primary-300: 143 176 212; --ts-primary-400: 95 132 171;  --ts-primary-500: 49 86 124;
  --ts-primary-600: 43 75 109;   --ts-primary-700: 34 61 89;    --ts-primary-800: 26 47 69;
  --ts-primary-900: 20 28 46;
  --ts-gray-50: 251 251 249;  --ts-gray-100: 241 242 238; --ts-gray-200: 221 224 218;
  --ts-gray-300: 195 200 194; --ts-gray-400: 154 161 174; --ts-gray-500: 107 115 130;
  --ts-gray-600: 76 85 104;   --ts-gray-700: 44 53 71;    --ts-gray-800: 31 39 56;
  --ts-gray-900: 20 28 46;    --ts-gray-950: 14 20 32;
  --ts-accent: 143 176 212;        /* light steel on the ink-navy masthead */
  --ts-page-bg: #f6f7f5;
}

:root { --ts-hair: rgb(20 28 46 / 0.09); --ts-hair-strong: rgb(20 28 46 / 0.16); }

/* ---- Base --------------------------------------------------------------- */
[v-cloak] { display: none !important; }
html { -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; }
body.ts-body {
  margin: 0; background: var(--ts-page-bg); color: rgb(var(--ts-gray-700));
  font-family: var(--ts-font); font-size: 0.875rem; line-height: 1.5;
}
/* Keep these at element specificity so .ts-area / .ts-mast-chip / .ts-item colours win. */
a { color: inherit; text-decoration: none; }
button { font-family: inherit; cursor: pointer; }
.ts-body .mdi::before { line-height: 1; }

/* ---- Masthead ----------------------------------------------------------- */
.ts-chrome { position: fixed; top: 0; left: 0; right: 0; z-index: 50; }
.ts-areas {
  height: var(--ts-chrome-h); display: flex; align-items: stretch; gap: 0; padding: 0 0.5rem 0 0.25rem;
  background: rgb(var(--ts-gray-900)); border-bottom: 1px solid rgb(255 255 255 / 0.08);
}
.ts-areas-scroll { display: flex; align-items: stretch; flex: 1; min-width: 0; overflow-x: auto; scrollbar-width: none; }
.ts-areas-scroll::-webkit-scrollbar { display: none; }
/* When the Area strip overflows (tablet/phone) the scrollbar is hidden, so fade the
   clipped edge(s) as the hint that more tabs are off-screen. tibr-shell.js toggles
   the classes on scroll/resize and scrolls the active tab into view on load. */
.ts-areas-scroll.ts-more-right { -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 2.25rem), transparent); mask-image: linear-gradient(to right, #000 calc(100% - 2.25rem), transparent); }
.ts-areas-scroll.ts-more-left { -webkit-mask-image: linear-gradient(to right, transparent, #000 2.25rem); mask-image: linear-gradient(to right, transparent, #000 2.25rem); }
.ts-areas-scroll.ts-more-left.ts-more-right { -webkit-mask-image: linear-gradient(to right, transparent, #000 2.25rem, #000 calc(100% - 2.25rem), transparent); mask-image: linear-gradient(to right, transparent, #000 2.25rem, #000 calc(100% - 2.25rem), transparent); }
.ts-area {
  display: flex; align-items: center; gap: 0.35rem; flex-shrink: 0; padding: 0 0.6rem;
  font-size: 0.62rem; font-weight: 600; letter-spacing: 0.09em; text-transform: uppercase; white-space: nowrap;
  color: rgb(var(--ts-gray-400)); border-bottom: 2px solid transparent; transition: color 0.15s, border-color 0.15s;
}
.ts-area .mdi { font-size: 0.9rem; }
.ts-area:hover { color: #fff; }
.ts-area--active { color: #fff; border-bottom-color: rgb(var(--ts-accent)); }
.ts-area--active .mdi { color: rgb(var(--ts-accent)); }
.ts-burger {
  display: flex; width: 34px; height: 100%; border: 0; background: transparent; color: rgb(var(--ts-gray-300));
  align-items: center; justify-content: center; font-size: 1.25rem; margin-right: 0.25rem;
}
.ts-burger:hover { color: #fff; }
.ts-mast-right { margin-left: auto; display: flex; align-items: center; gap: 0.3rem; padding-left: 0.5rem; flex-shrink: 0; }
.ts-mast-btn {
  width: 28px; height: 28px; border-radius: 6px; border: 0; background: transparent; color: rgb(var(--ts-gray-400));
  display: flex; align-items: center; justify-content: center; font-size: 1.05rem; position: relative;
}
.ts-mast-btn:hover { background: rgb(255 255 255 / 0.08); color: #fff; }
.ts-mast-avatar {
  width: 24px; height: 24px; border-radius: 99px; background: rgb(var(--ts-accent)); color: rgb(var(--ts-gray-900));
  font-size: 0.6rem; font-weight: 700; display: flex; align-items: center; justify-content: center; margin-left: 0.2rem;
}
body[data-brand="tibr"] .ts-mast-avatar { color: #fff; }
.ts-mast-chip {
  font-size: 0.6rem; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 700; padding: 0.15rem 0.45rem;
  border-radius: 4px; background: rgb(var(--ts-accent) / 0.18); color: rgb(var(--ts-accent)); margin-right: 0.35rem;
}
.ts-disabled { opacity: 0.45; pointer-events: none; }

/* ---- Sidebar ------------------------------------------------------------ */
.ts-sidebar {
  position: fixed; top: var(--ts-chrome-h); bottom: 0; left: 0; width: var(--ts-sidebar-w); z-index: 40;
  display: flex; flex-direction: column; background: #fff; border-right: 1px solid var(--ts-hair);
  transition: transform 0.2s ease;
}
.ts-sidebar-header { height: 3rem; flex-shrink: 0; display: flex; align-items: center; justify-content: space-between; padding: 0 1rem; border-bottom: 1px solid var(--ts-hair); }
.ts-brand { display: flex; align-items: center; }
.ts-logo { height: 1.5rem; width: auto; display: block; }
.ts-wordmark { font-family: 'Newsreader', Georgia, 'Times New Roman', serif; font-weight: 600; font-size: 1.25rem; letter-spacing: -0.01em; line-height: 1; color: rgb(var(--ts-gray-900)); }
.ts-wordmark .tick { color: rgb(var(--ts-primary-500)); }
.ts-sidebar-close { display: none; border: 0; background: transparent; color: rgb(var(--ts-gray-500)); font-size: 1.2rem; width: 30px; height: 30px; border-radius: 5px; }
.ts-sidebar-close:hover { background: rgb(var(--ts-gray-100)); color: rgb(var(--ts-gray-900)); }

.ts-nav { flex: 1; overflow-y: auto; padding: 0.9rem 0.6rem; display: flex; flex-direction: column; gap: 0.05rem; }
.ts-item {
  display: flex; align-items: center; gap: 0.55rem; padding: 0.32rem 0.45rem; border-radius: var(--ts-r);
  font-size: 0.74rem; font-weight: 500; color: rgb(var(--ts-gray-700)); transition: background-color 75ms;
}
.ts-item .mdi { font-size: 1.05rem; color: rgb(var(--ts-gray-500)); flex-shrink: 0; width: 1.05rem; text-align: center; }
.ts-item span { flex: 1; min-width: 0; white-space: normal; line-height: 1.2; overflow-wrap: normal; } /* labels wrap, never truncate */
.ts-item:hover { background: rgb(var(--ts-gray-100)); }
.ts-item--active { background: rgb(var(--ts-primary-50)); box-shadow: inset 2px 0 0 rgb(var(--ts-primary-500)); border-radius: 0 var(--ts-r) var(--ts-r) 0; color: rgb(var(--ts-primary-700)); }
.ts-item--active .mdi { color: rgb(var(--ts-primary-700)); }
.ts-item--muted { color: rgb(var(--ts-gray-500)); font-weight: 400; }
.ts-group { margin-top: 0.55rem; }
.ts-group + .ts-group { margin-top: 0.35rem; }
.ts-group[open] { margin-bottom: 0.4rem; }
.ts-group-label {
  list-style: none; display: flex; align-items: center; justify-content: space-between; cursor: pointer; user-select: none;
  padding: 0.2rem 0.45rem; font-size: 0.58rem; letter-spacing: 0.12em; text-transform: uppercase; font-weight: 600; color: rgb(var(--ts-gray-400));
}
.ts-group-label::-webkit-details-marker { display: none; }
.ts-group-label .mdi { font-size: 0.9rem; transition: transform 0.15s; }
.ts-group[open] > .ts-group-label .mdi { transform: rotate(180deg); }
.ts-group-label:hover { color: rgb(var(--ts-gray-700)); }
.ts-items { list-style: none; margin: 0.1rem 0 0; padding: 0; display: flex; flex-direction: column; gap: 0.05rem; }
.ts-nav-foot { margin-top: auto; padding-top: 0.75rem; }

.ts-roundel { display: flex; align-items: center; gap: 0.4rem; margin: 0.2rem 0.4rem 0.45rem; padding: 0.45rem 0.25rem 0; border-top: 1px solid var(--ts-hair); }
.ts-roundel-avatar { flex-shrink: 0; width: 1.6rem; height: 1.6rem; border-radius: 99px; background: rgb(var(--ts-gray-900)); color: #fff; font-size: 0.6rem; font-weight: 700; display: flex; align-items: center; justify-content: center; }
.ts-roundel-meta { display: flex; flex-direction: column; min-width: 0; flex: 1; }
.ts-roundel-name { font-size: 0.7rem; font-weight: 600; color: rgb(var(--ts-gray-800)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ts-roundel-email { font-size: 0.6rem; color: rgb(var(--ts-gray-400)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Tenant (company) name, between the user's name and email: one muted line, ellipsised. */
.ts-roundel-company { font-size: 0.62rem; font-weight: 600; color: rgb(var(--ts-gray-500)); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ts-roundel-logout button { border: 0; background: transparent; color: rgb(var(--ts-gray-400)); font-size: 1rem; display: flex; padding: 0.2rem; border-radius: 4px; }
.ts-roundel-logout button:hover { color: rgb(var(--ts-primary-600)); background: rgb(var(--ts-gray-100)); }

.ts-backdrop { position: fixed; inset: var(--ts-chrome-h) 0 0 0; background: rgb(20 28 46 / 0.45); z-index: 39; }
.ts-backdrop[hidden] { display: none; }

/* ---- Main column -------------------------------------------------------- */
.ts-main-ctn { padding-top: var(--ts-chrome-h); margin-left: var(--ts-sidebar-w); min-height: 100vh; transition: margin-left 0.2s ease; }
/* Desktop: the masthead burger collapses the sidebar off-canvas for screen real
   estate (state kept in localStorage by tibr-shell.js; body.ts-nav-collapsed is
   set before first paint by the inline script in partials.shell). */
@media (min-width: 960px) {
  body.ts-nav-collapsed .ts-sidebar { transform: translateX(-100%); }
  body.ts-nav-collapsed .ts-main-ctn { margin-left: 0; }
}
.ts-main { padding: 1rem 1.75rem 2.5rem; display: flex; flex-direction: column; gap: 0.9rem; }

/* Page header: breadcrumb (tracked caps) above the title; actions on the right */
.ts-header { display: flex; align-items: flex-end; justify-content: space-between; gap: 1rem; padding: 0 0 0.75rem; border-bottom: 1px solid var(--ts-hair); }
.ts-crumb { margin: 0 0 0.15rem; font-size: 0.66rem; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 600; color: rgb(var(--ts-gray-400)); }
.ts-crumb span { opacity: 0.4; margin: 0 0.3rem; }
.ts-crumb b { color: rgb(var(--ts-gray-700)); font-weight: 600; }
.ts-crumb a:hover { color: rgb(var(--ts-primary-600)); }
.ts-title { margin: 0; font-size: 1.05rem; font-weight: 600; letter-spacing: -0.015em; color: rgb(var(--ts-gray-950)); line-height: 1.3; }
/* Wraps: a page with more than one header action (badge + two buttons, e.g.
   Terminology) ran 430px wide and side-scrolled the whole page at 390. */
.ts-header-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; flex-shrink: 0; }

/* ---- Buttons ------------------------------------------------------------ */
.ts-btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.35rem; border: 0; border-radius: var(--ts-r);
  padding: 0.38rem 0.7rem; font-size: 0.74rem; font-weight: 600; line-height: 1.1rem; white-space: nowrap;
  transition: background-color 75ms, color 75ms; text-decoration: none;
}
.ts-btn .mdi { font-size: 0.95rem; }
.ts-btn:disabled { opacity: 0.5; cursor: default; }
.ts-btn-primary { background: rgb(var(--ts-gray-900)); color: #fff; }
.ts-btn-primary:hover:not(:disabled) { background: rgb(var(--ts-primary-700)); }
.ts-btn-gray { background: #fff; color: rgb(var(--ts-gray-800)); box-shadow: inset 0 0 0 1px var(--ts-hair-strong); }
.ts-btn-gray:hover:not(:disabled) { background: rgb(var(--ts-gray-50)); }
.ts-btn-ghost { background: transparent; color: rgb(var(--ts-gray-600)); }
.ts-btn-ghost:hover:not(:disabled) { background: rgb(var(--ts-gray-100)); color: rgb(var(--ts-gray-900)); }
.ts-btn-danger { background: #fff; color: #b3462e; box-shadow: inset 0 0 0 1px rgb(179 70 46 / 0.35); }
.ts-btn-danger:hover:not(:disabled) { background: #fbeeea; }
.ts-btn-sm { padding: 0.25rem 0.5rem; font-size: 0.68rem; }
.ts-icon-btn { width: 1.55rem; height: 1.55rem; border-radius: 4px; border: 0; background: transparent; color: rgb(var(--ts-gray-500)); display: inline-flex; align-items: center; justify-content: center; font-size: 0.95rem; }
.ts-icon-btn:hover:not(:disabled) { background: rgb(var(--ts-gray-100)); color: rgb(var(--ts-gray-900)); }
.ts-icon-btn.danger:hover:not(:disabled) { background: #fbeeea; color: #b3462e; }
.ts-icon-btn:disabled { opacity: 0.4; cursor: default; }
.ts-link { background: none; border: 0; padding: 0; font: inherit; font-size: 0.72rem; font-weight: 600; color: rgb(var(--ts-primary-600)); cursor: pointer; }
.ts-link:hover { text-decoration: underline; }

/* ---- Badges / notices / toast ------------------------------------------ */
.ts-badge { display: inline-flex; align-items: center; gap: 0.25rem; border-radius: 4px; padding: 0.05rem 0.4rem; font-size: 0.6rem; letter-spacing: 0.08em; text-transform: uppercase; font-weight: 600; line-height: 1.1rem; color: rgb(var(--ts-gray-600)); box-shadow: inset 0 0 0 1px var(--ts-hair-strong); white-space: nowrap; }
.ts-badge-success { background: #edf7f1; color: #1f7a52; box-shadow: inset 0 0 0 1px rgb(31 122 82 / 0.25); }
.ts-badge-info { background: rgb(var(--ts-primary-50)); color: rgb(var(--ts-primary-700)); box-shadow: inset 0 0 0 1px rgb(var(--ts-primary-500) / 0.25); }
.ts-notice { border: 1px solid var(--ts-hair); border-left: 3px solid rgb(var(--ts-primary-400)); background: #fff; border-radius: var(--ts-r); padding: 0.55rem 0.8rem; font-size: 0.74rem; color: rgb(var(--ts-gray-700)); }
.ts-notice-warn { border-left-color: #d97706; background: #fffbeb; color: #92400e; }
.ts-toast { position: fixed; top: calc(var(--ts-chrome-h) + 12px); right: 16px; z-index: 3100; padding: 0.5rem 0.9rem; border-radius: var(--ts-r); color: #fff; font-size: 0.78rem; font-weight: 600; box-shadow: 0 6px 18px rgb(20 28 46 / 0.2); }
.ts-toast-success { background: #1f7a52; }
.ts-toast-error { background: #b3462e; }

/* ---- Cards / sections --------------------------------------------------- */
.ts-card { background: #fff; border: 1px solid var(--ts-hair); border-radius: var(--ts-r); }
.ts-card-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.55rem 0.85rem; border-bottom: 1px solid var(--ts-hair); }
.ts-card-title { margin: 0; font-size: 0.8rem; font-weight: 600; color: rgb(var(--ts-gray-950)); }
.ts-card-body { padding: 0.8rem 0.85rem; }
.ts-muted { color: rgb(var(--ts-gray-500)); }
.ts-note { font-size: 0.68rem; color: rgb(var(--ts-gray-400)); margin: 0; }
.ts-note b { color: rgb(var(--ts-gray-600)); font-weight: 600; }

/* ---- Form fields -------------------------------------------------------- */
.ts-label { display: block; font-size: 0.68rem; font-weight: 600; color: rgb(var(--ts-gray-700)); margin-bottom: 0.3rem; }
.ts-input, .ts-select, .ts-textarea {
  display: block; width: 100%; font: inherit; font-size: 0.78rem; color: rgb(var(--ts-gray-900)); background: #fff;
  border: 1px solid var(--ts-hair-strong); border-radius: var(--ts-r); padding: 0.4rem 0.6rem; outline: none;
}
.ts-input:focus, .ts-select:focus, .ts-textarea:focus { border-color: rgb(var(--ts-primary-500)); box-shadow: 0 0 0 2px rgb(var(--ts-primary-500) / 0.15); }
.ts-input::placeholder { color: rgb(var(--ts-gray-400)); }
.ts-input:disabled, .ts-select:disabled { background: rgb(var(--ts-gray-50)); color: rgb(var(--ts-gray-500)); }

/* ---- Editable grid ------------------------------------------------------ */
.ts-grid-ctn { background: #fff; border: 1px solid var(--ts-hair); border-radius: var(--ts-r); overflow: hidden; }
.ts-grid-head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.6rem 0.85rem; border-bottom: 1px solid var(--ts-hair); }
.ts-grid-head p { margin: 0; font-size: 0.72rem; color: rgb(var(--ts-gray-500)); max-width: 72rem; line-height: 1.5; }
.ts-grid-head p a, .ts-grid-head p b { color: rgb(var(--ts-primary-600)); font-weight: 600; }
.ts-grid-head p b { color: rgb(var(--ts-gray-700)); }
.ts-grid-wrap { overflow-x: auto; }
.ts-grid { width: 100%; border-collapse: collapse; table-layout: fixed; }
.ts-grid th { text-align: left; padding: 0.4rem 0.6rem; font-size: 0.58rem; letter-spacing: 0.12em; text-transform: uppercase; font-weight: 600; color: rgb(var(--ts-gray-500)); background: rgb(var(--ts-gray-50)); border-bottom: 1px solid var(--ts-hair); white-space: nowrap; }
.ts-grid th.num, .ts-grid td.num { text-align: right; }
.ts-grid tbody tr { border-bottom: 1px solid var(--ts-hair); }
.ts-grid tbody tr:hover { background: rgb(var(--ts-primary-50) / 0.45); }
.ts-grid td { padding: 0.15rem 0.35rem; vertical-align: middle; height: 2.1rem; }
.ts-cell { display: block; width: 100%; border: 1px solid transparent; background: transparent; border-radius: 3px; padding: 0.3rem 0.35rem; font: inherit; font-size: 0.78rem; color: rgb(var(--ts-gray-900)); outline: none; min-height: 1.9rem; }
.ts-cell::placeholder { color: rgb(var(--ts-gray-400)); }
.ts-cell:hover:not(:disabled) { border-color: var(--ts-hair); }
.ts-cell:focus { background: #fff; border-color: rgb(var(--ts-primary-500)); box-shadow: 0 0 0 2px rgb(var(--ts-primary-500) / 0.15); }
.ts-cell:disabled { color: rgb(var(--ts-gray-600)); }
.ts-cell.num { text-align: right; font-variant-numeric: tabular-nums; font-family: var(--ts-mono); font-size: 0.74rem; }
.ts-cell.num::-webkit-outer-spin-button, .ts-cell.num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.ts-cell.num[type=number] { -moz-appearance: textfield; }
.ts-num-wrap { display: flex; align-items: center; justify-content: flex-end; gap: 0.25rem; }
.ts-num-wrap .unit { font-size: 0.68rem; color: rgb(var(--ts-gray-400)); padding-right: 0.35rem; }
.ts-default { display: inline-flex; align-items: center; gap: 0.35rem; font-size: 0.66rem; font-weight: 600; color: rgb(var(--ts-gray-500)); padding-left: 0.35rem; background: none; border: 0; cursor: pointer; font-family: inherit; }
.ts-default:disabled { cursor: default; }
.ts-default .ring { width: 0.85rem; height: 0.85rem; border-radius: 99px; border: 1.5px solid var(--ts-hair-strong); display: inline-flex; align-items: center; justify-content: center; flex-shrink: 0; }
.ts-default.on .ring { border-color: rgb(var(--ts-primary-600)); background: rgb(var(--ts-primary-600)); }
.ts-default.on .ring::after { content: ''; width: 0.3rem; height: 0.3rem; border-radius: 99px; background: #fff; }
.ts-default.on { color: rgb(var(--ts-primary-700)); }
.ts-default .set { opacity: 0; transition: opacity 0.12s; }
.ts-grid tbody tr:hover .ts-default:not(:disabled) .set { opacity: 1; }
.ts-row-actions { display: flex; justify-content: flex-end; gap: 0.15rem; opacity: 0; transition: opacity 0.12s; }
.ts-grid tbody tr:hover .ts-row-actions, .ts-grid tbody tr:focus-within .ts-row-actions, .ts-row-actions.always { opacity: 1; }
/* Section heading inside a grid: a tinted full-width row that labels the rows
   under it (registries that arrive grouped — Terminology, parameter sets). */
.ts-grid tr.ts-group, .ts-grid tbody tr.ts-group:hover { background: rgb(var(--ts-gray-50)); }
.ts-grid tr.ts-group td { height: auto; padding: 0.3rem 0.6rem; font-size: 0.58rem; letter-spacing: 0.12em; text-transform: uppercase; font-weight: 600; color: rgb(var(--ts-gray-500)); }
.ts-grid tr.ts-add { background: rgb(var(--ts-gray-50) / 0.6); }
.ts-grid tr.ts-add:hover { background: rgb(var(--ts-gray-50)); }
.ts-grid tr.ts-add td { height: 2.3rem; }
.ts-grid tr.ts-add .ts-cell { border-style: dashed; border-color: var(--ts-hair); background: #fff; }
.ts-grid tr.ts-add .ts-cell:focus { border-style: solid; }
.ts-grid-empty { padding: 1.2rem; text-align: center; font-size: 0.74rem; color: rgb(var(--ts-gray-400)); }
.ts-grid-foot { display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.45rem 0.85rem; font-size: 0.68rem; color: rgb(var(--ts-gray-500)); background: rgb(var(--ts-gray-50)); border-top: 1px solid var(--ts-hair); flex-wrap: wrap; }
.ts-grid-foot b { color: rgb(var(--ts-gray-700)); font-weight: 600; }
.ts-grid-foot .status { display: inline-flex; align-items: center; gap: 0.35rem; }
.ts-grid-foot .status i { width: 6px; height: 6px; border-radius: 99px; background: #1f7a52; display: inline-block; }
.ts-grid-foot .actions { display: flex; align-items: center; gap: 0.5rem; }

/* ---- Responsive --------------------------------------------------------- */
@media (max-width: 959.98px) {
  .ts-burger { display: flex; }
  .ts-sidebar { transform: translateX(-100%); box-shadow: none; width: min(18rem, 86vw); }
  body.ts-nav-open .ts-sidebar { transform: none; box-shadow: 0 0 40px rgb(20 28 46 / 0.25); }
  body.ts-nav-open { overflow: hidden; }
  .ts-sidebar-close { display: flex; align-items: center; justify-content: center; }
  .ts-main-ctn { margin-left: 0; }
  .ts-main { padding: 0.9rem 1rem 2rem; }
  .ts-item { padding: 0.5rem 0.55rem; font-size: 0.82rem; }
  .ts-item .mdi { font-size: 1.15rem; }
  .ts-group-label { padding: 0.35rem 0.55rem; font-size: 0.62rem; }
  .ts-row-actions { opacity: 1; }
  .ts-default .set { opacity: 1; }
}
/* Tablet: the seven Area tabs need ~709px at desktop spacing, so at 768–900 the
   strip overflowed by a few px and "Settings" faded out tucked against the "?"
   help button. Tighten tab padding + tracking so the full strip fits from ~730px
   up; below that it still scrolls with the edge fade. */
@media (min-width: 600px) and (max-width: 959.98px) {
  .ts-area { padding: 0 0.42rem; letter-spacing: 0.05em; gap: 0.3rem; }
  .ts-mast-right { padding-left: 0.75rem; }
}
@media (max-width: 599.98px) {
  .ts-header { flex-direction: column; align-items: flex-start; gap: 0.5rem; }
  .ts-mast-right .ts-mast-chip { display: none; }
  /* Grid → stacked cards: each row a card, each cell labelled by data-label */
  .ts-grid-wrap { overflow: visible; }
  .ts-grid, .ts-grid tbody, .ts-grid tr, .ts-grid td { display: block; width: 100%; }
  .ts-grid thead { display: none; }
  .ts-grid tbody tr { padding: 0.5rem 0.65rem 0.6rem; border-bottom: 1px solid var(--ts-hair); }
  .ts-grid tbody tr:hover { background: transparent; }
  .ts-grid td { height: auto; padding: 0.3rem 0; text-align: left !important; }
  .ts-grid td[data-label]::before { content: attr(data-label); display: block; font-size: 0.58rem; letter-spacing: 0.12em; text-transform: uppercase; font-weight: 600; color: rgb(var(--ts-gray-500)); margin-bottom: 0.1rem; }
  .ts-grid td.num .ts-num-wrap { justify-content: flex-start; }
  /* 2.5rem, not 2.4 — measured 38px, 2px short of the 40px tap target on every
     converted grid (Tax Rates, Payment Terms, Project Types, Team Names too). */
  .ts-cell { border-color: var(--ts-hair); min-height: 2.5rem; font-size: 0.9rem; }
  .ts-cell.num { text-align: left; }
  .ts-row-actions { justify-content: flex-start; gap: 0.5rem; padding-top: 0.25rem; }
  .ts-icon-btn { width: 2.5rem; height: 2.5rem; font-size: 1.15rem; box-shadow: inset 0 0 0 1px var(--ts-hair-strong); }
  /* Text buttons clear 40px too — the padding alone leaves them at 30px, which
     is short of the tap target on a phone (header actions, grid Add, footer
     reset). Same treatment .ts-icon-btn already gets above. */
  .ts-btn { min-height: 2.5rem; }
  /* ...and across. min-height alone left a short label under the tap target:
     the grid's inline-add "Add" measured 37px wide at 390 on every page with
     an add row (Tax Rates, Project Managers). 2.5rem = 40px either way. */
  .ts-btn { min-width: 2.5rem; }
  /* The breadcrumb link is 15px of text; give it a 40px hit area without
     drawing anything, the way .ts-switch does. */
  .ts-crumb a { position: relative; }
  .ts-crumb a::before {
    content: ''; position: absolute; top: 50%; left: 0; transform: translateY(-50%);
    width: 100%; height: 2.5rem;
  }
  /* 2.5rem = 40px, matching .ts-btn / .ts-icon-btn / .ts-cell. At 2.4rem this
     measured 38px on a phone — 2px under the tap target — on every grid that
     carries a default picker (Tax Rates). */
  .ts-default { min-height: 2.5rem; font-size: 0.8rem; }
  /* A section heading stays a heading in card view — a label, not a card. */
  .ts-grid tr.ts-group { padding: 0.55rem 0.65rem 0.15rem; border-bottom: 0; }
  .ts-grid tr.ts-group td { padding: 0; }
  .ts-grid tr.ts-add td { height: auto; }
  .ts-grid-foot { flex-direction: column; align-items: flex-start; }
}
@media (min-width: 960px) {
  .ts-sidebar-close { display: none; }
}

/* ---- Modals -------------------------------------------------------------- */
/* House dialog: a ts-card in an overlay. Scrolls its own body so a tall form
   (payment-plan stages) never pushes the footer buttons off screen, and drops
   to full width on phones. */
.ts-modal { position: fixed; inset: 0; z-index: 3000; display: flex; align-items: flex-start; justify-content: center; padding: 1rem; overflow-y: auto; background: rgb(20 28 46 / 0.45); }
.ts-modal-card { display: flex; flex-direction: column; width: 100%; max-width: 34rem; margin: auto; max-height: calc(100vh - 2rem); background: #fff; border: 1px solid var(--ts-hair); border-radius: var(--ts-r); }
.ts-modal-lg { max-width: 62rem; }
.ts-modal-head { flex-shrink: 0; display: flex; align-items: center; justify-content: space-between; gap: 1rem; padding: 0.55rem 0.85rem; border-bottom: 1px solid var(--ts-hair); }
.ts-modal-title { margin: 0; font-size: 0.8rem; font-weight: 600; color: rgb(var(--ts-gray-950)); }
/* A title long enough to wrap squeezed the close button to 39px wide on a phone. */
.ts-modal-head .ts-icon-btn { flex-shrink: 0; }
.ts-modal-body { flex: 1; min-height: 0; overflow-y: auto; padding: 0.8rem 0.85rem; }
.ts-modal-foot { flex-shrink: 0; display: flex; align-items: center; justify-content: flex-end; gap: 0.5rem; padding: 0.55rem 0.85rem; border-top: 1px solid var(--ts-hair); background: rgb(var(--ts-gray-50)); }
.ts-modal-foot .spacer { margin-right: auto; }
.ts-modal-text { margin: 0; font-size: 0.78rem; line-height: 1.55; color: rgb(var(--ts-gray-700)); white-space: pre-line; }
@media (max-width: 599.98px) {
  .ts-modal { padding: 0.5rem; align-items: stretch; }
  .ts-modal-card { max-height: calc(100vh - 1rem); }
  .ts-modal-foot { flex-wrap: wrap; }
  .ts-modal-foot .ts-btn { min-height: 2.4rem; flex: 1 1 auto; justify-content: center; }
  /* The rule above outranks the generic .ts-btn min-height and left dialog
     buttons at 38px — 2px short of the tap target on every page with a house
     modal (My Company, Payment Plans, Brand Kit, Project Managers). */
  .ts-modal-foot .ts-btn { min-height: 2.5rem; }
  /* KothingEditor (Spec Library, the quote page's Edit for this quote) draws its
     toolbar at 30x28 — under the tap target. The tray already wraps, so bigger
     buttons just take another row. Has to out-rank the vendor's own
     `.kothing-editor .ke-menu-list li .ke-btn { min-width: 30px }`. */
  .ts-modal .kothing-editor .ke-menu-list li .ke-btn,
  .ts-modal .kothing-editor .ke-menu-list li .ke-btn-select { min-width: 2.5rem; min-height: 2.5rem; height: 2.5rem; line-height: 2.5rem; }
}

/* ---- Additions (2026-09-04) ---------------------------------------------- */
/* The Tailwind forms reset strips the native select arrow, so a single-choice
   .ts-select would read as a text input. (Multi-selects — the Tax Rates state
   picker — are a listbox and keep the plain box.) */
select.ts-select:not([multiple]) {
  padding-right: 1.6rem;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23737477' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.45rem center;
  background-size: 0.8rem;
}

/* A grid with too many columns to survive tablet width can opt into the phone
   card layout early: .ts-grid-md-cards stacks from 959.98px down (the <600
   rules in Responsive above already cover phones for every grid). */
@media (min-width: 600px) and (max-width: 959.98px) {
  .ts-grid-md-cards-wrap { overflow: visible; }
  .ts-grid.ts-grid-md-cards, .ts-grid.ts-grid-md-cards tbody,
  .ts-grid.ts-grid-md-cards tr, .ts-grid.ts-grid-md-cards td { display: block; width: 100%; }
  .ts-grid.ts-grid-md-cards thead { display: none; }
  .ts-grid.ts-grid-md-cards tbody tr { padding: 0.5rem 0.85rem 0.6rem; border-bottom: 1px solid var(--ts-hair); }
  .ts-grid.ts-grid-md-cards tbody tr:hover { background: transparent; }
  .ts-grid.ts-grid-md-cards td { height: auto; padding: 0.3rem 0; text-align: left !important; }
  .ts-grid.ts-grid-md-cards td[data-label]::before { content: attr(data-label); display: block; font-size: 0.58rem; letter-spacing: 0.12em; text-transform: uppercase; font-weight: 600; color: rgb(var(--ts-gray-500)); margin-bottom: 0.1rem; }
  .ts-grid.ts-grid-md-cards td.num .ts-num-wrap { justify-content: flex-start; }
  .ts-grid.ts-grid-md-cards .ts-cell.num { text-align: left; }
  .ts-grid.ts-grid-md-cards .ts-row-actions { justify-content: flex-start; gap: 0.5rem; padding-top: 0.25rem; }
  .ts-grid.ts-grid-md-cards .ts-icon-btn { width: 2.5rem; height: 2.5rem; font-size: 1.15rem; box-shadow: inset 0 0 0 1px var(--ts-hair-strong); }
}

/* ---- Additions (2026-09-05) ---------------------------------------------- */
/* House switch: the on/off control for a single setting (Team Names' "Use
   Teams"). Flat like everything else — a hairline track that fills with the
   brand primary when on, no gradient, no shadow. Sits in a .ts-setting-row so
   the label/description and the switch line up inside a .ts-card-body. */
.ts-switch {
  position: relative; flex-shrink: 0; width: 2.15rem; height: 1.2rem; padding: 0; border: 0;
  border-radius: 99px; background: rgb(var(--ts-gray-300)); transition: background-color 120ms;
}
.ts-switch::after {
  content: ''; position: absolute; top: 2px; left: 2px; width: 0.85rem; height: 0.85rem;
  border-radius: 99px; background: #fff; transition: transform 120ms;
}
.ts-switch.on { background: rgb(var(--ts-primary-600)); }
.ts-switch.on::after { transform: translateX(0.95rem); }
.ts-switch:focus-visible { outline: 2px solid rgb(var(--ts-primary-500) / 0.5); outline-offset: 2px; }
.ts-switch:disabled { opacity: 0.5; cursor: default; }

/* A labelled setting line: text on the left, control on the right. */
.ts-setting-row { display: flex; align-items: center; justify-content: space-between; gap: 1rem; }
.ts-setting-row .label { font-size: 0.78rem; font-weight: 600; color: rgb(var(--ts-gray-900)); }
.ts-setting-row .help { margin: 0.1rem 0 0; font-size: 0.72rem; color: rgb(var(--ts-gray-500)); line-height: 1.5; }
@media (max-width: 599.98px) {
  /* Bigger tap target on phones, same flat look. The track only needs to be
     24px tall to read well, so ::before extends the hit area to 44px without
     drawing anything — the visual stays flat, the thumb target clears 40px. */
  .ts-switch { width: 2.6rem; height: 1.5rem; }
  .ts-switch::after { width: 1.1rem; height: 1.1rem; top: 3px; left: 3px; }
  .ts-switch.on::after { transform: translateX(1.1rem); }
  .ts-switch::before {
    content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: calc(100% + 0.5rem); height: 2.75rem;
  }
}

/* ---- Additions (2026-09-06) ---------------------------------------------- */
/* The toast is confirmation, never a control — but anchored top-right it landed
   exactly on .ts-header-actions, so for its four seconds elementFromPoint over
   a page's primary button returned the toast and a second save click was
   swallowed. Move it to the bottom-right, which every appv3 page leaves empty
   (the old floating help launcher is display:none here), and make it
   click-through so it can never intercept anything again. */
.ts-toast { top: auto; bottom: 16px; pointer-events: none; }

/* Tap targets on phones, round two (found measuring Regional at 390x844).
   Last night's pass covered .ts-icon-btn, .ts-btn and .ts-crumb a; these were
   still short.

   1. The house form controls. .ts-cell (the grid's inline input) was raised to
      2.4rem for phones, but .ts-input / .ts-select / .ts-textarea — what an
      ordinary settings form is built from — stayed at 34px. Regional is seven
      of them, so they grow to a real 40px.
   2. The masthead and sidebar chrome. These are sized to the 40px masthead and
      look right as they are, so they keep their drawn size and gain a 40px
      hit area the way .ts-switch does — nothing moves, nothing is redrawn. */
@media (max-width: 599.98px) {
  .ts-input, .ts-select, .ts-textarea { min-height: 2.5rem; }

  .ts-area, .ts-burger, .ts-mast-btn, .ts-mast-avatar,
  .ts-sidebar-close, .ts-brand, .ts-roundel-logout button { position: relative; }
  .ts-area::before, .ts-burger::before, .ts-mast-btn::before, .ts-mast-avatar::before,
  .ts-sidebar-close::before, .ts-brand::before, .ts-roundel-logout button::before {
    content: ''; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);
    width: max(100%, 2.5rem); height: 2.5rem;
  }
}

/* ---- Additions (2026-09-06, My Company) ---------------------------------- */
/* Inline form validation. The house form had a label, an input and a note, but
   no way to say "this field is wrong" — every page that validates on save was
   about to invent its own red text. First used by My Company's add/edit
   dialog; Freelancer Login, FX Rates and API Connections have the same need. */
.ts-req { color: #b3462e; font-weight: 700; }
.ts-field-err { display: block; margin-top: 0.25rem; font-size: 0.66rem; font-weight: 600; color: #b3462e; }
.ts-input.is-err, .ts-select.is-err, .ts-textarea.is-err { border-color: #b3462e; }
.ts-input.is-err:focus, .ts-select.is-err:focus, .ts-textarea.is-err:focus {
  border-color: #b3462e; box-shadow: 0 0 0 2px rgb(179 70 46 / 0.15);
}

/* Tap targets, round three (found measuring My Company at 390x844). In-prose
   links — the "Regional" cross-reference in a grid's head paragraph, the links
   in a .ts-note — sit on a 16px line and cannot grow without wrecking the
   paragraph, so they take the .ts-crumb a treatment: the drawn link is
   untouched and gains a 40px-tall hit area. Kept to the link's own width so it
   never reaches sideways over the words either side of it. Shared: Tax Rates
   and Payment Plans carry the same links. */
@media (max-width: 599.98px) {
  .ts-grid-head p a, .ts-note a { position: relative; display: inline-block; }
  .ts-grid-head p a::before, .ts-note a::before {
    content: ''; position: absolute; top: 50%; left: 0; transform: translateY(-50%);
    width: 100%; height: 2.5rem;
  }
}

/* ---- Additions (2026-09-07, Brand Kit) ----------------------------------- */
/* A colour field. The native swatch sits beside the hex box and the two read as
   one control: same hairline, same radius, and the swatch matches the height of
   a .ts-input so a colour row lines up with the text rows either side of it.
   The house form could ask for a name, a number or a choice but had no way to
   ask for a colour — Document Themes and Document Styles want the same pair. */
.ts-color { display: flex; align-items: center; gap: 0.4rem; }
.ts-color input[type="color"] {
  flex-shrink: 0; width: 2.4rem; height: 2rem; padding: 2px; background: #fff; cursor: pointer;
  border: 1px solid var(--ts-hair-strong); border-radius: var(--ts-r);
}
.ts-color input[type="color"]::-webkit-color-swatch-wrapper { padding: 0; }
.ts-color input[type="color"]::-webkit-color-swatch { border: 0; border-radius: 3px; }
.ts-color input[type="color"]::-moz-color-swatch { border: 0; border-radius: 3px; }
.ts-color input[type="color"]:disabled { opacity: 0.5; cursor: default; }
.ts-color .ts-input { flex: 1; min-width: 0; font-family: var(--ts-mono); font-size: 0.74rem; }

/* A read-only colour chip: shows a colour inline beside its hex, e.g. a
   suggestion the user has not accepted yet. */
.ts-swatch {
  display: inline-block; flex-shrink: 0; width: 0.9rem; height: 0.9rem; border-radius: 3px;
  border: 1px solid var(--ts-hair-strong); vertical-align: -0.1rem;
}

/* Machine values in prose — a hex, an id, a path. */
.ts-mono { font-family: var(--ts-mono); font-size: 0.72rem; color: rgb(var(--ts-gray-700)); }

@media (max-width: 599.98px) {
  /* The swatch opens the OS colour picker, so it is a tap target like the box
     next to it. 2.5rem square, matching .ts-input's phone height. */
  .ts-color input[type="color"] { width: 2.5rem; height: 2.5rem; }
}

/* ---- Additions (2026-09-08) ---------------------------------------------- */
/* Stat tiles: a short row of read-only counters above a settings page (Quote
   Numbering's adoption snapshot). Flat like a .ts-card, tabular figures, and
   an auto-fill track so three tiles become one column on a phone without any
   per-page media query. AI Usage and the Governance pages want the same. */
.ts-stats {
  display: grid; gap: 0.6rem; margin-bottom: 1rem;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
}
.ts-stat { padding: 0.75rem 0.9rem; background: #fff; border: 1px solid var(--ts-hair); border-radius: var(--ts-r); }
.ts-stat .k { font-size: 0.62rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: rgb(var(--ts-gray-500)); }
.ts-stat .v {
  margin-top: 0.15rem; font-size: 1.4rem; font-weight: 600; line-height: 1.15;
  color: rgb(var(--ts-gray-900)); font-variant-numeric: tabular-nums;
}
.ts-stat .v small { font-size: 0.78rem; font-weight: 400; color: rgb(var(--ts-gray-500)); }
.ts-stat .s { margin: 0.15rem 0 0; font-size: 0.68rem; color: rgb(var(--ts-gray-500)); line-height: 1.45; }

/* Segmented control: two or three mutually exclusive choices for one setting
   (Manual / AI on each Quote Numbering card). One hairline box, the chosen
   segment fills; replaces Vuetify's v-btn-toggle. */
.ts-seg { display: inline-flex; flex-shrink: 0; border: 1px solid var(--ts-hair-strong); border-radius: var(--ts-r); overflow: hidden; }
.ts-seg button {
  min-height: 1.9rem; padding: 0.2rem 0.7rem; border: 0; background: #fff; cursor: pointer;
  font: inherit; font-size: 0.72rem; font-weight: 600; color: rgb(var(--ts-gray-600));
}
.ts-seg button + button { border-left: 1px solid var(--ts-hair-strong); }
.ts-seg button:hover:not(.on) { background: rgb(var(--ts-gray-50)); }
.ts-seg button.on { background: rgb(var(--ts-gray-900)); color: #fff; }
.ts-seg button:focus-visible { outline: 2px solid rgb(var(--ts-primary-500) / 0.5); outline-offset: -2px; }

/* A worked example of what a rule produces — the "next five" strip under each
   convention card. Machine output, so monospace and quietly tinted. */
.ts-preview { padding: 0.55rem 0.7rem; background: rgb(var(--ts-gray-50)); border: 1px solid var(--ts-hair); border-radius: var(--ts-r); }
.ts-preview .k { font-size: 0.62rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase; color: rgb(var(--ts-gray-500)); }
.ts-preview .vals { margin-top: 0.25rem; font-family: var(--ts-mono); font-size: 0.72rem; color: rgb(var(--ts-gray-800)); overflow-wrap: anywhere; }
.ts-preview .vals .sep { color: rgb(var(--ts-gray-400)); }

@media (max-width: 599.98px) {
  /* Segments are tap targets like every other button on the page. */
  .ts-seg button { min-height: 2.5rem; min-width: 3.5rem; }
}

@media (max-width: 599.98px) {
  /* The settings accordion headers are the control that opens each group in
     the drawer, so they are tap targets like every item under them. They
     measured 33px on every converted page — the first phone measurement that
     opened the drawer and walked the sidebar caught it. */
  .ts-group-label { min-height: 2.5rem; }
}

/* ---- Chip picker -------------------------------------------------------- */
/* A type-to-filter list that reads back as chips. Sits in a grid cell like an
   .ts-cell does, so it hovers to an outline and focuses to the primary ring.
   The panel is position:fixed — the page places it off the control's rect so
   .ts-grid-wrap's overflow can never clip it. Added for Default Authoriser;
   Governance and Valuations want the same control. */
.ts-picker { position: relative; }
.ts-picker-control {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0.25rem; width: 100%; min-height: 1.9rem;
  border: 1px solid transparent; border-radius: 3px; background: transparent; padding: 0.25rem 1.25rem 0.25rem 0.35rem;
  font: inherit; font-size: 0.78rem; color: rgb(var(--ts-gray-900)); text-align: left; cursor: pointer; position: relative;
}
.ts-picker-control:hover:not(:disabled) { border-color: var(--ts-hair); }
.ts-picker-control:focus-visible, .ts-picker.open .ts-picker-control {
  outline: none; background: #fff; border-color: rgb(var(--ts-primary-500)); box-shadow: 0 0 0 2px rgb(var(--ts-primary-500) / 0.15);
}
.ts-picker-control:disabled { opacity: 0.5; cursor: default; }
.ts-picker-control .caret { position: absolute; right: 0.3rem; top: 50%; transform: translateY(-50%); font-size: 0.9rem; color: rgb(var(--ts-gray-400)); }
.ts-picker.open .ts-picker-control .caret { transform: translateY(-50%) rotate(180deg); }
.ts-picker-ph { color: rgb(var(--ts-gray-400)); }
.ts-chip {
  display: inline-flex; align-items: center; gap: 0.15rem; border-radius: 3px; padding: 0.05rem 0.1rem 0.05rem 0.35rem;
  font-size: 0.7rem; font-weight: 600; color: rgb(var(--ts-gray-700)); background: #fff; box-shadow: inset 0 0 0 1px var(--ts-hair-strong);
}
.ts-chip button { border: 0; background: none; padding: 0 0.15rem; color: rgb(var(--ts-gray-400)); cursor: pointer; display: inline-flex; align-items: center; justify-content: center; font-size: 0.8rem; }
.ts-chip button:hover { color: #b3462e; }
.ts-picker-panel {
  position: fixed; z-index: 1500; display: flex; flex-direction: column; background: #fff;
  border: 1px solid var(--ts-hair-strong); border-radius: var(--ts-r); box-shadow: 0 8px 24px rgb(20 28 46 / 0.16); overflow: hidden;
}
.ts-picker-search { display: flex; align-items: center; gap: 0.35rem; padding: 0.3rem 0.5rem; border-bottom: 1px solid var(--ts-hair); color: rgb(var(--ts-gray-400)); flex-shrink: 0; }
.ts-picker-search input { border: 0; outline: 0; width: 100%; font: inherit; font-size: 0.78rem; color: rgb(var(--ts-gray-900)); background: transparent; }
.ts-picker-list { overflow-y: auto; padding: 0.2rem; min-height: 0; }
.ts-picker-opt {
  display: flex; align-items: center; gap: 0.4rem; width: 100%; border: 0; background: none; padding: 0.35rem 0.4rem;
  border-radius: 3px; font: inherit; font-size: 0.78rem; color: rgb(var(--ts-gray-700)); text-align: left; cursor: pointer;
}
.ts-picker-opt:hover { background: rgb(var(--ts-gray-50)); }
.ts-picker-opt.on { color: rgb(var(--ts-primary-700)); font-weight: 600; }
.ts-picker-opt .mdi { color: rgb(var(--ts-gray-400)); font-size: 0.95rem; flex-shrink: 0; }
.ts-picker-opt.on .mdi { color: rgb(var(--ts-primary-600)); }
.ts-picker-empty { padding: 0.6rem; text-align: center; font-size: 0.72rem; color: rgb(var(--ts-gray-400)); }
/* In an inline add row the picker reads as "empty and waiting", like .ts-cell. */
.ts-grid tr.ts-add .ts-picker-control { border-style: dashed; border-color: var(--ts-hair); background: #fff; }
.ts-grid tr.ts-add .ts-picker.open .ts-picker-control { border-style: solid; }

@media (max-width: 599.98px) {
  /* Every part of the picker a finger lands on clears 40px: the control, each
     option, the search box and a chip's remove button. */
  .ts-picker-control { min-height: 2.5rem; font-size: 0.9rem; border-color: var(--ts-hair); }
  .ts-picker-opt { min-height: 2.5rem; font-size: 0.85rem; }
  .ts-picker-search { min-height: 2.5rem; }
  .ts-chip { font-size: 0.8rem; padding-left: 0.5rem; }
  .ts-chip button { min-width: 2.5rem; min-height: 2.5rem; font-size: 1rem; }
}

/* ---- Tree (hierarchical list) ------------------------------------------
   Added 2026-09-09 for Settings › Default Quote Items, the first page whose
   content is a parent/child tree rather than a flat grid. Rows WRAP instead of
   truncating: on the old page every item name in the 1/3-width column was
   ellipsised to nothing at 1440, and the row controls were clipped off the
   right edge. Cost Category Templates and the Spec Library want the same list. */
.ts-tree {
  display: flex; flex-direction: column; gap: 0.25rem; padding: 0.3rem; overflow-y: auto; outline: none;
  border: 1px solid var(--ts-hair); border-radius: var(--ts-r); background: rgb(var(--ts-gray-50));
}
.ts-tree:focus-visible { box-shadow: 0 0 0 2px rgb(var(--ts-primary-500) / 0.3); }
.ts-tree-row {
  display: flex; align-items: center; flex-wrap: wrap; gap: 0.25rem; padding: 0.2rem 0.3rem;
  background: #fff; border: 1px solid var(--ts-hair); border-radius: 4px; cursor: pointer;
}
.ts-tree-row:hover { border-color: var(--ts-hair-strong); }
.ts-tree-row.sel { border-color: rgb(var(--ts-primary-500)); background: rgb(var(--ts-primary-50) / 0.5); }
.ts-tree-row.off .ts-tree-name { color: rgb(var(--ts-gray-400)); font-weight: 400; }
.ts-tree-rail { display: inline-flex; flex-shrink: 0; color: rgb(var(--ts-gray-300)); }
.ts-tree-rail > span { width: 0.9rem; height: 1.4rem; display: inline-flex; align-items: center; justify-content: center; font-size: 0.72rem; }
.ts-tree-rail > span.line { border-left: 1px solid var(--ts-hair); }
.ts-tree-name { flex: 1 1 7rem; min-width: 0; font-size: 0.76rem; font-weight: 600; color: rgb(var(--ts-gray-900)); overflow-wrap: anywhere; }
.ts-tree-actions { display: inline-flex; align-items: center; gap: 0.1rem; margin-left: auto; flex-shrink: 0; }
.ts-tree-empty { padding: 1.2rem 0.6rem; text-align: center; font-size: 0.74rem; color: rgb(var(--ts-gray-400)); }
.ts-tree-empty b { color: rgb(var(--ts-gray-600)); font-weight: 600; }
@media (max-width: 599.98px) {
  /* Give the name its own line so the 40px controls sit under it, never
     squeezing it back to an ellipsis. */
  .ts-tree-name { flex-basis: 100%; order: -1; padding: 0.15rem 0.1rem 0; }
  .ts-tree-actions { margin-left: auto; }
}

/* Key cap for a keyboard-shortcut hint (Alt + arrows on the tree). */
.ts-kbd {
  display: inline-block; min-width: 1.15rem; padding: 0 0.25rem; margin: 0 0.05rem;
  border: 1px solid var(--ts-hair-strong); border-bottom-width: 2px; border-radius: 3px; background: #fff;
  font-family: var(--ts-mono); font-size: 0.62rem; font-weight: 600; line-height: 1.15rem;
  color: rgb(var(--ts-gray-600)); text-align: center;
}

/* ---- Additions (2026-09-09) ---------------------------------------------- */
/* A <select> that lives in a grid cell. .ts-cell strips the box down to a
   transparent field that outlines on hover, but the Tailwind forms reset also
   strips the native arrow — so a picker read as plain text. Draw the same
   chevron .ts-select uses, sized for the cell. Team Names hand-rolled this as
   .tm-select; Cost Category Templates has four such columns and Valuations and
   the Spec Library want the same. */
select.ts-cell {
  padding-right: 1.5rem;
  background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23737477' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M4 6l4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.35rem center;
  background-size: 0.8rem;
}

/* ---- Additions (2026-09-10) ---------------------------------------------- */
/* A tick box for selecting rows in a grid. The native control is 13px — far
   under the 40px tap target — and a pseudo-element on an <input> is not
   reliable, so the box lives inside a label that IS the hit area (clicking the
   label toggles the input natively). Excel Quote Print Templates selects rows
   this way; Word Quote Print Templates and the other template pages do too. */
.ts-check {
  display: inline-flex; align-items: center; justify-content: center;
  width: 1.5rem; height: 1.5rem; margin: 0; cursor: pointer;
}
.ts-check input[type="checkbox"] {
  width: 0.95rem; height: 0.95rem; margin: 0; cursor: pointer;
  accent-color: rgb(var(--ts-primary-600));
}
@media (max-width: 599.98px) {
  .ts-check { width: 2.5rem; height: 2.5rem; }
  .ts-check input[type="checkbox"] { width: 1.15rem; height: 1.15rem; }
}

/* A fold: a titled section of a long form that opens and closes. Flat like
   everything else — a hairline rule and a tracked uppercase title, no card
   inside a card. The Excel print settings are 200 fields in five folds; the
   MCP and Furniture Import Mapping pages have the same shape. */
.ts-fold { border-top: 1px solid var(--ts-hair); }
.ts-fold:first-child { border-top: 0; }
.ts-fold-head {
  display: flex; width: 100%; align-items: center; justify-content: space-between; gap: 0.5rem;
  padding: 0.72rem 0; background: none; border: 0; font: inherit; text-align: left; cursor: pointer;
}
.ts-fold-head .t {
  font-size: 0.72rem; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: rgb(var(--ts-gray-900));
}
.ts-fold-head .mdi { font-size: 1.05rem; color: rgb(var(--ts-gray-400)); }
.ts-fold-head:hover .t, .ts-fold-head:hover .mdi { color: rgb(var(--ts-primary-600)); }
.ts-fold-head:focus-visible { outline: 2px solid rgb(var(--ts-primary-500) / 0.5); outline-offset: 2px; }
.ts-fold-body { padding: 0.15rem 0 1.1rem; }
@media (max-width: 599.98px) {
  .ts-fold-head { min-height: 2.5rem; }
}

/* ---- Additions (2026-09-12) ---------------------------------------------- */
/* A refused field inside an editable grid. `.ts-input.is-err` above only ever
   covered the boxed form controls, so a red field in a .ts-grid cell — the
   Scope Templates level and column grids, the PO Templates mappings — drew no
   border at all and only the message underneath said anything was wrong. */
.ts-cell.is-err { border-color: #b3462e; background: rgb(179 70 46 / 0.04); }
.ts-cell.is-err:focus { border-color: #b3462e; box-shadow: 0 0 0 2px rgb(179 70 46 / 0.15); }

/* ---- Additions (2026-09-17, Valuations) --------------------------------- */
/* A radio list: one choice from a few, each with a sentence or two saying what
   it does (Valuations' three ways to carry internal variations). .ts-seg only
   fits short labels; this is the same idea for options that need explaining.
   The whole row is the <label>, so the row is the hit area on a phone and
   clicking anywhere on it picks the option natively. The Tailwind forms reset
   is commented out in app.css, so the radio is native — accent-color takes it off the browser's blue and onto the brand. Sync Connections
   and Connections have radio groups of the same shape. */
.ts-choices { display: flex; flex-direction: column; gap: 0.4rem; }
.ts-choice {
  display: flex; align-items: flex-start; gap: 0.65rem; margin: 0; padding: 0.6rem 0.75rem; cursor: pointer;
  background: #fff; border: 1px solid var(--ts-hair); border-radius: var(--ts-r); transition: border-color 75ms, background-color 75ms;
}
.ts-choice:hover { border-color: var(--ts-hair-strong); }
.ts-choice.on { border-color: rgb(var(--ts-primary-500)); background: rgb(var(--ts-primary-50) / 0.5); }
.ts-choice:focus-within { box-shadow: 0 0 0 2px rgb(var(--ts-primary-500) / 0.15); }
.ts-choice.is-disabled { cursor: default; }
.ts-choice.is-disabled:hover:not(.on) { border-color: var(--ts-hair); }
.ts-choice input[type="radio"] {
  flex-shrink: 0; width: 0.95rem; height: 0.95rem; margin: 0.12rem 0 0; cursor: inherit;
  accent-color: rgb(var(--ts-primary-600));
}
.ts-choice input[type="radio"]:focus { box-shadow: none; outline: none; }
.ts-choice .t { display: block; font-size: 0.78rem; font-weight: 600; color: rgb(var(--ts-gray-900)); }
.ts-choice .d { display: block; margin-top: 0.15rem; font-size: 0.72rem; color: rgb(var(--ts-gray-600)); line-height: 1.5; }
@media (max-width: 599.98px) {
  .ts-choice { min-height: 2.5rem; padding: 0.7rem 0.75rem; }
  .ts-choice input[type="radio"] { width: 1.15rem; height: 1.15rem; margin-top: 0.05rem; }
  .ts-choice .t { font-size: 0.85rem; }
}
/* The breadcrumb's "Settings" link is 15px tall — every converted page's one
   control under the 40px tap target on a phone. Additive: a ::before hit area
   the way .ts-switch does it, so nothing moves and nothing is redrawn.
   (2026-09-17, measured on Digital Workforce at 390.) */
@media (max-width: 599.98px) {
  .ts-crumb a { position: relative; display: inline-block; }
  .ts-crumb a::before {
    content: ""; position: absolute; left: -0.5rem; right: -0.5rem;
    top: 50%; transform: translateY(-50%); height: 2.5rem;
  }
}

/* A progress meter: how much of an allowance is used. Flat and hairline like
   everything else — a 6px track, a tinted fill, no gradient or shadow. Green
   until 80%, amber past it, red once the allowance is spent. Added for the
   AI Usage monthly cap (2026-09-24); additive, nothing above changes. */
.ts-meter { height: 6px; border-radius: 99px; background: rgb(var(--ts-gray-200)); overflow: hidden; }
.ts-meter > span { display: block; height: 100%; border-radius: 99px; background: #1f7a52; transition: width 0.2s ease; }
.ts-meter.warn > span { background: #b8860b; }
.ts-meter.over > span { background: #b3462e; }
