/* Console-style design refresh.
   ---------------------------------------------------------------------------
   Loaded after styles.css / dashboard-responsive.css / the mobile timeline
   sheets and BEFORE dark-mode.css on purpose: this file only ever sets shape,
   spacing, weight and *token* values, so dark-mode.css (which is both later and
   more specific, `:root[data-theme="dark"]:not(...)`) still owns every dark
   color. Anything here that needs a different value in the dark theme is a
   custom property declared in :root below and re-declared in dark-mode.css.

   The design it targets is the modern developer-console look (the OpenAI
   platform console being the direct reference): a soft grey page canvas with
   the content floating on it as one white rounded card, a text-first menu with
   phone-sized touch rows, the signed-in user pinned to the bottom of that menu
   instead of the header, regular-weight type at a readable size, pill buttons,
   and big quiet numbers instead of bordered stat boxes.

   Nothing in here changes behaviour; the two behavioural pieces of the same
   redesign are the menu search (dashboard-navigation.js) and the account block
   moving into the rail footer (dashboard.html). */

:root {
  /* The page canvas the whole app sits on, and the card that floats on it.
     styles.css's --bg stays white and is still what most components paint
     themselves with - these two are specifically the "two layers" of the
     redesign, so a component can opt into either one. */
  --canvas: #f2f1ef;
  --surface: #ffffff;
  --surface-soft: #f7f7f5;

  /* Neutral fill for pill buttons, menu hover, and the active menu row. */
  --pill: #ececea;
  --pill-hover: #e2e2df;
  --nav-active: #e6e6e2;

  /* The one tinted-alert family (the reference's "Auto recharge is off"
     card): soft background, dark readable text, one solid accent for its
     call to action. */
  --notice-bg: #fdf0e7;
  --notice-line: #f2d9c6;
  --notice-ink: #8a3f14;
  --notice-accent: #c9622a;

  --radius-surface: 20px;
  --radius-row: 12px;
}

/* ---------------------------------------------------------------------------
   1. Page distribution: grey canvas, one white content card
   ------------------------------------------------------------------------ */

.dashboard-app-shell,
.dashboard-shell.executive-dashboard-shell,
.executive-workspace {
  background: var(--canvas);
}

/* The header now sits *on* the canvas rather than being a white bar with a
   rule under it - the white starts at the content card below, which is what
   makes the card read as a distinct page surface. */
.app-top-bar {
  background: var(--canvas);
  border-bottom: 0;
  gap: 10px;
  min-height: 56px;
  padding: 10px 14px;
}

/* .executive-workspace is a single-row grid (styles.css), so padding here
   shrinks the row rather than overflowing it - the card child still fits
   exactly, no height math needed. */
.executive-workspace {
  padding: 4px 12px 12px;
}

.project-dashboard {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-surface);
  padding: 24px 26px 34px;
}

/* Sections inside the card are separated by air and hairlines, not by nested
   card chrome - a card inside a card is the thing this redesign removes. */
.project-dashboard .plan-section {
  background: none;
  border: 0;
  border-top: 1px solid var(--line);
  border-radius: 0;
  margin-top: 22px;
  padding: 22px 0 0;
}

/* The Reports page holds two things somebody has to choose between, so the gap
   between them is wider than the gap between a section's own heading and its
   content. Here rather than in styles.css: this file loads last, so an equally
   specific rule over there could never win the tie.  */
.project-dashboard .reports-section {
  margin-top: 34px;
  padding-top: 28px;
}

/* Reports: a list, where there used to be two tinted regions.
   ---------------------------------------------------------------------------
   Program Review and Executive Reports answer different questions - one is
   always current, the other is a frozen snapshot - and the reader has to know
   which they are looking at. That used to be said with colour: a teal rail and
   gradient for live, a violet one for frozen, both on the same screen.

   IT READ AS TWO SCREENS RATHER THAN TWO CHOICES. Reported as confusing, and
   the reason is that a tinted region says "this is a different place", not
   "this is one of two things you can pick". The distinction is now a row in a
   table with the word Live or Frozen in it, which needs no learning and
   survives a third report being added - there are only so many tints before a
   page is a paintbox.

   The badges keep their two colours (styles.css): on a row, beside a word, a
   colour is reinforcement rather than the whole message. */
.project-dashboard .reports-index-section {
  padding: 4px 0 0;
}

.reports-index {
  border-collapse: collapse;
  width: 100%;
}

.reports-index th,
.reports-index td {
  border-bottom: 1px solid var(--line);
  padding: 16px 12px;
  text-align: left;
  vertical-align: top;
}

