/* CPM — shared styles.
 *
 * Tokens first, so the report list and editor inherit one palette instead of
 * each page inventing its own blue.
 */

:root {
  --brand:            #1a4fa0;
  --brand-dark:       #15408a;
  --brand-darker:     #0f3270;
  --brand-tint:       #eef3fb;
  --brand-hover:      #e6eefb;   /* one step stronger, for row highlights */

  --ink:              #16202e;
  --ink-soft:         #4a5568;
  --ink-faint:        #8592a5;

  --surface:          #ffffff;
  --surface-sunken:   #f7f9fc;
  --border:           #dfe5ee;
  --border-strong:    #c6d0de;

  /* Secondary buttons: a faint grey surface and a neutral border, both taken
     from the design. Kept apart from --surface and --border-strong, which are
     the white of a card and the blue-grey edge of an input. */
  --surface-quiet:    #f9fafb;
  --border-quiet:     #dcdee2;

  --danger:           #c0392b;
  --danger-dark:      #a33024;
  --danger-tint:      #fdeceb;
  --success:          #1e7a4d;
  --success-tint:     #e8f5ee;
  --warning:          #9a6410;
  --warning-tint:     #fdf3e2;

  /* Changed since the last submission. Its own pair rather than the
     warning tint, which the editor already uses for an unsaved cell. */
  --changed-tint:     #ffeec7;
  --changed-edge:     #ead4a9;

  --radius:           8px;
  --radius-lg:        14px;
  --shadow:           0 1px 2px rgba(22, 32, 46, .06),
                      0 4px 12px rgba(22, 32, 46, .05);

  --font: "Segoe UI", system-ui, -apple-system, "Helvetica Neue", Arial, sans-serif;
}

*,
*::before,
*::after { box-sizing: border-box; }

html, body { height: 100%; }

body {
  margin: 0;
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink);
  background: var(--surface-sunken);
  -webkit-font-smoothing: antialiased;
}

a { color: var(--brand); }

/* The user agent hides [hidden] with a plain `display: none`, which any
   component rule setting `display` outranks -- `.btn` is `inline-flex`, so
   hiding a button would silently do nothing. Restore the attribute's meaning
   once here rather than remembering it per component. */
[hidden] { display: none !important; }

/* ------------------------------------------------------------------ */
/* Forms                                                               */
/* ------------------------------------------------------------------ */

.field { margin-bottom: 18px; }

.field label {
  display: block;
  margin-bottom: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}

.field input,
.field textarea {
  width: 100%;
  padding: 10px 12px;
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  transition: border-color .15s, box-shadow .15s;
}

/* Only vertically: a textarea dragged wider than its dialog is a worse
   problem than one that is too short. */
.field textarea { display: block; resize: vertical; min-height: 84px; }

.field input:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--brand);
  /* Visible focus ring: keyboard users need to see where they are. */
  box-shadow: 0 0 0 3px rgba(26, 79, 160, .16);
}

.field input[aria-invalid="true"] { border-color: var(--danger); }

/* The asterisk on a required field's label. */
.field__required { color: var(--danger); }

/* A field caption that is not a <label>, where something else inside the
   field already carries the `for`. Matches `.field label` exactly. */
.field__caption {
  display: block;
  margin-bottom: 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}

/* A file picker.
 *
 * The browser draws `input[type=file]` as a button of its own, in its own
 * font, and nothing in CSS reaches it. So the input is hidden and a label
 * opens it instead -- a label, not a button with script, so choosing a file
 * still works if the page's JavaScript does not.
 *
 * The pair is dressed as one input: same border, radius and surface as a
 * text field, with the button inset on the left and the chosen name beside
 * it, which is the arrangement the native control already teaches. */
.filepick {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  transition: border-color .15s, box-shadow .15s;
}

/* `.field label` is display:block and would drop the flex row, so this is
   deliberately more specific. */
.field .filepick__button {
  display: inline-flex;
  flex: none;
  align-items: center;
  margin: 0;
  padding: 7px 13px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-soft);
  background: var(--surface-quiet);
  border: 1px solid var(--border-quiet);
  border-radius: 7px;
  cursor: pointer;
  transition: color .15s, background .15s, border-color .15s;
}

.field .filepick__button:hover {
  color: var(--brand);
  background: var(--brand-tint);
  border-color: #bcd0ee;
}

/* The chosen file's name, or a reminder that there is not one yet. Clipped
   rather than wrapped: a long name must not grow the dialog. */
.filepick__name {
  min-width: 0;
  overflow: hidden;
  font-size: 13px;
  color: var(--ink-faint);
  text-overflow: ellipsis;
  white-space: nowrap;
}

.filepick__name.has-file { color: var(--ink); }

/* The ring the input cannot show, since it is the hidden one. */
.filepick:focus-within {
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(26, 79, 160, .16);
}

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 8px;
  width: 100%;
  padding: 11px 16px;
  font: inherit;
  font-weight: 600;
  color: #fff;
  background: var(--brand);
  border: 1px solid var(--brand);
  border-radius: var(--radius);
  cursor: pointer;
  transition: background .15s;
}

.btn:hover:not(:disabled) { background: var(--brand-dark); }

.btn:disabled {
  opacity: .6;
  cursor: not-allowed;
}

/* Announced to screen readers the moment it appears, not only seen. */
.form-error {
  display: none;
  margin-bottom: 16px;
  padding: 10px 12px;
  font-size: 13px;
  color: var(--danger);
  background: var(--danger-tint);
  border: 1px solid #f2c9c5;
  border-radius: var(--radius);
}

.form-error.is-visible { display: block; }

/* Progress through a batch of uploads, in the same slot as the error above
   but in the page's own colours: this is news, not a fault. Shown only once
   there is something to say, so it takes no room when idle. */
