﻿/* AMA Client Tracker - the tracker's own stylesheet (HTML 7-240), ported token for token.
   The only additions are classes for markup the tracker wrote as inline styles (group
   headings, pills, note boxes, the month grid, the preview table) and the sign-in page. */

:root {
  --bg: #15181a;
  --panel: #1e2226;
  --panel-alt: #242a2e;
  --border: #33393e;
  --accent: #C2CAD1;
  --accent-dim: #9BA4AC;
  --yellow: #ffc300;
  --red: #e13438;
  --teal: #4fa893;
  --text: #ece9e4;
  --text-mute: #A6AEB5;
  --text-mute2: #838C93;
  --mono: 'IBM Plex Mono', Consolas, monospace;
  --display: 'Oswald', 'Arial Narrow', sans-serif;
  --body: 'Inter', system-ui, sans-serif;
  /* The content column's width, shared with the environment ribbon so the two cannot
     drift apart: the ribbon is positioned against this column, not the viewport. */
  --wrap-max: 1100px;
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; background: var(--bg); }
body {
  font-family: var(--body);
  font-weight: 600;
  background: var(--bg);
  color: var(--text);
  min-height: 100vh;
  padding: 0 0 80px 0;
}
.wrap { max-width: var(--wrap-max); margin: 0 auto; padding: 20px 16px; }
a { color: inherit; text-decoration: none; }

/* HEADER */
.hdr {
  display: flex; justify-content: space-between; align-items: flex-end;
  border-bottom: 3px solid var(--accent);
  padding-bottom: 14px; margin-bottom: 18px; flex-wrap: wrap; gap: 10px;
}
.hdr-logo { height: 44px; display: block; }
.hdr-title { font-family: var(--display); font-weight: 700; font-size: 26px; letter-spacing: 1px; text-transform: uppercase; color: var(--text); }
.hdr-title span { color: var(--accent); }
.hdr-sub { font-family: var(--mono); font-size: 11px; color: var(--text-mute); letter-spacing: 0.5px; margin-top: 2px; }
.hdr-right { text-align: right; }
.hdr-stamp { font-family: var(--mono); font-size: 11px; color: var(--text-mute2); font-weight: 600; text-align: right; }
/* THE ADMIN STRIP. Seven items in one row with a 10px gap and nothing else read as one
   run-on sentence - "Thomas Juister Change password Reset a password People..." - so the
   signed-in person's NAME looked like the first link and no two items had a visible edge.
   It grew an item at a time (ADR-0005 added two, ADR-0006 a third) and nobody looked at it
   whole. A rule per item, and the name set apart as what it is rather than something to
   click. flex-wrap because the strip is now long enough to need it on a laptop. */
.hdr-user { font-family: var(--mono); font-size: 11px; color: var(--text-mute); margin-top: 4px; display: flex; gap: 0; flex-wrap: wrap; justify-content: flex-end; align-items: center; }
.hdr-user > * { padding: 0 9px; line-height: 1.6; }
.hdr-user > * + * { border-left: 1px solid var(--border); }
.hdr-user > *:first-child { padding-left: 0; color: var(--text); font-weight: 700; }
.hdr-user > *:last-child { padding-right: 0; }
.hdr-user a { color: var(--text-mute); text-decoration: none; }
.hdr-user a:hover, .hdr-user a:focus-visible { color: var(--text); text-decoration: underline; text-underline-offset: 3px; }
.hdr-user form { margin: 0; }
.link-btn { background: none; border: none; padding: 0; margin: 0; color: var(--accent); font: inherit; cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }

/* TABS */
.tabs {
  display: flex; gap: 4px; margin-bottom: 18px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 8px;
  padding: 4px; overflow-x: auto;
}
.tab-btn {
  flex: 1; min-width: 90px;
  font-family: var(--display);
  text-transform: uppercase; letter-spacing: 0.5px;
  font-size: 12.5px; font-weight: 700;
  background: transparent; border: none; color: var(--text-mute);
  padding: 10px 8px; border-radius: 6px; cursor: pointer;
  transition: all .15s; white-space: nowrap; text-align: center;
}
.tab-btn.active { background: var(--accent); color: #151515; font-weight: 700; }
/* Tabs that carry a count (Contacts' Everyone / Customers / Prospects): on a phone "PROSPECTS
   (1271)" is wider than a third of the screen, so the label wraps under itself rather than the
   strip scrolling a tab out of sight. */
@media (max-width: 640px) { .tabs.counted .tab-btn { white-space: normal; min-width: 0; line-height: 1.25; padding: 8px 4px; } }
.tab-btn:not(.active):hover { background: var(--panel-alt); color: var(--text); }

/* STATS BAR */
.stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: 10px; margin-bottom: 20px; }
.stats.three { grid-template-columns: repeat(3, 1fr); }
.stat-card { background: var(--panel); border: 1px solid var(--border); border-radius: 8px; padding: 12px 14px; }
.stat-num { font-family: var(--mono); font-size: 26px; font-weight: 700; }
.stat-label { font-size: 11px; color: var(--text-mute); text-transform: uppercase; letter-spacing: 0.5px; margin-top: 3px; font-weight: 700; }
.stat-card.crit .stat-num { color: var(--red); }
.stat-card.warn .stat-num { color: var(--yellow); }
.stat-card.good .stat-num { color: var(--teal); }
@media (max-width: 640px) { .stats, .stats.three { grid-template-columns: repeat(2, 1fr); } }

