/* @fileoverview The stylesheet's entry point: one partial per section, in cascade order. `npm run css` compiles
   it into public/css/app.css, which is committed; edit the partials, never the compiled file. */
/* @fileoverview Design tokens: the only place a raw size, colour or duration is written. Every rule elsewhere
   uses these. Colours are semantic roles, and dark mode (P2-07) redefines the roles and nothing else.

   The palette is the old app's (its static/scss/base/_variables.scss and _darkmode.scss), trimmed to the roles this
   app uses, with a value moved only where it fell short of the contrast it needs: text and every on-colour pair at
   least 4.5:1, control borders, filled buttons and the focus ring at least 3:1 against the page
   (test/css-theme.test.js computes each pair in both schemes).

   Which scheme a page gets: the account's setting puts data-theme="light" or "dark" on the html element
   (views/partials/top.ejs); with the setting at System there is no attribute and the device's own preference
   decides. Dark is for the screen only, so anything printed, such as the interview brief, is dark ink on white
   paper whatever the scheme. */
:root {
  /* Spacing, on a 4px step. */
  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.5rem;
  --space-6: 2rem;
  --space-7: 3rem;
  --space-8: 4rem;
  /* Type. */
  --font-sans: system-ui, -apple-system, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  --text-sm: 0.875rem;
  --text-base: 1rem;
  --text-lg: 1.125rem;
  --text-xl: 1.5rem;
  --text-2xl: 2rem;
  --leading-tight: 1.25;
  --leading: 1.55;
  --tracking-caps: 0.04em;
  --weight-normal: 400;
  --weight-bold: 650;
  --measure: 70ch;
  --underline-offset: 0.15em;
  --underline-offset-current: 0.3em;
  --page-width: 64rem;
  --field-min: 14rem;
  --field-narrow: 8rem;
  --cell-label: 7.5rem; /* The column of labels in a table collapsed to one block per row on a phone. */
  --self-view-width: 20rem; /* The practice session's picture of himself (P3-07): small, as on a video call. */
  --self-view-ratio: 16 / 9; /* The camera's shape as asked for, 640x360. */
  /* Shape and depth. */
  --radius-sm: 0.25rem; /* Buttons and fields: the old app's 4px corners. */
  --radius-md: 0.5rem;
  --radius-lg: 0.75rem;
  --radius-bubble: 1.25rem; /* A chat message's rounded corners, after the old app's message bubbles. */
  --border-width: 1px;
  --rule-width: 3px; /* The rule beside set-off words, such as a tighter version in a practice review (P3-04). */
  --shadow-1: 0 1px 2px rgb(0 0 0 / 0.08); /* Shadows are colours too: darker in dark mode, to show on a dark page. */
  --shadow-2: 0 4px 12px rgb(0 0 0 / 0.12);
  /* Motion. Every transition and animation stops under reduced motion (styles/_base.scss). */
  --dur-fast: 120ms;
  --dur: 200ms;
  --dur-turn: 800ms; /* One turn of the working mark beside a busy button. */
  --ease: cubic-bezier(0.2, 0, 0, 1);
  /* The working mark: a ring in the button's own text colour, open on one side. */
  --working-size: 1em;
  --working-width: 2px;
  /* Touch and focus. */
  --touch-min: 44px;
  --focus-width: 3px;
  --focus-offset: 2px;
  --opacity-disabled: 0.6;
  /* Stacking. */
  --z-header: 10;
  --z-dialog: 100;
  --z-skip-link: 1000;
  /* Colour roles, light. The old app's Resolution Blue, gunmetal text, white smoke and slate gray. Native controls
     (scroll bars, the file field's button, radio buttons) follow color-scheme. */
  color-scheme: light dark;
  --surface: #ffffff;
  --surface-raised: #f5f5f5;
  --surface-sunken: #ebebeb;
  --border: #d6d6d6; /* Rules and the edges of cards: decoration, not a control's boundary. */
  --border-control: #747c92; /* The edge of a field or an outlined button: 3:1 against the page. */
  --text: #1e3231;
  --text-muted: #5c6378;
  --accent: #1a237e;
  --accent-strong: #10165a;
  --text-on-accent: #ffffff;
  --success: #2e7d32;
  --text-on-success: #ffffff;
  --danger: #c62828;
  --danger-strong: #8e1c1c;
  --text-on-danger: #ffffff;
  --focus-ring: #1a237e;
  /* Print (P3-01): the printable interview brief. Ink and paper stay black on white whatever the scheme on screen,
     so dark mode leaves these alone. */
  --print-text: 11pt;
  --print-ink: #000000;
  --print-paper: #ffffff;
}

/* Light, chosen on the account: native controls stay light too, whatever the device prefers. */
:root[data-theme=light] {
  color-scheme: light;
}

