/* ===== Vinsights.Club dashboard =====
   Shares the marketing site's palette and damask backdrop so the app reads as
   the same brand. Unlike the marketing page this one scrolls — it holds long
   generated output. */

:root {
  --gold: #c9a54a;
  --gold-dark: #a9822f;
  --dark: #141414;
  --ink: #3a3226;
  --muted: #6b6354;
  --line: #e4ded0;
  --white: #ffffff;
  --font: "Helvetica Neue", Arial, sans-serif;
}

* { box-sizing: border-box; margin: 0; padding: 0; }

body {
  font-family: var(--font);
  color: var(--ink);
  line-height: 1.55;
  /* Tint only — the damask itself lives on the drifting layers below, matching
     the marketing site. */
  background-image:
    linear-gradient(180deg, rgba(255,255,255,0.45) 0%, rgba(255,255,255,0.15) 30%, rgba(255,255,255,0.15) 70%, rgba(20,16,8,0.12) 100%);
  background-repeat: no-repeat;
  background-size: cover;
  background-attachment: fixed;
  min-height: 100vh;
}

/* ===== Living backdrop: two-layer parallax drift =====
 *
 * Same effect as the marketing site, same numbers, so the two feel like one
 * product. Two copies of the damask at different scales drifting on different
 * axes; the mismatch between them is what reads as depth.
 *
 * FIXED AND BEHIND EVERYTHING (z-index -2): unlike the marketing page this one
 * scrolls, so the backdrop stays put while content moves over it — which is
 * what makes it read as a surface the interface sits on rather than wallpaper
 * glued to the page.
 *
 * TRANSFORMS, NOT background-position: transforms are composited, so the
 * pattern is never repainted. Animating background-position would repaint a
 * full-viewport image every frame — the usual reason effects like this drain
 * batteries.
 *
 * SEAMLESS LOOP: each layer travels exactly one tile — 380px across, or one
 * tile height (560 x 1228/756 = 909.6px) down. Change a background-size and
 * its keyframe must be recomputed or the reset will visibly jump.
 */
body::before,
body::after {
  content: "";
  position: fixed;
  top: -50%;
  left: -50%;
  width: 200%;
  height: 200%;
  z-index: -2;
  background-image: url("assets/backdrop.jpg");
  background-repeat: repeat;
  will-change: transform;
  pointer-events: none;
}

body::before {
  background-size: 380px auto;
  animation: drift-near 55s linear infinite;
}

body::after {
  background-size: 560px auto;
  opacity: 0.38;
  animation: drift-far 95s linear infinite;
}

@keyframes drift-near {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(-380px, 0, 0); }
}

@keyframes drift-far {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(0, -909.6px, 0); }
}

/* Decoration, so it stops for anyone whose system asks for less motion. This
   matters more here than on the marketing page: this is a tool people read and
   type in, not a page they glance at. */
@media (prefers-reduced-motion: reduce) {
  body::before,
  body::after {
    animation: none;
  }
}

/* ===== Header ===== */
.app-header {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 18px 5% 10px;
}

/* ===== The AI disclosure =====
 *
 * Sits between the header and the tabs, so it is above the work rather than
 * beside it. Full width, because a notice indented into a column reads as a
 * tip; this one is a statement about the whole product.
 *
 * QUIET, NOT HIDDEN. It is not a warning and should not look like one — red
 * would tell somebody something is wrong, when nothing is. The gold left edge
 * matches the rest of the product's furniture and marks it as ours.
 */
.ai-notice {
  /*
   * ===== SUBTLE, BECAUSE IT IS A STANDING FACT AND NOT AN ALERT =====
   *
   * It was a filled gold band the width of the window with a bordered button.
   * That reads as a system message - something has happened, deal with it -
   * when nothing has happened. Loud disclosure also trains the reflex it is
   * trying to prevent: a banner that shouts gets dismissed unread by week two,
   * which satisfies the rule in form and not in substance.
   *
   * So: no fill, a hairline of the product's own gold, muted text at the size
   * of a caption. It is still first on the page and still stops the reader for
   * a beat. It no longer looks like something broke.
   *
   * THE BUTTON USED TO SIT A THOUSAND PIXELS AWAY, and the cause was a cap on
   * the wrong element - the paragraph was capped at 78ch while its container
   * was flex: 1 1 auto and kept growing to fill the band. The measure now
   * lives on the paragraph and the row shrink-wraps it.
   */
  display: flex;
  align-items: center;
  gap: 14px;
  /*
   * ===== width: max-content CENTRED BADLY, AND HERE IS WHY =====
   *
   * max-content sizes to the widest the content wants to be and IGNORES the
   * space available. Text at 860px plus the button is ~940px, so in an 800px
   * window the band was 940px wide and margin: auto centred it by hanging 94px
   * off the left edge and 80px off the right. It was centred; it was also
   * wider than the screen.
   *
   * A plain max-width with width: 100% lets the row shrink when the window is
   * small and stops at the measure when it is not. The paragraph is
   * flex: 0 1 auto, so it gives way first and the button never gets squeezed.
   */
  width: 100%;
  max-width: 940px;
  margin: 0 auto 2px;
  padding: 7px 0 7px 12px;
  background: transparent;
  border-left: 2px solid var(--gold);
  border-radius: 0;
}

.ai-notice[hidden] { display: none; }

.ai-notice-text {
  flex: 0 1 auto;
  margin: 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--muted);
  /*
   * ===== 860px, MEASURED, NOT 78ch =====
   *
   * 78ch is the right measure for body copy at 15px. At 11.5px it is only
   * ~430px, which wrapped this sentence to FOUR lines with a single orphaned
   * word on the last - the opposite of compact.
   *
   * A caption read once tolerates a wider measure than a paragraph read for
   * minutes. 860px puts it on two lines on a normal window and it reflows down
   * gracefully on a narrow one.
   */
  max-width: 860px;
  /*
   * Justified, with hyphenation. Justification forces flush edges by
   * stretching word spacing, which at this measure opens visible rivers unless
   * long words can break. Without hyphens, "generated" and "confidently" are
   * exactly the words that blow a line apart.
   */
  text-align: justify;
  hyphens: auto;
}

/* Inline, not a heading. Carries the weight so the sentence still lands, but
   in the body colour rather than near-black. */
.ai-notice-lead { font-weight: 700; color: var(--ink); }

/* Reads as dismissal, not as agreement. There is nothing here to consent to -
   it is a statement of fact, and a button labelled "I agree" would imply the
   disclosure was a condition rather than an obligation on us.
   Quiet to match: a text button, not a bordered control. */
.ai-notice-close {
  flex: 0 0 auto;
  align-self: center;
  padding: 3px 8px;
  font-family: inherit;
  font-size: 11.5px;
  color: var(--muted);
  background: transparent;
  border: 0;
  border-radius: 3px;
  cursor: pointer;
  white-space: nowrap;
  text-decoration: underline;
  text-underline-offset: 2px;
  opacity: 0.85;
}

.ai-notice-close:hover { color: var(--ink); opacity: 1; }

/* Narrow: stack, and STOP JUSTIFYING. A short measure plus forced flush edges
   is where justified text falls apart — three words on a line become three
   words spread across it. */
@media (max-width: 700px) {
  .ai-notice {
    flex-direction: column;
    align-items: flex-start;
    gap: 10px;
    width: auto;
    max-width: none;
    margin: 0 4% 6px;
  }
  .ai-notice-text { text-align: left; hyphens: manual; }
  .ai-notice-close { align-self: flex-start; }
}

.brand { display: flex; align-items: center; gap: 12px; }
.brand-logo { width: 46px; height: auto; display: block; }

.brand-name {
  font-size: 22px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--dark);
}

.brand-dot { color: var(--gold-dark); }

/* ===== Main tabs ===== */
.tabs {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 8px;
  padding: 0 5% 18px;
}

.tab {
  font-family: var(--font);
  font-size: 12.5px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: var(--muted);
  background: rgba(255, 255, 255, 0.72);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 10px 16px;
  cursor: pointer;
  display: flex;
  align-items: center;
  gap: 8px;
  transition: color 0.15s ease, border-color 0.15s ease, background 0.15s ease;
}

.tab:hover { color: var(--gold-dark); border-color: var(--gold); }

.tab.is-active {
  color: var(--white);
  background: linear-gradient(135deg, var(--gold), var(--gold-dark));
  border-color: var(--gold-dark);
}

.tab-index {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px;
  height: 18px;
  border-radius: 50%;
  font-size: 10px;
  background: rgba(0, 0, 0, 0.09);
}

.tab.is-active .tab-index { background: rgba(255, 255, 255, 0.28); }

/* ===== Panels ===== */
.panels {
  max-width: 900px;
  margin: 0 auto;
  padding: 0 24px 80px;
}

.panel { display: none; }
.panel.is-active { display: block; }

.subtabs {
  display: flex;
  gap: 24px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 22px;
}

.subtab {
  font-family: var(--font);
  font-size: 13px;
  font-weight: 700;
  color: var(--muted);
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 10px 2px;
  margin-bottom: -1px;
  cursor: pointer;
}

.subtab:hover { color: var(--gold-dark); }

.subtab.is-active {
  color: var(--gold-dark);
  border-bottom-color: var(--gold);
}

.subpanel { display: none; }
.subpanel.is-active { display: block; }

.lede {
  font-size: 14px;
  color: var(--muted);
  margin-bottom: 24px;
  /*
   * FULL WIDTH, NOT A NARROWER MEASURE.
   *
   * This had max-width: 620px while the fields below run the full panel, so
   * the newly justified right edge formed a crisp line that aligned with
   * nothing beneath it. Justification draws the eye to that edge, which made
   * the mismatch more conspicuous than the ragged edge had been. Left and
   * right now line up down the whole page.
   *
   * Justified, with hyphenation.
   *
   * The two belong together: justifying a 620px column without hyphens forces
   * the browser to stretch word spacing on lines that end in a long word,
   * which opens "rivers" of white space down the paragraph — usually worse
   * than the ragged edge it replaced. Hyphens give it somewhere to break.
   */
  text-align: justify;
  hyphens: auto;
  -webkit-hyphens: auto;
}

/* ===== Form fields ===== */
.field { display: block; margin-bottom: 22px; }

.field-label {
  display: block;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--dark);
  margin-bottom: 8px;
}

.field-label em {
  font-style: normal;
  font-weight: 400;
  text-transform: none;
  letter-spacing: 0;
  color: var(--gold-dark);
  margin-left: 6px;
}

textarea {
  width: 100%;
  font-family: var(--font);
  font-size: 13.5px;
  line-height: 1.6;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.92);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 12px 14px;
  resize: vertical;
  outline: none;
}

textarea:focus { border-color: var(--gold); }

.upload-row {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-bottom: 10px;
}

.upload-input {
  position: absolute;
  width: 1px;
  height: 1px;
  opacity: 0;
  pointer-events: none;
}

.upload-btn {
  font-size: 12px;
  font-weight: 700;
  color: var(--gold-dark);
  background: rgba(255, 255, 255, 0.92);
  border: 1px dashed var(--gold);
  border-radius: 6px;
  padding: 7px 14px;
  cursor: pointer;
}

.upload-btn:hover { background: var(--white); }

.upload-status { font-size: 12px; color: var(--muted); }

/* Job-posting link field. Sized to actually show a long URL rather than
   scrolling it a few characters at a time. */
.url-input {
  flex: 1;
  min-width: 220px;
  font-family: var(--font);
  font-size: 12.5px;
  padding: 7px 10px;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.92);
  border: 1px solid var(--line);
  border-radius: 6px;
  outline: none;
}

.url-input:focus { border-color: var(--gold); }

button.upload-btn { flex: 0 0 auto; }
button.upload-btn:disabled { opacity: 0.55; cursor: default; }

/* ===== Buttons ===== */
.actions {
  display: flex;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
  margin-bottom: 8px;
}

.btn-primary {
  font-family: var(--font);
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--white);
  background: linear-gradient(135deg, var(--gold), var(--gold-dark));
  border: none;
  border-radius: 5px;
  padding: 11px 26px;
  cursor: pointer;
  transition: transform 0.15s ease, opacity 0.15s ease;
}

.btn-primary:hover:not(:disabled) { transform: translateY(-1px); }
.btn-primary:disabled { opacity: 0.55; cursor: default; }

.btn-lg { padding: 14px 34px; font-size: 13px; }

.btn-ghost {
  font-family: var(--font);
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--muted);
  background: none;
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 6px 12px;
  cursor: pointer;
}

.btn-ghost:hover { color: #a33; border-color: #d9b3b3; }

.hint { font-size: 12.5px; color: var(--muted); }

/* A REFUSAL MUST NOT LOOK LIKE A CAPTION.
   A hint beside a button is 12.5px muted grey — the same as every other note
   on the page. When the button stopped working and said so in that voice, the
   report was "nothing happens when I click Apply Stratagems", which is
   exactly right: a message nobody sees is a message nobody got. */
.hint.is-error {
  color: #8a2f2f;
  font-size: 13.5px;
  font-weight: 600;
}

/* And the box it is about says so too, so the eye lands on what to fix
   rather than on the sentence describing it. */
.field-invalid textarea,
textarea.field-invalid {
  border-color: #b34a4a;
  box-shadow: 0 0 0 3px rgba(179, 74, 74, 0.12);
}

/* ===== Status / errors ===== */
.status {
  font-size: 13px;
  color: var(--muted);
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid var(--line);
  border-left: 3px solid var(--gold);
  border-radius: 5px;
  padding: 12px 16px;
  margin: 18px 0;
}

.status.is-error {
  color: #8a2f2f;
  border-left-color: #b34a4a;
}

/* ===== Generated result ===== */
.result { margin-top: 26px; }

.result-card {
  background: rgba(255, 255, 255, 0.94);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 22px 24px;
  margin-bottom: 18px;
}

.result-card h3 {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--gold-dark);
  margin-bottom: 14px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--line);
}

.result-card p {
  font-size: 14px;
  margin-bottom: 10px;
  text-align: justify;
  hyphens: auto;
  -webkit-hyphens: auto;
}
.result-card p:last-child { margin-bottom: 0; }

.profile-badge {
  display: inline-block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--white);
  background: linear-gradient(135deg, var(--gold), var(--gold-dark));
  border-radius: 20px;
  padding: 5px 14px;
  margin-bottom: 12px;
}

/* Strategy Summary: scannable points rather than a wall of prose. */
.points {
  list-style: none;
  font-size: 14px;
}

.points > li {
  text-align: justify;
  hyphens: auto;
  -webkit-hyphens: auto;
  position: relative;
  padding-left: 18px;
  margin-bottom: 10px;
}

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

.points > li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 8px;
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--gold);
}

/* Single-item sections: no numbering, since there is nothing to enumerate.
   Kept as a list element so future multi-item sections still work. */
.single { list-style: none; }
.single > li { margin-bottom: 0; }

.numbered { list-style: none; counter-reset: item; }

.numbered > li {
  counter-increment: item;
  position: relative;
  padding-left: 34px;
  margin-bottom: 20px;
}

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

.numbered > li::before {
  content: counter(item);
  position: absolute;
  left: 0;
  top: 1px;
  width: 22px;
  height: 22px;
  border-radius: 50%;
  background: var(--gold);
  color: var(--white);
  font-size: 11px;
  font-weight: 700;
  display: flex;
  align-items: center;
  justify-content: center;
}

.item-title {
  display: block;
  font-size: 14px;
  font-weight: 700;
  color: var(--dark);
  margin-bottom: 5px;
}

.item-body { font-size: 13.5px; margin-bottom: 6px; }

.item-meta {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--gold-dark);
  margin-bottom: 5px;
}

.roles { display: flex; flex-wrap: wrap; gap: 6px; margin: 6px 0 8px; }

.role-chip {
  font-size: 11.5px;
  font-weight: 700;
  color: var(--gold-dark);
  background: rgba(201, 165, 74, 0.13);
  border-radius: 4px;
  padding: 3px 9px;
}

/* ===== Live job postings (real search results) ===== */
.jobs { list-style: none; margin-top: 4px; }

.jobs > li {
  border-left: 2px solid rgba(201, 165, 74, 0.45);
  padding: 2px 0 2px 12px;
  margin-bottom: 12px;
}

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

.job-title {
  display: block;
  font-size: 13.5px;
  font-weight: 700;
  color: var(--gold-dark);
}

a.job-title:hover { color: var(--gold); text-decoration: underline; }

/* Marks these as leaving the site — they are third-party postings. */
a.job-title::after {
  content: " ↗";
  font-size: 11px;
  opacity: 0.7;
}

.job-employer {
  display: block;
  font-size: 11.5px;
  color: var(--muted);
  margin-top: 1px;
}

.job-fit { font-size: 13px; margin-top: 4px; }

.job-none {
  font-size: 12.5px;
  color: var(--muted);
  font-style: italic;
  margin-top: 4px;
}







.usage-note {
  font-size: 11.5px;
  color: var(--muted);
  text-align: right;
  font-style: italic;
}