/* PANEL / CARD */
.panel { background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 16px; margin-bottom: 16px; }
.panel.accent { border-color: var(--accent); padding: 12px 16px; }
.panel-title {
  font-family: var(--display); text-transform: uppercase; letter-spacing: 0.5px;
  font-size: 14px; font-weight: 700; color: var(--text); margin-bottom: 12px;
  display: flex; justify-content: space-between; align-items: center; gap: 10px; flex-wrap: wrap;
}
.panel-title .hint { font-family: var(--body); font-weight: 600; font-size: 11.5px; color: var(--text-mute2); text-transform: none; letter-spacing: 0; }

/* A PANEL HEADING THAT IS ITS OWN SWITCH (Shared/CollapsiblePanel.razor, 2026-09-20). The
   whole bar is the target rather than the arrow: a small control beside a large inert one is
   the arrangement people miss. .panel-title is already a flex row, so the hint sits in the
   middle and the arrow is pushed to the right by margin-left:auto - nothing here changes the
   layout of an ordinary panel title. */
.panel-toggle { cursor: pointer; margin-bottom: 0; user-select: none; }
.panel-toggle:hover .panel-toggle-title, .panel-toggle:focus-visible .panel-toggle-title { color: var(--accent); }
.panel-toggle:focus-visible { outline: 2px solid var(--accent-dim); outline-offset: 3px; }
.panel-toggle-title { transition: color 0.12s ease; }
/* Pushed right whatever else is on the bar, and the same width open or shut so the heading
   does not shuffle sideways as it toggles. */
.panel-toggle-arrow { margin-left: auto; color: var(--text-mute2); font-size: 12px; width: 1em; text-align: center; }
.panel-toggle:hover .panel-toggle-arrow { color: var(--accent); }
/* The gap the heading drops when it is open, since .panel-toggle itself carries none. */
.panel-toggle + * { margin-top: 12px; }
.hint { font-weight: 600; font-size: 11.5px; color: var(--text-mute2); text-transform: none; letter-spacing: 0; }

/* FORMS */
label { font-size: 11.5px; color: var(--text-mute); text-transform: uppercase; letter-spacing: 0.5px; font-weight: 700; display: block; margin-bottom: 5px; }
input, select, textarea {
  width: 100%; background: var(--panel-alt); border: 1px solid var(--border);
  color: var(--text); padding: 9px 10px; border-radius: 6px; font-family: var(--body);
  font-size: 14px; font-weight: 600; margin-bottom: 12px;
}
input:focus, select:focus, textarea:focus { outline: none; border-color: var(--accent); }
textarea { resize: vertical; min-height: 60px; }
input[type="date"]::-webkit-calendar-picker-indicator { filter: invert(0.9); cursor: pointer; }
input[type="time"]::-webkit-calendar-picker-indicator { filter: invert(0.9); cursor: pointer; }
input[type="checkbox"] { width: auto; margin-bottom: 0; accent-color: var(--accent); }
input[type="file"] { padding: 8px; }
input::file-selector-button {
  font-family: var(--display); text-transform: uppercase; letter-spacing: 0.5px; font-weight: 700; font-size: 12px;
  background: transparent; border: 1px solid var(--border); color: var(--text); border-radius: 6px; padding: 6px 12px; margin-right: 10px; cursor: pointer;
}
.tight { margin-bottom: 0; }
.check-row { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; text-transform: none; font-size: 13px; color: var(--text); letter-spacing: 0; cursor: pointer; }
.check-row.tight { margin-bottom: 0; }
.note-box { margin: 12px 0; padding: 10px 12px; background: var(--panel-alt); border: 1px solid var(--border); border-radius: 8px; }
.note-box > .client-meta { margin-top: 4px; }
.warn-box { background: rgba(194, 202, 209, 0.15); border: 1px solid var(--accent); border-radius: 8px; padding: 10px 12px; margin: 10px 0; font-size: 12.5px; color: var(--text); }
.error-box { background: rgba(225, 52, 56, 0.12); border: 1px solid var(--red); border-radius: 8px; padding: 10px 12px; margin: 10px 0; font-size: 12.5px; color: var(--text); }
/* RefusalBoundary's Development-only exception dump. Capped and scrollable because a Blazor
   stack trace is fifty frames and would otherwise push the page it is explaining off screen. */