.upload-status {
  margin-bottom: 16px;
  padding: 10px 12px;
  font-size: 13px;
  color: var(--ink-soft);
  background: var(--surface-quiet);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

.upload-status:empty { display: none; }

/* ------------------------------------------------------------------ */
/* Sign-in                                                             */
/* ------------------------------------------------------------------ */

.auth {
  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: 100vh;
}

.auth__brand {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  padding: 44px 56px;
  color: #fff;
  background: var(--brand);
}

.auth__logo {
  display: flex;
  align-items: center;
  gap: 10px;
  font-size: 17px;
  font-weight: 700;
  letter-spacing: .2px;
}

.auth__logo-mark {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  background: rgba(255, 255, 255, .16);
  border-radius: var(--radius);
}

.auth__headline {
  max-width: 460px;
  margin: 0 0 20px;
  font-size: 34px;
  line-height: 1.22;
  font-weight: 700;
}

.auth__lede {
  max-width: 460px;
  margin: 0;
  font-size: 14.5px;
  line-height: 1.65;
  color: rgba(255, 255, 255, .84);
}

.auth__rule {
  max-width: 460px;
  height: 1px;
  margin: 34px 0 26px;
  background: rgba(255, 255, 255, .22);
  border: 0;
}

.auth__stats {
  display: flex;
  gap: 62px;
}

.auth__stat-value {
  font-size: 26px;
  font-weight: 700;
  line-height: 1.2;
}

.auth__stat-label {
  margin-top: 2px;
  font-size: 13px;
  color: rgba(255, 255, 255, .74);
}

.auth__footnote {
  font-size: 12.5px;
  color: rgba(255, 255, 255, .62);
}

.auth__panel {
  display: grid;
  place-items: center;
  padding: 44px;
  background: var(--surface);
}

.auth__form { width: 100%; max-width: 384px; }

.auth__title {
  margin: 0 0 4px;
  font-size: 23px;
  font-weight: 700;
}

.auth__subtitle {
  margin: 0 0 26px;
  color: var(--ink-soft);
}

/* Stack on narrow screens: the brand panel is decorative, so it goes first
 * but collapses to a strip rather than half the viewport. */
@media (max-width: 900px) {
  .auth { grid-template-columns: 1fr; }

  .auth__brand { padding: 28px 24px; }
  .auth__headline { font-size: 26px; }
  .auth__rule,
  .auth__stats,
  .auth__footnote { display: none; }

  .auth__panel { padding: 32px 24px; }
}

/* ------------------------------------------------------------------ */
/* Utilities                                                           */
/* ------------------------------------------------------------------ */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* ------------------------------------------------------------------ */
/* Application shell                                                   */
/* ------------------------------------------------------------------ */

.shell {
  display: grid;
  grid-template-columns: 236px 1fr;
  min-height: 100vh;
}

.sidebar {
  display: flex;
  flex-direction: column;
  padding: 18px 14px;
  background: var(--surface);
  border-right: 1px solid var(--border);
}

.sidebar__brand {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 6px 8px 18px;
  color: var(--ink);
  text-decoration: none;
}

.sidebar__mark {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  color: #fff;
  background: var(--brand);
  border-radius: var(--radius);
}

.sidebar__brand strong { display: block; font-size: 15px; }

.sidebar__brand small {
  display: block;
  font-size: 10.5px;
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.sidebar__nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
  flex: 1;
}

.sidebar__nav a {
  padding: 9px 12px;
  font-weight: 500;
  color: var(--ink-soft);
  text-decoration: none;
  border-radius: var(--radius);
}

.sidebar__nav a:hover { background: var(--surface-sunken); }

.sidebar__nav a.is-active {
  color: var(--brand);
  background: var(--brand-tint);
  font-weight: 600;
}

.sidebar__user {
  display: flex;
  align-items: center;
  gap: 10px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}

.sidebar__user-text { flex: 1; min-width: 0; }

.sidebar__user-text strong {
  display: block;
  font-size: 13px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sidebar__user-text small {
  display: block;
  font-size: 11.5px;
  color: var(--ink-faint);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sidebar__signout {
  padding: 6px;
  color: var(--ink-faint);
  background: none;
  border: 0;
  border-radius: var(--radius);
  cursor: pointer;
}

.sidebar__signout:hover { color: var(--danger); background: var(--danger-tint); }

.avatar {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  font-size: 11px;
  font-weight: 700;
  color: var(--brand);
  background: var(--brand-tint);
  border-radius: var(--radius);
}

.main { display: flex; flex-direction: column; min-width: 0; }

.topbar {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 28px;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.crumbs { display: flex; gap: 6px; font-size: 12.5px; color: var(--ink-faint); }
.crumbs span::after { content: " ›"; }
.crumbs span:last-child::after { content: ""; }

.topbar__title { margin: 2px 0 0; font-size: 20px; font-weight: 700; }
.topbar__actions { display: flex; gap: 8px; }

.content { padding: 22px 28px 40px; }

/* ------------------------------------------------------------------ */
/* Buttons, filters, cards, tables                                     */
/* ------------------------------------------------------------------ */

/* Same vertical padding as the inputs and select triggers it sits beside, so
 * a row of controls lines up. The full-width .btn keeps its taller padding --
 * the sign-in button stands alone and benefits from the weight. */
.btn--inline {
  width: auto;
  padding: 9px 15px;
  white-space: nowrap;
  text-decoration: none;
}
.btn--primary { color: #fff; background: var(--brand); border-color: var(--brand); }

/* A secondary button sits on a faint grey, not on white, with a neutral
   border rather than the blue-grey used for inputs. Sampled from the design:
   surface #f9fafb, border #dcdee2, against a white page. */
.btn--quiet {
  color: var(--ink-soft);
  background: var(--surface-quiet);
  border-color: var(--border-quiet);
}

/* Blue-tinted, not grey. A quiet button is still an action, and a grey wash
 * reads as "disabled" rather than "hovered" -- especially beside the primary
 * buttons it sits next to. */
.btn--quiet:hover:not(.is-disabled):not(:disabled) {
  color: var(--brand);
  background: var(--brand-tint);
  border-color: #bcd0ee;
}

.btn--danger { color: #fff; background: var(--danger); border-color: var(--danger); }

/* Qualified to outrank `.btn:hover:not(:disabled)`, which sets the brand blue
 * and is more specific than a bare `.btn--danger`. A destructive button that
 * turns blue under the cursor stops reading as destructive at the exact moment
 * it matters. */
/* Deliberately one step more specific than `.btn:hover:not(:disabled)` rather
 * than merely later in the file. A tie broken by source order would put the
 * blue back the first time someone reorders this stylesheet. */
.btn--danger:hover:not(.is-disabled):not(:disabled) {
  background: var(--danger-dark);
  border-color: var(--danger-dark);
}

.is-disabled { opacity: .45; pointer-events: none; }

.filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 16px;
}

.filters__search { flex: 1 1 260px; }

.filters input,
.filters select {
  padding: 9px 11px;
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
}

.filters input:focus,
.filters select:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(26, 79, 160, .14);
}

.filters__clear { font-size: 13px; color: var(--ink-soft); }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  overflow: hidden;
}

.card__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 18px;
  border-top: 1px solid var(--border);
}

.pager { display: flex; gap: 8px; }

/* Tables can exceed the viewport; only this wrapper scrolls sideways, so the
 * page itself never does. */
.table-scroll { overflow-x: auto; }

.table { width: 100%; border-collapse: collapse; }

.table th {
  padding: 11px 18px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .6px;
  text-transform: uppercase;
  text-align: left;
  color: var(--ink-faint);
  background: var(--surface-sunken);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

.table td {
  padding: 13px 18px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

.table tbody tr:last-child td { border-bottom: 0; }
.table tbody tr:hover { background: var(--surface-sunken); }

.table__strong { font-weight: 600; }
.table__muted { color: var(--ink-soft); }

/* Qualified with th/td deliberately. `.table th` sets text-align: left and, at
 * one class plus one element, outranks a bare `.table__action` -- so the header
 * stayed left while the button sat right, and the column read as misaligned.
 *
 * The column also hugs its content: `width: 1%` makes the table give it only
 * what the button needs and hand the slack to the text columns, which have
 * something to do with it. */
.table th.table__action,
.table td.table__action {
  width: 1%;
  text-align: right;
  white-space: nowrap;
}

/* Status badge: a colour dot, then the label.
 *
 * inline-flex with align-items:center is what keeps the dot on the text's
 * optical centre. Floating a round pseudo-element next to inline text leaves
 * it sitting on the baseline instead, which reads as a pixel or two low at
 * this size. vertical-align:middle then centres the badge itself within the
 * table cell it sits in. */
.badge {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 3px 11px 3px 9px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.5;
  white-space: nowrap;
  vertical-align: middle;
  border: 1px solid transparent;
  /* The same corner as the buttons rather than a full pill, so badges and
     controls read as one set of shapes. */
  border-radius: var(--radius);
}

/* currentColor, so a variant sets one colour and the dot follows. A dot
 * defined per variant is a second thing to remember and get wrong. */
.badge::before {
  content: "";
  flex: none;
  width: 6px;
  height: 6px;
  background: currentColor;
  border-radius: 50%;
}

.badge--info {
  color: var(--brand);
  background: var(--brand-tint);
  border-color: #bcd0ee;
}

.badge--success {
  color: var(--success);
  background: var(--success-tint);
  border-color: #bfe0cd;
}

.badge--danger {
  color: var(--danger);
  background: var(--danger-tint);
  border-color: #f2c9c5;
}

.badge--warning {
  color: var(--warning);
  background: var(--warning-tint);
  border-color: #f0dcb4;
}

.badge--muted {
  color: var(--ink-soft);
  background: var(--surface-sunken);
  border-color: var(--border);
}

.empty { padding: 48px 24px; text-align: center; }
.empty__title { margin: 0 0 6px; font-size: 15px; font-weight: 600; }
.empty__body { margin: 0; color: var(--ink-soft); }

.field__hint { margin: 6px 0 0; font-size: 12.5px; color: var(--ink-faint); }

.field select {
  width: 100%;
  padding: 10px 12px;
  font: inherit;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
}

/* ------------------------------------------------------------------ */
/* Dialog                                                              */
/* ------------------------------------------------------------------ */

.dialog {
  width: min(440px, calc(100vw - 32px));
  padding: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: 0 18px 48px rgba(22, 32, 46, .22);
}

.dialog::backdrop { background: rgba(16, 24, 38, .45); }

/* For a form that is a whole KPI: five rating bands side by side need the
 * width, and at 440px each band would be a two-word column. */
.dialog--wide { width: min(880px, calc(100vw - 32px)); }

.dialog__inner { padding: 22px; }
.dialog__title { margin: 0 0 16px; font-size: 17px; font-weight: 700; }

.dialog__actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 20px;
}

@media (max-width: 860px) {
  .shell { grid-template-columns: 1fr; }
  .sidebar { flex-direction: row; align-items: center; gap: 14px; }
  .sidebar__nav { flex-direction: row; flex: 1; }
  .sidebar__user { padding-top: 0; border-top: 0; }
  .content { padding: 18px 16px 32px; }
  .topbar { padding: 14px 16px; }
}

/* ------------------------------------------------------------------ */
/* Report editor                                                       */
/* ------------------------------------------------------------------ */

.panel { padding: 18px 20px; margin-bottom: 16px; }

.panel__facts {
  display: flex;
  flex-wrap: wrap;
  gap: 34px;
  margin-bottom: 16px;
}

.panel__label {
  display: block;
  margin-bottom: 2px;
  font-size: 11px;
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* Smaller here than in the list. It sits under a small caption alongside three
 * plain text facts, so at list size it shouts over the things it sits with. */
.panel__facts .badge {
  gap: 6px;
  padding: 2px 9px 2px 7px;
  font-size: 11.5px;
}

.panel__facts .badge::before {
  width: 5px;
  height: 5px;
}

.panel__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding-bottom: 16px;
  border-bottom: 1px solid var(--border);
}

.save-state {
  margin-right: auto;
  font-size: 12.5px;
  color: var(--ink-faint);
}

.save-state.is-saving { color: var(--brand); }
.save-state.is-error  { color: var(--danger); font-weight: 600; }

.notice {
  margin: 14px 0 0;
  padding: 10px 12px;
  font-size: 13px;
  color: var(--ink-soft);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

/* A small menu hanging off a button: Download Report's two formats.
 *
 * Fixed, with its coordinates computed in JS, exactly as the custom dropdown
 * and the tooltips are. Absolute positioning put it inside the report panel,
 * which has overflow:hidden -- so the menu was clipped to whatever room the
 * panel happened to have, and in full-screen mode, where the panel is only as
 * tall as its buttons, it was cut away entirely and could not be used. */
.menu { position: relative; }

.menu__panel {
  position: fixed;
  z-index: 200;
  min-width: 190px;
  padding: 6px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
}

.menu__item {
  display: flex;
  align-items: center;
  gap: 9px;
  width: 100%;
  padding: 8px 10px;
  font: inherit;
  font-size: 13.5px;
  color: var(--ink);
  text-align: left;
  text-decoration: none;
  background: none;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}

.menu__item:hover,
.menu__item:focus-visible {
  color: var(--brand);
  background: var(--brand-tint);
  outline: none;
}

.menu__item svg { flex: none; color: var(--ink-faint); }
.menu__item:hover svg { color: var(--brand); }

/* Finance's standing send-authority, in the report panel.
 *
 * Brand-tinted rather than a warning: nothing is wrong, someone has simply
 * been given leave to act. */
.authority {
  display: flex;
  align-items: center;
  gap: 10px;
  margin: 14px 0 0;
  padding: 10px 14px;
  font-size: 13px;
  color: var(--ink-soft);
  background: var(--brand-tint);
  border: 1px solid #d3e0f5;
  border-radius: var(--radius);
}

.authority svg { flex: none; color: var(--brand); }
.authority strong { color: var(--ink); }
.authority .link-btn { margin-left: auto; font-size: 12px; white-space: nowrap; }

/* The outstanding return: what a reviewer sent back and why.
 *
 * Carries the danger tint because it is the one thing on the page that is
 * blocking progress, and the note itself is set in the reader's normal text
 * colour -- it is someone's words, not an error string. */
.returned {
  margin: 14px 0 0;
  padding: 12px 14px;
  background: var(--danger-tint);
  border: 1px solid #f2c9c5;
  border-radius: var(--radius);
}

.returned p { margin: 0; }

.returned__subject { font-weight: 600; color: var(--danger); }

/* Preserves the line breaks the reviewer typed, and wraps long words rather
   than pushing the card sideways. */
.returned__comment {
  margin-top: 4px;
  color: var(--ink);
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.returned__meta { margin-top: 6px; font-size: 12px; color: var(--ink-soft); }

/* Stepper — scrolls sideways on a narrow screen rather than wrapping into an
 * illegible pile. */
.stepper {
  display: flex;
  gap: 6px;
  margin: 16px 0 0;
  padding: 0;
  overflow-x: auto;
  list-style: none;
}

.stepper__step {
  display: flex;
  align-items: center;
  gap: 7px;
  padding: 6px 12px;
  font-size: 12.5px;
  white-space: nowrap;
  color: var(--ink-faint);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: 999px;
}

.stepper__index {
  display: grid;
  place-items: center;
  width: 18px;
  height: 18px;
  font-size: 10.5px;
  font-weight: 700;
  color: var(--ink-faint);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 50%;
}

.stepper__step.is-done { color: var(--success); background: var(--success-tint); border-color: #bfe0cd; }
.stepper__step.is-done .stepper__index { color: var(--success); border-color: #bfe0cd; }

.stepper__step.is-current {
  font-weight: 600;
  color: var(--brand);
  background: var(--brand-tint);
  border-color: #bcd0ee;
}

.stepper__step.is-current .stepper__index { color: #fff; background: var(--brand); border-color: var(--brand); }

.chips { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }

.chip {
  padding: 5px 12px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink-soft);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
}

.chip--good { color: var(--success); background: var(--success-tint); border-color: #bfe0cd; }
.chip--bad  { color: var(--danger);  background: var(--danger-tint);  border-color: #f2c9c5; }

/* The changes filter: a switch and its label, on the page rather than in a
   box. The chips beside it report a number; this one changes the view, and
   a border would file it with them. */
.changes-filter {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  user-select: none;
}


.issues {
  margin-bottom: 14px;
  padding: 10px 14px;
  font-size: 13px;
  background: var(--warning-tint);
  border: 1px solid #f0dcb4;
  border-radius: var(--radius);
}

.issues summary { font-weight: 600; color: var(--warning); cursor: pointer; }
.issues ul { margin: 8px 0 0; padding-left: 18px; color: var(--ink-soft); }

/* The grid.
 *
 * Only this wrapper scrolls sideways, so the page never does. The KPI-name
 * column is pinned, because a wide table whose row labels scroll away is
 * unreadable past the fourth column. */
.grid-scroll { overflow-x: auto; }

.grid { width: max-content; min-width: 100%; border-collapse: separate; border-spacing: 0; }

.grid th,
.grid td {
  padding: 8px 12px;
  text-align: left;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

.grid thead th {
  position: sticky;
  top: 0;
  z-index: 2;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .5px;
  text-transform: uppercase;
  white-space: nowrap;
  color: var(--ink-faint);
  background: var(--surface-sunken);
}

.grid__sticky {
  position: sticky;
  left: 0;
  z-index: 1;
  width: 400px;
  min-width: 400px;
  background: var(--surface);
  /* A shadow rather than a border: it marks the seam only while content is
   * actually scrolled underneath. */
  box-shadow: 1px 0 0 var(--border);
}

.grid thead .grid__sticky { z-index: 3; background: var(--surface-sunken); }

.grid__group th,
.grid__group td { background: var(--brand-tint); }

.grid__group .grid__sticky {
  font-size: 13px;
  font-weight: 700;
  background: var(--brand-tint);
}

/* Flex rows inside the cells, not on the cells themselves: a <th> has to stay
 * display:table-cell or its column drops out of the table's width
 * calculation. This is what puts the delete control on the text's centre line
 * -- it used to float, which aligns to the top of the line box instead. */
.grid__group-inner,
.grid__name-inner {
  display: flex;
  align-items: center;
  gap: 6px;
  width: 100%;
}

.grid__group-name { white-space: nowrap; }

/* Always visible, and pushed to the end of the row. Revealing a delete control
 * only on hover hides the fact that it exists, and leaves it unreachable to
 * anyone not using a mouse. */
.grid__delete { margin-left: auto; }

/* Sub-KPIs.
 *
 * Indented under the KPI they break down, one step per level, with a small
 * elbow so the relationship reads at a glance rather than from the gap alone.
 * The name input already takes the free space, so the row tools stay in their
 * straight columns however deep the row sits. */
.grid__sub .grid__name-inner { padding-left: calc(var(--depth, 1) * 20px); }
.grid__name-inner .grid__delete { margin-left: 0; }

.grid__branch {
  flex: none;
  width: 9px;
  height: 9px;
  margin: -6px 1px 0 0;
  border-left: 1.5px solid var(--border-strong);
  border-bottom: 1.5px solid var(--border-strong);
  border-bottom-left-radius: 3px;
}

.grid__sub .grid__name .cell { font-weight: 500; }

/* Every row tool occupies the same slot, and its glyph sits at the same place
 * within it, so the comment markers, the break-down forks and the bins each
 * form a straight column. The comment marker is wider only to the right of
 * its glyph, where its count goes; without this the tools after it were
 * pushed further along than the gap between themselves.
 *
 * The slot is as narrow as a two-digit count allows, and the tools close the
 * flex gap between themselves, so the three controls on a row read as one
 * group rather than as three scattered buttons. */
.grid__name-inner .row-tool,
.grid__group-inner .grid__delete {
  width: 34px;
  justify-content: flex-start;
  padding-left: 4px;
}

.grid__name-inner .row-tool + .row-tool { margin-left: -6px; }

/* The dimension's bin is the last thing in its header, as a KPI's bin is the
 * last thing in its row, so the two land in the same column. */
.grid__group-inner .grid__delete { margin-left: auto; }

/* Amber rather than the comment marker's blue: three tools on a row, three
 * answers to "which one am I on". Not a warning -- breaking a KPI down is an
 * ordinary edit -- but nothing else inside a row is amber, so it reads as
 * this control and no other. */
.grid__split:hover,
.grid__split:focus-visible {
  color: var(--warning);
  background: var(--warning-tint);
  outline: none;
}

/* A weight nobody types: it is what the sub-KPIs add up to. */
.cell--summed { color: var(--ink-soft); font-weight: 600; }

/* A main KPI below the 10% floor: the number in red, inside a red box. The
 * rule itself is on the cell's hover, and in the issue list above the grid. */
.grid__weight--thin .cell {
  color: var(--danger);
  font-weight: 600;
  border-color: var(--danger);
}

/* Red through hover and focus as well, where a cell otherwise turns grey and
 * then brand blue -- a border that changes colour on the way to being typed
 * in reads as though the problem has gone. */
.grid__weight--thin .cell:hover {
  border-color: var(--danger-dark);
  background: var(--danger-tint);
}

.grid__weight--thin .cell:focus {
  border-color: var(--danger);
  box-shadow: 0 0 0 3px rgba(192, 57, 43, .14);
}

/* A summed weight is text, not an input, and carries no box of its own. It
 * gets one here so a KPI that is too thin looks the same whether its weight
 * was typed or added up from its sub-KPIs. The second rule is not redundant:
 * without it the transparent border above wins on equal specificity, and the
 * summed cell is the one case that shows no box at all. */
.grid__weight .cell--static {
  border: 1px solid transparent;
  border-radius: 6px;
}

.grid__weight--thin .cell--static { border-color: var(--danger); }

.disclosure {
  padding: 2px;
  margin-right: 2px;
  color: var(--ink-soft);
  background: none;
  border: 0;
  border-radius: 4px;
  cursor: pointer;
  vertical-align: middle;
}

.disclosure svg { transition: transform .15s; }
.disclosure[aria-expanded="false"] svg { transform: rotate(-90deg); }
.disclosure:hover { background: rgba(26, 79, 160, .12); }

.grid__name { font-weight: 600; }
.grid__weight { width: 110px; }
.grid__add .grid__sticky { background: var(--surface); }
.grid__empty { padding: 34px; text-align: center; color: var(--ink-faint); }

.cell {
  width: 100%;
  min-width: 150px;
  padding: 6px 8px;
  font: inherit;
  color: var(--ink);
  background: transparent;
  border: 1px solid transparent;
  border-radius: 6px;
  /* Long definitions are the norm, and letting one set a column's width would
     push the other eight off the screen. Clipped with an ellipsis instead, and
     shown in full on hover. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cell:hover { border-color: var(--border); }

.cell:focus {
  outline: none;
  background: var(--surface);
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(26, 79, 160, .14);
}

.cell.is-dirty  { background: var(--warning-tint); }
.cell.is-error  { border-color: var(--danger); background: var(--danger-tint); }

/* Different from the last submission.
 *
 * A fill, sampled from the design: #ffeec7 on #ead4a9. What it means is on
 * hover -- the value it used to hold -- because the colour alone says only
 * "something about this cell".
 *
 * The fill is deliberately the only property it takes, because a weight can
 * be too thin and changed at the same time and both have to be readable. An
 * invalid weight marks itself with a red border and red bold figures and no
 * resting fill, so the two compose rather than overwrite each other: amber
 * behind, red around and through. Neither is worth losing -- one says fix
 * this before sending, the other says look at this before approving.
 */
.cell.is-changed {
  background: var(--changed-tint);
  border-color: var(--changed-edge);
}

/* The thin-weight hover paints the cell red to keep the problem visible
   while it is being typed in. On a cell that is also changed, the red border
   and red figures already say so, so the amber stays and the history is not
   lost for as long as the pointer is over it. */
.grid__weight--thin .cell.is-changed,
.grid__weight--thin .cell.is-changed:hover {
  background: var(--changed-tint);
  border-color: var(--danger);
}

.grid__weight--thin .cell.is-changed:hover { border-color: var(--danger-dark); }

/* A rejected edit outranks both. It is about the keystroke just made, it is
   actionable now, and it goes away as soon as the value is fixed. */
.cell.is-error,
.cell.is-error:hover {
  border-color: var(--danger);
  background: var(--danger-tint);
}

/* A line that was not in the last submission at all. The same amber, said
   once on the row: every field on it is new, and nine marked cells would
   say less than one bar. */
.grid tr.is-added .grid__name-inner::before {
  content: "";
  position: absolute;
  left: 0;
  top: 6px;
  bottom: 6px;
  width: 3px;
  background: var(--changed-edge);
  border-radius: 0 3px 3px 0;
}

.grid tr.is-added .grid__name-inner { position: relative; }

/* Showing the differences alone. Applied to the table, so a row that is
   neither changed nor a dimension heading simply stops being drawn. */
.grid.is-changes-only tr[data-kpi-row]:not([data-changed]) { display: none; }
.cell--static {
  display: block;
  min-width: 150px;
  /* The ellipsis above only bites once something caps the width. The grid is
     `width: max-content`, so a cell's own text is what sizes its column, and
     one rambling definition took the column to 1338px -- three times what the
     real ones need -- pushing weight and the five rating bands off screen.
     Capped here rather than on the column, because the editable cells are
     `width: 100%` and follow whatever the static ones settle on, so edit and
     read-only end up the same width. The rest is on hover. */
  max-width: 480px;
  padding: 6px 8px;
  color: var(--ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.grid__weight .cell { min-width: 70px; }
.grid__name .cell { flex: 1; min-width: 150px; }

/* The lock sits with the KPI name, muted so it reads as an annotation rather
 * than competing with the text it qualifies. */
.lock {
  display: inline-flex;
  flex: none;
  color: var(--ink-faint);
}

.tag {
  display: inline-block;
  margin-left: 8px;
  padding: 2px 8px;
  font-size: 11px;
  font-weight: 600;
  color: var(--ink-soft);
  white-space: nowrap;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: 999px;
}

/* The tag carries an icon, so it lines up like the badges do. */
.tag--lock {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  color: var(--ink-faint);
}

.tag--good { color: var(--success); background: var(--success-tint); border-color: #bfe0cd; }
.tag--bad  { color: var(--danger);  background: var(--danger-tint);  border-color: #f2c9c5; }

.icon-btn {
  /* Centred flex rather than the default, so a link and a button carrying
     the same glyph come out the same height. As an inline element an <a>
     puts its icon on the text baseline and takes its line-height with it,
     which made the download sit 1.4px above the bin beside it. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 4px 8px;
  font-size: 13px;
  color: var(--ink-faint);
  background: none;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}

/* Tones, one per kind of action. There is no bare `.icon-btn:hover`: every
   icon control here names its own colour, and a default would win over the
   ones declared earlier in this file -- the fork's amber among them. */
.icon-btn--brand:hover { color: var(--brand); background: var(--brand-tint); }
.icon-btn--danger:hover { color: var(--danger); background: var(--danger-tint); }

.link-btn {
  padding: 4px 0;
  font: inherit;
  font-weight: 600;
  color: var(--brand);
  background: none;
  border: 0;
  cursor: pointer;
}

.link-btn:hover { text-decoration: underline; }

.add-dimension { display: flex; gap: 8px; }

.add-dimension select {
  padding: 9px 11px;
  font: inherit;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
}

/* Bottom-aligned: "Needs improvement (2)" wraps to two lines while
 * "Outstanding (5)" does not, which would otherwise push some inputs down a
 * row and leave the group looking broken. */
.field-row {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 10px;
  align-items: end;
}
.field-row .field { margin-bottom: 12px; }
.field-row .field input { min-width: 0; }

.dialog__body { margin: 0; color: var(--ink-soft); }

/* When something follows the explanation -- a picker to choose from -- it
   needs air, or the label reads as part of the sentence above it. */
.dialog__body + .field { margin-top: 16px; }

/* Wide enough that the five rating-band labels each fit on one line. At 760px
 * a column was ~135px and "Needs improvement (2)" wrapped, which is what made
 * the row look ragged even once it was bottom-aligned. */
#kpi-dialog { width: min(940px, calc(100vw - 48px)); }

@media (max-width: 760px) {
  .field-row { grid-template-columns: 1fr 1fr; }
}

/* ------------------------------------------------------------------ */
/* Back link, nav icons, save failures, full screen                    */
/* ------------------------------------------------------------------ */

/* Plain text at rest, a tinted pill on hover.
 *
 * It is navigation rather than an action, so it does not need to advertise
 * itself as a control the way Save Draft does. A transparent border rather
 * than none keeps its box the same size in both states, so nothing shifts
 * under the pointer. */
.back-link {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 14px;
  padding: 9px 13px;
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1;
  color: var(--ink);
  text-decoration: none;
  background: transparent;
  border: 1px solid transparent;
  border-radius: var(--radius);
  transition: background .15s, color .15s;
}

.back-link:hover {
  color: var(--brand);
  background: var(--brand-tint);
}

.sidebar__nav a {
  display: flex;
  align-items: center;
  gap: 9px;
}

/* display:block removes the inline baseline gap under an SVG, which is what
 * leaves an icon looking a pixel or two high next to its label. flex:none stops
 * it being squashed when the label is long. */
.btn svg,
.back-link svg {
  display: block;
  flex: none;
}

/* Only failures are announced. A running "saved" confirmation is noise on a
 * page where every field saves itself. */
.save-error {
  margin-left: auto;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--danger);
}

/* Full screen gives the grid the whole viewport. The chrome around it is not
 * what anyone is reading nine columns of KPI data for. */
/* Two selectors for one mode. `is-fullscreen` on <body> is the live state;
 * `is-fullscreen-boot` is set on <html> by a script in <head>, before the body
 * exists, so a reload after an edit does not flash the chrome back in. */
.is-fullscreen .sidebar,
.is-fullscreen .topbar,
.is-fullscreen .back-link,
.is-fullscreen .panel__facts,
.is-fullscreen .stepper,
.is-fullscreen-boot .sidebar,
.is-fullscreen-boot .topbar,
.is-fullscreen-boot .back-link,
.is-fullscreen-boot .panel__facts,
.is-fullscreen-boot .stepper { display: none; }

.is-fullscreen .shell,
.is-fullscreen-boot .shell { grid-template-columns: 1fr; }

.is-fullscreen .content,
.is-fullscreen-boot .content { padding: 14px; }

.is-fullscreen .panel,
.is-fullscreen-boot .panel { margin-bottom: 12px; }

.is-fullscreen .panel__actions,
.is-fullscreen-boot .panel__actions { padding-bottom: 0; border-bottom: 0; }

/* ------------------------------------------------------------------ */
/* Interactive affordances (user flow §12)                             */
/*                                                                      */
/* "Clickable controls use a pointer cursor and are visually presented  */
/* as interactive elements."                                            */
/* ------------------------------------------------------------------ */

/* Every select, wherever it is styled. Browsers default a select to the text
 * cursor, which makes a dropdown feel inert until it is clicked. Applied to
 * the element rather than to each of the three class rules that style one, so
 * a select added later cannot be missed. */
select { cursor: pointer; }

/* The same hover the quiet buttons use -- brand text on a tint -- so every
 * control in the filter bar responds the same way as the buttons beside it.
 * Distinct from focus, which says "your typing lands here" rather than merely
 * "this responds". */
.filters input:hover,
.filters .select__trigger:hover,
.add-dimension .select__trigger:hover,
.add-dimension select:hover {
  color: var(--brand);
  background: var(--brand-tint);
  border-color: #bcd0ee;
}

/* A hovered search field keeps its white background: tinting the surface a
 * user is about to type into makes the text harder to read for no gain. */
.filters input:hover { background: var(--surface); }

.field input:hover,
.field .select__trigger:hover,
.field select:hover {
  border-color: var(--brand);
}

/* A text field keeps the text cursor -- it is typed into, not clicked -- but
 * still shows it responds to the pointer. */
.filters input,
.field input { transition: border-color .15s, box-shadow .15s; }

.filters select:focus,
.field select:focus,
.add-dimension select:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(26, 79, 160, .14);
}

/* The search field's native clear button, where the browser offers one. */
.filters input[type="search"]::-webkit-search-cancel-button { cursor: pointer; }

/* Rows in the report list are not clickable -- the Open Report button is -- so
 * the row highlight must not suggest otherwise. Left as the default cursor
 * deliberately. */

/* ------------------------------------------------------------------ */
/* Custom select                                                       */
/*                                                                      */
/* A native <option> cannot be styled -- the open list is drawn by the  */
/* operating system -- so a dropdown that matches the design has to be  */
/* built from ordinary elements.                                        */
/*                                                                      */
/* The real <select> stays in the DOM, hidden but still form-bound, so  */
/* the filter form keeps submitting normally and still works with no    */
/* JavaScript at all. This is decoration over a working control, not a  */
/* replacement for one.                                                 */
/* ------------------------------------------------------------------ */

.select { position: relative; display: inline-block; }

/* Hidden from sight and from the tab order: the custom trigger takes focus,
 * and two focusable controls for one value would be a keyboard trap. */
.select > select {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  border: 0;
}

.select__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  width: 100%;
  padding: 9px 11px;
  font: inherit;
  color: var(--ink);
  text-align: left;
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  cursor: pointer;
  transition: border-color .15s, box-shadow .15s;
}

.select__trigger:hover { border-color: var(--brand); }

/* A prompt reads as one, not as a value already chosen. */
.select.is-placeholder .select__trigger { color: var(--ink-faint); }

.select__trigger:focus-visible,
.select.is-open .select__trigger {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(26, 79, 160, .14);
}

.select__chevron {
  flex: none;
  color: var(--ink-faint);
  transition: transform .15s;
}

.select.is-open .select__chevron { transform: rotate(180deg); }

/* Positioned against the viewport rather than the trigger.
 *
 * As an absolutely positioned child it was trapped inside whatever scrolls:
 * a <dialog> has a max-height and overflow:auto, so a long list made the
 * dialog itself scroll and clipped the panel's rounded edge. Fixed positioning
 * escapes every scrolling ancestor; the coordinates are set in select.js from
 * the trigger's position, and refreshed while the list is open. */
.select__panel {
  position: fixed;
  z-index: 100;
  max-height: 280px;
  padding: 5px;
  overflow-y: auto;
  overscroll-behavior: contain;   /* scrolling the list never scrolls the page */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(22, 32, 46, .14),
              0 2px 6px rgba(22, 32, 46, .06);
}

.select__option {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  width: 100%;
  padding: 9px 11px;
  font: inherit;
  color: var(--ink);
  text-align: left;
  white-space: nowrap;
  background: none;
  border: 0;
  border-radius: 8px;
  cursor: pointer;
  transition: color .1s, background .1s;
}

/* One highlight class for both hover and keyboard, so arrowing through the
 * list looks exactly like moving the mouse through it.
 *
 * A step stronger than --brand-tint: that tint is used for large fills like
 * the dimension header row, where it has area to read from. A single 38px row
 * needs slightly more to register. */
.select__option:hover,
.select__option.is-active {
  color: var(--brand);
  background: var(--brand-hover);
}

.select__option[aria-selected="true"] {
  font-weight: 600;
  color: var(--brand);
}

.select__check { flex: none; opacity: 0; color: var(--brand); }
.select__option[aria-selected="true"] .select__check { opacity: 1; }

/* Inside the filter bar the trigger should size like the native control it
 * replaced, rather than collapsing to its text. */
.filters .select { min-width: 168px; }
.field .select,
.field .select__trigger { width: 100%; }

/* ------------------------------------------------------------------ */
/* Field validation tooltip                                            */
/*                                                                      */
/* Anchored to the field it describes. A message in the page header is  */
/* easy to miss entirely when the eye is on a cell nine columns across. */
/* ------------------------------------------------------------------ */

.field-tip {
  position: fixed;              /* escapes the grid's horizontal scroll */
  z-index: 200;
  /* Wide enough that a whole definition does not become a narrow column of
     twenty lines. Nothing is cut short here: the cell clips what it cannot
     fit and this is where the rest is read. */
  max-width: 340px;
  /* A tip may be several lines -- what a value was, who moved it, when --
     and each belongs on its own. Long single lines still wrap. */
  white-space: pre-line;
  padding: 6px 11px;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.4;
  color: #fff;
  background: var(--brand);
  border-radius: 6px;
  box-shadow: 0 6px 16px rgba(22, 32, 46, .22);
  /* Never intercepts a click meant for the field underneath. */
  pointer-events: none;
}

.field-tip[hidden] { display: none; }

/* Pointer down at the field. Positioned in JS along with the tip itself. */
.field-tip::after {
  content: "";
  position: absolute;
  top: 100%;
  left: var(--tip-arrow, 14px);
  border: 5px solid transparent;
  border-top-color: var(--brand);
}

/* Flipped below when there is no room above, so the pointer follows. */
.field-tip--below::after {
  top: auto;
  bottom: 100%;
  border-top-color: transparent;
  border-bottom-color: var(--brand);
}

.field-tip--info { z-index: 190; font-weight: 500; }

/* Each tip takes the colour of the control it belongs to: amber for breaking
 * a KPI down, red for removing one. `--error` is the editor's sticky
 * field error, which is red for its own reason and shares the rules. */
.field-tip--warning { background: var(--warning); }
.field-tip--warning::after { border-top-color: var(--warning); }
.field-tip--warning.field-tip--below::after {
  border-top-color: transparent;
  border-bottom-color: var(--warning);
}

.field-tip--error,
.field-tip--danger { background: var(--danger); }

.field-tip--error::after,
.field-tip--danger::after { border-top-color: var(--danger); }

.field-tip--error.field-tip--below::after,
.field-tip--danger.field-tip--below::after {
  border-top-color: transparent;
  border-bottom-color: var(--danger);
}

/* ------------------------------------------------------------------ */
/* Comments                                                            */
/* ------------------------------------------------------------------ */

/* A borderless button that tints blue on hover: the Comments button.
 *
 * The hover selector carries :not(:disabled) deliberately. A bare
 * `.btn--ghost:hover` is less specific than `.btn:hover:not(:disabled)`, which
 * paints the dark brand fill -- the same collision that turned the quiet and
 * danger buttons the wrong colour before. */
.btn--ghost {
  color: var(--ink);
  background: transparent;
  border-color: transparent;
}

.btn--ghost:hover:not(.is-disabled):not(:disabled) {
  color: var(--brand);
  background: var(--brand-tint);
  border-color: transparent;
}

/* "3 · 2 new" beside the Comments button. Blue when something is unread,
   grey when everything has been seen. */
.count-badge {
  display: inline-flex;
  align-items: center;
  padding: 1px 7px;
  font-size: 11px;
  font-weight: 700;
  line-height: 1.6;
  white-space: nowrap;
  color: var(--ink-soft);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: 6px;
}

.count-badge--new { color: #fff; background: var(--brand); border-color: var(--brand); }

/* The list column. Same two tones as the badge, with the bubble glyph. */
.comment-pill {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 8px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--ink-soft);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: 6px;
}

.comment-pill--new { color: #fff; background: var(--brand); border-color: var(--brand); }

/* No comments. The same box as a pill, without its fill, so the dash begins
   exactly where a pill's bubble does and stands at the same height -- as bare
   text it sat a few pixels to the left of the column the pills form. */
.comment-pill--none {
  color: var(--ink-faint);
  background: none;
  border-color: transparent;
}

/* The tools at the end of a KPI name cell: comments, then delete.
 *
 * One fixed box for both, so they stand in two straight columns down the
 * grid. The bin used to be an inline element sitting on the text baseline
 * while the comment marker was a centred flex box, which put them at
 * different heights side by side. Rows without a bin carry a spacer of the
 * same size, or their marker would slide into the bin column. */
.row-tool {
  display: inline-flex;
  flex: none;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 28px;
  padding: 0;
}

.row-tool--spacer { visibility: hidden; }

/* The comment marker. Always shown, so a conversation about a KPI can be
 * started from the KPI itself.
 *
 * Wider than the bin, with the icon pinned left, so the bubbles line up
 * whether a row shows no count, "2" or "12". */
.kpi-comments {
  justify-content: flex-start;
  gap: 3px;
  width: 42px;
  padding-left: 7px;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-faint);
  background: none;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}

.kpi-comments.has-comments { color: var(--ink-soft); }

/* Optical, not geometric, alignment with the fork and the bin beside it.
 *
 * Centring the boxes is not enough. A speech bubble's tail hangs below its
 * body, so centring the whole glyph puts its body -- the part the eye reads
 * as the icon -- about a pixel above its neighbours. The pixel down corrects
 * that. The count's digits sit on a text baseline, a couple of pixels below
 * the body, so they come up by one.
 *
 * What made the bubble look low was not this nudge but its size: it was drawn
 * at 14px against the other two at 15, and a smaller box centred in the same
 * 28px slot starts half a pixel low before the nudge adds its own. Drawn at
 * 15px the three glyph bodies agree to within 0.3px, measured in Chrome. */
.kpi-comments svg { transform: translateY(1px); }
.kpi-comments [data-kpi-comment-count] { transform: translateY(-1px); }

.kpi-comments:hover,
.kpi-comments:focus-visible {
  color: var(--brand);
  background: var(--brand-tint);
  outline: none;
}

/* Unread activity: solid brand blue, as in the design. Hover darkens rather
   than turning pale, so it does not look as though it has just been read. */
.kpi-comments.has-new { color: #fff; background: var(--brand); }

.kpi-comments.has-new:hover,
.kpi-comments.has-new:focus-visible { color: #fff; background: var(--brand-dark); }

/* The drawer: a modal <dialog> pinned to the right edge, full height.
 *
 * No padding on the dialog itself, so a click whose target *is* the dialog
 * can only have landed on the backdrop -- which is how comments.js closes it
 * on an outside click. */
.drawer {
  width: min(480px, 100vw);
  max-width: 100vw;
  height: 100%;
  max-height: 100%;
  margin: 0 0 0 auto;
  padding: 0;
  border: 0;
  border-left: 1px solid var(--border);
  box-shadow: -18px 0 48px rgba(22, 32, 46, .18);
}

.drawer::backdrop { background: rgba(16, 24, 38, .45); }

.drawer__inner {
  display: flex;
  flex-direction: column;
  height: 100%;
  background: var(--surface-sunken);
}

.drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  padding: 16px 18px 10px;
  background: var(--surface);
}

.drawer__title { margin: 0; font-size: 16px; font-weight: 700; }

.drawer__close {
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  color: var(--ink-soft);
  background: none;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}

/* The same blue tint as the other quiet controls. The grey it had before was
   the drawer's own background, so hovering showed nothing at all. */
.drawer__close:hover,
.drawer__close:focus-visible {
  color: var(--brand);
  background: var(--brand-tint);
  outline: none;
}

/* Channel tabs wrap rather than scroll: a CPM role sees five, and a tab
   hidden off to the side is a conversation nobody finds. */
.drawer__tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  padding: 0 14px 10px;
  background: var(--surface);
}

.drawer__tab {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  font: inherit;
  font-size: 13px;
  color: var(--ink-soft);
  background: none;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
}

.drawer__tab:hover { color: var(--brand); background: var(--brand-tint); }

.drawer__tab.is-active {
  font-weight: 600;
  color: var(--brand);
  background: var(--brand-tint);
}

.drawer__tab-count {
  min-width: 18px;
  padding: 0 5px;
  font-size: 11px;
  font-weight: 700;
  line-height: 18px;
  text-align: center;
  color: #fff;
  background: var(--brand);
  border-radius: 9px;
}

.drawer__note {
  margin: 0;
  padding: 8px 18px;
  font-size: 12px;
  color: var(--ink-faint);
  background: var(--surface);
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}

.drawer .form-error { margin: 12px 14px 0; }

.drawer__filter {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin: 12px 14px 0;
  padding: 7px 12px;
  font-size: 13px;
  color: var(--brand);
  background: var(--brand-tint);
  border-radius: var(--radius);
}

.drawer__filter .link-btn { font-size: 12px; }

.drawer__body {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 12px 14px;
}

.drawer__empty {
  margin: 36px 0;
  text-align: center;
  font-size: 13px;
  color: var(--ink-faint);
}

/* What the files under it belong to: the report itself, or one KPI. Small
   and quiet -- it is a divider, not a heading competing with the names. */
.drawer__group {
  margin: 16px 0 6px;
  padding: 0 4px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .5px;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.drawer__group:first-child { margin-top: 0; }

/* One recorded difference, in the archive's changes drawer. */
.change {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 4px 8px;
  padding: 8px 10px;
  border-radius: var(--radius);
}

.change + .change { border-top: 1px solid var(--border); border-radius: 0; }

/* What kind of difference it is, or which field moved. */
.change__kind {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .3px;
  text-transform: uppercase;
  color: var(--ink-faint);
}

.change__kind--add { color: var(--success); }
.change__kind--delete { color: var(--danger); }

/* Before and after, marked up as what they are: del and ins carry the
   meaning to a screen reader, which a pair of coloured spans would not. */
.change__values {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
  width: 100%;
  font-size: 13px;
}

/* The old value, still legible: it is being compared, not discarded. */
.change__values del {
  color: var(--ink-faint);
  text-decoration: line-through;
  text-decoration-color: var(--border-strong);
}

.change__values ins {
  color: var(--ink);
  font-weight: 600;
  text-decoration: none;
}

/* Its own element between the two, so it sits evenly and is not struck
   through along with the value it follows. Hidden from screen readers,
   which read del and ins for themselves. */
.change__arrow {
  flex: none;
  color: var(--ink-faint);
}

.change__who {
  width: 100%;
  font-size: 12px;
  color: var(--ink-faint);
}

/* A file in the drawer. The whole row is the download link: there is one
   thing to do with it, and a panel this narrow has no room for a column
   that repeats what the name already offers. */
.filerow {
  display: flex;
  align-items: center;
  gap: 11px;
  padding: 9px 10px;
  color: inherit;
  text-decoration: none;
  border-radius: var(--radius);
}

.filerow:hover { background: var(--brand-tint); }
.filerow:hover .filerow__icon { color: var(--brand); }

.filerow__icon {
  display: inline-flex;
  flex: none;
  color: var(--ink-faint);
}

/* Clipped, not wrapped: a long name must not push the facts under it out of
   line with its neighbours. */
.filerow__text { display: flex; flex-direction: column; min-width: 0; }

.filerow__text strong {
  overflow: hidden;
  font-size: 13.5px;
  font-weight: 600;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.filerow__text small {
  font-size: 12px;
  color: var(--ink-faint);
}

.drawer__foot {
  padding: 12px 14px 14px;
  background: var(--surface);
  border-top: 1px solid var(--border);
}

.drawer__foot textarea,
.thread__reply-box textarea {
  display: block;
  width: 100%;
  min-height: 64px;
  padding: 10px 12px;
  font: inherit;
  color: var(--ink);
  background: var(--surface);
  border: 1px solid var(--border-strong);
  border-radius: var(--radius);
  resize: vertical;
}

.drawer__foot textarea:focus,
.thread__reply-box textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px rgba(26, 79, 160, .16);
}

.drawer__foot-actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  margin-top: 10px;
}

.drawer__hint { font-size: 12px; color: var(--ink-faint); }

.drawer__closed {
  margin: 0;
  padding: 14px 18px;
  font-size: 13px;
  color: var(--ink-soft);
  background: var(--surface);
  border-top: 1px solid var(--border);
}

/* One conversation. */
.thread {
  margin-bottom: 10px;
  padding: 12px 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

/* Unread: a brand edge rather than a fill, so a drawer full of new threads
   still reads as text and not as a wall of blue. */
.thread.is-new { padding-left: 12px; border-left: 3px solid var(--brand); }

.thread p { margin: 0; }

.thread__head {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}

.avatar--sm { flex: none; width: 26px; height: 26px; font-size: 10px; }

.thread__name { font-size: 13px; }

.role-tag {
  padding: 1px 7px;
  font-size: 11px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--brand);
  background: var(--brand-tint);
  border-radius: 5px;
}

.thread__new {
  padding: 1px 6px;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: #fff;
  background: var(--brand);
  border-radius: 4px;
}

.thread__time {
  margin-left: auto;
  font-size: 12px;
  white-space: nowrap;
  color: var(--ink-faint);
}

/* `.thread p` resets margins to zero; these two are qualified to add their
   own back rather than fighting it with !important. */
.thread p.thread__about { margin-top: 8px; font-size: 12px; color: var(--brand); }

/* Line breaks as the writer typed them; long words wrap instead of pushing
   the card sideways. */
.thread p.thread__message {
  margin-top: 6px;
  font-size: 14px;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}

.thread__actions {
  display: flex;
  align-items: center;
  gap: 14px;
  margin-top: 8px;
  font-size: 12px;
}

.thread__actions .link-btn { font-size: 12px; }

.thread__status {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 0;
  font: inherit;
  font-size: 12px;
  color: var(--ink-soft);
  background: none;
  border: 0;
}

button.thread__status { cursor: pointer; }
button.thread__status:hover { color: var(--brand); }
.thread__status.is-resolved { color: var(--success); }

.thread__replies {
  margin-top: 10px;
  padding-left: 12px;
  border-left: 2px solid var(--border);
}

.thread__reply + .thread__reply { margin-top: 10px; }
.thread__reply-mark { color: var(--ink-faint); }

.thread__reply-box { margin-top: 10px; }

.thread__edited {
  margin-left: -4px;
  font-size: 12px;
  white-space: nowrap;
  color: var(--ink-faint);
  cursor: default;
}

/* Edit and Delete on what the reader wrote, kept apart from Reply and
 * Resolve at the far end of the row. */
.thread__own {
  display: inline-flex;
  align-items: center;
  gap: 14px;
  margin-left: auto;
}

.thread__reply-own { display: flex; justify-content: flex-end; margin-top: 2px; }

.link-btn--edit,
.link-btn--danger {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  font-size: 12px;
  font-weight: 500;
}

.link-btn--edit { color: var(--ink-soft); }
.link-btn--edit:hover { color: var(--brand); }
.link-btn--danger { color: var(--danger); }
.link-btn--danger:hover { color: var(--danger-dark); }

.thread__edit-box { margin-top: 8px; }

.thread__confirm {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-top: 10px;
  padding: 10px 12px;
  background: var(--danger-tint);
  border-radius: 8px;
}

.thread__confirm-text {
  flex: 1 1 160px;
  font-size: 13px;
  font-weight: 600;
  color: var(--danger);
}

.thread__reply-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  margin-top: 8px;
}

.btn--sm { padding: 6px 12px; font-size: 13px; }

/* "View Version": compact, dark text on white with a faint lift, as in the
   design. Hover comes from .btn--quiet -- a light blue fill, blue border and
   blue text -- so it matches every other quiet button. */
/* Look only, no size: the secondary buttons in the report panel wear it at
   their own size, beside the primary action. Pair it with .btn--sm for the
   compact version of it. */
.btn--view {
  color: var(--ink);
  border-color: var(--border-quiet);
  box-shadow: 0 1px 2px rgba(22, 32, 46, .06);
}

/* ------------------------------------------------------------------ */
/* Tabs on the report page                                             */
/* ------------------------------------------------------------------ */

/* A segmented control, as in the design: a grey track, the open tab lifted
   out of it in white. Plain links underneath, so each tab has a URL. */
.tabs {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 2px;
  margin: 4px 0 16px;
  padding: 4px;
  background: #eef1f6;
  border-radius: var(--radius);
}

.tabs__tab {
  padding: 7px 14px;
  font-size: 14px;
  font-weight: 600;
  color: var(--ink-soft);
  text-decoration: none;
  border-radius: 6px;
}

.tabs__tab:hover { color: var(--brand); }

.tabs__tab.is-active {
  color: var(--ink);
  background: var(--surface);
  box-shadow: 0 1px 3px rgba(22, 32, 46, .12);
}

/* ------------------------------------------------------------------ */
/* Version history                                                     */
/* ------------------------------------------------------------------ */

.versions { margin: 0; padding: 0; list-style: none; }

/* Icon, who and when, status, what it did, the button.
 *
 * The middle columns have a preferred width so all five line up from row to
 * row -- with auto columns, each description started wherever its row's chip
 * happened to end. They can give room back when the window is narrow, though:
 * fixed at 280 and 270 they squeezed the description into a six-line ribbon
 * around 1150px, and the row grew to twice its height. */
.versions__row {
  display: grid;
  grid-template-columns:
    17px minmax(190px, 280px) minmax(170px, 270px) minmax(230px, 1fr) auto;
  align-items: center;
  gap: 16px;
  padding: 17px 18px;
}

.versions__row + .versions__row { border-top: 1px solid var(--border); }

.versions__icon { display: inline-flex; color: var(--ink-faint); }

.versions__who strong { display: flex; align-items: center; gap: 8px; font-size: 15px; }
.versions__who small { display: block; margin-top: 2px; font-size: 12.5px; color: var(--ink-soft); }

.versions__latest {
  padding: 1px 7px;
  font-size: 11px;
  font-weight: 700;
  color: var(--brand);
  background: var(--brand-tint);
  border-radius: 5px;
}

/* Neutral on purpose: where the report went then, not its state now. */
.versions__status {
  justify-self: start;
  padding: 3px 9px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--ink-soft);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: 6px;
}

.versions__what { margin: 0; font-size: 13px; color: var(--ink-soft); }

.versions__empty { margin: 0; padding: 22px 18px; font-size: 13px; color: var(--ink-faint); }

/* Narrow screens: stack rather than squeeze the description to one word a
   line. */
/* Below this the five columns cannot all be read, so they stack rather than
   shrink into slivers. */
@media (max-width: 1100px) {
  .versions__row { grid-template-columns: auto 1fr; }
  .versions__status,
  .versions__what,
  .versions__row .btn { grid-column: 2; justify-self: start; }
}

/* ------------------------------------------------------------------ */
/* An archived version                                                 */
/* ------------------------------------------------------------------ */

.archived-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-bottom: 18px;
}

.archived-head__title {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-size: 16px;
  font-weight: 700;
}

.archived-head__title svg { color: var(--ink-soft); }

/* Amber, as in the design: a caution that nothing here can be changed. */
.archived-badge {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px 9px;
  font-size: 12px;
  font-weight: 600;
  color: var(--warning);
  background: var(--warning-tint);
  border: 1px solid #f0dcb4;
  border-radius: 6px;
}

/* Clear of the divider above it, like everything else below that line -- the
   notice, the return banner, the authority note all stand 14px off it. With
   margin:0 this one sat against it. */
.archived-what { margin: 14px 0 0; font-size: 13px; color: var(--ink-soft); }

.chip--total { color: var(--ink); background: var(--surface-sunken); }

/* ------------------------------------------------------------------ */
/* The Archive page                                                    */
/* ------------------------------------------------------------------ */

.filters__report { min-width: 260px; }

/* Names read as one unit; the table scrolls sideways before it breaks one. */
.table__nowrap { white-space: nowrap; }

.archive-note { margin: -6px 0 16px; font-size: 13px; color: var(--ink-faint); }

.archive-head {
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 3px 16px;
  padding: 16px 18px;
  border-bottom: 1px solid var(--border);
}

.archive-head small { grid-column: 1; font-size: 12.5px; color: var(--ink-soft); }

.archive-head__open {
  grid-row: 1 / span 2;
  grid-column: 2;
  align-self: center;
  font-size: 13px;
  font-weight: 600;
  color: var(--brand);
  text-decoration: none;
}

.archive-head__open:hover { text-decoration: underline; }

.empty-state { padding: 34px; text-align: center; color: var(--ink-faint); }
.empty-state p { margin: 0; }

/* ------------------------------------------------------------------ */
/* The error page                                                      */
/* ------------------------------------------------------------------ */

.error-page {
  display: grid;
  place-items: center;
  min-height: 100vh;
  padding: 24px;
}

.error-page__card {
  width: min(480px, 100%);
  padding: 34px 32px;
  text-align: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
}

.error-page__mark {
  display: inline-grid;
  place-items: center;
  width: 38px;
  height: 38px;
  color: #fff;
  background: var(--brand);
  border-radius: var(--radius);
}

.error-page__status {
  margin: 18px 0 0;
  font-size: 13px;
  font-weight: 700;
  letter-spacing: .08em;
  color: var(--ink-faint);
}

.error-page__title { margin: 4px 0 8px; font-size: 22px; font-weight: 700; }

.error-page__message {
  margin: 0 auto;
  max-width: 38ch;
  color: var(--ink-soft);
}

.error-page__actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 10px;
  margin-top: 24px;
}

/* ------------------------------------------------------------------ */
/* Print                                                               */
/* ------------------------------------------------------------------ */

/*
 * "PDF (print)" is this stylesheet plus the browser's own dialog. What comes
 * out is the report: its facts, its weights and the whole grid -- without the
 * navigation, the controls, or anything else that only makes sense to click.
 *
 * Landscape, because the grid is nine columns wide and portrait would either
 * shrink it past reading or cut it in half.
 */
@page {
  size: A4 landscape;
  margin: 12mm;
}

/* ------------------------------------------------------------------ */
/* Administration                                                      */
/* ------------------------------------------------------------------ */

/* Said once, above the table: nothing on these screens takes effect when it
 * is saved, and that is the first thing to know about them. */
.admin-note {
  margin: 0 0 14px;
  padding: 10px 14px;
  font-size: 13px;
  color: var(--ink-soft);
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-left: 3px solid var(--brand);
  border-radius: var(--radius);
}

/* A deactivated account is still listed -- it holds history -- but it is not
 * the thing anyone is looking for. */
tr.is-inactive .table__strong,
tr.is-inactive td { color: var(--ink-faint); }

/* The change, field by field. A grid rather than a table: it sits inside a
 * dialog, where a table's own layout fights the dialog's width. */
.diff {
  display: grid;
  gap: 1px;
  margin: 4px 0 18px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

.diff__row {
  display: grid;
  grid-template-columns: 150px 1fr 1fr;
  gap: 12px;
  padding: 9px 14px;
  font-size: 13px;
  background: var(--surface);
}

.diff__row--head {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--ink-faint);
  background: var(--surface-sunken);
}

/* Only what actually differs. Everything else is context, and colouring it
 * would leave a reviewer hunting for the change again. */
.diff__row--changed { background: var(--warning-tint); }
.diff__row--changed .diff__value--to { font-weight: 700; color: var(--ink); }

.diff__field { font-weight: 600; color: var(--ink-soft); }
.diff__value { overflow-wrap: anywhere; }

/* Wider than the default dialog: this one is two columns of text to compare,
 * and at 440px every value wraps to three lines. */
#request-dialog { width: min(760px, calc(100vw - 32px)); }
#user-dialog { width: min(620px, calc(100vw - 32px)); }

.dialog__note {
  margin: -8px 0 14px;
  font-size: 13px;
  color: var(--ink-soft);
}

/* The people table.
 *
 * A person is a name, and the rest of the row describes them, so the name
 * carries the avatar and the username while the columns beside it stay one
 * fact each: their address, what the app lets them do, what they are called
 * at work, and where they work.
 */
.people__person {
  display: flex;
  align-items: center;
  gap: 10px;
  white-space: nowrap;
}

.people__name { display: flex; flex-direction: column; line-height: 1.25; }
.people__name strong { font-weight: 600; }
.people__name small { font-size: 12px; color: var(--ink-faint); }

.people__email { color: var(--brand); }
.people__segments { font-size: 13px; color: var(--ink-soft); }

/* A flex row *inside* the cell, never on the cell itself: a <td> set to
 * display:flex leaves table layout, and its contents then sit outside the
 * column -- here, on top of the segments beside them. */
.people__tools {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: 10px;
}

.people__edit:hover:not(:disabled) { color: var(--brand); background: var(--brand-tint); }
.people__edit:disabled { opacity: .4; cursor: not-allowed; }

/* A new password is not destructive, but it does lock someone out of the one
   they have, so it takes the same amber as breaking a KPI down: its own
   colour, distinct from edit beside it. */
.people__reset:hover:not(:disabled) { color: var(--warning); background: var(--warning-tint); }
.people__reset:disabled { opacity: .4; cursor: not-allowed; }

.people__pending {
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .03em;
  text-transform: uppercase;
  color: var(--warning);
}

/* What the app lets someone do, as a tag. Coloured by family rather than by
 * rank, so a list of twenty accounts can be scanned for "who is CPM".
 *
 * Colours and corner taken from the design, sampled rather than guessed: a
 * softly rounded rectangle, not a lozenge. */
.role-pill {
  display: inline-block;
  padding: 4px 10px;
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
  border-radius: 6px;
}

/* The three CPM roles that read reports share one blue; the one that routes
   them is solid, which is the only pill that carries weight in a list. */
.role-pill--cpm         { color: #15448c; background: #e7f1ff; }
.role-pill--manager     { color: #ffffff; background: #1b54aa; }
.role-pill--coordinator { color: #2e8bb9; background: #e9f3f7; }
.role-pill--finance     { color: #14874e; background: #e7f2ed; }
.role-pill--leader      { color: #2f1e02; background: #f9f0df; }

/* Active or not, as a switch rather than a bin: an account is never deleted,
 * it is turned off, and a switch says that where a bin would promise
 * something the app does not do. */
.switch {
  position: relative;
  flex: none;
  width: 38px;
  height: 22px;
  padding: 0;
  background: var(--border-strong);
  border: 0;
  border-radius: 999px;
  cursor: pointer;
  transition: background .15s;
}

.switch__knob {
  position: absolute;
  top: 3px;
  left: 3px;
  width: 16px;
  height: 16px;
  background: #fff;
  border-radius: 50%;
  box-shadow: 0 1px 2px rgba(22, 32, 46, .3);
  transition: transform .15s;
}

.switch.is-on { background: var(--success); }

/* A switch that filters a view rather than turning something on or
   off. Blue, because green here would read as a state of the report. */
.switch--filter.is-on { background: var(--brand); }
.switch.is-on .switch__knob { transform: translateX(16px); }

.switch:hover:not(:disabled):not(.switch--fixed) { filter: brightness(1.08); }
.switch:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }

.switch:disabled { opacity: .45; cursor: not-allowed; }

/* Your own account: shown, never offered. */
.switch--fixed { cursor: default; opacity: .55; }

/* Master data: four short tables on one screen, each introduced by what it
   is for -- the rows alone do not say what a "dimension" decides. */
.master { margin-bottom: 18px; }

.master__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 18px 12px;
}

.master__title { margin: 0; font-size: 15px; font-weight: 700; }

/* The button a section carries in its own header, rather than in the topbar:
 * these act on the section under them, not on the page. */
.master__actions { display: flex; flex: none; gap: 8px; }

/* A file picker is an input the browser draws its own way, so the control
 * people press is its label and the input itself is hidden. Nothing else
 * makes a label look pressable, so it is said here. */
.master__actions label.btn { cursor: pointer; }

.master__note {
  margin: 3px 0 0;
  font-size: 12.5px;
  color: var(--ink-soft);
  max-width: 70ch;
}

/* A corporate KPI's wording, read rather than filled in: label on the left,
 * the sentence on the right, so the five rating bands line up as the one
 * scale they are. */
.kpi-detail {
  display: grid;
  grid-template-columns: minmax(0, 220px) minmax(0, 1fr);
  gap: 10px 20px;
  margin: 0;
  padding-top: 16px;
  border-top: 1px solid var(--border);
}

.kpi-detail dt {
  font-size: 12px;
  font-weight: 600;
  color: var(--ink-faint);
}

.kpi-detail dd {
  margin: 0;
  font-size: 13.5px;
  color: var(--ink);
}

@media (max-width: 720px) {
  .kpi-detail { grid-template-columns: minmax(0, 1fr); gap: 2px 0; }
  .kpi-detail dd { margin-bottom: 10px; }
}

/* Segment checkboxes: as many as there are, wrapping rather than scrolling. */
.checks {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px 18px;
  margin-top: 8px;
  padding: 12px 14px;
  background: var(--surface-sunken);
  border: 1px solid var(--border);
  border-radius: var(--radius);
}

/* Box then label, on one line.
 *
 * Both selectors are deliberately more specific than the field rules they
 * sit inside: `.field label` is display:block, which would drop the grid,
 * and `.field input` is width:100%, which stretched the tick across the
 * row and left its label underneath. */
.checks .check {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
  gap: 8px;
  margin: 0;
  font-size: 13.5px;
  font-weight: 500;
  color: var(--ink);
  /* The box is the control; the label is its name.
   *
   * A label fills its grid cell, so clicking anywhere in the row -- including
   * the empty space past the words -- toggled the box, which reads as a
   * misclick rather than a choice. Pointer events are taken off the label and
   * given back to the box alone, which also keeps the cursor an arrow
   * everywhere except over the box itself.
   *
   * The `for`/wrapping association stays, so a screen reader still reads the
   * segment's name as the box's label. */
  pointer-events: none;
  cursor: default;
}

/* "(inactive)" beside a segment nobody can be newly assigned to. */
.check__note { color: var(--ink-faint); font-weight: 400; }

.checks .check input[type="checkbox"] {
  width: 15px;
  height: 15px;
  margin: 0;
  padding: 0;
  accent-color: var(--brand);
  pointer-events: auto;
  cursor: pointer;
}

/* A card that is a form rather than a table: your own password lives in one. */
.form-card { padding: 22px 24px 24px; max-width: 520px; }
.form-card__title { margin: 0 0 4px; font-size: 16px; font-weight: 700; }
.form-card .field { margin-top: 14px; }
.form-card__actions { margin-top: 18px; }

.form-ok {
  margin: 12px 0 0;
  padding: 9px 12px;
  font-size: 13px;
  font-weight: 600;
  color: var(--success);
  background: var(--success-tint);
  border-radius: 8px;
}

.form-ok[hidden] { display: none; }

/* The sidebar's name is a link to your own account, and has to look like one
 * only when pointed at -- it is a person's name, not a button. */
a.sidebar__user-text { text-decoration: none; }
a.sidebar__user-text:hover strong { color: var(--brand); }

@media print {
  /* Nothing that can be pressed, and nothing that navigates. */
  .sidebar,
  .crumbs,
  .back-link,
  .panel__actions,
  .tabs,
  .filters,
  .pager,
  .issues,
  .disclosure,
  .row-tool,
  .grid__add,
  .save-error,
  .drawer,
  dialog,
  .card__footer,
  .add-dimension,
  .field-tip { display: none !important; }

  body { background: #fff; }
  .shell { display: block; }
  .main { display: block; }
  .content { padding: 0; }

  .topbar {
    position: static;
    padding: 0 0 10px;
    border: 0;
  }

  .topbar__title { font-size: 16pt; }

  /* Flat on paper: a shadow prints as a grey smear, and a card's own border
     doubles the grid's. */
  .card {
    box-shadow: none;
    border: 0;
    margin-bottom: 10px;
  }

  .panel__facts { gap: 24px; }

  /* The grid is scrolled on screen and pinned at its first column. On paper
     there is nowhere to scroll, so it lays out in full. */
  .grid-scroll { overflow: visible; }

  .grid {
    width: 100%;
    font-size: 8.5pt;
    table-layout: fixed;
  }

  .grid th,
  .grid td { padding: 4px 6px; }

  /* Column headings wrap instead of being clipped: on screen they are one
     line and the table scrolls, but a printed page cannot scroll and
     "Unsatisfacto" helps nobody. */
  .grid thead th {
    white-space: normal;
    font-size: 7.5pt;
    /* The tracking that spaces these out on screen is what pushed
       "Unsatisfactory" past its column and clipped it. */
    letter-spacing: 0;
    overflow-wrap: anywhere;
    vertical-align: bottom;
  }

  /* Fixed layout needs the shares stated, or the browser splits the width
     evenly and the wide text columns spill over their neighbours. The KPI
     name and its definition carry sentences; the five bands are short. */
  .grid th:nth-child(1), .grid td:nth-child(1) { width: 15%; }
  .grid th:nth-child(2), .grid td:nth-child(2) { width: 21%; }
  .grid th:nth-child(3), .grid td:nth-child(3) { width: 5%; }
  .grid th:nth-child(4), .grid td:nth-child(4) { width: 13%; }
  /* Wide enough for "Unsatisfactory" to stay one word. */
  .grid th:nth-child(n+5), .grid td:nth-child(n+5) { width: 9.2%; }

  .grid__sticky { position: static; }

  .cell,
  .cell--static {
    /* On screen a cell clips to one line and reveals the rest on hover.
       Paper has no hover, so every cell shows all of its text. The screen's
       width cap goes with it: here the column shares below decide the width,
       and a cap inside them could only hold text back. */
    height: auto;
    max-width: none;
    overflow: visible;
    white-space: normal;
    text-overflow: clip;
  }

  input.cell {
    padding: 0;
    border: 0;
    background: none;
  }

  /* A folded dimension is a viewing preference, not a decision about what the
     report contains: everything prints. */
  .grid tr[hidden] { display: table-row !important; }

  /* Keep a row whole, and repeat the column headings on every page. */
  .grid tr { break-inside: avoid; }
  .grid thead { display: table-header-group; }

  a[href]::after { content: ""; }      /* no URLs spelled out after links */
}