/* Colour roles, dark: the old app's charcoal, light indigo and light cyan. The same block twice, once for a device
   that prefers dark when the account says System, once for Dark chosen on the account; keep the two the same
   (test/css-theme.test.js fails when they differ). Screen only, so a printed page keeps the light roles. */
@media screen and (prefers-color-scheme: dark) {
  :root:not([data-theme=light]) {
    color-scheme: dark;
    --surface: #303030;
    --surface-raised: #383838;
    --surface-sunken: #262626;
    --border: #4f4f4f;
    --border-control: #8a8a8a;
    --text: #e0f7fa;
    --text-muted: #b0bec5;
    --accent: #c5cae9;
    --accent-strong: #e8eaf6;
    --text-on-accent: #1a237e;
    --success: #81c784;
    --text-on-success: #1b1b1b;
    --danger: #f28b82;
    --danger-strong: #ffb4ab;
    --text-on-danger: #1b1b1b;
    --focus-ring: #c5cae9;
    --shadow-1: 0 1px 2px rgb(0 0 0 / 0.4);
    --shadow-2: 0 4px 12px rgb(0 0 0 / 0.5);
  }
}
@media screen {
  :root[data-theme=dark] {
    color-scheme: dark;
    --surface: #303030;
    --surface-raised: #383838;
    --surface-sunken: #262626;
    --border: #4f4f4f;
    --border-control: #8a8a8a;
    --text: #e0f7fa;
    --text-muted: #b0bec5;
    --accent: #c5cae9;
    --accent-strong: #e8eaf6;
    --text-on-accent: #1a237e;
    --success: #81c784;
    --text-on-success: #1b1b1b;
    --danger: #f28b82;
    --danger-strong: #ffb4ab;
    --text-on-danger: #1b1b1b;
    --focus-ring: #c5cae9;
    --shadow-1: 0 1px 2px rgb(0 0 0 / 0.4);
    --shadow-2: 0 4px 12px rgb(0 0 0 / 0.5);
  }
}
/* Whatever the scheme on screen, paper is printed light. */
@media print {
  :root,
  :root[data-theme] {
    color-scheme: light;
  }
}
/* US Letter, for any page printed. Custom properties do not reach an @page rule, so its size and margin are
   written here, with the tokens, and nowhere else. */
@page {
  size: letter;
  margin: 0.6in;
}
/* @fileoverview Element defaults, the one focus style, the skip link and reduced motion: the account's setting
   (data-motion on the html element, views/partials/top.ejs), or the device's preference when the setting is System. */
*,
*::before,
*::after {
  box-sizing: border-box;
}

html {
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  margin: 0;
  background: var(--surface);
  color: var(--text);
  font-family: var(--font-sans);
  font-size: var(--text-base);
  line-height: var(--leading);
  overflow-wrap: anywhere;
}

/* The hidden attribute wins over any display a class sets, such as .button's inline-flex, so a control the script
   has not shown yet stays hidden without JavaScript. */
[hidden] {
  display: none !important;
}

h1,
h2,
h3 {
  line-height: var(--leading-tight);
  margin: 0 0 var(--space-4);
}

h1 {
  font-size: var(--text-2xl);
}

h2 {
  font-size: var(--text-xl);
}

h3 {
  font-size: var(--text-lg);
}

p,
ul,
ol {
  margin: 0 0 var(--space-4);
  max-width: var(--measure);
}

a {
  color: var(--accent);
  text-underline-offset: var(--underline-offset);
}

code,
pre {
  font-family: var(--font-mono);
  font-size: var(--text-sm);
}

/* The one focus style for every control. Never add a second one or remove an outline. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: var(--focus-width) solid var(--focus-ring);
  outline-offset: var(--focus-offset);
}

.skip-link {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-min);
  position: absolute;
  left: var(--space-4);
  top: calc(-1 * var(--space-8));
  z-index: var(--z-skip-link);
  padding: var(--space-2) var(--space-4);
  background: var(--accent);
  color: var(--text-on-accent);
  border-radius: var(--radius-sm);
}

.skip-link:focus {
  top: var(--space-2);
}

.lead {
  font-size: var(--text-lg);
}

.muted {
  color: var(--text-muted);
}

/* Pasted text such as a posting keeps its line breaks. */
.pre-wrap {
  white-space: pre-wrap;
}

/* Reduced motion, by one rule in two places: the device asks for it and the account has not chosen Full, or the
   account has chosen Reduced. Every transition and animation, the working mark's included, stops at once, and
   scrolling jumps instead of gliding. Keep the two blocks the same (test/css-theme.test.js). */
@media (prefers-reduced-motion: reduce) {
  :root:not([data-motion=full]),
  :root:not([data-motion=full]) *,
  :root:not([data-motion=full]) *::before,
  :root:not([data-motion=full]) *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}
:root[data-motion=reduced],
:root[data-motion=reduced] *,
:root[data-motion=reduced] *::before,
:root[data-motion=reduced] *::after {
  animation-duration: 0.01ms !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
  scroll-behavior: auto !important;
}