.dev-exception { white-space: pre-wrap; word-break: break-word; max-height: 40vh; overflow: auto; margin: 10px 0 0; font-family: var(--mono); font-size: 11px; color: var(--text-mute); }
.ac-wrap { position: relative; }
.ac-dropdown {
  position: absolute; top: 100%; left: 0; right: 0; z-index: 50;
  background: var(--panel-alt); border: 1px solid var(--border); border-radius: 6px;
  max-height: 240px; overflow-y: auto; margin-top: -10px;
  box-shadow: 0 8px 20px rgba(0, 0, 0, 0.5);
}
.ac-item { padding: 8px 10px; cursor: pointer; border-bottom: 1px solid var(--border); }
.ac-item:last-child { border-bottom: none; }
.ac-item:hover { background: var(--panel); }
.ac-item-title { font-weight: 700; font-size: 13px; color: var(--text); }
.ac-item-sub { font-size: 11px; font-weight: 600; color: var(--text-mute); font-family: var(--mono); margin-top: 2px; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 12px; }
.form-grid.three { grid-template-columns: 1fr 1fr 1fr; }
.form-grid.four { grid-template-columns: 1fr 1fr 1fr 1fr; }
@media (max-width: 900px) { .form-grid.four { grid-template-columns: 1fr 1fr; } }
@media (max-width: 640px) { .form-grid, .form-grid.three, .form-grid.four { grid-template-columns: 1fr; } }
/* Four short selects (the Contacts filters) stay two to a line on a phone; one to a line put the
   list itself another screen further down. A form of text boxes keeps one to a line. */
@media (max-width: 640px) { .form-grid.four.filters { grid-template-columns: 1fr 1fr; } }
.rep-stats-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 800px) { .rep-stats-grid { grid-template-columns: 1fr; } }
.inline-add { display: flex; gap: 8px; margin-bottom: 12px; }
.inline-add input { margin-bottom: 0; }
/* On a phone the search box takes the line and its buttons go under it: beside View and Add
   Contact it was squeezed to "Type to searc". */
@media (max-width: 640px) { .inline-add { flex-wrap: wrap; } .inline-add > .ac-wrap { flex: 1 1 100%; } }
.btn {
  font-family: var(--display); text-transform: uppercase; letter-spacing: 0.5px;
  font-weight: 700; font-size: 13px;
  background: var(--accent); color: #151515; border: none; border-radius: 6px;
  padding: 11px 18px; cursor: pointer; transition: background .15s; display: inline-block; line-height: normal;
}
.btn:hover { background: var(--accent-dim); }
.btn:disabled { opacity: 0.5; cursor: default; }
.btn.secondary { background: transparent; border: 1px solid var(--border); color: var(--text); }
.btn.secondary:hover { border-color: var(--accent); color: var(--accent); }
.btn.danger { background: transparent; border: 1px solid var(--red); color: var(--red); }
.btn.small { padding: 5px 10px; font-size: 11px; }
.btn.compact { padding: 8px 14px; font-size: 11px; white-space: nowrap; }
.btn-row { display: flex; gap: 10px; flex-wrap: wrap; align-items: center; }

/* PAGER - above and below Contacts and the Jobs Log (Shared/Pager.razor) */
.pager { display: flex; gap: 8px; flex-wrap: wrap; align-items: center; justify-content: center; margin: 10px 0; }
.pager-where { font-size: 12px; color: var(--text-mute2); padding: 0 6px; white-space: nowrap; }

/* A phone number or an address you can tap (ADR-0007): quiet until pointed at. */
.contact-link { color: inherit; text-decoration: underline; text-decoration-color: var(--border); text-underline-offset: 2px; }
.contact-link:hover, .contact-link:focus-visible { color: var(--accent); text-decoration-color: var(--accent); }