/* ===== Placeholders (tabs 2 and 3) ===== */
.placeholder-card {
  background: rgba(255, 255, 255, 0.8);
  border: 1px dashed #c9c2b0;
  border-radius: 8px;
  padding: 44px 24px;
  text-align: center;
}

.placeholder-card h3 {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--dark);
  margin-bottom: 8px;
}

.placeholder-card p { font-size: 13.5px; color: var(--muted); }

/* ===== Archive ===== */
.archive-list { display: flex; flex-direction: column; gap: 10px; }

.archive-item {
  display: flex;
  align-items: center;
  gap: 14px;
  background: rgba(255, 255, 255, 0.92);
  border: 1px solid var(--line);
  border-radius: 7px;
  padding: 14px 18px;
}

.archive-item-main { flex: 1; min-width: 0; cursor: pointer; }

/*
 * TWO LINES, THEN AN ELLIPSIS — not one line cut mid-word.
 *
 * Saved interview questions live in this list beside strategy titles, and a
 * question is a sentence: "How would you design a scorecard for an innovation
 * fund" on one line reads as damage rather than as a title. The full text is
 * stored and shown on hover; how much of it fits is a display decision and
 * belongs here rather than in the database.
 */
.archive-item-title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  font-size: 13.5px;
  font-weight: 700;
  line-height: 1.4;
  color: var(--dark);
}

.archive-item-meta { font-size: 11.5px; color: var(--muted); }



.archive-item-main:hover .archive-item-title { color: var(--gold-dark); }

/* ===== Mentors ===== */
.mentor-list { display: flex; flex-direction: column; gap: 12px; }

.mentor-card {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  background: rgba(255, 255, 255, 0.92);
  border: 1px solid var(--line);
  border-left: 3px solid var(--gold);
  border-radius: 7px;
  padding: 16px 18px;
}

.mentor-main { flex: 1; min-width: 0; }

.mentor-name {
  font-size: 14.5px;
  font-weight: 700;
  color: var(--dark);
  text-decoration: none;
}

a.mentor-name:hover { color: var(--gold-dark); text-decoration: underline; }

a.mentor-name::after {
  content: " ↗";
  font-size: 11px;
  opacity: 0.7;
}

.mentor-headline {
  font-size: 13px;
  color: var(--ink);
  margin-top: 2px;
}

.mentor-meta {
  font-size: 11.5px;
  color: var(--muted);
  margin-top: 3px;
}

.mentor-email {
  font-size: 12.5px;
  margin-top: 4px;
}

.mentor-email a { color: var(--gold-dark); }

.mentor-email-caveat {
  font-size: 10.5px;
  color: var(--muted);
  font-style: italic;
  margin-left: 6px;
}

/* ===== A GUESSED ADDRESS MUST NOT LOOK LIKE A FOUND ONE =====
 * Amber and solid, not italic grey. The grey caveat above is the "unknown"
 * case and is meant to be quiet; this one is a warning that the address was
 * assembled by a model and confirmed by nobody, and sending to it means cold-
 * emailing a stranger at an address that may belong to someone else. */
.mentor-email-caveat.is-guess {
  font-style: normal;
  font-weight: 600;
  color: #a06a12;
  background: rgba(214, 138, 20, 0.12);
  border: 1px solid rgba(214, 138, 20, 0.35);
  border-radius: 999px;
  padding: 1px 7px;
  cursor: help;
}

/* Quiet on purpose. "Found" is the expected case, and shouting it would make
   the guessed badge blend in as just another label. */
.mentor-email-caveat.is-found {
  font-style: normal;
  color: #5c7a4a;
  cursor: help;
}

/* ===== Outreach status ===== */
.status-filter {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 14px;
}

.status-chip {
  font-family: var(--font);
  font-size: 11.5px;
  font-weight: 700;
  color: var(--muted);
  background: rgba(255, 255, 255, 0.8);
  border: 1px solid var(--line);
  border-radius: 20px;
  padding: 5px 12px;
  cursor: pointer;
}

.status-chip:hover:not(:disabled) { color: var(--gold-dark); border-color: var(--gold); }

.status-chip.is-active {
  color: var(--white);
  background: linear-gradient(135deg, var(--gold), var(--gold-dark));
  border-color: var(--gold-dark);
}

.status-chip:disabled { opacity: 0.45; cursor: default; }

.chip-count {
  display: inline-block;
  margin-left: 4px;
  padding: 0 5px;
  border-radius: 10px;
  background: rgba(0, 0, 0, 0.08);
  font-size: 10.5px;
}

.status-chip.is-active .chip-count { background: rgba(255, 255, 255, 0.3); }

.mentor-side {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 8px;
  flex-shrink: 0;
}

.status-select {
  font-family: var(--font);
  font-size: 11.5px;
  font-weight: 700;
  color: var(--gold-dark);
  background: rgba(255, 255, 255, 0.95);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 6px 8px;
  cursor: pointer;
}

.status-select:disabled { opacity: 0.6; }

/* Autosave feedback. An invisible autosave reads as a broken one. */
.notes-state {
  display: block;
  font-size: 10.5px;
  color: var(--muted);
  min-height: 13px;
  margin-top: 2px;
}

.mentor-notes {
  width: 100%;
  margin-top: 10px;
  font-size: 12.5px;
  padding: 8px 10px;
  background: rgba(255, 255, 255, 0.7);
}

.empty {
  font-size: 13.5px;
  color: var(--muted);
  background: rgba(255, 255, 255, 0.8);
  border: 1px dashed #c9c2b0;
  border-radius: 8px;
  padding: 34px 24px;
  text-align: center;
}

/* ===== Mobile ===== */
@media (max-width: 700px) {
  .tab { font-size: 11px; padding: 9px 12px; }
  .panels { padding: 0 16px 60px; }
}

/* ===== Tab 2 — Stratagems for Work Excellence =====
 *
 * Reuses tab 1's .result-card and .points so the two tabs read as one
 * product. What is new here is the citation card: every stratagem and bias
 * carries a number, and the number needs to be visible enough that the reader
 * can go and check it against the library. Counsel that cites its sources but
 * hides the citation is just assertion with extra steps.
 */

.counsel-phase {
  display: flex;
  align-items: flex-start;
  gap: 14px;
  background: rgba(255, 255, 255, 0.88);
  border: 1px solid var(--line);
  border-left: 3px solid var(--gold);
  border-radius: 8px;
  padding: 14px 16px;
  margin-bottom: 22px;
}

.counsel-phase h3 {
  font-size: 14px;
  color: var(--dark);
  margin-bottom: 4px;
}

.counsel-phase p {
  font-size: 13px;
  color: var(--muted);
  line-height: 1.55;
}

.counsel-phase-badge {
  flex: 0 0 auto;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--white);
  background: linear-gradient(135deg, var(--gold), var(--gold-dark));
  border-radius: 4px;
  padding: 4px 8px;
  margin-top: 2px;
}

/* Citation card — one per stratagem or bias. */
.cite {
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 14px 16px;
  margin-bottom: 12px;
}

.cite-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 8px;
}

/* The number is the verifiable part, so it is styled to be found, not hidden. */
.cite-num {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--gold-dark);
  background: rgba(201, 165, 74, 0.14);
  border-radius: 10px;
  padding: 2px 9px;
  white-space: nowrap;
}

.cite-title {
  font-size: 14px;
  font-weight: 700;
  color: var(--dark);
}

.cite p {
  font-size: 13px;
  line-height: 1.6;
  margin-bottom: 8px;
}

.cite p:last-child { margin-bottom: 0; }

/* The library's own words, set apart from the tailored application so the
   reader can tell what is canon from what is advice about their situation. */
.cite-library {
  font-style: italic;
  color: var(--muted);
  border-left: 2px solid var(--line);
  padding-left: 10px;
}

.cite-risk,
.cite-counter {
  font-size: 12.5px;
  color: var(--muted);
}

.cite-risk strong { color: #a5502f; }
.cite-counter strong { color: #2e7d4f; }

.cite-bias { border-left: 3px solid #a5502f; }
.cite-stratagem { border-left: 3px solid var(--gold); }

/* The single move. Visually terminal — it is the thing to act on. */
.move-card {
  background: rgba(255, 255, 255, 0.94);
  border: 1px solid var(--gold);
  border-radius: 8px;
  padding: 16px 18px;
}

.move-action {
  font-size: 15px;
  line-height: 1.55;
  color: var(--dark);
  margin-bottom: 10px;
}

.move-when {
  display: inline-block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--white);
  background: linear-gradient(135deg, var(--gold), var(--gold-dark));
  border-radius: 4px;
  padding: 3px 9px;
  margin-bottom: 10px;
}

.move-cost {
  font-size: 12.5px;
  color: var(--muted);
  line-height: 1.55;
}

.move-cost strong { color: var(--dark); }







/* ===== The strategist's working file =====
 *
 * Collapsed by default. It is a control, not a headline — most sessions the
 * user should not need to think about it, but it must be one click away the
 * moment the counsel says something that misreads their position.
 */
.dossier {
  /* ===== SPACED ON BOTH SIDES, NOT JUST ABOVE =====
     This had margin-top only, so the field underneath started at a gap of
     ZERO — the "Your CV" label sat flat against the bottom border of the box
     and read as part of it. Every other element in the column is separated by
     22px, which made the one collapsed join the thing your eye caught.

     28 rather than 22 because a dossier is not a form field: it is a
     collapsible aside sitting in the middle of one, and it needs to read as
     its own object. Adjacent margins collapse to the larger of the two, so
     28 above and 28 below is what actually renders on both sides. */
  margin: 28px 0;
  background: rgba(255, 255, 255, 0.72);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 12px 16px;
}

.dossier > summary {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--dark);
  cursor: pointer;
  list-style: none;
  display: flex;
  align-items: baseline;
  gap: 10px;
}

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

.dossier > summary::before {
  content: "▸";
  color: var(--gold-dark);
  font-size: 11px;
}

.dossier[open] > summary::before { content: "▾"; }

.dossier-meta {
  font-size: 10.5px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--muted);
}

.dossier-lede {
  font-size: 12.5px;
  color: var(--muted);
  line-height: 1.55;
  margin: 12px 0 10px;
  text-align: justify;
  hyphens: auto;
  -webkit-hyphens: auto;
}

.dossier textarea { font-size: 13px; }

.dossier .actions { margin-top: 10px; }

/* Save, not delete — so it does NOT reuse .btn-ghost, whose red hover marks
   the destructive controls. Two buttons that look alike and do opposite
   things is how people click the wrong one. */
.btn-quiet {
  font-family: var(--font);
  font-size: 12px;
  font-weight: 700;
  color: var(--gold-dark);
  background: rgba(255, 255, 255, 0.92);
  border: 1px solid var(--gold);
  border-radius: 6px;
  padding: 7px 14px;
  cursor: pointer;
}

.btn-quiet:hover { background: var(--white); }

/* Credits remaining. Deliberately quiet — it is a fact, not a warning, until
   it nearly runs out. */
/*
 * ===== THE RIGHT-HAND GROUP =====
 *
 * Holds the credits line and the account link together. Both used to place
 * themselves on the right edge independently — the credits absolutely at
 * right:5%, the link with margin-left:auto — so they sat on top of each other.
 * Two elements competing for one edge overlap by default; the fix is to give
 * them a shared parent that owns the edge and spaces them.
 *
 * Absolute, because .app-header centres the brand: in normal flow this group
 * would push the brand off-centre.
 */
.header-right {
  position: absolute;
  right: 5%;
  display: flex;
  align-items: center;
  gap: 18px;
}

/* ===== ABSOLUTE POSITIONING CANNOT BE PUSHED BACK =====
   Holding both halves of the header in one flex row fixed the collision
   between "7 credits" and "Your account" — the two things on the right — but
   the group as a whole is still taken out of the flow at right:5%, so it
   floats over the brand as soon as the window is narrow enough. At 582px the
   wordmark and the credits overlap by ten pixels and read as one word:
   "VINSIGHTS.CLUBcredits".

   Layout is the only real fix. Below the width where they would meet, the
   header becomes two stacked rows and the right-hand group returns to the
   flow, where it can no longer sit on top of anything. There were no media
   queries on the header at all before this. */
@media (max-width: 760px) {
  .app-header {
    flex-direction: column;
    gap: 10px;
    padding-bottom: 14px;
  }

  .header-right {
    position: static;
    right: auto;
  }
}

.usage-line {
  font-size: 11px;
  color: var(--muted);
  cursor: help;
  white-space: nowrap;
}

.usage-line.is-low { color: #a5502f; font-weight: 700; }

.app-header { position: relative; }

/* ===== Constraints =====
 *
 * Set apart from the plain form fields above: these are not more input, they
 * are the boundary the advice has to live inside. Visually a brief, not a
 * questionnaire.
 */
.constraints {
  background: rgba(255, 255, 255, 0.72);
  border: 1px solid var(--line);
  border-left: 3px solid var(--gold);
  border-radius: 8px;
  padding: 18px 20px 6px;
  margin-bottom: 24px;
}

.constraints-head {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--dark);
  margin-bottom: 6px;
}

.constraints-lede {
  font-size: 12.5px;
  color: var(--muted);
  line-height: 1.55;
  margin-bottom: 16px;
}

/* The questions are the point, so they carry more weight than a field label
   and are not shouted in uppercase. */
.constraints .field-label {
  text-transform: none;
  letter-spacing: 0;
  font-size: 13px;
  font-weight: 500;
  color: var(--ink);
}

.constraints .url-input { width: 100%; }

.constraints-empty {
  font-size: 13px;
  color: var(--muted);
  background: rgba(20, 16, 8, 0.035);
  border: 1px dashed var(--line);
  border-radius: 8px;
  padding: 14px 16px;
  margin-bottom: 24px;
}

.constraints-empty a { color: var(--gold-dark); font-weight: 700; }

/* ===== The sequence =====
 *
 * A chain, not three cards. The claim of the section is that the order is
 * load-bearing, so the layout has to carry that — a connecting rule down the
 * left says "these follow each other" before a word is read.
 */
.steps {
  list-style: none;
  position: relative;
  padding-left: 4px;
}

.step {
  display: flex;
  gap: 16px;
  position: relative;
  padding-bottom: 22px;
}

.step:last-child { padding-bottom: 0; }

/* The line between the steps. Stops at the last one so it reads as an arrival
   rather than a continuation. */
.step:not(:last-child)::before {
  content: "";
  position: absolute;
  left: 25px;
  top: 44px;
  bottom: 4px;
  width: 2px;
  background: linear-gradient(180deg, var(--gold), rgba(201, 165, 74, 0.25));
}

.step-when {
  flex: 0 0 52px;
  height: 52px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  background: linear-gradient(135deg, var(--gold), var(--gold-dark));
  color: var(--white);
  line-height: 1;
}

.step-days { font-size: 21px; font-weight: 700; }

.step-body { flex: 1; padding-top: 2px; }

/* The job each move does, above the move. Bold and small-caps so it reads as
   a label rather than as the first line of the instruction — at body weight
   the eye runs the two together and "CV Update Rewrite your DSTA entries" is
   one bad sentence. */
/* ===== The requirements audit, in three groups =====
   Same pattern as the Competency sub-tabs, so the app has one way of doing
   this: a coloured bar that doubles as the legend, and the group beneath it.

   This replaced a strip of anonymous coloured blocks — thirteen unlabelled
   rectangles you had to click one by one to discover what they were. A
   summary you have to interrogate is not a summary. */
.req-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  border-bottom: 1px solid var(--line);
  margin-bottom: 14px;
}

.req-tab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: none;
  border: none;
  border-bottom: 2px solid transparent;
  padding: 8px 12px 9px;
  margin-bottom: -1px;
  font-size: 12.5px;
  font-family: inherit;
  color: var(--muted);
  cursor: pointer;
}

.req-tab:hover { color: var(--dark); }

.req-swatch {
  width: 9px;
  height: 9px;
  border-radius: 2px;
  background: currentColor;
  flex: none;
}

.req-count {
  font-size: 11px;
  font-weight: 700;
  background: rgba(0, 0, 0, 0.06);
  border-radius: 9px;
  padding: 1px 7px;
}

/* The colour lives on the tab so the swatch, the count and the underline all
   take it from one place. */