/* @fileoverview The page frame: header and navigation, the content column, the footer. */
.site-header {
  border-bottom: var(--border-width) solid var(--border);
  background: var(--surface-raised);
}

.site-header__inner,
.site-footer__inner,
.page {
  max-width: var(--page-width);
  margin: 0 auto;
  padding-left: var(--space-4);
  padding-right: var(--space-4);
}

.site-header__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-5);
  min-height: var(--space-8);
}

.site-header__name {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-min);
  color: var(--text);
  font-weight: var(--weight-bold);
  font-size: var(--text-lg);
  text-decoration: none;
}

.site-nav__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin: 0;
  padding: 0;
  list-style: none;
}

.site-nav__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-min);
  padding: 0 var(--space-3);
  border-radius: var(--radius-sm);
  color: var(--text);
  text-decoration: none;
}

.site-nav__form {
  display: inline;
  margin: 0;
}

.site-nav__button {
  border: 0;
  background: none;
  font: inherit;
  cursor: pointer;
}

/* On a phone the links sit closer, so the seven of them take two lines under the app's name instead of three. Each
   is still at least 44px tall and wider than that. The same 600px as the tables' (styles/_tables.scss). */
@media (max-width: 37.5em) {
  .site-nav__link {
    padding: 0 var(--space-2);
  }
}
.site-nav__link[aria-current=page] {
  font-weight: var(--weight-bold);
  text-decoration: underline;
  text-underline-offset: var(--underline-offset-current);
}

@media (hover: hover) {
  .site-nav__link:hover {
    background: var(--surface-sunken);
  }
}
.site-nav__link:active {
  background: var(--surface-sunken);
}

.page {
  padding-top: var(--space-6);
  padding-bottom: var(--space-7);
}

.site-footer {
  border-top: var(--border-width) solid var(--border);
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2);
  min-height: var(--space-8);
}

.site-footer__version {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--touch-min);
  min-width: var(--touch-min);
  padding: 0 var(--space-2);
  color: var(--text-muted);
}

.section {
  margin-bottom: var(--space-7);
}

.totals {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-4) var(--space-6);
  margin: 0 0 var(--space-4);
}

.totals__item dt {
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.totals__item dd {
  margin: 0;
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
}

.toc__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1) var(--space-3);
  margin: 0 0 var(--space-6);
  padding: 0;
  list-style: none;
}

.toc__link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--touch-min);
  min-width: var(--touch-min);
}

.actions-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.tabs__list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-1);
  margin: var(--space-5) 0 var(--space-4);
  padding: 0;
  border-bottom: var(--border-width) solid var(--border);
  list-style: none;
}

.tabs__link {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-min);
  padding: 0 var(--space-4);
  color: var(--text);
  text-decoration: none;
}

.tabs__link[aria-current=page] {
  font-weight: var(--weight-bold);
  box-shadow: inset 0 calc(-1 * var(--focus-width)) 0 var(--accent);
}

.posting {
  max-width: var(--measure);
  padding: var(--space-4);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
}

.status {
  display: inline-block;
  padding: 0 var(--space-2);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-sm);
  font-size: var(--text-sm);
  white-space: nowrap;
}

/* A link that stands on its own, not inside a sentence, gets a full-size target (agent/ui-standards.md). */
.table__link,
.link-target {
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-min);
  min-width: var(--touch-min);
}

/* The posting analysis (P1-09): plain lists, the terms worth echoing as a wrapped row, and the names offer. */
.analysis-list {
  max-width: var(--measure);
  margin: 0 0 var(--space-5);
  padding-left: var(--space-5);
}

.analysis-list li {
  margin-bottom: var(--space-1);
}

.analysis-list--terms {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  padding-left: 0;
  list-style: none;
}

.analysis-list--terms li {
  margin: 0;
  padding: var(--space-1) var(--space-2);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-raised);
  font-size: var(--text-sm);
}

.suggestion {
  max-width: var(--measure);
  margin: 0 0 var(--space-5);
  padding: var(--space-4);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-md);
}

/* The tailored resume (P1-10): the preview as a page, with the gaps and notes beside it on a wide screen and
   under it on a narrow one. */
.resume-layout {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: var(--space-5);
  margin-bottom: var(--space-6);
}

.resume-preview {
  flex: 3 1 var(--measure);
  min-width: 0;
  padding: var(--space-5);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  box-shadow: var(--shadow-1);
}

.resume-preview h3 {
  margin: var(--space-5) 0 var(--space-2);
  padding-bottom: var(--space-1);
  border-bottom: var(--border-width) solid var(--border);
  font-size: var(--text-base);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
}

.resume-preview h4 {
  margin: 0;
  font-size: var(--text-base);
}

.resume-preview__name {
  margin-bottom: var(--space-1);
}