/* CLIENT LIST / GAUGE ROWS */
.client-row {
  display: flex; align-items: center; gap: 14px;
  padding: 12px 10px; border-bottom: 1px solid var(--border); cursor: pointer;
  transition: background .1s;
}
.client-row.static { cursor: default; }
.client-row:last-child { border-bottom: none; }
.client-row:hover { background: var(--panel-alt); }
.client-name-block { flex: 1; min-width: 0; }
.client-name { font-weight: 700; font-size: 15.5px; letter-spacing: 0.1px; }
.client-name.small { font-size: 14px; }
.client-meta { font-family: var(--mono); font-size: 12px; font-weight: 600; color: var(--text-mute); margin-top: 3px; line-height: 1.5; }
.client-meta.italic { font-style: italic; }
.client-meta.inline { display: inline; }
.client-meta strong { color: var(--text); }
.client-meta strong.over { color: var(--red); }
.mono-sub { color: var(--text-mute); font-weight: 400; font-family: var(--mono); font-size: 11px; }
.mono-sub.larger { font-size: 12px; }
.company-tag { color: var(--accent); cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.company-tag.mono { font-family: var(--mono); font-size: 11px; font-weight: 500; }
.contact-name-link { cursor: pointer; text-decoration: underline; text-underline-offset: 2px; }
.flag-badge {
  font-family: var(--display); font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.5px; padding: 4px 9px; border-radius: 20px; white-space: nowrap;
}
.flag-ok { background: rgba(79, 168, 147, 0.15); color: var(--teal); }
.flag-warn { background: rgba(255, 195, 0, 0.15); color: var(--yellow); }
.flag-high { background: rgba(194, 202, 209, 0.18); color: var(--accent); }
.flag-crit {
  background: repeating-linear-gradient(45deg, rgba(225, 52, 56, 0.25), rgba(225, 52, 56, 0.25) 6px, rgba(225, 52, 56, 0.4) 6px, rgba(225, 52, 56, 0.4) 12px);
  color: #ff9b9d;
}
/* Nobody has worked for or called them yet - a prospect, not an emergency (ADR-0007). */
.flag-none { background: transparent; border: 1px solid var(--border); color: var(--text-mute2); }
.flag-teal { background: rgba(79, 168, 147, 0.15); color: var(--teal); }
.flag-accent { background: rgba(194, 202, 209, 0.15); color: var(--accent); }
.flag-yellow { background: rgba(255, 195, 0, 0.15); color: var(--yellow); }
.flag-red { background: rgba(225, 52, 56, 0.15); color: var(--red); }
.priority-select { width: 130px; margin-bottom: 0; font-size: 11px; }

/* A LIST ROW ON A PHONE (2026-09-22). Every list - Contacts, the Dashboard, the Jobs Log, Team,
   Roles, the Rep View - lays a row out in one line: the name block, then whatever sits beside it
   (a badge, a gauge, the priority select, Edit and Delete). On a 375-pixel phone the controls took
   nearly all of it and squeezed the name block to NOTHING: Contacts' names, numbers and dates
   wrapped one word to a line, a row ran 350 to 450 pixels, and the priority pill sat on top of the
   text. Mike: "the contact list looks terrible on a phone". So on a phone the name block takes the
   whole first line and the controls go under it, Edit and Delete to the right. The priority PILL
   gives way where the priority SELECT is beside it - it says the same word; somebody who may not
   change priorities has no select, and keeps the pill. A Contacts row is about 125 pixels now. */
@media (max-width: 640px) {
  .client-row { flex-wrap: wrap; row-gap: 8px; column-gap: 10px; }
  .client-row > .client-name-block { flex: 1 1 100%; }
  .client-row > .btn-row { flex-wrap: nowrap; margin-left: auto; }
  .client-row > .flag-badge:has(+ .priority-select) { display: none; }
}

/* GAUGE BAR */
.gauge { width: 150px; flex-shrink: 0; }
.gauge-track {
  position: relative; height: 8px; border-radius: 4px; overflow: hidden;
  background: linear-gradient(90deg, var(--teal) 0 15%, var(--yellow) 15% 33%, var(--accent) 33% 66%, var(--red) 66% 100%);
}
.gauge-marker { position: absolute; top: -3px; width: 2px; height: 14px; background: #fff; box-shadow: 0 0 4px rgba(0, 0, 0, 0.6); }
.gauge-days { font-family: var(--mono); font-size: 11px; color: var(--text-mute); text-align: right; margin-top: 3px; font-weight: 700; }
@media (max-width: 640px) { .gauge { width: 70px; } }

/* GROUP HEADINGS (the tracker's inline Oswald headings) */
.group-head {
  font-family: var(--display); text-transform: uppercase; letter-spacing: 0.5px; font-size: 12px; font-weight: 700;
  color: var(--accent); padding: 10px 0 6px; border-top: 1px solid var(--border); margin-top: 10px;
}
.group-head.first { border-top: none; margin-top: 0; }
.group-head.underline { font-weight: 600; padding: 8px 0 4px; border-top: none; border-bottom: 1px solid var(--border); margin: 0 0 4px; }
.group-head .count { color: var(--text-mute2); font-weight: 400; }
.group-head.red { color: var(--red); }
.group-head.yellow { color: var(--yellow); }
.group-head.teal { color: var(--teal); }
.group-head.mute { color: var(--text-mute); }
.group-head.clickable { cursor: pointer; }
.group { margin-bottom: 14px; }
.section-label { text-transform: uppercase; letter-spacing: 0.5px; margin: 14px 0 6px; color: var(--text-mute); }

/* PILLS */
.pills { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 10px; }
.pill { display: flex; align-items: center; gap: 6px; background: var(--panel-alt); border: 1px solid var(--border); padding: 5px 10px; border-radius: 20px; color: var(--text); font-size: 12px; }
.pill .x { cursor: pointer; color: var(--red); font-weight: 700; }

/* TIMELINE */
.timeline-item { display: flex; gap: 12px; padding: 12px 0; border-left: 2px solid var(--border); margin-left: 6px; padding-left: 16px; position: relative; }
.timeline-item::before { content: ''; position: absolute; left: -7px; top: 16px; width: 12px; height: 12px; border-radius: 50%; background: var(--accent); border: 2px solid var(--bg); }
.timeline-item.job::before { background: var(--teal); }
.timeline-content { flex: 1; }
.timeline-top { display: flex; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
.timeline-type { font-family: var(--display); font-size: 11.5px; font-weight: 700; text-transform: uppercase; color: var(--accent); letter-spacing: 0.5px; }
.timeline-item.job .timeline-type { color: var(--teal); }
.timeline-date { font-family: var(--mono); font-size: 11.5px; font-weight: 700; color: var(--text-mute); }
.timeline-note { font-size: 14px; font-weight: 600; color: var(--text); margin-top: 4px; line-height: 1.55; }
.timeline-rep { font-size: 11.5px; color: var(--text-mute2); margin-top: 3px; font-weight: 700; }

.empty-state { text-align: center; padding: 40px 20px; color: var(--text-mute2); font-weight: 600; }
.empty-state.tight { padding: 16px; }
.empty-state .big { font-family: var(--display); font-weight: 700; font-size: 16px; color: var(--text-mute); margin-bottom: 6px; text-transform: uppercase; }

.back-link { color: var(--accent); cursor: pointer; font-size: 12px; font-family: var(--mono); font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 14px; display: inline-block; }

.toast-stack { position: fixed; bottom: 20px; left: 50%; transform: translateX(-50%); display: flex; flex-direction: column; align-items: center; gap: 8px; z-index: 999; width: max-content; max-width: 90vw; }
/* On a phone the dialogs are bottom sheets, and a toast that now stays long enough to read would
   sit over their Save row - a tap meant for Save would dismiss the toast instead. At the top it
   covers nothing anybody is about to press. */
@media (max-width: 640px) { .toast-stack { top: 12px; bottom: auto; } }
.toast {
  background: var(--panel-alt); border: 1px solid var(--accent); color: var(--text);
  padding: 10px 18px; border-radius: 8px; font-size: 13px; font-weight: 600;
  box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4);
  font-family: var(--mono); max-width: min(90vw, 720px); text-align: center;
  cursor: pointer; /* a click dismisses it (ToastService.Dismiss) */
}
.stub-note { color: var(--text-mute); font-weight: 400; }

::-webkit-scrollbar { height: 6px; width: 6px; }
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 3px; }

.fab {
  position: fixed; bottom: 20px; right: 20px;
  background: var(--accent); color: #151515; border: none;
  width: 56px; height: 56px; border-radius: 50%; font-size: 26px; font-weight: 700;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5); cursor: pointer; z-index: 100;
  display: flex; align-items: center; justify-content: center;
}
.modal-overlay { position: fixed; inset: 0; background: rgba(0, 0, 0, 0.6); z-index: 200; display: flex; align-items: flex-end; justify-content: center; }
@media (min-width: 641px) { .modal-overlay { align-items: center; } }
.modal { background: var(--panel); border: 1px solid var(--border); border-radius: 14px 14px 0 0; padding: 20px; width: 100%; max-width: 480px; max-height: 88vh; overflow-y: auto; }
.modal.wide { max-width: 640px; }
@media (min-width: 641px) { .modal { border-radius: 14px; } }
.modal .message { font-size: 13.5px; line-height: 1.55; color: var(--text); white-space: pre-line; margin-bottom: 14px; }

