/* The console's design system.
 *
 * Every value here was read off the computed styles of the WAP admin Peter built
 * (http://77.237.234.23:8080/admin), not eyeballed from a screenshot. That app is a shadcn token
 * set rather than the marketing brand, which is the right register for a board four advisors keep
 * open all day. D01 in docs/tasks records the mapping.
 *
 * Served as a file, not inlined, so the CSP can drop 'unsafe-inline'. There is no client-side JS
 * anywhere in this console and nothing in this sheet assumes any.
 */

@font-face {
  font-family: Inter;
  src: url('/assets/inter-400.woff2?v=8909904ab6c8') format('woff2');
  font-weight: 400;
  font-display: swap;
}
@font-face {
  font-family: Inter;
  src: url('/assets/inter-600.woff2?v=f9a06e79cd3a') format('woff2');
  font-weight: 600;
  font-display: swap;
}

:root {
  --background: #f6f6f8;
  --foreground: #020812;
  --card: #ffffff;
  --border: #d6e3f5;
  --hairline: #e2e8f0; /* the admin's section rules, a shade lighter than --border */
  --primary: #0d33d7;
  --primary-foreground: #ffffff;
  --secondary: #0e2039;
  --muted: #eaf1fa;
  --muted-foreground: #5c6f8a;
  --destructive: #dc2626;
  --destructive-soft: #fef2f2;
  --radius: 16px;
  --radius-pill: 14px;

  /* One hue per status, the admin's pill palette. Fill, ink, edge. */
  --pill-violet: #f5f3ff; --pill-violet-ink: #6d28d9; --pill-violet-edge: #ddd6fe;
  --pill-blue: #eff6ff;   --pill-blue-ink: #0d33d7;   --pill-blue-edge: #bfdbfe;
  --pill-cyan: #ecfeff;   --pill-cyan-ink: #0e7490;   --pill-cyan-edge: #a5f3fc;
  --pill-amber: #fffbeb;  --pill-amber-ink: #b45309;  --pill-amber-edge: #fde68a;
  --pill-grey: #f6f6f8;   --pill-grey-ink: #5c6f8a;   --pill-grey-edge: #e2e8f0;
  --pill-red: #fef2f2;    --pill-red-ink: #b91c1c;    --pill-red-edge: #fecaca;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--background);
  color: var(--foreground);
  font: 400 16px/24px Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* --- shell ------------------------------------------------------------- */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 57px;
  padding: 0 24px;
  background: var(--card);
  border-bottom: 1px solid var(--hairline);
}
.brand { display: flex; align-items: center; gap: 8px; font-weight: 600; letter-spacing: -0.3px; }
.brand .mark { color: var(--primary); font-size: 18px; }
.brand .pill { font-size: 10px; letter-spacing: .08em; padding: 1px 8px; }
.topbar .who { display: flex; align-items: center; gap: 16px; font-size: 13px; color: var(--muted-foreground); }
.topbar .who a { color: var(--muted-foreground); text-decoration: none; }
.topbar .who a:hover { color: var(--foreground); text-decoration: underline; }

.wrap { max-width: 1152px; margin: 0 auto; padding: 32px 16px; }
/* The lane board is a kanban and wants the screen; the list is a document and does not. */
.wrap.wide { max-width: 1440px; }

/* The title block takes what is left; the control row never shrinks, so the Search button
   does not drop to a line of its own the moment the count sentence gets long. */
.head { display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; margin-bottom: 20px; }
.head > div:first-child { flex: 1 1 auto; min-width: 0; }
.head .controls { flex: 0 0 auto; }
h1 { margin: 0; font-size: 20px; line-height: 28px; font-weight: 600; letter-spacing: -0.5px; }
.sub { margin: 2px 0 0; font-size: 14px; color: var(--muted-foreground); }
/* The inputs are width:100% by default, which in a flex row makes every control claim its
   own line. Named widths here, so the row stays a row. */
.controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.controls input[type=text] { width: 15rem; }
.controls select { width: auto; min-width: 11rem; }
.controls .btn { height: 36px; }

.back {
  display: inline-block;
  margin-bottom: 12px;
  font-size: 14px;
  color: var(--muted-foreground);
  text-decoration: none;
}
.back:hover { color: var(--foreground); }

/* --- surfaces ----------------------------------------------------------- */

.card {
  background: var(--card);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: 32px;
}
.card + .card { margin-top: 16px; }
.card > h2 {
  margin: 0 0 16px;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}

.empty { color: var(--muted-foreground); font-size: 14px; }
.muted { color: var(--muted-foreground); }