.resume-preview__headline {
  margin: 0 0 var(--space-1);
  font-weight: var(--weight-bold);
}

.resume-preview__contact,
.resume-preview__meta {
  margin: 0 0 var(--space-2);
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.resume-preview__entry {
  margin-bottom: var(--space-4);
}

.resume-preview__entry ul,
.resume-preview__skills {
  margin: 0;
  padding-left: var(--space-5);
}

.resume-aside {
  flex: 1 1 var(--field-min);
  min-width: 0;
}

.resume-aside h2 {
  font-size: var(--text-lg);
}

.versions {
  padding-left: var(--space-5);
}

.release {
  margin-bottom: var(--space-6);
}

.release__date {
  color: var(--text-muted);
  font-size: var(--text-base);
  font-weight: var(--weight-normal);
}

.release__where {
  font-weight: var(--weight-bold);
}

/* @fileoverview Buttons: at least 44px, hover only where there is a pointer that hovers, and a press state; the old
   app's look (square-ish 4px corners, bold words, a raised fill for the plain button); and the working state of a
   button whose call to Claude is under way. */
.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-height: var(--touch-min);
  min-width: var(--touch-min);
  padding: 0 var(--space-4);
  border: var(--border-width) solid var(--border-control);
  border-radius: var(--radius-sm);
  background: var(--surface-raised);
  color: var(--text);
  font: inherit;
  font-weight: var(--weight-bold);
  text-decoration: none;
  cursor: pointer;
  touch-action: manipulation;
  transition: background-color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}

.button--primary {
  border-color: var(--accent);
  background: var(--accent);
  color: var(--text-on-accent);
}

.button--danger {
  border-color: var(--danger);
  background: var(--danger);
  color: var(--text-on-danger);
}

.button:disabled {
  cursor: not-allowed;
  opacity: var(--opacity-disabled);
}

@media (hover: hover) {
  .button:hover:not(:disabled) {
    background: var(--surface-sunken);
  }
  .button--primary:hover:not(:disabled) {
    background: var(--accent-strong);
  }
  .button--danger:hover:not(:disabled) {
    background: var(--danger-strong);
  }
}
.button:active:not(:disabled) {
  background: var(--surface-sunken);
}

.button--primary:active:not(:disabled) {
  background: var(--accent-strong);
}

.button--danger:active:not(:disabled) {
  background: var(--danger-strong);
}

/* A button whose call is under way (public/js/stream-form.js, chat.js and usage.js set data-working on the button
   pressed, and take it off when the call ends): still disabled, so a second press does not pay twice, but drawn at
   full strength with a turning mark after its words, so it reads as working rather than unavailable. The mark is
   decoration (the status line under the button says what is happening), and it stands still under reduced motion. */
.button[data-working] {
  cursor: progress;
}

.button[data-working]:disabled {
  opacity: 1;
}

.button[data-working]::after {
  content: "";
  flex: none;
  width: var(--working-size);
  height: var(--working-size);
  border: var(--working-width) solid currentColor;
  border-right-color: transparent;
  border-radius: 50%;
  animation: working-turn var(--dur-turn) linear infinite;
}

@keyframes working-turn {
  to {
    transform: rotate(1turn);
  }
}
/* @fileoverview Forms: labelled fields with the old app's 4px corners and an edge that stands out from the page, and a
   refused form's note beside the button and under each field. */
.field {
  display: grid;
  gap: var(--space-1);
  margin-bottom: var(--space-4);
  max-width: var(--measure);
}

.field__label {
  font-weight: var(--weight-bold);
}

.field__hint {
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.input,
.textarea,
.select {
  width: 100%;
  min-height: var(--touch-min);
  padding: var(--space-2) var(--space-3);
  border: var(--border-width) solid var(--border-control);
  border-radius: var(--radius-sm);
  background: var(--surface);
  color: var(--text);
  font: inherit;
}

.textarea {
  min-height: calc(var(--touch-min) * 4);
  resize: vertical;
}

.input[aria-invalid=true],
.textarea[aria-invalid=true],
.select[aria-invalid=true] {
  border-color: var(--danger);
}

.field__error,
.form__note {
  color: var(--danger);
  font-size: var(--text-sm);
}

.notice {
  padding: var(--space-3) var(--space-4);
  border: var(--border-width) solid var(--success);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  max-width: var(--measure);
}

.notice--mock {
  border-color: var(--accent);
}

.notice--warning {
  border-color: var(--danger);
}

.stream-status,
.progress-status {
  color: var(--text-muted);
  font-size: var(--text-sm);
}

/* Says a call to Claude is under way (public/js/import.js). Always in the page, so it is read out; no room while empty. */
.progress-status:empty {
  margin: 0;
}

/* A file field: the browser draws its own button inside, so less padding than a text field. */
.input--file {
  padding: var(--space-2);
}

.stream-output {
  min-height: calc(var(--touch-min) * 2);
  max-width: var(--measure);
  padding: var(--space-3) var(--space-4);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
  white-space: pre-wrap;
}

.field-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--field-min), 1fr));
  gap: 0 var(--space-4);
  max-width: var(--measure);
}