/* BIG NUMBERS (Rep View cards) */
.split { display: flex; }
.split > div { flex: 1; text-align: center; padding: 6px 0; }
.split > div + div { border-left: 1px solid var(--border); }
.big-num { font-family: var(--mono); font-size: 32px; font-weight: 600; color: var(--teal); }
.big-num.red { color: var(--red); }
.big-num.larger { font-size: 36px; }
.centered { text-align: center; padding: 6px 0 2px; }
.breakdown { border-top: 1px solid var(--border); margin-top: 8px; padding-top: 6px; }
.breakdown-row { display: flex; justify-content: space-between; padding: 3px 0; }
.breakdown-row .client-meta { margin: 0; }
.breakdown-row .client-meta.value { color: var(--text); font-weight: 700; }

/* MONTH GRID */
.month-head { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; font-family: var(--mono); font-size: 10px; color: var(--text-mute); text-align: center; margin-bottom: 4px; }
.month-grid { display: grid; grid-template-columns: repeat(7, 1fr); gap: 4px; }
.day-cell { padding: 8px 4px; text-align: center; cursor: pointer; border-radius: 6px; border: 1px solid transparent; font-family: var(--mono); font-size: 12px; color: var(--text); }
.day-cell.today { background: var(--panel-alt); border-color: var(--accent); }
.day-cell:hover { background: var(--panel-alt); }
.day-dot { width: 5px; height: 5px; border-radius: 50%; background: var(--accent); margin: 3px auto 0; }
.day-title { font-family: var(--display); font-size: 16px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; margin-bottom: 8px; }
.day-title .today-tag { color: var(--accent); font-size: 11px; font-family: var(--mono); }