/* --- controls ----------------------------------------------------------- */

.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  height: 32px;
  padding: 0 12px;
  border: 1px solid var(--foreground);
  border-radius: var(--radius-pill);
  background: transparent;
  color: var(--foreground);
  font: 500 12px/1 Inter, system-ui, sans-serif;
  text-decoration: none;
  cursor: pointer;
  box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .05);
}
.btn:hover { background: var(--muted); }
.btn-primary {
  background: var(--primary);
  border-color: var(--primary);
  color: var(--primary-foreground);
  box-shadow: none;
}
.btn-primary:hover { background: #0a29ac; }

input[type=text], input[type=password], textarea, select {
  width: 100%;
  padding: 8px 12px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--card);
  color: var(--foreground);
  font: 400 14px/20px Inter, system-ui, sans-serif;
}
input:focus, textarea:focus, select:focus { outline: 2px solid var(--primary); outline-offset: -1px; }
/* Tall enough to write a paragraph in. The approve box holds the text that goes into the
   client letter, and a browser-default two-row box is how that gets written short. */
textarea { min-height: 7rem; resize: vertical; }
label { display: block; margin: 0 0 6px; font-size: 12px; font-weight: 600; color: var(--muted-foreground); }
.field + .field { margin-top: 16px; }

a { color: var(--primary); }
a:hover { text-decoration: underline; }

/* The raw intake payload, and anything else dumped verbatim. Wrapping is not decoration:
   without it a long single-line JSON string scrolls the whole page sideways. */
pre {
  margin: 0;
  padding: 12px;
  background: var(--muted);
  border-radius: 12px;
  font: 400 12px/1.5 ui-monospace, SFMono-Regular, Menlo, monospace;
  white-space: pre-wrap;
  word-break: break-word;
}
details { margin-top: 16px; }
details summary { cursor: pointer; font-size: 14px; color: var(--muted-foreground); }

/* --- tabs, as links: this console has no client-side JS ------------------ */

.tabs {
  display: inline-flex;
  gap: 2px;
  padding: 4px;
  margin-bottom: 20px;
  background: var(--muted);
  border-radius: var(--radius);
}
.tab {
  padding: 4px 12px;
  border-radius: var(--radius-pill);
  font-size: 14px;
  font-weight: 500;
  color: var(--muted-foreground);
  text-decoration: none;
}
.tab-count {
  display: inline-block;
  min-width: 16px;
  padding: 0 5px;
  margin-left: 4px;
  border-radius: 999px;
  background: var(--primary);
  color: var(--primary-foreground);
  font-size: 11px;
  font-weight: 600;
  text-align: center;
}
.tab[aria-current] {
  background: var(--background);
  color: var(--foreground);
  box-shadow: 0 1px 3px 0 rgba(0, 0, 0, .1), 0 1px 2px -1px rgba(0, 0, 0, .1);
}

/* --- pills -------------------------------------------------------------- */

.pill {
  display: inline-block;
  padding: 2px 10px;
  border: 1px solid var(--pill-grey-edge);
  border-radius: var(--radius-pill);
  background: var(--pill-grey);
  color: var(--pill-grey-ink);
  font-size: 12px;
  font-weight: 600;
  white-space: nowrap;
}
.pill-blue   { background: var(--pill-blue);   color: var(--pill-blue-ink);   border-color: var(--pill-blue-edge); }
.pill-violet { background: var(--pill-violet); color: var(--pill-violet-ink); border-color: var(--pill-violet-edge); }
.pill-cyan   { background: var(--pill-cyan);   color: var(--pill-cyan-ink);   border-color: var(--pill-cyan-edge); }
.pill-amber  { background: var(--pill-amber);  color: var(--pill-amber-ink);  border-color: var(--pill-amber-edge); }
.pill-red    { background: var(--pill-red);    color: var(--pill-red-ink);    border-color: var(--pill-red-edge); }

/* --- tables ------------------------------------------------------------- */

.table { width: 100%; border-collapse: collapse; font-size: 14px; }
.table th {
  padding: 0 8px 8px;
  font-weight: 500;
  color: var(--muted-foreground);
  text-align: left;
  border-bottom: 1px solid var(--hairline);
}
.table td { padding: 12px 8px; border-bottom: 1px solid var(--hairline); vertical-align: top; }
/* A column heading is a link, so it has to look like the heading and not like a link — the
   underline goes, the muted ink stays, and the hover is the only thing that says it is one. */