.req-tab.is-unwritten { color: var(--gold-dark); }
.req-tab.is-evidenced { color: #2e7d4f; }
.req-tab.is-absent { color: #9c9184; }

.req-tab.is-on {
  border-bottom-color: currentColor;
  font-weight: 700;
}

.req-tab.is-on .req-count { background: rgba(0, 0, 0, 0.09); }

.req-note { font-size: 12.5px; color: var(--muted); margin: 0 0 12px; }

.req-items { list-style: none; margin: 0; padding: 0; }

.req-items li {
  border-left: 3px solid currentColor;
  padding: 9px 0 9px 13px;
  margin-bottom: 10px;
}

.req-items li:last-child { margin-bottom: 0; }

.req-panel.is-unwritten .req-items li { color: var(--gold); }
.req-panel.is-evidenced .req-items li { color: #2e7d4f; }
.req-panel.is-absent .req-items li { color: #c9bfae; }

/* The requirement is what the posting asked; the line under it is why the
   audit said what it said — the part that makes the verdict checkable. */
.req-what {
  margin: 0;
  font-size: 13.5px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--dark);
}

.req-why {
  margin: 3px 0 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--muted);
}

/* The posting-fit verdict. A coloured left bar over a tint — the house
   pattern for "this is a judgement, here is its basis" — rather than a badge,
   which would read as a score. */
.fit {
  border-left: 3px solid var(--line);
  background: rgba(0, 0, 0, 0.02);
  border-radius: 0 6px 6px 0;
  padding: 11px 14px;
  margin: 0 0 18px;
  font-size: 13.5px;
  line-height: 1.55;
}

.fit-label {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.7px;
  text-transform: uppercase;
  margin-bottom: 3px;
}

.fit.is-fits { border-left-color: #2e7d4f; background: rgba(46, 125, 79, 0.06); }
.fit.is-fits .fit-label { color: #2e7d4f; }

.fit.is-partial { border-left-color: var(--gold); background: rgba(201, 165, 74, 0.08); }
.fit.is-partial .fit-label { color: var(--gold-dark); }

.fit.is-mismatch { border-left-color: #c9622f; background: rgba(201, 98, 47, 0.07); }
.fit.is-mismatch .fit-label { color: #a8511f; }

.fit.is-none .fit-label { color: var(--muted); }

.step-head {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.7px;
  text-transform: uppercase;
  color: var(--gold-dark);
  margin-bottom: 5px;
}

.step-move {
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--dark);
  margin-bottom: 8px;
  text-align: justify;
  hyphens: auto;
  -webkit-hyphens: auto;
}

/* ===== Who to ask =====
 *
 * Named people from the user's own Mentors tab. Styled as contacts rather
 * than advice, because that is what they are — the one section here that is
 * a fact about their world rather than a judgement about it.
 */



/* ===== Interview questions =====
 *
 * MATCHED TO .ask AND .step, NOT GIVEN ITS OWN LOOK.
 *
 * The first attempt gave each question a full 1px border and a white fill,
 * which put a box inside a box: five bordered cards sitting inside a bordered
 * result card, on a page where nothing else is boxed. The house pattern for a
 * repeated block is a coloured left bar over a tint — see .ask — so that is
 * what this uses. Gold rather than green, because green is reserved for
 * people you can contact.
 */
.qs { list-style: none; }

.q {
  border-left: 3px solid var(--gold);
  background: rgba(201, 165, 74, 0.07);
  border-radius: 0 6px 6px 0;
  padding: 11px 14px;
  margin-bottom: 10px;
}

.q:last-child { margin-bottom: 0; }

.q-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 6px;
}

/* A label, not a badge: small caps in gold, no fill. */
.q-type {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.7px;
  text-transform: uppercase;
  color: var(--gold-dark);
}

/*
 * Quiet by design. Five bordered buttons down the card competed with the
 * questions for attention, and the questions are the product.
 */
.q-save {
  font-family: var(--font);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.3px;
  color: var(--muted);
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  white-space: nowrap;
}

.q-save:hover:not(:disabled) { color: var(--gold-dark); text-decoration: underline; }

/* Stays visibly done rather than reverting — the entry is in the Archive and
   saving again would only duplicate it. */
.q-save.is-saved {
  color: #2e7d4f;
  cursor: default;
  text-decoration: none;
}

.q-save:disabled { opacity: 0.75; }

/* Justified like .step-move; the meta lines below stay ragged like
   the meta lines, because justifying two short lines opens gaps after the bold
   run-in label instead of closing them. */
.q-text {
  font-size: 14.5px;
  line-height: 1.55;
  color: var(--dark);
  margin-bottom: 8px;
  text-align: justify;
  hyphens: auto;
  -webkit-hyphens: auto;
}

.q-meta {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--muted);
  margin-bottom: 5px;
}

.q-meta:last-child { margin-bottom: 0; }
.q-meta strong { color: var(--dark); }

/* ===== Outcome logging =====
 *
 * Quiet until used. This sits under every move, three times per strategy, so
 * anything louder would compete with the advice it is attached to — and the
 * advice is what the reader came for. Once a status is set, that one chip
 * goes solid and the rest stay muted, so the state is readable at a glance
 * from across the page.
 */
.oc {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px dashed var(--line);
}

.oc-label {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 7px;
}

.oc-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.oc-chip {
  font-family: var(--font);
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  background: none;
  border: 1px solid var(--line);
  border-radius: 12px;
  padding: 4px 11px;
  cursor: pointer;
  transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}

.oc-chip:hover { border-color: var(--gold); color: var(--gold-dark); }

.oc-chip.is-on {
  color: var(--white);
  background: var(--gold-dark);
  border-color: var(--gold-dark);
}

.oc-note {
  display: block;
  width: 100%;
  margin-top: 9px;
  font-family: var(--font);
  font-size: 12.5px;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.7);
  border: 1px solid var(--line);
  border-radius: 5px;
  padding: 7px 10px;
}

.oc-note:focus { outline: none; border-color: var(--gold); }







.reqs { list-style: none; }
















/* ===== Draft the email ===== */

/*
 * Sits above the outcome chips, because it is the thing to do and they are
 * the thing to record afterwards. Solid rather than ghosted: this is the one
 * control on the page that produces something you can send.
 */
.draft-btn {
  font-family: var(--font);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.3px;
  color: var(--white);
  background: var(--gold-dark);
  border: none;
  border-radius: 5px;
  padding: 7px 14px;
  margin-top: 12px;
  cursor: pointer;
}

.draft-btn:hover:not(:disabled) { background: var(--gold); }
.draft-btn:disabled { opacity: 0.6; cursor: default; }

/*
 * A real <dialog>: focus trapping, Escape to close and the backdrop are the
 * browser's, and each is something a hand-rolled overlay gets subtly wrong.
 */
.draft-dialog {
  width: min(620px, 92vw);
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 22px 24px;
  background: var(--paper, #fdfcf9);
  color: var(--ink);
  box-shadow: 0 18px 50px rgba(0, 0, 0, 0.22);
}

.draft-dialog::backdrop { background: rgba(28, 24, 18, 0.45); }

.draft-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  padding-bottom: 12px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--line);
}

.draft-to {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 4px;
}

.draft-subject { display: block; font-size: 15px; font-weight: 700; color: var(--dark); }

.draft-close {
  font-size: 22px;
  line-height: 1;
  color: var(--muted);
  background: none;
  border: none;
  cursor: pointer;
  padding: 0 2px;
}

/*
 * Editable, not display-only. Nobody sends a first draft unchanged, and making
 * them paste it elsewhere to change a line would leave in place the very step
 * this feature exists to remove.
 */
.draft-body {
  width: 100%;
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.65;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.85);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 14px 16px;
  resize: vertical;
}

.draft-body:focus { outline: none; border-color: var(--gold); }

/*
 * The STAR answer is a rehearsal script, not a letter.
 *
 * WHY IT GETS ITS OWN SIZE. It carries five labelled sections against the
 * letter's one block of prose, so at the letter's line height the headings stop
 * reading as headings and the whole thing becomes a wall. The extra leading is
 * what keeps SITUATION / TASK / ACTION visible while scrolling.
 *
 * Not monospace, though the labels tempt it: this is meant to be spoken aloud,
 * and prose set in a code face invites reading it as a document to be checked
 * rather than a script to be practised.
 */
.answer-body {
  line-height: 1.8;
  min-height: 320px;
}

.draft-note {
  font-size: 12px;
  line-height: 1.55;
  color: var(--muted);
  margin-top: 10px;
}

.draft-actions {
  display: flex;
  gap: 10px;
  align-items: center;
  margin-top: 16px;
}

/*
 * Errors from the draft button, shown beside the button that caused them.
 * The previous version wrote them into the Generate panel's status line,
 * which is not on screen when the click came from the Archive — so a failure
 * was indistinguishable from a dead button.
 */
.draft-error {
  font-size: 12px;
  line-height: 1.5;
  color: #a8443c;
  margin-top: 7px;
}

/*
 * What a button will cost, before it is pressed.
 *
 * Filled from the server's price table on load. A user who cannot see the
 * price until after they have been charged audits everything else you tell
 * them, and they are right to.
 */
.credit-cost {
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.4px;
  text-transform: uppercase;
  color: var(--gold-dark);
  background: rgba(201, 165, 74, 0.14);
  border-radius: 10px;
  padding: 3px 10px;
  white-space: nowrap;
}

.credit-cost:empty { display: none; }

/*
 * Sits beside Draft the email rather than under it, so the price is read in
 * the same glance as the button. Nudged up because the button has more
 * vertical padding than the label does.
 */
.draft-cost {
  margin-left: 8px;
  vertical-align: 2px;
}

/* ===== Tab 3: Assets ===== */

/*
 * What the app already knows, shown before anything is generated.
 *
 * The tab it replaces opened on an empty seven-field form. Opening instead on
 * a count of your own material makes the point the whole tab rests on: you are
 * not short of things to say, you are short of them being visible.
 */
.material-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 12px;
  margin-bottom: 26px;
}

.material-card {
  border-left: 3px solid var(--gold);
  background: rgba(201, 165, 74, 0.07);
  border-radius: 0 6px 6px 0;
  padding: 12px 14px;
}

/* ===== The two cards that open =====
   A count the reader cannot inspect looks like information and carries none —
   which is what "11 things your CV never mentions" became once the audit it
   counts left Tab 1's page. These use <details>, so the triangle says they
   open before anyone clicks to find out. */
.material-card.is-open > summary {
  cursor: pointer;
  list-style: none;
  display: block;
}

.material-card.is-open > summary::-webkit-details-marker { display: none; }

.material-more {
  display: inline-block;
  margin-top: 8px;
  font-size: 12px;
  font-weight: 700;
  color: var(--gold-dark);
  border-bottom: 1px solid rgba(169, 130, 47, 0.45);
}

.material-card.is-open[open] .material-more { border-bottom-color: transparent; }
.material-card.is-open[open] .material-more::after { content: " — close"; }

.material-list {
  list-style: none;
  margin: 14px 0 0;
  padding: 14px 0 0;
  border-top: 1px solid rgba(169, 130, 47, 0.28);
}

.material-list li { margin-bottom: 12px; }
.material-list li:last-child { margin-bottom: 0; }

.material-req {
  margin: 0;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--dark);
  line-height: 1.45;
}

/* The evidence is why the item is on the list at all — the thing in their
   history that meets the requirement their CV does not mention. */
.material-ev {
  margin: 4px 0 0;
  font-size: 12.5px;
  color: var(--muted);
  line-height: 1.5;
}

.material-trunc { margin: 12px 0 0; font-size: 12px; color: var(--muted); }

/* The label on a ribbon point. Only Tab 3 uses these — its three points are
   different in KIND from each other (a hook is not a call to action), where
   Tabs 1 and 2 pass three readings of one situation and a label there would
   be a heading over a single line. */
.ribbon-point-label {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.7px;
  text-transform: uppercase;
  color: var(--gold-dark);
  margin-bottom: 2px;
}

/* Dimmed rather than hidden: an empty count tells you which tab to go and use. */
.material-card.is-empty {
  border-left-color: var(--line);
  background: rgba(0, 0, 0, 0.02);
  opacity: 0.65;
}

.material-n {
  display: block;
  font-size: 24px;
  font-weight: 700;
  line-height: 1;
  color: var(--gold-dark);
  margin-bottom: 6px;
}

.material-card.is-empty .material-n { color: var(--muted); }

.material-label {
  display: block;
  font-size: 12.5px;
  font-weight: 600;
  color: var(--dark);
  margin-bottom: 3px;
}

.material-note { display: block; font-size: 11.5px; color: var(--muted); }

.select {
  display: block;
  width: 100%;
  max-width: 340px;
  font-family: var(--font);
  font-size: 14px;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.85);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 9px 11px;
  margin-bottom: 7px;
}

.select:focus { outline: none; border-color: var(--gold); }

/* An angle, with the evidence it rests on. */
.angle {
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 16px 18px;
  margin-top: 14px;
  background: rgba(255, 255, 255, 0.9);
}

.angle-claim {
  font-size: 15px;
  font-weight: 600;
  line-height: 1.5;
  color: var(--dark);
  margin-bottom: 9px;
}

.angle-meta {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--muted);
  margin-bottom: 5px;
}

.angle-meta strong { color: var(--dark); }
.angle-write { margin-top: 11px; }

/* The finished posts, one platform at a time. */
.post-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 14px;
}

.post-tab {
  font-family: var(--font);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.3px;
  color: var(--muted);
  background: none;
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 5px 13px;
  cursor: pointer;
}

.post-tab:hover { border-color: var(--gold); color: var(--gold-dark); }

.post-tab.is-on {
  color: var(--white);
  background: var(--gold-dark);
  border-color: var(--gold-dark);
}

/* Marks a platform whose post has something worth looking at before posting. */
.post-warn {
  display: inline-block;
  margin-left: 6px;
  font-weight: 700;
  color: #c9622f;
}

.post-tab.is-on .post-warn { color: var(--white); }

.post-panel[hidden] { display: none; }

/*
 * Editable, and never silently corrected. These go out under the user's own
 * name; a product that quietly rewrites what you are about to publish has
 * taken a decision that was not its to take.
 */
.post-problem {
  font-size: 12px;
  line-height: 1.5;
  color: #c9622f;
  background: rgba(201, 98, 47, 0.08);
  border-radius: 5px;
  padding: 8px 11px;
  margin-bottom: 10px;
}

.post-body {
  width: 100%;
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.65;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.85);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 14px 16px;
  resize: vertical;
}

.post-body:focus { outline: none; border-color: var(--gold); }

.post-foot {
  display: flex;
  align-items: center;
  gap: 12px;
  margin-top: 12px;
}