/* SHEET IMPORT PREVIEW */
.scroll-x { overflow-x: auto; }
.preview-table { width: 100%; border-collapse: collapse; font-family: var(--mono); font-size: 11px; }
.preview-table th { text-align: left; border-bottom: 1px solid var(--border); padding: 4px 8px; color: var(--text-mute); font-weight: 700; white-space: nowrap; }
.preview-table td { padding: 4px 8px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.preview-table .none { color: var(--text-mute2); }

/* THE AUDIT VIEWER'S TABLE (ADR-0006). `data-table` was NEVER DEFINED - the page named a
   class that no stylesheet carried, so it rendered with no rules at all: no borders, no row
   separation, and columns sized by whatever the browser guessed from content. "Who" ran into
   "What" because nothing bounded either. The page had never been rendered by anything before
   2026-09-20 (no bUnit here, and the host tests cover endpoints rather than Razor), which is
   exactly the kind of thing only opening it finds.
   Not folded into .preview-table: that one is nowrap by design for a short import preview,
   and this table's whole job is long sentences that must wrap. */
.data-table { width: 100%; border-collapse: collapse; table-layout: fixed; font-family: var(--mono); font-size: 12px; }
.data-table th { text-align: left; padding: 8px 10px; border-bottom: 1px solid var(--border); color: var(--text-mute); font-weight: 700; white-space: nowrap; }
.data-table td { padding: 8px 10px; border-bottom: 1px solid var(--border); vertical-align: top; overflow-wrap: anywhere; }
.data-table tbody tr:hover { background: var(--panel-alt); }
.data-table td.nowrap { white-space: nowrap; }
/* Fixed proportions, because content-sized columns are what let one long machine name
   squeeze the column carrying the sentence. When never wraps; What gets the remainder. */
/* 23ch is the timestamp - "2026-09-20 13:41:56 MDT" is exactly 23 characters - and the cell
   also carries 10px of padding each side. Set to 23ch it overflowed, and because this column
   is nowrap the overflow ran straight into Who: the two columns printed as one string with no
   gap. Found on production, on the screen this rule was written to fix. */
.data-table th:nth-child(1), .data-table td:nth-child(1) { width: calc(23ch + 24px); }
.data-table th:nth-child(2), .data-table td:nth-child(2) { width: 26%; }
.data-table th:nth-child(4), .data-table td:nth-child(4) { width: 20%; }
.data-table details { margin-top: 5px; }
.data-table summary { cursor: pointer; color: var(--text-mute2); }
.data-table summary:hover { color: var(--text-mute); }

/* SIGN-IN */
.login-wrap { min-height: 100vh; display: flex; align-items: center; justify-content: center; padding: 20px 16px; }
.login-card { width: 100%; max-width: 400px; }
.login-card .hdr { justify-content: center; }
.validation-message { color: var(--red); font-size: 12px; font-family: var(--mono); margin: -8px 0 10px; }

/* THE HOSTING NOTICE (Components/Shared/HostingNotice.razor) - the one thing in the app
   that is meant to be read rather than used, so it is built to be noticed and then to be
   calm: the staging yellow down its left edge, the panel's own background, and prose in
   the body font. It sits above the stat cards on the Dashboard, and it exists only while
   the site is not Production. */
.notice {
  background: var(--panel); border: 1px solid var(--border); border-left: 4px solid var(--yellow);
  border-radius: 10px; padding: 16px 18px; margin-bottom: 16px;
}
.notice-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 10px; }
.notice-title {
  font-family: var(--display); font-weight: 700; font-size: 16px; letter-spacing: 0.5px;
  text-transform: uppercase; color: var(--text);
}
/* Put away: one line and the way back into it. Same yellow edge, so the thing he
   collapsed is visibly the same thing he can open again. */
.notice.collapsed { display: flex; justify-content: space-between; align-items: center; gap: 12px; padding: 10px 14px; }
.notice-line { font-size: 12.5px; color: var(--text-mute); font-weight: 600; }
.notice-foot { color: var(--text-mute2) !important; font-size: 12px !important; }
.notice p { margin: 9px 0; font-size: 13px; color: var(--text-mute); line-height: 1.6; max-width: 74ch; }
.notice strong { color: var(--text); font-weight: 700; }
.notice ul { margin: 8px 0 10px; padding-left: 20px; max-width: 74ch; }
.notice li { font-size: 13px; color: var(--text-mute); line-height: 1.55; margin: 5px 0; }
.notice li::marker { color: var(--yellow); }