/* A group of radio buttons under one label, such as the cover letter's length (P1-14). */
.choice {
  min-width: 0;
  max-width: var(--measure);
  margin: 0 0 var(--space-4);
  padding: 0;
  border: 0;
}

.choice legend {
  margin-bottom: var(--space-1);
  padding: 0;
}

.checkbox {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--touch-min);
  margin-bottom: var(--space-3);
  cursor: pointer;
}

.checkbox input {
  width: var(--space-5);
  height: var(--space-5);
  margin: 0;
  accent-color: var(--accent);
}

.entry-list {
  margin: 0 0 var(--space-4);
  padding: 0;
  list-style: none;
  max-width: var(--measure);
}

.entry {
  margin-bottom: var(--space-4);
  padding: var(--space-4);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-md);
}

.entry__heading {
  font-size: var(--text-lg);
}

.entry__controls {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--space-2) var(--space-4);
}

.entry__order-field {
  max-width: var(--field-narrow);
  margin-bottom: 0;
}

.entry__buttons {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

/* Says what Remove did (public/js/profile.js), in the removed entry's place. Always in the page, so it is read out;
   takes no room while empty. */
.entry__status {
  margin: 0;
}

/* A removed entry waits for the save as the line that says so, where the entry was, so the person sees it beside the
   entry that takes the focus. Its fields stay in the form, ticked to be removed. */
.entry--removed {
  padding-block: var(--space-2);
  border-style: dashed;
}

.entry--removed > :not(.entry__status) {
  display: none;
}

/* With JavaScript the buttons do the ordering and removing, so the order number and the Remove box are not shown. */
.js .entry__order-field,
.js .entry__remove-field {
  display: none;
}

.form__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-3);
}

/* @fileoverview Tables of records (the applications, the AI calls, past imports). On a wide screen, a table that scrolls
   inside its box if it must. On a phone (below 600px), each row becomes one block and each cell a line with its
   column's name beside it, from the cell's data-label, which the template prints from the same list as the header
   (P2-07). The page's tables also carry their roles (table, rowgroup, row, columnheader, cell), because a browser that
   draws table parts as blocks may stop telling a screen reader it is a table; with the roles it stays one, header
   cells included. */
.table-wrap {
  max-width: 100%;
  overflow-x: auto;
  margin-bottom: var(--space-5);
}

.table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--text-sm);
}

.table th,
.table td {
  overflow-wrap: normal;
  white-space: nowrap;
  padding: var(--space-2) var(--space-3);
  border-bottom: var(--border-width) solid var(--border);
  text-align: left;
  /* Baseline, so a cell's text lines up with a 44px link's text in the next cell. */
  vertical-align: baseline;
}

.table th {
  color: var(--text-muted);
  font-weight: var(--weight-bold);
}

/* The phone breakpoint, the stylesheet's one width breakpoint (the top bar's links use it too): 600px at the default
   text size, in em so it moves with a larger text setting. Custom properties do not reach a media query, so it is
   written where it is used. */
@media (max-width: 37.5em) {
  .table--stack,
  .table--stack tbody,
  .table--stack tr,
  .table--stack td {
    display: block;
  }
  /* The header row is out of sight but still read: each cell shows its own column's name instead. */
  .table--stack thead {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }
  .table--stack tr {
    padding: var(--space-2) 0;
    border-bottom: var(--border-width) solid var(--border);
  }
  /* Two columns, the name and the value. Each of the app's cells holds one thing (a word, a link, a date), which is
     the value; a cell holding words and elements side by side would need a wrapper around them. */
  .table--stack td {
    display: grid;
    grid-template-columns: var(--cell-label) minmax(0, 1fr);
    gap: var(--space-3);
    align-items: baseline;
    padding: var(--space-1) 0;
    border-bottom: 0;
    overflow-wrap: anywhere;
    white-space: normal;
  }
  /* The column's name, seen but not read out a second time where the browser takes the alternative text after the
     slash; one that does not reads the name, as the line's label. */
  .table--stack td::before {
    content: attr(data-label);
    content: attr(data-label)/"";
    color: var(--text-muted);
    font-weight: var(--weight-bold);
  }
  /* A cell with nothing in it (no location, say) keeps its line, with a dash for its value that a screen reader does
     not read out. Never display: none here: it takes the cell out of the table a screen reader is told about, and every
     later cell in the row is then read under the wrong column's name. */
  .table--stack td:empty::after {
    content: "-";
    content: "-"/"";
    color: var(--text-muted);
  }
}
/* @fileoverview The interview brief (P3-01): its questions, what to be ready for and the questions to ask, on the
   Interview tab and on the printable view; and the printable view's own page, plain on screen and black on white on
   US Letter paper when printed (the paper size is in _tokens.scss). */