.sortlink { color: inherit; text-decoration: none; display: inline-block; }
.sortlink:hover { color: var(--primary); }
.table th[aria-sort=ascending] .sortlink, .table th[aria-sort=descending] .sortlink {
  color: var(--foreground); font-weight: 600;
}
/* The one cell allowed to wrap and to grow. A failed row is taller than the others because a
   failed row has more to say — the reason a run stopped is the whole point of that row. */
.table .status-cell { white-space: normal; max-width: 26rem; }
.table .status-cell .err { margin-top: 6px; }
.table tr:last-child td { border-bottom: 0; }
.table .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.table td .acts { margin-top: 0; flex-wrap: nowrap; }
.table td { white-space: nowrap; }
.table .empty { text-align: center; padding: 24px; }
.scroll { overflow-x: auto; }
/* The profile panel is key/value, not a grid: its first cell is the label, so it gets the
   header styling a `thead` would otherwise carry and a width that keeps values aligned. */
.table.kv th { width: 200px; padding: 12px 8px; font-weight: 500; border-bottom: 1px solid var(--hairline); }
.table.kv tr:last-child th, .table.kv tr:last-child td { border-bottom: 0; }

/* --- the board ---------------------------------------------------------- */

/* Five columns, always, scrolling sideways rather than reflowing: a lane that moves to a
   second row is a lane an advisor stops seeing, and this view exists to be glanced at. */
/* `align-items: stretch` and a min-height, so the five lanes are five wells of the same
   depth rather than five cards of different heights floating above the fold. A lane with one
   case then reads as a lane with capacity, which is what it is — the emptiness is honest, it
   just has to look deliberate rather than look like a card that failed to load. */
.lanes {
  display: grid; grid-template-columns: repeat(5, minmax(232px, 1fr));
  gap: 12px; align-items: stretch; overflow-x: auto; padding-bottom: 8px;
  /* Measured, not guessed: header 58 + wrap padding 32 + head 100 + tabs 60 + wrap
     padding-bottom 32 = 282. At 260 the empty board grew a scrollbar with nothing to
     scroll. A banner pushes past it, which is correct — a banner is content. */
  min-height: calc(100vh - 282px);
}
/* The cards inside sit on --card, so they read as objects on a surface rather than as panels
   inside a panel, and the flex column top-aligns them: the gap belongs at the bottom of the
   lane, where it means "room for more", not spread between the cases. */
.lanes > .card { display: flex; flex-direction: column; background: var(--muted); }
/* `.card + .card` gives stacked cards their 16px gap. In the lane grid the gap is the grid's
   job, and that margin pushed four of the five columns 16px below the first. */
.lanes > .card { padding: 16px; margin-top: 0; }
.lane-head { display: flex; align-items: baseline; justify-content: space-between; gap: 8px; margin: 0 0 12px; }
.lane-head h2 {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
.lane-count {
  font-size: 11px;
  font-weight: 600;
  color: var(--muted-foreground);
  background: var(--muted);
  border-radius: 999px;
  padding: 1px 7px;
}

.cases { display: grid; gap: 8px; }
.case {
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--card);
}
.case.stuck { border-left: 3px solid var(--destructive); }
.case-top { display: flex; align-items: center; justify-content: space-between; gap: 8px; flex-wrap: wrap; }
/* Never broken across lines. A wrapped reference is unreadable and unclickable-looking, and
   it is the one string on the card an advisor matches against something else. */
.ref { font-weight: 600; font-size: 14px; white-space: nowrap; }
.ref a { color: var(--foreground); text-decoration: none; }
.ref a:hover { color: var(--primary); text-decoration: underline; }
.client { font-size: 14px; color: var(--muted-foreground); }
.meta { margin-top: 6px; font-size: 12px; color: var(--muted-foreground); }
.flagline { margin-top: 8px; font-size: 12px; }
.blocking { color: var(--destructive); font-weight: 600; }
/* The documents a round produced. A row each, because the decision is per document. */
.docs { list-style: none; margin: 16px 0 0; padding: 0; }
.doc {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 14px 0; border-bottom: 1px solid var(--hairline);
}
.doc:last-child { border-bottom: 0; }
.doc-name { display: block; font-weight: 500; }
.doc .sub { margin: 2px 0 0; }
/* The letter, in its own panel above the rest: the document this tab exists for, so its name
   is a size up and its Open button is the solid one. Nothing else on the page changes. */
.doc-primary { padding-top: 4px; }
.doc-primary .doc-name { font-size: 16px; }

/* The client-facing wording that goes into the letter verbatim. Read as prose, so it keeps a
   measure — a paragraph across 1152px is one nobody reads to the end of. */