/* ENVIRONMENT RIBBON - a corner tag on every non-Production host (Components/Shared/
   EnvironmentRibbon.razor): the same site on the dev rig and on the staging address look
   identical otherwise, and the book kept on staging is not the one production will carry.
   Mistaking one for the other is the failure this exists to prevent, so the ribbon is
   deliberately laid ACROSS the AMA logo: a header that looks untouched is exactly what
   would let a rehearsal site pass for the real one. Hence the left offset - it tracks the
   content column (--wrap-max), not the viewport, so the ribbon crosses the logo at every
   window width instead of drifting into the empty margin on a wide screen. Fixed, so it
   survives scrolling; under the modals; and pointer-events none, so it covers no control. */
.env-ribbon {
  position: fixed; top: 0; left: max(0px, calc((100vw - var(--wrap-max)) / 2));
  width: 140px; height: 140px; overflow: hidden;
  z-index: 150; pointer-events: none;
}
.env-ribbon span {
  position: absolute; top: 30px; left: -42px; width: 190px;
  transform: rotate(-45deg);
  background: var(--yellow); color: #15181a;
  font-family: var(--display); font-weight: 700; font-size: 12px; letter-spacing: 2px; text-transform: uppercase;
  text-align: center; line-height: 24px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.45);
  opacity: 0.92;
}
.env-ribbon.dev span { background: var(--teal); color: #15181a; }

/* Blazor's reconnect / error UI, restyled to the panel palette */
#blazor-error-ui { display: none; position: fixed; bottom: 0; left: 0; right: 0; background: var(--panel-alt); border-top: 1px solid var(--red); color: var(--text); padding: 12px 20px; font-size: 13px; z-index: 1000; }
#blazor-error-ui .dismiss { position: absolute; right: 16px; top: 10px; cursor: pointer; }
/* The reconnect dialog. This used to be one line - `#components-reconnect-modal { color:
   var(--text) }` - which was worse than nothing: it left the framework drawing its own white
   card and coloured the text on it near-white, so the ONE message this site could show about
   a dropped connection was white on white.

   The class names below are the framework's contract, read off
   _framework/blazor.web.js rather than remembered. blazor.web.js has two implementations: its
   own dialog, and this one - "user-specified" - which is used the moment an element with this
   id exists. The difference that matters is that the default's rejected() calls
   location.reload() and the user-specified one only adds a class, so the link out of the
   rejected state below is load-bearing rather than decoration. A publish produces exactly
   that state (docs/deployment.md). */
#components-reconnect-modal { display: none; }

#components-reconnect-modal.components-reconnect-show,
#components-reconnect-modal.components-reconnect-retrying,
#components-reconnect-modal.components-reconnect-paused,
#components-reconnect-modal.components-reconnect-failed,
#components-reconnect-modal.components-reconnect-resume-failed,
#components-reconnect-modal.components-reconnect-rejected {
  display: flex; position: fixed; inset: 0; z-index: 1001;
  background: rgba(0, 0, 0, 0.6); align-items: center; justify-content: center;
}

.reconnect-card {
  background: var(--panel-alt); border: 1px solid var(--border); border-radius: 8px;
  color: var(--text); padding: 18px 20px; max-width: 420px; margin: 16px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.5);
}

.reconnect-title {
  font-family: var(--display); font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.5px; font-size: 13px; color: var(--accent); margin-bottom: 8px;
}

/* One line per state, and the state class on the dialog decides which. */
.reconnect-line { display: none; font-size: 13px; margin: 0 0 12px; }

#components-reconnect-modal.components-reconnect-show .reconnect-when-trying,
#components-reconnect-modal.components-reconnect-retrying .reconnect-when-trying,
#components-reconnect-modal.components-reconnect-paused .reconnect-when-trying,
#components-reconnect-modal.components-reconnect-failed .reconnect-when-failed,
#components-reconnect-modal.components-reconnect-resume-failed .reconnect-when-failed,
#components-reconnect-modal.components-reconnect-rejected .reconnect-when-rejected {
  display: block;
}

/* READ-ONLY BANNER - ADR-0003's recovery state, on every screen (Components/Shared/
   ReadOnlyBanner.razor). Red rather than the grey of .warn-box, and in the content column
   rather than a toast: this is not advice that can scroll past in 2.2 seconds, it is the
   difference between typing an afternoon in and having it saved. */
.read-only-banner {
  background: rgba(224, 82, 82, 0.12); border: 1px solid var(--red); border-radius: 8px;
  padding: 10px 12px; margin: 0 0 14px; font-size: 13px; color: var(--text);
}
.read-only-banner strong {
  color: var(--red); font-family: var(--display); text-transform: uppercase;
  letter-spacing: 0.5px; margin-right: 8px;
}