.brief {
  max-width: var(--measure);
  margin-bottom: var(--space-6);
}

.brief__group {
  margin-top: var(--space-5);
}

.brief__list {
  margin: 0 0 var(--space-4);
  padding-left: var(--space-5);
}

.brief__item {
  margin-bottom: var(--space-4);
}

.brief__question {
  margin: 0 0 var(--space-1);
  font-weight: var(--weight-bold);
}

.brief__detail {
  margin: 0 0 var(--space-1);
}

.brief__label {
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.brief__answers {
  margin: 0 0 var(--space-1);
  padding-left: var(--space-5);
}

.print-sheet {
  max-width: var(--page-width);
  margin: 0 auto;
  padding: var(--space-6) var(--space-4) var(--space-7);
}

@media print {
  .print-page {
    background: var(--print-paper);
    color: var(--print-ink);
    font-size: var(--print-text);
  }
  .print-page .muted,
  .print-page .brief__label {
    color: var(--print-ink);
  }
  .print-page h1 {
    font-size: var(--text-xl);
  }
  .print-page h2 {
    font-size: var(--text-lg);
  }
  .print-page h3 {
    font-size: var(--text-base);
  }
  .print-sheet,
  .print-page .brief {
    max-width: none;
    padding: 0;
  }
  .print-hint {
    display: none;
  }
  .print-page .brief__item {
    break-inside: avoid;
  }
  .print-page h2,
  .print-page .brief__group {
    break-after: avoid;
  }
}
/* @fileoverview The chat component (P3-03; the free chat and the profile interview reuse it), the practice part of
   the Interview tab, and the Coach's list of conversations (P4-01). A Copy reply button, which public/js/chat.js adds
   under each of the Coach's finished replies, sits in a wrapping row with the line that says whether it copied. The message list has no height or overflow of its own: the page scrolls, so every message can be
   scrolled back to and nothing is cut off, at any width (the old app's chat stopped scrolling after a CSS change;
   Ryan's 2024 whiteboard, R38). public/js/chat.js keeps the newest line in view as a reply arrives, unless the person
   has scrolled up to read. Do not give the list a max-height, an overflow or a fixed position without asking Ryan. */
.chat {
  max-width: var(--measure);
}

.chat__list {
  margin: 0 0 var(--space-4);
  padding: 0;
  list-style: none;
}

.chat__message {
  margin: 0 0 var(--space-4);
  padding: var(--space-3) var(--space-4);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-bubble);
  /* An address ending in #latest opens with the newest message clear of the top edge. */
  scroll-margin-top: var(--space-4);
}

/* Bubbles, after the old app's messages: rounded, with the corner nearest the speaker's side left square. */
.chat__message--assistant {
  border-top-left-radius: var(--radius-sm);
  background: var(--surface-raised);
}

.chat__message--user {
  margin-left: var(--space-5);
  border-top-right-radius: var(--radius-sm);
  background: var(--surface);
}

.chat__who {
  margin: 0 0 var(--space-1);
  color: var(--text-muted);
  font-size: var(--text-sm);
  font-weight: var(--weight-bold);
}

.chat__time {
  font-weight: var(--weight-normal);
}

.chat__body {
  min-width: 0;
  overflow-wrap: anywhere;
}

/* What the person wrote, a reply still arriving (built by chat.js without data-rendered), and Markdown the libraries
   could not render keep their line breaks. A rendered reply is HTML and takes its breaks from its own elements. */
.chat__body--plain,
.chat__body:not([data-rendered]) {
  white-space: pre-wrap;
}

.chat__body > :last-child {
  margin-bottom: 0;
}

.chat__body ul,
.chat__body ol {
  padding-left: var(--space-5);
}

/* A code block or a table in a reply scrolls inside itself, so the page never scrolls sideways on a phone. */
.chat__body pre,
.chat__body table {
  display: block;
  max-width: 100%;
  overflow-x: auto;
}

.chat__body pre {
  padding: var(--space-2) var(--space-3);
  border-radius: var(--radius-sm);
  background: var(--surface-sunken);
}

/* Code in a sentence and a quotation in a reply, as the old app's messages showed them. */
.chat__body :not(pre) > code {
  padding: 0 var(--space-1);
  border-radius: var(--radius-sm);
  background: var(--surface-sunken);
}

.chat__body blockquote {
  margin: 0 0 var(--space-4);
  padding-left: var(--space-3);
  border-left: var(--rule-width) solid var(--border);
  color: var(--text-muted);
  font-style: italic;
}

.chat__mark,
.chat__waiting {
  color: var(--danger);
  font-size: var(--text-sm);
}

.chat__mark {
  margin: var(--space-2) 0 0;
}