.takeaways h3 { margin: 20px 0 8px; font-size: 13px; font-weight: 600; color: var(--muted-foreground); }
.takeaway { margin-top: 12px; max-width: 78ch; }
.takeaway p { margin: 0; font-size: 14px; line-height: 21px; white-space: pre-wrap; }
.takeaway .sub { margin: 0 0 4px; font-size: 12px; }
/* An earlier round's wording is not the wording in the letter. Muted and indented, so it
   cannot be mistaken for the current paragraph by someone skimming. */
.takeaway-historical { color: var(--muted-foreground); border-left: 2px solid var(--hairline); padding-left: 12px; }

.acts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 10px; }
.acts .btn { height: 28px; padding: 0 10px; }
.err { margin-top: 8px; font-size: 12px; color: var(--destructive); word-break: break-word; }

/* --- the case page's panel grid ------------------------------------------ */
/* Three across for the panels that hold four lines, full width for the tables that hold
   seven columns. Stacking everything full-width left two thirds of the screen empty; giving
   everything a third left the holdings table behind a scrollbar. */
.panels { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; align-items: start; }
.panels > .card { margin-top: 0; padding: 20px; }
.panels > .card.wide { grid-column: 1 / -1; }
.panels > .card.twothirds { grid-column: span 2; }
.table .when { white-space: nowrap; font-variant-numeric: tabular-nums; }
@media (max-width: 1100px) { .panels { grid-template-columns: repeat(2, 1fr); } }
@media (max-width: 760px) {
  .panels { grid-template-columns: 1fr; }
  .panels > .card.twothirds { grid-column: 1 / -1; }
}

/* --- the projection charts (case-charts.js owns the markup) --------------- */
/* One column, deck order, full width. A two-column auto-grid put seven unrelated figures
   into a masonry whose reading order nobody could predict, and left ragged gaps where the
   cards happened to differ in height. The advisor reads this page as a sequence: what the
   client holds, what the model wants, what it costs, and how the programme gets there. */
.charts { display: grid; grid-template-columns: 1fr; gap: 16px; }
.chart-block {
  background: var(--card);
  border: 1px solid var(--hairline);
  border-radius: var(--radius);
  padding: 24px 28px;
}
.chart-block h3 {
  margin: 0;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--muted-foreground);
}
/* What the figure is measured against -- market value or cost basis, with or without cash.
   Three of these figures are shares and three are amounts on a different denominator, and
   an advisor approving them has to know which. */
.tabs + .basis { margin-top: -8px; }
.basis { margin: 6px 0 0; max-width: 78ch; font-size: 13px; line-height: 20px; color: var(--muted-foreground); }
.chart { margin: 16px 0 0; }
.chart svg { max-width: 100%; height: auto; display: block; }
.chart-empty { padding: 8px 0; }

/* The exact-data table, folded away. It carries the same figures the bars do, in full
   precision, and it is what a screen reader reads -- so it stays in the document and only
   its visibility is folded. <details> needs no JavaScript, which is the whole point. */
details.chart-data { margin-top: 12px; }
details.chart-data > summary {
  display: inline-block;
  padding: 4px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-pill);
  background: var(--card);
  font-size: 12px;
  color: var(--muted-foreground);
}
details.chart-data > summary:hover { background: var(--muted); color: var(--foreground); }
details.chart-data[open] > summary { margin-bottom: 12px; }

.chart-summary { width: 100%; font-size: 14px; max-width: 640px; }
/* case-charts.js emits the older names; they mean the same thing as .table and .scroll. */
.tablewrap { overflow-x: auto; }
table.data { width: 100%; border-collapse: collapse; font-size: 14px; }
table.data th { padding: 0 8px 8px; font-weight: 500; color: var(--muted-foreground); text-align: left; border-bottom: 1px solid var(--hairline); }
table.data td { padding: 10px 8px; border-bottom: 1px solid var(--hairline); }
table.data tr:last-child td { border-bottom: 0; }
table.data td.num, table.data th.num { text-align: right; font-variant-numeric: tabular-nums; }
/* The totals figure is the one number on this page an advisor reads against the PDF. */
table.chart-summary th { border-bottom: 1px solid var(--hairline); font-weight: 400; }
table.chart-summary tr:last-child th, table.chart-summary tr:last-child td { border-bottom: 0; }
/* Why a figure is missing, or why it does not mean what it looks like. Never a blank box.
   The three levels are defined on `.notice` itself, not under `.charts`: render.js draws one
   of them ABOVE the chart block, and a level that only paints inside one container is a level
   that silently renders as plain text the first time it is used anywhere else. */