/* FOCUS, VISIBLE. The rule above turns the outline off on every form control and swaps the
   border colour instead, which reads well and works - for a control that HAS a border. A
   checkbox, a radio and a file input do not, so for those the swap is invisible and the
   whole screen could be tabbed through with nothing showing where you were. :focus-visible
   rather than :focus, so a mouse click does not paint a ring nobody asked for. */
input[type="checkbox"]:focus-visible,
input[type="radio"]:focus-visible,
input[type="file"]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px;
}

/* The tracker's rows, name links and company tags are divs and spans that behave as buttons
   (Keys.Activate gives them Enter and Space). A real button shows focus for free; these have
   to be told to. */
[role="button"]:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px;
}

.modal:focus { outline: none; }

/* The two administration screens (Team, Roles), added with the permission system.
   They reuse .panel, .client-row and .btn wholesale - the only genuinely new thing here is the
   permission checklist, which has no precedent in the tracker because the tracker had nothing
   to grant. A switched-off person is dimmed rather than hidden: they are still the name on
   two years of touchpoints, and a list that hid them would make that history look authorless. */
.client-row.off .client-name { color: var(--text-mute); text-decoration: line-through; }
.client-row.off { opacity: 0.72; }

.perm-checklist { margin: 14px 0 4px; }
.perm-group { border: 1px solid var(--border); border-radius: 10px; padding: 10px 14px 6px; margin-bottom: 12px; }
.perm-group > legend { font-size: 12px; font-weight: 700; letter-spacing: 0.4px; text-transform: uppercase; color: var(--text-mute); padding: 0 6px; }
.perm-group > .hint { margin: 0 0 8px; }
.perm-row { display: block; padding: 7px 0; border-bottom: 1px solid var(--border); cursor: pointer; }
.perm-row:last-child { border-bottom: none; }
.perm-row > input { margin-right: 8px; vertical-align: top; }
.perm-title { font-weight: 700; font-size: 13.5px; }
/* The consequence, not the name, is what somebody deciding this actually reads. */
.perm-consequence { display: block; margin: 2px 0 0 22px; font-size: 12px; color: var(--text-mute); line-height: 1.45; }
.perm-row-locked { cursor: not-allowed; opacity: 0.55; }
.perm-locked-note { display: block; margin: 3px 0 0 22px; font-size: 11.5px; color: var(--yellow); }

/* ---- THE BUSY RING (Services/BusyService.cs, Shared/BusyHost.razor) ----
   A layer over the page while something runs. It takes every tap from the first moment, but
   stays clear and empty for the first 600 ms - longer than the ring's own minimum hold, so a
   save that finishes in a quarter of a second does not flash a spinner. Above every dialog (200) so it covers a dialog's own Save; below the
   confirm dialog (400), which an upload shows part-way through its work; below the toasts
   (999), the error bar (1000) and the reconnect card (1001), so a dropped connection still
   shows over it. */
.modal-overlay.confirm { z-index: 400; }
.busy-overlay { position: fixed; inset: 0; z-index: 300; display: flex; align-items: center; justify-content: center; padding: 16px; background: rgba(0, 0, 0, 0); cursor: progress; animation: busy-dim 0.2s ease 0.6s forwards; }
.busy-card { background: var(--panel); border: 1px solid var(--border); border-radius: 14px; padding: 22px 26px; width: 100%; max-width: 420px; text-align: center; opacity: 0; animation: busy-show 0.2s ease 0.6s forwards; box-shadow: 0 12px 40px rgba(0, 0, 0, 0.45); }
.busy-ring { width: 44px; height: 44px; margin: 0 auto 14px; border-radius: 50%; border: 4px solid var(--border); border-top-color: var(--accent); animation: busy-spin 0.9s linear infinite; }
.busy-text { font-size: 15px; font-weight: 600; color: var(--text); line-height: 1.45; }
.busy-step { margin-top: 6px; font-size: 13px; color: var(--text-mute); line-height: 1.45; }
.busy-hint { margin-top: 12px; font-size: 12px; color: var(--text-mute2); }
@keyframes busy-spin { to { transform: rotate(360deg); } }
@keyframes busy-dim { to { background: rgba(0, 0, 0, 0.55); } }
@keyframes busy-show { to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .busy-ring { animation-duration: 2.8s; } }

/* The plain forms - sign in, the two password screens, sign out - post without a circuit, so
   the ring cannot reach them; wwwroot/js/tracker.js marks the button instead while the post is
   on its way, and refuses a second one. */
.btn.is-busy, .link-btn.is-busy { opacity: 0.8; cursor: progress; }
.btn.is-busy::before { content: ""; display: inline-block; width: 12px; height: 12px; margin-right: 8px; vertical-align: -1px; border-radius: 50%; border: 2px solid currentColor; border-right-color: transparent; animation: busy-spin 0.8s linear infinite; }