.post-count { font-size: 11.5px; color: var(--muted); }
.post-count.is-over { color: #a8443c; font-weight: 700; }

/* ===== Qualifying questions ===== */

.qualifiers { margin: 22px 0; }

.qual {
  border: none;
  padding: 0;
  margin: 0 0 18px;
}

.qual-q {
  padding: 0;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
  margin-bottom: 4px;
}

.qual-hint {
  font-size: 12px;
  color: var(--muted);
  margin-bottom: 8px;
}

.qual-opts {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

/*
 * Nothing is preselected. A default here is a guess wearing the user's
 * authority, and an unanswered question is simply left out of the prompt
 * rather than filled in on their behalf.
 */
.qual-opt {
  font-family: var(--font);
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  background: none;
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 6px 13px;
  cursor: pointer;
  text-align: left;
}

.qual-opt:hover { border-color: var(--gold); color: var(--gold-dark); }

.qual-opt.is-on {
  color: var(--white);
  background: var(--gold-dark);
  border-color: var(--gold-dark);
}

/* ===== Angle bands and the risk advisory ===== */

.angle-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin-bottom: 10px;
}

/* Rank, not a score. The order is the judgement; a number out of 100 would be
   a claim about engagement that nothing here can support. */
.angle-rank {
  flex: 0 0 auto;
  width: 24px;
  height: 24px;
  display: flex;
  align-items: center;
  justify-content: center;
  border-radius: 50%;
  font-size: 12px;
  font-weight: 700;
  color: var(--white);
  background: var(--gold-dark);
}

.angle-bands { display: flex; flex-wrap: wrap; gap: 6px; }

.band {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  border-radius: 10px;
  padding: 3px 9px;
}

.band.is-reach-broad { color: #2e7d4f; background: rgba(46, 125, 79, 0.12); }
.band.is-reach-peer { color: var(--gold-dark); background: rgba(201, 165, 74, 0.16); }
.band.is-reach-narrow { color: var(--muted); background: rgba(0, 0, 0, 0.05); }

.band.is-risk-low { color: #2e7d4f; background: rgba(46, 125, 79, 0.12); }
.band.is-risk-considered { color: #c9622f; background: rgba(201, 98, 47, 0.12); }
.band.is-risk-high { color: #a8443c; background: rgba(168, 68, 60, 0.14); }

/*
 * Open, never folded. A risk warning behind a click is a risk warning nobody
 * reads, and the whole point is that it is seen before the decision.
 */
.risk {
  border-left: 3px solid #c9622f;
  background: rgba(201, 98, 47, 0.06);
  border-radius: 0 6px 6px 0;
  padding: 11px 14px;
  margin-top: 12px;
}

.risk.is-low { border-left-color: #2e7d4f; background: rgba(46, 125, 79, 0.05); }
.risk.is-high { border-left-color: #a8443c; background: rgba(168, 68, 60, 0.07); }

.risk-head {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: #c9622f;
  margin-bottom: 6px;
}

.risk.is-low .risk-head { color: #2e7d4f; }
.risk.is-high .risk-head { color: #a8443c; }

.risk-line {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--muted);
  margin-bottom: 4px;
}

.risk-line:last-child { margin-bottom: 0; }
.risk-line strong { color: var(--dark); }

.angle-foot {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 13px;
}

/* ===== Positioning ===== */

.positioning { margin: 4px 0 24px; }



.pos-label {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--gold-dark);
  margin-bottom: 5px;
}

.pos-empty { font-size: 13px; color: var(--muted); }

/* Shown as a gap rather than smoothed over: a statement that reads fine with
   a hole in it is one nobody notices is unfinished. */
.pos-gap {
  color: var(--muted);
  font-style: italic;
  border-bottom: 1px dashed var(--line);
}

/* ===== Tab 4: the character self-assessment ===== */

.ass-group {
  border: none;
  padding: 0;
  margin: 0 0 30px;
}

.ass-construct {
  padding: 0;
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--gold-dark);
}

.ass-meaning {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--muted);
  margin: 4px 0 14px;
}

.ass-item {
  padding: 12px 0;
  border-top: 1px solid rgba(0, 0, 0, 0.05);
}

.ass-text {
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--dark);
  margin-bottom: 9px;
}

.ass-scale {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

/*
 * The full label on every option, not a bare 1-5.
 *
 * A numbered scale makes people optimise the number; a worded one makes them
 * answer the question. It costs horizontal space and is worth it.
 */
.ass-opt {
  font-family: var(--font);
  font-size: 11.5px;
  font-weight: 600;
  color: var(--muted);
  background: none;
  border: 1px solid var(--line);
  border-radius: 14px;
  padding: 5px 12px;
  cursor: pointer;
}

.ass-opt:hover { border-color: var(--gold); color: var(--gold-dark); }

.ass-opt.is-on {
  color: var(--white);
  background: var(--gold-dark);
  border-color: var(--gold-dark);
}

.ass-progress {
  font-size: 12.5px;
  font-weight: 600;
  color: var(--muted);
  margin-top: 6px;
}

.ass-progress.is-done { color: #2e7d4f; }

/* ===== The result ===== */

.ass-results { list-style: none; }

.ass-result {
  display: grid;
  grid-template-columns: 1fr 56px 170px;
  align-items: baseline;
  gap: 10px;
  padding: 10px 0 10px 12px;
  border-left: 3px solid var(--line);
  border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}

.ass-result:last-child { border-bottom: none; }

.ass-result.is-consistent { border-left-color: #2e7d4f; }
.ass-result.is-present { border-left-color: var(--gold); }
.ass-result.is-situational { border-left-color: #c9622f; }
.ass-result.is-untested { border-left-color: #c9bfae; }

.ass-result-name { font-size: 14px; font-weight: 700; color: var(--dark); }

/* The band leads; the total is deliberately quieter. Three questions do not
   resolve to a point, and putting the number first invites comparing an 11
   with a 12 as though the difference meant something. */
.ass-result-pct { font-size: 19px; font-weight: 700; color: var(--gold-dark); text-align: right; }
.ass-result-band { font-size: 12.5px; color: var(--muted); }


.ass-caveat {
  font-size: 12px;
  line-height: 1.55;
  color: var(--muted);
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--line);
}

.lean-name {
  font-size: 12px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--gold-dark);
  margin-bottom: 8px;
}

.dev-line { font-size: 13px; line-height: 1.55; color: var(--muted); margin-bottom: 6px; }
.dev-line strong { color: var(--dark); }

/* The experiment is the point of the tab, so it is the loudest thing on the
   card. Character moves through experience, not through being told about it. */
.experiment {
  border-left: 3px solid var(--gold);
  background: rgba(201, 165, 74, 0.08);
  border-radius: 0 6px 6px 0;
  padding: 13px 15px;
  margin-top: 12px;
}

.experiment-head {
  display: block;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.6px;
  text-transform: uppercase;
  color: var(--gold-dark);
  margin-bottom: 6px;
}

.experiment p { font-size: 14px; line-height: 1.6; color: var(--dark); }

.markets { list-style: none; }

.market {
  padding: 12px 0;
  border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}

.market:last-child { border-bottom: none; }

.market-name {
  font-size: 14px;
  font-weight: 700;
  color: var(--dark);
  margin-bottom: 5px;
}

.market-line { font-size: 12.5px; line-height: 1.55; color: var(--muted); margin-bottom: 4px; }
.market-line strong { color: var(--dark); }

.market-note {
  font-size: 12px;
  line-height: 1.5;
  color: #c9622f;
  background: rgba(201, 98, 47, 0.08);
  border-radius: 5px;
  padding: 9px 12px;
  margin-top: 12px;
}


/* ===== The reading =====
 *
 * Prose, deliberately. An earlier version returned three bullets and two short
 * cards, which read as a summary of the numbers rather than an argument about
 * them — and put a disclaimer where the conclusion belonged.
 */
.prose {
  font-size: 14.5px;
  line-height: 1.68;
  color: var(--ink);
  margin-bottom: 10px;
  text-align: justify;
  hyphens: auto;
  -webkit-hyphens: auto;
}

.prose:last-child { margin-bottom: 0; }

/* The part the reader cannot see from the inside. */
.tension {
  border-left: 3px solid var(--gold-dark);
  background: rgba(201, 165, 74, 0.08);
  border-radius: 0 6px 6px 0;
  padding: 15px 18px;
}

.closing {
  font-size: 14px;
  line-height: 1.7;
  color: var(--muted);
  font-style: italic;
  padding: 4px 24px 8px;
  margin-bottom: 18px;
}

/*
 * What to verify before acting on a market claim.
 *
 * Deliberately not styled as a warning: sending the reader to the source is
 * not an admission of weakness, it is the difference between a claim and a
 * lead. Quiet, attached, and impossible to miss.
 */
.market-verify {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--muted);
  border-left: 2px solid var(--gold);
  padding-left: 11px;
  margin-top: 8px;
}

.market-verify strong { color: var(--gold-dark); }


/* Whether anyone is actually arguing about the claim, from a live search. */
.band.is-contested-actively { color: #2e7d4f; background: rgba(46, 125, 79, 0.12); }
.band.is-contested-quietly { color: var(--gold-dark); background: rgba(201, 165, 74, 0.16); }
.band.is-contested-none { color: var(--muted); background: rgba(0, 0, 0, 0.05); }

/* Why the angles are in this order. Printed rather than assumed: a ranking the
   reader cannot check is one they will eventually disagree with and have no
   way to resolve. */
.rank-rule {
  font-size: 12px;
  line-height: 1.55;
  color: var(--muted);
  margin-bottom: 4px;
}

/* ===== Milestones ribbon =====
 *
 * A banner between the main tabs and the sub-tabs, with equal air above and
 * below so it belongs to neither.
 *
 * FIVE PIECES, ONE SHAPE. A real banner is a strip of cloth folded twice: a
 * raised centre panel, two tails hanging lower, and — where the cloth turns —
 * a wedge of its own underside showing beneath each end of the panel. That
 * wedge is what makes it read as folded rather than as three rectangles that
 * happen to overlap, and it is the piece every earlier attempt was missing.
 *
 *      ┌──────────────────────────────┐      <- .ribbon-band (raised panel)
 *   ╱╲ │                              │ ╱╲
 *  ╱  ╲└──▉───────────────────────▉───┘╱  ╲   <- ::before/::after on the band
 *  ╲  ╱                                ╲  ╱      (the fold wedges)
 *   ╲╱                                  ╲╱    <- ::before/::after on .ribbon
 *                                                 (the tails, notched)
 *
 * WHAT WAS TRIED AND REJECTED, so it is not tried again: tails angled
 * downward, tails curved as SVG with modelled shading, and the whole band
 * narrowed to 680px. All three read as ornament competing with the content.
 * Flat geometry at full width is what holds up at this size.
 *
 * Drawn in CSS, not an image: nothing to attribute, it takes the app's own
 * gold rather than a stock red, and it stretches to any width without tiling.
 */
.ribbon {
  position: relative;
  /*
   * WIDTH IS SET TWICE ON PURPOSE. max-width alone let the tails run off both
   * edges of the window the moment the viewport was narrower than the ribbon —
   * the notches were sliced off and the banner became a bar again. The calc
   * keeps a clear margin at every width, so the shape is never cropped by the
   * one thing it cannot control.
   *
   * Wider than the 900px content column beneath it, deliberately: a banner
   * that stops short of the text it heads reads as a card.
   */
  max-width: 1000px;
  width: calc(100% - 64px);
  /* Extra bottom margin: the tails hang below the band and need the air. */
  margin: 30px auto 48px;
  /*
   * FLUID, NOT A BREAKPOINT. With a fixed tail width every size below the
   * breakpoint squeezed the panel instead of the tails, so the five figures
   * wrapped and the band grew a line — and a single breakpoint made it worse,
   * because the compact tails gave a WIDER panel than the size just above
   * them. Scaling the tails keeps the panel's share at every width, with no
   * step to fall off.
   *
   * vw, NOT %. Percentages here resolve against two different boxes: padding
   * against this element's containing block, but the tails' width against
   * this element's own padding box, because they are absolutely positioned.
   * Written as percentages the two drift apart as the window narrows and the
   * 5px gap silently becomes an overlap — which, with translucent gold, shows
   * up as a dark bar rather than as nothing. vw means one number everywhere.
   */
  padding: 0 calc(clamp(65px, 11vw, 112px) + 5px);
}

.ribbon[hidden] { display: none; }

/*
 * THE TAILS.
 *
 * NOTHING OVERLAPS ANYTHING. On a solid banner the tails simply pass behind
 * the panel and you cannot tell. These pieces are translucent, so an overlap
 * does not read as one shape in front of another — it reads as a darker
 * rectangle where two washes of gold stack up, which is exactly how the first
 * attempt failed. Every piece therefore sits in its own space with a hairline
 * of background between, which is what the gaps in a printed banner are doing
 * too.
 *
 * They sit LOWER than the panel, by a third of its height. That drop is the
 * whole silhouette: level with the panel they are one strip, and below it they
 * are a banner.
 */
.ribbon::before,
.ribbon::after {
  content: "";
  position: absolute;
  /* Roughly 40% of the panel's height down, and hanging as far again below
     it. Level with the panel they read as one strip; this drop is the whole
     silhouette. */
  top: 38px;
  bottom: -34px;
  /* Kept in step with the padding above — same expression, same unit. */
  width: clamp(65px, 11vw, 112px);
  z-index: 0;
}

.ribbon::before {
  left: 0;
  /* Darkening toward the fold is the shading a real fold has: the cloth turns
     away from the light as it approaches the panel. */
  background: linear-gradient(
    to right,
    rgba(169, 130, 47, 0.24) 0%,
    rgba(169, 130, 47, 0.28) 55%,
    rgba(146, 110, 36, 0.38) 100%
  );
  /* The swallowtail: a V cut into the outer end. */
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%, 32% 50%);
}

.ribbon::after {
  right: 0;
  background: linear-gradient(
    to left,
    rgba(169, 130, 47, 0.24) 0%,
    rgba(169, 130, 47, 0.28) 55%,
    rgba(146, 110, 36, 0.38) 100%
  );
  clip-path: polygon(0 0, 100% 0, 68% 50%, 100% 100%, 0 100%);
}

/*
 * THE FRONT PANEL. Flat ends on purpose: the notch belongs to the tails, and
 * putting one here too would give the shape four points and no logic.
 */
.ribbon-band {
  position: relative;
  z-index: 1;
  /* Tight side padding: the panel is narrower than it was once the tails
     stopped tucking under it, and the five figures have to stay on one line.
     Wrapped to two, the band grows by 26px and the ribbon starts to feel like
     a panel rather than a status line. */
  padding: 18px 24px;
  background: rgba(201, 165, 74, 0.24);
  /* A hairline at each fold, so the panel has an edge to sit against. */
  box-shadow:
    -1px 0 0 rgba(138, 103, 33, 0.3),
    1px 0 0 rgba(138, 103, 33, 0.3);
}

/*
 * THE FOLD WEDGES — the underside of the cloth, showing below each end of the
 * panel where it turns back to become a tail. This is the piece every earlier
 * attempt was missing, and the reason those read as three rectangles that
 * happened to be near each other.
 *
 * Thick against the fold and tapering inward, because that is the direction
 * the cloth is travelling. Darker than either neighbour: this face is turned
 * away from the light, and without the contrast it looks like a stray triangle
 * rather than the same ribbon seen from behind.
 */
.ribbon-band::before,
.ribbon-band::after {
  content: "";
  position: absolute;
  top: calc(100% + 3px);
  height: 31px;
  width: clamp(58px, 9.7vw, 98px);
  /* The darkest wash in the ribbon. At the alphas the rest of it uses, this
     triangle simply disappeared into the damask behind it — and with it the
     fold, which is the only thing making this a banner rather than a bar. */
  background: rgba(104, 76, 20, 0.5);
  z-index: 2;
}

.ribbon-band::before {
  left: 0;
  clip-path: polygon(0 0, 100% 0, 0 100%);
}

.ribbon-band::after {
  right: 0;
  clip-path: polygon(0 0, 100% 0, 100% 100%);
}

.ribbon-line {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 6px 12px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--muted);
  margin: 0;
}

.ribbon-head {
  font-size: 13.5px;
  letter-spacing: 0.2px;
  color: var(--dark);
  white-space: nowrap;
}

.ribbon-head strong {
  font-size: 16px;
  font-weight: 700;
  color: var(--gold-dark);
}

.ribbon-parts {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 4px 6px;
}

/* Each figure carries a tooltip listing what is outstanding, so the cursor
   change is a promise the hover keeps. */
.ribbon-part {
  font-size: 12.5px;
  white-space: nowrap;
  cursor: help;
  border-bottom: 1px dotted transparent;
}

.ribbon-part:hover { border-bottom-color: var(--gold); color: var(--dark); }

/* A tab with all five done stops being grey. The only reward on offer here,
   and it costs nothing to earn. */
.ribbon-part.is-full { color: #2e7d4f; font-weight: 600; }

.ribbon-sep { color: rgba(169, 130, 47, 0.5); }

/* ===== The band in summary mode =====
   The progress line is four short percentages, so it flexes into a centred
   row. The summary is three sentences that must be read IN ORDER, and reusing
   that row wrapped them into two columns with the third stranded below —
   the reader had to reconstruct the sequence from the layout.

   So: a stacked list, each point running the full width of the band. */
.ribbon.is-summary .ribbon-band { display: block; }

/* NO max-width. The band gives 622px of usable width and a 62ch measure was
   using 448 of it, so each point wrapped early and the block sat marooned in
   the middle of its own ribbon — which is the complaint this started from.
   The band's own padding is the margin; nothing else needs to be. */
.ribbon-points {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
  text-align: left;
}

.ribbon-points li {
  position: relative;
  padding-left: 18px;
  margin: 0 0 9px;
  font-size: 13px;
  line-height: 1.5;
  color: var(--ink);
}

.ribbon-points li:last-child { margin-bottom: 0; }

/* A short gold rule rather than a disc: a bullet sits badly against the
   band's texture, and the rule reads as a marker without competing. */
.ribbon-points li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 9px;
  width: 10px;
  height: 2px;
  background: var(--gold-dark);
}

.ribbon.is-summary .ribbon-head {
  display: block;
  text-align: center;
  font-weight: 700;
  color: var(--gold-dark);
}

@media (max-width: 640px) {
  /* Everything shortens together — tails, notch and wedges. Scaling only some
     of them is what makes a small banner look like a large one squashed. */
  .ribbon {
    margin: 20px auto 32px;
    width: calc(100% - 24px);
  }

  /* The widths are fluid already; only the vertical drop needs shortening,
     because the text inside wraps to two or three lines at this size and a
     deep tail would leave the ribbon taller than the tabs above it. */
  .ribbon::before,
  .ribbon::after {
    top: 20px;
    bottom: -18px;
  }

  .ribbon-band { padding: 14px 18px; }

  .ribbon-band::before,
  .ribbon-band::after { height: 17px; }
}

/* ===== Tab 6: Competency =====
 *
 * The plot is the tab. Everything else here is in service of being able to
 * read a dot: the quadrant tints say which corner is which without a legend,
 * the routes say where a declining skill goes, and the detail card is what a
 * dot means when you ask it.
 */
.cmp-verdict {
  font-size: 15.5px;
  line-height: 1.55;
  color: var(--dark);
  border-left: 3px solid var(--gold);
  padding-left: 14px;
  margin: 4px 0 22px;
}

.cmp-plot-wrap {
  position: relative;
  max-width: 560px;
  margin: 0 auto 6px;
  padding: 0 0 22px 22px;
}

.cmp-plot {
  width: 100%;
  height: auto;
  display: block;
  overflow: visible;
  background: rgba(255, 253, 248, 0.55);
  border: 1px solid var(--line);
  border-radius: 6px;
}

/* Only two quadrants are tinted. Shading all four turns the plot into a
   chessboard and the eye stops finding the corner that matters. */
.cmp-q-tr { fill: rgba(44, 107, 72, 0.09); }
.cmp-q-br { fill: rgba(156, 80, 73, 0.08); }

.cmp-axis { stroke: rgba(58, 50, 38, 0.22); stroke-width: 0.4; }

.cmp-qlabel {
  font-size: 3.1px;
  letter-spacing: 0.35px;
  font-weight: 700;
  fill: rgba(58, 50, 38, 0.4);
}

/* Dashed, and under the dots: a route is the argument, not the evidence. */
/* The two dashed marks on this chart mean different things and were being
   confused: a gold curve is a CONVERSION ROUTE (a declining skill and the
   target it turns into), a faint grey line is DRIFT (where a skill was last
   time). Nothing on the chart said which was which. */
.cmp-legend {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  color: var(--muted);
  line-height: 1.5;
}

.cmp-legend i {
  display: inline-block;
  width: 18px;
  height: 0;
  vertical-align: middle;
  margin-right: 7px;
}

.cmp-legend-route { border-top: 2px dashed var(--gold-dark); }
.cmp-legend-drift { border-top: 2px dashed rgba(58, 50, 38, 0.45); }

/* Not a failure, so not red — an honest statement that there is nothing to
   show yet. */
.cmp-legend-none { color: var(--muted); font-style: italic; }

.cmp-route {
  fill: none;
  stroke: var(--gold-dark);
  stroke-width: 0.55;
  stroke-dasharray: 2 1.6;
  opacity: 0.75;
}

.cmp-dot { cursor: pointer; }
.cmp-dot circle { transition: r 0.16s ease, opacity 0.16s ease; }
.cmp-halo { opacity: 0; }
.cmp-dot:hover .cmp-halo,
.cmp-dot:focus .cmp-halo,
.cmp-dot.is-on .cmp-halo { opacity: 0.22; }
.cmp-dot.is-on .cmp-halo { r: 8; }
.cmp-dot:focus { outline: none; }
.cmp-dot:focus-visible .cmp-halo { opacity: 0.4; }

.cmp-axis-x,
.cmp-axis-y {
  position: absolute;
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--muted);
}

.cmp-axis-x { bottom: 2px; left: 50%; transform: translateX(-50%); }
.cmp-axis-y {
  left: 2px;
  top: 50%;
  transform: rotate(-90deg) translateX(50%);
  transform-origin: left center;
  white-space: nowrap;
}

.cmp-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 18px;
  justify-content: center;
  margin: 14px 0 4px;
}

.cmp-key {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: 12px;
  color: var(--muted);
}

.cmp-key i {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  display: inline-block;
}

.cmp-key-route i {
  width: 16px;
  height: 0;
  border-radius: 0;
  border-top: 2px dashed var(--gold-dark);
}

.cmp-hint {
  text-align: center;
  font-size: 12px;
  color: var(--muted);
  margin: 2px 0 16px;
}

.cmp-card {
  background: var(--white);
  border: 1px solid var(--line);
  border-left: 3px solid var(--cmp, var(--gold));
  border-radius: 8px;
  padding: 16px 18px;
  margin-bottom: 12px;
}

.cmp-card h4 { font-size: 15px; margin: 6px 0 8px; }

.cmp-card-tag {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--cmp, var(--gold-dark));
}

.cmp-scores {
  display: flex;
  gap: 16px;
  font-size: 11.5px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--muted);
  margin: 0 0 10px;
}

.cmp-scores strong { color: var(--dark); font-size: 13px; }

/* ===== Grip on the skill, as distinct from the skill's market value =====
   Deliberately quieter than .cmp-scores and set apart from the prose rows
   below: these qualify the card rather than adding to it, and a reader
   skimming for the finding should pass over them until the finding surprises
   them. Chips rather than a sentence so the two read as one glance. */
.cmp-bands {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 0 0 10px;
}

.cmp-band {
  font-size: 11px;
  line-height: 1.4;
  padding: 3px 8px;
  border-radius: 999px;
  background: rgba(0, 0, 0, 0.035);
  border: 1px solid rgba(0, 0, 0, 0.07);
  color: var(--muted);
  white-space: nowrap;
}

/* Thin evidence under a strong claim. Amber, not red: this is a reason to
   check the card, not a finding that it is wrong. */
.cmp-band.is-thin {
  background: rgba(176, 122, 0, 0.09);
  border-color: rgba(176, 122, 0, 0.28);
  color: #8a6100;
}

.cmp-row { font-size: 13.5px; line-height: 1.55; margin: 0 0 6px; }
.cmp-row strong { color: var(--dark); }

/* ===== Whether a target capability is a recognised kind of work =====
   Set off by a rule rather than folded in with the prose rows: those are the
   audit's own claims, and this is an outside check on one of them. Keeping
   the two visually distinct is what stops the check reading as just another
   assertion from the same source. */
.cmp-recognised {
  font-size: 12.5px;
  line-height: 1.55;
  margin: 10px 0 0;
  padding-top: 9px;
  border-top: 1px dashed rgba(0, 0, 0, 0.12);
  color: var(--muted);
}

.cmp-recognised strong { color: var(--dark); }

.cmp-recognised-note { display: block; margin-top: 2px; font-size: 11.5px; }

/* ===== Where a target capability is headed =====
 *
 * The one block on this card that can deliver bad news, so it is the one block
 * that does not look like the labelled rows above it. Same reasoning as the
 * separate model call that produces it: everything else on the card is
 * descriptive, and letting this melt into the list would soften a warning back
 * into a bullet point.
 *
 * A left border rather than a full tinted box. A filled panel repeated on three
 * target cards becomes a wall and gets skipped; a 3px edge is enough to mark
 * the block as different in kind without shouting three times per screen.
 * Square corners, because a single-sided border with rounded corners renders as
 * a stray notch.
 */
.cmp-frontier {
  margin: 12px 0 0;
  padding: 10px 0 10px 12px;
  border-left: 3px solid var(--line);
  border-radius: 0;
}

.cmp-frontier-band {
  margin: 0;
  font-size: 13px;
  font-weight: 600;
  line-height: 1.45;
  color: var(--dark);
}

.cmp-frontier-survives {
  margin: 4px 0 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--ink);
}