.chat__form {
  margin-bottom: var(--space-4);
}

.chat__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
  margin-top: var(--space-3);
}

.chat__copied {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.coach__list {
  max-width: var(--measure);
  margin: 0 0 var(--space-4);
  padding: 0;
  list-style: none;
}

.coach__item {
  margin-bottom: var(--space-2);
}

.coach__link {
  overflow-wrap: anywhere;
}

.practice {
  margin-top: var(--space-7);
  padding-top: var(--space-5);
  border-top: var(--border-width) solid var(--border);
}

.practice__sessions {
  margin: 0 0 var(--space-4);
  padding: 0;
  list-style: none;
}

.practice__session {
  margin-bottom: var(--space-2);
}

/* @fileoverview The review of a practice session (P3-04), on the session's page and on the Interview tab: the form that
   asks for one, and the review itself, laid out compactly toward the plan's one-screen review: each point names an
   answer with the person's own words quoted on the same line, its parts and points sit close together, and a tighter
   version is a short block set off by a rule at its side, kept in its line breaks. The answer and story links keep
   their full-size targets (agent/ui-standards.md), so each of those lines is at least 44px tall, however short. Under
   "How you spoke" (P3-06), each spoken answer's figures sit beside its link as a quote does. */
.review-start {
  max-width: var(--measure);
  margin-top: var(--space-6);
  padding-top: var(--space-5);
  border-top: var(--border-width) solid var(--border);
}

.review {
  max-width: var(--measure);
  margin: var(--space-5) 0 var(--space-6);
  overflow-wrap: anywhere;
}

/* The line under the heading saying which review it is, and "None" under a part, sit close to what follows. */
.review > .muted {
  margin-bottom: var(--space-2);
}

.review__overall {
  margin-bottom: var(--space-3);
  font-size: var(--text-lg);
}

/* The headings of the review's parts sit close to what they head. */
.review__part {
  margin: var(--space-3) 0 var(--space-1);
  font-size: var(--text-lg);
  line-height: var(--leading-tight);
}

.review__list {
  margin: 0;
  padding-left: var(--space-5);
}

/* Points sit close together: a linked answer's or story's line already has room above its words, from the link's
   full-size target. */
.review__item {
  margin-bottom: var(--space-1);
}

.review__answer {
  margin: 0;
  font-weight: var(--weight-bold);
}

.review__quote,
.review__figures {
  color: var(--text-muted);
  font-weight: var(--weight-normal);
}

.review__detail {
  margin: 0;
}

.review__label {
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.review__tighter {
  margin: var(--space-1) 0 0;
  padding-left: var(--space-2);
  border-left: var(--rule-width) solid var(--border);
  white-space: pre-wrap;
}

/* @fileoverview The story bank (P3-02): the list of stories on the Stories page, each a card with its state, the entry
   it is about, its themes as a wrapped row of tags and its four parts; the themes' boxes in a story's form; and each
   draft on the review page. */
.story-list {
  max-width: var(--measure);
  margin: 0;
  padding: 0;
  list-style: none;
}

.story {
  margin-bottom: var(--space-5);
  padding: var(--space-4);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-md);
  overflow-wrap: anywhere;
}

.story__title {
  margin: 0 0 var(--space-2);
  font-size: var(--text-lg);
}

.story__meta {
  margin: 0 0 var(--space-2);
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.story__state {
  font-weight: var(--weight-bold);
}

.story__state--ready {
  color: var(--success);
}

.tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
  margin: 0 0 var(--space-3);
  padding: 0;
  list-style: none;
}

.tags li {
  padding: var(--space-1) var(--space-2);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface-raised);
  font-size: var(--text-sm);
}

.story__parts {
  margin: 0 0 var(--space-3);
}

.story__parts dt {
  font-weight: var(--weight-bold);
}

.story__parts dd {
  margin: 0 0 var(--space-3);
}

/* The themes' boxes, two or more to a row where there is room. */
.choice__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(var(--field-narrow), 1fr));
  gap: 0 var(--space-4);
}

.story-draft {
  max-width: var(--measure);
  margin-bottom: var(--space-6);
  padding-bottom: var(--space-4);
  border-bottom: var(--border-width) solid var(--border);
}

/* @fileoverview The profile interview (P4-02): each proposal's card under the reply that made it, on the page's own
   surface so it stands out from the reply's raised bubble: what it proposes (the bullet it would replace muted), a
   warning in the danger colour when it cannot be accepted as it stands, Accept and Decline in a wrapping row, and Edit, a
   disclosure drawn as a button (the summary is the control, so it gets the button's 44px) that opens the box and Save;
   once decided, the line that says how, in the success colour when the profile changed. A proposal the app could not use
   is a muted line. On the Profile page, "Interview me about this job" sits under the job's bullets. */