.reports-index thead th {
  border-bottom-width: 1px;
  color: var(--muted, #64748b);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  padding-bottom: 8px;
  padding-top: 0;
}

.reports-index tbody tr:last-child th,
.reports-index tbody tr:last-child td {
  border-bottom: 0;
}

/* The whole row opens the report, so the whole row has to look like it does. */
.reports-index-row {
  cursor: pointer;
}

.reports-index-row:hover,
.reports-index-row:focus-visible {
  background: var(--pill, #f2f2f0);
  outline: none;
}

.reports-index-row:focus-visible {
  box-shadow: inset 0 0 0 2px var(--accent-brand);
}

.reports-index-name {
  display: block;
  font-size: 1rem;
  font-weight: 600;
}

/* Capped so the second column starts at a predictable place on a wide screen -
   a summary running the full width pushes Kind and Reports out of the reader's
   first glance, which is where the comparison happens. */
.reports-index-summary {
  display: block;
  font-weight: 400;
  margin-top: 4px;
  max-width: 62ch;
}

.reports-index-last {
  display: block;
}

.reports-index-open {
  color: var(--muted, #64748b);
  font-size: 1.3rem;
  text-align: right;
  width: 1%;
}

.reports-back {
  margin: 0 0 4px;
}

/* Acting on a reading, under the reading it acts on.
   ---------------------------------------------------------------------------
   Inside the expanded entry rather than on the collapsed header, so the offer
   only appears once somebody has actually read the thing they would be
   applying. The cost line sits with the button rather than in a tooltip: what
   it spends and that nothing is written yet are both things to know BEFORE
   pressing, and a tooltip is read after. */
.ai-reading-actions {
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin-top: 10px;
  padding-top: 10px;
}

.ai-reading-actions button {
  align-self: flex-start;
}

/* ---------------------------------------------------------------------------
   theruKO as a chat.

   Oldest at the top, the box pinned to the bottom, your messages on the right
   and theruKO's on the left - the layout every chat has already taught. The
   box is sticky rather than outside the scroller so the drawer's structure,
   which the Recommendations tab shares, is unchanged.
   ------------------------------------------------------------------------ */
.ai-chat {
  display: flex;
  flex-direction: column;
  gap: 12px;
  /* Pushes the box to the bottom of an empty or short conversation, where a
     chat's box always is, instead of floating under the first message. */
  min-height: 100%;
}

.ai-chat-log {
  display: flex;
  flex: 1 0 auto;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}

/* Its children join .ai-chat's flow, so the form can be sticky against the
   WHOLE conversation rather than only against its own wrapper. */
.ai-chat-compose {
  display: contents;
}

.ai-chat-empty {
  margin: auto 0 0;
  text-align: center;
}

/* Your side. */
.ai-chat .ai-chat-mine {
  align-items: flex-end;
  align-self: flex-end;
  border: 0;
  display: flex;
  flex-direction: column;
  max-width: 85%;
  padding: 0;
}

.ai-chat-mine .ai-chat-said {
  margin-left: 0;
}

/* "You asked" said what side a bubble was on while every entry sat in one
   column. The side says it now. */
.ai-chat-mine .ai-ask-said-who {
  display: none;
}

.ai-chat-time {
  color: var(--muted, #64748b);
  font-size: 0.7rem;
  margin: 3px 4px 0;
}

/* theruKO's side: one line that opens. */
.ai-chat .ai-chat-theirs {
  background: color-mix(in srgb, var(--ink) 4%, transparent);
  border: 0;
  border-radius: 12px 12px 12px 4px;
  margin-right: 8%;
  padding: 6px 12px;
}

.ai-chat .ai-chat-theirs.is-open {
  padding-bottom: 10px;
}

.ai-chat .ai-chat-theirs .ai-reading-open {
  padding-left: 0;
}

/* How much is behind the line. A pill, so it reads as a measure beside the
   sentence rather than as the end of it. */
.ai-reading-size {
  background: var(--pill, color-mix(in srgb, var(--ink) 8%, transparent));
  border-radius: 999px;
  color: var(--muted, #64748b);
  flex: none;
  font-size: 0.7rem;
  padding: 1px 7px;
}

/* theruKO reading, where its answer will land. */
.ai-chat-typing {
  align-items: center;
  align-self: flex-start;
  background: color-mix(in srgb, var(--ink) 4%, transparent);
  border-radius: 12px 12px 12px 4px;
  color: var(--muted, #64748b);
  display: inline-flex;
  font-size: 0.82rem;
  gap: 8px;
  padding: 8px 12px;
}

.ai-chat-typing-dots {
  display: inline-flex;
  gap: 3px;
}

.ai-chat-typing-dots i {
  animation: ai-chat-dot 1.2s infinite ease-in-out;
  background: currentColor;
  border-radius: 50%;
  display: block;
  height: 5px;
  opacity: 0.35;
  width: 5px;
}

.ai-chat-typing-dots i:nth-child(2) {
  animation-delay: 0.15s;
}

.ai-chat-typing-dots i:nth-child(3) {
  animation-delay: 0.3s;
}

@keyframes ai-chat-dot {
  0%, 80%, 100% { opacity: 0.35; transform: translateY(0); }
  40% { opacity: 1; transform: translateY(-2px); }
}

@media (prefers-reduced-motion: reduce) {
  .ai-chat-typing-dots i {
    animation: none;
    opacity: 0.6;
  }
}

/* The box, pinned to the bottom of the conversation. Bleeds to the drawer's
   edges (its padding is 18px) so what scrolls under it is cut cleanly. */
.ai-ask {
  background: var(--panel, #fff);
  border-top: 1px solid var(--line);
  bottom: -18px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  margin: 0 -18px -18px;
  padding: 10px 18px 14px;
  position: sticky;
  z-index: 1;
}

.ai-ask-box {
  position: relative;
}

.ai-ask-input {
  border: 1px solid var(--line);
  border-radius: 20px;
  display: block;
  font: inherit;
  line-height: 1.4;
  max-height: 160px;
  min-height: 44px;
  /* Room for the chip and the disc inside the box, so text never runs under them. */
  padding: 11px 76px 11px 14px;
  resize: none;
  width: 100%;
}

/* The arrow, and the square.
   The button is the pair - the AI chip and the disc - so the chip is inside
   the control and in its accessible name ("Send AI"), as ai-badge.js requires
   of every control that spends a credit, without being squeezed onto the
   corner of a 32px circle where it read as a rendering fault. */
.ai-ask-send {
  align-items: center;
  background: none;
  border: 0;
  bottom: 6px;
  box-shadow: none;
  color: var(--panel, #fff);
  cursor: pointer;
  display: flex;
  /* The chip sits LEFT of the disc while coming after "Send" in the markup,
     so a screen reader hears the action first and the marking second. */
  flex-direction: row-reverse;
  gap: 6px;
  min-height: 0;
  padding: 0;
  position: absolute;
  right: 6px;
}

.ai-ask-send-disc {
  align-items: center;
  background: var(--accent-brand);
  border-radius: 50%;
  display: flex;
  height: 32px;
  justify-content: center;
  width: 32px;
}

.ai-ask-send:hover .ai-ask-send-disc {
  background: color-mix(in srgb, var(--accent-brand) 85%, var(--ink));
}

.ai-ask-send:focus-visible {
  outline: none;
}

.ai-ask-send:focus-visible .ai-ask-send-disc {
  outline: 2px solid var(--accent-brand);
  outline-offset: 2px;
}

/* Quiet until there is something to send - it still works, and pressing it on
   an empty box does nothing, but it no longer looks like it is waiting on you. */
.ai-ask-box:has(.ai-ask-input:placeholder-shown) .ai-ask-send:not(.is-stop) {
  opacity: 0.45;
}

.ai-ask-send.is-stop .ai-ask-send-disc {
  background: var(--ink);
}

.ai-ask-send .ai-badge {
  color: var(--accent-brand);
}

/* A placeholder is a hint, not text. Left to the browser it inherited the
   field's colour in dark mode and read as something already typed - which
   matters now that it names the plan the box changes. */
.ai-ask-input::placeholder {
  color: var(--muted, #64748b);
  opacity: 0.7;
}

.ai-ask-input:focus-visible {
  border-color: var(--accent-brand);
  outline: none;
}

/* The price sits with the send button rather than under the box: what it
   spends and that nothing is written yet are both things to know BEFORE
   pressing. Wraps on a narrow column so the button never leaves the screen. */
.ai-ask-actions {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: space-between;
}

.ai-ask-actions .admin-meta {
  flex: 1 1 12rem;
  min-width: 0;
}

/* Your half of the exchange, drawn as your half.
   ---------------------------------------------------------------------------
   The column holds both voices now, so what you typed gets a bubble on your
   side and the answer below reads as a reply rather than as the next line of a
   log. Tinted rather than outlined: a border on every entry turns a column of
   short exchanges into a ladder. */
.ai-ask-said {
  border-left: 2px solid var(--line);
  padding-left: 10px;
}

.ai-chat-said {
  /* --accent-brand, not an invented --brand with a light-mode literal behind
     it. A token that does not exist falls back to its hardcoded default in BOTH
     themes, which is how this bubble became a dark green on a near-black panel
     and read as missing. 12% rather than 7%: the same wash that is legible over
     white disappears over #0d0f13. */
  background: color-mix(in srgb, var(--accent-brand) 12%, transparent);
  border: 0;
  border-radius: 12px 12px 4px 12px;
  margin-left: 22px;
  padding: 8px 12px;
}

.ai-ask-said-who {
  color: var(--muted, #64748b);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  margin: 0 0 2px;
  text-transform: uppercase;
}

/* Folded to a few lines until it is pressed. A pasted transcript is the long
   case rather than the rare one, and a thousand words of your own text above
   the answer is the record burying the thing it records.

   -webkit-line-clamp is the only cross-browser way to clamp by LINES rather
   than by pixels, which is what matters here: the column's width is now the
   reader's, so a fixed max-height would clamp to a different amount of text at
   every width. */
.ai-said-text {
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 3;
  display: -webkit-box;
  overflow: hidden;
}

.ai-said-text.is-expanded {
  -webkit-line-clamp: unset;
  display: block;
}

/* Only on a block that is genuinely cut off - the class is added after
   measuring, so a short ask carries no affordance and no pointer. */
.ai-said-text.is-clampable {
  cursor: pointer;
}

/* On the BUBBLE, not on the clamped block: ::after inside a -webkit-box is
   laid out as part of the box and gets clamped away with the text it is
   meant to reveal. */
.ai-chat-said.has-more::after {
  color: var(--accent-brand);
  content: "Show more";
  display: block;
  font-size: 0.72rem;
  font-weight: 600;
  margin-top: 2px;
}

.ai-chat-said.has-more.is-open::after {
  content: "Show less";
}

.ai-said-text.is-clampable:focus-visible {
  border-radius: 6px;
  outline: 2px solid var(--accent-brand);
  outline-offset: 2px;
}

.ai-said-text p {
  margin: 0;
}

/* The unaccounted-for days, on the Change log.
   ---------------------------------------------------------------------------
   It sits between the waterfall and the record because that is the order of the
   questions: how much went, what is still unaccounted for, then the entry by
   entry detail.

   NO COLOURED RAIL, deliberately. The first version had an amber one, on the
   reasoning that this is the band to act on. Rendered, it did not show at all -
   .plan-section's own border won - and chasing that was the moment to notice
   the rail should not come back even if it worked: the tinted rails on Reports
   were removed this same day for making one page read as several. The eyebrow
   and the heading say what this band is, in words, which is what the Reports
   change concluded as well. */
.slip-gap-header h3 {
  margin: 2px 0 6px;
}

.slip-gap-table {
  border-collapse: collapse;
  margin-top: 14px;
  width: 100%;
}

.slip-gap-table th,
.slip-gap-table td {
  border-bottom: 1px solid var(--line);
  padding: 12px 10px;
  text-align: left;
  vertical-align: top;
}

.slip-gap-table thead th {
  color: var(--muted, #64748b);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  padding: 0 10px 8px;
}

.slip-gap-table tbody tr:last-child th,
.slip-gap-table tbody tr:last-child td {
  border-bottom: 0;
}

.slip-gap-milestone {
  display: block;
  font-weight: 600;
}

.slip-gap-project {
  display: block;
  font-weight: 400;
}

/* Tabular figures so the day counts line up as a column somebody can scan down
   - this list is read by size, which is also the order it is sorted in. */
.slip-gap-days {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.slip-gap-when {
  font-variant-numeric: tabular-nums;
}

.slip-gap-when .admin-meta {
  display: block;
}

.slip-gap-action {
  text-align: right;
  white-space: nowrap;
  width: 1%;
}

.slip-gap-more {
  margin-top: 12px;
}

/* The unconfirmed band, quieter than the one above it.
   ---------------------------------------------------------------------------
   These days ARE accounted for - just not by anybody. Giving them the same
   weight as the days with no account at all would bury the band that matters
   more, so the heading is a step smaller and the explanation carries it. */
.slip-likely-section .slip-gap-header h3 {
  font-size: 1.05rem;
}

.slip-likely-cause {
  color: var(--muted, #64748b);
  max-width: 44ch;
}

.slip-gap-action .link-button {
  display: block;
  margin-top: 6px;
}

@media (max-width: 720px) {
  .slip-likely-cause { display: none; }
}

@media (max-width: 720px) {
  /* The move and the timestamp are context; the milestone, the days and the
     action are the row. */
  .slip-gap-when { display: none; }
  .slip-gap-table th,
  .slip-gap-table td { padding: 10px 6px; }
}

@media (max-width: 720px) {
  /* The summary is the first thing to go: on a phone the name and the kind are
     what the list is for, and a paragraph per row turns two choices into a
     scroll. */
  .reports-index-summary { display: none; }
  .reports-index th,
  .reports-index td { padding: 12px 8px; }
}

.project-dashboard .plan-section:first-child {
  border-top: 0;
  margin-top: 0;
  padding-top: 0;
}

/* Milestones is the first .plan-section inside .project-focus-main, so the
   :first-child reset above zeroed its top margin - and it follows the tinted
   Attention Needed card, whose own padding ends where that margin would have
   started. The result was a heading and an "Add Milestone" button sitting flush
   against the card above them, reading as part of it. The group gets its own
   air instead. */
.project-dashboard .project-workspace-focus {
  margin-top: 26px;
}

.executive-project-hero {
  background: none;
  border-bottom: 0;
  margin-bottom: 4px;
  padding: 0 0 10px;
}

/* ---------------------------------------------------------------------------
   2. Typography: regular weights, larger sizes
   ------------------------------------------------------------------------ */

/* The app was set almost entirely in 700-900 at 0.66-0.88rem, which is what
   made dense screens read as shouting. Body copy goes back to regular, only
   real emphasis (strong/dt/headings) carries weight, and the sizes come up to
   where they are comfortable on a phone. Deliberately element-level selectors
   scoped to the shell so they lose to any component that has a considered
   weight of its own (two-class selectors elsewhere still win). */
.dashboard-app-shell p,
.dashboard-app-shell span,
.dashboard-app-shell small,
.dashboard-app-shell li,
.dashboard-app-shell dd,
.dashboard-app-shell label,
.dashboard-app-shell td,
.dashboard-app-shell input,
.dashboard-app-shell select,
.dashboard-app-shell textarea {
  font-weight: 400;
}

.dashboard-app-shell strong,
.dashboard-app-shell b,
.dashboard-app-shell dt,
.dashboard-app-shell th {
  font-weight: 600;
}

.dashboard-app-shell button {
  font-weight: 500;
}

.dashboard-app-shell h1,
.dashboard-app-shell h2,
.dashboard-app-shell h3,
.dashboard-app-shell h4 {
  font-weight: 600;
  letter-spacing: -0.015em;
}

/* Page title. The project/portfolio name is this app's equivalent of the
   reference's "Billing" - it gets the same oversized, tightly-tracked
   treatment instead of being one more 1.08rem line. */
.executive-project-hero h2 {
  font-size: 1.75rem;
  line-height: 1.15;
  margin: 2px 0 0;
}

.executive-project-hero p {
  font-size: 0.88rem;
  font-weight: 400;
}

.plan-section h3 {
  font-size: 1.05rem;
  margin: 0 0 12px;
}

.plan-section li {
  font-size: 0.9rem;
  line-height: 1.45;
  padding-top: 10px;
}

/* .primary-button joins the element selector here so a link and a button
   carrying that class are the same size. Without it the anchor kept the base
   42px while every real button next to it shrank to 38px, which is subtle
   enough to look like a mistake and obvious enough to notice. */
.executive-dashboard-shell button,
.executive-dashboard-shell .primary-button {
  font-size: 0.88rem;
  min-height: 38px;
}

/* ---------------------------------------------------------------------------
   3. Header: switchers on the left, menu button on the right
   ------------------------------------------------------------------------ */

/* Reference header is [avatar] Org ⌄ / Project ⌄ ............ [menu]. The
   hamburger was first in the DOM (so it sits left by default); order pins it
   to the far right without moving the markup, and the account block that used
   to live here is now the menu's footer instead. */
.mobile-nav-toggle {
  border-radius: 10px;
  margin-left: auto;
  order: 9;
}

.top-switcher-toggle {
  border-radius: 8px;
  font-weight: 500;
  gap: 7px;
  min-height: 38px;
  padding: 4px 8px;
}

.top-switcher-toggle:hover,
.top-switcher-toggle[aria-expanded="true"] {
  background: var(--pill);
}

.top-switcher-name {
  font-size: 0.98rem;
  font-weight: 600;
}

.top-switcher-sep {
  color: var(--muted);
  font-weight: 400;
  opacity: 0.7;
}

.top-switcher-intake-toggle {
  border-radius: 999px;
  height: 38px;
  width: 38px;
}

/* Floating menus attached to the header read as sheets over the canvas. */
.rail-section-body-floating,
.workspace-intake-menu,
.notification-dropdown,
.account-popover {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 16px;
  box-shadow: 0 18px 42px rgba(17, 22, 29, 0.14);
  padding: 8px;
}

/* ---------------------------------------------------------------------------
   4. The menu: text-first rows, phone-sized targets
   ------------------------------------------------------------------------ */

/* Rail is part of the canvas now (no white fill, no divider rule) and lays
   out as [search + categories] / [account footer]. On mobile it becomes the
   white overlay sheet - see the media query at the bottom. */
.workspace-copilot {
  background: var(--canvas);
  border-right: 0;
  grid-template-rows: minmax(0, 1fr) auto;
}

/* minmax(0, 1fr), not the implicit `auto` column: an auto grid column sizes to
   its max-content, and a bare <input type="search"> contributes a ~20-character
   intrinsic width (about 219px). Inside a 220px rail that pushed the column to
   ~306px and gave the menu its own horizontal scrollbar. Capping the column at
   the rail's width makes every row - search and category toggles alike - fit
   the space it actually has. */
.primary-rail-nav {
  gap: 2px;
  grid-template-columns: minmax(0, 1fr);
  padding: 12px 10px 14px;
}

/* Search sits inside .primary-rail-nav on purpose: dashboard.js collapses the
   flyout on any click outside #primaryRailNav/#primaryRailFlyout, so a search
   box anywhere else in the rail would close the very list it filters. */
.rail-search {
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 999px;
  display: flex;
  gap: 8px;
  margin-bottom: 10px;
  padding: 0 10px 0 12px;
}

.rail-search:focus-within {
  border-color: var(--accent);
}

.rail-search svg {
  color: var(--muted);
  flex: 0 0 auto;
  height: 16px;
  width: 16px;
}

/* width:100% as well as min-width:0 - min-width only governs how far the input
   may shrink inside this flex row, while width caps what it reports as its
   intrinsic size to any ancestor that measures content (the rail grid above).
   Without it the field keeps asking for its default ~20 characters. */
.rail-search input {
  appearance: none;
  background: none;
  border: 0;
  color: var(--ink);
  flex: 1 1 auto;
  font-size: 0.95rem;
  min-width: 0;
  outline: none;
  padding: 11px 0;
  width: 100%;
}

.rail-search input::-webkit-search-cancel-button {
  display: none;
}

.rail-search kbd {
  background: var(--pill);
  border-radius: 6px;
  color: var(--muted);
  flex: 0 0 auto;
  font-family: inherit;
  font-size: 0.72rem;
  padding: 3px 6px;
}

/* The hint is a nicety; the field is the function. Once the rail narrows, the
   badge is taking about a third of the typing room - and below this width the
   device increasingly has no Ctrl/Cmd key to press anyway. The shortcut itself
   keeps working; only its advertisement goes away. */
@media (max-width: 1180px) {
  .rail-search kbd {
    display: none;
  }
}

/* Two classes on purpose throughout this block: the rail's buttons all sit
   inside .executive-dashboard-shell, whose `.executive-dashboard-shell button`
   rule in styles.css (class+element) outranks a single-class selector for
   padding and min-height - and the bare `button` rule centers its content, so
   every menu row has to say flex-start explicitly to read as a list. */
.primary-rail-nav .primary-rail-nav-group-toggle {
  border-left: 0;
  justify-content: flex-start;
  border-radius: var(--radius-row);
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  padding: 10px 12px;
  text-transform: uppercase;
}

/* The category is a section label, not a highlighted pill - the reference's
   menu has no selected state above the item rows. */
.primary-rail-nav .primary-rail-nav-group-toggle[aria-expanded="true"] {
  background: none;
  border-left-color: transparent;
  color: var(--ink);
}

.primary-rail-nav-group-toggle .project-menu-icon {
  height: 16px;
  width: 16px;
}

.primary-rail-nav-group-toggle .admin-pill {
  display: none;
}

.primary-rail-nav-flyout {
  align-content: start;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: 18px;
  box-shadow: 0 18px 42px rgba(17, 22, 29, 0.14);
  gap: 1px;
  padding: 8px;
  width: 244px;
}

/* positionPrimaryRailFlyout (dashboard.js) pins an inline height equal to the
   whole rail, which is what the old flush-against-the-rail panel wanted. Now
   that it is a floating card it should hug its own items instead - overriding
   an inline style is what the !important is for. */
@media (min-width: 901px) {
  .primary-rail-nav-flyout {
    height: auto !important;
    max-height: calc(100dvh - 96px);
  }
}

/* Text-only rows at reading size with a generous hit area, and a full-width
   soft-grey rounded highlight for the current one - the single most
   recognisable detail of the reference menu. The icons come off here (they
   stay on the two category toggles above) so the list reads as one column of
   words rather than a toolbar. */
.primary-rail-nav-flyout .primary-rail-nav-item {
  border-radius: 10px;
  color: var(--ink);
  font-size: 0.98rem;
  font-weight: 400;
  gap: 0;
  justify-content: flex-start;
  min-height: 46px;
  padding: 11px 14px;
}

.primary-rail-nav-item .project-menu-icon {
  display: none;
}

.primary-rail-nav-flyout .primary-rail-nav-item:hover,
.primary-rail-nav-flyout .primary-rail-nav-item:focus-visible {
  background: var(--pill);
  color: var(--ink);
}

.primary-rail-nav-flyout .primary-rail-nav-item.active {
  background: var(--nav-active);
  color: var(--ink);
  font-weight: 500;
}

/* While a search is running both categories' items are on screen at once, so
   each group announces itself with a small label instead of relying on the
   category toggle that is no longer the only thing showing. */
.primary-rail-nav-flyout.searching .primary-rail-nav-flyout-group[hidden] {
  display: grid;
}

.primary-rail-nav-flyout.searching .primary-rail-nav-flyout-group::before {
  color: var(--muted);
  content: attr(data-group-label);
  display: block;
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  padding: 10px 14px 4px;
  text-transform: uppercase;
}

.primary-rail-nav-flyout .rail-search-hidden {
  display: none;
}

.rail-search-empty {
  color: var(--muted);
  display: none;
  font-size: 0.9rem;
  padding: 12px 14px;
}

.primary-rail-nav-flyout.searching.no-results .rail-search-empty {
  display: block;
}

/* ---------------------------------------------------------------------------
   5. User settings: the account block is the menu's footer
   ------------------------------------------------------------------------ */

/* Moved out of the header (dashboard.html) into the bottom of the menu, where
   the reference keeps the avatar and Settings. Full-width row, avatar left,
   name and plan stacked next to it, popover opening upward. */
.workspace-copilot .workspace-account-card {
  border-top: 1px solid var(--line);
  display: block;
  padding: 10px !important;
  width: 100%;
}

.workspace-copilot .workspace-account-main {
  border-radius: var(--radius-row) !important;
  gap: 10px;
  justify-content: flex-start;
  min-height: 52px;
  padding: 7px 10px;
  width: 100%;
}

.workspace-copilot .workspace-account-main:hover,
.workspace-copilot .workspace-account-main:focus-visible {
  background: var(--pill) !important;
}

.workspace-copilot .workspace-account-main > div {
  display: grid;
  min-width: 0;
}

.workspace-copilot #workspaceUserName {
  font-size: 0.92rem;
  font-weight: 500;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Plan name was visually hidden in the header (styles.css clips
   #workspaceProductName). In the footer there is room for it, and it is the
   line that makes the row read as an account row rather than a name. */
.workspace-copilot #workspaceProductName {
  clip: auto;
  clip-path: none;
  color: var(--muted);
  font-size: 0.78rem;
  height: auto;
  overflow: hidden;
  position: static;
  text-overflow: ellipsis;
  white-space: nowrap;
  width: auto;
}

.workspace-copilot .account-popover {
  bottom: calc(100% + 8px);
  left: 10px;
  right: 10px;
  top: auto;
  width: auto;
}

.workspace-copilot .account-popover-item {
  border-radius: 10px;
  justify-content: flex-start;
  font-size: 0.92rem;
  font-weight: 400;
  min-height: 44px;
  padding: 11px 12px;
}

.workspace-copilot .account-popover-item:hover,
.workspace-copilot .account-popover-item:focus-visible {
  background: var(--pill);
}

/* ---------------------------------------------------------------------------
   6. Buttons: pills
   ------------------------------------------------------------------------ */

/* Same reason as the size rule above: the class has to travel with the element
   selector or a link styled as a primary button keeps square corners while
   every button beside it is rounded. */
.dashboard-app-shell button,
.dashboard-app-shell .primary-button {
  border-radius: 10px;
}

/* Secondary actions are borderless grey pills; a row of them scrolls
   sideways on a narrow screen rather than wrapping into a block of chrome. */
.dashboard-app-shell .secondary-button,
.dashboard-app-shell .sample-button {
  background: var(--pill);
  border: 0;
  border-radius: 999px;
  color: var(--ink);
  padding: 0 18px;
}

.dashboard-app-shell .secondary-button:hover,
.dashboard-app-shell .sample-button:hover {
  background: var(--pill-hover);
}

.project-hero-actions,
.portfolio-hero-actions {
  -ms-overflow-style: none;
  gap: 8px;
  overflow-x: auto;
  scrollbar-width: none;
}

.project-hero-actions::-webkit-scrollbar,
.portfolio-hero-actions::-webkit-scrollbar {
  display: none;
}

.project-hero-actions > * {
  flex: 0 0 auto;
}

/* ---------------------------------------------------------------------------
   7. Stat tiles: big quiet numbers
   ------------------------------------------------------------------------ */

/* Bordered boxes with a 0.66rem/800 caption become a large number over a
   small regular-weight label, sitting straight on the card. Score bands keep
   their meaning through a colored left rule only. */
.project-focus-summary,
.executive-metrics {
  gap: 6px 22px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  margin-bottom: 18px;
}

.project-focus-summary div,
.executive-metrics div,
.executive-metrics button {
  background: none;
  border: 0;
  border-radius: 10px;
  gap: 2px;
  min-height: 0;
  padding: 10px 0;
}

.executive-metrics button:hover {
  background: var(--surface-soft);
  border: 0;
  padding: 10px;
}

.project-focus-summary strong,
.executive-metrics strong {
  font-size: 1.7rem;
  font-weight: 600;
  letter-spacing: -0.02em;
  line-height: 1.1;
}

.project-focus-summary span,
.executive-metrics span {
  color: var(--muted);
  font-size: 0.82rem;
  font-weight: 400;
  letter-spacing: 0;
  line-height: 1.3;
  text-transform: none;
}

.project-focus-summary .score-low,
.project-focus-summary .score-mid,
.project-focus-summary .score-warn,
.project-focus-summary .score-high {
  background: none;
  border-radius: 0;
  padding-left: 12px;
}

/* ---------------------------------------------------------------------------
   8. Tinted notice card
   ------------------------------------------------------------------------ */

.project-dashboard .plan-section.attention-section {
  background: var(--notice-bg);
  border: 1px solid var(--notice-line);
  border-radius: 16px;
  padding: 18px 20px;
}

.project-dashboard .plan-section.attention-section h3 {
  color: var(--notice-ink);
}

.project-dashboard .plan-section.attention-section li {
  border-top-color: var(--notice-line);
}

.chat-upgrade-notice,
.upgrade-limit-status {
  background: var(--notice-bg);
  border: 1px solid var(--notice-line);
  border-radius: 14px;
  color: var(--notice-ink);
  padding: 14px 16px;
}

/* ---------------------------------------------------------------------------
   9. List rows: rounded initial tile + title + subtitle
   ------------------------------------------------------------------------ */

.portfolio-list-row,
.project-list-row {
  border-radius: var(--radius-row);
}

.portfolio-list-item,
.project-list-item {
  align-items: center;
  border-radius: var(--radius-row);
  display: flex;
  gap: 12px;
  justify-content: flex-start;
  min-height: 56px;
  padding: 10px 12px;
  width: 100%;
}

.portfolio-list-item:hover,
.project-list-item:hover {
  background: var(--pill);
}

/* Rendered by dashboard.js as the row's first child. Same grey rounded tile
   the reference puts in front of "Payment methods". */
.list-row-initial {
  align-items: center;
  background: var(--pill);
  border-radius: 10px;
  color: var(--ink);
  display: flex;
  flex: 0 0 auto;
  font-size: 0.95rem;
  font-weight: 600;
  height: 38px;
  justify-content: center;
  text-transform: uppercase;
  width: 38px;
}

.portfolio-list-item > span:not(.list-row-initial),
.project-list-item > span:not(.list-row-initial) {
  display: grid;
  gap: 1px;
  min-width: 0;
  text-align: left;
}

.portfolio-list-item strong,
.project-list-item strong {
  font-size: 0.95rem;
  font-weight: 500;
}

.portfolio-list-item small,
.project-list-item small {
  color: var(--muted);
  font-size: 0.8rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   10. Mobile
   ------------------------------------------------------------------------ */

@media (max-width: 900px) {
  .app-top-bar {
    padding: 8px 12px;
  }

  .executive-workspace {
    padding: 4px 8px 10px;
  }

  /* Two header rows, not three: [logo ....... bell menu] over
     [portfolio / project +]. dashboard-responsive.css orders the pieces for a
     header that still contained the account block; with that gone the menu
     button takes its place at the end of the first row instead of being
     pushed onto a row of its own by the order: 9 above. */
  .mobile-nav-toggle {
    margin-left: 0;
    order: 3;
  }

  .project-dashboard {
    border-radius: 16px;
    padding: 18px 16px 26px;
  }

  .executive-project-hero h2 {
    font-size: 1.5rem;
  }

  /* Off-canvas drawer: the white sheet the reference's menu overlay is, with
     the same three bands - search, list, account footer. */
  .workspace-copilot {
    background: var(--surface);
    border-radius: 0 18px 18px 0;
    grid-template-rows: auto minmax(0, 1fr) auto;
  }

  .primary-rail-nav-flyout {
    border: 0;
    border-radius: 0;
    box-shadow: none;
    padding: 0 10px 8px;
    width: auto;
  }

  .primary-rail-nav-flyout .primary-rail-nav-item {
    min-height: 50px;
  }

  /* The account card's header-era mobile ordering no longer applies now that
     it is a rail child; keep it a plain block at the bottom of the drawer. */
  .workspace-copilot .workspace-account-card {
    order: 0;
  }
}

/* ---------------------------------------------------------------------------
   The change log (rail section "changes").

   Colours come from tokens only, so appearance-apply.js's admin-chosen palette
   and dark-mode.css both still win. The one place real colour is spent is the
   slip figure and the mitigation verdict - the two facts on the page somebody
   scans for - and everything else stays quiet so they carry.
   ------------------------------------------------------------------------ */
.change-log-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.change-headline { margin: 6px 0 0; color: var(--muted); font-size: 0.95rem; }
.change-log-filter select { border: 1px solid var(--line); background-color: var(--panel); color: var(--ink); border-radius: var(--radius-control); padding: 6px 32px 6px 10px; }

.change-list { list-style: none; margin: 18px 0 0; padding: 0; display: flex; flex-direction: column; gap: 2px; }
.change-entry { padding: 14px 16px; background: var(--panel); border: 1px solid var(--line); border-radius: var(--radius-tile); }
.change-entry-head { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.change-milestone { font-weight: 600; }
.change-project { color: var(--muted); font-size: 0.85rem; }

.change-entry-meta { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-top: 6px; color: var(--muted); font-size: 0.85rem; }
.change-dates, .change-status { font-variant-numeric: tabular-nums; }
.change-source, .change-who { border: 1px solid var(--line); border-radius: var(--radius-pill); padding: 1px 8px; }

/* Direction is carried by the sign AND the word, never by colour alone. */
.change-slip { font-variant-numeric: tabular-nums; font-weight: 600; border-radius: var(--radius-pill); padding: 1px 8px; font-size: 0.85rem; }
.change-slip-late { color: #b42318; background: color-mix(in srgb, #b42318 12%, transparent); }
.change-slip-early { color: #067647; background: color-mix(in srgb, #067647 12%, transparent); }
.change-slip-unknown { color: var(--muted); background: var(--panel-strong); font-weight: 500; }

.change-reason { margin: 8px 0 0; font-style: italic; }
.change-cause { margin: 8px 0 0; font-size: 0.92rem; }
.change-cause-none { color: var(--muted); }
.change-owner { color: var(--muted); }
.change-mitigation { margin: 4px 0 0; font-size: 0.9rem; color: var(--muted); }

/* "likely" is the page's most important word: it separates a cause somebody
   recorded from one this code guessed. It is given a visible border rather than
   a colour tint so it survives forced-colours and print. */
.change-inferred { border: 1px dashed var(--line-strong); border-radius: var(--radius-pill); padding: 0 7px; font-size: 0.78rem; text-transform: uppercase; letter-spacing: 0.04em; color: var(--muted); }

.change-verdict { border-radius: var(--radius-pill); padding: 1px 8px; font-size: 0.82rem; }
.change-verdict-good { color: #067647; background: color-mix(in srgb, #067647 12%, transparent); }
.change-verdict-bad { color: #b42318; background: color-mix(in srgb, #b42318 12%, transparent); }
.change-verdict-neutral, .change-verdict-pending { color: var(--muted); background: var(--panel-strong); }

/* The slippage waterfall, at the head of the change log. Bars are scaled to the
   largest cause rather than the total, so the smallest cause stays visible when
   one dominates. */
.waterfall-section { margin-bottom: 2px; }
.waterfall-list { list-style: none; margin: 14px 0 0; padding: 0; display: flex; flex-direction: column; gap: 6px; }
.waterfall-row { display: grid; grid-template-columns: minmax(120px, 34%) 1fr auto; align-items: center; gap: 12px; }
.waterfall-label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 0.9rem; }
/* The owner follows the cause in the same line with nothing between them, so
   "Board asked for an early balance report" and "Ana Ruiz" run together into
   one sentence - muted and a shade smaller is not enough separation at this
   size. The separator is decorative, so it goes in ::before rather than into
   the text a screen reader reads out. */
.waterfall-owner { color: var(--muted); font-size: 0.82rem; }
.waterfall-owner::before { content: "·"; margin: 0 0.4em; }
.waterfall-track { background: var(--panel-strong); border-radius: var(--radius-pill); height: 12px; overflow: hidden; }
.waterfall-bar { display: block; height: 100%; border-radius: var(--radius-pill); }
.waterfall-bar-cause { background: var(--accent); }
/* Deliberately not the accent: unexplained slip is not one more cause, it is the
   part of the number nobody can account for, and it should not read as a peer. */
.waterfall-bar-unexplained { background: repeating-linear-gradient(45deg, var(--line-strong) 0 4px, transparent 4px 8px); }
.waterfall-days { font-variant-numeric: tabular-nums; font-weight: 600; min-width: 3.5em; text-align: right; }
.waterfall-note { margin: 10px 0 0; color: var(--muted); font-size: 0.85rem; }

/* The one moment of motion in the product.
   ---------------------------------------------------------------------------
   Bars grow from nothing on first paint, staggered down the list, and nothing
   else on the page moves. That is the point: one orchestrated moment reads as
   intent, while hover effects scattered over a dozen components read as a
   template. It is spent here because this chart is the product's own argument -
   where the time went - and it is now the first thing on Home.

   scaleX rather than width: the widths are inline and computed per bar, so a
   keyframe cannot know them, and animating width relayouts the row on every
   frame. A transform is composited and ends at exactly the width the data set.

   Delays are per row rather than per bar so a row's bar and its neighbours stay
   in step. Eight steps covers the list, which slices to eight. */
@keyframes waterfall-grow {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}

.waterfall-bar {
  transform-origin: left center;
  animation: waterfall-grow 420ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

.waterfall-row:nth-child(1) .waterfall-bar { animation-delay: 0ms; }
.waterfall-row:nth-child(2) .waterfall-bar { animation-delay: 40ms; }
.waterfall-row:nth-child(3) .waterfall-bar { animation-delay: 80ms; }
.waterfall-row:nth-child(4) .waterfall-bar { animation-delay: 120ms; }
.waterfall-row:nth-child(5) .waterfall-bar { animation-delay: 160ms; }
.waterfall-row:nth-child(6) .waterfall-bar { animation-delay: 200ms; }
.waterfall-row:nth-child(7) .waterfall-bar { animation-delay: 240ms; }
.waterfall-row:nth-child(8) .waterfall-bar { animation-delay: 280ms; }

/* Not a flourish worth overriding somebody's stated preference for. The bars
   arrive at their final width immediately instead. */
@media (prefers-reduced-motion: reduce) {
  .waterfall-bar {
    animation: none;
  }
}

/* The waterfall's two lenses. "By cause" answers where the time went; "By
   project" answers who is in trouble. Offered only when more than one project is
   in scope - filtered to one, the project lens is a single bar at 100%. */
.waterfall-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.waterfall-lens { display: inline-flex; border: 1px solid var(--line); border-radius: var(--radius-pill); overflow: hidden; }
.waterfall-lens-button { border: 0; background: transparent; color: var(--muted); padding: 5px 14px; font: inherit; font-size: 0.85rem; cursor: pointer; }
.waterfall-lens-button.active { background: var(--panel-strong); color: var(--ink); font-weight: 600; }
.waterfall-lens-button:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ---------------------------------------------------------------------------
   The AI badge.

   Worn by every control that calls a model, and the only thing any of them say
   about cost. Deliberately quiet: it is a property of the button, not a call to
   action, and a loud badge on nine controls would read as nine adverts.

   Sized in em rather than px so it tracks whatever it sits inside - the same
   badge rides a primary button and a link-button without being retuned.
   ------------------------------------------------------------------------ */
.ai-badge {
  display: inline-block;
  font-size: 0.68em;
  font-weight: 700;
  letter-spacing: 0.06em;
  line-height: 1.6;
  padding: 0 0.45em;
  border-radius: var(--radius-pill);
  border: 1px solid currentColor;
  /* Inherits the button's ink rather than picking its own colour, so it stays
     legible on a primary button, a secondary one and a link alike - and keeps
     working when appearance-apply.js rewrites the palette at runtime. */
  color: inherit;
  opacity: 0.72;
  vertical-align: 0.15em;
  /* Never the thing that wraps a label onto two lines. */
  white-space: nowrap;
}

/* A disabled control's badge should not look more present than its label. */
button:disabled .ai-badge,
[disabled] .ai-badge {
  opacity: 0.45;
}

/* The win-back trial offer, inside a wall.
   Quiet on purpose: it sits under the "See Pro" button and must not compete
   with it - somebody who has decided to buy should not have to decline a free
   period first. */
.win-back-trial {
  display: block;
  margin-top: 6px;
  font-size: 0.88rem;
  color: var(--muted);
  text-align: left;
}
.win-back-trial:hover { color: var(--ink); }

/* ---------------------------------------------------------------------------
   Workspace setup.

   A modal that cannot be dismissed, so it has no close control and no
   click-outside - offering either would suggest the questions are optional when
   the flow does not continue without them.
   ------------------------------------------------------------------------ */
body.setup-open { overflow: hidden; }

.setup-backdrop {
  position: fixed;
  inset: 0;
  z-index: 1000;
  display: grid;
  place-items: center;
  padding: 24px;
  background: color-mix(in srgb, var(--ink) 55%, transparent);
  overflow-y: auto;
}

.setup-modal {
  /* 560px was sized for the first steps - a language choice and a row of chips.
     The terms step then grew two document cards with their own summaries, an
     intro, a read-first note, a checkbox and a signature field, and kept the
     same column, so it scrolled internally on a desktop with most of the screen
     unused around it.

     The measure still has an upper bound, because a 1400px-wide paragraph is
     not an improvement - it just moves the reading problem. 720px is roughly
     where a line stops being comfortable, and min() keeps it honest on a phone
     where the old value was already doing the right thing. */
  width: min(720px, 100%);
  max-height: min(88vh, 100%);
  overflow-y: auto;
  background: var(--panel);
  color: var(--ink);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  padding: 28px;
  display: flex;
  flex-direction: column;
  gap: 18px;
}

/* The rhythm inside a step, which is where the crowding actually was.
   -------------------------------------------------------------------------
   The modal's own gap separates header / body / error / footer and was never
   the problem. Everything the tester saw sitting on top of itself lives inside
   .setup-body, where the terms step stacks an intro, two document cards, a
   read-first note, a checkbox and a signature field with nothing between them.

   Spacing here says what belongs with what: tight under a heading, open before
   the acceptance controls, because that is where reading stops and committing
   starts. */
.setup-body {
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.setup-body > h2 {
  margin: 0;
}

.setup-body > .setup-doc-cards {
  margin-top: 2px;
}

.setup-body > .setup-agree {
  margin-top: 10px;
}

.setup-header { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; }
.setup-progress { color: var(--muted); font-size: 0.85rem; font-variant-numeric: tabular-nums; }
.setup-modal h2 { margin: 0; }
.setup-help { margin: 0; color: var(--muted); font-size: 0.92rem; }

/* Step one. Each option carries its own explanation in its own language, so the
   rows are deliberately tall - this is the one screen somebody may be reading
   in a language the rest of the app is not yet in. */
.setup-language-options { display: flex; flex-direction: column; gap: 8px; }
.setup-language-option {
  display: flex;
  flex-direction: column;
  gap: 4px;
  text-align: left;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: var(--radius-tile);
  background: var(--panel);
  color: var(--ink);
  font: inherit;
  cursor: pointer;
}
.setup-language-option span { color: var(--muted); font-size: 0.88rem; }
.setup-language-option:hover { border-color: var(--line-strong); }
.setup-language-option.selected { border-color: var(--accent); box-shadow: inset 0 0 0 1px var(--accent); }

.setup-chips { display: flex; flex-wrap: wrap; gap: 8px; }
.setup-chip {
  padding: 7px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  background: var(--panel);
  color: var(--ink);
  font: inherit;
  font-size: 0.9rem;
  cursor: pointer;
}
.setup-chip:hover { border-color: var(--line-strong); }
.setup-chip.selected { border-color: var(--accent); background: color-mix(in srgb, var(--accent) 12%, transparent); font-weight: 600; }

.setup-other { display: flex; flex-direction: column; gap: 6px; margin-top: 4px; }
.setup-other span { color: var(--muted); font-size: 0.85rem; }
.setup-other input {
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  border-radius: var(--radius-control);
  padding: 9px 12px;
  font: inherit;
}

.setup-documents { margin: 0; padding-left: 20px; display: flex; flex-direction: column; gap: 6px; }

/* The documents somebody is about to agree to.
   Cards rather than a bulleted list of blue links: this is the most consequential
   screen in onboarding and it read like a footer. Each carries the document's own
   summary, so what is being agreed to is visible without opening three tabs, and
   its own read-state, because the checkbox below is about to claim they were
   read. */
.setup-doc-cards { display: grid; gap: 8px; }

.setup-doc-card {
  align-items: stretch;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  color: inherit;
  display: grid;
  gap: 4px;
  padding: 12px 14px;
  text-decoration: none;
  transition: border-color 120ms ease, background 120ms ease;
}

.setup-doc-card:hover { background: var(--panel-strong); border-color: var(--accent); }
.setup-doc-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

.setup-doc-head { align-items: center; display: flex; gap: 10px; justify-content: space-between; }
.setup-doc-title { font-weight: 600; }

/* "Open" is an instruction until it has been done, then it is a receipt. Same
   slot either way so the card does not jump when it changes. */
.setup-doc-state {
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--muted);
  flex: none;
  font-size: 0.74rem;
  padding: 2px 9px;
}

.setup-doc-card.is-read .setup-doc-state {
  background: rgba(31, 111, 92, 0.12);
  border-color: rgba(31, 111, 92, 0.4);
  color: #1b6152;
}

.setup-doc-summary { color: var(--muted); font-size: 0.85rem; line-height: 1.5; }

.setup-agree { display: flex; align-items: flex-start; gap: 10px; }

/* Our preference, stated once, above the box.
   Reading is encouraged and not required, so this sits where somebody will see
   it before ticking and reads as a sentence rather than as an obstacle. */
/* No negative margin. It pulled this line up against the cards above it to
   fight the flat 18px gap, which is the sort of fix that works until the gap
   changes - and the gap has now changed. It belongs with the cards it refers
   to, so it says so by sitting close to them rather than by cancelling out
   somebody else's spacing. */
.setup-read-intent { margin: 0 0 2px; }

.setup-error { margin: 0; color: #b42318; font-size: 0.9rem; }
.setup-footer { display: flex; justify-content: space-between; align-items: center; gap: 12px; }

.setup-language-option:focus-visible,
.setup-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

/* ---------------------------------------------------------------------------
   The AI narrative in a program review.

   Everything else on that page is arithmetic. This is the one part that was
   written rather than counted, and a reader scanning a review in a meeting has
   to be able to tell which is which at a glance - the two carry very different
   weight, and a narrative that reads like a measurement is the one thing this
   page must not produce.

   Tinted and ruled rather than coloured-text: the paragraphs still have to be
   comfortable to read at length, and coloured body copy is not.
   ------------------------------------------------------------------------ */
.review-narrative {
  background: color-mix(in srgb, var(--accent) 6%, transparent);
  border-left: 3px solid var(--accent);
  border-radius: var(--radius-tile);
  padding: 16px 20px;
  margin: 12px 0;
}
.review-narrative .review-subheading { color: var(--accent-strong, var(--accent)); }

.review-narrative-attribution {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 0 0 10px;
  color: var(--muted);
  font-size: 0.82rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* Stale: shown, because somebody paid for it, but saying so. */
.review-narrative.is-stale { opacity: 0.82; border-left-style: dashed; }
.review-narrative-stale {
  margin: 0 0 12px;
  padding: 8px 12px;
  border-radius: var(--radius-control);
  background: var(--panel-strong);
  color: var(--ink);
  font-size: 0.88rem;
}

@media print {
  /* A printed review is read without the colour cue, so the rule and the
     attribution line do the work instead. */
  .review-narrative { background: none; border-left: 2px solid #000; }
}

/* The workspace picker and the name editor.
   The picker reuses the setup modal's shell deliberately: it is the same kind
   of moment - one question, asked once, that the app does not continue past. */
.workspace-picker-host .setup-modal { width: min(480px, 100%); }

.workspace-name-editor { margin-bottom: 2px; }
.workspace-name-row { display: flex; gap: 8px; align-items: center; margin-top: 8px; }
.workspace-name-row input {
  flex: 1;
  border: 1px solid var(--line);
  background: var(--panel);
  color: var(--ink);
  border-radius: var(--radius-control);
  padding: 9px 12px;
  font: inherit;
}
.workspace-name-status { margin: 8px 0 0; color: var(--muted); font-size: 0.85rem; }

/* --- Demo mode strip -------------------------------------------------------
   Replaces the toast that used to announce demo mode over the page heading and
   then disappear. This is session-long context, so it sits in the page rather
   than over it, above the app shell, and stays. Hidden unless the body carries
   .demo-dashboard-mode, so a signed-in workspace never renders it. */
.demo-mode-strip {
  display: none;
}

body.demo-dashboard-mode .demo-mode-strip {
  align-items: baseline;
  background: color-mix(in srgb, var(--accent-brand) 10%, var(--canvas, var(--bg)));
  border-bottom: 1px solid color-mix(in srgb, var(--accent-brand) 26%, transparent);
  color: var(--ink);
  display: flex;
  flex-wrap: wrap;
  font-size: 0.82rem;
  gap: 4px 10px;
  justify-content: center;
  line-height: 1.45;
  padding: 9px 20px;
  text-align: center;
}

body.demo-dashboard-mode .demo-mode-strip strong {
  color: var(--accent-brand);
  font-weight: 600;
  letter-spacing: 0.02em;
}

body.demo-dashboard-mode .demo-mode-strip span {
  color: var(--muted);
}

/* --- Type: two families, and a scale with real jumps ----------------------
   The app was set in one sans at four sizes between 0.9rem and 1.75rem, with
   weight doing most of the hierarchy work. That reads as competent and as
   nothing in particular - Inter at 600 is the house style of every dashboard,
   and a client reviewing this said as much.

   Two changes. Titles move to the serif (Newsreader, one weight - see
   public/fonts/README.md): family now separates "what this page is" from
   "what is on it", so a heading no longer has to be heavier to be a heading.
   And the steps get further apart - 2.15 / 1.3 / 0.95rem is roughly a 1.35
   ratio, where 1.75 / 1.05 / 0.95 left section titles a hair above body copy.

   Deliberately narrow in scope: page titles, section titles and the figure that
   headlines a card. Table headers, labels, eyebrows, buttons and every piece of
   running text stay in Inter, which is the right face for dense data and for
   anything with a number in it. A serif applied everywhere would be a costume. */
.dashboard-app-shell .executive-project-hero h2,
.dashboard-app-shell .plan-section h3,
.dashboard-app-shell .needs-you-head h3,
.dashboard-app-shell .change-strip-head h3,
.dashboard-app-shell .editable-section-header h3,
.dashboard-app-shell .page-empty-state .empty-state-headline {
  font-family: Newsreader, ui-serif, Georgia, "Times New Roman", serif;
  /* One weight exists. Asking for 600 here would synthesise a faux-bold. */
  font-weight: 500;
  /* Gentler than the -0.015em the sans headings carry: negative tracking that
     flatters a grotesk closes up a serif's own spacing and muddies its joins. */
  letter-spacing: -0.005em;
}

.dashboard-app-shell .executive-project-hero h2 {
  font-size: 2.15rem;
  line-height: 1.12;
}

.dashboard-app-shell .plan-section h3,
.dashboard-app-shell .editable-section-header h3 {
  font-size: 1.3rem;
  line-height: 1.25;
  margin-bottom: 14px;
}

/* The two cards that open Home. Their headings sit on a row with a count on the
   right, so they take the section size without the section's bottom margin. */
.dashboard-app-shell .needs-you-head h3,
.dashboard-app-shell .change-strip-head h3 {
  font-size: 1.3rem;
  line-height: 1.25;
  margin: 0;
}

.dashboard-app-shell .page-empty-state .empty-state-headline {
  font-size: 1.35rem;
  line-height: 1.3;
}

/* Figures keep the sans. Newsreader's numerals are lovely and the wrong tool
   here: these columns line up, and Inter's tabular set is what keeps them
   lining up when a 1 replaces a 7. */
.dashboard-app-shell .project-card-count,
.dashboard-app-shell .change-figures,
.dashboard-app-shell .heat-overall {
  font-variant-numeric: tabular-nums;
}

@media (max-width: 720px) {
  .dashboard-app-shell .executive-project-hero h2 {
    font-size: 1.7rem;
  }

  .dashboard-app-shell .plan-section h3,
  .dashboard-app-shell .editable-section-header h3,
  .dashboard-app-shell .needs-you-head h3,
  .dashboard-app-shell .change-strip-head h3 {
    font-size: 1.15rem;
  }
}

/* A dependency that cannot be taken, said before it is clicked rather than after
   Save is pressed. The row stays readable - this is information, not an error -
   and the note names the reason the tooltip spells out. */
.checkbox-row-blocked {
  cursor: not-allowed;
  opacity: 0.55;
}

.dependency-blocked-note {
  color: var(--warn);
  font-size: 0.68rem;
  font-style: normal;
  letter-spacing: 0.03em;
  margin-left: auto;
  text-transform: uppercase;
  white-space: nowrap;
}

/* The collections a project has nothing in yet.
   
   Not hidden: there is no rail entry for action items, resources or delivery
   dates, so these buttons are the only way to add the first one. Demoted from a
   tile reading "0" to a name you can click, which is what an empty collection
   actually is - a thing you could start, not a measurement worth a tile. */
.project-card-empties {
  align-items: baseline;
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  font-size: 0.82rem;
  gap: 4px 12px;
  margin: 10px 0 0;
}

.project-card-empties .link-button {
  font-size: 0.82rem;
  padding: 0;
}

/* "The record has gaps", under the findings.
   ---------------------------------------------------------------------------
   Quieter than a row and louder than nothing. It is real work and it is not
   trouble, so it reads as a footnote to the card rather than as a fourth
   finding competing with the three above it - which is the whole point of
   taking it out of the rows. */
.needs-you-gaps {
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.45;
  margin: 12px 0 0;
  padding-top: 12px;
}

.needs-you-gaps-label {
  color: var(--ink);
  font-weight: 600;
}

/* --- The answers Home is opened for ---------------------------------------
   Sentences, not tiles. A tile is a number looking for a caption; each of
   these is already a sentence with the number inside it, so the shape follows:
   a readable headline and one quieter line under it, wide enough to say
   something rather than sized to fit a digit.

   Tone is a left edge rather than a fill. These sit directly under "What needs
   you", which already uses filled severity bands on its rows, and a second
   block of filled colour under it turns the top of the page into a traffic
   light nobody reads. The stripe says which way the answer points without
   competing for the same attention. */
.home-answers {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  /* Bottom margin, not top: this row leads the page now, and without it the
     cards sit flush against the "What needs you" panel underneath - measured
     in a screenshot, where the two borders read as one broken box. */
  margin: 0 0 16px;
}

.executive-dashboard-shell .home-answer {
  align-items: start;
  background: var(--surface, #ffffff);
  border: 1px solid var(--line);
  border-left: 3px solid var(--line);
  border-radius: 10px;
  color: var(--ink);
  display: grid;
  gap: 3px;
  justify-items: start;
  padding: 12px 14px;
  text-align: left;
}

.executive-dashboard-shell .home-answer:hover,
.executive-dashboard-shell .home-answer:focus-visible {
  border-color: var(--accent-brand);
}

.home-answer-headline {
  font-size: 0.95rem;
  font-weight: 600;
  line-height: 1.3;
}

.home-answer-detail {
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.35;
}

/* Four tones, and only three of them are a warning. "good" is deliberately not
   green-filled: an all-clear that shouts is the thing people learn to skip. */
.executive-dashboard-shell .home-answer-bad { border-left-color: #b91c1c; }
.executive-dashboard-shell .home-answer-warn { border-left-color: #c99b11; }
.executive-dashboard-shell .home-answer-good { border-left-color: #15803d; }
.executive-dashboard-shell .home-answer-neutral { border-left-color: var(--line); }

/* --- What needs you / Attention rows --------------------------------------
   renderAttentionList's markup is shared by the Attention panel and by the
   "What needs you" card on Home, but its styling was not: the rules that made
   the fact line small and muted were written as `.plan-section li span`, and
   the Home card is not a .plan-section. So on Home the finding and the sentence
   explaining it rendered at the same size in the same severity colour, with no
   padding and no rule between rows - three problems reading as one block of
   coloured text. Measured on a real workspace: label and fact both 13.44px,
   both rgb(159,47,36), li padding 0, li border-top 0.

   Styled on the row's own classes here so both surfaces get it.

   The indent is the alignment the rows were missing: the icon sits in the left
   channel and everything else - label, fact, buttons - starts at one line, so a
   column of rows reads as a column instead of as ragged text. */
.needs-you-list li,
.attention-section li {
  border-top: 1px solid var(--line);
  list-style: none;
  padding: 14px 0 14px 24px;
  position: relative;
}

/* The lane the row's controls are revealed into, on a pointer that can hover.
   -------------------------------------------------------------------------
   Declared here rather than beside the reveal rules in styles.css because the
   shorthand directly above sets padding-right to 0, this file loads last, and
   a longhand written earlier loses to a shorthand written later however
   specific it is. Measured before it was moved: computed padding-right 0px,
   and three buttons over the sentence they belong to.

   Kept whether or not the buttons are showing, so the fact line wraps the same
   way at rest as it does under the pointer - a sentence that reflowed on hover
   would be a row that moves while it is being read. */
@media (hover: hover) and (pointer: fine) {
  .needs-you-list li,
  .attention-section li {
    padding-right: 240px;
  }
}

.needs-you-list li:first-child,
.attention-section li:first-child {
  border-top: 0;
  padding-top: 0;
}

/* Pulled back into the left channel the padding above reserves. */
.needs-you-list .attention-item-heading,
.attention-section .attention-item-heading {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 8px;
  margin-left: -24px;
}

.needs-you-list .attention-item-heading strong,
.attention-section .attention-item-heading strong {
  font-size: 0.9rem;
}

/* The sentence under the finding. Quieter and smaller than the label, which is
   the hierarchy that was missing - and allowed to wrap to as many lines as it
   needs. A real finding names a milestone, and milestone names in the wild run
   to a full column heading; truncating one mid-word says less than nothing. */
.needs-you-list .attention-item-fact,
.attention-section .attention-item-fact {
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.5;
  margin: 4px 0 0;
  overflow-wrap: anywhere;
}

/* The project the finding belongs to, as a label rather than a sentence opener.
   Several rows commonly share one project, so this repeats - it should read as
   a tag you can skip once you have it, not as the first words of the finding. */
.needs-you-list .attention-item-project,
.attention-section .attention-item-project,
.ai-quick-wins-list .attention-item-project {
  background: var(--pill);
  border-radius: 5px;
  color: var(--ink);
  font-size: 0.72rem;
  margin-right: 7px;
  padding: 2px 7px;
  white-space: nowrap;
}

/* Leads the row at portfolio level, the same way it leads a row on Home, and
   capped so a long project name cannot crowd out the milestone it names. */
.ai-quick-wins-list .attention-item-project {
  flex: none;
  margin-right: 0;
  max-width: 40%;
  overflow: hidden;
  text-overflow: ellipsis;
}

.needs-you-list .attention-item-actions,
.attention-section .attention-item-actions {
  margin-top: 10px;
}

/* Whose account you are in, on Billing & Data.
   
   Deliberately a notice rather than a heading: it is context for the controls
   below it, and it has to read as "before you press anything, this is not
   yours" without competing with the page title above it. */
.workspace-owner-banner {
  align-items: baseline;
  background: var(--notice-bg);
  border: 1px solid var(--notice-line);
  border-radius: 12px;
  color: var(--ink);
  display: flex;
  flex-wrap: wrap;
  font-size: 0.85rem;
  gap: 6px 10px;
  line-height: 1.5;
  margin: 0 0 16px;
  padding: 12px 14px;
}

/* Your own account is not a warning, so it drops the amber and states the fact
   on the page's own surface. */
.workspace-owner-banner.is-owner {
  background: var(--surface-soft, var(--panel));
  border-color: var(--line);
}

.workspace-owner-role {
  background: var(--notice-accent);
  border-radius: 5px;
  color: #ffffff;
  flex: 0 0 auto;
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  padding: 3px 8px;
  text-transform: uppercase;
}

.workspace-owner-banner.is-owner .workspace-owner-role {
  background: var(--pill);
  color: var(--muted);
}

/* The account menu is pinned to the viewport by placeAccountPopover() on open -
   position:fixed with measured coordinates, so it escapes every ancestor
   stacking context and cannot be painted over by the page, which is what used to
   happen to it. This only has to make sure the stylesheet's own z-index is high
   enough to sit above the rest of the chrome once it is fixed. */
.workspace-copilot .account-popover {
  z-index: 1000;
}

/* Guided flows: one question per screen, counted, with the cost of skipping
   stated rather than implied. See public/guidedFlow.js for the rules and
   openGuidedFlow in dashboard.js for the screen.

   The step counter is deliberately the first thing in the panel: an unbounded
   sequence of dialogs feels like being trapped, and a counted one is a task
   with an end. */
.guided-step-count {
  font-size: 0.78rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted, #6b7280);
  margin: 0 0 6px;
}

.guided-step-title {
  margin: 0 0 10px;
  font-size: 1.05rem;
  font-weight: 600;
  text-wrap: balance;
}

.guided-step-why {
  color: var(--muted, #4b5563);
  font-size: 0.92rem;
  line-height: 1.5;
}

.guided-step-why p { margin: 0 0 10px; }

.guided-choice-note {
  border-left: 3px solid var(--line, #e5e7eb);
  padding: 2px 0 2px 12px;
  margin: 0 0 10px;
}

.guided-choice-note strong { display: block; font-size: 0.9rem; }
.guided-choice-note p { margin: 2px 0 0; font-size: 0.86rem; }

/* The skip warning is a cost, not a scold - amber rather than red, and it
   appears beside the skip button it describes rather than after the fact. */
.guided-skip-warning {
  background: rgba(217, 119, 6, 0.08);
  border: 1px solid rgba(217, 119, 6, 0.25);
  border-radius: 10px;
  padding: 10px 12px;
  margin: 12px 0;
}

.guided-skip-warning strong { display: block; font-size: 0.82rem; margin-bottom: 4px; }
.guided-skip-warning p { margin: 0; font-size: 0.86rem; line-height: 1.45; }

/* Said before the reader answers, not after they press Continue.
   The flow refuses on the same rule either way; this only moves the telling
   earlier, which is the whole of the complaint it answers. */
.guided-step-required {
  color: var(--muted, #6b7280);
  font-size: 0.74rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  margin: 12px 0 0;
  text-transform: uppercase;
}

.guided-step-field { margin: 12px 0; }

/* The field the refusal is about, marked. A sentence saying what is missing,
   with nothing pointing at which box it means, is a puzzle on top of a form. */
.guided-step-field.is-invalid input,
.guided-step-field.is-invalid select,
.guided-step-field.is-invalid textarea {
  border-color: #b91c1c;
  outline: 2px solid rgba(185, 28, 28, 0.18);
  outline-offset: 1px;
}

/* A risk can threaten more than one date, so this one step takes several
   answers - laid out as a list rather than a row, because milestone names are
   sentences and wrapped checkboxes read as one long line of text. */
.guided-checkbox-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 260px;
  overflow-y: auto;
}

.guided-checkbox {
  align-items: baseline;
  display: flex;
  font-size: 0.9rem;
  gap: 8px;
}

/* Radio and checkbox rows inside a guided step.
   ---------------------------------------------------------------------------
   THEY WERE RUNNING TOGETHER ON ONE LINE, in production, on the first screen
   of the notes review - the one that asks whether to walk through the changes
   or apply them blind. Both options read as a single paragraph: "Take me
   through them one at a time - see each change, the reason behind it, and
   decide. Apply everything without reviewing". Somebody skimming that cannot
   see there are two choices, let alone which one is selected.

   The cause was that .checkbox-row is styled ONLY under .entity-editor-form
   and .risk-milestone-checklist, and a guided step is neither - so the label
   fell back to display: inline, which is what a <label> is. Measured at
   display: "inline" in a browser rather than guessed.

   Scoped to .guided-step-field rather than fixing the seventeen call sites,
   because every guided step that ever uses a checkbox row wants exactly this
   and one of the seventeen would otherwise be missed. Geometry deliberately
   matches .guided-checkbox above, so the two patterns are indistinguishable
   on screen and nobody has to know which one a given step happened to use.

   align-items: flex-start rather than baseline: an option whose description
   wraps to a second line should keep its control beside the FIRST line, not
   floated against the middle of the block. */
.guided-step-field label.checkbox-row {
  align-items: flex-start;
  display: flex;
  font-size: 0.9rem;
  gap: 10px;
  line-height: 1.45;
  padding: 6px 0;
}

.guided-step-field label.checkbox-row + label.checkbox-row {
  /* A hairline between the options, so two stacked rows read as two choices
     rather than as one block of text that happens to have circles in it. */
  border-top: 1px solid var(--line, #e2e8f0);
}

.guided-step-field label.checkbox-row input {
  /* Fixed against the text's own line box so it cannot drift as the
     description wraps, and never shrunk by the flex row. */
  flex: 0 0 auto;
  margin: 0;
  margin-top: 0.2em;
}

/* A description of what went wrong is a sentence or two, and a one-line box
   asks for four words. */
.guided-step-field textarea.admin-field-input {
  line-height: 1.5;
  min-height: 96px;
  resize: vertical;
  width: 100%;
}

.guided-choice-buttons { display: flex; gap: 10px; flex-wrap: wrap; }

.guided-step-error { color: #b91c1c; }

/* Pinned to the bottom of the panel's scrolling body.
   ---------------------------------------------------------------------------
   The actions are rendered inside the body (openGuidedFlow writes one block of
   HTML), and .floating-detail-panel-body is the scroll container - so on a
   laptop-height window a step with a long explanation pushed its own Continue
   button below the fold. It was still reachable, but only by discovering that
   the dialog scrolls, which is a thing to discover before you can answer a
   question you were just asked.

   Sticky rather than restructuring the panel into a header/body/footer: the
   panel type is shared with the read-alongside shelves, and this is one rule
   against a change to all of them.

   The background is the panel's own token, so the bar is opaque in both themes -
   a transparent one shows the question scrolling underneath the buttons. */
.guided-step-actions {
  display: flex;
  gap: 10px;
  align-items: center;
  flex-wrap: wrap;
  margin-top: 16px;

  /* Above the owner picker's suggestion list.
     ------------------------------------------------------------------------
     That list is position: absolute at z-index 30, and in a flow the field is
     often the last thing before this bar - so typing a name dropped a panel of
     suggestions squarely over Continue, and the step could not be completed
     without first dismissing a list the reader had asked for. Measured with
     elementFromPoint at the button's own centre: the top element was an
     .owner-picker-option, not the button.

     The bar wins rather than the list, because a suggestion hidden behind it
     can still be reached by scrolling or by typing more, whereas a Continue
     button that cannot be clicked ends the flow. */
  z-index: 31;

  background: var(--panel-strong);
  border-top: 1px solid var(--line);
  bottom: 0;
  padding-top: 12px;
  position: sticky;
}

/* Back sits away from the two that commit, so the one that undoes is never
   next to the one that proceeds. */
.guided-step-actions [data-guided-back] { margin-right: auto; }

/* The review, read as a diff.
   ===========================================================================
   Left is what the plan says now, right is what the notes propose. The
   vocabulary is deliberately git's, because that is the thing people already
   know how to read: a before column, an after column, and a decision per row.

   THE PALETTE IS THE APP'S, NOT GIT'S. The first version used #fdf2f2 and
   #f0fbf4 - cool, saturated, and lifted straight from a diff viewer. Against
   this app's warm neutrals (--canvas #f2f1ef, --pill #ececea) they read as
   something pasted in from another product, which is most of what made the
   screen look unfinished. These are warmed to sit in the same family, and
   desaturated: the tint marks which side of the change a cell is on, it does
   not shout about it.

   COLOUR IS NEVER THE ONLY SIGNAL. The before column is struck through, the
   after column carries a rule down its inside edge, the chip names the kind of
   change in words, and the decision is a real checkbox. */
.plan-diff {
  --diff-before: #faf4f2;
  --diff-before-ink: #8c3b32;
  --diff-before-rule: #e6cdc6;
  --diff-after: #f4f8f2;
  --diff-after-ink: #35633f;
  --diff-after-rule: #cadfc9;
}

.plan-diff-toolbar {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  margin-bottom: 14px;
}

/* Wide content in a fixed-width panel. Without this the panel itself grows
   past the window on a laptop and the Apply button goes with it. */
.plan-diff-table {
  border-collapse: separate;
  border-spacing: 0;
  display: block;
  max-height: 52vh;
  overflow: auto;
  width: 100%;
}

/* Fixed proportions rather than letting the browser divide by content. Left to
   itself it gave the names 185px and wrapped "A risk in those notes" onto three
   lines while the After column had room to spare. */
.plan-diff-table col.plan-diff-col-accept { width: 44px; }
.plan-diff-table col.plan-diff-col-what { width: 26%; }
.plan-diff-table col.plan-diff-col-before { width: 24%; }
.plan-diff-table col.plan-diff-col-after { width: auto; }
.plan-diff-table col.plan-diff-col-more { width: 96px; }

.plan-diff-table thead th {
  background: var(--surface, #fff);
  border-bottom: 1px solid var(--line, #e2e8f0);
  color: var(--muted, #64748b);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 0 10px 8px;
  position: sticky;
  text-align: left;
  text-transform: uppercase;
  /* The header stays put while the rows scroll - on eleven changes the column
     meanings are otherwise gone by the third row. */
  top: 0;
  z-index: 1;
}

.plan-diff-group + .plan-diff-group .plan-diff-row td {
  border-top: 1px solid var(--line, #e2e8f0);
}

.plan-diff-row td {
  padding: 12px 10px;
  vertical-align: top;
}

.plan-diff-what strong {
  display: block;
  font-size: 0.94rem;
  font-weight: 600;
  line-height: 1.35;
  margin-top: 6px;
}

.plan-diff-chip {
  border-radius: 999px;
  display: inline-block;
  font-size: 0.64rem;
  font-weight: 600;
  letter-spacing: 0.06em;
  padding: 3px 9px;
  text-transform: uppercase;
}

/* Muted enough to label without competing with the milestone name beside it. */
.plan-diff-chip-date { background: #eceef6; color: #414a70; }
.plan-diff-chip-risk { background: #f6efe2; color: #7a5a22; }
.plan-diff-chip-new { background: #e9f1ea; color: #35633f; }

.plan-diff-before {
  background: var(--diff-before);
  color: var(--diff-before-ink);
  font-size: 0.84rem;
  line-height: 1.6;
  /* Dates read as a column when their digits line up, and as a jumble when
     they do not. */
  font-variant-numeric: tabular-nums;
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}

.plan-diff-after {
  background: var(--diff-after);
  /* The rule carries the "this is the new side" meaning for anyone who cannot
     separate the two tints, and gives the block an edge rather than letting it
     bleed into the row. */
  box-shadow: inset 2px 0 0 var(--diff-after-rule);
  min-width: 240px;
}

/* A row that ADDS something has nothing to strike through. Striking "Not in
   the register" says that line is being removed, which is the opposite of what
   is happening - it is what will exist once the change is accepted. */
.plan-diff-absent {
  color: var(--muted, #64748b);
  /* inline-block, and that is the whole trick. text-decoration PROPAGATES to
     descendants rather than being inherited, so a child cannot switch it off
     with text-decoration: none - which is what the first attempt did, and the
     line stayed struck through. An inline-block establishes its own box and
     the parent's line-through stops at it. */
  display: inline-block;
  font-style: italic;
  text-decoration: none;
}

.plan-diff-move {
  color: var(--diff-after-ink);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  line-height: 1.5;
  margin: 8px 0 0;
}

.plan-diff-dates {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.plan-diff-dates label {
  display: flex;
  flex: 1 1 0;
  flex-direction: column;
  font-size: 0.68rem;
  gap: 4px;
  letter-spacing: 0.04em;
  /* min-width: 0, and without it these stacked. A flex item's default
     min-width is auto, which is the content's intrinsic width - and the input
     inside is width: 100% with padding, so the item refused to shrink below
     its natural size and wrapped instead. Two date fields then took three rows
     of height each, which is most of why a three-change table ran to 700px. */
  min-width: 0;
  text-transform: uppercase;
}

/* The native inputs were the most obviously unstyled thing on the screen -
   default borders, default height, digits that did not line up with the text
   above them. */
.plan-diff-after input[type="date"],
.plan-diff-text {
  background: var(--surface, #fff);
  border: 1px solid var(--line, #e2e8f0);
  border-radius: 8px;
  font: inherit;
  font-size: 0.86rem;
  font-variant-numeric: tabular-nums;
  padding: 7px 9px;
  width: 100%;
}

.plan-diff-after input[type="date"]:focus,
.plan-diff-text:focus {
  border-color: var(--diff-after-rule);
  outline: 2px solid color-mix(in srgb, var(--diff-after-ink) 25%, transparent);
  outline-offset: 1px;
}

/* Details, looking like something that opens. It was bare text with no
   affordance at all, on the far right where nothing suggested it was a
   control. */
.plan-diff-disclosure {
  align-items: center;
  background: none;
  border: 1px solid transparent;
  border-radius: 999px;
  color: var(--muted, #64748b);
  cursor: pointer;
  display: inline-flex;
  font: inherit;
  font-size: 0.78rem;
  gap: 5px;
  padding: 4px 10px;
}

.plan-diff-disclosure:hover {
  background: var(--pill, #ececea);
  color: var(--ink, #1f2937);
}

.plan-diff-caret {
  height: 12px;
  transition: transform 120ms ease;
  width: 12px;
}

@media (prefers-reduced-motion: reduce) {
  .plan-diff-caret { transition: none; }
}

.plan-diff-disclosure[aria-expanded="true"] .plan-diff-caret {
  transform: rotate(180deg);
}

.plan-diff-detail {
  background: var(--surface-soft, #f7f7f5);
  border-top: 1px solid var(--line, #e2e8f0);
  padding: 16px 14px 20px;
}

/* Declined, not hidden. A change that vanished when unticked would leave the
   reader unable to check what they had just turned down, or to change their
   mind without starting the review again. */
.plan-diff-group.is-declined .plan-diff-what,
.plan-diff-group.is-declined .plan-diff-before,
.plan-diff-group.is-declined .plan-diff-after {
  opacity: 0.4;
}

.plan-diff-group.is-declined .plan-diff-after {
  background: transparent;
  box-shadow: none;
}

.plan-diff-group.is-invalid .plan-diff-row td:first-child {
  box-shadow: inset 3px 0 0 #b42318;
}

.plan-diff-accept { padding-left: 4px; }
.plan-diff-tick { display: block; padding-top: 3px; }

/* Visually hidden, still announced. The accept and details columns have no
   visible header - a tick and a button are self-evident on screen and nameless
   to a screen reader. */
.sr-only {
  border: 0;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

@media (max-width: 720px) {
  /* Four columns of dates do not fit a phone. The table scrolls sideways
     inside its own box rather than taking the page with it. */
  .plan-diff-table { display: block; overflow-x: auto; }
}

/* Names the notes gave that this plan has no milestone for. Amber rather than
   red: nothing is wrong, the reader just has to say which milestone they meant
   or add it under the name the plan uses. */
.plan-notes-unmatched {
  background: var(--notice-bg, #fdf0e7);
  border: 1px solid var(--notice-line, #f2d9c6);
  border-radius: 8px;
  color: var(--notice-ink, #8a3f14);
  font-size: 0.84rem;
  line-height: 1.5;
  margin: 10px 0 0;
  padding: 10px 12px;
}

.plan-notes-unmatched p { margin: 0; }
.plan-notes-unmatched p + p { margin-top: 6px; }

/* What a connector sync changed. Same before/after vocabulary as the notes
   diff on purpose: a date that moved is the same event whether it arrived from
   a meeting or from Jira, and two ways of reading it would be two ways to
   misread it. Quieter than the notes table, because this reports what already
   happened rather than asking for a decision. */
.sync-diff {
  background: var(--surface-soft, #f7f7f5);
  border: 1px solid var(--line, #e2e8f0);
  border-radius: 10px;
  margin: 12px 0 0;
  padding: 12px 14px;
}

.sync-diff-when {
  color: var(--muted, #64748b);
  font-size: 0.78rem;
  margin: 0 0 8px;
}

.sync-diff-list {
  display: flex;
  flex-direction: column;
  gap: 10px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.sync-diff-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 10px;
  align-items: baseline;
  font-size: 0.86rem;
}

.sync-diff-chip {
  background: var(--pill, #ececea);
  border-radius: 999px;
  color: var(--muted, #64748b);
  font-size: 0.64rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 2px 8px;
}

.sync-diff-chip-new { background: #e9f1ea; color: #35633f; }

.sync-diff-field,
.sync-diff-kept {
  font-size: 0.8rem;
  font-variant-numeric: tabular-nums;
}

.sync-diff-field s { color: #8c3b32; }
.sync-diff-field b { color: #35633f; font-weight: 600; }

/* A field a person edited that Jira now disagrees with. Amber: nothing is
   wrong, but it is the one line somebody might want to act on. */
.sync-diff-kept {
  color: var(--notice-ink, #8a3f14);
}

/* The one thing on this row that is asking something of the reader.
   ---------------------------------------------------------------------------
   Quiet on purpose. The change log's job is still to be read - a row of loud
   buttons would turn a list somebody scans into a list somebody has to process,
   and most rows are not asking anything. This is a link that happens to be a
   button, pushed to the end of the row so the change itself is what the eye
   lands on first. `margin-left: auto` rather than a fixed gap because the row
   wraps, and on a narrow panel it should fall to its own line rather than
   squeezing the dates. */
.sync-diff-explain {
  background: none;
  border: 0;
  border-bottom: 1px solid currentColor;
  border-radius: 0;
  color: var(--accent-brand);
  cursor: pointer;
  font: inherit;
  font-size: 0.78rem;
  margin-left: auto;
  padding: 0;
}

.sync-diff-explain:hover { opacity: 0.75; }

.sync-history {
  margin-top: 12px;
}

.sync-history > summary {
  color: var(--muted, #64748b);
  cursor: pointer;
  font-size: 0.82rem;
}

/* A removal row reads as a subtraction: the "after" is an absence, so it is
   struck and quiet rather than shouting in red. The unticked box is what
   carries the caution - colour on top of that would make every removal look
   like an error the product had found. */
.plan-diff-removed {
  color: var(--muted, #64748b);
  text-decoration: line-through;
}

.plan-diff-quote {
  font-style: italic;
}

/* What theruKO can clear for you now.
   ---------------------------------------------------------------------------
   Rows rather than cards: this sits above a text box the reader is about to
   type in, and three bordered cards there would read as the main event rather
   than as suggestions for it. The fact is quieter than the title because the
   title is what they recognise and the fact is why it is here. */
.ai-quick-wins {
  border-bottom: 1px solid var(--line);
  margin-bottom: 10px;
  padding-bottom: 10px;
}

/* In the chat the box below already draws the edge; a second rule directly
   above it read as a gap in the page rather than a boundary. */
.ai-chat .ai-quick-wins {
  border-bottom: 0;
  margin-bottom: 0;
  padding-bottom: 0;
}

.ai-quick-wins-head {
  color: var(--muted, #64748b);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  margin: 0 0 6px;
  text-transform: uppercase;
}

.ai-quick-wins-list {
  display: grid;
  gap: 4px;
  list-style: none;
  margin: 0 0 6px;
  padding: 0;
}

.ai-quick-win {
  align-items: baseline;
  background: transparent;
  border: 0;
  border-radius: 8px;
  /* INHERIT, because styles.css's global `button` rule sets `color: white` for
     filled accent buttons - and this rule cleared that rule's background and
     border while leaving its colour behind. The result was a white title on a
     white panel: the row's own name, the thing you recognise it by, invisible,
     with only the grey fact line underneath it showing.

     Inherit rather than a token so it follows the list's colour in both themes. */
  color: inherit;
  cursor: pointer;
  /* ONE LINE: the name, then why it is here. The same global rule's 42px
     minimum height and bold weight are a filled button's shape, reset here
     once rather than fought one child at a time. */
  display: flex;
  font-weight: 400;
  gap: 8px;
  min-height: 0;
  overflow: hidden;
  padding: 6px 8px;
  text-align: left;
  white-space: nowrap;
  width: 100%;
}

.ai-quick-win:hover,
.ai-quick-win:focus-visible {
  background: color-mix(in srgb, var(--accent-brand) 10%, transparent);
  outline: none;
}

.ai-quick-win-title {
  /* Does not shrink until it reaches its cap - shrinking both halves together
     cut "Ledger freeze" to "Ledger fre..." while the reason beside it kept
     room it did not need. The name is what the row is recognised by. */
  flex: 0 0 auto;
  font-size: 0.86rem;
  font-weight: 600;
  /* The name gives way before the reason does not: past this it ellipsises,
     so a long milestone name cannot push "2 days overdue" off the row. */
  max-width: 62%;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ai-quick-win-fact {
  color: var(--muted, #64748b);
  flex: 1 1 0;
  font-size: 0.78rem;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

.ai-ask-paste {
  margin-right: auto;
}

/* ---------------------------------------------------------------------------
   theruKO: taps that leave no stain.

   styles.css gives every <button> a dark navy gradient on :hover, and
   `button:hover` (an element plus a pseudo-class) outranks a single class like
   .ai-reading-toggle - so `background: none` on the class lost the moment the
   pointer was over it. On a phone :hover STICKS after a tap, which turned the
   line somebody had just pressed into a navy bar with dark text on it, the
   exact moment they were trying to read it. Each control here says what its
   hover, press and focus look like, at a specificity that holds.
   ------------------------------------------------------------------------ */
.ai-reading-toggle:hover,
.ai-reading-toggle:active,
.ai-reading-toggle:focus-visible,
.ai-reading-scope:hover,
.ai-reading-scope:active,
.ai-reading-scope:focus-visible,
.ai-ask-send:hover,
.ai-ask-send:active,
.ai-applied-item.is-gone,
.ai-applied-item.is-gone:hover {
  background: none;
  box-shadow: none;
  transform: none;
}

/* The pressed line is marked the way the rest of the chat marks things - a
   faint wash of the brand, legible in both themes - not by a colour swap. */
.ai-chat .ai-chat-theirs:has(.ai-reading-toggle:hover),
.ai-chat .ai-chat-theirs:has(.ai-reading-toggle:focus-visible) {
  background: color-mix(in srgb, var(--accent-brand) 9%, transparent);
}

.ai-reading-toggle:focus-visible {
  outline: 2px solid var(--accent-brand);
  outline-offset: 2px;
}

/* A folded answer: what it was on the first line, its facts under it.
   The chevron takes the first column so both lines start at the same edge. */
.ai-chat .ai-chat-theirs .ai-reading-toggle {
  align-items: center;
  column-gap: 8px;
  display: grid;
  grid-template-columns: 8px minmax(0, 1fr);
  row-gap: 1px;
}

.ai-chat .ai-chat-theirs .ai-reading-toggle::before {
  grid-column: 1;
  grid-row: 1;
  margin: 0;
}

.ai-chat .ai-chat-theirs .ai-reading-summary,
.ai-chat .ai-chat-theirs .ai-reading-facts {
  grid-column: 2;
}

.ai-reading-facts {
  align-items: baseline;
  color: var(--muted, #64748b);
  display: flex;
  flex-wrap: wrap;
  font-size: 0.72rem;
  gap: 2px 8px;
  min-width: 0;
}

/* Beside the time on the second line, the size reads as a plain fact; a pill
   there was a second thing to look at on a line meant to be skimmed. */
.ai-chat .ai-reading-facts .ai-reading-size {
  background: none;
  font-size: inherit;
  padding: 0;
}

.ai-chat .ai-reading-facts .ai-reading-size::after {
  content: "\00b7";
  margin-left: 8px;
}

.ai-chat .ai-reading-facts .ai-reading-when {
  font-size: inherit;
}

/* Only there while it has something to say. */
.ai-ask-status {
  margin: 0;
}

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

/* The way to paste notes or a whole plan: a real control, with a picture of
   what it takes, rather than a line of green link text that read as a caption. */
.ai-ask-actions .ai-ask-paste {
  align-items: center;
  background: var(--panel, #fff);
  border: 1px solid var(--line);
  border-radius: 999px;
  box-shadow: none;
  color: var(--ink);
  cursor: pointer;
  display: inline-flex;
  font-size: 0.82rem;
  font-weight: 500;
  gap: 7px;
  margin-right: 0;
  min-height: 0;
  padding: 6px 12px 6px 10px;
  text-align: left;
}

.ai-ask-actions .ai-ask-paste svg {
  color: var(--accent-brand);
  flex: none;
}

.ai-ask-actions .ai-ask-paste:hover,
.ai-ask-actions .ai-ask-paste:focus-visible {
  background: color-mix(in srgb, var(--accent-brand) 9%, var(--panel, #fff));
  border-color: color-mix(in srgb, var(--accent-brand) 45%, var(--line));
  box-shadow: none;
  color: var(--ink);
  transform: none;
}

.ai-ask-actions .ai-ask-paste:focus-visible {
  outline: 2px solid var(--accent-brand);
  outline-offset: 2px;
}

.ai-ask-actions .ai-ask-paste:active {
  transform: none;
}

/* ---------------------------------------------------------------------------
   theruKO: the report that answers the same question, offered under the answer.
   A quiet band inside the reply rather than a second card - it belongs to the
   answer it follows. The build control is a pill like the paste one, with its
   hover/press stated at a specificity that beats `button:hover` in styles.css.
   ------------------------------------------------------------------------ */
.ai-report-offer {
  align-items: center;
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 8px 12px;
  margin-top: 10px;
  padding-top: 10px;
}

.ai-report-offer .ai-report-offer-text {
  color: var(--ink);
  flex: 1 1 100%;
  font-size: 0.86rem;
  margin: 0;
}

.ai-report-offer .ai-report-offer-eyebrow {
  color: var(--accent-brand);
  flex: 1 1 100%;
  font-size: 0.74rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  margin: 0;
}

.ai-report-offer .ai-report-offer-status {
  flex: 1 1 100%;
  margin: 0;
}

.ai-report-offer button.ai-report-offer-build {
  align-items: center;
  background: var(--accent-brand);
  border: 1px solid var(--accent-brand);
  border-radius: 999px;
  box-shadow: none;
  color: var(--panel, #fff);
  display: inline-flex;
  font-size: 0.82rem;
  font-weight: 500;
  gap: 6px;
  min-height: 0;
  padding: 6px 14px;
}

.ai-report-offer button.ai-report-offer-build:hover,
.ai-report-offer button.ai-report-offer-build:focus-visible {
  background: color-mix(in srgb, var(--accent-brand) 86%, var(--ink));
  color: var(--panel, #fff);
}

.ai-report-offer button.ai-report-offer-build:disabled {
  opacity: 0.6;
}

.ai-report-offer .ai-report-offer-link {
  color: var(--accent-brand);
  font-size: 0.86rem;
  font-weight: 500;
}

/* theruKO: "Ask next" - questions that carry the thread on after an answer.
   Chips rather than rows: they are offers to continue, not a worklist. The
   hover is stated at a specificity that beats `button:hover` in styles.css. */
.ai-follow-ups {
  display: grid;
  gap: 6px;
  margin: 0 0 8px;
}

.ai-follow-ups .ai-follow-ups-label {
  color: var(--muted, var(--ink));
  font-size: 0.74rem;
  font-weight: 600;
  margin: 0;
}

.ai-follow-ups .ai-follow-ups-row {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.ai-follow-ups button.ai-follow-up {
  background: var(--panel, #fff);
  border: 1px solid var(--line);
  border-radius: 999px;
  box-shadow: none;
  color: var(--ink);
  cursor: pointer;
  font-size: 0.8rem;
  font-weight: 500;
  line-height: 1.3;
  min-height: 0;
  padding: 5px 11px;
  text-align: left;
}

.ai-follow-ups button.ai-follow-up:hover,
.ai-follow-ups button.ai-follow-up:focus-visible {
  background: color-mix(in srgb, var(--accent-brand) 9%, var(--panel, #fff));
  border-color: var(--accent-brand);
  color: var(--ink);
}

/* theruKO speaking first: the news it opens a project with, and the one next
   step it offers. A plain list - this is computed from the plan, not written
   by a model, so it wears no AI chip. */
.ai-opener .ai-opener-list {
  display: grid;
  gap: 4px;
  margin: 6px 0 0;
  padding-left: 18px;
}

.ai-opener .ai-opener-list li {
  color: var(--ink);
  font-size: 0.86rem;
  line-height: 1.4;
}

.ai-opener .ai-opener-more {
  color: var(--muted, var(--ink));
  list-style: none;
  margin-left: -18px;
}

.ai-opener .ai-opener-actions {
  margin-top: 10px;
}

.ai-opener button.ai-report-prepare {
  background: var(--panel, #fff);
  border: 1px solid var(--accent-brand);
  border-radius: 999px;
  box-shadow: none;
  color: var(--accent-brand);
  font-size: 0.82rem;
  font-weight: 500;
  min-height: 0;
  padding: 6px 14px;
}

.ai-opener button.ai-report-prepare:hover,
.ai-opener button.ai-report-prepare:focus-visible {
  background: color-mix(in srgb, var(--accent-brand) 9%, var(--panel, #fff));
  color: var(--accent-brand);
}

/* The collapsed handle, when theruKO has something to say. */
.ai-drawer-tab.has-news::after {
  background: var(--accent-brand);
  border-radius: 999px;
  content: "";
  height: 8px;
  position: absolute;
  right: 6px;
  top: 6px;
  width: 8px;
}

/* theruKO's suggested next step: grey text in the empty box, and one quiet
   control to take it (Tab does the same). Styled like the paste pill beside
   it, with its hover stated above `button:hover` in styles.css. */
.ai-ask-actions button.ai-ask-use-suggestion {
  background: var(--panel, #fff);
  border: 1px solid var(--accent-brand);
  border-radius: 999px;
  box-shadow: none;
  color: var(--accent-brand);
  cursor: pointer;
  font-size: 0.82rem;
  font-weight: 500;
  min-height: 0;
  padding: 6px 12px;
}

.ai-ask-actions button.ai-ask-use-suggestion:hover,
.ai-ask-actions button.ai-ask-use-suggestion:focus-visible {
  background: color-mix(in srgb, var(--accent-brand) 9%, var(--panel, #fff));
  color: var(--accent-brand);
}

.ai-ask-actions button.ai-ask-use-suggestion[hidden] {
  display: none;
}

/* theruKO's answer as one card with numbered parts, and the grey suggestion
   in the box wearing the same mark - so "here is what I found" and "here is
   what you could do" read as a pair across the column. The bar and the "1"
   are the link: the same accent, in both places. */
.ai-step-mark {
  align-items: center;
  background: var(--accent-brand);
  border-radius: 999px;
  color: var(--panel, #fff);
  display: inline-flex;
  flex: none;
  font-size: 0.68rem;
  font-weight: 700;
  height: 16px;
  justify-content: center;
  width: 16px;
}

.ai-step-mark.is-second {
  background: var(--muted, var(--ink));
}

.ai-answer-part-label {
  align-items: center;
  color: var(--ink);
  display: flex;
  font-size: 0.74rem;
  font-weight: 600;
  gap: 6px;
  letter-spacing: 0.02em;
  margin: 0;
}

.ai-answer-step {
  border-left: 3px solid var(--accent-brand);
  border-top: 1px solid var(--line);
  display: grid;
  gap: 6px;
  justify-items: start;
  margin-top: 10px;
  padding: 10px 0 2px 10px;
  transition: background-color 0.4s ease;
}

.ai-answer-step .ai-answer-step-text {
  color: var(--ink);
  font-size: 0.86rem;
  line-height: 1.4;
  margin: 0;
}

.ai-answer-step button.ai-answer-step-use {
  background: var(--panel, #fff);
  border: 1px solid var(--accent-brand);
  border-radius: 999px;
  box-shadow: none;
  color: var(--accent-brand);
  font-size: 0.8rem;
  font-weight: 500;
  min-height: 0;
  padding: 4px 12px;
}

.ai-answer-step button.ai-answer-step-use:hover,
.ai-answer-step button.ai-answer-step-use:focus-visible {
  background: color-mix(in srgb, var(--accent-brand) 9%, var(--panel, #fff));
  color: var(--accent-brand);
}

.ai-answer-step button.ai-answer-step-use[hidden],
.ai-answer-step .ai-answer-step-taken[hidden] {
  display: none;
}

.ai-answer-step .ai-answer-step-taken {
  color: var(--accent-brand);
  font-size: 0.8rem;
  font-weight: 600;
  margin: 0;
}

.ai-answer-step.is-flashed {
  animation: ai-step-flash 1.4s ease;
}

@keyframes ai-step-flash {
  0%, 40% { background: color-mix(in srgb, var(--accent-brand) 14%, transparent); }
  100% { background: transparent; }
}

@media (prefers-reduced-motion: reduce) {
  .ai-answer-step.is-flashed {
    animation: none;
    outline: 2px solid var(--accent-brand);
  }
}

/* Inside the card, the report offer is part 2 rather than a block of its own. */
.where-to-start-explain .ai-report-offer {
  margin-top: 10px;
}

/* The box, while a next step is waiting in it: the same bar as the answer's
   step, and the label that leads back to it. */
.ai-ask.has-suggestion .ai-ask-box {
  box-shadow: inset 3px 0 0 var(--accent-brand);
}

.ai-ask button.ai-ask-suggestion-source {
  align-items: center;
  background: none;
  border: 0;
  box-shadow: none;
  color: var(--accent-brand);
  cursor: pointer;
  display: inline-flex;
  font-size: 0.76rem;
  font-weight: 600;
  gap: 6px;
  justify-self: start;
  margin: 0 0 6px;
  min-height: 0;
  padding: 0;
  text-align: left;
  width: auto;
}

.ai-ask button.ai-ask-suggestion-source:hover,
.ai-ask button.ai-ask-suggestion-source:focus-visible {
  background: none;
  color: var(--accent-brand);
  text-decoration: underline;
}

/* ---------------------------------------------------------------------------
   What theruKO is talking about (see aiScopeKey in dashboard.js).
   Brand accent, never red: red here means late or a loss, and a subject change
   is neither. Every colour from tokens, so both themes follow. */

.ai-drawer-title-row {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  min-width: 0;
  position: relative;
}

.ai-drawer-scope {
  align-items: center;
  background: none;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--ink);
  cursor: pointer;
  display: inline-flex;
  font: inherit;
  font-size: 0.8rem;
  gap: 5px;
  max-width: 100%;
  min-width: 0;
  padding: 3px 10px;
}

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

.ai-drawer-scope:hover,
.ai-drawer-scope[aria-expanded="true"] {
  border-color: var(--accent-brand);
}

.ai-drawer-scope:focus-visible {
  outline: 2px solid var(--accent-brand);
  outline-offset: 2px;
}

.ai-drawer-scope-about {
  color: var(--muted);
}

.ai-drawer-scope-name {
  font-weight: 600;
  max-width: 22ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ai-drawer-scope-chevron {
  color: var(--muted);
  font-size: 0.7rem;
}

.ai-drawer-scope-menu {
  background: var(--panel-strong, var(--panel));
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 10px 28px color-mix(in srgb, var(--ink) 18%, transparent);
  display: grid;
  left: 0;
  max-height: 320px;
  min-width: 240px;
  overflow-y: auto;
  padding: 6px;
  position: absolute;
  top: calc(100% + 6px);
  z-index: 30;
}

.ai-drawer-scope-menu button {
  background: none;
  border: 0;
  border-radius: 6px;
  color: var(--ink);
  cursor: pointer;
  font: inherit;
  font-size: 0.85rem;
  padding: 7px 10px;
  text-align: left;
}

.ai-drawer-scope-menu button:hover,
.ai-drawer-scope-menu button:focus-visible {
  background: color-mix(in srgb, var(--accent-brand) 10%, transparent);
  outline: none;
}

.ai-drawer-scope-menu button[aria-current="true"] {
  color: var(--accent-brand);
  font-weight: 600;
}

/* "Now talking about X" - a rule across the conversation, where it changed,
   and the way back centred under it. */
.ai-chat-scope-change {
  align-items: center;
  color: var(--muted);
  display: flex;
  flex-direction: column;
  font-size: 0.8rem;
  gap: 4px;
  margin: 12px 0;
}

.ai-chat-scope-line {
  align-items: center;
  display: flex;
  gap: 10px;
  width: 100%;
}

.ai-chat-scope-rule {
  border-top: 1px solid color-mix(in srgb, var(--accent-brand) 45%, var(--line));
  flex: 1 1 16px;
  min-width: 16px;
}

.ai-chat-scope-text {
  flex: 0 1 auto;
  text-align: center;
}

.ai-chat-scope-change strong {
  color: var(--accent-brand);
}

/* Specific enough to beat the column's own button styling, which otherwise
   draws a pill around a link. */
.ai-chat-scope-change button.ai-chat-scope-back {
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  color: var(--accent-brand);
  cursor: pointer;
  font: inherit;
  min-height: 0;
  padding: 0;
  text-decoration: underline;
}

/* "About X" on a folded answer. */
.ai-reading-about {
  color: var(--accent-brand);
  font-size: 0.72rem;
  max-width: 24ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The top bar's name and the header chip, for a moment, when the subject
   changes - then back to normal. */
.is-scope-flash {
  animation: ai-scope-flash 2.2s ease;
  border-radius: 999px;
}

@keyframes ai-scope-flash {
  0%, 45% {
    background: color-mix(in srgb, var(--accent-brand) 18%, transparent);
    box-shadow: 0 0 0 2px var(--accent-brand);
  }
  100% {
    background: transparent;
    box-shadow: 0 0 0 0 transparent;
  }
}

@media (prefers-reduced-motion: reduce) {
  .is-scope-flash {
    animation: none;
    box-shadow: 0 0 0 2px var(--accent-brand);
  }
}