/* Colour marks the two bands that are warnings. The two that are reassurance
   keep the neutral edge — if every band were tinted the tint would stop
   meaning anything, and the reader would learn to skip the block. */
.cmp-frontier.is-warn {
  border-left-color: #b8791a;
  background: rgba(184, 121, 26, 0.05);
}

.cmp-frontier.is-calm {
  border-left-color: rgba(44, 107, 72, 0.55);
}

/* Could not be placed confidently. Deliberately the quietest of the three:
   a hedge that looked as assured as a finding would be worse than no reading
   at all. */
.cmp-frontier.is-unsure {
  border-left-style: dashed;
  border-left-color: rgba(0, 0, 0, 0.2);
}

.cmp-frontier.is-unsure .cmp-frontier-band,
.cmp-frontier.is-unsure .cmp-frontier-survives {
  color: var(--muted);
}

.cmp-frontier-hedge {
  margin: 6px 0 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--muted);
  font-style: italic;
}

/* The CC BY credit. One line, quiet, and last — but never hidden: the licence
   grants the right to use the data on condition it is credited, so making
   this invisible would forfeit the permission rather than tidy the page.
   9.5px with reduced opacity is about as far as that can reasonably go while
   still being readable, which is the test that matters. */
.cmp-credit {
  margin: 20px 0 0;
  font-size: 9.5px;
  line-height: 1.5;
  color: var(--muted);
  opacity: 0.6;
  cursor: help;
}

/* Legible on hover, for anyone who actually wants to read it. */
.cmp-credit:hover { opacity: 0.95; }

.cmp-credit a { color: inherit; }

/* The sub-tab bar doubles as the legend: the swatch is the same colour as the
   dot, so the map and the list share one vocabulary instead of two. */
.cmp-subtabs {
  gap: 20px;
  margin-top: 20px;
  flex-wrap: wrap;
}

.cmp-subtab {
  display: inline-flex;
  align-items: center;
  gap: 7px;
}

.cmp-subtab.is-active {
  color: var(--cmp);
  border-bottom-color: var(--cmp);
}

.cmp-swatch {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  display: inline-block;
}

.cmp-count {
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
  background: rgba(58, 50, 38, 0.07);
  border-radius: 9px;
  padding: 1px 7px;
}

.cmp-subtab.is-active .cmp-count {
  color: var(--white);
  background: var(--cmp);
}

/*
 * A smooth scroll that ends on one of four similar cards leaves no clue which
 * one was asked for, so the arriving card says so and then stops — a highlight
 * that persists becomes a second selection state to reason about.
 */
@keyframes cmp-found {
  from { background: color-mix(in srgb, var(--cmp) 14%, var(--white)); }
  to { background: var(--white); }
}

.cmp-card.is-found { animation: cmp-found 1.6s ease-out; }

@media (prefers-reduced-motion: reduce) {
  .cmp-card.is-found { animation: none; outline: 2px solid var(--cmp); }
}

@media (prefers-reduced-motion: reduce) {
  .cmp-dot circle { transition: none; }
}

/* ===== Competency: the previous audit, and the archive ===== *
 *
 * The ghost is hollow and grey on purpose. Two sets of solid dots would be a
 * different chart — a comparison of two things — when what is meant is one
 * thing that moved.
 */
.cmp-ghost {
  fill: none;
  stroke: rgba(58, 50, 38, 0.38);
  stroke-width: 0.5;
  stroke-dasharray: 1.4 1.1;
}

.cmp-move {
  stroke: rgba(58, 50, 38, 0.32);
  stroke-width: 0.4;
}

.cmp-shift {
  text-align: center;
  font-size: 12.5px;
  color: var(--muted);
  margin: 0 0 6px;
}

.cmp-shift strong { color: var(--dark); }

.cmp-subtab-archive { margin-left: auto; }

.cmp-archive-lede {
  font-size: 13px;
  color: var(--muted);
  margin: 0 0 14px;
  max-width: 60ch;
}

.cmp-audit {
  display: flex;
  align-items: baseline;
  gap: 14px;
  width: 100%;
  text-align: left;
  font-family: var(--font);
  font-size: 13.5px;
  color: var(--ink);
  background: var(--white);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 12px 16px;
  margin-bottom: 8px;
  cursor: pointer;
}

.cmp-audit:hover { border-color: var(--gold); }

.cmp-audit.is-current {
  border-left: 3px solid var(--gold);
  cursor: default;
}

.cmp-audit-when {
  font-weight: 700;
  white-space: nowrap;
  color: var(--dark);
}