.signin .notice, .wrap.narrow .notice { margin-bottom: 16px; }
.notice.alert { background: var(--destructive-soft); color: var(--destructive); border: 1px solid var(--pill-red-edge); }
.notice.attention { background: var(--pill-amber); color: var(--pill-amber-ink); border: 1px solid var(--pill-amber-edge); }
.notice.info, .notice.note { background: var(--muted); color: var(--muted-foreground); border: 1px solid var(--border); }
.charts .notice { margin: 0; }
/* A flag rendered above the charts because it changes what one of them means. Sits outside
   .charts, so it needs the block spacing the grid would otherwise have given it. */
.notice.case-flag { display: block; margin: 0 0 16px; padding: 14px 16px; line-height: 20px; }
.notice.case-flag strong { display: block; margin-bottom: 4px; }
/* The answer, when somebody has given one. Same ink as the notice — muted grey on amber is
   unreadable, and this line is not less important than the sentence above it. */
.notice.case-flag .answered { display: block; margin-top: 6px; font-style: italic; }
.sr-only { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; }

/* --- flag cards --------------------------------------------------------- */

.flag { padding: 16px; border: 1px solid var(--hairline); border-radius: 12px; }
.flag + .flag { margin-top: 12px; }
.flag-blocking { border-left: 3px solid var(--destructive); background: var(--destructive-soft); }
.flag-resolved { background: var(--background); color: var(--muted-foreground); }
.flag h3 { margin: 0 0 4px; font-size: 14px; font-weight: 600; }
.flag p { margin: 0; font-size: 14px; }

/* --- the narrow pages: login, password, the three action forms ----------- */

/* The pages reached without a session, or in place of one: login, the password change, sign
   out, and the answer to an action. One 400px column, centred, the wordmark above the card
   and no header — there is no board to go back to from a login page, and a Sign out link on
   the sign-out page is a loop. */
.centred { display: flex; justify-content: center; padding: 64px 16px; }
.signin { width: 100%; max-width: 400px; }
.signin > .brand { margin: 0 0 16px; }
.signin > .card { margin-top: 0; padding: 24px; }
.signin h1 { margin-bottom: 2px; }
.signin .sub { margin-bottom: 20px; }
.signin .pill { margin-bottom: 10px; }
.centred .card { width: 100%; max-width: 400px; padding: 24px; }

/* A form is 640px. The board is 1440 and a document is 1152; a textarea stretched across a
   document column is one nobody can read their own sentence back in. */
.wrap.narrow { max-width: 640px; }
.wrap.narrow h1 + .sub { margin-bottom: 20px; }
.wrap.narrow .card { padding: 24px; }

/* The helper line under a label, and the sentence under a form. Smaller than the body and
   muted, so it reads as instruction rather than as content. */
.note { margin: 0 0 8px; font-size: 13px; line-height: 19px; color: var(--muted-foreground); }
.field .note { margin: -2px 0 8px; }
.card + .note, form + .note { margin-top: 16px; }

/* Submit and cancel on one row. Cancel is a link styled as a button, not a second button:
   it is not an action, it is leaving. */
.formfoot { display: flex; align-items: center; gap: 8px; margin: 20px 0 0; }
.btn.block { flex: 1 1 auto; justify-content: center; }

/* A code word inside a sentence — `answer_flag`, the tool the advisor has to go and use. */
code {
  font: 400 13px/19px ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--muted); border: 1px solid var(--border);
  border-radius: 6px; padding: 1px 5px;
}

/* Any button that did not ask for a class still looks like the primary one rather than like
   1995. There should not be one — every submit in this console names `.btn` — but a form
   added later must not be able to ship an unstyled control. */
button { font: inherit; }
.notice { padding: 12px; border-radius: 12px; font-size: 14px; margin-bottom: 16px; }
/* There is one notice vocabulary and it is `.notice.alert` / `.attention` / `.info`, above.
   D01 shipped a second -- `.notice-error` and `.notice-ok` -- and the two overlapped: the
   -error rule's three declarations were character-for-character `.notice.alert`'s. `-ok` never
   had an emitter at all, from D01 to the sixth review pass. Two vocabularies for one thing is
   the failure this project names as its worst, so the pair is gone and the board's two banners
   say `notice attention`: neither is something an advisor got wrong, which is what red means
   everywhere else here. */

@media (max-width: 640px) {
  .wrap { padding: 20px 12px; }
  .card { padding: 20px; }
  .head { flex-direction: column; }
}