.proposal {
  margin-top: var(--space-4);
  padding: var(--space-3) var(--space-4);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface);
  overflow-wrap: anywhere;
  /* A decision opens the page at its card (an address ending in #proposal- and its id), clear of the top edge. */
  scroll-margin-top: var(--space-4);
}

.proposal__heading {
  margin: 0 0 var(--space-2);
  font-weight: var(--weight-bold);
}

.proposal__label {
  margin: 0;
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.proposal__text {
  margin: 0 0 var(--space-3);
}

.proposal__text--old {
  color: var(--text-muted);
}

.proposal__warning {
  margin: 0 0 var(--space-3);
  color: var(--danger);
  font-size: var(--text-sm);
}

.proposal__decision {
  margin: 0 0 var(--space-2);
  font-weight: var(--weight-bold);
}

.proposal--accepted .proposal__decision,
.proposal--edited .proposal__decision {
  color: var(--success);
}

.proposal__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.proposal__actions form {
  margin: 0;
}

.proposal .form__note:empty {
  margin: 0;
}

/* A refused Accept, Decline or Save opens the page at the card's note (an address ending in #proposal- and its id,
   then -note), under the buttons and right above the Edit box, clear of the top edge, so the refusal is in view on a
   phone however long the card's bullets are. */
.proposal > .form__note {
  scroll-margin-top: var(--space-4);
}

.proposal__edit {
  margin-top: var(--space-2);
}

.proposal__edit > summary {
  list-style: none;
}

.proposal__edit > summary::-webkit-details-marker {
  display: none;
}

.proposal__edit > .form {
  margin-top: var(--space-3);
}

.proposal-unusable {
  margin: var(--space-3) 0 0;
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.entry__interview {
  margin: 0 0 var(--space-4);
}

/* @fileoverview The practice session's voice level (P3-06): the sentence saying where his voice goes and the
   microphone's row under the answer box ("Speak your answer", "I'm done", the Listening mark, the offer to download the speech pack), the words still being recognized,
   greyed, the status line that says why speaking is off, the line under a spoken answer with its time and pace, and the
   reading-aloud controls below the conversation. Colours only through the tokens; the Listening mark is a still dot
   in the danger colour beside its word, so nothing moves and the word carries the meaning. */
.voice-mic {
  max-width: var(--measure);
  margin-bottom: var(--space-3);
}

.voice-mic__buttons {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-2) var(--space-3);
}

.voice-mic__listening {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--touch-min);
  color: var(--danger);
  font-weight: var(--weight-bold);
}

.voice-mic__listening::before {
  content: "";
  width: var(--space-3);
  height: var(--space-3);
  border-radius: 50%;
  background: var(--danger);
}

/* Where his voice goes, above the microphone's buttons. */
.voice-mic__hint {
  margin: 0 0 var(--space-2);
  font-size: var(--text-sm);
}

/* Words still being recognized: greyed, and not a live region, so a screen reader is not read every guess. */
.voice-mic__interim {
  min-height: calc(var(--text-base) * var(--leading));
  margin: var(--space-2) 0 0;
  color: var(--text-muted);
  font-style: italic;
  overflow-wrap: anywhere;
}

.voice-note {
  max-width: var(--measure);
  margin: 0 0 var(--space-3);
  font-size: var(--text-sm);
}

.voice-note:empty {
  margin: 0;
}

.chat__spoken {
  margin: var(--space-2) 0 0;
  color: var(--text-muted);
  font-size: var(--text-sm);
}

.voice {
  max-width: var(--measure);
  margin: var(--space-5) 0;
  padding: var(--space-4);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
}

.voice h2 {
  margin-top: 0;
}

.voice .select {
  max-width: var(--field-min);
}

/* @fileoverview The practice session's video level (P3-07): the camera and recording panel below the conversation, a
   card like the reading-aloud one; the small picture of himself, in the camera's own shape, cropped rather than
   stretched, and turned as a mirror unless he turns that off; and the Recording mark, a still dot in the danger colour
   beside its word, so nothing moves and the word carries the meaning. Colours only through the tokens. */
.video {
  max-width: var(--measure);
  margin: var(--space-5) 0;
  padding: var(--space-4);
  border: var(--border-width) solid var(--border);
  border-radius: var(--radius-md);
  background: var(--surface-raised);
}

.video h2 {
  margin-top: 0;
}

.video__frame {
  width: 100%;
  max-width: var(--self-view-width);
  aspect-ratio: var(--self-view-ratio);
  margin-bottom: var(--space-3);
  overflow: hidden;
  border-radius: var(--radius-md);
  background: var(--surface-sunken);
}

.video__self {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.video__self--mirror {
  transform: scaleX(-1);
}

.video__recording {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  min-height: var(--touch-min);
  color: var(--danger);
  font-weight: var(--weight-bold);
}

.video__recording::before {
  content: "";
  width: var(--space-3);
  height: var(--space-3);
  border-radius: 50%;
  background: var(--danger);
}