.cmp-audit-obj {
  color: var(--muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.cmp-audit-tag {
  margin-left: auto;
  font-size: 10px;
  font-weight: 700;
  letter-spacing: 1px;
  text-transform: uppercase;
  color: var(--gold-dark);
  white-space: nowrap;
}

/* ===== Insights: the radar ===== *
 *
 * Five percentages down a list are five separate facts. The thing worth seeing
 * is the shape they make together — whether the profile is round or spiky, and
 * which way it leans.
 */
.rad-wrap { max-width: 420px; margin: 0 auto 18px; }

.rad { width: 100%; height: auto; overflow: visible; display: block; }

.rad-ring { fill: none; stroke: rgba(58, 50, 38, 0.13); stroke-width: 0.35; }
.rad-spoke { stroke: rgba(58, 50, 38, 0.13); stroke-width: 0.35; }

.rad-now {
  fill: rgba(201, 165, 74, 0.26);
  stroke: var(--gold-dark);
  stroke-width: 0.7;
  stroke-linejoin: round;
}

/* Hollow and dashed, like the Competency ghost: one thing that moved, not two
   things being compared. */
.rad-ghost {
  fill: none;
  stroke: rgba(58, 50, 38, 0.34);
  stroke-width: 0.5;
  stroke-dasharray: 1.6 1.3;
}

/* The four band ids come from BAND_CLASS in app.js, which emits is-*. These
   rules said band-* and had never matched anything: every dot rendered gold
   and the band the colour was supposed to carry was invisible. It survived a
   class-coverage test because the class is interpolated into the template. */
.rad-dot { fill: var(--gold); }
.rad-dot.is-consistent { fill: #2e7d4f; }
.rad-dot.is-present { fill: var(--gold); }
.rad-dot.is-situational { fill: #c9622f; }
.rad-dot.is-untested { fill: #c9bfae; }

.rad-label {
  font-size: 3.4px;
  font-weight: 700;
  letter-spacing: 0.25px;
  fill: var(--ink);
}

.rad-value { font-size: 3.6px; font-weight: 700; fill: var(--gold-dark); }

/* The bands, once, instead of five rows repeating the chart. The swatch
   colour is the same one the dots use, which is what lets a single line
   replace a table without losing what the table said. */
.rad-bands { display: flex; flex-wrap: wrap; gap: 14px; justify-content: center; margin: 2px 0 14px; }

.rad-band { font-size: 11.5px; color: var(--muted); display: inline-flex; align-items: center; gap: 6px; }

.rad-band::before { content: ""; width: 9px; height: 9px; border-radius: 50%; background: var(--gold-dark); }

.rad-band.is-consistent::before { background: #2e7d4f; }
.rad-band.is-present::before { background: var(--gold); }
.rad-band.is-situational::before { background: #c9622f; }
.rad-band.is-untested::before { background: #c9bfae; }

.rad-key {
  display: flex;
  align-items: center;
  gap: 8px;
  justify-content: center;
  font-size: 11.5px;
  color: var(--muted);
  margin: 4px 0 0;
}

.rad-key-ghost {
  width: 18px;
  border-top: 2px dashed rgba(58, 50, 38, 0.4);
}

/* ===== Insights: the diagnostic scorecard ===== */
/* NO max-width HERE, deliberately.
   .result-card p justifies every paragraph in a card — the house style — and
   this one carried a 68ch measure on top of it. Justified to a measure
   narrower than the table it introduces, its right edge stopped short of the
   table's while its word spacing stretched to meet a margin that was not
   there. Matching the table's width is what makes the justification read as
   alignment rather than as a mistake. */
.sc-lede { font-size: 13.5px; color: var(--muted); margin: 0 0 14px; }

.sc-wrap { overflow-x: auto; }

.sc {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}

.sc thead th {
  text-align: left;
  font-size: 10.5px;
  letter-spacing: 0.7px;
  text-transform: uppercase;
  color: var(--muted);
  border-bottom: 1px solid var(--line);
  padding: 0 12px 8px 0;
  font-weight: 700;
}

.sc td,
.sc th[scope="row"] {
  vertical-align: top;
  padding: 14px 12px 14px 0;
  border-bottom: 1px solid var(--line);
  text-align: left;
  font-weight: 400;
}

.sc-letter {
  display: inline-block;
  font-weight: 700;
  color: var(--gold-dark);
  margin-right: 6px;
}

.sc-title { font-weight: 700; color: var(--dark); }

.sc-scale {
  display: block;
  font-size: 10.5px;
  color: var(--muted);
  margin-top: 3px;
  letter-spacing: 0.2px;
}

.sc-score { white-space: nowrap; }
.sc-score b { margin-left: 8px; font-size: 15px; color: var(--dark); }

.sc-pips { display: inline-flex; gap: 3px; vertical-align: middle; }

.sc-pip {
  width: 7px;
  height: 14px;
  border-radius: 2px;
  background: rgba(58, 50, 38, 0.11);
  display: inline-block;
}

/*
 * ONE COLOUR RAMP IS ONLY HONEST BECAUSE EVERY DIMENSION HAS 1 AS THE BAD END.
 * If a future row scored the other way round, this would quietly lie.
 */
.sc-s1 .sc-pip.is-on { background: #9c5049; }
.sc-s2 .sc-pip.is-on { background: #b57340; }
.sc-s3 .sc-pip.is-on { background: #a9822f; }
.sc-s4 .sc-pip.is-on { background: #5d7f4a; }
.sc-s5 .sc-pip.is-on { background: #2c6b48; }

/* The rule between rows carries the whole table: four readings that must be
   compared row by row, with cells of very different lengths. Without it the
   observations run together and the eye loses which score it is reading. */
.sc-row > * { border-top: 1px solid var(--line); padding: 13px 14px 13px 0; vertical-align: top; }
.sc-row:hover > * { background: rgba(201, 165, 74, 0.05); }
.sc-row th { text-align: left; font-weight: 400; padding-right: 22px; }

.sc-obs { color: var(--ink); max-width: 34ch; }
.sc-dir { color: var(--gold-dark); font-weight: 600; max-width: 30ch; }





/* The rule made visible: a verdict the reader can check beats one pronounced. */

/* Link to the account page — where export and reset live. Sits at the right
   of the header, away from the tabs, because it is account administration
   rather than part of the work. Positioning is the group's job, not its own. */
.account-link-header {
  font-size: 12.5px;
  color: #9a8032;
  text-decoration: none;
  border-bottom: 1px solid rgba(154, 128, 50, 0.4);
  white-space: nowrap;
}

.account-link-header:hover { border-bottom-color: #9a8032; }

/* ===== How Your CV Reads =====
   Sits beside the requirements audit but must never be mistaken for it: that
   panel checks against a list an employer published, this one against the
   objective the reader wrote themselves. Same card furniture, no shared
   colour vocabulary, no status chips — chips would imply a grading scheme
   that does not exist here. */
.cvr-lede {
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--muted);
  margin: 0 0 16px;
}

.cvr-list { list-style: none; margin: 0; padding: 0; }

.cvr-item {
  padding: 0 0 14px;
  margin: 0 0 14px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.07);
}

.cvr-item:last-child { border-bottom: 0; margin-bottom: 0; padding-bottom: 0; }

.cvr-finding {
  font-size: 14.5px;
  line-height: 1.5;
  color: var(--dark);
  font-weight: 600;
  margin: 0 0 7px;
}

.cvr-where,
.cvr-why {
  font-size: 13px;
  line-height: 1.55;
  margin: 0 0 5px;
  color: var(--muted);
}

.cvr-where strong,
.cvr-why strong { color: var(--dark); font-weight: 600; }

/* The rewrite is the thing they act on, so it is the thing that looks
   liftable. Boxed and monospace-adjacent rather than styled as more prose. */
.cvr-rewrite {
  margin: 9px 0 0;
  padding: 9px 11px;
  background: rgba(154, 128, 50, 0.07);
  border-left: 2px solid #9a8032;
  border-radius: 3px;
  font-size: 13px;
  line-height: 1.55;
}

.cvr-rewrite strong { color: #7d6720; font-weight: 600; }
.cvr-rewrite span { color: var(--dark); }


/* The objective builder's styles lived here: .ob, .ob-step, .ob-input and the
   rest. The helper was removed on 10 October 2026 and the objectives box asks
   its own question now, so nothing renders them. See app.js. */

/* ===== ONE RULE SURVIVED THE BUILDER, UNDER A HONEST NAME =====
   The coaching email field borrowed .ob-input, so deleting every .ob rule
   with the builder left it unstyled - a plain browser input in the middle of
   a styled form. It was never a builder style; it was the generic single-line
   field style with the wrong prefix on it. Renamed rather than restored, so
   the next person deleting a feature is not held hostage by its name. */

.field-input {
  width: 100%;
  box-sizing: border-box;
  padding: 9px 11px;
  font: inherit;
  font-size: 14px;
  border: 1px solid rgba(0, 0, 0, 0.16);
  border-radius: 4px;
  background: #fff;
}

.field-input:focus {
  outline: none;
  border-color: #9a8032;
  box-shadow: 0 0 0 2px rgba(154, 128, 50, 0.15);
}


/* Placeholder under an unanswered fork in Tab 2. Deliberately plain text and
   not a greyed-out set of questions: showing one branch disabled would put
   the old grievance assumption straight back, and someone with good news
   would read "how much visible conflict can you afford?" before they had
   said anything and take the hint. */
.qual-await {
  margin: 10px 0 0;
  font-size: 12.5px;
  color: var(--muted);
  font-style: italic;
}

/* ===== Review a Draft =====
   Bands and named properties, never a score — see renderReview in app.js for
   why a number here would be a machine for making posts worse. */
.rv-verdict {
  font-size: 15px;
  line-height: 1.6;
  color: var(--dark);
  margin: 0 0 18px;
  padding-left: 12px;
  border-left: 3px solid #9a8032;
}

.rv-block {
  padding: 0 0 14px;
  margin: 0 0 14px;
  border-bottom: 1px solid rgba(0, 0, 0, 0.07);
}

.rv-block:last-child { border-bottom: 0; margin-bottom: 0; padding-bottom: 0; }

.rv-badge {
  margin: 0 0 6px;
  font-size: 11.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: var(--muted);
}

.rv-badge.is-good { color: #4a7c3f; }
.rv-badge.is-warn { color: #8a6100; }
.rv-badge.is-bad { color: #9c3a2e; }

.rv-note { margin: 0 0 8px; font-size: 12.5px; line-height: 1.55; color: var(--muted); }
.rv-row { margin: 0 0 5px; font-size: 13.5px; line-height: 1.55; }
.rv-row strong { color: var(--dark); }
.rv-contested { color: var(--muted); font-size: 12.5px; }

/* Repeated on every review, deliberately. The moment a band reads as a
   forecast, people start editing to raise it. */
.rv-caveat {
  margin: 8px 0 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--muted);
  font-style: italic;
}

.rv-list { margin: 0; padding-left: 18px; font-size: 13.5px; line-height: 1.6; }
.rv-list li { margin-bottom: 6px; }

.rv-blocking { list-style: none; padding-left: 0; }
.rv-blocking li { margin-bottom: 12px; }
.rv-problem { display: block; color: var(--dark); font-weight: 600; }
.rv-fix { display: block; margin-top: 3px; color: var(--muted); }
.rv-fix strong { color: var(--dark); }

.rv-rewrite {
  margin: 0;
  padding: 11px 13px;
  background: rgba(154, 128, 50, 0.08);
  border-left: 2px solid #9a8032;
  border-radius: 3px;
  font-size: 14px;
  line-height: 1.6;
  color: var(--dark);
}

/* ===== The optional draft box on the Assets diagnostic =====
   A <details>, closed by default. It is genuinely optional — leave it shut
   and this tab behaves exactly as it did before the feature existed — and a
   collapsed disclosure says that more honestly than a textarea sitting open
   with the word "optional" beside it. */
.draft-box {
  border: 1px dashed rgba(154, 128, 50, 0.45);
  background: rgba(154, 128, 50, 0.04);
  border-radius: 6px;
  padding: 0;
  margin: 0 0 18px;
}

.draft-box > summary {
  cursor: pointer;
  list-style: none;
  padding: 13px 16px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--dark);
  display: flex;
  align-items: baseline;
  gap: 10px;
}

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

.draft-box > summary::before {
  content: "▸";
  color: #9a8032;
  font-size: 11px;
}

.draft-box[open] > summary::before { content: "▾"; }

.draft-optional {
  margin-left: auto;
  font-weight: 400;
  font-size: 11.5px;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}

.draft-box > *:not(summary) { margin-left: 16px; margin-right: 16px; }
.draft-box > *:last-child { margin-bottom: 16px; }

.draft-lede {
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--muted);
  margin: 0 0 14px;
}

/* ===== The narrative arc on an angle =====
   Set apart from the angle's own claims: those are what to say, this is how
   to say it. Boxed rather than another prose row so the two do not blur. */
.angle-shape {
  margin: 10px 0 0;
  padding: 10px 12px;
  background: rgba(0, 0, 0, 0.025);
  border-left: 2px solid rgba(154, 128, 50, 0.55);
  border-radius: 3px;
}

.angle-shape-name {
  margin: 0;
  font-size: 11.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: #7d6720;
}

.angle-shape-how {
  margin: 3px 0 0;
  font-size: 12.5px;
  line-height: 1.5;
  color: var(--muted);
}

/* The justification. Never hidden: a shape name alone is a label to trust,
   and this is the line that makes it checkable against their own record. */
.angle-shape-why,
.angle-shape-open {
  margin: 6px 0 0;
  font-size: 12.5px;
  line-height: 1.55;
  color: var(--dark);
}

.angle-shape-why strong,
.angle-shape-open strong { color: #7d6720; }

/* ===== Coaching ===== ----------------------------------------------------
   The locked tab stays visible and clickable. A greyed-out button that does
   nothing teaches people the app is broken; one that opens and explains what
   is outstanding turns the lock into a reason to finish. */
.tab.is-locked { opacity: 0.62; }
.tab.is-locked:hover { opacity: 0.8; }
.tab-lock { margin-left: 6px; font-size: 10px; }

/* ===== THE PADLOCK BELONGS TO THE LOCKED STATE, NOT TO THE TAB =====
   It was written straight into the markup with nothing to take it away, so an
   unlocked Coaching tab still wore a padlock. A person who has finished the
   work is told, by the only symbol on the button, that they have not. */
.tab:not(.is-locked) .tab-lock { display: none; }

.coach-locked {
  max-width: 620px;
  margin: 10px auto 0;
  padding: 26px 28px;
  border: 1px dashed rgba(154, 128, 50, 0.5);
  border-radius: 8px;
  background: rgba(154, 128, 50, 0.05);
  text-align: left;
}

.coach-lock-title {
  margin: 0 0 6px;
  font-size: 16px;
  font-weight: 600;
  color: var(--dark);
}

/* What is behind the locked door. Plain body text, because it is the only
   sentence here that is not about the lock. */
.coach-lock-what {
  margin: 0 0 12px;
  font-size: 14px;
  line-height: 1.65;
  color: var(--muted);
}

.coach-lock-line {
  margin: 0 0 14px;
  font-size: 14px;
  color: #7d6720;
  font-weight: 600;
}

/* Labels the list below it, so the milestone names read as instructions
   rather than as a bare inventory. */
.coach-lock-next {
  margin: 0 0 6px;
  font-size: 13.5px;
  font-weight: 600;
  color: var(--dark);
}

/* What is still outstanding. The point of the locked state — a closed door
   with no sign is just a dead end. */
.coach-lock-list {
  margin: 0 0 16px;
  padding-left: 18px;
  font-size: 13.5px;
  line-height: 1.7;
  color: var(--muted);
}

/*
 * ===== THE TESTIMONIAL ON THE UNLOCKED COACHING TAB =====
 *
 * Deliberately NOT a .coach-card. The cards below it are things to do —
 * write a reflection, pick a slot — and giving a quotation the same white
 * box would put it in that sequence, as though it were a step.
 *
 * So it is a quiet band instead: no border on three sides, a gold rule down
 * the left, and the panel's own background showing through. It reads as a
 * voice rather than a control.
 */
.coach-quote {
  margin: 0 0 22px;
  padding: 4px 0 4px 20px;
  border-left: 3px solid var(--gold, #c9a54a);
}

.coach-quote p {
  margin: 0;
  font-size: 15.5px;
  line-height: 1.75;
  font-style: italic;
  /* Not full strength. It is supporting the page, not making its argument. */
  opacity: 0.88;
}

/*
 * <cite> is italic by default, and the quotation above it already is. Left
 * italic, the attribution stops reading as the speaker and starts reading as
 * more of the quotation.
 */
.coach-quote cite {
  display: block;
  margin-top: 10px;
  font-style: normal;
  font-size: 13px;
  letter-spacing: 0.3px;
  color: #9a8032;
}

.coach-quote cite::before { content: "\2014\00a0"; }

@media (max-width: 520px) {
  .coach-quote { padding-left: 14px; }
  .coach-quote p { font-size: 14.5px; }
}

.coach-card {
  background: #fff;
  border: 1px solid rgba(0, 0, 0, 0.08);
  border-radius: 8px;
  padding: 22px 24px;
  margin: 0 0 18px;
}

.coach-card h3 {
  margin: 0 0 8px;
  font-size: 13px;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: #9a8032;
}

.coach-card-note {
  margin: 0 0 16px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--muted);
}

.coach-price { margin: 0 0 10px; font-size: 15px; color: var(--dark); }
.coach-price strong { font-size: 20px; color: #7d6720; }

/* Two offers side by side, stacking on narrow screens. */
/* One offer, one column. This was a two-column grid while a cohort card sat
   beside the one-to-one card; with the cohort retired, 1fr stops the single
   remaining card from sitting at half the width of its own section. */
.coach-options { display: grid; grid-template-columns: 1fr; gap: 18px; }

.coach-window {
  margin: 0 0 12px;
  font-size: 12px;
  color: var(--muted);
  font-style: italic;
}

.coach-day { margin-bottom: 14px; }

.coach-day-name {
  margin: 0 0 6px;
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--muted);
}

/* Every slot carries both clocks. The Singapore time is the contract; the
   local one is what stops somebody booking 9pm SGT thinking it is their
   evening. */
.coach-slot,

.coach-slot:hover,

.coach-slot-sgt,
.coach-slot-local,

/* Said before the button, not after. */
.coach-invoice-note {
  margin: 4px 0 0;
  padding: 12px 14px;
  background: rgba(0, 0, 0, 0.03);
  border-radius: 5px;
  font-size: 12.5px;
  line-height: 1.6;
  color: var(--muted);
}

.coach-invoice-note strong { color: var(--dark); }

.coach-result {
  margin: 14px 0 0;
  padding: 13px 15px;
  background: rgba(74, 124, 63, 0.09);
  border-left: 3px solid #4a7c3f;
  border-radius: 4px;
  font-size: 13.5px;
  line-height: 1.6;
}

/* The one screen in this product where a human reads what you write. Stated
   above the box rather than below it, and coloured so it is not skimmed —
   everywhere else the promise is the opposite, so the exception has to work
   harder than a note. */
.coach-routing {
  margin: 0 0 14px;
  padding: 11px 13px;
  background: rgba(154, 128, 50, 0.09);
  border-left: 3px solid #9a8032;
  border-radius: 4px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--dark);
}

.coach-legal {
  margin: 12px 0 0;
  font-size: 12px;
  color: var(--muted);
}

.coach-legal a { color: #7d6720; }

/* ===== THE MENTOR MAP =====
   A dark inset in a light page, deliberately. The marketing site is dark and
   gold, so the panel reads as the same brand rather than as a foreign widget —
   and faint grey edges on a damask background would be invisible, which is the
   practical reason. */
.mgraph {
  background: var(--dark);
  border-radius: 10px;
  padding: 16px 18px 14px;
  margin: 0 0 22px;
}

.mgraph-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 10px;
}

.mgraph-title {
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.3px;
  color: #efe9dc;
}

/* Always available, never loud. The map is worth space only while it is being
   read; somebody working down the list should be able to put it away. */
.mgraph-toggle {
  background: none;
  border: 0;
  padding: 0;
  font-family: inherit;
  font-size: 12px;
  color: #8f887a;
  cursor: pointer;
  text-decoration: underline;
}

.mgraph-toggle:hover { color: var(--gold); }

.mgraph-body { position: relative; }

/* Fixed drawing size, fluid display size. The layout maths runs in canvas
   pixels; pointer coordinates are scaled back in app.js. */
.mgraph canvas {
  display: block;
  width: 100%;
  height: auto;
}

/* Sits over the canvas rather than below it, so reading a name does not move
   the panel and shift the dot out from under the cursor. */
.mgraph-tip {
  position: absolute;
  left: 0;
  bottom: 6px;
  background: rgba(20, 20, 20, 0.92);
  border: 1px solid rgba(201, 165, 74, 0.4);
  border-radius: 5px;
  padding: 5px 9px;
  font-size: 12px;
  color: #efe9dc;
  pointer-events: none;
}

.mgraph-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 10px;
}

.mgraph-key {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  color: #8f887a;
}

.mgraph-key i { border-radius: 50%; display: inline-block; }

/* The sentence under the picture. Carries the finding the shape is meant to
   show, so the panel still says something to a reader who does not parse
   networks by eye. */
.mgraph-note {
  margin-top: 8px;
  font-size: 12px;
  line-height: 1.5;
  color: #b3ac9c;
}

.mgraph-note:empty { display: none; }

/* Where a click on the map lands. Brief and gold rather than a persistent
   selected state: it answers "which one did I just click" and then gets out of
   the way. */
.mentor-card.is-found {
  box-shadow: 0 0 0 2px var(--gold);
  transition: box-shadow 0.25s ease;
}

/* Two rows: status owns colour, sector owns the tint behind the dots. Kept
   apart because they answer different questions — "how far have I got with
   this person" and "roughly what world are they in". */
.mgraph-statuses {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

.mgraph-sectors {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px;
  margin-top: 10px;
  padding-top: 10px;
  border-top: 1px solid rgba(190, 186, 176, 0.14);
}

/* "loosely" is doing real work. The tint is read off the employer and job
   title by a keyword table, so it is a guess — a quiet one, but the label must
   not let it pass for a record. */
.mgraph-sector-lede {
  font-size: 11px;
  color: #6f695d;
  margin-right: 4px;
}

.mgraph-sector {
  font-size: 11px;
  padding: 2px 8px;
  border-radius: 10px;
  white-space: nowrap;
}

.mgraph-sector b { font-weight: 600; opacity: 0.75; }

/* ===== Finding people, from inside the app =====
 *
 * This replaced a Chrome extension panel injected into LinkedIn. See
 * routes/mentors.js for why the extension went; the short version is that it
 * needed the user to hold an Anthropic API key, and LinkedIn's terms name
 * browser add-ons and profiles in the same sentence.
 *
 * Sits ABOVE the saved list and the graph, because it is where the tab now
 * starts. An empty Mentors tab used to be a dead end with instructions to go
 * and install something.
 */
.msearch {
  margin: 0 0 26px;
  padding: 16px 18px;
  background: rgba(255, 255, 255, 0.6);
  border: 1px solid var(--line);
  border-radius: 8px;
}

.msearch-label {
  display: block;
  font-size: 13px;
  font-weight: 700;
  color: var(--dark);
  margin: 0 0 7px;
}

.msearch-input {
  width: 100%;
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.5;
  color: var(--ink);
  background: rgba(255, 255, 255, 0.9);
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 10px 12px;
  resize: vertical;
}

.msearch-input:focus { outline: none; border-color: var(--gold); }

.msearch-hint {
  margin: 7px 0 0;
  font-size: 11.5px;
  line-height: 1.5;
  color: var(--muted);
}

.msearch-actions {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-top: 12px;
}

.msearch-btn {
  padding: 9px 22px;
  font-family: inherit;
  font-size: 13px;
  font-weight: 700;
  color: var(--dark);
  background: var(--gold);
  border: none;
  border-radius: 6px;
  cursor: pointer;
}

.msearch-btn:disabled { opacity: 0.55; cursor: default; }

/* The status line carries the whole wait. Twenty to forty seconds of silence
   reads as a broken button, so this says what is happening and then what has
   been found. */
.msearch-status {
  margin: 12px 0 0;
  font-size: 12.5px;
  color: var(--muted);
  font-style: italic;
}

.msearch-status.is-error { color: #9c3a2f; font-style: normal; }

.msearch-note {
  margin: 12px 0 0;
  font-size: 11.5px;
  line-height: 1.55;
  color: var(--muted);
}

.msearch-results { margin-top: 14px; }

/* ===== A RESULT IS NOT A SAVED CONTACT =====
 *
 * Deliberately lighter than .mentor-card below it. These are candidates the
 * reader is deciding about; the saved list is people they chose. If the two
 * looked the same, saving would feel like it had already happened and the
 * decision — which is the whole point — would stop being made.
 */
.found {
  padding: 12px 14px;
  margin: 0 0 10px;
  background: rgba(255, 255, 255, 0.85);
  border: 1px solid var(--line);
  border-left: 3px solid var(--gold);
  border-radius: 0;
}

.found-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.found-head h4 { margin: 0; font-size: 14.5px; font-weight: 700; color: var(--dark); }

/* Confidence is stated, not implied. An unlabelled result reads as certain,
   and "unsure" is the most useful thing this search can tell you about a name
   you were about to write to. */
.found-conf { font-size: 10.5px; letter-spacing: 0.03em; text-transform: uppercase; }
.found-conf.is-high { color: #2c6b48; }
.found-conf.is-medium { color: #8a6d3b; }
.found-conf.is-low { color: #9c3a2f; }

.found-role { margin: 4px 0 0; font-size: 12.5px; color: var(--ink); }
.found-loc { margin: 2px 0 0; font-size: 11.5px; color: var(--muted); }
.found-why { margin: 7px 0 0; font-size: 12.5px; line-height: 1.55; color: var(--ink); }

.found-links { margin: 7px 0 0; font-size: 11.5px; }
.found-links a { color: var(--gold-dark); }

.found-actions { display: flex; align-items: center; gap: 10px; margin-top: 10px; }

.found-save {
  padding: 5px 14px;
  font-family: inherit;
  font-size: 11.5px;
  color: var(--ink);
  background: transparent;
  border: 1px solid rgba(0, 0, 0, 0.22);
  border-radius: 4px;
  cursor: pointer;
}

.found-save:hover:not(:disabled) { background: rgba(255, 255, 255, 0.9); }
.found-save:disabled { opacity: 0.5; cursor: default; }

.found-state { font-size: 11.5px; color: var(--muted); }


/* ================================================================
   ASCENT — ported from the marketing site's chat widget
   ================================================================

   SAME MARKUP AND SAME CSS, deliberately. Ascent is one character and should
   look like one character in both places; two stylesheets drifting apart is
   how the same falcon ends up a different size on two pages of one product.

   WHAT DIFFERS HERE, and only this: the app scrolls and the marketing site
   does not, so nothing below depends on a locked viewport.

   The mobile rules at the bottom are the ones that matter. The panel is
   near-full-height, and the owner chose to show the AI-disclosure banner and
   Ascent at the same time — so on a 375px screen a banner at the top and an
   open panel below can cover most of the app between them. The toggle is
   already reduced there for the same reason.
   ================================================================ */

.chat-widget {
  position: fixed;
  right: 20px;
  bottom: 20px;
  z-index: 50;
}

.chat-toggle {
  width: 88px; /* source avatar is 300px, so this stays crisp */
  height: 88px;
  border-radius: 50%;
  border: 3px solid var(--gold);
  background: var(--white);
  padding: 0;
  cursor: pointer;
  overflow: hidden;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: transform 0.2s ease, border-color 0.2s ease;
}

.chat-toggle:hover {
  transform: scale(1.06);
  border-color: var(--gold-dark);
}

.chat-toggle img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.chat-panel {
  position: absolute;
  right: 0;
  bottom: 102px;
  width: min(320px, calc(100vw - 40px));
  height: min(420px, calc(100vh - 110px));
  background: var(--white);
  border: 1px solid #e4ded0;
  border-radius: 12px;
  display: none;
  flex-direction: column;
  overflow: hidden;
}

.chat-panel.open { display: flex; }

.chat-header {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 14px;
  background: var(--dark);
  color: var(--white);
  flex: 0 0 auto;
}

.chat-header-avatar {
  width: 34px;
  height: 34px;
  border-radius: 50%;
  object-fit: cover;
  border: 1px solid var(--gold);
}

.chat-header-text { flex: 1; min-width: 0; }

.chat-header-title {
  font-size: 13px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}

.chat-header-status {
  font-size: 11px;
  opacity: 0.7;
}

.chat-close {
  background: none;
  border: none;
  color: var(--white);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 4px;
  opacity: 0.8;
}

.chat-close:hover { opacity: 1; }

.chat-messages {
  flex: 1;
  overflow-y: auto;
  padding: 14px;
  display: flex;
  flex-direction: column;
  gap: 10px;
  background: #faf8f3;
}

.chat-msg {
  max-width: 82%;
  padding: 9px 12px;
  border-radius: 10px;
  font-size: 13px;
  line-height: 1.45;
}

.chat-msg.bot {
  align-self: flex-start;
  background: var(--white);
  border: 1px solid #e4ded0;
  color: var(--dark);
}

.chat-msg.user {
  align-self: flex-end;
  background: var(--gold);
  color: var(--white);
}

.chat-msg a { color: inherit; }

.chat-input-row {
  flex: 0 0 auto;
  display: flex;
  border-top: 1px solid #e4ded0;
}

.chat-input-row input {
  flex: 1;
  border: none;
  padding: 12px 14px;
  font-size: 13px;
  font-family: var(--font);
  outline: none;
}

.chat-input-row button {
  border: none;
  background: var(--gold);
  color: var(--white);
  width: 46px;
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  transition: background 0.2s ease;
}

.chat-input-row button:hover { background: var(--gold-dark); }

/* ===== Mobile ===== */
@media (max-width: 600px) {
  .nav-links { gap: 16px; }
  .nav-links a { font-size: 12px; }

  .chat-panel {
    right: -10px;
    width: calc(100vw - 24px);
  }

  .about-columns {
    flex-direction: column;
    gap: 10px;
  }
}

/* ===== Special-offer countdown =====
   Fills the band between the call to action and the first section, which was
   bare damask. Transparent rather than a card: the pattern behind it is the
   brand, and boxing this off would make it read as an advert dropped onto the
   page instead of part of it. */
/* ===== CENTRED IN ITS OWN GAP, BY CONSTRUCTION =====
 *
 * MEASURED, the band's top edge sat at 358 and the button's bottom at 359 — a
 * one-pixel OVERLAP, with 26px below it and 64px of slack pooling uselessly at
 * the bottom of the column. It read as jammed against the button because it
 * was touching it.
 *
 * Two causes, both of them arithmetic rather than taste:
 *
 *   ASYMMETRIC PADDING. 10px above and 14px below meant the band could never
 *   look centred no matter what the margins did — the content inside it sat
 *   2px high of its own middle.
 *
 *   THE CONTAINER STARTS ABOVE THE BUTTON. .content-sections carries
 *   margin-top:-30px, so its top edge is 8px past the button's bottom once its
 *   own 14px of padding is counted. A margin-top here is therefore 8px shorter
 *   on screen than it reads in the file, which is exactly the sort of thing
 *   that gets "fixed" by nudging a number until it looks right.
 *
 * So the 8px is cancelled explicitly and the two gaps are stated as one
 * number. GAP + 8 above, GAP below — equal on screen, and if anything above
 * this ever moves, the only value to re-solve is the 8.
 *
 * WHY NOT flex:1 AND align-items:center, which would centre it in whatever
 * space exists at any height: because the 64px of slack lives at the BOTTOM of
 * the column, so absorbing it here would push the film and the chat line down
 * by 64px — and both are positioned by fixed-pixel transforms solved against
 * their current positions. That is the bug that put the chat line through the
 * footer once already. The gap above this band does not change with viewport
 * height, so centring it is a calculation, not a layout mode.
 */
.offer-band {
  flex: 0 0 auto;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  justify-content: center;
  gap: 6px 14px;
  padding: 12px 8%;
  /*
   * ===== THE TWO NUMBERS ARE NOT THE GAP. THEY SOLVE FOR IT. =====
   *
   * Neither margin equals what you see on screen, because two other things sit
   * in the same space and both were missed on the first attempt:
   *
   *   ABOVE   .content-sections has margin-top:-30px, so its top edge is
   *           already 8px past the button's bottom once its 14px of padding
   *           is counted. A margin here is 8px shorter than it reads.
   *
   *   BELOW   #about carries 28px of its own padding-top before its heading.
   *           That padding is the real spacer under the band, and no margin
   *           here can be smaller than it without going negative.
   *
   * So:  visible above = marginTop - 8      visible below = marginBottom + 28
   *
   * Re-solved to 23 / -17 once the hero stopped absorbing space (see
   * .cta-button). Measured across simulated viewport heights afterwards: 11
   * above, 11 below, at 919, 1100, 1330 and 1600. That is the check that
   * matters — an earlier pass that ignored the 28px produced 17 and 44 while
   * looking perfectly reasonable in the file, and the pass before THAT was
   * measured at a single viewport height and missed the hero entirely.
   *
   * THE FOOTPRINT IS DELIBERATELY UNCHANGED. 21 + 50 + (-15) = 56, against the
   * 55 the old band occupied. Everything below — the film and the chat line —
   * is positioned by fixed-pixel transforms solved against those positions, so
   * growing this band is what once pushed the chat line through the footer.
   * One pixel is inside the tolerance; thirty would not have been.
   *
   * WHY NOT flex:1 AND CENTRE, so it self-solves at any height: the column's
   * spare space pools at the BOTTOM, not here, so absorbing it would move the
   * film and the note by the full amount. The gap above this band is fixed at
   * every viewport height, which makes centring a calculation rather than a
   * layout mode.
   */
  margin: 23px auto -17px;
  max-width: 900px;
  width: 100%;
}

.offer-band[hidden] { display: none; }

/* ===== THE HEADING BELOW HAS TO STOP CLIMBING INTO THIS SPACE =====
   #about h2 carries margin-top: -70px, added long before this band existed to
   drag "3 Impact Tools" up and close the empty gap under the call to action.
   That is the exact gap the countdown now occupies, so the two were drawn on
   top of each other — the band laid out correctly at 428-477 and the heading
   was pulled back up to 439, straight through it.

   Cancelled only WHILE THE BAND IS ON SCREEN. Written as a sibling rule rather
   than by editing the -70px away, because the band removes itself the moment
   the offer expires: on 2 December the countdown vanishes, this rule stops
   matching, and the original spacing comes back by itself instead of leaving a
   seventy-pixel hole nobody is around to notice. */
.offer-band:not([hidden]) + #about h2 {
  margin-top: 0;
}

.offer-lede {
  font-size: clamp(12px, 1.4vw, 14px);
  letter-spacing: 0.4px;
  color: var(--dark);
  font-weight: 600;
}

.offer-date {
  font-size: clamp(12px, 1.4vw, 14px);
  color: var(--gold-dark);
  font-weight: 600;
}

/* The clock proper. Tabular figures so the seconds column does not shuffle the
   whole row sideways every tick — proportional digits make a running timer
   visibly twitch, which reads as a bug. */
.offer-clock {
  display: inline-flex;
  align-items: baseline;
  gap: 2px;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum";
}

.offer-unit {
  display: inline-flex;
  align-items: baseline;
  gap: 3px;
}

.offer-unit b {
  font-size: clamp(15px, 2vw, 19px);
  font-weight: 700;
  color: var(--dark);
  min-width: 1.6ch;
  text-align: right;
}

.offer-unit i {
  font-style: normal;
  font-size: 10px;
  letter-spacing: 0.5px;
  text-transform: uppercase;
  color: var(--gold-dark);
}

.offer-sep {
  color: var(--gold-dark);
  opacity: 0.5;
  padding: 0 2px;
}

/* Short viewports have no room to spare — the whole page is locked to 100vh
   with overflow hidden, so this band has to give ground before the sections
   below it do. */
/* The 40vh hero used to overflow below ~793px tall, pushing the button through
   the countdown band. .hero now sizes to content at every height, so the
   override that used to live here is unnecessary. */

@media (max-height: 620px) {
  /* Symmetric, matching the band's normal padding — the asymmetric 4/8 here
     predated the centring work and would have tilted it again at this size. */
  .offer-band { padding: 6px 8%; gap: 2px 10px; }
  .offer-unit i { display: none; }
}

/* ================================================================
   THE RESOURCE RAIL — five other places this work lives

   Sits in the space under the film that the removed liner gave back.

   ===== WHY AN ARC AND NOT A ROW =====

   The space is wide and short: roughly 230px of run between the columns
   above and the footer, most of which the film takes. Six equal cards in a
   row at that height are all too small to read. An arc spends the height on
   ONE card and lets the rest recede, so the front card is legible at a
   glance and the others read as "there is more here" — which is the whole
   job of this element.

   ===== WHY CSS 3D AND NOT THREE.JS =====

   This is five link cards on a marketing page with no build step and no
   framework. React Three Fiber would have meant several hundred kilobytes of
   React and Three.js, a bundler this site does not have, and a WebGL context
   on a page whose entire budget is one viewport — to draw what `perspective`
   and `rotateY` do natively in a few lines. The reference image is a card
   ring, and a card ring is what CSS transforms are for.
   ================================================================ */

.reslink {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 4px;
  width: 100%;
  /* A minimum, not a gap. See the note above #about h2. */
  margin-top: 14px;
  /*
   * Sized in vh as well as px so a short window shrinks the rail instead of
   * pushing it under the footer, where the page's overflow:hidden would cut
   * it in half without a scrollbar to reveal it. That failure has already
   * happened once on this page — see the film's comment.
   */
  height: clamp(84px, 13vh, 112px);
}

.reslink-stage {
  position: relative;
  flex: 1;
  height: 100%;
  max-width: 560px;
  /*
   * The vanishing point. Larger than the card depth on purpose: a tight
   * perspective throws the side cards into a keystone so severe the text
   * stops being readable, which turns a navigation element into decoration.
   */
  perspective: 900px;
  perspective-origin: 50% 50%;
}

.reslink-ring {
  position: absolute;
  inset: 0;
  list-style: none;
  margin: 0;
  padding: 0;
  transform-style: preserve-3d;
}

/*
 * One slot per card. `--d` is how many steps this card sits from the front,
 * signed, and is written by resources.js on every move. Everything below is
 * derived from it, so the script sets one number and the stylesheet owns the
 * entire look — no inline styles carrying design decisions.
 */
.reslink-slot {
  position: absolute;
  top: 50%;
  left: 50%;
  width: clamp(132px, 20vw, 172px);
  height: clamp(72px, 11vh, 96px);
  margin: 0;
  transform-style: preserve-3d;
  transition:
    transform 420ms cubic-bezier(0.22, 0.61, 0.36, 1),
    opacity 420ms cubic-bezier(0.22, 0.61, 0.36, 1);
  transform:
    translate(-50%, -50%)
    translateX(calc(var(--d, 0) * 52%))
    translateZ(calc(var(--d, 0) * var(--d, 0) * -46px))
    rotateY(calc(var(--d, 0) * -34deg));
}

/*
 * Depth banding rather than a formula, because opacity that falls off
 * smoothly leaves the third card faintly visible behind the second and the
 * arc reads as smudged. Three clean steps and then nothing.
 */
.reslink-slot[data-depth="0"] { opacity: 1;    z-index: 4; }
.reslink-slot[data-depth="1"] { opacity: 0.62; z-index: 3; }
.reslink-slot[data-depth="2"] { opacity: 0.26; z-index: 2; }
.reslink-slot[data-depth="3"] { opacity: 0;    z-index: 1; }

.reslink-card {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 4px;
  /*
   * Text-only is the default layout. A card with a thumbnail switches to a row
   * via .has-thumb below — written that way round because most cards will not
   * have one for a while, and because a broken image path removes the class at
   * runtime and has to land back on something that works.
   */
  width: 100%;
  height: 100%;
  padding: 10px 14px;
  text-decoration: none;
  text-align: center;
  border-radius: 10px;
  /*
   * TRANSPARENT, as asked. The damask backdrop drifts behind the whole page,
   * so a solid card would punch five opaque holes in a surface that is meant
   * to be continuous. The wash is just enough to hold the text against a
   * moving pattern.
   */
  background: rgba(255, 253, 249, 0.42);
  backdrop-filter: blur(7px) saturate(1.15);
  -webkit-backdrop-filter: blur(7px) saturate(1.15);
  border: 1px solid rgba(201, 165, 74, 0.55);
  box-shadow:
    0 10px 26px rgba(20, 16, 8, 0.14),
    inset 0 1px 0 rgba(255, 255, 255, 0.55);
  transition: border-color 180ms ease, box-shadow 180ms ease, background 180ms ease;
}

.reslink-slot[data-depth="0"] .reslink-card:hover,
.reslink-card:focus-visible {
  background: rgba(255, 253, 249, 0.62);
  border-color: var(--gold);
  box-shadow:
    0 14px 32px rgba(20, 16, 8, 0.2),
    inset 0 1px 0 rgba(255, 255, 255, 0.7);
}

/* Visible on the damask, and on the card, without a second rule per state. */
.reslink-card:focus-visible {
  outline: 2px solid var(--gold-dark);
  outline-offset: 3px;
}

.reslink-label {
  font-size: clamp(13px, 1.5vw, 16px);
  font-weight: 700;
  letter-spacing: 0.01em;
  color: var(--dark);
}

.reslink-blurb {
  font-size: clamp(10px, 1.05vw, 12px);
  line-height: 1.3;
  color: #6b6354;
}

/*
 * ===== A CARD WITH NO URL REFUSES TO LOOK FINISHED =====
 *
 * href="#" would have rendered as a perfectly normal card that jumps the page
 * to the top when clicked. Six of those could sit on the homepage looking
 * correct until somebody happened to press one. This is deliberately visible
 * from across the room.
 */
.reslink-card.is-unset {
  border-style: dashed;
  border-color: rgba(169, 130, 47, 0.45);
  cursor: not-allowed;
}
.reslink-card.is-unset .reslink-label { color: #8a8272; }
.reslink-card.is-unset .reslink-blurb { font-style: italic; }

.reslink-arrow {
  flex: 0 0 auto;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  font-size: 20px;
  line-height: 1;
  color: var(--gold-dark);
  background: rgba(255, 253, 249, 0.5);
  border: 1px solid rgba(201, 165, 74, 0.45);
  border-radius: 50%;
  cursor: pointer;
  transition: background 160ms ease, transform 160ms ease;
}

.reslink-arrow:hover { background: rgba(255, 253, 249, 0.8); transform: scale(1.08); }
.reslink-arrow:focus-visible { outline: 2px solid var(--gold-dark); outline-offset: 2px; }

/*
 * ===== REDUCED MOTION REMOVES THE MOVEMENT, NOT THE LINKS =====
 *
 * The arc stays, so the layout does not jump between preferences. What goes
 * is the travel: transitions become instant here and resources.js never
 * starts its timer. Arrows, arrow keys and drag all still work, so every one
 * of the five is still reachable — a motion preference should cost an
 * animation, never a destination.
 */
@media (prefers-reduced-motion: reduce) {
  .reslink-slot { transition: none; }
  .reslink-card { transition: none; }
  .reslink-arrow { transition: none; }
}

/*
 * Narrow screens: the side cards would overlap the arrows and each other, so
 * the arc flattens toward a single card with a hint of the next.
 */
@media (max-width: 560px) {
  .reslink-slot {
    transform:
      translate(-50%, -50%)
      translateX(calc(var(--d, 0) * 62%))
      translateZ(calc(var(--d, 0) * var(--d, 0) * -60px))
      rotateY(calc(var(--d, 0) * -26deg));
  }
  .reslink-slot[data-depth="2"] { opacity: 0; }
}

/* Narrow screens: the same reduction the marketing site makes, for the same
   reason — an 88px avatar 20px off the corner lands on whatever control
   happens to be at the bottom right. */
@media (max-width: 768px) {
  .chat-widget { right: 12px; bottom: 12px; }
  .chat-toggle { width: 58px; height: 58px; border-width: 2px; }
  .chat-panel { bottom: 68px; }
}

/* ================================================================
   MOOD CARDS + THE DATED JOURNAL ENTRY
   ================================================================

   Ported from the prototype that beat How We Feel in a blind comparison.
   What survived the critic, and why, is worth keeping here:

   ALL SIXTEEN VISIBLE AT ONCE. The reference design is a pannable canvas whose
   labels are clipped on every edge, which turns one tap into browse-then-tap.
   A 4x4 grid that fits under the entry is the whole advantage; if this ever
   needs scrolling it has lost the argument it won.

   COLOUR IS THE SECOND AXIS, NOT DECORATION. Energy x pleasantness, four
   families. It is what lets a month of entries be scanned without reading them.

   LITERAL HEX, NOT var() INSIDE --c. The prototype stored the string
   "var(--hi-unpleasant)" in a custom property. That resolves in a plain
   background and falls apart inside color-mix(), which is exactly what the
   critic caught: the selected card filled a muddy mauve matching nothing on
   screen. A custom property holding another var() is a substitution, not a
   colour. app.js sets --c to a hex.
   ================================================================ */

.journal-field .field-label {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
}

/* Keeps the name and its "required" marker together as ONE flex item, so
   space-between pushes the date to the right and nothing lands mid-row. */
.journal-field .field-label-name { display: inline-flex; align-items: baseline; gap: 6px; }

/* Small and quiet. The date is what makes this a journal; it is not the point
   of the screen. */
.journal-date {
  font-size: 12px;
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  color: var(--muted);
}

.mood-field { margin: -6px 0 20px; }

.mood-q {
  font-size: 13px;
  color: var(--muted);
  margin-bottom: 9px;
}

.mood-optional {
  font-size: 11px;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  opacity: 0.75;
}

.mood-cards {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 7px;
}

.mood-card {
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.2;
  padding: 10px 4px;
  border-radius: 8px;
  border: 1.5px solid transparent;
  background: var(--white);
  color: var(--ink);
  cursor: pointer;
  text-align: center;
  transition: transform 140ms ease, box-shadow 140ms ease;
}

/* The dot carries the quadrant before the word is read, which is what makes
   the grid scannable rather than a wall of sixteen words. */
.mood-card::before {
  content: "";
  display: block;
  width: 10px;
  height: 10px;
  border-radius: 50%;
  margin: 0 auto 5px;
  background: var(--c);
}

.mood-card { border-color: color-mix(in srgb, var(--c) 30%, var(--white)); }
.mood-card:hover { transform: translateY(-1px); }

.mood-card[aria-pressed="true"] {
  background: var(--c);
  border-color: var(--c);
  color: var(--white);
  transform: translateY(-1px);
  box-shadow: 0 4px 12px color-mix(in srgb, var(--c) 45%, transparent);
}
.mood-card[aria-pressed="true"]::before { background: var(--white); }

/* Under about 380px four columns leaves each card too narrow for a word like
   "Recognised", which then wraps to three lines and breaks the grid's rhythm.
   Two columns keeps every label on one or two lines and still fits without
   scrolling. */
@media (max-width: 380px) {
  .mood-cards { grid-template-columns: repeat(2, 1fr); }
}

/* ===== The mood on an archive row =====
   The reference design titles past entries by the FEELING rather than the text,
   which is what makes a month scannable. This is the same idea at the scale of
   a list row: a coloured stripe and the word, both readable without opening
   anything. */
.arch-mood {
  display: inline-block;
  padding: 1px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 700;
  color: var(--white);
  background: var(--c, var(--muted));
}

/* ================================================================
   THE WORKING STATE — proof that something is still happening
   ================================================================

   The status line already carried the stage and a second counter, and both sat
   perfectly still between ticks. A thirty-second generation looked exactly like
   a hung page, and the reader had no way to tell which they were watching.

   INDETERMINATE ON PURPOSE. The server sends named stages, not percentages. A
   bar that fills to 40 and then 90 and then sits at 90 makes a promise about
   time that nothing here can keep, and being lied to by a progress bar is worse
   than having none.

   ERRORS ARE NEVER "WORKING", so the motion stops the instant a run fails.
   That contrast is the whole signal: moving means alive, still means finished
   or broken, and the text says which.
   ================================================================ */

.status.is-working {
  position: relative;
  overflow: hidden;
}

/* Sits on the box's own bottom edge rather than adding height, so the layout
   does not shift when a run starts or ends. */
.status.is-working::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: 0;
  height: 2px;
  width: 40%;
  background: linear-gradient(
    90deg,
    transparent,
    var(--gold) 35%,
    var(--gold-dark) 50%,
    var(--gold) 65%,
    transparent
  );
  animation: status-sweep 1.5s ease-in-out infinite;
}

@keyframes status-sweep {
  from { transform: translateX(-100%); }
  to   { transform: translateX(350%); }
}

/*
 * Reduced motion gets a STATIC bar, not nothing.
 *
 * Removing it entirely would take the working state back to the problem this
 * exists to solve — a still screen that cannot be told from a broken one. A
 * bar that is simply present says "running" without moving anything.
 */
@media (prefers-reduced-motion: reduce) {
  .status.is-working::after {
    animation: none;
    width: 100%;
    opacity: 0.5;
  }
}

/* ===== Ascent's FAQ menu bar =====

   Sits between the transcript and the text field, and stays there. Both ways
   of asking — tap one, or type your own — are visible at the same moment,
   which is the whole point of having it. */
.chat-menu {
  display: flex;
  gap: 6px;
  padding: 8px 10px;
  border-top: 1px solid rgba(20, 16, 8, 0.10);
  /* Scrolls sideways rather than stacking into a tall block that eats the
     transcript. Five questions will not fit across a 320px phone at a
     readable size, and a wrapped four-line menu is worse than a swipe. */
  overflow-x: auto;
  /* Firefox and the standard property; WebKit's bar is hidden below. */
  scrollbar-width: none;
}

.chat-menu::-webkit-scrollbar { display: none; }

/* Empty until the panel is first opened. Without this the bar's padding and
   border draw as a stray 17px strip under the transcript. */
.chat-menu:empty {
  display: none;
}

.chat-menu-btn {
  /* No shrinking and no wrapping: a chip squeezed to "How much does it…" is
     a chip nobody can read, and the row scrolls instead. */
  flex: 0 0 auto;
  white-space: nowrap;
  padding: 7px 12px;
  border: 1px solid rgba(169, 130, 47, 0.45);
  border-radius: 999px;
  background: rgba(169, 130, 47, 0.08);
  color: inherit;
  font: inherit;
  font-size: 12px;
  cursor: pointer;
  transition: background 0.15s ease, border-color 0.15s ease;
}

.chat-menu-btn:hover,
.chat-menu-btn:focus-visible {
  background: rgba(169, 130, 47, 0.2);
  border-color: var(--gold-dark, #a9822f);
}

.chat-menu-btn:focus-visible {
  outline: 2px solid var(--gold-dark, #a9822f);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .chat-menu-btn { transition: none; }
}

/* ===== Adding a mentor by hand =====
 *
 * Closed by default (a <details>), because open it is a seven-field form
 * sitting between the search box and its results — which makes the search look
 * like the secondary option when it is the primary one. Closed, it is a single
 * line telling the reader the door is there.
 *
 * Two fields per row on a desktop, one on a phone. The wrap is done with
 * flex-basis rather than a media query so it reflows at whatever width the
 * panel happens to be, including inside the narrow column the tab uses on a
 * tablet.
 */
.madd {
  margin: 18px 0 6px;
  padding: 0;
  border: 1px solid var(--line, #e4ded0);
  border-radius: 10px;
  background: rgba(255, 255, 255, 0.5);
}

.madd-summary {
  padding: 11px 14px;
  font-size: 13px;
  font-weight: 600;
  color: var(--gold-dark, #9a7b28);
  cursor: pointer;
  list-style: revert;
}

.madd-summary:focus-visible {
  outline: 2px solid var(--gold-dark, #9a7b28);
  outline-offset: -2px;
}

.madd[open] .madd-summary {
  border-bottom: 1px solid var(--line, #e4ded0);
}

.madd-lede {
  margin: 12px 14px 4px;
  font-size: 12.5px;
  line-height: 1.5;
  opacity: 0.75;
}

.madd-form {
  padding: 4px 14px 14px;
}

.madd-row {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.madd-field {
  display: flex;
  flex-direction: column;
  gap: 4px;
  /* 220px is the point below which a label and its input stop reading as a
     pair. Under that the row wraps to one field per line instead of shrinking
     both past usefulness. */
  flex: 1 1 220px;
  margin-bottom: 10px;
  font-size: 12px;
  font-weight: 600;
}

.madd-field-wide { flex-basis: 100%; }

/* "optional" must not compete with the label it qualifies. */
.madd-field em {
  font-style: normal;
  font-weight: 400;
  opacity: 0.55;
}

.madd-field input,
.madd-field textarea {
  padding: 8px 10px;
  font: inherit;
  font-weight: 400;
  font-size: 13px;
  color: inherit;
  background: #fff;
  border: 1px solid var(--line, #e4ded0);
  border-radius: 7px;
  resize: vertical;
}

.madd-field input:focus,
.madd-field textarea:focus {
  outline: 2px solid var(--gold-dark, #9a7b28);
  outline-offset: -1px;
}

.madd-actions {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* Says the quiet part where the price would normally be, so nobody has to
   press the button to find out whether it costs anything. */
.madd-free {
  font-size: 12px;
  font-weight: 600;
  opacity: 0.6;
}

.madd-status {
  margin: 10px 0 0;
  font-size: 12.5px;
  font-weight: 600;
}

/* ==========================================================================
 * MENTOR SEARCH EXAMPLES
 * ======================================================================== */

/*
 * Three one-click examples under the search box. They look like quiet links
 * rather than primary buttons: the action on this panel is "Find people", and
 * three competing buttons above it would make the real one harder to find.
 */
.msearch-examples {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 8px;
  margin: 8px 0 0;
}

.msearch-examples-label {
  font-size: 0.78rem;
  color: var(--muted);
}

.msearch-example {
  font: inherit;
  font-size: 0.78rem;
  padding: 4px 10px;
  border: 1px solid var(--line);
  border-radius: 999px;
  background: var(--white);
  color: var(--ink);
  cursor: pointer;
  text-align: left;
}

.msearch-example:hover { border-color: var(--gold, #c9a54a); }

.msearch-example:focus-visible {
  outline: 2px solid var(--gold-dark, #a9822f);
  outline-offset: 2px;
}

/* ==========================================================================
   TAB 6 — CHALLENGES

   Reuses .coach-card, .coach-locked and .coach-routing rather than restating
   them: the two locked tabs are the same kind of screen, and a second set of
   near-identical rules is two things to keep in step by hand.
   ========================================================================== */

/* The employer, above the question. Small and gold like every other label in
   the app, so it reads as attribution rather than as the headline - the question
   is still the thing being answered. */
.chal-company {
  margin: 0 0 6px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: #9a8032;
  /* A long trading name must wrap rather than widen the card. */
  overflow-wrap: anywhere;
}

/* Withheld at the employer's request. Quieter and not gold: it is an absence,
   and dressing it up like a name invites a second look for one that is not
   there. */
.chal-company.is-withheld {
  color: var(--muted);
  font-weight: 600;
  font-style: italic;
  text-transform: none;
  letter-spacing: 0;
}

/* The statement is the headline, so it is not the small gold uppercase label
   .coach-card h3 gives every other card. It is a sentence somebody wrote and it
   has to read as one. */
.chal-card h3 {
  font-size: 17px;
  line-height: 1.45;
  text-transform: none;
  letter-spacing: 0;
  color: var(--dark);
  margin: 0 0 14px;
}

.chal-label {
  margin: 14px 0 4px;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.7px;
  color: #9a8032;
}

.chal-body {
  margin: 0;
  font-size: 14px;
  line-height: 1.65;
  color: var(--dark);
  /* Employer text arrives with its own line breaks; collapsing them turns a
     three-point list into one run-on paragraph. */
  white-space: pre-wrap;
}

.chal-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-top: 18px;
}

/* A <label>, not a <button>, because it drives the hidden file input — which is
   the accessible way to do it: keyboard-reachable and announced correctly,
   which a div with a click handler is not. It therefore needs the display and
   cursor a button gets for free. */
.chal-choose {
  display: inline-block;
  font-family: var(--font);
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.6px;
  color: #7d6720;
  background: rgba(154, 128, 50, 0.1);
  border: 1px solid rgba(154, 128, 50, 0.35);
  border-radius: 5px;
  padding: 10px 16px;
  cursor: pointer;
}

.chal-choose:hover { background: rgba(154, 128, 50, 0.18); }

/* Visible focus, because the input it stands in for is hidden and a keyboard
   user would otherwise have no idea where they are. */
.chal-file:focus-visible + .chal-choose {
  outline: 2px solid #9a8032;
  outline-offset: 2px;
}

.chal-status {
  /* Long filenames must not push the buttons apart or off the row. */
  flex: 1 1 200px;
  min-width: 0;
  overflow-wrap: anywhere;
}

.chal-note { margin-top: 2px; }

/* Already answered. Quiet and final: there is nothing to do here. */
.chal-done {
  margin: 16px 0 0;
  padding: 11px 13px;
  background: rgba(0, 0, 0, 0.035);
  border-left: 3px solid rgba(0, 0, 0, 0.18);
  border-radius: 4px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--muted);
}

@media (max-width: 560px) {
  /* Stacked, and each control full width. Two buttons squeezed onto one line on
     a phone is how the wrong one gets tapped — and the wrong tap here sends a
     file to an employer. */
  .chal-actions { flex-direction: column; align-items: stretch; }
  .chal-choose, .chal-send { width: 100%; text-align: center; }
}
