/* Self-hosted (not a Google Fonts CDN link) - this app's CSP is deliberately
   strict with no external font-src/style-src origins allowed (see
   contentSecurityPolicy() in src/server.js), so the variable-weight Inter
   file is fetched and served from /fonts instead. font-family below already
   named "Inter" first, but with no matching @font-face it silently fell back
   to the OS system font - this is what makes that declaration actually apply. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url("/fonts/Inter-Variable-latin.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* The display face. One weight, self-hosted for the same CSP reason as Inter -
   see public/fonts/README.md, which also explains why it is 23KB rather than
   132KB and why nothing should ask it for a font-weight.

   Why a second face at all: Inter alone is the default every dashboard reaches
   for, and a product whose whole argument is that it keeps the record - the
   previous value and the reason for the change - can afford a title face with
   some authority to it. Newsreader is a screen-first serif: bookish without
   being antique, and it separates "what this page is" from "what is on it" by
   family rather than by making the heading heavier, which is what the app was
   doing before. */
@font-face {
  font-family: "Newsreader";
  font-style: normal;
  font-weight: 500;
  font-display: swap;
  src: url("/fonts/Newsreader-latin-500.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* The palette. One block, one place.
   ---------------------------------------------------------------------------
   This used to have a twin about 5,950 lines below it, commented "Executive
   design system": same `:root`, same specificity, later in the file, so it
   silently won for all eleven tokens the two shared. The block up here was dead
   text that still read like the source of truth, and the values it held had not
   rendered anything in a long time.

   Merged here at the twin's values, which is what has actually been on screen -
   so this change moves no pixel. What it does expose is that the literals
   written against the ORIGINAL values are orphans: `#d4d4d8` appears ~50 times
   as a border colour and used to be --line, but --line has been #d7dde5 since
   the twin landed. That is the two-slightly-different-greys effect on every
   screen, and those literals are substituted back onto the tokens rather than
   left to drift further.

   Anything that needs a different value in the dark theme is re-declared in
   dark-mode.css against `:root[data-theme="dark"]`, which is both later and more
   specific. Adding a colour here means checking whether it needs a counterpart
   there - a literal in a component is exactly how a component stops responding
   to the theme at all. */
:root {
  color-scheme: light;

  /* Surfaces */
  --bg: #ffffff;
  --panel: #f7f9fb;
  --panel-strong: #ffffff;

  /* Type */
  --ink: #11161d;
  --muted: #66717e;

  /* Rules and edges */
  --line: #d7dde5;
  --line-strong: #b6c0cc;

  /* Accent.
     --accent is the dark slate the buttons are built from - a neutral, not a
     hue. --accent-brand is the coloured highlight: the petrol that marks an
     active nav group, a hovered risk node, a scoped panel's edge.

     It replaces Tailwind's #2563eb in those places, and measures better where
     it lands: petrol on white is 7.09:1 against the blue's 5.17:1, and white on
     petrol is the same 7.09 (the account circle's contrast note is updated to
     match). Dark mode cannot use this value - petrol on #0d0f13 is 2.71:1 - so
     dark-mode.css carries a lightened counterpart at 6.73:1.

     It deliberately does NOT replace every blue. On the timeline, blue is the
     fork colour: .milestone-focus-legend pairs var(--theruka-green) for "main"
     against blue for "fork", and the gantt fork bar and track label follow it.
     Painting those petrol would collapse main and fork into one colour and make
     the ghost bars unreadable. The .gantt-row-color-blue swatch is likewise a
     user-chosen row colour that is named blue.

     It shares its light value with --theruka-green today. They are different
     roles - one is brand accent, one is the "main milestone" semantic - so they
     are separate tokens and their dark values already differ. */
  --accent: #24303d;
  --accent-strong: #111820;
  --accent-soft: #e8edf2;
  --accent-brand: #14635a;
  --focus: #657a92;

  /* Conversation bubbles */
  --sent: #edf2f7;
  --received: #ffffff;

  /* Semantic tints.
     Three families, each with the tiers that were actually in use: a wash to
     sit a panel on, a line to edge it, a surface for a filled chip, and a
     strong value for ink and solid fills.

     These consolidate values that were already decided and then written four
     ways - #fbebe9, #fdf3f2, #fdf2f2 and #fef2f2 were all the same pale alert
     background, within deltaE 2.1 of each other. Only near-identical values
     were merged: members share a bucket's hue to within 12 degrees and a
     comparable chroma, because at these lightnesses deltaE alone will happily
     call an orange wash and a yellow wash the same colour.

     --warn already existed and every deep amber ink in the file sat within
     deltaE 4.2 of it, so they fold onto it rather than onto a rival token.

     LIGHT VALUES ONLY for now. A literal does not flip in dark mode either, so
     this changes nothing there - the ~48 live rules in dark-mode.css still do
     that job. Giving these tokens dark counterparts is the step that retires
     those rules, and it is a design decision rather than a rename. */
  --warn: #7a4b10;
  --warn-surface: #fde68a;
  --warn-line: #f0d3a4;
  --warn-wash: #fffbeb;

  --danger-strong: #b91c1c;
  --danger-surface: #fee2e2;
  --danger-line: #f0cfcf;
  --danger-wash: #fbebe9;

  --good-line: #2f7d55;

  /* Text-weight green, the partner of --danger-strong. Was referenced as an

     undefined --success, so it only ever rendered its fallback and never

     followed the theme. 5.02:1 on white. */

  --good-strong: #1a7f4b;
  --good-wash: #f0f7f2;

  --shadow-resting: 0 1px 3px rgba(17, 22, 29, 0.06), 0 1px 2px rgba(17, 22, 29, 0.04);
  --shadow-hover: 0 10px 24px rgba(17, 22, 29, 0.10), 0 2px 6px rgba(17, 22, 29, 0.06);
  --shadow-modal: 0 32px 70px rgba(17, 22, 29, 0.24), 0 8px 20px rgba(17, 22, 29, 0.12);
  --shadow-soft: 0 1px 2px rgba(17, 22, 29, 0.05), 0 14px 32px rgba(17, 22, 29, 0.06);
  --shadow-raised: 0 20px 54px rgba(17, 22, 29, 0.18);

  --radius-shell: 30px;
  --radius-card: 22px;
  --radius-tile: 14px;
  --radius-control: 8px;
  --radius-pill: 999px;

  font-family: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
}

* {
  box-sizing: border-box;
}

/* Titles read as titles through size, not boldness - this is the fallback
   for any heading that doesn't set its own font-weight below; headings that
   previously used a heavy explicit weight (700-900) have that weight lowered
   and their font-size increased at their own selector instead. */
h1,
h2,
h3,
h4,
h5,
h6 {
  font-weight: 600;
}

body {
  margin: 0;
  min-height: 100vh;
  background: var(--bg);
  color: var(--ink);
}

.site-footer {
  align-items: center;
  background: #ffffff;
  border-top: 1px solid var(--line);
  color: var(--muted);
  display: flex;
  font-size: 0.78rem;
  font-weight: 700;
  justify-content: center;
  line-height: 1.4;
  min-height: 42px;
  padding: 10px 18px;
  text-align: center;
}

.app-page-footer {
  background: var(--panel);
}

.image-copyright,
.copyrighted-image figcaption {
  color: rgba(255, 255, 255, 0.92);
  font-size: 0.68rem;
  font-weight: 800;
  line-height: 1.2;
}

.image-copyright-overlay {
  background: rgba(6, 33, 63, 0.72);
  border-radius: 6px;
  bottom: 12px;
  left: 12px;
  padding: 7px 9px;
  position: absolute;
  z-index: 3;
}

.copyrighted-image {
  display: grid;
  margin: 0;
  position: relative;
}

.copyrighted-image figcaption {
  background: rgba(6, 33, 63, 0.72);
  border-radius: 6px;
  bottom: 10px;
  left: 10px;
  padding: 7px 9px;
  position: absolute;
  right: 10px;
}

button,
input,
select,
textarea {
  font: inherit;
}

button {
  border: 0;
  border-radius: 6px;
  background: var(--accent);
  color: white;
  cursor: pointer;
  font-weight: 700;
  min-height: 42px;
  padding: 0 16px;
}

button:hover {
  background: var(--accent-strong);
}

.app-shell {
  display: grid;
  grid-template-columns: 300px minmax(0, 1fr) 380px;
  min-height: 100vh;
}

.input-panel {
  background: var(--panel);
  border-right: 1px solid var(--line);
  display: grid;
  grid-template-rows: auto auto auto 1fr;
  min-height: 100vh;
}

.chat-panel {
  display: grid;
  grid-template-rows: auto minmax(0, 1fr);
  min-height: 100vh;
}

.top-bar,
.chat-header,
.side-header {
  align-items: center;
  border-bottom: 1px solid var(--line);
  display: flex;
  gap: 20px;
  justify-content: space-between;
  padding: 18px 22px;
}

.top-bar {
  background: #27272a;
  color: white;
}

.chat-header {
  background: var(--panel-strong);
}

.eyebrow {
  color: inherit;
  font-size: 0.75rem;
  font-weight: 800;
  letter-spacing: 0;
  margin: 0 0 4px;
  opacity: 0.72;
  text-transform: uppercase;
}

h1,
h2 {
  font-size: 1.34rem;
  line-height: 1.2;
  margin: 0;
}

.user-control {
  align-items: center;
  display: flex;
  gap: 8px;
}

.user-control label {
  font-size: 0.85rem;
  font-weight: 700;
  opacity: 0.8;
}

.user-control input {
  background: rgba(255, 255, 255, 0.13);
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 6px;
  color: white;
  min-height: 38px;
  padding: 0 10px;
  width: 180px;
}

.messages {
  align-content: start;
  display: grid;
  gap: 10px;
  overflow-y: auto;
  padding: 22px;
}

.message {
  border: 1px solid var(--line);
  border-left: 4px solid #a1a1aa;
  border-radius: 6px;
  box-shadow: 0 1px 2px rgba(24, 24, 27, 0.04);
  max-width: min(760px, 96%);
  padding: 12px 14px;
  white-space: pre-wrap;
}

.message.user {
  background: var(--sent);
  border-left-color: #52525b;
  justify-self: stretch;
}

.message.bot {
  background: var(--received);
  justify-self: stretch;
}

.message.system {
  background: var(--warn-wash);
  color: var(--warn);
  justify-self: center;
  max-width: min(720px, 96%);
}

.composer {
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  display: grid;
  gap: 10px;
  padding: 14px;
}

.composer label {
  color: var(--muted);
  font-size: 0.8rem;
  font-weight: 800;
}

.composer select,
.composer textarea {
  border: 1px solid var(--line);
  border-radius: 6px;
  /* background-COLOR: the shorthand would drop the select's chevron. */
  background-color: var(--panel-strong);
  color: var(--ink);
  padding: 12px;
  width: 100%;
}

.composer select {
  min-height: 42px;
}

.composer textarea {
  min-height: 180px;
  resize: vertical;
}

.composer button {
  width: 100%;
}

.side-panel {
  background: var(--panel);
  border-left: 1px solid var(--line);
  display: grid;
  grid-template-rows: auto auto 1fr;
  min-height: 100vh;
}

.side-header {
  align-items: start;
}

.side-header button {
  background: #52525b;
  min-height: 38px;
}

.quick-start {
  border-bottom: 1px solid var(--line);
  display: grid;
  gap: 8px;
  padding: 12px;
}

.sample-button {
  background: #e4e4e7;
  color: var(--ink);
  flex: 1;
  min-height: 38px;
}

.tester-links {
  border-bottom: 1px solid var(--line);
  display: grid;
  gap: 8px;
  padding: 12px;
}

.tester-links a {
  color: var(--accent-strong);
  font-weight: 800;
  text-decoration: none;
}

/* The class four things already used and nothing defined.
   On a <button> it was landing on the generic button styling and looked right by
   accident; on an <a> it was a bare blue underlined link sitting next to real
   buttons, which is what "Open review" on the Reports page looked like. Written
   element-agnostically so a link and a button are indistinguishable. */
.primary-button {
  align-items: center;
  background: var(--accent);
  border: 0;
  border-radius: 6px;
  color: #ffffff;
  cursor: pointer;
  display: inline-flex;
  font-weight: 700;
  justify-content: center;
  min-height: 42px;
  padding: 0 16px;
  text-decoration: none;
  white-space: nowrap;
}

.primary-button:hover {
  background: var(--accent-strong);
  color: #ffffff;
}

.secondary-button {
  background: #e4e4e7;
  color: var(--ink);
}

.secondary-button:hover {
  background: #d4d4d8;
}

.dashboard-shell {
  display: grid;
  grid-template-columns: 340px minmax(0, 1fr);
  min-height: 100vh;
}

.dashboard-chat {
  background: var(--panel);
  border-right: 1px solid var(--line);
  display: grid;
  grid-template-rows: auto minmax(0, 1fr) auto;
  min-height: 100vh;
}

.dashboard-chat-header {
  align-items: center;
  background: #27272a;
  color: white;
  display: flex;
  justify-content: space-between;
  padding: 18px 20px;
}

.dashboard-chat-header a {
  color: white;
  font-weight: 800;
  text-decoration: none;
}

.dashboard-chat-messages {
  padding: 10px;
}

.dashboard-composer {
  border-bottom: 0;
  border-top: 1px solid var(--line);
}

.dashboard-composer textarea {
  min-height: 92px;
}

.dashboard-main {
  display: grid;
  grid-template-rows: auto auto minmax(0, 1fr);
  min-height: 100vh;
}

.dashboard-header {
  align-items: center;
  background: var(--panel-strong);
  border-bottom: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  padding: 18px 22px;
}

.dashboard-actions {
  display: flex;
  gap: 8px;
  margin-left: auto;
}

.executive-dashboard-shell button {
  font-size: 0.82rem;
  min-height: 34px;
  padding: 0 11px;
}

.plan-upload-panel {
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  display: grid;
  gap: 12px;
  padding: 16px 22px;
}

.upload-row {
  align-items: stretch;
  display: grid;
  gap: 8px;
  grid-template-columns: minmax(0, 1fr) auto;
}

.file-drop {
  align-items: center;
  background: var(--panel-strong);
  border: 1px dashed #a1a1aa;
  border-radius: 8px;
  color: var(--muted);
  cursor: pointer;
  display: flex;
  font-weight: 800;
  min-height: 38px;
  padding: 0 11px;
}

.file-drop input {
  display: none;
}

.plan-text-label {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 800;
}

.plan-text-input {
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink);
  font-size: 0.84rem;
  line-height: 1.35;
  padding: 9px;
  resize: vertical;
  width: 100%;
}

.plan-workspace {
  overflow-y: auto;
  padding: 18px 22px 28px;
}

.plan-empty {
  border: 1px dashed var(--line);
  border-radius: 8px;
  display: grid;
  gap: 8px;
  min-height: 180px;
  padding: 18px;
  place-content: center;
  text-align: center;
}

.plan-empty h2 {
  color: var(--muted);
  font-size: 0.95rem;
  font-weight: 700;
  max-width: 560px;
}

.plan-overview {
  align-items: center;
  background: #f4f4f5;
  border: 1px solid var(--line);
  border-radius: 8px;
  display: flex;
  justify-content: space-between;
  margin-bottom: 14px;
  padding: 16px;
}

.plan-overview h2 {
  font-size: 1.4rem;
}

.health-score {
  align-items: end;
  display: grid;
  gap: 2px;
  text-align: right;
}

.health-score strong {
  font-size: 1.35rem;
  line-height: 1;
}

.health-score span {
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 800;
}

.plan-grid {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.plan-section {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 128px;
  padding: 10px;
}

.plan-section h3 {
  font-size: 0.88rem;
  margin: 0 0 7px;
}

.plan-section ul {
  display: grid;
  gap: 6px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.plan-section li {
  border-top: 1px solid var(--line);
  color: var(--muted);
  display: grid;
  font-size: 0.8rem;
  gap: 2px;
  line-height: 1.28;
  padding-top: 6px;
}

.plan-section li:first-child {
  border-top: 0;
  padding-top: 0;
}

.plan-section li strong {
  color: var(--ink);
}

.plan-section li span {
  color: var(--muted);
  font-size: 0.74rem;
}

.attention-item-heading {
  align-items: center;
  display: flex;
  gap: 6px;
}

.attention-item-heading .project-menu-icon {
  flex: 0 0 auto;
  height: 14px;
  width: 14px;
}

.attention-item-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 4px;
}

.attention-item-actions button {
  font-size: 0.72rem;
  min-height: 34px;
  padding: 0 10px;
}

.executive-dashboard-shell {
  background: var(--bg);
  font-size: 0.84rem;
  grid-template-columns: 300px minmax(0, 1fr) 300px;
}

body.executive-view-mode .executive-dashboard-shell {
  grid-template-columns: minmax(0, 1fr);
}

body.executive-view-mode .workspace-copilot,
body.executive-view-mode .project-portfolio,
body.executive-view-mode #samplePlanButton {
  display: none;
}

body.executive-view-mode .executive-workspace {
  min-height: 100vh;
}

.workspace-copilot {
  background: var(--panel);
  border-right: 1px solid var(--line);
  display: grid;
  grid-template-rows: minmax(0, 1fr);
  max-height: 100vh;
  min-height: 100vh;
  overflow: hidden;
}

/* .dashboard-app-shell's top bar now takes up real vertical space above this
   rail, so the rail can no longer size itself to a flat 100vh (see the
   .dashboard-app-shell > .dashboard-shell.executive-dashboard-shell override
   above) - it must stretch to fill whatever the grid row actually has instead,
   or the account card at its bottom renders past the viewport's edge. */
.dashboard-app-shell .workspace-copilot {
  max-height: none;
  min-height: 0;
}

.workspace-copilot .message {
  font-size: 0.78rem;
  max-width: 94%;
  padding: 8px 9px;
}

.workspace-copilot .dashboard-composer {
  background: var(--panel);
  border-bottom: 1px solid var(--line);
  position: sticky;
  top: 0;
  z-index: 2;
}

.workspace-copilot .dashboard-composer textarea {
  min-height: 76px;
}

.workspace-copilot .dashboard-chat-messages {
  align-content: start;
  min-height: 0;
  overflow-y: auto;
}

.executive-workspace {
  display: grid;
  grid-template-rows: minmax(0, 1fr);
  min-height: 0;
  min-width: 0;
}

.app-top-bar {
  align-items: center;
  background: #ffffff;
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  padding: 12px 16px;
  position: sticky;
  top: 0;
  z-index: 30;
}

/* .workspace-account-card lives right after .dashboard-brand in the DOM now
   (so it can share the brand's row on mobile - see the mobile order override
   below), which would otherwise reorder it before .top-switcher-group/
   .dashboard-actions on desktop too. Pin the desktop order back to how it
   already reads left-to-right. */
.dashboard-brand {
  order: 1;
}

.top-switcher-group {
  order: 2;
}

.dashboard-actions {
  order: 3;
}

.workspace-account-card {
  order: 4;
}

.workspace-upload {
  background: #f4f4f5;
  display: grid;
  gap: 10px;
  padding: 12px;
}

.workspace-upload h3 {
  font-size: 0.84rem;
  margin: 0;
}

.workspace-upload .plan-text-input {
  min-height: 150px;
}

.pending-plan-review {
  display: none;
}

.pending-plan-review:not(:empty) {
  align-items: stretch;
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 8px;
  display: grid;
  gap: 12px;
  grid-template-columns: minmax(170px, 0.8fr) minmax(260px, 1.4fr) auto;
  padding: 12px;
}

.pending-plan-review strong,
.pending-plan-review span {
  display: block;
}

.pending-plan-summary {
  align-content: start;
  display: grid;
  gap: 4px;
  min-width: 0;
}

.pending-plan-summary strong {
  color: var(--ink);
  font-size: 0.9rem;
  font-weight: 900;
  overflow-wrap: anywhere;
}

.pending-project-name {
  display: grid;
  gap: 4px;
  min-width: 0;
}

.pending-project-name span {
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 800;
  margin: 0;
  text-transform: uppercase;
}

.pending-project-name input,
.pending-project-name select {
  border: 1px solid var(--line);
  border-radius: 6px;
  color: var(--ink);
  font-size: 0.84rem;
  font-weight: 800;
  min-height: 34px;
  padding: 6px 30px 6px 8px;
  width: 100%;
}

.pending-plan-review span {
  color: var(--muted);
  font-size: 0.86rem;
  margin-top: 3px;
}

.pending-version-options {
  display: grid;
  gap: 9px;
  min-width: 0;
}

.pending-actions {
  align-content: center;
  display: grid;
  gap: 8px;
  min-width: 150px;
}

.pending-actions button:disabled {
  cursor: not-allowed;
  opacity: 0.5;
}

.pending-portfolio-sheet-list {
  display: grid;
  gap: 8px;
  list-style: none;
  margin: 0;
  max-height: 220px;
  overflow-y: auto;
  padding: 0;
}

.pending-portfolio-sheet-list li {
  display: grid;
  gap: 4px;
}

.pending-portfolio-sheet-list small {
  color: var(--muted);
  font-size: 0.72rem;
}

.project-portfolio,
.project-dashboard {
  min-height: 0;
  overflow-y: auto;
}

.project-portfolio {
  background: var(--bg);
  border-left: 1px solid var(--line);
  padding: 12px;
}

.project-portfolio .portfolio-actions button,
.project-portfolio .right-row-actions button,
.project-portfolio .project-delete-button {
  opacity: 0;
  pointer-events: none;
  transition: opacity 140ms ease;
}

.project-portfolio .portfolio-actions:hover button,
.project-portfolio .portfolio-list-row:hover .right-row-actions button,
.project-portfolio .portfolio-list-row:hover .project-delete-button,
.project-portfolio .project-list-row:hover .right-row-actions button,
.project-portfolio .project-list-row:hover .project-delete-button,
.project-portfolio .portfolio-actions button:focus-visible,
.project-portfolio .right-row-actions button:focus-visible,
.project-portfolio .project-delete-button:focus-visible {
  opacity: 1;
  pointer-events: auto;
}

.project-dashboard {
  padding: 14px 16px 18px;
}

.access-removed-panel {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 18px;
}

.access-removed-panel h2 {
  font-size: 1.1rem;
  margin: 4px 0 8px;
}

.access-removed-panel p:last-child {
  color: var(--muted);
  font-size: 0.86rem;
  font-weight: 700;
  margin: 0;
}

.account-blocked-overlay {
  align-items: center;
  background: rgba(0, 0, 0, 0.55);
  display: flex;
  inset: 0;
  justify-content: center;
  padding: 20px;
  position: fixed;
  z-index: 9999;
}

.account-blocked-panel {
  background: var(--panel, #ffffff);
  border: 1px solid var(--line, #d4d4d8);
  border-radius: 12px;
  box-shadow: var(--shadow-modal, 0 20px 45px rgba(0, 0, 0, 0.3));
  max-width: 460px;
  padding: 28px;
  text-align: left;
}

.account-blocked-panel .eyebrow {
  color: var(--muted);
}

.account-blocked-panel h2 {
  color: var(--danger-strong);
  font-size: 1.25rem;
  margin: 4px 0 12px;
}

.account-blocked-panel p {
  color: var(--ink);
  line-height: 1.55;
  margin: 0 0 18px;
}

.account-blocked-panel button {
  background: var(--ink, #18181b);
  border: none;
  border-radius: 999px;
  color: var(--bg, #fff);
  cursor: pointer;
  font-weight: 700;
  padding: 10px 20px;
}

/* Deliberately NOT a white panel with a modal shadow.

   It used to be exactly that, and a dialog is also a white panel with a modal
   shadow - so a notice firing over an open dialog read as one panel torn in
   half rather than as two separate things. Reported from a phone as "what are
   those messages on top?". A transient system notice should look like nothing
   else on the page: dark, compact, obviously floating over the app rather than
   part of it.

   It stays above every overlay (dialogs top out at 150) on purpose. The notices
   routed here are refusals - a save rejected, a permission denied - and hiding
   one behind the dialog the person is looking at would lose the only
   explanation they get. Overlapping a dialog is the lesser fault; looking like
   a broken dialog was the actual bug. */
.system-notice-host {
  background: rgba(24, 24, 27, 0.94);
  border: 1px solid rgba(255, 255, 255, 0.12);
  border-radius: 10px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, 0.28);
  color: #fafafa;
  font-size: 0.86rem;
  left: 50%;
  line-height: 1.5;
  /* Long refusals quote the offending record's name, which in imported plans
     runs to a full sentence - scroll rather than grow to half the screen. */
  max-height: min(40vh, 260px);
  max-width: min(480px, calc(100vw - 32px));
  opacity: 0;
  overflow-y: auto;
  padding: 14px 18px;
  pointer-events: none;
  position: fixed;
  /* Clear of the notch and the browser's own chrome on a phone. */
  top: calc(16px + env(safe-area-inset-top, 0px));
  transform: translate(-50%, -12px);
  transition: opacity 0.2s ease, transform 0.2s ease;
  z-index: 10000;
}

.system-notice-host.visible {
  opacity: 1;
  transform: translate(-50%, 0);
}

/* Kept at the top even with a dialog open, and that is a choice between two
   imperfect options rather than a good one. A centred dialog leaves no gap tall
   enough for a notice, so it is going to cover something: at the top it covers
   the dialog's title, at the foot it covers the dialog's buttons. Measured both
   - docking to the bottom moved the overlap from the heading onto "Explain
   this" and "Close", which is the half somebody needs to reach. A title can be
   re-read after seven seconds; a button that looks buried cannot be pressed.
   The notice is pointer-events: none regardless, so nothing under it is ever
   actually blocked. */
body:has(.confirm-overlay) .system-notice-host {
  /* Tighter against the edge while a dialog is up, so it eats as little of the
     dialog's head as it can. */
  max-height: min(30vh, 200px);
  top: calc(8px + env(safe-area-inset-top, 0px));
}

.system-notice-host.success {
  border-color: #4ba777;
}

.system-notice-host.clickable {
  cursor: pointer;
}

.system-notice-host.clickable.visible {
  pointer-events: auto;
}

.portfolio-intake-actions {
  display: grid;
  gap: 8px;
  margin-bottom: 12px;
}

.portfolio-upload-button {
  width: 100%;
}

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

.scratch-plan-form {
  display: grid;
  gap: 10px;
}

.scratch-plan-form label {
  display: grid;
  gap: 5px;
}

.scratch-plan-form span {
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 800;
}

.scratch-plan-form input,
.scratch-plan-form textarea {
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink);
  font-size: 0.8rem;
  line-height: 1.35;
  padding: 8px;
  resize: vertical;
  width: 100%;
}

.scratch-plan-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.portfolio-panel {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 8px;
  display: grid;
  gap: 10px;
  margin-bottom: 12px;
  padding: 12px;
}

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

.portfolio-rail-button {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink);
  font-weight: 850;
  margin-bottom: 12px;
  min-height: 42px;
  width: 100%;
}

.portfolio-rail-button:hover {
  border-color: #a1a1aa;
  background: var(--panel);
}

.rail-section-heading {
  align-items: start;
  display: flex;
  gap: 10px;
  justify-content: space-between;
}

.rail-section-toggle {
  background: none;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  font: inherit;
  padding: 4px;
  text-align: left;
  width: 100%;
}

.rail-section-toggle:hover,
.rail-section-toggle:focus-visible {
  background: rgba(17, 22, 29, 0.04);
}

.rail-section-chevron {
  align-self: center;
  border-bottom: 2px solid var(--muted);
  border-right: 2px solid var(--muted);
  flex: 0 0 auto;
  height: 7px;
  transform: rotate(45deg);
  transition: transform 0.15s ease;
  width: 7px;
}

.rail-section-toggle[aria-expanded="true"] .rail-section-chevron {
  transform: rotate(-135deg);
}

.rail-section-body {
  display: grid;
  gap: 10px;
  margin-top: 10px;
}

.rail-section-body[hidden] {
  display: none;
}

/* Portfolios/Projects lists float above the rest of the sidebar (positioned
   in JS via positionFloatingRailBody, dashboard.js) instead of pushing the
   "Project menu" nav below them down - a long list used to get clipped by
   .workspace-copilot-switcher's max-height/overflow-y:auto or squeeze the
   nav out of view. Needs its own background/border/shadow because floating
   removes it from its .portfolio-panel parent's bordered box visually. */
.rail-section-body-floating {
  background: var(--panel-strong);
  border: 1px solid var(--line-strong, #d4d4d8);
  border-radius: 8px;
  box-shadow: 0 16px 34px rgba(24, 24, 27, 0.22);
  margin-top: 0;
  max-height: min(420px, calc(100vh - 96px));
  overflow-y: auto;
  padding: 10px;
  position: fixed;
  z-index: 50;
}

.rail-section-body,
.collapsible-section-body,
.account-subscription-panel {
  opacity: 1;
  transform: translateY(0);
  transition: opacity 180ms ease, transform 180ms ease, display 180ms allow-discrete;
}

.rail-section-body[hidden],
.collapsible-section-body[hidden],
.account-subscription-panel[hidden] {
  opacity: 0;
  transform: translateY(-6px);
}

@starting-style {
  .rail-section-body:not([hidden]),
  .collapsible-section-body:not([hidden]),
  .account-subscription-panel:not([hidden]) {
    opacity: 0;
    transform: translateY(-6px);
  }
}

#portfolioListSummary,
#projectListSummary {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.collapsible-section-heading {
  align-items: center;
  display: flex;
  gap: 10px;
  justify-content: flex-start;
}

.collapsible-heading-title {
  align-items: center;
  display: flex;
  gap: 8px;
  min-width: 0;
}

.collapsible-heading-title h3 {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.collapsible-section-heading .collapsible-heading-title h3 {
  font-size: 1.02rem;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
}

.collapsible-section-heading .rail-section-chevron {
  flex: 0 0 auto;
  margin-left: auto;
}

.collapsible-section-heading .section-edit-button,
.collapsible-section-heading .milestone-focus-legend {
  margin-left: auto;
}

/* Attention rows repeat their actions on every item, so at full secondary-button
   weight they were most of the ~60 buttons on a project page. As compact text
   actions they keep the same hit area and discoverability (unlike a hover-only
   reveal, which would strand touch users) while reading as row actions rather
   than as a second toolbar per line. */
.attention-inline-action {
  background: none;
  border: 0;
  border-radius: 6px;
  color: var(--accent, #2563eb);
  cursor: pointer;
  font-size: 0.78rem;
  font-weight: 700;
  padding: 6px 8px;
}

.attention-inline-action:hover,
.attention-inline-action:focus-visible {
  background: rgba(37, 99, 235, 0.12);
  text-decoration: underline;
}

/* A section with nothing in it (see collapsibleSectionHeader) has no toggle and
   no body, so it collapses to a single quiet row rather than a full-height
   accordion whose only message is "0". The header action stays, so it is still
   the way to add the first item. */
.collapsible-section-heading.is-empty-section {
  padding: 2px 0;
}

.collapsible-section-heading.is-empty-section .collapsible-heading-title h3 {
  color: var(--muted);
  font-size: 0.92rem;
}

.collapsible-section-heading.is-empty-section .section-edit-button {
  margin-left: auto;
}

.collapsible-section:has(.collapsible-section-heading.is-empty-section) {
  min-height: 0;
  padding: 8px 10px;
}

.collapsible-toggle-inline {
  align-items: center;
  background: none;
  border: 0;
  cursor: pointer;
  display: flex;
  font: inherit;
  gap: 8px;
  justify-content: flex-start;
  padding: 4px;
  text-align: left;
  width: auto;
}

.collapsible-toggle-inline:hover,
.collapsible-toggle-inline:focus-visible {
  background: rgba(17, 22, 29, 0.04);
  border-radius: 6px;
}

.collapsible-section-body[hidden] {
  display: none;
}

.collapsible-section,
.portfolio-panel {
  transition: box-shadow 200ms ease, transform 200ms ease;
}

.collapsible-section:has(.collapsible-section-heading:hover),
.portfolio-panel:has(.rail-section-toggle:hover) {
  box-shadow: var(--shadow-hover);
  transform: translateY(-1px);
}

.plan-section.collapsible-section,
.project-detail-panel.collapsible-section {
  min-height: 0;
}

.rail-section-heading h3 {
  color: var(--ink);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.1;
  margin: 0;
}

.rail-section-heading p {
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 750;
  line-height: 1.3;
  margin: 4px 0 0;
}

.portfolio-field {
  display: grid;
  gap: 4px;
}

.portfolio-field span {
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 800;
}

.portfolio-field input,
.portfolio-field select,
.entity-editor-form input,
.entity-editor-form select {
  border: 1px solid var(--line);
  border-radius: 6px;
  color: var(--ink);
  font-size: 0.72rem;
  min-height: 32px;
  padding: 6px 30px 6px 8px;
  width: 100%;
}

.portfolio-actions {
  display: grid;
  gap: 6px;
  grid-template-columns: 1fr;
}

.share-editor-form .admin-meta {
  overflow-wrap: anywhere;
}

.share-editor-form .admin-meta a {
  color: var(--accent-strong);
  font-weight: 800;
  text-decoration: none;
}

.share-scope-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.share-scope-grid label:first-child {
  grid-column: 1 / -1;
}

.share-expiration-fieldset {
  border: 1px solid var(--line);
  border-radius: 8px;
  display: grid;
  gap: 10px;
  margin: 0;
  padding: 12px;
}

.share-expiration-fieldset legend {
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 900;
  padding: 0 4px;
  text-transform: uppercase;
}

.share-expiration-choice {
  align-items: center;
  display: flex;
  gap: 8px;
}

.share-expiration-choice input[type="number"] {
  flex: 0 0 88px;
}

.share-existing-heading {
  margin: 18px 0 10px;
}

.sharing-links-list {
  display: grid;
  gap: 10px;
}

/* The counter that stands in for the rest of a long share list, and opens the
   full history. A quiet row rather than a button-looking button: it is a way
   into a record, not an action on the thing above it. */
.share-history-button {
  align-items: baseline;
  background: none;
  border: 1px dashed var(--line, #d4d4d8);
  border-radius: 8px;
  color: inherit;
  cursor: pointer;
  display: flex;
  flex-wrap: wrap;
  font: inherit;
  font-weight: 700;
  gap: 8px;
  justify-content: flex-start;
  padding: 9px 12px;
  text-align: left;
  width: 100%;
}

.share-history-button:hover {
  border-style: solid;
}

.share-history-button span {
  color: var(--muted, #6b7280);
  font-weight: 500;
}

.share-history-panel {
  max-width: 620px;
  width: min(620px, 94vw);
}

.share-history-list {
  display: grid;
  gap: 10px;
  margin: 4px 0 16px;
  max-height: 52vh;
  overflow-y: auto;
}

/* A closed link is history, not an option - it recedes, and says so in words as
   well, since colour alone would not survive a greyscale print or a reader who
   cannot see the difference. */
.share-history-closed {
  opacity: 0.62;
}

/* .sharing-link-row is a single-column grid, so a second child lands underneath
   the detail rather than beside it - which read as a stray word under each row.
   Inside the history these rows are two columns: what was shared, and whether
   it still works. */
.share-history-list .sharing-link-row {
  align-items: center;
  grid-template-columns: 1fr auto;
}

.share-history-state {
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  white-space: nowrap;
}

.sharing-link-row {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  display: grid;
  gap: 8px;
  padding: 10px;
}

.sharing-link-row strong,
.sharing-link-row span,
.sharing-link-row a {
  display: block;
}

.sharing-link-row span {
  color: var(--muted);
  font-size: 0.74rem;
  font-weight: 700;
  margin-top: 3px;
}

.sharing-link-row a {
  color: var(--accent-strong);
  font-size: 0.74rem;
  font-weight: 800;
  overflow-wrap: anywhere;
  text-decoration: none;
}

.sharing-link-actions {
  align-items: center;
  display: grid;
  gap: 8px;
  grid-template-columns: minmax(0, 1fr) auto;
}

.member-role-actions {
  grid-template-columns: minmax(0, 1fr) auto auto;
}

.sharing-external-link,
.sharing-link-row .sharing-external-link {
  align-items: center;
  display: inline-flex;
  gap: 8px;
  min-width: 0;
}

.sharing-link-url {
  min-width: 0;
  overflow-wrap: anywhere;
}

.external-link-icon {
  color: currentColor;
  flex: 0 0 14px;
  height: 14px;
  position: relative;
  width: 14px;
}

.external-link-icon::before {
  border: 1.5px solid currentColor;
  border-radius: 2px;
  bottom: 1px;
  content: "";
  height: 8px;
  left: 1px;
  opacity: 0.72;
  position: absolute;
  width: 8px;
}

.external-link-icon::after {
  border-right: 1.5px solid currentColor;
  border-top: 1.5px solid currentColor;
  content: "";
  height: 7px;
  position: absolute;
  right: 1px;
  top: 1px;
  width: 7px;
}

.sharing-delete-button {
  opacity: 1;
  pointer-events: auto;
  white-space: nowrap;
}

.shared-readonly .portfolio-intake-actions,
.shared-readonly .portfolio-actions,
.shared-readonly .right-row-actions,
.shared-readonly .project-delete-button,
.shared-readonly .section-edit-button,
.shared-readonly #samplePlanButton {
  display: none;
}

.portfolio-list {
  display: grid;
  gap: 2px;
}

.portfolio-list-row {
  background: none;
  border: 0;
  border-radius: 6px;
  box-shadow: none;
  display: grid;
  gap: 6px;
  padding: 2px;
}

.portfolio-list-row.active,
.portfolio-list-row:hover {
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  box-shadow: none;
}

.portfolio-list-item {
  background: transparent;
  color: var(--ink);
  display: block;
  min-height: 44px;
  padding: 4px;
  text-align: left;
  width: 100%;
}

.portfolio-list-item strong,
.portfolio-list-item small {
  display: block;
}

.portfolio-list-item small {
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 700;
  line-height: 1.35;
  margin-top: 3px;
}

.upload-window {
  align-items: start;
  background: rgba(24, 24, 27, 0.34);
  display: none;
  inset: 0;
  justify-content: center;
  padding: 72px 24px 24px;
  position: fixed;
  z-index: 20;
}

.upload-window.open {
  display: flex;
}

.section-editor-window.open ~ #accountModal.open {
  display: none;
}

.upload-window-panel {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-modal);
  /* max-height/max-width are the resize ceiling (nearly the full viewport),
     not the default size - see the same pattern/rationale on
     .section-editor-panel above. Default width raised from 720 to 860 so
     Create/Upload opens noticeably roomier even before anyone drags it. */
  height: calc(100vh - 96px);
  max-height: 96vh;
  max-width: 96vw;
  min-height: 320px;
  overflow-y: auto;
  resize: both;
  width: min(860px, 100%);
}

@media (min-width: 640px) {
  .upload-window-panel {
    min-width: 480px;
  }
}

.upload-window-header {
  align-items: center;
  border-bottom: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  padding: 12px;
}

.upload-window-header h3 {
  font-size: 1.06rem;
  margin: 0;
}

/* Shared by the onboarding welcome's "Start from a template" step and the
   Create modal's own template picker (#createPlanTemplatePicker) - both
   render the same PLAN_TEMPLATES buttons via JS, this is just the layout. */
.onboarding-template-picker {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 8px;
}

.onboarding-template-button {
  background: #eef2f7;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--theruka-green, #14635a);
  cursor: pointer;
  font-size: 0.82rem;
  font-weight: 700;
  padding: 8px 14px;
}

.onboarding-template-button:hover {
  background: #e2e8f0;
}

.create-plan-templates {
  border-bottom: 1px solid var(--line);
  display: grid;
  gap: 10px;
  margin-bottom: 16px;
  padding-bottom: 16px;
}

/* display:grid above beats the browser's default [hidden]{display:none} UA
   rule (author styles win over UA styles), so the picker stayed visible
   after loadPlanTemplate() sets hidden=true and switches to upload mode -
   this restores the hide. */
.create-plan-templates[hidden] {
  display: none;
}

.create-plan-templates-intro {
  color: var(--muted);
  font-size: 0.84rem;
  margin: 0;
}

.create-plan-templates-divider {
  align-items: center;
  color: var(--muted);
  display: flex;
  font-size: 0.68rem;
  font-weight: 800;
  gap: 10px;
  letter-spacing: 0.02em;
  margin: 2px 0 0;
  text-transform: uppercase;
}

.create-plan-templates-divider::before,
.create-plan-templates-divider::after {
  background: var(--line);
  content: "";
  flex: 1;
  height: 1px;
}

/* Start-from-Jira, above the template picker in the Create Plan modal. Same
   shape as .create-plan-templates so the two read as one stack of choices -
   and, like it, needs the explicit [hidden] rule, since display:grid here
   beats the browser's own [hidden]{display:none}. */
/* No border-bottom, unlike .create-plan-templates: this section ends with a
   labelled divider of its own, and the two together drew a pair of parallel
   rules 16px apart. */
.create-plan-jira {
  display: grid;
  gap: 10px;
  margin-bottom: 16px;
}

.create-plan-jira[hidden] {
  display: none;
}

.create-plan-jira h4 {
  font-size: 0.95rem;
  margin: 0;
}

.create-plan-jira-intro {
  color: var(--muted);
  font-size: 0.84rem;
  margin: 0;
}

/* Same field shape as the blank form below it - stacked caption over a
   full-width control - so the two read as one modal rather than two. */
.create-plan-jira-form {
  display: grid;
  gap: 10px;
}

.create-plan-jira-form label {
  display: grid;
  gap: 5px;
}

.create-plan-jira-form label > span {
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 800;
}

.create-plan-jira-form input,
.create-plan-jira-form select {
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink);
  font-size: 0.8rem;
  line-height: 1.35;
  padding: 8px 30px 8px 8px;
  width: 100%;
}

/* The status line is empty most of the time; an empty <p> still reserves its
   own height, which left a gap under the fields that read as a missing field. */
.create-plan-jira-form p:empty {
  display: none;
}

.compact-heading {
  background: transparent;
  border-bottom: 0;
  padding: 0 0 12px;
}

.project-list {
  display: grid;
  gap: 2px;
}

.project-list-row {
  background: none;
  border: 0;
  border-radius: 6px;
  box-shadow: none;
  display: grid;
  gap: 6px;
  padding: 2px;
}

.right-row-actions {
  display: flex;
  gap: 6px;
  justify-content: flex-end;
}

.right-row-actions button {
  flex: 1;
  min-height: 30px;
}

.row-edit-button,
.row-share-button {
  font-size: 0.72rem;
  padding: 0 9px;
}

.project-list-row.active,
.project-list-row:hover {
  background: color-mix(in srgb, var(--ink) 6%, transparent);
  box-shadow: none;
}

.project-list-item {
  align-items: center;
  background: transparent;
  border-radius: 6px;
  color: var(--ink);
  display: flex;
  justify-content: space-between;
  min-height: 50px;
  padding: 4px;
  text-align: left;
  width: 100%;
}

.project-list-item:hover {
  background: rgba(244, 244, 245, 0.85);
}

.project-list-item strong,
.project-list-item small,
.project-list-item em {
  display: block;
}

.project-list-item small {
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 700;
  line-height: 1.35;
  margin-top: 4px;
}

.project-list-item em,
.score-badge {
  background: #3f3f46;
  border-radius: 999px;
  color: white;
  font-size: 0.7rem;
  font-style: normal;
  font-weight: 800;
  padding: 6px 8px;
}

.project-score-stack {
  display: grid;
  gap: 3px;
  justify-items: end;
  min-width: 74px;
}

.project-score-stack small {
  font-size: 0.64rem;
  margin: 0;
  text-align: right;
}

/* "Nd late" on a project list row. Two-class selector on purpose: a later sheet
   sets `.project-score-stack strong { color: var(--theruka-strong-text) }` at
   (0,1,1), so a single class here loses the cascade and the figure renders in
   ordinary ink - which the colour gate caught. --danger-strong is the token
   rather than a literal because it is defined for both themes; the flat red it
   replaced is unreadable on the dark ground. */
.project-score-stack strong.project-late {
  color: var(--danger-strong);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
}

.project-delete-button {
  background: var(--danger-surface);
  color: #8a2d20;
  font-size: 0.72rem;
  justify-self: end;
  min-height: 28px;
  padding: 0 9px;
}

.project-delete-button:hover {
  background: #fecaca;
}

.executive-project-hero {
  align-items: center;
  background: #ffffff;
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  display: flex;
  justify-content: space-between;
  margin-bottom: 10px;
  padding: 12px;
  position: relative;
}

.executive-project-hero > div:first-child {
  margin-right: auto;
}

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

.executive-project-hero p {
  color: var(--muted);
  font-size: 0.76rem;
  font-weight: 700;
  margin: 6px 0 0;
}

.executive-health strong {
  font-size: 1.45rem;
}

.score-cycles {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: flex-end;
  margin-left: 14px;
  position: relative;
}

.score-cycle {
  align-items: center;
  aspect-ratio: 1;
  appearance: none;
  background: #ffffff;
  border: 3px solid rgba(24, 24, 27, 0.12);
  border-radius: 50%;
  cursor: pointer;
  display: grid;
  flex: 0 0 auto;
  font: inherit;
  justify-items: center;
  padding: 0;
  place-content: center;
  text-align: center;
  width: 82px;
}

.score-cycle:hover {
  box-shadow: 0 0 0 4px rgba(82, 82, 91, 0.12);
  transform: translateY(-1px);
}

.score-cycle:focus-visible {
  outline: 3px solid rgba(82, 82, 91, 0.28);
  outline-offset: 3px;
}

.score-cycle strong {
  color: inherit;
  font-size: 1.02rem;
  line-height: 1;
}

.score-cycle span {
  color: inherit;
  font-size: 0.58rem;
  font-weight: 900;
  line-height: 1;
  margin-top: 4px;
  max-width: 62px;
  overflow-wrap: anywhere;
  text-transform: uppercase;
}

.score-cycle.score-low {
  background: #fecaca;
  border-color: var(--danger-strong);
  color: #7f1d1d;
}

.score-cycle.score-mid {
  background: var(--warn-surface);
  border-color: #ca8a04;
  color: var(--warn);
}

.score-cycle.score-warn {
  background: #fdba74;
  border-color: #ea580c;
  color: #7c2d12;
}

.score-cycle.score-high {
  background: #86efac;
  border-color: #16a34a;
  color: #14532d;
}

.score-explanation {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 16px 34px rgba(24, 24, 27, 0.14);
  display: grid;
  gap: 8px;
  padding: 12px 34px 12px 12px;
  position: fixed;
  transform: translateX(-50%);
  width: min(320px, calc(100vw - 48px));
  z-index: 80;
}

.score-explanation[hidden] {
  display: none;
}

.score-explanation::before,
.score-explanation::after {
  content: "";
  left: calc(50% + var(--score-arrow-x, 0px));
  position: absolute;
  transform: translateX(-50%);
}

.score-explanation::before {
  border-left: 9px solid transparent;
  border-right: 9px solid transparent;
  border-top: 9px solid var(--line);
  bottom: -9px;
}

.score-explanation::after {
  border-left: 8px solid transparent;
  border-right: 8px solid transparent;
  border-top: 8px solid #ffffff;
  bottom: -7px;
}

.score-explanation.score-explanation-below::before {
  border-bottom: 9px solid var(--line);
  border-top: 0;
  bottom: auto;
  top: -9px;
}

.score-explanation.score-explanation-below::after {
  border-bottom: 8px solid #ffffff;
  border-top: 0;
  bottom: auto;
  top: -7px;
}

.score-explanation h3 {
  font-size: 0.9rem;
  margin: 2px 0 0;
}

.score-explanation-close {
  align-items: center;
  appearance: none;
  background: #f4f4f5;
  border: 1px solid var(--line);
  border-radius: 50%;
  color: var(--ink);
  cursor: pointer;
  display: grid;
  font-size: 1rem;
  font-weight: 800;
  height: 24px;
  line-height: 1;
  padding: 0;
  place-content: center;
  position: absolute;
  right: 8px;
  top: 8px;
  width: 24px;
}

.score-explanation-close:hover {
  background: #e4e4e7;
}

.score-explanation ul {
  color: var(--ink);
  display: grid;
  gap: 5px;
  margin: 0;
  padding-left: 18px;
}

.score-explanation li {
  font-size: 0.75rem;
  line-height: 1.35;
}

.executive-metrics {
  display: grid;
  gap: 8px;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin-bottom: 10px;
}

.executive-metrics div,
.executive-metrics button {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: none;
  color: var(--ink);
  display: grid;
  gap: 3px;
  min-height: 62px;
  padding: 9px;
  text-align: left;
}

.executive-metrics button {
  cursor: pointer;
}

.executive-metrics button:hover {
  background: var(--panel-strong);
  border-color: var(--line-strong);
}

.executive-metrics strong {
  font-size: 0.98rem;
  line-height: 1.1;
}

.executive-metrics span {
  color: var(--muted);
  font-size: 0.66rem;
  font-weight: 800;
  line-height: 1.25;
}

.project-focus-summary {
  display: grid;
  gap: 8px;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin-bottom: 10px;
}

.project-focus-summary div {
  background: var(--bg);
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  box-shadow: none;
  display: grid;
  gap: 3px;
  min-height: 64px;
  padding: 10px;
}

.project-focus-summary strong {
  color: var(--ink);
  font-size: 1rem;
  line-height: 1.1;
}

.project-focus-summary span {
  color: var(--muted);
  font-size: 0.66rem;
  font-weight: 800;
  line-height: 1.25;
}

.project-focus-summary .score-low {
  background: var(--bg);
  border-color: var(--line);
  border-left: 4px solid #b64c3e;
}

.project-focus-summary .score-mid {
  background: var(--bg);
  border-color: var(--line);
  border-left: 4px solid #c99b11;
}

.project-focus-summary .score-warn {
  background: var(--bg);
  border-color: var(--line);
  border-left: 4px solid #d0702c;
}

.project-focus-summary .score-high {
  background: var(--bg);
  border-color: var(--line);
  border-left: 4px solid #3a9b5d;
}

.project-workspace-focus {
  align-items: start;
  display: grid;
  gap: 12px;
  grid-template-columns: minmax(0, 1fr);
}

/* ---------------------------------------------------------------------------
   Project home: scores in a column, one card per collection beside them

   The scores used to take the full width before any content started, and every
   list was stacked below them - so nothing was visible without scrolling. The
   scores are context for the counts, so they get a column and the cards get
   the rest.
   ------------------------------------------------------------------------ */
/* Cards left, scores right. The counts are the page's content and the thing
   that gets clicked; the scores are the reading of it. */
.project-hero-columns {
  align-items: start;
  display: grid;
  gap: 22px;
  grid-template-columns: minmax(0, 1fr) minmax(210px, 260px);
}

/* The second column is now usually absent - the four scores that filled it were
   removed, and it carries only the AI score when there is one to carry. Without
   this the counts keep a 260px column reserved beside them for nothing, which
   reads as a panel that failed to load rather than as one that isn't there. */
.project-hero-columns-single {
  grid-template-columns: minmax(0, 1fr);
}

/* Denser than the page-wide block these came from: at that size five rows of
   label, number and bar took more height than everything they described. The
   numbers stay legible - only the air between them goes. */
.project-hero-columns .score-bars {
  gap: 8px;
}

.project-hero-columns .score-bar-row {
  gap: 3px;
}

.project-hero-columns .score-bar-label {
  font-size: 0.74rem;
}

.project-hero-columns .score-bar-value {
  font-size: 0.9rem;
}

.project-hero-columns .score-bar-lead .score-bar-value {
  font-size: 1.15rem;
}

.project-hero-columns .score-bar-track {
  height: 5px;
}

.project-hero-columns .score-bar-lead .score-bar-track {
  height: 7px;
}

.project-hero-columns .score-bar-detail {
  font-size: 0.66rem;
}

.project-card-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
}

/* Two classes rather than one: a card is a <button>, and
   `.executive-dashboard-shell button` (0,1,1) already sets the padding and
   min-height every control in the shell gets. A bare `.project-card` (0,1,0)
   loses that tie, which is how the cards ended up with a 34px pill's padding
   and their contents centred inside it. */
.project-card-grid .project-card {
  align-items: start;
  background: var(--panel-strong, #ffffff);
  border: 1px solid var(--line);
  border-radius: 12px;
  /* The base <button> rule paints white text for a filled button; this one is a
     surface, so it has to say what colour its own text is or the count and the
     label render white on white. */
  color: var(--ink);
  cursor: pointer;
  display: grid;
  gap: 2px;
  justify-content: stretch;
  justify-items: start;
  min-height: 92px;
  padding: 12px 14px;
  text-align: left;
  transition: border-color 0.12s ease, transform 0.12s ease;
}

.project-card-grid .project-card:hover,
.project-card-grid .project-card:focus-visible {
  border-color: var(--accent, #2563eb);
  transform: translateY(-1px);
}

.project-card-count {
  font-size: 1.6rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
  line-height: 1.1;
}

.project-card-label {
  font-size: 0.82rem;
  font-weight: 700;
}

.project-card-signal {
  color: var(--muted);
  font-size: 0.72rem;
  line-height: 1.3;
}

/* The one card that is a verdict rather than a quantity, so it is the one that
   changes colour with its own number. */
/* Scoped to match the two-class rule above, or the plain surface set there
   would repaint over this one. */
.project-card-grid .project-card-alert {
  background: var(--notice-bg, #fdf1e7);
  border-color: var(--notice-line, #e6c9ab);
}

.project-card-alert .project-card-count,
.project-card-alert .project-card-label {
  color: #93301f;
}

.project-card-clear .project-card-count {
  color: var(--muted);
}

/* ---- the list behind a card -------------------------------------------- */
.project-card-overlay {
  align-items: center;
  background: rgba(24, 24, 27, 0.4);
  display: flex;
  inset: 0;
  justify-content: center;
  padding: 24px;
  position: fixed;
  /* Below the section editor (120), the date-change prompt (130) and confirm
     (150): all three can be opened from inside this modal and must land on top
     of it rather than behind it. */
  z-index: 110;
}

.project-card-panel {
  background: var(--panel-strong, #ffffff);
  border: 1px solid var(--line);
  border-radius: 14px;
  box-shadow: var(--shadow-modal);
  display: grid;
  gap: 0;
  grid-template-rows: auto minmax(0, 1fr);
  max-height: calc(100vh - 48px);
  max-width: min(1080px, calc(100vw - 32px));
  width: min(1080px, calc(100vw - 32px));
}

.project-card-panel-head {
  align-items: start;
  border-bottom: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: space-between;
  padding: 18px 22px;
}

.project-card-panel-head h3 {
  margin: 0;
}

.project-card-panel-signal {
  color: var(--muted);
  font-size: 0.78rem;
  margin: 4px 0 0;
}

.project-card-panel-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* The list scrolls, the header does not - a milestone table can be long, and
   the Close button going off screen with it is the failure to avoid. */
.project-card-panel-body {
  min-height: 0;
  overflow-y: auto;
  padding: 18px 22px 22px;
}

/* These lists used to sit inside .plan-section, which is where they got their
   list styling from. Out here they came back as bulleted and indented, so the
   same treatment follows them into the modal. */
.project-card-panel-body ul {
  display: grid;
  gap: 10px;
  list-style: none;
  margin: 0;
  padding-left: 0;
}

.project-card-panel-body li {
  border-top: 1px solid var(--line);
  color: var(--muted);
  display: grid;
  font-size: 0.82rem;
  gap: 3px;
  line-height: 1.35;
  padding-top: 10px;
}

.project-card-panel-body li:first-child {
  border-top: 0;
  padding-top: 0;
}

.project-card-panel-body li strong {
  color: var(--ink);
}

.project-card-panel-body h4 {
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  margin: 0 0 10px;
  text-transform: uppercase;
}

/* The two-column pairs the tabbed sections used (Open / Done, Open Risks /
   Resolved) - the modal is wide enough to keep them side by side. */
.project-card-panel-body .risk-list-groups {
  display: grid;
  gap: 22px;
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}

@media (max-width: 900px) {
  .project-hero-columns {
    grid-template-columns: minmax(0, 1fr);
  }
}

.project-menu-icon {
  color: var(--muted);
  flex: 0 0 auto;
  height: 16px;
  width: 16px;
}

.project-focus-main {
  display: grid;
  gap: 12px;
  min-width: 0;
}

.milestone-mobile-list {
  display: block;
}

.visually-hidden {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

.empty-cell {
  color: var(--muted);
  font-style: italic;
}

/* A TPM should be able to read milestone/when/owner/risk status for the
   whole project in one glance - no card, just a plain table with a bottom
   line under each row (same flat, line-divided language as the rest of the
   redesigned page - see .executive-project-hero above). */
.milestone-table-scroll {
  overflow-x: auto;
}

.milestone-table {
  border-collapse: collapse;
  font-size: 0.86rem;
  width: 100%;
}

.milestone-table th {
  border-bottom: 2px solid var(--line, #e4e4e7);
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  padding: 8px 12px;
  text-align: left;
  text-transform: uppercase;
  white-space: nowrap;
}

.milestone-table td {
  border-bottom: 1px solid var(--line, #e4e4e7);
  padding: 10px 12px;
  vertical-align: top;
}

.milestone-table tbody tr:hover {
  background: var(--panel, #f4f4f5);
}

.milestone-table td strong {
  display: block;
  font-size: 0.92rem;
}

.milestone-table-status-pill {
  background: var(--panel, #f4f4f5);
  border-radius: 999px;
  color: var(--muted);
  display: inline-block;
  font-size: 0.72rem;
  font-weight: 800;
  padding: 3px 10px;
  white-space: nowrap;
}

.milestone-table-status-pill.score-high {
  background: rgba(21, 128, 61, 0.12);
  color: #15803d;
}

.milestone-table-status-pill.score-mid {
  background: rgba(37, 99, 235, 0.12);
  color: #1d4ed8;
}

.milestone-table-status-pill.score-low {
  background: rgba(185, 28, 28, 0.12);
  color: var(--danger-strong, #b91c1c);
}

.milestone-table td[data-label="End time"] {
  white-space: nowrap;
}

.comment-count-badge {
  align-items: center;
  color: var(--muted);
  display: inline-flex;
  font-size: 0.78rem;
  gap: 3px;
  margin-left: 6px;
}

.milestone-table-overdue {
  color: var(--danger-strong, #b91c1c);
  font-weight: 800;
}

.milestone-table-overdue-tag {
  background: rgba(185, 28, 28, 0.12);
  border-radius: 999px;
  color: var(--danger-strong, #b91c1c);
  display: inline-block;
  font-size: 0.66rem;
  font-weight: 800;
  margin-left: 6px;
  padding: 1px 7px;
  text-transform: uppercase;
}

.milestone-table td[data-label="Risks"] {
  max-width: 360px;
}

.milestone-table-risk-summary {
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  display: -webkit-box;
  line-height: 1.35;
  overflow: hidden;
}

.milestone-table-risk-count {
  margin-top: 4px;
}

.milestone-table td[data-label="Actions"] .attention-item-actions {
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* Four buttons per row at full size took a column wide enough to squeeze the
   milestone's own name into three lines. Compact here only - they are a row's
   controls, not the page's actions. */
.milestone-table td[data-label="Actions"] .attention-item-actions button {
  font-size: 0.74rem;
  min-height: 30px;
  padding: 0 10px;
}

/* Enough width for two of them per line. Left to itself the column sized to
   whatever was left over and broke the four into 1 + 2 + 1, which reads as
   three unrelated rows of controls rather than one block. */
.milestone-table td[data-label="Actions"] {
  min-width: 190px;
}

/* The name is what the row is; it gets the width before the columns that only
   hold a date or a pill do. */
.milestone-table td[data-label="Milestone"],
.milestone-table th:first-child {
  min-width: 230px;
}

/* The row's own flag, in the words Attention Needed would have used. A flagged
   milestone stays in this table now instead of being lifted out of it, so this
   is what carries "overdue" down the left edge where the eye already is. */
.milestone-flag {
  border-radius: 999px;
  display: inline-block;
  font-size: 0.64rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  margin-bottom: 4px;
  padding: 2px 8px;
  text-transform: uppercase;
}

.milestone-flag.score-low {
  background: var(--danger-surface);
  color: #93301f;
}

.milestone-flag.score-warn {
  background: #fbeeda;
  color: #8a5a15;
}

.milestone-flag.score-mid {
  background: var(--panel, #f4f4f5);
  color: var(--muted);
}

/* A tinted left edge on the flagged rows, so which ones need attention is
   legible while scanning the table rather than only while reading it. */
.milestone-table tbody tr.milestone-row-flagged td:first-child {
  box-shadow: inset 3px 0 0 currentColor;
  padding-left: 15px;
}

.milestone-table tbody tr.milestone-row-flagged.score-low td:first-child {
  color: #c0392b;
}

.milestone-table tbody tr.milestone-row-flagged.score-warn td:first-child {
  color: #c08a1e;
}

.milestone-table tbody tr.milestone-row-flagged td:first-child strong {
  color: var(--ink);
}

/* An email is one long unbreakable token, so it was being split mid-word
   ("dana@example.c / om"). Given a column of its own it can end in an ellipsis
   instead, with the full address on hover. */
.milestone-table td[data-label="Owner"] {
  max-width: 180px;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.milestone-focus-board {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 8px;
  display: grid;
  gap: 12px;
  /* A grid item's automatic minimum size resolves to 0 (not its content
     size) once overflow isn't visible - without this override, this board
     gets crushed to near-zero height whenever it's a row inside another
     grid that's short on space (e.g. the milestone focus modal on mobile),
     silently hiding the whole timeline. This min-height is an explicit
     value (not auto), so it sidesteps that quirk on its own - overflow can
     stay visible so a fork-marker popover isn't clipped when it opens near
     the bottom of the board. */
  min-height: min-content;
  overflow: visible;
  padding: 12px;
}

.milestone-focus-header {
  align-items: start;
  display: flex;
  gap: 12px;
  justify-content: space-between;
}

.milestone-focus-header h3 {
  font-size: 1rem;
  margin: 2px 0 0;
}

.milestone-focus-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: flex-end;
}

.milestone-focus-legend span {
  align-items: center;
  color: var(--muted);
  display: inline-flex;
  font-size: 0.68rem;
  font-weight: 850;
  gap: 5px;
}

.milestone-focus-legend i {
  border-radius: 999px;
  display: inline-block;
  height: 8px;
  width: 22px;
}

.milestone-focus-legend .main {
  background: var(--theruka-green);
}

.milestone-focus-legend .fork {
  background: #2563eb;
}

.milestone-focus-legend .risk {
  background: var(--danger-strong);
}

.milestone-focus-legend .dependency {
  background: #7c3aed;
  height: 2px;
}

.milestone-focus-legend .dependency.conflict {
  background: var(--danger-strong);
}

.project-timeline-drift {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin: 10px 0;
  padding: 10px 12px;
}

.project-timeline-drift-stat {
  display: grid;
  gap: 2px;
}

.project-timeline-drift-stat span {
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 850;
  text-transform: uppercase;
}

.project-timeline-drift-stat strong {
  font-size: 0.92rem;
}

.project-timeline-drift-contributors {
  border-top: 1px solid var(--theruka-line, #e4e4e7);
  display: grid;
  gap: 4px;
  grid-column: 1 / -1;
  padding-top: 8px;
}

.project-timeline-drift-contributors span {
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 850;
  text-transform: uppercase;
}

.project-timeline-drift-contributors ul {
  display: flex;
  flex-wrap: wrap;
  gap: 6px 16px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.project-timeline-drift-contributors li {
  align-items: center;
  display: inline-flex;
  font-size: 0.78rem;
  gap: 6px;
}

.milestone-calendar-scale {
  background:
    linear-gradient(90deg, rgba(203, 213, 225, 0.7) 1px, transparent 1px),
    #f9fafb;
  background-size: 25% 100%;
  border: 1px solid var(--line);
  border-radius: 7px;
  height: 34px;
  position: relative;
}

.milestone-calendar-scale span {
  color: var(--muted);
  font-size: 0.66rem;
  font-weight: 850;
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  white-space: nowrap;
}

.milestone-gantt-list {
  display: grid;
  gap: 10px;
  position: relative;
}

.milestone-dependency-overlay {
  inset: 0;
  pointer-events: none;
  position: absolute;
  z-index: 2;
}

.milestone-dependency-arrow {
  fill: none;
  stroke: #7c3aed;
  stroke-width: 1.75;
}

.milestone-dependency-arrow.conflict {
  stroke: var(--danger-strong);
  stroke-dasharray: 4 3;
}

.milestone-dependency-arrowhead {
  fill: #7c3aed;
}

.milestone-dependency-arrowhead.conflict {
  fill: var(--danger-strong);
}

.milestone-dependency-label {
  color: #6d28d9 !important;
}

.milestone-dependency-label.conflict {
  color: var(--danger-strong) !important;
}

/* Map + detail panel side by side. The map wants width (the ribbons are only
   legible with a wide gutter between columns) and the detail panel is a fixed
   reading column, so this is 1fr + a fixed sidebar rather than an even split. */
.risk-map-layout {
  align-items: start;
  display: grid;
  gap: 18px;
  grid-template-columns: minmax(0, 1fr) 320px;
  margin-bottom: 18px;
}

.risk-map-board {
  display: grid;
  gap: 14px;
  min-width: 0;
}

/* The gutter is where the ribbons live, so it scales with the viewport instead
   of staying at a fixed 28px - at desktop width that gap was too narrow for a
   dozen flows to be told apart. */
.risk-map-columns {
  display: grid;
  gap: clamp(52px, 6vw, 116px);
  /* Scaled by the MAP's width where the browser can say (cqw is 1% of the
     .risk-map-frame container), not the window's: with theruKO's column open
     a wide window no longer means a wide map. */
  gap: clamp(28px, 7cqw, 116px);
  position: relative;
}

.risk-map-column {
  display: grid;
  gap: 10px;
  grid-template-rows: auto 1fr;
  min-width: 0;
}

.risk-map-column h4 {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.03em;
  margin: 0;
  text-transform: uppercase;
}

/* Centred rather than top-aligned: columns hold very different numbers of nodes
   (3 projects vs 9 classifications), and centring makes the flows fan out
   symmetrically instead of dragging every ribbon downward. */
.risk-map-node-list {
  align-content: center;
  display: grid;
  gap: 10px;
}

.risk-map-node {
  align-items: center;
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 10px;
  cursor: pointer;
  display: flex;
  gap: 8px;
  justify-content: space-between;
  min-width: 0;
  padding: 10px 12px;
  position: relative;
  text-align: left;
  transition: border-color 0.15s ease, box-shadow 0.15s ease, opacity 0.15s ease;
  z-index: 3;
}

.risk-map-node:hover,
.risk-map-node:focus-visible {
  border-color: var(--accent-brand);
}

.risk-map-node.is-selected {
  background: rgba(20, 99, 90, 0.14);
  border-color: var(--accent-brand);
  box-shadow: 0 0 0 2px rgba(20, 99, 90, 0.32);
}

.risk-map-node.is-linked {
  border-color: rgba(37, 99, 235, 0.55);
}

/* Dimmed, not hidden - the shape of the whole map stays readable while one
   path through it is picked out. */
.risk-map-node.is-dimmed {
  opacity: 0.3;
}

.risk-map-node-label {
  color: var(--ink);
  font-size: 0.82rem;
  font-weight: 800;
  overflow-wrap: anywhere;
}

/* The count and its share of the column, together.
   The count answers "how many"; the share answers the question somebody opens
   this map with, which is whether one cause has become the story. */
.risk-map-node-figures {
  align-items: center;
  display: flex;
  flex-shrink: 0;
  gap: 6px;
}

.risk-map-node-share {
  color: var(--muted);
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  font-weight: 700;
}

/* More than half the column in one classification. --warn rather than a danger
   red: this is "look here", not "something is broken" - a dominant cause can be
   perfectly healthy if it is the one you already know about and are working on. */
.risk-map-node-share.is-dominant {
  color: var(--warn);
}

.risk-map-link-overlay {
  inset: 0;
  pointer-events: none;
  position: absolute;
  z-index: 2;
}

/* Filled bands, not hairlines: thickness is the flow's share of the node edge it
   leaves, so the map reads by shape before anyone reads a number. */
.risk-map-link {
  fill: var(--accent-brand);
  /* 0.24 was a wash - on a light panel the ribbons read as faint blocks of
     tint rather than as flow, which is most of why the map looked like three
     rectangles that connected nothing. Enough presence to be the subject of the
     picture, with a defined edge so adjacent bands separate where they meet. */
  opacity: 0.38;
  stroke: var(--accent-brand);
  stroke-opacity: 0.5;
  stroke-width: 1;
  transition: opacity 0.15s ease;
}

.risk-map-board.has-selection .risk-map-link.is-active {
  opacity: 0.62;
}

.risk-map-board.has-selection .risk-map-link.is-dimmed {
  opacity: 0.07;
}

.risk-map-link-label {
  fill: var(--ink);
  font-size: 11px;
  font-weight: 800;
  paint-order: stroke fill;
  stroke: var(--panel);
  stroke-linejoin: round;
  stroke-width: 3px;
}

/* Detail panel (renderRiskMapDetailHtml) - what a clicked node is actually
   made of. Sticky so it stays beside the map on a tall board. */
.risk-map-detail {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 14px;
  min-width: 0;
  padding: 16px;
  position: sticky;
  top: 12px;
}

.risk-map-detail h3 {
  font-size: 1.05rem;
  margin: 2px 0 4px;
  overflow-wrap: anywhere;
}

.risk-map-detail-lead {
  color: var(--ink);
  font-size: 0.82rem;
  font-weight: 700;
  margin: 0 0 10px;
}

.risk-map-detail-hint {
  color: var(--muted);
  font-size: 0.78rem;
  margin: 0 0 14px;
}

.risk-map-breakdown {
  margin-top: 14px;
}

.risk-map-breakdown h5 {
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  margin: 0 0 6px;
  text-transform: uppercase;
}

.risk-map-breakdown ul {
  display: grid;
  gap: 4px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.risk-map-breakdown li {
  display: block;
}

/* Each row jumps to the node it names (see gotoRiskMapNode), so it is a button
   styled as a plain row rather than anything button-shaped. */
.risk-map-breakdown-row {
  align-items: center;
  background: none;
  border: 0;
  border-radius: 5px;
  /* Inherit, not a literal. The generic button style paints white text for a
     filled pill, and this row dropped that background without dropping the
     colour with it - so the label was white on a white panel, legible only
     across the part of the row the proportional bar happened to cover. The
     shorter the bar, the more of its own label it hid, which is exactly
     backwards. Inheriting also keeps it right in dark mode, where the panel's
     ink is light and a hardcoded dark value would have reintroduced the bug
     with the themes swapped. */
  color: inherit;
  cursor: pointer;
  display: grid;
  gap: 8px;
  grid-template-columns: minmax(0, 1fr) auto;
  padding: 5px 8px;
  position: relative;
  text-align: left;
  width: 100%;
}

.risk-map-breakdown-row:hover,
.risk-map-breakdown-row:focus-visible {
  outline: 1px solid rgba(37, 99, 235, 0.5);
}

/* The lens strip only exists below 900px - see the media query in
   dashboard-responsive.css, which is also where it becomes visible. */
.risk-map-lens {
  display: none;
}

/* The proportional bar is a background layer behind the label rather than a
   separate column, so long classification names keep the full row width. */
.risk-map-breakdown-bar {
  background: rgba(37, 99, 235, 0.16);
  border-radius: 5px;
  inset: 0 auto 0 0;
  position: absolute;
  width: var(--risk-map-share, 0%);
}

.risk-map-breakdown-label {
  font-size: 0.78rem;
  overflow-wrap: anywhere;
  position: relative;
}

.risk-map-breakdown-count {
  font-size: 0.78rem;
  font-weight: 800;
  position: relative;
}

.risk-map-clear {
  margin-top: 16px;
  width: 100%;
}

@media (max-width: 1180px) {
  .risk-map-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .risk-map-detail {
    position: static;
  }
}

/* THE SPACE THE MAP HAS, NOT THE WINDOW'S.
   ---------------------------------------------------------------------------
   The rule above asks how wide the WINDOW is. With theruKO's column open on a
   wide monitor the window is still wide, so the map kept its 320px side panel
   and its three columns were squeezed until every label wrapped one letter
   per line (reported from a 1920px screen). The map now asks how wide it
   itself is: under 1120px - three readable columns, their ribbons and the
   panel - the panel moves below the map and the map takes the full width. */
.risk-map-frame {
  container-name: risk-map;
  container-type: inline-size;
}

@container risk-map (max-width: 1120px) {
  .risk-map-layout {
    grid-template-columns: minmax(0, 1fr);
  }

  .risk-map-detail {
    position: static;
  }
}

/* A label breaks between words, never inside one. overflow-wrap: anywhere let
   "Resource constraints" become a column of single letters the moment the
   node got narrow; a word that genuinely cannot fit still breaks, but only
   then. */
.risk-map-node-label {
  overflow-wrap: break-word;
  word-break: normal;
}

.milestone-gantt-row {
  display: grid;
  gap: 10px;
  grid-template-columns: minmax(180px, 0.28fr) minmax(0, 1fr);
  min-height: 108px;
}

.milestone-gantt-label {
  background: #fbfcfd;
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink);
  display: grid;
  gap: 5px;
  min-width: 0;
  padding: 10px;
  text-align: left;
}

.milestone-gantt-label:hover {
  background: #f4f4f5;
  border-color: #a1a1aa;
}

.milestone-gantt-label strong,
.milestone-gantt-label span,
.milestone-gantt-label small {
  overflow-wrap: anywhere;
}

.milestone-gantt-label span,
.milestone-gantt-label small {
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 780;
  line-height: 1.3;
}

.milestone-gantt-track {
  background:
    linear-gradient(90deg, rgba(212, 212, 216, 0.65) 1px, transparent 1px),
    #f9fafb;
  background-size: 25% 100%;
  border: 1px solid var(--line);
  border-radius: 8px;
  min-height: 126px;
  /* Was overflow:hidden, which clipped the fork-marker popover whenever it opened near
     the bottom of the track (thin-line rows are compact, so this happens often). Bars
     are already constrained to 0-100% width by construction, so nothing relies on this
     clipping the way a filled pill's rounded corners once might have. */
  overflow: visible;
  position: relative;
}

/* Track lines are drawn thin (a hairline plus two small end-dots) rather than
   filled pills - closer to a plain timeline diagram than a Gantt chart bar,
   and the duration/caption moves out to milestone-track-label so the line
   itself never has to stretch to fit text. */
/* Scoped under .milestone-gantt-track (rather than the bare class alone) so
   this beats .executive-dashboard-shell button's min-height/padding reset -
   these are hairline click targets, not visible buttons. */
.milestone-gantt-track .milestone-gantt-bar,
.milestone-gantt-track .milestone-fork-bar {
  background: none;
  border: 0;
  color: var(--theruka-green);
  cursor: pointer;
  height: 16px;
  min-height: 0;
  min-width: 16px;
  padding: 0;
  position: absolute;
}

.milestone-gantt-bar::before,
.milestone-fork-bar::before {
  background: currentColor;
  border-radius: 999px;
  content: "";
  height: 3px;
  left: 0;
  position: absolute;
  right: 0;
  top: 50%;
  transform: translateY(-50%);
}

.milestone-gantt-track .milestone-gantt-bar {
  top: 24px;
}

.milestone-gantt-track .milestone-fork-bar {
  color: #2563eb;
}

.track-dot {
  background: currentColor;
  border: 2px solid #f9fafb;
  border-radius: 999px;
  height: 8px;
  position: absolute;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 8px;
}

.milestone-fork-bar .track-dot {
  height: 7px;
  width: 7px;
}

.track-dot--start {
  left: 0;
}

.track-dot--end {
  left: 100%;
}

/* The duration + caption pair that used to sit inside the pill now floats
   just past the line's end dot, matching a plain-timeline-diagram label
   rather than a chart-bar label. */
.milestone-track-label {
  align-items: baseline;
  display: flex;
  gap: 5px;
  margin-left: 10px;
  position: absolute;
  transform: translateY(-50%);
  white-space: nowrap;
}

/* Once the line's end dot is close to the track's right edge, flip the label
   to sit before the line's start dot instead - otherwise overflow:hidden on
   the track silently clips it. */
.milestone-track-label.flip {
  flex-direction: row-reverse;
  margin-left: 0;
  margin-right: 10px;
  transform: translate(-100%, -50%);
}

/* The bar spans so much of the track that the label fits on neither side. Pinned
   to the inner right edge, in normal reading order - flip reverses the row
   because it reads backwards from the start dot, which does not apply here.
   The tiny backdrop keeps it readable where it lands over its own bar. */
.milestone-track-label.pin {
  /* Nearly opaque, and lifted: a pinned label lands over its own bar and,
     on a crowded row, close to the delta chip at the track's edge. At 88% the
     bar showed through the digits. */
  background: color-mix(in srgb, var(--panel-strong) 96%, transparent);
  border-radius: 4px;
  margin-left: 0;
  margin-right: 10px;
  padding: 1px 5px;
  position: absolute;
  transform: translate(-100%, -50%);
  z-index: 3;
}

.milestone-track-label strong {
  color: var(--theruka-green);
  font-size: 0.72rem;
  font-weight: 900;
}

.milestone-track-label em {
  color: var(--muted);
  font-size: 0.64rem;
  font-style: normal;
  font-weight: 780;
}

.milestone-track-label.fork strong {
  color: #2563eb;
}

.milestone-track-label.fork.risk-open strong {
  color: var(--danger-strong);
}

/* Groups the duration+title pair as its own inline-flex unit, always in row
   (never reversed) order, so .milestone-track-label.flip's row-reverse only
   swaps which piece sits closer to the anchor dot (this group vs. the delta
   note) instead of scrambling "32d"/"Milestone revision" out of order. */
.milestone-track-label-group {
  align-items: baseline;
  display: inline-flex;
  gap: 5px;
}

/* How a fork's end date compares to the original commitment and to the fork
   right before it - the date itself lives in a pill up at the ruler level
   (.milestone-track-date.fork below), linked to this row by a thin
   connector line, so this is just the comparison, not the date again. Numbers
   only ("+7d · +3d") rather than spelling out "vs original"/"vs prev" inline -
   that wording used to overflow past the track edge once a milestone had a
   few forks, especially when the label had to flip to the line's start side
   near "Today". The full wording lives in the title (hover) and the fork
   detail modal (click, via [data-open-fork-delta] below) instead. A <button>,
   not a <small>, so it's independently clickable from the row's own bar/label
   - needs the global button chrome reset since it should read as a small
   inline chip, not a full-size button. */
.milestone-track-label-delta {
  background: none;
  border: 0;
  border-radius: 3px;
  color: var(--muted);
  cursor: pointer;
  font-size: 0.6rem;
  font-weight: 700;
  min-height: 0;
  padding: 0 3px;
  text-decoration: underline;
  text-decoration-color: currentColor;
  text-decoration-style: dotted;
  text-underline-offset: 2px;
}

.milestone-track-label-delta:hover {
  background: rgba(37, 99, 235, 0.12);
  color: var(--ink);
}

.milestone-date-pair {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}

.milestone-date-pair b,
.milestone-track-date {
  background: #eef2f7;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: #314155;
  font-size: 0.62rem;
  font-weight: 900;
  line-height: 1;
  padding: 5px 7px;
  white-space: nowrap;
}

.milestone-track-date {
  /* Nudged down a tier (see layoutMilestoneTrackDatePills in dashboard.js)
     whenever it would otherwise overlap a neighboring pill - dates close
     together in time (a narrow range, or forks only days apart) would
     otherwise render mounted right on top of each other. */
  position: absolute;
  top: calc(8px + var(--date-pill-tier, 0) * 26px);
  transform: translateX(-50%);
  z-index: 2;
}

.milestone-track-date.finish {
  background: #f1f5f9;
}

/* A fork's end date lives up here (same row as the original's start/finish
   pills) rather than crowding its own row down at the fork line, so it reads
   at a glance alongside "24d Original" instead of buried in a caption - how
   that date compares to the original/prior fork is on the row below instead
   (.milestone-track-label-delta), linked back up here by a connector line. */
.milestone-track-date.fork {
  background: #eff6ff;
  border-color: #bfdbfe;
  color: #1d4ed8;
}

.milestone-track-date.fork.risk-open {
  background: var(--danger-wash);
  border-color: #fecaca;
  color: var(--danger-strong);
}

/* The thin line tying a fork's end-date pill back down to its own row -
   deliberately unstyled/undecorated (no z-index, first in the DOM among the
   track's absolute-positioned children) so it paints behind the original
   bar/label and every other fork's content instead of competing with it. */
.milestone-fork-connector {
  background: var(--line);
  opacity: 0.5;
  position: absolute;
  transform: translateX(-50%);
  width: 1px;
}

.milestone-fork-connector.risk-open {
  background: #fca5a5;
  opacity: 0.7;
}

.milestone-today-line {
  background: #9a3412;
  bottom: 8px;
  position: absolute;
  top: 8px;
  transform: translateX(-50%);
  width: 2px;
  z-index: 3;
}

.milestone-today-line em {
  background: #fff7ed;
  border: 1px solid #fdba74;
  border-radius: 999px;
  color: #9a3412;
  font-size: 0.58rem;
  font-style: normal;
  font-weight: 900;
  left: 50%;
  padding: 4px 6px;
  position: absolute;
  top: -2px;
  transform: translate(-50%, -100%);
  white-space: nowrap;
}

.milestone-gantt-track .milestone-fork-bar.risk-open {
  color: var(--danger-strong);
}

/* The fork marker - an inverted red triangle with "!" - sits at the exact
   point a milestone's date changed. It's a <details>/<summary> popover so
   clicking it reveals the risk, mitigation, and reason with no extra JS. */
.milestone-fork-marker {
  position: absolute;
  z-index: 4;
}

.milestone-fork-marker summary {
  align-items: flex-start;
  background: var(--danger-strong);
  clip-path: polygon(0% 0%, 100% 0%, 50% 100%);
  color: #ffffff;
  cursor: pointer;
  display: flex;
  font-size: 12px;
  font-weight: 900;
  height: 20px;
  justify-content: center;
  line-height: 1;
  list-style: none;
  padding-top: 3px;
  width: 22px;
}

.milestone-fork-marker summary::-webkit-details-marker {
  display: none;
}

.milestone-fork-marker[open] summary {
  background: #991616;
}

.milestone-fork-marker div {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 16px 34px rgba(24, 24, 27, 0.18);
  display: grid;
  gap: 6px;
  left: 0;
  padding: 10px;
  position: absolute;
  top: 20px;
  width: min(280px, calc(100vw - 80px));
  z-index: 5;
}

/* Small pointer connecting the popover back to the triangle marker it opened
   from, matching a tooltip-with-pointer style. */
.milestone-fork-marker div::before {
  background: inherit;
  border-left: inherit;
  border-top: inherit;
  content: "";
  height: 10px;
  left: 4px;
  position: absolute;
  top: -6px;
  transform: rotate(45deg);
  width: 10px;
}

.milestone-fork-marker:not([open]) div {
  display: none;
}

.milestone-fork-marker div strong {
  color: #7f1d1d;
  font-size: 0.75rem;
}

.milestone-fork-marker div span,
.milestone-fork-marker div p {
  color: var(--ink, #18181b);
  font-size: 0.72rem;
  line-height: 1.4;
  margin: 0;
}

.milestone-fork-detail {
  display: grid;
  gap: 2px;
}

.milestone-fork-detail + .milestone-fork-detail {
  border-top: 1px solid var(--line);
  padding-top: 6px;
}

.milestone-fork-marker .milestone-fork-detail span {
  color: var(--muted);
  font-size: 0.6rem;
  font-weight: 900;
  text-transform: uppercase;
}

/* Clicking a fork's "!" triangle opens this full-screen overlay instead of the
   old in-flow popover (see the delegated click listener in dashboard.js), so
   the why/impact/mitigation detail is always readable on top of everything
   else on screen, including the milestone focus modal itself. */
.fork-detail-overlay {
  align-items: center;
  background: rgba(24, 24, 27, 0.5);
  display: flex;
  inset: 0;
  justify-content: center;
  padding: 24px;
  position: fixed;
  z-index: 140;
}

.fork-detail-panel {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-modal);
  display: grid;
  gap: 12px;
  max-width: min(380px, 100%);
  padding: 22px;
  position: relative;
  width: 100%;
}

.fork-detail-panel-title {
  color: #7f1d1d;
  font-size: 0.9rem;
  margin: 0;
  padding-right: 24px;
}

.fork-detail-close {
  background: transparent;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 1.4rem;
  line-height: 1;
  padding: 4px;
  position: absolute;
  right: 12px;
  top: 12px;
}

.fork-detail-body {
  display: grid;
  gap: 8px;
}

.fork-detail-body span,
.fork-detail-body p {
  color: var(--ink, #18181b);
  font-size: 0.84rem;
  line-height: 1.5;
  margin: 0;
}

.fork-detail-body .milestone-fork-detail span {
  color: var(--muted);
  font-size: 0.66rem;
  font-weight: 900;
  text-transform: uppercase;
}

/* Depends on/Comments in the milestone focus modal used to always render
   expanded inline, crowding out room for the timeline above them. Collapsed
   to a summary + count pill (matching Risks/Revision history); clicking
   opens the real content here instead - a non-blocking floating panel
   (draggable by its header, resizable via the native browser handle, no
   dimming backdrop) so the rest of the page stays visible and usable while
   it's open, rather than the app's usual modal dialogs which go fully inert
   behind a backdrop. See openFloatingDetailPanel in dashboard.js. */
.floating-detail-panel {
  background: var(--panel-strong);
  border: 1px solid var(--line-strong, #d4d4d8);
  border-radius: 10px;
  box-shadow: 0 20px 45px rgba(24, 24, 27, 0.28);
  display: flex;
  flex-direction: column;
  max-height: calc(100vh - 32px);
  max-width: calc(100vw - 32px);
  min-height: 160px;
  min-width: 280px;
  overflow: hidden;
  position: fixed;
  resize: both;
  width: 380px;
  z-index: 125;
}

.floating-detail-panel-header {
  align-items: center;
  border-bottom: 1px solid var(--line);
  cursor: grab;
  display: flex;
  flex: 0 0 auto;
  gap: 8px;
  justify-content: space-between;
  padding: 10px 12px;
  touch-action: none;
}

.floating-detail-panel-header.dragging {
  cursor: grabbing;
}

.floating-detail-panel-header h4 {
  font-size: 0.85rem;
  margin: 0;
}

.floating-detail-panel-close {
  background: transparent;
  border: none;
  color: var(--muted);
  cursor: pointer;
  flex: 0 0 auto;
  font-size: 1.2rem;
  line-height: 1;
  padding: 2px 6px;
}

.floating-detail-panel-body {
  overflow: auto;
  padding: 12px;
}

/* The panel header already shows the section title, so the identical <h4> baked
   into renderCommentsSectionHtml() (still needed for its other, non-floating
   uses) would just be a duplicate line here. */
.floating-detail-panel-body > .comments-section > h4 {
  display: none;
}

/* Risks/Revision history's own list, moved here from the inline-expanded
   <details> they used to be (same idea as .comments-thread). */
.floating-detail-list {
  display: grid;
  gap: 8px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.floating-detail-list li {
  background: var(--panel-strong, #f9fafb);
  border: 1px solid var(--line-strong, #e4e4e7);
  border-radius: 6px;
  display: grid;
  gap: 2px;
  padding: 8px 10px;
}

.floating-detail-list li strong {
  color: var(--ink);
  font-size: 0.72rem;
}

.floating-detail-list li span {
  color: var(--muted);
  font-size: 0.76rem;
}

.project-detail-panel {
  min-height: 220px;
}

.milestone-focus-detail {
  display: grid;
  gap: 14px;
}

.milestone-focus-detail dl,
.milestone-focus-related {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  margin: 0;
}

.milestone-focus-detail dl div,
.milestone-focus-related details {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 10px;
}

.milestone-focus-related details summary {
  align-items: center;
  cursor: pointer;
  display: flex;
  gap: 8px;
  justify-content: space-between;
}

.milestone-focus-related details summary h4 {
  margin: 0;
}

.milestone-focus-detail dt {
  color: var(--muted);
  font-size: 0.64rem;
  font-weight: 900;
  text-transform: uppercase;
}

.milestone-focus-detail dd {
  color: var(--ink);
  font-size: 0.84rem;
  font-weight: 850;
  margin: 3px 0 0;
}

.milestone-focus-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.milestone-focus-related h4 {
  font-size: 0.8rem;
  margin: 0 0 8px;
}

/* "If this slips" - the blast radius of a proposed date change. Rows are a
   three-part grid rather than a table: the name, where it lives, and the push,
   with the push right-aligned because it is the number being scanned for. */
.milestone-impact-panel .admin-field {
  margin: 8px 0;
}

.milestone-impact-summary {
  font-weight: 600;
  margin: 12px 0 6px;
}

.milestone-impact-clear {
  color: #14635a;
  margin: 12px 0 0;
}

.milestone-impact-list {
  display: grid;
  gap: 6px;
  list-style: none;
  margin: 0 0 10px;
  padding: 0;
}

.milestone-impact-list li {
  align-items: baseline;
  border-left: 3px solid #d6e0dd;
  display: grid;
  gap: 2px 10px;
  grid-template-columns: 1fr auto;
  padding: 4px 0 4px 10px;
}

/* The only visual difference that matters here: this one leaves the project,
   so somebody else has to be told. */
.milestone-impact-list li.is-cross {
  border-left-color: #14635a;
}

.milestone-impact-name {
  font-weight: 600;
}

.milestone-impact-days {
  color: #b4342c;
  font-variant-numeric: tabular-nums;
  grid-row: span 2;
  white-space: nowrap;
}

.milestone-impact-where {
  color: #6b7a77;
  font-size: 0.78rem;
  grid-column: 1;
}

/* The dependency picker: which project, then which milestone in it.
   ---------------------------------------------------------------------------
   Two columns, because that is the shape of the question. The project column is
   narrow and fixed; the milestone column takes the rest and is the only one that
   scrolls far, since one project's milestones are a readable number where the
   whole workspace's were not. On a phone they stack, and the project list keeps
   its own short scroll so the milestones are still reachable without paging past
   every project first. */
/* Still worn by the "if this slips" list, which names the same cross-project
   reach the picker's own tag does. */
.milestone-dependency-cross {
  background: #eef4f2;
  border-radius: 999px;
  color: #14635a;
  font-size: 0.68rem;
  font-weight: 500;
  padding: 1px 7px;
}

.dependency-picker-columns {
  display: grid;
  gap: 14px;
  grid-template-columns: minmax(180px, 240px) 1fr;
  margin: 12px 0;
}

@media (max-width: 640px) {
  .dependency-picker-columns {
    grid-template-columns: 1fr;
  }

  .dependency-picker-column .dependency-picker-list {
    max-height: 160px;
  }
}

.dependency-picker-column-head {
  color: #4a5b57;
  font-size: 0.78rem;
  font-weight: 600;
  margin: 0 0 6px;
}

.dependency-picker-list {
  display: grid;
  gap: 4px;
  list-style: none;
  margin: 0;
  max-height: 280px;
  overflow-y: auto;
  padding: 0;
}

/* `color` is load-bearing and was missing: the base rule is
   `button { background: var(--accent); color: white }`, so overriding the
   background alone left every project name as white text on a white panel. The
   list rendered as a column of counts with no names beside them - invisible in
   the CSS, obvious in a screenshot. */
.dependency-project-row {
  align-items: center;
  background: none;
  border: 1px solid transparent;
  border-radius: 8px;
  color: var(--ink, #1f2937);
  cursor: pointer;
  display: flex;
  font: inherit;
  gap: 6px;
  padding: 7px 10px;
  text-align: left;
  width: 100%;
}

.dependency-project-row:hover {
  background: var(--panel-strong, #f3f4f6);
}

/* The open project is a state, not a hover: it says which list is on the right,
   so it has to hold while the pointer is over there. */
.dependency-project-row.is-open {
  background: #eef4f2;
  border-color: #14635a;
}

.dependency-project-name {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.dependency-project-tag {
  background: #eef4f2;
  border-radius: 999px;
  color: #14635a;
  flex: none;
  font-size: 0.62rem;
  padding: 1px 6px;
}

.dependency-project-count {
  color: #6b7a77;
  font-size: 0.72rem;
}

.dependency-milestone-row {
  align-items: center;
  border-radius: 8px;
  cursor: pointer;
  display: flex;
  gap: 8px;
  padding: 6px 8px;
}

.dependency-milestone-row:hover {
  background: var(--panel-strong, #f3f4f6);
}

.dependency-milestone-name {
  flex: 1;
}

.dependency-milestone-date {
  color: #6b7a77;
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
}

/* Dimmed, but never silent: the row beneath it carries the button that says
   why, so this is a state with an explanation rather than a dead control. */
.dependency-milestone-row.is-blocked {
  cursor: default;
  opacity: 0.55;
}

.dependency-why-not {
  display: block;
  font-size: 0.72rem;
  margin: 0 0 4px 30px;
}

.dependency-picker-chosen {
  border-top: 1px solid var(--line);
  margin-bottom: 12px;
  padding-top: 10px;
}

.dependency-chosen-list {
  display: grid;
  gap: 4px;
  list-style: none;
  margin: 6px 0 0;
  padding: 0;
}

.dependency-chosen-list li {
  align-items: center;
  display: flex;
  gap: 10px;
  justify-content: space-between;
}

.dependency-chosen-list em {
  color: #6b7a77;
  font-size: 0.72rem;
  font-style: normal;
}

.dependency-problem-steps {
  margin: 8px 0;
  padding-left: 18px;
}

/* Selectable and monospaced because its purpose is to be photographed and read
   back by somebody who is not in front of this workspace. */
.dependency-problem-reference {
  color: #6b7a77;
  font-size: 0.75rem;
  margin: 10px 0 0;
  user-select: all;
  word-break: break-all;
}

.comments-thread {
  display: grid;
  gap: 8px;
  list-style: none;
  margin: 0 0 10px;
  max-height: 220px;
  overflow-y: auto;
  padding: 0;
}

.comments-thread-item {
  background: #fff;
  border: 1px solid var(--line);
  border-radius: 6px;
  padding: 8px 10px;
}

.comments-thread-item p {
  margin: 4px 0 0;
  white-space: pre-wrap;
  word-break: break-word;
}

.comments-thread-meta {
  align-items: center;
  color: var(--muted);
  display: flex;
  font-size: 0.68rem;
  gap: 8px;
}

.comments-thread-meta strong {
  color: var(--ink);
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.attribution-role {
  background: #e4e4e7;
  border-radius: 999px;
  color: #3f3f46;
  flex: none;
  font-size: 0.62rem;
  font-weight: 800;
  padding: 2px 7px;
  white-space: nowrap;
}

.comments-delete-button {
  background: none;
  border: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 1rem;
  line-height: 1;
  padding: 0 2px;
}

.comments-delete-button:hover {
  color: var(--danger-strong, #b91c1c);
}

.comments-form {
  display: grid;
  gap: 6px;
}

.comments-mention-input-wrapper {
  position: relative;
}

.comments-form textarea {
  font: inherit;
  padding: 6px 8px;
  resize: vertical;
  width: 100%;
}

/* Typing @ in the textarea opens this, filtered live against the roster -
   see wireCommentsSection's input handler. Positioned to hug the textarea
   rather than tracking the caret exactly (a plain textarea has no native
   caret-coordinates API), which keeps this simple for a 1-2 line composer. */
.comments-mention-dropdown {
  background: var(--panel-strong, #ffffff);
  border: 1px solid var(--line, #e4e4e7);
  border-radius: 8px;
  box-shadow: var(--theruka-shadow, 0 12px 28px rgba(15, 23, 42, 0.16));
  left: 0;
  list-style: none;
  margin: 4px 0 0;
  max-height: 220px;
  overflow-y: auto;
  padding: 4px;
  position: absolute;
  right: 0;
  top: 100%;
  z-index: 20;
}

.comments-mention-dropdown li {
  border-radius: 6px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 6px 8px;
}

.comments-mention-dropdown li strong {
  font-size: 0.84rem;
}

.comments-mention-dropdown li span {
  color: var(--muted);
  font-size: 0.72rem;
}

.comments-mention-dropdown li:hover,
.comments-mention-dropdown li.active {
  background: var(--panel, #f4f4f5);
}

.comments-form-actions {
  display: flex;
  justify-content: flex-end;
}

.comments-form-status {
  color: var(--warn);
}

.executive-report {
  display: grid;
  gap: 10px;
}

.executive-report-meta {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 700;
  margin: 0;
  text-align: right;
}

.ai-recommendations {
  display: grid;
  gap: 10px;
  margin-bottom: 10px;
}

.ai-recommendation-list {
  display: grid;
  gap: 8px;
}

.ai-recommendation-card {
  align-items: start;
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  display: grid;
  gap: 9px;
  grid-template-columns: auto minmax(0, 1fr);
  padding: 10px;
}

.recommendation-priority {
  align-items: center;
  border-radius: 999px;
  display: inline-flex;
  font-size: 0.62rem;
  font-weight: 900;
  gap: 4px;
  line-height: 1;
  padding: 6px 8px;
  text-transform: uppercase;
}

.recommendation-priority-icon {
  flex: 0 0 auto;
  height: 11px;
  width: 11px;
}

.recommendation-priority.score-low {
  background: #f7d8d3;
  color: #8a2d20;
}

.recommendation-priority.score-mid {
  background: var(--warn-surface);
  color: var(--warn);
}

.recommendation-priority.score-warn {
  background: #fdba74;
  color: #7c2d12;
}

.ai-recommendation-card strong,
.ai-recommendation-card p,
.ai-recommendation-card small {
  display: block;
}

.ai-recommendation-card p {
  color: var(--ink);
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1.35;
  margin: 3px 0 0;
}

.ai-recommendation-card small {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1.35;
  margin-top: 5px;
}

.ai-recommendation-actions {
  display: flex;
  justify-content: flex-end;
  margin-top: 8px;
}

.ai-recommendation-actions button {
  font-size: 0.7rem;
  min-height: 28px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 140ms ease;
}

.ai-recommendation-card:hover .ai-recommendation-actions button,
.ai-recommendation-actions button:focus-visible {
  opacity: 1;
  pointer-events: auto;
}

.shared-readonly .ai-recommendation-actions {
  display: none;
}

.ai-recommendation-hint {
  color: var(--muted);
  display: block;
  font-size: 0.72rem;
  font-weight: 700;
  margin: 6px 0;
}

.ai-portfolio-score {
  align-items: flex-start;
  display: flex;
  flex-direction: column;
  gap: 4px;
  margin: 6px 0 12px;
}

.ai-portfolio-score-summary {
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1.5;
  margin: 0;
}

.ai-recommendation-project-link,
.ai-recommendation-take-action {
  display: inline-block;
  font-size: 0.76rem;
  margin-top: 4px;
}

/* Double-class selector so this reliably beats .link-button's own
   border:none - both are single-class selectors of equal specificity, and
   .link-button is declared later in this file, so a plain
   .ai-recommendation-take-action rule here would lose regardless of the
   property order within it. */
.link-button.ai-recommendation-project-link,
.link-button.ai-recommendation-take-action {
  border: 1px solid currentColor;
  border-radius: 999px;
  padding: 3px 10px;
}

.ai-recommendation-take-action {
  margin-left: 12px;
}

.ai-recommendation-context {
  margin: 0 0 12px;
}

.ai-recommendation-upsell {
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1.5;
  padding: 4px 0;
}

/* Marks recommendations served from an earlier generation because the plan has
   moved on since. Deliberately more prominent than .ai-recommendation-hint -
   the whole point is that this advice must not be read as current. */
.ai-recommendation-stale {
  background: var(--panel);
  border-left: 3px solid var(--warn);
  border-radius: var(--radius-control);
  color: var(--muted);
  font-size: 0.8rem;
  line-height: 1.5;
  margin: 8px 0 0;
  padding: 8px 12px;
}

.ai-timeline-summary h3 {
  font-size: 0.96rem;
  margin: 0 0 8px;
}

/* Evidence, sitting above the AI suggestions in the mitigation dialog: what
   this team's own decisions did in situations like this one. Set apart from the
   form fields because it is something to read, not something to fill in. */
.mitigation-precedent {
  background: #f4f7f6;
  border-left: 3px solid #14635a;
  border-radius: 6px;
  margin: 10px 0;
  padding: 10px 12px;
}

.mitigation-precedent-title {
  font-size: 0.8rem;
  font-weight: 600;
  margin: 0 0 6px;
}

.mitigation-precedent-list {
  display: grid;
  gap: 5px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.mitigation-precedent-list li {
  align-items: baseline;
  display: grid;
  font-size: 0.8rem;
  gap: 2px 10px;
  grid-template-columns: 1fr auto;
}

.mitigation-precedent-action {
  font-weight: 600;
}

/* The approach currently selected in the picker below. The list is a
   comparison; this is what turns it into feedback on a live choice. */
.mitigation-precedent-list li.is-chosen {
  background: #e6efec;
  border-radius: 4px;
  margin: 0 -6px;
  padding: 2px 6px;
}

.mitigation-precedent-chosen {
  color: #14635a;
  font-size: 0.68rem;
  font-weight: 500;
  text-transform: uppercase;
}

.mitigation-precedent-rate {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.mitigation-precedent-sample,
.mitigation-precedent-caveat,
.mitigation-precedent-empty {
  color: #6b7a77;
  font-size: 0.74rem;
  grid-column: 1 / -1;
  margin: 0;
}

/* Lift outranks the raw rate in the ladder, so it is allowed to look like the
   stronger statement it is - same size, but ink rather than grey, and a rule to
   mark it as a finding rather than a footnote. */
.mitigation-precedent-lift {
  border-left: 2px solid #2f6f60;
  color: #24423c;
  font-size: 0.76rem;
  grid-column: 1 / -1;
  line-height: 1.45;
  margin: 0;
  padding-left: 9px;
}

/* The decision-quality grid. Two by two, so it is laid out as two by two -
   the shape carries the argument, and a flat list of four counts would not. */
.decision-quality-grid {
  display: grid;
  gap: 10px;
  /* Exactly two columns, not auto-fit. This is a two-by-two and the shape is
     the argument - auto-fit put three cells on the first row and stranded the
     fourth, which reads as a list of four counts and loses the two axes
     entirely. Pinned by the layout assertion in
     test/dashboard/decision-quality-render.test.js. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  list-style: none;
  margin: 12px 0 0;
  max-width: 720px;
  padding: 0;
}

/* Below this there is no room for two readable columns, so it stacks. The grid
   reading is lost, but an unreadable grid communicates less than a list. */
@media (max-width: 620px) {
  .decision-quality-grid { grid-template-columns: minmax(0, 1fr); }
}

.decision-quality-cell {
  background: #fff;
  border: 1px solid rgba(15, 40, 35, 0.12);
  border-radius: 10px;
  display: grid;
  gap: 4px;
  padding: 14px 16px;
}

/* "Got away with it" is the only cell that is bad news while looking like good
   news, so it is the only one that gets colour. Amber rather than red: it is a
   thing to look at, not a failure. */
.decision-quality-cell.is-warning {
  background: #fdf6ec;
  border-color: #d8b177;
}

.decision-quality-count {
  font-size: 1.5rem;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  line-height: 1.1;
}

.decision-quality-cell strong { font-size: 0.9rem; font-weight: 600; }

.decision-quality-meaning {
  color: #4d5a57;
  font-size: 0.8rem;
  line-height: 1.45;
}

.decision-quality-habit {
  color: #4d5a57;
  font-size: 0.85rem;
  line-height: 1.5;
  margin: 12px 0 0;
  max-width: 68ch;
}

/* The degraded-feature modal. Plain and calm on purpose: this is not an error
   the reader caused and cannot act on beyond forwarding it, so it reads as a
   notice rather than an alarm. */
.degraded-modal p { color: #24423c; line-height: 1.55; margin: 0 0 12px; max-width: 56ch; }
.degraded-modal .degraded-note { color: #5f6d6a; font-size: 0.85rem; }

/* The reference is the one thing worth copying, so it is set in mono at a size
   that survives a phone screenshot - which is how it will actually travel. */
.degraded-reference {
  background: rgba(15, 40, 35, 0.05);
  border-radius: 8px;
  display: grid;
  gap: 2px 14px;
  grid-template-columns: auto 1fr;
  margin: 0 0 12px;
  padding: 12px 14px;
}
.degraded-reference dt { color: #5f6d6a; font-size: 0.8rem; }
.degraded-reference dd {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.95rem;
  font-weight: 600;
  margin: 0;
  user-select: all;
}

/* Borrowed evidence, marked as borrowed. Dashed rather than solid, so it reads
   as provisional beside the solid rule the workspace's own lift gets. */
.mitigation-precedent-library {
  border-left: 2px dashed #7a8a86;
  color: #4d5a57;
  font-size: 0.76rem;
  grid-column: 1 / -1;
  line-height: 1.45;
  margin: 0;
  padding-left: 9px;
}

/* The pattern library settings page. Reads as a document rather than a form -
   the decision it asks for is one somebody should make after reading. */
.pattern-library-page { display: grid; gap: 16px; max-width: 64ch; }
.pattern-library-head h2 { margin: 0 0 8px; }
.pattern-library-head p,
.pattern-library-card p { color: #4d5a57; line-height: 1.55; margin: 0 0 10px; }
.pattern-library-card {
  background: #fff;
  border: 1px solid rgba(15, 40, 35, 0.12);
  border-radius: 12px;
  padding: 16px 18px;
}
.pattern-library-card h3 { font-size: 0.95rem; margin: 0 0 8px; }
.pattern-library-toggle {
  border-top: 1px solid rgba(15, 40, 35, 0.12);
  padding-top: 14px;
}
.pattern-library-toggle label { align-items: center; display: flex; font-weight: 600; gap: 9px; }
.pattern-library-note { color: #4d5a57; font-size: 0.83rem; line-height: 1.5; margin: 8px 0 0; }
.pattern-library-status { color: #24423c; font-size: 0.83rem; margin: 8px 0 0; min-height: 1.2em; }

/* The latency comparison under the triage list. Deliberately quiet: it is
   context for the rows above it, not another thing demanding a decision. */
.needs-you-latency {
  border-top: 1px solid rgba(15, 40, 35, 0.08);
  /* #6b7a77 - the muted grey used by the precedent rules nearby - measures
     4.21:1 on this page's ground, under the 4.5:1 floor for body text. This is
     one step darker and still reads as secondary. Pinned by the contrast
     assertion in test/dashboard/latency-and-lift-render.test.js. */
  color: #5f6d6a;
  font-size: 0.76rem;
  line-height: 1.45;
  margin: 10px 0 0;
  padding-top: 9px;
}

.mitigation-precedent-caveat {
  margin-top: 6px;
}

.ai-mitigation-suggestions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 8px 0;
}

.ai-mitigation-suggestions-label {
  color: var(--muted);
  flex-basis: 100%;
  font-size: 0.72rem;
  font-weight: 700;
}

.link-button.ai-mitigation-suggestion-chip {
  border: 1px solid currentColor;
  border-radius: 999px;
  padding: 3px 10px;
  text-align: left;
}

/* Inside the mitigation dialog these are not chips. A pill shape says "short
   label", and each of these is a two-line instruction - "Hold a 2-hour
   assumptions-validation workshop with key stakeholders and SMEs within one
   week..." - so the pill wrapped into a lozenge with three lines of text in it.
   Here they are cards in a column: one per row, aligned, readable. */
.mitigation-prompt-body .ai-mitigation-suggestions {
  display: grid;
  gap: 8px;
  margin: 0;
}

.mitigation-prompt-body .link-button.ai-mitigation-suggestion-chip {
  background: var(--panel, #ffffff);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: inherit;
  display: block;
  font-size: 0.82rem;
  /* .link-button is bold, which is right for a link and wrong for two lines of
     prose - it made all three read as headings. */
  font-weight: 400;
  line-height: 1.45;
  padding: 9px 11px;
  white-space: normal;
  width: 100%;
}

.mitigation-prompt-body .link-button.ai-mitigation-suggestion-chip:hover,
.mitigation-prompt-body .link-button.ai-mitigation-suggestion-chip:focus-visible {
  border-color: var(--accent, #3b5bdb);
  text-decoration: none;
}

.ai-executive-summary-sections {
  display: grid;
  gap: 4px 12px;
  grid-template-columns: max-content 1fr;
  margin: 10px 0 0;
}

.ai-executive-summary-sections dt {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 700;
  padding-top: 2px;
  text-transform: uppercase;
}

.ai-executive-summary-sections dd {
  margin: 0 0 10px;
}

.ai-premortem-result h5 {
  font-size: 0.85rem;
  margin: 12px 0 6px;
}

.ai-premortem-result h5:first-child {
  margin-top: 0;
}

.ai-premortem-result ul {
  margin: 0;
  padding-left: 18px;
}

.ai-premortem-result li {
  margin-bottom: 4px;
}

.executive-report-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.executive-report-projects {
  grid-column: 1 / -1;
}

.executive-report-section h3 {
  font-size: 0.96rem;
  margin: 0 0 8px;
}

.executive-project-score-list {
  display: grid;
  border: 1px solid var(--line);
  border-radius: 8px;
  overflow: hidden;
}

.executive-project-score-row {
  align-items: stretch;
  background: #ffffff;
  border-top: 1px solid #edf1f5;
  display: grid;
  gap: 0;
  grid-template-columns: minmax(240px, 1.3fr) repeat(3, minmax(148px, 0.7fr));
  min-height: 96px;
}

.executive-project-score-row:first-child {
  border-top: 0;
}

.executive-project-score-row:hover {
  background: #fbfcfd;
}

.score-project-identity,
.score-table-cell {
  display: grid;
  min-width: 0;
  padding: 14px 16px;
}

.score-project-identity {
  align-content: center;
  gap: 5px;
}

.score-project-identity strong {
  color: #11161d;
  font-size: 0.95rem;
  font-weight: 880;
  line-height: 1.18;
  overflow-wrap: anywhere;
}

.score-project-identity span,
.score-project-identity small {
  color: #697789;
  font-weight: 750;
  line-height: 1.25;
  overflow-wrap: anywhere;
}

.score-project-identity span {
  font-size: 0.76rem;
}

.score-project-identity small {
  font-size: 0.68rem;
}

.score-table-cell {
  --score-accent: #52687f;
  --score-soft: #f4f7fa;
  align-content: center;
  border-left: 1px solid #edf1f5;
  gap: 7px;
}

.score-table-label {
  color: #697789;
  font-size: 0.62rem;
  font-weight: 900;
  letter-spacing: 0;
  text-transform: uppercase;
}

.score-table-cell strong {
  color: #10161d;
  font-size: 1.18rem;
  font-weight: 900;
  line-height: 1;
}

.score-table-track {
  background: #e7edf3;
  border-radius: 999px;
  display: block;
  height: 6px;
  overflow: hidden;
}

.score-table-track i {
  background: var(--score-accent);
  border-radius: inherit;
  display: block;
  height: 100%;
}

.score-table-cell small {
  background: var(--score-soft);
  border: 1px solid color-mix(in srgb, var(--score-accent) 20%, transparent);
  border-radius: 999px;
  color: var(--score-accent);
  display: inline-flex;
  font-size: 0.64rem;
  font-weight: 900;
  justify-self: start;
  line-height: 1;
  padding: 5px 7px;
}

.score-table-cell.score-low {
  --score-accent: #a63a2f;
  --score-soft: #fff4f2;
}

.score-table-cell.score-mid {
  --score-accent: #9a6b05;
  --score-soft: #fff7d7;
}

.score-table-cell.score-warn {
  --score-accent: #b65c1f;
  --score-soft: #fff1e5;
}

.score-table-cell.score-high {
  --score-accent: #1f7a46;
  --score-soft: #eefaf2;
}

.executive-plan-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 10px;
}

.editable-plan-section {
  cursor: pointer;
}

.editable-plan-section.selected {
  border-color: #a1a1aa;
  box-shadow: 0 0 0 3px rgba(82, 82, 91, 0.12);
}

.editable-section-header {
  align-items: center;
  display: flex;
  gap: 10px;
  justify-content: space-between;
  margin-bottom: 8px;
}

.editable-section-header h3 {
  margin: 0;
}

.editable-section-actions {
  align-items: center;
  display: flex;
  gap: 6px;
}

.section-edit-button {
  font-size: 0.68rem;
  min-height: 28px;
  padding: 0 10px;
}

.section-view-button {
  font-size: 0.68rem;
  min-height: 28px;
  padding: 0 10px;
}

.section-edit-button:disabled {
  cursor: not-allowed;
  opacity: 0.45;
}

.section-editor-window {
  background: rgba(24, 24, 27, 0.34);
  display: none;
  inset: 0;
  padding: 28px;
  place-items: center;
  position: fixed;
  z-index: 120;
}

.section-editor-window.open {
  display: grid;
}

.section-editor-panel {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-modal);
  display: grid;
  gap: 12px;
  /* Implicit "auto" rows can under-allocate space to a row whose item has
     non-visible overflow (see .milestone-focus-board), letting later rows
     start before that item visually finishes. min-content sizes each row
     off its item's real content instead. */
  grid-auto-rows: min-content;
  /* max-height/max-width are the resize ceiling (nearly the full viewport),
     not the default size - the panel still opens at its usual width, sized
     to its content as before. resize (below) lets the user drag it bigger
     up to that ceiling, or smaller down to min-width/min-height. The wide
     variant (.section-editor-window-wide .section-editor-panel, used by the
     Gantt and Milestone focus modals) pins a starting height too, since
     those two always render tall enough to hit the old 760px cap anyway. */
  max-height: 96vh;
  max-width: 96vw;
  min-height: 240px;
  overflow: auto;
  padding: 14px;
  resize: both;
  width: min(920px, calc(100vw - 56px));
}

/* A plain min-width here would be dead on arrival - dashboard-responsive.css
   loads after this file and resets .section-editor-panel to min-width:0
   (deliberately, so the modal can still shrink to fit a narrow phone
   viewport instead of blowing out the grid). The extra .section-editor-window
   ancestor only raises specificity to win that fight, and is gated to wider
   viewports so the resize floor never fights the same mobile-shrink need. */
@media (min-width: 640px) {
  .section-editor-window .section-editor-panel {
    min-width: 420px;
  }
}

/* The risk editor, which is the densest row editor in the app: every row
   carries text, cause, classification, an owner picker, a milestone checkbox
   list, impacts and mitigations. At the shared 920px default those columns
   collapsed onto each other and the row read as a wall rather than a form.

   Roomier than the default but not as wide as the Gantt variant, and it pins a
   starting height so a single new risk opens at a workable size instead of a
   letterbox that has to be dragged before it can be filled in. */
.section-editor-window-roomy .section-editor-panel {
  height: min(720px, calc(100vh - 56px));
  max-width: 96vw;
  width: min(1180px, calc(100vw - 40px));
}

.section-editor-window-wide .section-editor-panel {
  height: min(760px, calc(100vh - 56px));
  max-width: 96vw;
  width: min(1320px, calc(100vw - 32px));
}

.section-editor-header {
  align-items: center;
  border-bottom: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  padding-bottom: 10px;
}

.section-editor-header h3 {
  font-size: 1.06rem;
  margin: 2px 0 0;
}

.section-editor-header-actions {
  align-items: center;
  display: flex;
  gap: 8px;
}

.section-editor-rows {
  display: grid;
  gap: 10px;
}

.export-option-form {
  display: grid;
  gap: 10px;
}

.export-option-form label {
  align-items: center;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink);
  cursor: pointer;
  display: flex;
  gap: 10px;
  padding: 12px;
}

.export-option-form label:hover {
  background: #f4f4f5;
  border-color: #a1a1aa;
}

.export-option-form input {
  accent-color: #52525b;
  flex: 0 0 auto;
  height: 16px;
  width: 16px;
}

.export-option-form span {
  color: var(--ink);
  font-size: 0.88rem;
  font-weight: 700;
}

.export-print-theme {
  border: 0;
  display: grid;
  gap: 10px;
  margin: 0;
  padding: 0;
}

.export-print-theme legend {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 800;
  letter-spacing: 0.03em;
  margin-bottom: 2px;
  padding: 0;
  text-transform: uppercase;
}

.section-editor-row {
  align-items: end;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  display: grid;
  gap: 10px;
  grid-template-columns: minmax(0, 1fr) auto;
  padding: 10px;
}

.section-editor-row.resolved {
  background: #f4f4f5;
}

.section-editor-row.resolved input {
  color: var(--muted);
  cursor: not-allowed;
  text-decoration: line-through;
}

.section-editor-fields {
  display: grid;
  gap: 8px;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
}

.section-editor-fields label {
  display: grid;
  gap: 4px;
}

.section-editor-fields span {
  color: var(--muted);
  font-size: 0.66rem;
  font-weight: 800;
}

.section-editor-fields input,
.section-editor-fields select,
.section-editor-fields textarea {
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink);
  font-size: 0.76rem;
  min-height: 34px;
  padding: 7px 8px;
}

.section-editor-fields textarea {
  font-family: inherit;
  line-height: 1.35;
  resize: vertical;
}

.priority-editor-row .section-editor-fields {
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}

.priority-list-item {
  gap: 4px;
}

.priority-list-item.score-low,
.risk-list-item.score-low {
  border-left: 3px solid #b64c3e;
  padding-left: 9px;
}

.priority-list-item.score-mid,
.risk-list-item.score-mid {
  border-left: 3px solid #c99b11;
  padding-left: 9px;
}

.priority-list-item.score-high,
.risk-list-item.score-high {
  border-left: 3px solid #3a9b5d;
  padding-left: 9px;
}

.risk-mitigation-preview {
  color: var(--muted);
  font-size: 0.72rem;
  font-style: italic;
}

.milestone-timeline {
  display: grid;
  gap: 12px;
  padding: 4px 0 8px;
  position: relative;
}

.milestone-timeline::before {
  background: #d4d4d8;
  bottom: 8px;
  content: "";
  left: 104px;
  position: absolute;
  top: 8px;
  width: 2px;
}

.milestone-timeline-item {
  display: grid;
  gap: 10px;
  grid-template-columns: 116px minmax(0, 1fr);
  position: relative;
}

.milestone-timeline-rail {
  align-items: start;
  display: grid;
  gap: 8px;
  grid-template-columns: minmax(0, 88px) 18px;
  padding-top: 11px;
  position: relative;
  z-index: 1;
}

.milestone-timeline-date,
.impact-date-chip {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 999px;
  color: var(--ink);
  display: inline-flex;
  font-size: 0.66rem;
  font-weight: 900;
  justify-content: center;
  line-height: 1;
  padding: 7px 8px;
  white-space: nowrap;
}

.milestone-timeline-marker {
  background: #ffffff;
  border: 3px solid #52525b;
  border-radius: 50%;
  display: block;
  height: 14px;
  margin-top: 4px;
  position: relative;
  width: 14px;
  z-index: 1;
}

.milestone-timeline-card {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  display: grid;
  gap: 10px;
  padding: 12px;
}

.milestone-timeline-card header {
  align-items: start;
  display: flex;
  gap: 12px;
  justify-content: space-between;
}

.milestone-timeline-card h4 {
  font-size: 0.92rem;
  margin: 2px 0 0;
}

.milestone-timeline-card header > span {
  background: #e4e4e7;
  border-radius: 999px;
  color: var(--ink);
  flex: 0 0 auto;
  font-size: 0.66rem;
  font-weight: 900;
  padding: 6px 8px;
}

.milestone-timeline-card > p {
  color: var(--muted);
  font-size: 0.74rem;
  font-weight: 700;
  margin: 0;
}

.milestone-impact-list {
  display: grid;
  gap: 8px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.milestone-impact-list li {
  align-items: start;
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 8px;
  display: grid;
  gap: 6px;
  grid-template-columns: auto minmax(0, 1fr) auto;
  padding: 10px;
  position: relative;
}

.milestone-impact-list .impact-date-chip {
  align-self: start;
  background: #eef2f7;
  border-color: var(--line);
}

.milestone-impact-list li div:first-child {
  display: grid;
  gap: 2px;
}

.milestone-impact-list li > p {
  grid-column: 2 / 3;
}

.milestone-impact-list strong {
  color: var(--ink);
  font-size: 0.78rem;
}

.milestone-impact-list span,
.milestone-impact-list p {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1.4;
  margin: 0;
}

.timeline-risk-popover {
  justify-self: end;
  position: relative;
}

.timeline-risk-popover summary {
  align-items: center;
  background: var(--danger-surface);
  border: 1px solid #fca5a5;
  border-radius: 50%;
  color: #7f1d1d;
  cursor: pointer;
  display: grid;
  font-size: 0.78rem;
  font-weight: 900;
  height: 28px;
  list-style: none;
  place-content: center;
  width: 28px;
}

.timeline-risk-popover summary::-webkit-details-marker {
  display: none;
}

.timeline-risk-popover div {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 16px 34px rgba(24, 24, 27, 0.14);
  display: grid;
  gap: 6px;
  padding: 10px;
  position: absolute;
  right: 0;
  top: 34px;
  width: min(320px, calc(100vw - 80px));
  z-index: 5;
}

.timeline-risk-popover:not([open]) div {
  display: none;
}

.entity-editor-form {
  display: grid;
  gap: 12px;
}

.entity-editor-form label {
  display: grid;
  gap: 5px;
}

.entity-editor-form label span {
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 800;
}

.automation-rules-form h4 {
  margin: 0;
}

.automation-rule-row {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  display: grid;
  gap: 8px;
  margin-bottom: 10px;
  padding: 10px;
}

.automation-rule-row input[type="text"] {
  border-radius: 8px;
  font-size: 0.8rem;
  padding: 7px 10px;
}

.recipient-chip-list {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.recipient-chip {
  align-items: center;
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 999px;
  display: inline-flex;
  font-size: 0.72rem;
  font-weight: 700;
  gap: 6px;
  padding: 4px 6px 4px 10px;
}

.recipient-chip button {
  background: none;
  border-radius: 50%;
  box-shadow: none;
  color: var(--muted);
  font-size: 0.9rem;
  line-height: 1;
  min-height: 0;
  padding: 0;
  width: 18px;
}

.recipient-chip button:hover {
  background: #f4f4f5;
  color: var(--ink);
}

.recipient-add-row {
  display: flex;
  gap: 8px;
}

.recipient-add-row input {
  border-radius: 8px;
  flex: 1;
  font-size: 0.8rem;
  padding: 7px 10px;
}

.recipient-add-row button {
  flex: 0 0 auto;
}

.webhook-slack-section h4,
.webhook-slack-section + hr + h4 {
  margin: 0 0 4px;
}

.webhook-list {
  display: grid;
  gap: 8px;
  list-style: none;
  margin: 0 0 12px;
  max-height: 260px;
  overflow-y: auto;
  padding: 0;
}

.webhook-list-item {
  align-items: center;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: space-between;
  padding: 10px;
}

.webhook-list-item-editing {
  align-items: stretch;
  display: block;
}

.webhook-list-item-editing .entity-editor-form {
  width: 100%;
}

.webhook-list-item-main {
  display: grid;
  gap: 3px;
  min-width: 0;
}

.webhook-list-item-main strong {
  overflow-wrap: anywhere;
}

.webhook-list-item-main span,
.webhook-list-item-main small {
  color: var(--muted);
  font-size: 0.72rem;
}

.webhook-list-item-actions {
  align-items: center;
  display: flex;
  gap: 10px;
}

.webhook-event-fieldset {
  border: 1px solid var(--line);
  border-radius: 8px;
  display: grid;
  gap: 6px;
  padding: 10px;
}

.webhook-event-fieldset legend {
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 900;
  text-transform: uppercase;
}

.entity-editor-form input,
.entity-editor-form select {
  border-radius: 8px;
  font-size: 0.8rem;
  min-height: 38px;
}

.entity-editor-form label.checkbox-row {
  align-items: center;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
  display: flex;
  gap: 10px;
  padding: 12px;
}

.entity-editor-form label.checkbox-row input {
  accent-color: #52525b;
  flex: 0 0 auto;
  height: 16px;
  min-height: 0;
  width: 16px;
}

.section-row-delete {
  background: var(--danger-surface);
  color: #8a2d20;
  min-height: 34px;
}

.risk-row-actions {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  justify-content: flex-end;
}

.risk-row-actions button {
  min-height: 32px;
}

.risk-lifecycle-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.risk-lifecycle-meta span {
  background: #e4e4e7;
  border-radius: 999px;
  color: #3f3f46;
  font-size: 0.62rem;
  font-weight: 800;
  padding: 5px 7px;
}

.risk-status-pill {
  border-radius: 999px;
  font-size: 0.64rem;
  font-weight: 900;
  line-height: 1;
  padding: 7px 9px;
  text-transform: uppercase;
}

.risk-status-pill.open {
  background: var(--warn-surface);
  color: var(--warn);
}

.risk-status-pill.closed {
  background: #e4e4e7;
  color: #3f3f46;
}

/* Open and unanswered. Red rather than the amber of a managed open risk, so the
   column separates "we are handling this" from "nobody has decided" instead of
   printing one colour all the way down. */
.risk-status-pill.undecided {
  background: var(--danger-surface);
  color: var(--danger-strong);
}

.adoption-status-pill {
  border-radius: 999px;
  font-size: 0.64rem;
  font-weight: 900;
  line-height: 1;
  padding: 7px 9px;
  text-transform: uppercase;
}

.adoption-status-pill.good {
  background: #e4e4e7;
  color: #3f3f46;
}

.adoption-status-pill.warn {
  background: var(--warn-surface);
  color: var(--warn);
}

.adoption-status-pill.stale {
  background: #fecaca;
  color: #7f1d1d;
}

.adoption-status-pill.none {
  background: #e4e4e7;
  color: var(--muted);
  font-style: italic;
}

.adoption-trend-wrap {
  margin-bottom: 20px;
}

.adoption-trend-chart {
  width: 100%;
  height: auto;
  max-width: 640px;
}

.adoption-trend-legend {
  display: flex;
  gap: 16px;
  font-size: 0.8rem;
  color: var(--muted);
  margin-top: 6px;
}

.adoption-trend-legend i {
  display: inline-block;
  width: 10px;
  height: 10px;
  border-radius: 999px;
  margin-right: 4px;
}

.closed-risk-item span {
  text-decoration: line-through;
}

.risk-list-filter {
  margin-bottom: 10px;
}

.risk-list-filter label {
  display: flex;
  align-items: center;
  gap: 8px;
}

.risk-list-filter span {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 700;
}

.risk-list-filter select {
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink);
  font-size: 0.76rem;
  min-height: 32px;
  padding: 5px 30px 5px 8px;
}

.risk-list-groups {
  display: grid;
  gap: 10px;
}

.risk-list-group {
  display: grid;
  gap: 6px;
}

.risk-list-group h4 {
  color: var(--ink);
  font-size: 0.78rem;
  margin: 0;
}

.section-version-upload {
  min-height: 84px;
}

/* The milestone-only block between the quick form's fields and its buttons.
   Dependencies are a relationship rather than a field, so they are separated
   from the inputs above by a rule rather than sitting in the run of them and
   pretending to be one. */
.quick-edit-relations {
  border-top: 1px solid var(--line);
  margin: 14px 0 16px;
  padding-top: 14px;
}

.quick-edit-relations details {
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
}

.quick-edit-relations summary {
  align-items: center;
  cursor: pointer;
  display: flex;
  gap: 10px;
  justify-content: space-between;
  padding: 10px 12px;
}

.quick-edit-relations summary h4 {
  font-size: 0.88rem;
  margin: 0;
}

.quick-edit-relations summary::-webkit-details-marker { display: none; }
.quick-edit-relations summary::marker { content: ""; }

.section-editor-actions {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: flex-end;
}

/* "More..." is a way out of this form, not a second Save, so it sits away from
   the button somebody is aiming for. */
.section-editor-actions [data-open-milestone-focus] {
  margin-right: auto;
}

.version-note {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--muted);
  display: flex;
  gap: 8px;
  justify-content: space-between;
  font-size: 0.8rem;
  margin-top: 10px;
  padding: 9px;
}

.version-note strong {
  color: var(--ink);
}

.executive-empty {
  background: #ffffff;
}

.portfolio-detail-list {
  min-height: 180px;
}

.portfolio-drilldown-list {
  display: grid;
  gap: 7px;
}

.portfolio-drilldown-item {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink);
  align-items: start;
  display: grid;
  gap: 3px;
  justify-content: stretch;
  justify-items: start;
  min-height: 48px;
  padding: 8px;
  text-align: left;
  width: 100%;
}

.portfolio-drilldown-item:hover {
  background: #f4f4f5;
  border-color: #a1a1aa;
}

.portfolio-drilldown-item strong,
.portfolio-drilldown-item span {
  display: block;
  text-align: left;
  width: 100%;
}

.portfolio-drilldown-item span {
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 700;
}

.score-low,
.score-low strong {
  color: #9f2f24;
}

.score-mid,
.score-mid strong {
  color: #9a5b00;
}

.score-warn,
.score-warn strong {
  color: #c2410c;
}

.score-high,
.score-high strong {
  color: #15803d;
}

.score-badge.score-low {
  background: #f7d8d3;
  color: #8a2d20;
}

.score-badge.score-mid {
  background: var(--warn-surface);
  color: var(--warn);
}

.score-badge.score-warn {
  background: #fdba74;
  color: #7c2d12;
}

.score-badge.score-high {
  background: #86efac;
  color: #14532d;
}

.decision-list {
  display: grid;
  gap: 10px;
  overflow-y: auto;
  padding: 12px;
}

.decision-card {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 12px;
}

.decision-card h3 {
  font-size: 0.98rem;
  line-height: 1.35;
  margin: 0 0 10px;
}

.decision-row {
  color: var(--muted);
  display: grid;
  gap: 2px;
  font-size: 0.86rem;
  line-height: 1.35;
  margin-top: 8px;
}

.decision-row strong {
  color: var(--ink);
}

.status {
  border-radius: 999px;
  display: inline-flex;
  font-size: 0.76rem;
  font-weight: 800;
  margin-top: 10px;
  padding: 4px 8px;
}

.status.awaiting_outcome {
  background: #fff0ba;
  color: #6f5000;
}

.status.completed {
  background: #e4e4e7;
  color: #3f3f46;
}

.card-action {
  background: #52525b;
  margin-top: 10px;
  min-height: 36px;
  width: 100%;
}

.card-action:hover {
  background: #3f3f46;
}

.empty-state {
  color: var(--muted);
  font-size: 0.92rem;
  line-height: 1.5;
  padding: 8px;
}

.admin-shell {
  margin: 0 auto;
  max-width: 1180px;
  min-height: 100vh;
  padding: 18px;
}

.admin-header {
  align-items: center;
  background: #27272a;
  color: white;
  display: flex;
  justify-content: space-between;
  padding: 18px 20px;
}

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

.admin-actions a {
  color: white;
  font-weight: 800;
  text-decoration: none;
}

.admin-actions button {
  background: rgba(255, 255, 255, 0.16);
  border: 1px solid rgba(255, 255, 255, 0.24);
}

.admin-actions .danger-button {
  background: #a33a2a;
  border-color: #a33a2a;
}

.admin-actions .danger-button:hover {
  background: #832c20;
}

.inline-admin-actions {
  justify-content: flex-start;
  margin-top: 12px;
}

.admin-summary {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(3, 1fr);
  margin: 14px 0;
}

.admin-section {
  border-top: 1px solid var(--line);
  margin: 18px 0;
  padding-top: 18px;
}

.section-heading {
  align-items: center;
  display: flex;
  justify-content: space-between;
  margin-bottom: 12px;
}

.section-heading h2 {
  font-size: 1.15rem;
}

.admin-pill {
  background: #e4e4e7;
  border-radius: 999px;
  color: var(--ink);
  font-size: 0.78rem;
  font-weight: 800;
  padding: 6px 10px;
}

.admin-summary div {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 8px;
  display: grid;
  gap: 4px;
  padding: 14px;
}

.admin-summary strong {
  font-size: 1.5rem;
}

.admin-summary span {
  color: var(--muted);
  font-size: 0.86rem;
  font-weight: 700;
}

.admin-notice {
  display: none;
  margin: 0 0 14px;
}

.admin-notice.info,
.admin-notice.error {
  border-radius: 8px;
  display: grid;
  gap: 10px;
  padding: 12px;
}

.admin-notice.info {
  background: #f4f4f5;
  border: 1px solid var(--line);
  color: #3f3f46;
}

.admin-notice.error {
  background: #fff0eb;
  border: 1px solid #e6b4a8;
  color: #7d2e20;
}

.admin-notice pre {
  background: white;
  border: 1px solid var(--line);
  border-radius: 6px;
  margin: 0;
  overflow-x: auto;
  padding: 10px;
  white-space: pre-wrap;
}

.admin-list {
  display: grid;
  gap: 14px;
}

.compact-list {
  margin-top: 14px;
}

.compact-card {
  padding: 12px;
}

.compact-card h3 {
  font-size: 0.95rem;
  margin: 0 0 6px;
}

.admin-card {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 16px;
}

.admin-card h2,
.admin-card h3,
.admin-card h4 {
  margin: 0 0 10px;
}

.admin-card h2 {
  font-size: 1.1rem;
}

.admin-card h3 {
  font-size: 1rem;
  margin-top: 18px;
}

.admin-card h4 {
  font-size: 0.9rem;
  margin-top: 12px;
}

.admin-meta {
  color: var(--muted);
  font-size: 0.82rem;
  margin: 0 0 12px;
}

.admin-meta.subscription-limit-warning {
  color: var(--warn);
  font-weight: 700;
}

.admin-form {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.admin-form button {
  justify-self: start;
}

.cost-event-form {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 8px;
  margin-bottom: 14px;
  padding: 14px;
}

.global-budget-card {
  position: relative;
}

.global-budget-inline-form {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 12px 28px rgba(24, 24, 27, 0.12);
  display: grid;
  gap: 8px;
  left: 10px;
  opacity: 0;
  padding: 10px;
  pointer-events: none;
  position: absolute;
  right: 10px;
  top: calc(100% - 6px);
  transition: opacity 140ms ease;
  z-index: 10;
}

.global-budget-card:hover .global-budget-inline-form,
.global-budget-card:focus-within .global-budget-inline-form {
  opacity: 1;
  pointer-events: auto;
}

.global-budget-inline-form .admin-field span,
.global-budget-inline-form .checkbox-field {
  font-size: 0.72rem;
}

.global-budget-inline-form button {
  min-height: 32px;
}

.cost-grid {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  margin-bottom: 14px;
}

.cost-subtitle {
  font-size: 1rem;
  margin: 18px 0 10px;
}

.cost-card {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 8px;
  display: grid;
  gap: 8px;
  padding: 14px;
}

.cost-card h3 {
  font-size: 0.95rem;
  line-height: 1.3;
  margin: 0;
}

.cost-card p {
  color: var(--muted);
  font-size: 0.86rem;
  line-height: 1.45;
  margin: 0;
}

.client-resource-grid {
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.client-resource-card details {
  border-top: 1px solid var(--line);
  padding-top: 8px;
}

.client-resource-card summary {
  color: var(--ink);
  cursor: pointer;
  font-size: 0.82rem;
  font-weight: 900;
}

.client-resource-card strong {
  color: var(--ink);
}

.cost-limit-form {
  align-content: start;
}

.checkbox-field {
  align-items: center;
  color: var(--ink);
  display: flex;
  font-size: 0.86rem;
  font-weight: 800;
  gap: 8px;
}

.checkbox-field input {
  height: 18px;
  width: 18px;
}

.admin-field {
  display: grid;
  gap: 5px;
}

.admin-field span {
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 800;
}

.admin-field input,
.admin-field select,
.admin-field textarea {
  border: 1px solid var(--line);
  border-radius: 6px;
  color: var(--ink);
  min-height: 40px;
  padding: 8px 10px;
  width: 100%;
}

.admin-field textarea {
  resize: vertical;
}

.admin-milestone {
  border: 1px solid var(--line);
  border-radius: 8px;
  margin-top: 10px;
  padding: 12px;
}

.admin-milestone summary {
  cursor: pointer;
  font-weight: 800;
  margin-bottom: 12px;
}

.admin-updates {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 6px;
  color: var(--muted);
  font-size: 0.86rem;
  line-height: 1.45;
  padding: 10px;
}

.admin-updates p {
  margin: 0 0 8px;
}

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

.dashboard-profile-list {
  display: grid;
  gap: 10px;
  margin: 14px 0;
}

.dashboard-profile-row {
  align-items: center;
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 8px;
  display: grid;
  gap: 12px;
  grid-template-columns: minmax(0, 1fr) auto;
  padding: 12px;
}

.dashboard-profile-row strong,
.dashboard-profile-row span {
  display: block;
}

.dashboard-profile-row span {
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 700;
  line-height: 1.35;
  margin-top: 3px;
  overflow-wrap: anywhere;
}

.dashboard-profile-actions {
  display: grid;
  gap: 8px;
  justify-items: end;
}

.dashboard-profile-link {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 7px;
  color: #202833;
  font-size: 0.78rem;
  font-weight: 800;
  min-height: 34px;
  padding: 8px 12px;
  text-decoration: none;
  white-space: nowrap;
}

.dashboard-profile-link:hover {
  background: #f4f7fa;
  border-color: var(--line-strong);
}

/* Executive design system.
   Its :root block moved to the top of this file - see the palette comment there
   for why having two was a problem. */

html {
  background: var(--bg);
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-size: 15px;
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a,
button {
  -webkit-tap-highlight-color: rgba(101, 122, 146, 0.18);
}

button {
  align-items: center;
  background: linear-gradient(180deg, #2f3a47 0%, #202833 100%);
  border: 1px solid rgba(17, 22, 29, 0.18);
  border-radius: var(--radius-control);
  box-shadow: 0 1px 1px rgba(17, 22, 29, 0.08), inset 0 1px 0 rgba(255, 255, 255, 0.08);
  color: #ffffff;
  display: inline-flex;
  gap: 8px;
  justify-content: center;
  min-height: 40px;
  padding: 0 14px;
  transition: background 150ms ease, border-color 150ms ease, box-shadow 150ms ease, color 150ms ease, opacity 150ms ease, transform 150ms ease;
}

button:hover {
  background: linear-gradient(180deg, #25303c 0%, #151c25 100%);
  box-shadow: 0 5px 14px rgba(17, 22, 29, 0.12), inset 0 1px 0 rgba(255, 255, 255, 0.08);
}

button:active {
  transform: translateY(1px);
}

button:disabled {
  background: #e7ebf0;
  border-color: var(--line);
  box-shadow: none;
  color: #98a2ad;
  cursor: not-allowed;
  transform: none;
}

button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
a:focus-visible {
  outline: 3px solid rgba(101, 122, 146, 0.28);
  outline-offset: 2px;
}

.secondary-button,
.sample-button {
  background: #ffffff;
  border: 1px solid var(--line);
  box-shadow: 0 1px 1px rgba(17, 22, 29, 0.04);
  color: #202833;
}

.secondary-button:hover,
.sample-button:hover {
  background: #f4f7fa;
  border-color: var(--line-strong);
  box-shadow: 0 6px 18px rgba(17, 22, 29, 0.08);
}

/* ---- Form controls -------------------------------------------------------
   This block used to set three properties - background, border-colour, ink -
   and leave border WIDTH, radius, height and padding to the user agent. The
   result was a product whose buttons and cards had been designed and whose
   inputs were still wearing the operating system's clothes: a 2px inset border,
   square corners, and on <select> the platform's own drop-down chrome. It is
   the single most reliable tell of unfinished work, and it appeared on the plan
   calendar six times over.

   This is a FLOOR, not a ceiling: every component rule that styles its own field
   is at least a class, and must keep winning. That is why the type exclusions
   are wrapped in :where() - a bare :not([type=...]) contributes its argument's
   specificity, so five of them scored (0,5,1) and the "floor" quietly overrode
   the rail search, which had deliberately asked for no border at all. :where()
   contributes zero, leaving these at the specificity of a bare element.

   min-height rather than height for the same reason: a compact control that
   sets its own padding stays compact instead of being forced to 40px.

   Checkboxes, radios, ranges, colours and file pickers are excluded: they are
   not text fields, and giving a checkbox a 40px height and 12px of side padding
   detaches the box from its own label. --------------------------------------- */
input:where(:not([type="checkbox"], [type="radio"], [type="range"], [type="color"], [type="file"])),
select,
textarea {
  background: #ffffff;
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-control);
  color: var(--ink);
  padding: 0 12px;
  transition: border-color 120ms ease, box-shadow 120ms ease;
}

input:where(:not([type="checkbox"], [type="radio"], [type="range"], [type="color"], [type="file"])),
select {
  min-height: 40px;
}

textarea {
  line-height: 1.5;
  padding: 10px 12px;
  resize: vertical;
}

input:where(:not([type="checkbox"], [type="radio"], [type="range"], [type="color"], [type="file"])):hover:where(:not(:disabled)),
select:hover:where(:not(:disabled)),
textarea:hover:where(:not(:disabled)) {
  border-color: var(--muted);
}

/* The platform drop-down arrow replaced with one of ours. appearance:none only
   changes the closed control - the picker itself, keyboard behaviour and screen
   reader semantics are the native ones still.

   One grey chevron serves both themes rather than two assets: a data URI cannot
   read currentColor, and this mid-grey holds against the light panel and the
   dark surface alike. padding-right keeps the longest option clear of it. */
select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1.5 6 6.5l5-5' stroke='%238b95a1' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-position: right 12px center;
  background-repeat: no-repeat;
  background-size: 12px 8px;
  padding-right: 32px;
}

/* The date field's calendar button. It cannot be replaced, only toned down, so
   it stops competing with the value beside it and picks up the pointer the rest
   of the control has. The dark-mode inversion is in dark-mode.css. */
input[type="date"]::-webkit-calendar-picker-indicator {
  cursor: pointer;
  opacity: 0.45;
  transition: opacity 120ms ease;
}

input[type="date"]::-webkit-calendar-picker-indicator:hover {
  opacity: 0.9;
}

input::placeholder,
textarea::placeholder {
  color: #9aa4b0;
}

h1,
h2,
h3,
strong {
  letter-spacing: 0;
}

.eyebrow {
  color: #5f6d7c;
  font-size: 0.68rem;
  letter-spacing: 0;
  opacity: 1;
}

.dashboard-app-shell {
  display: flex;
  flex-direction: column;
  height: 100vh;
  height: 100dvh;
}

/* Higher specificity than the bare .dashboard-shell.executive-dashboard-shell
   rule below on purpose - this pair is now nested one level deeper, under the
   new unified .app-top-bar, so it must give up its own 100dvh sizing (the
   dashboard-app-shell ancestor already owns that) and just fill the flex
   space left over below the header. */
.dashboard-app-shell > .dashboard-shell.executive-dashboard-shell {
  flex: 1 1 auto;
  grid-template-rows: minmax(0, 1fr);
  height: auto;
  min-height: 0;
}

/* dashboard-premium.css (loaded first, lowest cascade priority) still floats
   this shell as a rounded, shadowed, margined card separate from the top bar
   above it - that's the "menu and top bar are different cards" look. Zero all
   of that out here so the rail+content grid is just one continuous surface
   flush against the top bar, edge-to-edge with the viewport. */
.dashboard-shell.executive-dashboard-shell {
  background: var(--bg);
  border: 0;
  border-radius: 0;
  box-shadow: none;
  grid-template-columns: minmax(220px, 15vw) minmax(0, 1fr);
  height: 100vh;
  height: 100dvh;
  margin: 0;
  max-width: none;
  min-height: 0;
  overflow: hidden;
  width: auto;
}

/* This used to be an always-dark chat-sidebar background from an older
   design (the .workspace-copilot-switcher/.dashboard-composer rules below
   belonged to it) - that chat UI is gone from dashboard.html, but this rule
   was never brought back in line with the current design, so the live rail
   (#mobileNavDrawer/#primaryRailNav) rendered a fixed dark navy panel even in
   light mode: a totally different fill from the top bar and content next to
   it, which is exactly what read as "the menu is its own disconnected card."
   Same background as the canvas/top bar now, with just a thin divider line -
   dark mode's own rail color still comes from dark-mode.css's --theruka-rail
   override further down the cascade. */
.workspace-copilot {
  background: var(--bg);
  border-right: 1px solid var(--line);
  color: var(--ink);
  grid-template-rows: minmax(0, 1fr);
}

.workspace-copilot-switcher {
  background: #f4f6f8;
  border-bottom: 1px solid #293241;
  max-height: 42vh;
  overflow-y: auto;
  padding: 14px;
}

.workspace-copilot-switcher .portfolio-panel:last-child {
  margin-bottom: 0;
}

.workspace-copilot .dashboard-composer {
  background: #151b24;
  border-bottom: 1px solid rgba(215, 221, 229, 0.13);
  border-top: 0;
  padding: 14px 14px 16px;
}

.workspace-copilot .dashboard-composer label {
  color: #aeb8c5;
}

.workspace-copilot .dashboard-composer textarea {
  background: #0f141b;
  border-color: #303a48;
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.03);
  color: #f8fafc;
  min-height: 108px;
}

.workspace-copilot .dashboard-composer button {
  background: #ffffff;
  border-color: #ffffff;
  color: #151b24;
}

.workspace-copilot .dashboard-composer button:hover {
  background: #e9eef4;
}

.workspace-copilot .dashboard-chat-messages {
  background: #121821;
  padding: 12px;
}

.workspace-copilot .message {
  background: #1d2530;
  border-color: #303a48;
  border-left-color: #718197;
  box-shadow: none;
  color: #dbe3ec;
  max-width: 100%;
}

.workspace-copilot .message.user {
  background: #eef3f8;
  border-color: var(--line);
  border-left-color: #8aa0b6;
  color: #11161d;
}

.workspace-copilot .message.system {
  background: #2b261c;
  border-color: #59451f;
  color: #f4d79a;
}

/* !important here beats dashboard-premium.css's generic
   ".dashboard-actions button"/".workspace-account-card" surface rules (that
   file is injected by dashboard-navigation.js, styled for the old dark-rail
   placement of this card) - same fix already used by .notification-bell-button
   for the same reason, now that this card lives in .dashboard-actions too. */
.workspace-account-card {
  background: none !important;
  border: 0 !important;
  box-shadow: none !important;
  display: inline-flex;
  flex: 0 0 auto;
  margin: 0 !important;
  padding: 0 !important;
  position: relative;
}

/* Small anchored popover, not a full-screen modal - same pattern as
   .notification-dropdown (position:absolute off the trigger's own relatively-
   positioned wrapper), just for the account menu instead of notifications.
   Deliberately minimal: theme toggle + sign out, nothing else. */
.account-popover {
  background: var(--panel-strong, #ffffff);
  border: 1px solid var(--line, #e4e4e7);
  border-radius: 10px;
  box-shadow: 0 16px 32px rgba(15, 23, 42, 0.16);
  display: grid;
  gap: 2px;
  padding: 6px;
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  width: 180px;
  z-index: 50;
}

/* This rule's own display:grid above would otherwise beat the UA [hidden]
   default (higher specificity, same origin), silently keeping the popover
   rendered/interactable even while the "hidden" attribute is set. */
.account-popover[hidden] {
  display: none;
}

.account-popover-item {
  background: none;
  border: 0;
  border-radius: 6px;
  color: var(--ink);
  cursor: pointer;
  font-size: 0.86rem;
  font-weight: 700;
  padding: 9px 10px;
  text-align: left;
  width: 100%;
}

.account-popover-item:hover,
.account-popover-item:focus-visible {
  background: var(--panel, #f4f4f5);
}

.account-popover-group {
  border-top: 1px solid var(--line, #e4e4e7);
  display: grid;
  gap: 6px;
  margin-top: 2px;
  padding: 9px 10px;
}

.account-popover-label {
  color: var(--ink);
  font-size: 0.86rem;
  font-weight: 700;
}

.account-language-choices {
  display: grid;
  gap: 4px;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.account-language-option {
  background: var(--panel, #f4f4f5);
  border: 1px solid var(--line, #e4e4e7);
  border-radius: 6px;
  color: var(--ink);
  cursor: pointer;
  font-size: 0.76rem;
  font-weight: 700;
  padding: 6px 0;
}

.account-language-option:hover,
.account-language-option:focus-visible {
  border-color: var(--accent, #06213f);
}

/* aria-pressed carries the state, so the selected option is styled off the
   attribute rather than a parallel class - one source of truth, and a screen
   reader and a sighted reader cannot end up disagreeing about which is set. */
.account-language-option[aria-pressed="true"] {
  background: var(--accent, #06213f);
  border-color: var(--accent, #06213f);
  color: var(--panel, #ffffff);
}

.account-language-option[disabled] {
  cursor: progress;
  opacity: 0.6;
}

.account-popover-note {
  color: var(--muted, #6b7280);
  font-size: 0.72rem;
  line-height: 1.35;
}

.workspace-account-main {
  align-items: center;
  background: none !important;
  border: 0 !important;
  border-radius: 999px !important;
  box-shadow: none !important;
  cursor: pointer;
  display: flex;
  padding: 2px;
  text-align: left;
}

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

/* Solid accent circle with the account's initials. It used to be a white
   circle carrying the Theruka mark, which had two problems: it repeated the
   brand logo already sitting in the same top bar, and on the free tier the
   white circle vanished against the light-mode header. The brand petrol keeps
   white initials at 7.09:1 - better than the #2563eb it replaced, which was
   5.17:1 - and the circle reads against the light page at the same 7.09:1.
   Dark mode swaps --accent-brand for a lifted petrol at 6.73:1. */
.workspace-account-icon {
  --account-icon-ink: #ffffff;
  align-items: center;
  background: var(--accent-brand);
  border-radius: 999px;
  color: #ffffff;
  display: inline-flex;
  font-size: 0.8rem;
  font-weight: 900;
  height: 36px;
  justify-content: center;
  letter-spacing: 0.02em;
  overflow: hidden;
  width: 36px;
}

/* Only an Enterprise account's own uploaded logo renders as an image now -
   see updateWorkspaceAccountCard, which leaves src empty otherwise. */
.workspace-account-icon img {
  display: none;
  height: 100%;
  object-fit: cover;
  width: 100%;
}

.workspace-account-icon.has-custom-logo img {
  display: block;
}

.workspace-account-icon.has-custom-logo b {
  display: none;
}

/* The initials take their colour from --account-icon-ink rather than plain
   inheritance: the icon is a <span>, and dark-mode.css's `[data-theme="dark"]
   span { color: var(--theruka-muted-text) }` rule (0,2,1) outranks the class
   selector above (0,1,0), which would otherwise mute the initials to 72% white
   (3.44:1 on the blue circle). A custom property is immune to that override. */
.workspace-account-icon b {
  color: var(--account-icon-ink);
  display: inline;
}

/* Shown before the real plan is known (profile still loading) - deliberately
   distinct from every real tier's color (white=free, grey=pro, gold=max,
   light blue=enterprise) so it never reads as "you're on plan X" while loading. */
.workspace-account-icon.tier-loading {
  --account-icon-ink: #e5e7eb;
  background: #111827;
  color: #e5e7eb;
}

.workspace-account-icon.tier-loading img {
  filter: brightness(0) invert(1);
  opacity: 0.65;
}

.workspace-account-icon.tier-pro {
  --account-icon-ink: #111827;
  background: #9ca3af;
  color: #111827;
}

.workspace-account-icon.tier-max {
  --account-icon-ink: #1f1603;
  background: #d6a83a;
  color: #1f1603;
}

.workspace-account-icon.tier-enterprise {
  --account-icon-ink: #1e3a5f;
  background: #93c5fd;
  color: #1e3a5f;
}

/* Pro/Business sit on a dark-enough background for the logo mark to read clearly
   rendered in white. */
.workspace-account-icon.tier-pro img,
.workspace-account-icon.tier-max img {
  filter: brightness(0) invert(1);
}

/* Enterprise's light blue background needs a dark accent instead - recolors the
   logo to a deep red via the standard black-silhouette-to-hex filter technique. */
.workspace-account-icon.tier-enterprise img {
  filter: brightness(0) saturate(100%) invert(9%) sepia(80%) saturate(4500%) hue-rotate(355deg) brightness(75%) contrast(110%);
}

/* An account's own uploaded logo (Enterprise only, set via decisionmemoryadmin)
   replaces the Theruka mark - shown as-is, not recolored. */
.workspace-account-icon.tier-enterprise img.workspace-account-icon-custom-logo {
  filter: none;
}

/* The account name is shown again. It was hidden here on the grounds that it
   "read as filler", but that judgement was made against the placeholder text
   ("Workspace user") - displayUserName() resolves to the signed-in person's
   real name or email local-part, which is exactly what you want to see to
   confirm which account you are in. Only the plan-tier label stays hidden:
   the icon's colour already encodes the tier (.workspace-account-icon.tier-*)
   and Billing & Data states it in full. */
#workspaceProductName {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

.executive-workspace {
  background: var(--bg);
  min-width: 0;
}

/* Same solid background as the rail and the page canvas behind it (see
   .workspace-copilot above) - no frosted/blurred "floating bar" effect, so
   the top bar reads as one continuous surface with the rail below it,
   divided only by the border-bottom line. */
.app-top-bar {
  background: var(--bg);
  border-bottom: 1px solid var(--line);
  min-height: 62px;
  padding: 14px 20px;
}

.dashboard-actions {
  align-items: center;
  flex-wrap: wrap;
}

.executive-dashboard-shell button {
  font-size: 0.78rem;
  min-height: 34px;
}

.project-dashboard {
  background: var(--bg);
  padding: 18px 20px 24px;
  scroll-padding: 18px;
}

.project-portfolio {
  background: var(--bg);
  border-left: 1px solid var(--line);
  padding: 14px;
}

.portfolio-intake-actions {
  gap: 8px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin-bottom: 14px;
}

.portfolio-upload-button {
  width: 100%;
}

/* Same canvas background as the page around them, not a lighter/whiter card
   fill - a border line is what defines each section, per the flat
   Neon-style layout this was redesigned against (see .executive-project-hero
   and .workspace-copilot above for the same treatment). */
.plan-section,
.executive-metrics div,
.executive-metrics button,
.portfolio-drilldown-item,
.sharing-link-row,
.access-removed-panel,
.version-note,
.pending-plan-review {
  background: var(--bg);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: none;
}

/* .portfolio-panel is only ever used as the wrapper around the header's
   portfolio/project switcher buttons (.top-switcher-group in dashboard.html) -
   kept flush with the top bar, no card chrome, matching the Neon-console-style
   switcher this was redesigned against. */
.portfolio-panel {
  background: none;
  border: 0;
  box-shadow: none;
  gap: 10px;
  margin-bottom: 0;
  padding: 0;
}

.portfolio-rail-button {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: 0 1px 2px rgba(17, 22, 29, 0.04);
  color: var(--ink);
  font-weight: 850;
  margin-bottom: 14px;
  min-height: 42px;
  width: 100%;
}

.rail-section-heading {
  align-items: start;
  display: flex;
  gap: 10px;
  justify-content: space-between;
}

.rail-section-heading h3 {
  color: var(--ink);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.1;
  margin: 0;
}

.rail-section-heading p {
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 750;
  line-height: 1.3;
  margin: 4px 0 0;
}

.compact-heading {
  margin-bottom: 10px;
  padding: 0;
}

.section-heading h2,
.compact-heading h3 {
  color: var(--ink);
  font-size: 0.98rem;
  font-weight: 600;
}

.admin-pill {
  background: #e6ebf1;
  color: #263241;
  font-size: 0.72rem;
}

.portfolio-list,
.project-list {
  gap: 2px;
}

.portfolio-list-row,
.project-list-row {
  gap: 7px;
  padding: 6px 8px;
  transition: background 120ms ease;
}

.portfolio-list-row.active,
.portfolio-list-row:hover,
.project-list-row.active,
.project-list-row:hover {
  background: color-mix(in srgb, var(--ink) 6%, transparent);
}

.portfolio-list-item,
.project-list-item {
  border: 0;
  box-shadow: none;
  color: var(--ink);
  min-width: 0;
}

.portfolio-list-item:hover,
.project-list-item:hover {
  background: transparent;
  box-shadow: none;
}

.portfolio-list-item span,
.project-list-item > span:first-child,
.ai-recommendation-card > div,
.executive-project-hero > div:first-child {
  min-width: 0;
}

.portfolio-list-item strong,
.project-list-item strong,
.portfolio-drilldown-item strong {
  color: #121821;
  font-size: 0.82rem;
  font-weight: 850;
  overflow-wrap: anywhere;
}

.portfolio-list-item small,
.project-list-item small,
.portfolio-drilldown-item span {
  color: var(--muted);
  font-size: 0.68rem;
}

.project-list-item {
  align-items: flex-start;
  gap: 10px;
  min-height: 58px;
}

.project-score-stack {
  min-width: 78px;
}

.right-row-actions {
  gap: 6px;
}

.right-row-actions button,
.row-edit-button,
.row-share-button,
.project-delete-button {
  border-radius: 6px;
  font-size: 0.7rem;
  min-height: 28px;
}

.project-delete-button,
.section-row-delete {
  background: var(--danger-wash);
  border-color: var(--danger-line);
  color: #8a2d20;
}

.project-delete-button:hover,
.section-row-delete:hover {
  background: #f7d8d3;
  border-color: #dfa39b;
}

/* Same background as the page canvas, not a lighter/whiter card fill -
   defined only by its border, matching every other section below. */
.executive-project-hero {
  align-items: flex-start;
  background: var(--bg);
  border-color: var(--line);
  box-shadow: none;
  display: grid;
  gap: 16px;
  grid-template-columns: 1fr;
  margin-bottom: 12px;
  padding: 18px;
}

.project-hero-summary {
  display: grid;
  gap: 12px;
  min-width: 0;
}

.executive-project-hero h2 {
  color: #0f141b;
  font-size: clamp(1.6rem, 2.3vw, 2.2rem);
  font-weight: 600;
  line-height: 1.08;
  overflow-wrap: anywhere;
}

.executive-project-hero p {
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.35;
  max-width: 820px;
  overflow-wrap: anywhere;
}

.project-detail-strip {
  display: grid;
  gap: 8px;
  grid-template-columns: minmax(130px, 0.7fr) minmax(190px, 1.2fr) minmax(150px, 0.8fr);
  margin: 0;
}

.project-detail-strip div {
  background: var(--bg);
  border: 0;
  border-bottom: 1px solid var(--line);
  border-radius: 0;
  min-width: 0;
  padding: 9px 10px;
}

.project-detail-strip dt {
  color: #6b7887;
  font-size: 0.62rem;
  font-weight: 900;
  line-height: 1;
  margin: 0 0 5px;
  text-transform: uppercase;
}

.project-detail-strip dd {
  color: #1a2430;
  font-size: 0.76rem;
  font-weight: 800;
  line-height: 1.25;
  margin: 0;
  overflow-wrap: anywhere;
}


.score-cycles {
  align-items: flex-start;
  gap: 11px;
  margin-left: 10px;
}

.score-cycle {
  background: #eef2f6;
  border: 1px solid rgba(17, 22, 29, 0.08);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.48), 0 2px 8px rgba(17, 22, 29, 0.08);
  color: #263241;
  min-height: auto;
  transition: box-shadow 150ms ease, transform 150ms ease, filter 150ms ease;
  width: clamp(72px, 5.8vw, 90px);
}

.score-cycle:hover {
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.5), 0 10px 20px rgba(17, 22, 29, 0.14);
  filter: saturate(1.03);
}

.score-cycle strong {
  color: inherit;
  font-size: clamp(0.96rem, 1.1vw, 1.16rem);
  font-weight: 900;
}

.score-cycle span {
  font-size: 0.55rem;
  letter-spacing: 0;
}

.score-cycle.score-low {
  background: #f5c7c1;
  border-color: #df9e96;
  color: #641f18;
}

.score-cycle.score-mid {
  background: var(--warn-surface);
  border-color: #d8b942;
  color: #563b08;
}

.score-cycle.score-warn {
  background: #f4a866;
  border-color: #d3843f;
  color: #4f270e;
}

.score-cycle.score-high {
  background: #98d7a8;
  border-color: #65b27a;
  color: #103d22;
}

.executive-hero-scores {
  align-items: flex-start;
  display: flex;
  flex-direction: column;
  gap: 10px;
  min-width: 0;
}

.global-score-gauge {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: none;
  display: grid;
  flex: 0 0 auto;
  justify-content: center;
  justify-items: center;
  align-content: center;
  padding: 14px;
  width: 144px;
}

.global-score-gauge-visual {
  position: relative;
  width: 100%;
}

.global-score-gauge-visual svg {
  display: block;
  width: 100%;
  height: auto;
}

.global-score-gauge-track {
  stroke: #e5ebf1;
  stroke-width: 14;
}

.global-score-gauge-fill {
  stroke: var(--score-accent, #52687f);
  stroke-width: 14;
}

.global-score-gauge-readout {
  bottom: 2px;
  display: grid;
  justify-items: center;
  left: 50%;
  position: absolute;
  transform: translateX(-50%);
}

.global-score-gauge-readout strong {
  color: #10161d;
  font-size: 1.3rem;
  font-weight: 900;
  line-height: 1;
  white-space: nowrap;
}

.global-score-gauge-readout span {
  color: var(--score-accent, #52687f);
  font-size: 0.58rem;
  font-weight: 900;
  text-transform: uppercase;
  white-space: nowrap;
}

.global-score-gauge-label {
  color: var(--muted);
  font-size: 0.62rem;
  font-weight: 800;
  margin: 4px 0 0;
  text-align: center;
  text-transform: uppercase;
}

.global-score-gauge.perfect-score {
  box-shadow: 0 0 0 2px rgba(31, 122, 70, 0.28);
}

.confetti-burst {
  height: 100%;
  left: 0;
  overflow: visible;
  pointer-events: none;
  position: absolute;
  top: 0;
  width: 100%;
}

.confetti-piece {
  --angle: calc(var(--i) * 36deg);
  animation: confetti-fly 900ms ease-out forwards;
  animation-delay: calc(var(--i) * 18ms);
  background: hsl(calc(var(--i) * 47), 70%, 52%);
  border-radius: 2px;
  height: 7px;
  left: 50%;
  opacity: 0;
  position: absolute;
  top: 38%;
  transform: rotate(var(--angle));
  width: 7px;
}

@keyframes confetti-fly {
  0% {
    opacity: 1;
    transform: rotate(var(--angle)) translateY(0) scale(1);
  }
  100% {
    opacity: 0;
    transform: rotate(var(--angle)) translateY(-46px) scale(0.4);
  }
}

@media (prefers-reduced-motion: reduce) {
  .confetti-piece {
    animation: none;
    display: none;
  }
}

.global-score-gauge.score-low {
  --score-accent: #a63a2f;
}

.global-score-gauge.score-mid {
  --score-accent: #9a6b05;
}

.global-score-gauge.score-warn {
  --score-accent: #b65c1f;
}

.global-score-gauge.score-high {
  --score-accent: #1f7a46;
}

.executive-hero-gauges {
  display: flex;
  gap: 16px;
}

.global-score-gauge.score-locked {
  --score-accent: var(--muted);
  opacity: 0.72;
}

.global-score-gauge.score-locked .global-score-gauge-readout strong {
  color: var(--muted);
}

.global-score-gauge.score-locked.score-pending {
  opacity: 0.9;
}

.score-breakdown-toggle {
  color: var(--muted);
  font-size: 0.78rem;
  font-weight: 700;
}

.executive-scorecard {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 8px;
  box-shadow: none;
  display: grid;
  flex: 1;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  margin-left: 0;
  max-width: 900px;
  min-width: 0;
  position: relative;
  width: 100%;
}

.executive-score-card {
  --score-accent: #52687f;
  --score-soft: #eef3f7;
  align-content: start;
  appearance: none;
  background: transparent;
  border: 0;
  border-left: 1px solid var(--line);
  border-radius: 0;
  color: #11161d;
  cursor: pointer;
  display: grid;
  gap: 7px;
  min-height: 106px;
  min-width: 0;
  padding: 11px 13px;
  text-align: left;
}

.executive-score-card:first-child {
  border-left: 0;
}

.executive-score-card:hover {
  background: var(--score-soft);
  box-shadow: inset 0 3px 0 var(--score-accent);
}

.executive-score-card:focus-visible {
  outline: 3px solid rgba(82, 104, 127, 0.22);
  outline-offset: -3px;
}

.executive-score-card-top {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 6px;
  justify-content: space-between;
  min-width: 0;
}

.executive-score-card-top span {
  color: #526070;
  font-size: 0.63rem;
  font-weight: 900;
  overflow: hidden;
  text-overflow: ellipsis;
  text-transform: uppercase;
  white-space: nowrap;
}

.executive-score-card-label {
  align-items: center;
  display: flex;
  gap: 5px;
  min-width: 0;
}

.executive-score-card-icon {
  color: var(--score-accent);
  flex: 0 0 auto;
  height: 13px;
  width: 13px;
}

.executive-score-card-top em {
  background: var(--score-soft);
  border: 1px solid color-mix(in srgb, var(--score-accent) 28%, transparent);
  border-radius: 999px;
  color: var(--score-accent);
  flex: 0 1 auto;
  font-size: 0.58rem;
  font-style: normal;
  font-weight: 900;
  line-height: 1.15;
  min-width: 0;
  padding: 4px 6px;
  text-align: center;
}

.executive-score-card > strong {
  color: #10161d;
  font-size: 1.45rem;
  font-weight: 900;
  line-height: 1;
}

.executive-score-bar {
  background: #e5ebf1;
  border-radius: 999px;
  display: block;
  height: 7px;
  overflow: hidden;
}

.executive-score-bar i {
  background: var(--score-accent);
  border-radius: inherit;
  display: block;
  height: 100%;
}

.executive-score-card small {
  color: #253141;
  font-size: 0.7rem;
  font-weight: 800;
  line-height: 1.25;
}

.executive-score-detail {
  color: #7a8795;
  font-size: 0.64rem;
  font-weight: 800;
  line-height: 1.2;
}

.executive-score-card.score-low {
  --score-accent: #a63a2f;
  --score-soft: #fff4f2;
}

.executive-score-card.score-mid {
  --score-accent: #9a6b05;
  --score-soft: #fff7d7;
}

.executive-score-card.score-warn {
  --score-accent: #b65c1f;
  --score-soft: #fff1e5;
}

.executive-score-card.score-high {
  --score-accent: #1f7a46;
  --score-soft: #eefaf2;
}

.executive-metrics {
  gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  margin-bottom: 12px;
}

.project-detail-metrics {
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
}

.executive-metrics div,
.executive-metrics button {
  min-height: 66px;
  padding: 11px 12px;
  position: relative;
}

.executive-metrics button:hover {
  background: var(--panel-strong);
  border-color: var(--line-strong);
  box-shadow: none;
}

.executive-metrics strong {
  color: #10161d;
  font-size: 1.1rem;
  font-weight: 880;
}

.executive-metrics span {
  color: var(--muted);
  font-size: 0.68rem;
  letter-spacing: 0;
}

.executive-metrics .score-low {
  background: var(--panel-strong);
  border-left: 4px solid #b64c3e;
}

.executive-metrics .score-mid {
  background: var(--panel-strong);
  border-left: 4px solid #c99b11;
}

.executive-metrics .score-warn {
  background: var(--panel-strong);
  border-left: 4px solid #d0702c;
}

.executive-metrics .score-high {
  background: var(--panel-strong);
  border-left: 4px solid #3a9b5d;
}

.executive-plan-grid,
.executive-report-grid {
  gap: 12px;
  grid-template-columns: repeat(auto-fit, minmax(340px, 1fr));
}

.plan-section {
  min-height: 150px;
  overflow-wrap: anywhere;
  padding: 14px;
}

.plan-section h3 {
  color: #11161d;
  font-size: 1.02rem;
  font-weight: 600;
}

.plan-section ul {
  gap: 8px;
}

.plan-section li {
  border-top-color: #e8edf2;
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.36;
  padding-top: 8px;
}

.plan-section li strong {
  color: #141a22;
  font-weight: 850;
}

.plan-section li span {
  color: var(--muted);
  font-size: 0.76rem;
}

.empty-state {
  /* Was #72808f - 4.04:1 on white, under the 4.5:1 AA minimum. */
  color: #667482;
  font-size: 0.86rem;
  line-height: 1.45;
  padding: 8px 0;
}

/* The illustrated variant, for the few empty states somebody lands on before
   they have formed a view of the product. Centred and given room, because the
   point is that arriving at nothing should not feel like an error message. */
.empty-state-illustrated {
  align-items: center;
  display: flex;
  flex-direction: column;
  gap: 4px;
  padding: 34px 16px 30px;
  text-align: center;
}

.empty-state-art {
  height: auto;
  margin-bottom: 10px;
  max-width: 200px;
  width: 100%;
}

.empty-state-headline {
  color: var(--ink);
  font-size: 0.95rem;
  font-weight: 600;
  margin: 0;
}

/* Held near the measure that running text wants, so the sentence explaining
   what will appear here does not stretch the width of a dashboard. */
.empty-state-detail {
  margin: 0;
  max-width: 46ch;
}

/* A page with nothing on it, rather than a section. Given a floor so the card
   has a shape of its own, and a ceiling so it never grows to the height of a
   tall viewport - the previous version had no card at all and let one grey
   sentence sit at the top of 800px of white. */
/* The workspace is a block, so a section inside it cannot be told to fill the
   height - it sat 340px tall at the top of an 888px area with the rest left
   white, which reads as content that failed to load below the message. The
   :has() turns the workspace into a flex column for this case only, so the
   state centres in everything that is actually available. */
.project-dashboard:has(> .page-empty-state) {
  display: flex;
  flex-direction: column;
}

.page-empty-state {
  align-items: center;
  display: flex;
  flex: 1 1 auto;
  justify-content: center;
  min-height: 340px;
  padding: 24px;
}

.page-empty-state .empty-state-illustrated {
  gap: 6px;
  padding: 0;
}

.page-empty-state .empty-state-art {
  max-width: 240px;
}

.page-empty-state .empty-state-headline {
  font-size: 1.15rem;
}

.empty-state-action {
  margin-top: 14px;
}

.empty-state-icon {
  align-items: flex-start;
  display: flex;
  gap: 10px;
}

.empty-state-glyph {
  color: #a7b2bd;
  flex: 0 0 auto;
  height: 22px;
  margin-top: 1px;
  width: 22px;
}

.empty-state-icon .empty-state-cta {
  display: block;
  margin-top: 8px;
}

.editable-plan-section {
  transition: border-color 150ms ease, box-shadow 150ms ease, transform 150ms ease;
}

.editable-plan-section:hover,
.editable-plan-section.selected {
  border-color: var(--line-strong);
  box-shadow: inset 0 0 0 1px rgba(104, 125, 148, 0.16), 0 10px 22px rgba(17, 22, 29, 0.07);
}

.editable-section-header {
  min-height: 30px;
}

.section-edit-button {
  opacity: 0;
  pointer-events: none;
  transform: translateY(-2px);
}

.editable-plan-section:hover .section-edit-button:not(:disabled),
.editable-plan-section.selected .section-edit-button:not(:disabled),
.section-edit-button:focus-visible {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

.ai-recommendations {
  margin-bottom: 12px;
}

.ai-recommendation-list {
  gap: 9px;
}

.ai-recommendation-card {
  background: var(--bg);
  border-color: var(--line);
  box-shadow: none;
  grid-template-columns: auto minmax(0, 1fr);
  padding: 12px;
}

.ai-recommendation-card strong {
  color: #11161d;
  font-size: 0.86rem;
  font-weight: 850;
}

.ai-recommendation-card p {
  color: #293342;
  font-size: 0.8rem;
}

.ai-recommendation-card small {
  color: var(--muted);
}

.recommendation-priority {
  border: 1px solid rgba(17, 22, 29, 0.08);
  font-size: 0.58rem;
}

.recommendation-priority.score-low {
  background: var(--danger-wash);
  color: #8a2d20;
}

.recommendation-priority.score-mid {
  background: #fff5cf;
  color: var(--warn);
}

.recommendation-priority.score-warn {
  background: #ffe9d3;
  color: var(--warn);
}

.score-badge {
  border: 1px solid rgba(17, 22, 29, 0.08);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.35);
}

.score-badge.score-low {
  background: var(--danger-wash);
  color: #8a2d20;
}

.score-badge.score-mid {
  background: #fff5cf;
  color: var(--warn);
}

.score-badge.score-warn {
  background: #ffe9d3;
  color: var(--warn);
}

.score-badge.score-high {
  background: #dff5e5;
  color: #176136;
}

.score-explanation,
.upload-window-panel,
.section-editor-panel,
.global-budget-inline-form {
  border-color: var(--line);
  box-shadow: var(--shadow-raised);
}

.score-explanation {
  padding: 14px 38px 14px 14px;
}

.score-explanation h3,
.section-editor-header h3,
.upload-window-header h3 {
  color: #11161d;
  font-weight: 600;
}

.score-explanation-close {
  background: #f4f7fa;
  border-color: var(--line);
  box-shadow: none;
}

.upload-window,
.section-editor-window {
  background: rgba(17, 22, 29, 0.48);
  backdrop-filter: blur(4px);
}

.upload-window-panel,
.section-editor-panel {
  border-radius: var(--radius-card);
}

.upload-window-header,
.section-editor-header {
  border-bottom-color: var(--line);
  padding: 14px;
}

.workspace-upload {
  background: #f7f9fb;
  padding: 14px;
}

.file-drop,
.plan-text-input,
.scratch-plan-form input,
.scratch-plan-form textarea,
.portfolio-field input,
.portfolio-field select,
.entity-editor-form input,
.entity-editor-form select,
.section-editor-fields input,
.admin-field input,
.admin-field select,
.admin-field textarea,
.composer select,
.composer textarea {
  border-color: var(--line);
  border-radius: var(--radius-control);
  box-shadow: inset 0 1px 0 rgba(17, 22, 29, 0.02);
}

.file-drop:hover {
  border-color: var(--line-strong);
  color: #3f4b59;
}

.pending-plan-review {
  padding: 12px;
}

.section-editor-window {
  padding: 24px;
}

.section-editor-panel {
  padding: 16px;
}

.section-editor-row,
.export-option-form label,
.cost-card,
.admin-card,
.decision-card {
  background: #fbfcfd;
  border-color: var(--line);
  border-radius: 8px;
}

.section-editor-row.resolved {
  background: #f2f5f8;
}

.risk-row-actions button {
  min-height: 30px;
}

.risk-lifecycle-meta span,
.risk-status-pill.closed,
.status.completed {
  background: #e7ebf0;
  color: #3e4a57;
}

.risk-status-pill.open,
.status.awaiting_outcome {
  background: #fff0c2;
  color: var(--warn);
}

.closed-risk-item span {
  color: #8a95a1;
}

.sharing-link-row {
  background: #fbfcfd;
}

.sharing-link-row a {
  color: #304f73;
}

.admin-header,
.top-bar {
  background: #151b24;
}

.admin-summary div,
.cost-card,
.admin-card,
.decision-card {
  box-shadow: 0 1px 2px rgba(17, 22, 29, 0.04);
}

.global-budget-inline-form {
  border-radius: 8px;
}

.project-dashboard,
.project-portfolio,
.workspace-copilot .dashboard-chat-messages,
.section-editor-panel,
.upload-window-panel {
  -webkit-overflow-scrolling: touch;
  scrollbar-color: #b7c1cc transparent;
  scrollbar-width: thin;
}

@supports (padding: max(0px)) {
  .dashboard-shell.executive-dashboard-shell {
    padding-left: env(safe-area-inset-left);
    padding-right: env(safe-area-inset-right);
  }

  .app-top-bar {
    padding-top: max(14px, env(safe-area-inset-top));
  }

  .admin-shell {
    padding-left: max(18px, env(safe-area-inset-left));
    padding-right: max(18px, env(safe-area-inset-right));
  }
}

@media (hover: none) {
  .project-portfolio .portfolio-actions button,
  .project-portfolio .right-row-actions button,
  .project-portfolio .project-delete-button,
  .ai-recommendation-actions button {
    opacity: 1;
    pointer-events: auto;
  }

  .editable-plan-section.selected .section-edit-button:not(:disabled) {
    opacity: 1;
    pointer-events: auto;
    transform: translateY(0);
  }
}

@media (max-width: 900px) {
  .project-focus-summary {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .project-workspace-focus {
    grid-template-columns: 1fr;
  }

  .milestone-focus-header {
    align-items: stretch;
    flex-direction: column;
  }

  .milestone-focus-legend {
    justify-content: flex-start;
  }

  .milestone-gantt-row {
    grid-template-columns: 1fr;
  }

  .milestone-gantt-track {
    min-height: 118px;
  }
}

@media (max-width: 560px) {
  .project-focus-summary {
    grid-template-columns: 1fr;
  }

  .milestone-calendar-scale {
    height: 42px;
  }

  .milestone-calendar-scale span {
    font-size: 0.6rem;
  }

  .milestone-gantt-label {
    padding: 9px;
  }

  .milestone-track-label strong {
    font-size: 0.68rem;
  }

  .milestone-track-label em {
    font-size: 0.6rem;
  }

  .milestone-focus-actions {
    display: grid;
    grid-template-columns: 1fr;
  }
}

/* Risk editor refinement */
.section-editor-panel {
  max-width: 1120px;
  width: min(1120px, calc(100vw - 48px));
}

.risk-editor-row {
  align-items: stretch;
  gap: 12px;
  grid-template-columns: minmax(0, 1fr);
  padding: 12px;
}

.risk-editor-row .section-editor-fields {
  align-items: end;
  gap: 12px;
  grid-template-columns: minmax(320px, 1fr) minmax(220px, auto);
}

.risk-editor-row .section-editor-fields label {
  min-width: 0;
}

.risk-editor-row .section-editor-fields span {
  color: #5f6d7c;
  font-size: 0.72rem;
}

.risk-editor-row .section-editor-fields input {
  font-size: 0.84rem;
  min-height: 42px;
  width: 100%;
}

.risk-editor-row.resolved input {
  background: #f5f7fa;
  color: #5f6d7c;
  text-decoration: none;
}

.risk-lifecycle-meta {
  align-items: center;
  align-self: end;
  display: flex;
  gap: 6px;
  justify-content: flex-start;
  min-width: 0;
}

.risk-lifecycle-meta span {
  align-items: center;
  background: #eef2f6;
  border: 1px solid var(--line);
  border-radius: 7px;
  color: #3e4a57;
  display: inline-flex;
  font-size: 0.68rem;
  font-weight: 800;
  min-height: 30px;
  padding: 0 9px;
  white-space: nowrap;
}

.risk-milestone-picker {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.risk-milestone-picker > span {
  color: #5f6d7c;
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
}

.risk-milestone-checklist {
  display: grid;
  gap: 6px;
  grid-template-columns: repeat(auto-fill, minmax(180px, 1fr));
  max-height: 140px;
  overflow-y: auto;
}

.risk-milestone-checklist label.checkbox-row {
  align-items: center;
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
  display: flex;
  gap: 8px;
  padding: 8px 10px;
}

.risk-milestone-checklist label.checkbox-row input {
  accent-color: #52525b;
  flex: 0 0 auto;
  height: 15px;
  width: 15px;
}

.section-editor-row-error {
  color: var(--danger-strong);
  font-size: 0.76rem;
  font-weight: 700;
  margin: 0;
}

.risk-subentities {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(2, minmax(220px, 1fr));
}

.risk-subentity-block {
  background: #f8fafc;
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 10px 12px;
}

.risk-subentity-header {
  align-items: center;
  display: flex;
  justify-content: space-between;
  margin-bottom: 6px;
}

.risk-subentity-header > span {
  color: #5f6d7c;
  font-size: 0.72rem;
  font-weight: 800;
  text-transform: uppercase;
}

.risk-subentity-list {
  display: flex;
  flex-direction: column;
  gap: 6px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.risk-subentity-list li {
  align-items: flex-start;
  background: #fff;
  border: 1px solid #e9edf2;
  border-radius: 6px;
  display: flex;
  gap: 8px;
  justify-content: space-between;
  padding: 8px 10px;
}

.risk-subentity-list li > div {
  min-width: 0;
}

.risk-subentity-list li p {
  color: #23292f;
  font-size: 0.82rem;
  margin: 0;
  overflow-wrap: anywhere;
}

.risk-subentity-meta {
  color: #6b7684;
  display: block;
  font-size: 0.7rem;
  margin-top: 2px;
}

.risk-subentity-empty {
  color: #8a95a1;
  font-size: 0.78rem;
  font-style: italic;
  list-style: none;
}

.risk-subentity-remove {
  color: #b23b3b;
  flex-shrink: 0;
}

.risk-row-actions {
  align-items: center;
  border-top: 1px solid var(--line);
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: flex-end;
  padding-top: 10px;
}

.risk-row-actions .risk-status-pill {
  margin-right: auto;
}

.risk-row-actions button {
  min-width: 86px;
}

.risk-status-pill {
  align-items: center;
  border: 1px solid rgba(17, 22, 29, 0.08);
  border-radius: 999px;
  display: inline-flex;
  font-size: 0.68rem;
  font-weight: 850;
  justify-content: center;
  line-height: 1;
  min-height: 30px;
  padding: 0 11px;
  text-transform: none;
  white-space: nowrap;
}

.risk-status-pill.open {
  background: #fff0c2;
  color: var(--warn);
}

.risk-status-pill.closed {
  background: #e7ebf0;
  color: #3e4a57;
}

@media (max-width: 1180px) {
  .dashboard-shell.executive-dashboard-shell {
    grid-template-columns: minmax(230px, 30vw) minmax(0, 1fr);
  }
}

@media (max-width: 860px) {
  .app-shell {
    grid-template-columns: 1fr;
  }

  .input-panel,
  .chat-panel,
  .side-panel {
    min-height: auto;
  }

  .input-panel {
    border-right: 0;
  }

  .side-panel {
    border-left: 0;
    border-top: 1px solid var(--line);
  }

  .top-bar,
  .chat-header,
  .side-header {
    align-items: stretch;
    flex-direction: column;
  }

  .user-control {
    align-items: stretch;
    flex-direction: column;
  }

  .user-control input {
    width: 100%;
  }

  .admin-header,
  .admin-actions {
    align-items: stretch;
    flex-direction: column;
  }

  .dashboard-profile-row {
    align-items: stretch;
    grid-template-columns: 1fr;
  }

  .dashboard-profile-actions {
    justify-items: stretch;
  }

  .dashboard-profile-link {
    text-align: center;
  }

  .admin-summary,
  .admin-form,
  .cost-grid {
    grid-template-columns: 1fr;
  }

  .dashboard-shell,
  .plan-grid,
  .upload-row,
  .workspace-grid,
  .executive-plan-grid,
  .executive-metrics,
  .executive-report-grid {
    grid-template-columns: 1fr;
  }

  .dashboard-shell.executive-dashboard-shell {
    height: auto;
    min-height: 100vh;
    overflow: visible;
  }

  .workspace-copilot {
    display: grid;
    max-height: none;
    min-height: auto;
  }

  body.executive-view-mode .workspace-copilot {
    display: none;
  }

  .project-dashboard {
    overflow: visible;
  }

  .risk-editor-row .section-editor-fields {
    grid-template-columns: 1fr;
  }

  .risk-lifecycle-meta {
    align-self: auto;
  }

  .risk-subentities {
    grid-template-columns: 1fr;
  }

  .executive-project-score-row {
    grid-template-columns: minmax(0, 1fr);
  }

  .score-table-cell {
    border-left: 0;
    border-top: 1px solid #edf1f5;
  }

  .dashboard-chat,
  .executive-copilot {
    min-height: 68vh;
  }

  .dashboard-header,
  .dashboard-actions,
  .plan-overview,
  .pending-plan-review,
  .version-note {
    align-items: stretch;
    flex-direction: column;
  }

  .pending-plan-review:not(:empty) {
    grid-template-columns: 1fr;
  }

  .pending-actions {
    min-width: 0;
  }

  .executive-project-hero {
    grid-template-columns: 1fr;
  }

  .executive-hero-scores {
    flex-direction: column;
  }

  .executive-hero-gauges {
    flex-direction: column;
    width: 100%;
  }

  .global-score-gauge {
    width: 100%;
  }

  .global-score-gauge-visual {
    max-width: 220px;
  }

  .project-detail-strip {
    grid-template-columns: 1fr;
  }

  .health-score {
    align-items: start;
    text-align: left;
  }

  .score-cycles {
    justify-content: flex-start;
    margin-left: 0;
  }

  .executive-scorecard {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    margin-left: 0;
    min-width: 0;
    width: 100%;
  }

  .executive-score-card:nth-child(odd) {
    border-left: 0;
  }

  .executive-score-card:nth-child(n + 3) {
    border-top: 1px solid var(--line);
  }
}

@media (max-width: 560px) {
  .executive-project-hero h2 {
    font-size: 1.35rem;
  }

  .executive-scorecard {
    grid-template-columns: 1fr;
  }

  .executive-score-card,
  .executive-score-card:nth-child(odd) {
    border-left: 0;
  }

  .executive-score-card + .executive-score-card {
    border-top: 1px solid var(--line);
  }
}

.landing-body,
.onboarding-body {
  --theruka-navy: #06213f;
  --theruka-ink: #0c1f35;
  --theruka-muted: #617083;
  --theruka-paper: #fbf7f0;
  --theruka-line: rgba(6, 33, 63, 0.16);
  --theruka-green: #14635a;
  --theruka-gold: #b2813a;
  background: var(--theruka-paper);
  color: var(--theruka-ink);
}

.landing-nav {
  align-items: center;
  background: rgba(251, 247, 240, 0.96);
  border-bottom: 1px solid var(--theruka-line);
  display: grid;
  gap: 20px;
  grid-template-columns: minmax(150px, 220px) minmax(0, 1fr) auto;
  min-height: 78px;
  padding: 12px clamp(18px, 4vw, 56px);
  position: sticky;
  top: 0;
  z-index: 20;
}

.landing-brand {
  align-items: center;
  display: inline-flex;
  min-width: 0;
  position: relative;
  text-decoration: none;
}

.landing-brand img {
  display: block;
  /* 46px in a 68px bar left the mark reading small against everything beside
     it. 52px is a shade over three quarters of the bar - present without
     crowding the nav actions on the right.

     THE WIDTH IS 52 BECAUSE THE ARTWORK IS SQUARE, and this is worth writing
     down because the numbers here have been lying for a while.
     theruka-logo.png is 1254x1254. The rule said 178x46, which object-fit:
     contain then letterboxed down to 46x46 - so 132px of the reserved 178 was
     empty space, pushing the nav links right for nothing, and the height was
     the only value that ever affected what a visitor saw. Raising the width in
     proportion would have reserved even more nothing.

     Explicit rather than `width: auto` so the box is still reserved before the
     image loads and the nav does not jump. If the mark is ever replaced with a
     wide wordmark, this pair has to move together - contain will not distort
     it, but it will sit in a box that is the wrong shape.

     Shared with onboarding.html, which reuses .landing-brand. */
  height: 52px;
  object-fit: contain;
  object-position: left center;
  width: 52px;
}

.landing-nav nav {
  align-items: center;
  display: flex;
  gap: clamp(16px, 3vw, 34px);
  justify-content: flex-end;
  min-width: 0;
}

.landing-nav a {
  color: var(--theruka-ink);
  font-size: 0.88rem;
  font-weight: 700;
  text-decoration: none;
}

.landing-signin,
.landing-primary,
.landing-secondary,
.google-fallback-button {
  align-items: center;
  border-radius: 6px;
  display: inline-flex;
  font-weight: 800;
  justify-content: center;
  min-height: 44px;
  padding: 0 18px;
  text-decoration: none;
}

.landing-signin,
.landing-primary,
.google-fallback-button {
  background: var(--theruka-navy);
  color: #fff;
}

.landing-secondary {
  background: rgba(255, 255, 255, 0.74);
  border: 1px solid var(--theruka-line);
  color: var(--theruka-navy);
}

.landing-hero {
  align-items: center;
  background-image:
    linear-gradient(90deg, rgba(251, 247, 240, 0.96) 0%, rgba(251, 247, 240, 0.86) 36%, rgba(6, 33, 63, 0.1) 100%),
    url("/assets/theruka-brand-board.png");
  background-position: center;
  background-size: cover;
  display: flex;
  min-height: 78vh;
  padding: clamp(42px, 8vw, 96px) clamp(22px, 6vw, 86px);
}

.landing-hero-content {
  max-width: 580px;
}

.landing-hero h1,
.landing-section h2,
.onboarding-brand h1 {
  color: var(--theruka-navy);
  /* No font-family here. landing-refresh.css sets `inherit` on these exact
     selectors and loads later, so the Georgia that used to sit on this line had
     not rendered since that file landed - see its typography note for why the
     serif went. Leaving a dead declaration naming a face the product does not
     use is how a survey of this file concludes it has three typefaces. */
  font-weight: 500;
  line-height: 1.05;
  margin: 0;
}

.landing-hero h1 {
  font-size: 5.2rem;
  max-width: 680px;
}

.landing-hero p,
.landing-section p,
.onboarding-brand p,
.onboarding-panel p {
  color: var(--theruka-muted);
  font-size: 1rem;
  line-height: 1.7;
}

.landing-hero-content > p:not(.eyebrow) {
  max-width: 540px;
}

.landing-hero-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  margin-top: 26px;
}

.landing-hero-steps {
  display: none;
}

.landing-section {
  padding: clamp(42px, 7vw, 82px) clamp(22px, 6vw, 86px);
}

.landing-product {
  align-items: start;
  display: grid;
  gap: 34px;
  grid-template-columns: minmax(240px, 360px) minmax(0, 1fr);
}

.landing-section h2 {
  font-size: 3rem;
}

.landing-feature-grid {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.landing-feature-grid article {
  background: rgba(255, 255, 255, 0.58);
  border: 1px solid var(--theruka-line);
  border-radius: 8px;
  box-shadow: 0 10px 24px rgba(6, 33, 63, 0.05);
  min-height: 180px;
  padding: 22px;
  transition: box-shadow 200ms ease, transform 200ms ease;
}

.landing-feature-grid article:hover {
  box-shadow: 0 16px 34px rgba(6, 33, 63, 0.1);
  transform: translateY(-2px);
}

.landing-feature-icon {
  align-items: center;
  background: rgba(6, 33, 63, 0.06);
  border-radius: 10px;
  color: #06213f;
  display: flex;
  height: 38px;
  justify-content: center;
  margin-bottom: 14px;
  width: 38px;
}

.landing-feature-icon svg {
  height: 20px;
  width: 20px;
}

.landing-feature-grid strong {
  color: var(--theruka-navy);
  display: block;
  font-size: 1rem;
  margin-bottom: 12px;
}

.landing-feature-grid p {
  font-size: 0.92rem;
  margin: 0;
}

.landing-pricing {
  background: #f7f3ec;
  display: grid;
  gap: 30px;
}

.landing-pricing-heading {
  display: grid;
  gap: 12px;
  max-width: 760px;
}

.landing-pricing-heading p {
  margin: 0;
}

.pricing-plan-grid {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
}

.pricing-plan {
  background: rgba(255, 255, 255, 0.76);
  border: 1px solid var(--theruka-line);
  border-radius: 8px;
  display: grid;
  gap: 18px;
  grid-template-rows: auto 1fr auto;
  min-height: 100%;
  padding: 22px;
}

.pricing-plan-featured {
  background: #ffffff;
  border-color: rgba(20, 99, 90, 0.4);
  box-shadow: 0 18px 42px rgba(6, 33, 63, 0.12);
}

.pricing-plan-contact {
  background: #06213f;
  border-color: rgba(255, 255, 255, 0.16);
}

.pricing-plan-contact .pricing-plan-header {
  border-bottom-color: rgba(255, 255, 255, 0.18);
}

.pricing-plan-contact .pricing-plan-header strong,
.pricing-plan-contact .pricing-plan-header span {
  color: #ffffff;
}

.pricing-plan-contact .pricing-plan-header small,
.pricing-plan-contact ul {
  color: rgba(255, 255, 255, 0.78);
}

.pricing-plan-contact li {
  border-bottom-color: rgba(255, 255, 255, 0.14);
}

.pricing-plan-contact .landing-secondary {
  background: #ffffff;
  border-color: #ffffff;
  color: var(--theruka-navy);
}

.pricing-plan-header {
  border-bottom: 1px solid var(--theruka-line);
  display: grid;
  gap: 10px;
  padding-bottom: 16px;
}

.pricing-plan-header strong {
  color: var(--theruka-navy);
  font-size: 1.2rem;
  font-weight: 900;
}

.pricing-plan-header span {
  color: var(--theruka-green);
  font-size: 1.7rem;
  font-weight: 900;
  line-height: 1;
}

.pricing-plan-header small {
  color: var(--theruka-muted);
  font-size: 0.82rem;
  font-weight: 800;
}

.pricing-plan ul {
  color: var(--theruka-muted);
  display: grid;
  gap: 10px;
  align-content: start;
  line-height: 1.4;
  list-style: none;
  margin: 0;
  padding: 0;
}

.pricing-plan li {
  border-bottom: 1px solid rgba(6, 33, 63, 0.08);
  padding-bottom: 10px;
}

.pricing-plan li:last-child {
  border-bottom: 0;
  padding-bottom: 0;
}

.pricing-plan .landing-primary,
.pricing-plan .landing-secondary {
  align-self: end;
  justify-self: start;
  margin-top: 4px;
}

.landing-demo {
  align-items: center;
  background: #eef4f2;
  display: grid;
  gap: 42px;
  grid-template-columns: minmax(260px, 420px) minmax(0, 1fr);
}

.landing-demo-copy p {
  margin-bottom: 24px;
}

.landing-demo-board {
  background: #ffffff;
  border: 1px solid rgba(20, 99, 90, 0.2);
  border-radius: 22px;
  box-shadow: 0 22px 60px rgba(6, 33, 63, 0.12);
  display: grid;
  gap: 18px;
  padding: clamp(18px, 3vw, 30px);
}

.demo-board-header {
  align-items: center;
  border-bottom: 1px solid var(--theruka-line);
  color: var(--theruka-navy);
  display: flex;
  gap: 16px;
  justify-content: space-between;
  padding-bottom: 16px;
}

.demo-board-header > span:first-child {
  font-weight: 800;
}

.demo-health-badge {
  align-items: center;
  background: rgba(20, 99, 90, 0.1);
  border-radius: 999px;
  color: var(--theruka-green);
  display: inline-flex;
  gap: 6px;
  padding: 6px 12px;
}

.demo-health-badge svg {
  height: 15px;
  width: 15px;
}

.demo-board-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(4, minmax(0, 1fr));
}

.demo-board-grid > span {
  background: #f7faf9;
  border: 1px solid rgba(20, 99, 90, 0.14);
  border-radius: 14px;
  color: var(--theruka-muted);
  font-size: 0.82rem;
  min-height: 86px;
  padding: 14px;
}

.demo-stat-icon {
  align-items: center;
  background: rgba(20, 99, 90, 0.1);
  border-radius: 8px;
  color: var(--theruka-green);
  display: flex;
  height: 24px;
  justify-content: center;
  margin-bottom: 8px;
  width: 24px;
}

.demo-stat-icon svg {
  height: 14px;
  width: 14px;
}

.demo-board-grid strong {
  color: var(--theruka-navy);
  display: block;
  font-size: 1.7rem;
  margin-top: 8px;
}

.demo-trend {
  align-items: end;
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(4, 1fr);
  height: 132px;
}

.demo-trend span {
  background: linear-gradient(180deg, var(--theruka-green), #d9eee9);
  border-radius: 10px 10px 0 0;
  display: block;
}

.demo-trend span:nth-child(1) {
  height: 42%;
}

.demo-trend span:nth-child(2) {
  height: 66%;
}

.demo-trend span:nth-child(3) {
  height: 52%;
}

.demo-trend span:nth-child(4) {
  height: 82%;
}

.landing-resource-band {
  background: var(--theruka-navy);
  color: #fff;
}

.landing-resource-band h2,
.landing-resource-band p {
  color: #fff;
  max-width: 900px;
}

.landing-resource-roadmap {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
  list-style: none;
  margin: 28px 0 0;
  padding: 0;
}

.landing-resource-roadmap li {
  align-items: center;
  background: rgba(255, 255, 255, 0.08);
  border: 1px solid rgba(255, 255, 255, 0.16);
  border-radius: 999px;
  display: flex;
  gap: 10px;
  padding: 10px 18px 10px 14px;
}

.landing-resource-roadmap li.available {
  background: rgba(52, 168, 83, 0.16);
  border-color: rgba(52, 168, 83, 0.35);
}

.landing-resource-roadmap svg {
  flex: 0 0 auto;
  height: 18px;
  opacity: 0.9;
  width: 18px;
}

.landing-resource-roadmap span {
  display: grid;
  gap: 1px;
}

.landing-resource-roadmap strong {
  font-size: 0.78rem;
  letter-spacing: 0.01em;
}

.landing-resource-roadmap small {
  color: rgba(255, 255, 255, 0.72);
  font-size: 0.78rem;
}

.landing-company {
  align-items: center;
  display: grid;
  gap: 34px;
  grid-template-columns: 220px minmax(0, 1fr);
}

.landing-company-logo {
  background: #fff;
  border: 1px solid var(--theruka-line);
  border-radius: 8px;
  display: block;
  padding: 16px;
  width: 100%;
}

.landing-footer {
  align-items: center;
  border-top: 1px solid var(--theruka-line);
  color: var(--theruka-muted);
  display: flex;
  /* space-between on a non-wrapping flex row pushes the last link past the
     right edge once the two children no longer fit side by side - measured at
     320px, "Trust and data handling" started at 321px. The gap only applies
     once they wrap, so wide layouts are unchanged. */
  flex-wrap: wrap;
  gap: 8px 16px;
  justify-content: space-between;
  padding: 24px clamp(22px, 6vw, 86px);
}

.landing-footer a {
  color: var(--theruka-navy);
  font-weight: 800;
  text-decoration: none;
}

.landing-body .site-footer,
.onboarding-body .site-footer {
  background: var(--theruka-paper);
  border-top-color: var(--theruka-line);
  color: var(--theruka-muted);
}

.landing-nav-accessible {
  height: 1px;
  left: 0;
  opacity: 0;
  overflow: hidden;
  padding: 0;
  pointer-events: none;
  position: absolute;
  top: 0;
  width: 1px;
}

.landing-hero {
  background: #edf2f7;
  display: grid;
  height: 100vh;
  height: 100dvh;
  max-height: 100vh;
  max-height: 100dvh;
  min-height: 0;
  overflow: hidden;
  padding: 0;
  place-items: center;
  position: relative;
}

.landing-hero-frame {
  height: 100%;
  margin: 0;
  max-height: none;
  max-width: none;
  position: relative;
  width: 100%;
  z-index: 1;
}

.landing-hero-image {
  display: block;
  height: 100%;
  min-height: 0;
  object-fit: fill;
  object-position: center center;
  width: 100%;
}

.landing-hero-content {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

.landing-hotspot {
  background: transparent;
  border-radius: 8px;
  color: transparent;
  display: block;
  position: absolute;
  text-decoration: none;
}

.landing-hotspot:focus-visible {
  outline: 3px solid rgba(6, 33, 63, 0.75);
  outline-offset: 4px;
}

.landing-hotspot-product {
  height: 5%;
  left: 35.6%;
  top: 6%;
  width: 5.2%;
}

.landing-hotspot-features {
  height: 5%;
  left: 44.4%;
  top: 6%;
  width: 6.2%;
}

.landing-hotspot-pricing {
  height: 5%;
  left: 53.5%;
  top: 6%;
  width: 5.2%;
}

.landing-hotspot-resources {
  height: 5%;
  left: 61.2%;
  top: 6%;
  width: 7%;
}

.landing-hotspot-company {
  height: 5%;
  left: 70.8%;
  top: 6%;
  width: 6.6%;
}

.landing-hotspot-nav-start {
  height: 5.3%;
  left: 83.4%;
  top: 6%;
  width: 9.4%;
}

.landing-hotspot-start {
  height: 6.2%;
  left: 4.4%;
  top: 54%;
  width: 11.2%;
}

.landing-hotspot-demo {
  height: 6.2%;
  left: 17.4%;
  top: 54%;
  width: 12%;
}

.onboarding-shell {
  align-items: center;
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(360px, 480px);
  min-height: 100vh;
  overflow: hidden;
  padding: clamp(20px, 4vw, 56px);
  position: relative;
}

.onboarding-shell::before {
  background: linear-gradient(90deg, rgba(251, 247, 240, 0) 0%, rgba(251, 247, 240, 0.14) 48%, rgba(251, 247, 240, 0.58) 100%);
  content: "";
  inset: 0;
  pointer-events: none;
  position: absolute;
  z-index: 1;
}

.onboarding-hero-image {
  height: 100%;
  inset: 0;
  object-fit: cover;
  object-position: center top;
  position: absolute;
  width: 100%;
  z-index: 0;
}

.onboarding-background-logo-link {
  border-radius: 10px;
  display: block;
  height: 18%;
  left: 2.2%;
  position: absolute;
  text-decoration: none;
  top: 7.2%;
  width: 18.8%;
  z-index: 4;
}

.onboarding-background-link {
  border-radius: 8px;
  display: block;
  height: 5.8%;
  position: absolute;
  text-decoration: none;
  top: 11.8%;
  z-index: 4;
}

.onboarding-background-product {
  left: 35.6%;
  width: 5.2%;
}

.onboarding-background-features {
  left: 44.4%;
  width: 6.2%;
}

.onboarding-background-pricing {
  left: 53.5%;
  width: 5.2%;
}

.onboarding-background-resources {
  left: 61.2%;
  width: 7%;
}

.onboarding-background-company {
  left: 70.8%;
  width: 6.6%;
}

.onboarding-background-logo-link:focus-visible,
.onboarding-background-link:focus-visible {
  outline: 3px solid rgba(6, 33, 63, 0.75);
  outline-offset: 4px;
}

.onboarding-brand {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

.onboarding-brand h1 {
  font-size: 4.6rem;
  max-width: 720px;
}

.onboarding-brand p {
  max-width: 600px;
}

.onboarding-panel {
  align-self: center;
  background: rgba(255, 255, 255, 0.96);
  -webkit-backdrop-filter: blur(16px);
  backdrop-filter: blur(16px);
  border: 1px solid var(--theruka-line);
  border-radius: 8px;
  box-shadow: 0 24px 80px rgba(6, 33, 63, 0.22);
  display: grid;
  gap: 14px;
  grid-column: 2;
  justify-self: end;
  margin: 0;
  padding: 34px;
  position: relative;
  width: min(100%, 440px);
  z-index: 2;
}

.onboarding-panel h2 {
  color: var(--theruka-navy);
  font-size: 1.5rem;
  margin: 0;
}

.onboarding-copy,
.onboarding-status {
  margin: 0;
}

.google-signin-host {
  min-height: 44px;
}

.onboarding-action-grid {
  display: grid;
  gap: 10px;
}

/* The email sign-in form, shown alongside the Google button rather than instead
   of it - so it is styled as a peer, not a fallback. */
.magic-link-form {
  display: grid;
  gap: 10px;
  margin-top: 14px;
}

/* Needed for the same reason as the divider rule below: display:grid above
   outranks the user-agent's [hidden] { display: none }. Without this the form
   renders even when the server cannot send mail, which is the one thing the
   magicLinkEnabled flag exists to prevent. */
.magic-link-form[hidden] {
  display: none;
}

.magic-link-divider {
  align-items: center;
  color: var(--muted, #6b7280);
  display: flex;
  font-size: 13px;
  gap: 10px;
  margin: 4px 0 0;
}

/* Needed because the display:flex above outranks the user-agent's
   [hidden] { display: none } - without this, hiding the divider does nothing. */
.magic-link-divider[hidden] {
  display: none;
}

.magic-link-divider::before,
.magic-link-divider::after {
  background: var(--line, #e5e7eb);
  content: "";
  flex: 1;
  height: 1px;
}

.magic-link-field {
  display: grid;
  gap: 4px;
}

.magic-link-field > span {
  color: var(--muted, #6b7280);
  font-size: 13px;
}

.magic-link-field input {
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 8px;
  font: inherit;
  padding: 10px 12px;
  width: 100%;
}

.magic-link-form button[disabled] {
  cursor: progress;
  opacity: 0.6;
}

.google-fallback-button {
  line-height: 1.3;
  min-height: 58px;
  overflow-wrap: anywhere;
  padding: 10px 18px;
  white-space: normal;
  width: 100%;
}

/* --- Requests to invite ---------------------------------------------------
   Sits above the roster because it is the only part of the People page waiting
   on a decision. Tinted rather than bordered-loud: it is a to-do, not a
   warning - nothing is broken, somebody just has to choose. */
.seat-request-queue {
  background: var(--panel, #f4f4f5);
  border: 1px solid var(--line, #e4e4e7);
  border-radius: var(--radius-card, 12px);
  display: grid;
  gap: 10px;
  margin: 0 0 18px;
  padding: 16px 18px;
}

.seat-request-queue[hidden] {
  display: none;
}

.seat-request-heading {
  font-size: 0.95rem;
  font-weight: 700;
  margin: 0;
}

.seat-request-intro {
  color: var(--muted, #6b7280);
  font-size: 0.84rem;
  margin: 0;
}

.seat-request-list {
  display: grid;
  gap: 8px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.seat-request-row {
  align-items: center;
  background: var(--surface, #fff);
  border: 1px solid var(--line, #e4e4e7);
  border-radius: var(--radius-control, 8px);
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: space-between;
  padding: 11px 13px;
}

.seat-request-row > div:first-child {
  display: grid;
  gap: 2px;
}

.seat-request-row strong {
  font-size: 0.92rem;
}

.seat-request-email,
.seat-request-note {
  color: var(--muted, #6b7280);
  font-size: 0.8rem;
}

/* Named rather than just greyed: without an email there is nothing to invite,
   and the disabled button alone does not say why. */
.seat-request-no-email {
  color: var(--warn, #92400e);
}

.seat-request-row small {
  color: var(--muted, #6b7280);
  font-size: 0.74rem;
}

.seat-request-actions {
  display: flex;
  gap: 8px;
}

/* The state line. Slightly stronger than the metadata around it, because it is
   the sentence somebody is actually here to read. */
.seat-request-state {
  font-size: 0.82rem;
  line-height: 1.45;
}

/* A left edge per state, so the three read apart at a glance without colouring
   the text itself - a whole line in red would say "something is wrong", and a
   declined request is a decision, not a fault. */
.seat-request-row {
  border-left: 3px solid transparent;
}

.seat-request-row.is-pending {
  border-left-color: var(--accent, #2f6df6);
}

.seat-request-row.is-pending .seat-request-state {
  color: var(--ink, #18181b);
}

.seat-request-row.is-invited {
  border-left-color: #2f6b4f;
}

.seat-request-row.is-invited .seat-request-state {
  color: #2f6b4f;
}

/* Resolved rows are quieter than open ones: they are an answer somebody may
   still need, not a question anybody is waiting on. */
.seat-request-row.is-declined {
  border-left-color: var(--line, #d4d4d8);
}

.seat-request-row.is-declined .seat-request-state,
.seat-request-row.is-declined strong {
  color: var(--muted, #6b7280);
}

/* --- The demo, told apart from signing in ---------------------------------
   Teal rather than the page's navy, and deliberately not a third shade of it.
   Everything else in this panel starts an account; this one does not, and two
   buttons in the same colour say "these are alternatives of the same kind"
   when the whole point is that they are not.

   Teal rather than a warning colour too: exploring is the thing we WANT a
   visitor to do. The caution belongs on the one line about work not being
   saved, not on the button that invites the click. */
.onboarding-demo-panel {
  background: rgba(15, 92, 77, 0.05);
  border: 1px solid rgba(15, 92, 77, 0.2);
  border-radius: 14px;
  display: grid;
  gap: 10px;
  margin-top: 18px;
  padding: 18px 18px 20px;
}

.onboarding-demo-title {
  color: #0f3d34;
  font-size: 0.98rem;
  font-weight: 800;
  margin: 0;
}

.onboarding-demo-lead {
  color: #3f5b55;
  font-size: 0.85rem;
  line-height: 1.5;
  margin: 0;
}

.onboarding-demo-points {
  display: grid;
  gap: 7px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.onboarding-demo-points li {
  color: #3f5b55;
  font-size: 0.82rem;
  line-height: 1.45;
  padding-left: 16px;
  position: relative;
}

.onboarding-demo-points li::before {
  color: #0f5c4d;
  content: "\2022";
  left: 2px;
  position: absolute;
}

/* The one line that is a caution rather than an invitation. Weight and a rule
   carry it instead of red: a red sentence beside a teal button reads as an
   error on the page, and this is not an error - it is how the demo works. */
/* Selector carries both classes so it out-specifies `.onboarding-demo-points li`
   above on its own. The colour would otherwise need !important, which is the
   cascade fight rather than the fix. */
.onboarding-demo-points li.onboarding-demo-warning {
  border-top: 1px solid rgba(15, 92, 77, 0.16);
  color: #2c4a44;
  font-weight: 600;
  margin-top: 3px;
  padding-top: 9px;
}

.onboarding-demo-button {
  align-items: center;
  background: #0f5c4d;
  border: 1px solid #0f5c4d;
  border-radius: 999px;
  color: #ffffff;
  display: flex;
  font-size: 0.9rem;
  font-weight: 700;
  justify-content: center;
  margin-top: 4px;
  min-height: 52px;
  text-decoration: none;
  transition: background 140ms ease, border-color 140ms ease;
  width: 100%;
}

.onboarding-demo-button:hover {
  background: #0c4a3e;
  border-color: #0c4a3e;
}

.onboarding-demo-button:focus-visible {
  outline: 3px solid rgba(15, 92, 77, 0.35);
  outline-offset: 3px;
}

#requestAccessButton {
  min-height: 52px;
  width: 100%;
}

.workspace-switcher {
  background: #ffffff;
  border: 1px solid rgba(6, 33, 63, 0.16);
  border-radius: 999px;
  color: #12453f;
  font-size: 0.74rem;
  font-weight: 700;
  max-width: 220px;
  min-height: 34px;
  padding: 0 10px;
}

.workspace-switcher[hidden] {
  display: none;
}

.notification-bell-wrap {
  position: relative;
}

.notification-bell-button {
  align-items: center;
  background: #ffffff !important;
  border: 1px solid rgba(6, 33, 63, 0.16) !important;
  border-radius: 999px !important;
  box-shadow: none !important;
  /* Loses to `.dashboard-actions button:not(.secondary-button)`, which is later
     and more specific and sets white - see the rule below that fixes it. Kept
     here so this block still describes the control on its own. */
  color: var(--muted, #66717e);
  display: flex;
  height: 34px;
  justify-content: center;
  position: relative;
  width: 34px;
}

/* An SVG now rather than an emoji, so it is sized in pixels and inherits the
   button's colour instead of arriving pre-coloured by the operating system.

   `flex: none` is load-bearing: the button is a flex container whose global
   padding leaves barely any content box, so the icon was shrinking to 4px wide
   at its full 18px height - a vertical sliver, which is what a bell looks like
   when a flex item is allowed to shrink below its own size. */
/* Specificity, deliberately, rather than !important.
   ---------------------------------------------------------------------------
   `.dashboard-actions button:not(.secondary-button)` paints every button in
   this bar white, and had been winning here since the rule was written - which
   nobody could see, because the icon was an emoji and an emoji ignores `color`.
   The moment it became a stroked SVG taking currentColor, the bell was white on
   a white button.

   !important would have fixed the light theme and broken the dark one:
   dark-mode.css sets this button's colour WITHOUT !important, so it would have
   been beaten and the bell would have gone near-black on a dark background.
   Three classes beats that white rule and still loses to dark mode's own, which
   is exactly the order these three should resolve in.

   --muted is the same token the expand button beside it uses, so the two icon
   buttons in this bar finally match. */
.dashboard-actions .notification-bell-wrap .notification-bell-button {
  color: var(--muted, #66717e);
}

.notification-bell-icon {
  flex: none;
  height: 18px;
  width: 18px;
}

.notification-bell-badge {
  background: #dc2626;
  border-radius: 999px;
  color: #fff;
  font-size: 0.62rem;
  font-weight: 800;
  min-width: 16px;
  padding: 1px 4px;
  position: absolute;
  right: -4px;
  text-align: center;
  top: -4px;
}

.notification-dropdown {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: 0 16px 32px rgba(15, 23, 42, 0.16);
  max-height: 360px;
  overflow-y: auto;
  position: absolute;
  right: 0;
  top: calc(100% + 8px);
  width: min(320px, 90vw);
  z-index: 40;
}

.notification-dropdown-header {
  align-items: center;
  border-bottom: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  padding: 10px 12px;
}

.link-button {
  background: none;
  border: none;
  /* The generic button{} reset's subtle bevel (box-shadow: 0 1px 1px + inset
     highlight) was never cleared here - invisible against a light background,
     but the inset white highlight line reads as a distinct ghost-outline box
     against dark mode's near-black canvas, on top of that reset's
     min-height:40px/border-radius still shaping an invisible box for it to
     trace. */
  box-shadow: none;
  color: var(--theruka-green-strong, #0f6b42);
  cursor: pointer;
  font-size: 0.72rem;
  font-weight: 700;
  min-height: auto;
  padding: 0;
}

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

.notification-item {
  border-bottom: 1px solid #f1f1f3;
  cursor: default;
  padding: 10px 12px;
}

.notification-item:last-child {
  border-bottom: none;
}

.notification-item p {
  margin: 0;
}

.notification-item span {
  color: var(--muted);
  font-size: 0.68rem;
}

.notification-item.unread {
  background: var(--good-wash);
  cursor: pointer;
}

.notification-item.unread p {
  font-weight: 700;
}

/* Only rows that actually go somewhere invite a click. A notification about
   something outside the workspace stays inert, and looks it. */
.notification-item.notification-navigable {
  cursor: pointer;
}

.notification-item.notification-navigable:hover,
.notification-item.notification-navigable:focus-visible {
  background: #eef2ff;
}

.notification-item.notification-navigable p::after {
  color: var(--muted);
  content: " →";
  font-weight: 400;
}

.subscription-limit-banner {
  background: var(--warn-wash);
  border: 1px solid #f3d98b;
  border-radius: 10px;
  color: var(--warn);
  display: grid;
  gap: 6px;
  margin-bottom: 12px;
  padding: 12px 14px;
}

.subscription-limit-banner ul {
  display: grid;
  font-size: 0.82rem;
  gap: 4px;
  margin: 0;
  padding-left: 18px;
}

/* What the account is paying for today, above the controls that change it.
   Replaced the AI-credits-only tile that used to sit here: the page below is
   all choices, and none of them state the current position without being read
   against each other. */
.account-summary {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  display: grid;
  gap: 12px;
  padding: 14px 16px;
}

.account-summary header strong {
  color: var(--ink);
}

/* auto-fit rather than a fixed three columns: the three facts are equal
   citizens, and on a narrow screen they should stack rather than squeeze. */
.account-summary-grid {
  display: grid;
  gap: 12px 20px;
  grid-template-columns: repeat(auto-fit, minmax(160px, 1fr));
  margin: 0;
}

.account-summary-item dt {
  color: var(--muted);
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.account-summary-item dd {
  color: var(--ink);
  font-size: 1.1rem;
  margin: 2px 0 0;
}

.account-summary-item p {
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.4;
  margin: 2px 0 0;
}

.account-plan-grid {
  display: grid;
  gap: 10px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

@media (max-width: 560px) {
  .account-plan-grid {
    grid-template-columns: 1fr;
  }
}

.account-plan-card {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: var(--radius-tile);
  box-shadow: var(--shadow-resting);
  display: grid;
  gap: 6px;
  padding: 12px;
}

.account-plan-card.current {
  border-color: #14635a;
  box-shadow: 0 0 0 1px #14635a inset;
}

.account-plan-card header {
  align-items: flex-start;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  justify-content: space-between;
}

.account-plan-badge {
  background: #eef4f2;
  border-radius: 999px;
  color: #12453f;
  flex: 0 0 auto;
  font-size: 0.62rem;
  font-weight: 800;
  padding: 2px 8px;
  white-space: nowrap;
}

.account-plan-price {
  font-size: 1.05rem;
  font-weight: 850;
}

.account-plan-positioning {
  color: var(--muted);
  font-size: 0.74rem;
}

.account-plan-card ul {
  display: grid;
  gap: 3px;
  font-size: 0.74rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

/* The list price a seat discount replaced. Struck rather than removed, so the
   lower number reads as a discount instead of as a miscalculation. */
.plan-card-was {
  color: var(--muted);
  text-decoration: line-through;
}

.account-plan-contact,
.account-plan-action {
  margin-top: 4px;
}

.account-plan-contact {
  color: var(--muted);
  font-size: 0.72rem;
  margin-bottom: 0;
}

.account-addon-list {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: var(--radius-tile);
  box-shadow: var(--shadow-resting);
  display: grid;
  gap: 6px;
  padding: 12px;
}

.account-addon-list header strong {
  font-size: 0.92rem;
}

/* ---------------------------------------------------------------------------
   AI credit packs, as a table you choose from.

   This was a list of "100 credits - $19" lines each with its own small Buy
   button, which made three packs look like three unrelated offers and gave the
   reader nothing to compare them on: $89 against $169 says nothing about which
   is better value. The table adds the figure that does - price per credit - and
   the discount the plan's own pricing already carried but never showed.

   One buy button for the group rather than one per row, because there is one
   decision here: which pack. See the credit pack block in dashboard.js.
   ------------------------------------------------------------------------ */
.credit-pack-table-wrap {
  /* A price table must never make the page scroll sideways. */
  overflow-x: auto;
}

.credit-pack-table {
  border-collapse: collapse;
  font-size: 0.85rem;
  margin-top: 6px;
  width: 100%;
}

.credit-pack-table th {
  border-bottom: 1px solid var(--line, #e4eaf0);
  color: var(--muted, #6b7280);
  font-size: 0.7rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  padding: 0 8px 5px;
  text-align: left;
  text-transform: uppercase;
  white-space: nowrap;
}

.credit-pack-table td {
  border-bottom: 1px solid var(--line, #e4eaf0);
  padding: 9px 8px;
  vertical-align: middle;
}

.credit-pack-table tbody tr:last-child td {
  border-bottom: none;
}

.credit-pack-row:has(input:checked) {
  background: color-mix(in srgb, var(--accent-brand) 6%, transparent);
}

/* A pack this plan will not sell is shown rather than hidden - "Contact us" is
   an answer, and a row vanishing per plan makes the table look inconsistent -
   but it must not look pressable. */
.credit-pack-row.is-unavailable {
  opacity: 0.6;
}

.credit-pack-choice {
  align-items: center;
  cursor: pointer;
  display: flex;
  gap: 8px;
}

.credit-pack-row.is-unavailable .credit-pack-choice {
  cursor: default;
}

.credit-pack-size {
  font-weight: 600;
  white-space: nowrap;
}

.credit-pack-figure {
  /* Digits line up column-wise, which is the point of a price table. */
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* The struck-through list price. Only rendered when the plan's own price is
   lower, so its presence is the discount. */
.credit-pack-was {
  color: var(--muted, #6b7280);
  font-size: 0.78rem;
  text-decoration: line-through;
}

.credit-pack-unit {
  color: var(--muted, #6b7280);
  font-size: 0.75rem;
}

.credit-pack-contact {
  color: var(--muted, #6b7280);
}

.credit-pack-badge {
  background: var(--accent-soft, rgba(37, 99, 235, 0.12));
  border-radius: 999px;
  color: var(--accent, #2563eb);
  font-size: 0.7rem;
  font-style: normal;
  font-weight: 600;
  padding: 0.1rem 0.45rem;
  white-space: nowrap;
}

/* The upgrade nudge spans the table rather than sitting in a column: it is a
   sentence about the row above it, not a fifth figure. */
.credit-pack-offer-row td {
  padding-top: 0;
}

.credit-pack-buy {
  display: grid;
  gap: 6px;
  justify-items: start;
  margin-top: 12px;
}

.credit-pack-buy-button {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.credit-pack-buy-note {
  color: var(--muted, #6b7280);
  font-size: 0.78rem;
  margin: 0;
}

.account-addon-list ul {
  display: grid;
  gap: 4px;
  font-size: 0.8rem;
  list-style: none;
  margin: 2px 0 0;
  padding: 0;
}

.account-addon-list li {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  justify-content: space-between;
}

/* The upgrade comparison is a third child of a row built for two, so it takes a
   full-width line of its own rather than squeezing between the price and the
   Buy button. */
.account-addon-upgrade {
  color: #4a5b57;
  flex-basis: 100%;
  font-size: 0.75rem;
  line-height: 1.45;
  margin: 0;
}

/* Same money or less for more credits - worth a colour, since it is a fact about
   the customer's own money rather than a pitch. */
.account-addon-upgrade.better {
  color: #14635a;
}

.account-addon-upgrade .link-button {
  font-size: inherit;
  margin-left: 4px;
}

.account-plan-footnote {
  margin: 0;
}

.subscription-pending-banner {
  align-items: center;
  background: #eef4f2;
  border: 1px solid #14635a;
  border-radius: 10px;
  display: flex;
  flex-wrap: wrap;
  font-size: 0.8rem;
  gap: 8px 12px;
  justify-content: space-between;
  margin-bottom: 12px;
  padding: 10px 14px;
}

.account-terminate-button {
  color: #a33a2a;
}

.confirm-overlay {
  align-items: center;
  background: rgba(24, 24, 27, 0.4);
  display: flex;
  inset: 0;
  justify-content: center;
  padding: 24px;
  position: fixed;
  /* Above every other overlay (section editor 120, date-change prompt 130, fork
     detail 140) - confirmDialog() is always a final yes/no on top of whatever's
     already open (e.g. the ask-on-save impact/mitigation prompts fired from inside
     the full-screen section editor), so it must never render invisibly behind it. */
  z-index: 150;
}

.confirm-panel {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-modal);
  display: grid;
  gap: 18px;
  max-width: 420px;
  padding: 22px;
  width: min(420px, 100%);
}

.confirm-message {
  color: var(--ink);
  font-size: 0.875rem;
  line-height: 1.5;
  margin: 0;
}

.confirm-actions {
  display: flex;
  gap: 10px;
  justify-content: flex-end;
}

.confirm-danger-button {
  background: #a33a2a;
  border-color: #a33a2a;
  color: #ffffff;
}

.confirm-danger-button:hover {
  background: #832c20;
  border-color: #832c20;
}

.date-change-prompt-overlay {
  z-index: 130;
}

.date-change-prompt-panel {
  max-width: 460px;
  width: min(460px, 100%);
}

.date-change-prompt-heading {
  font-size: 1rem;
  margin: 0;
}

/* --- "This needs a little more": the required fields of a new record -------
   Wider than the plain confirm panel because it holds real inputs rather than
   a sentence, but deliberately not as wide as the mitigation dialog - it is
   never more than two fields, and a two-field form spread over 900px reads as
   a form with something missing from it. */
.required-fields-panel {
  /* Wider than it was. Each field now carries a sentence explaining what is
     lost without it, and at 560px those wrapped to four cramped lines apiece,
     which made the dialog look like fine print to skip rather than the reason
     it opened. */
  max-width: min(680px, calc(100vw - 32px));
  width: min(680px, calc(100vw - 32px));
}

.required-field-label {
  font-size: 0.9rem;
  font-weight: 600;
}

/* The reason the field is required, under the question and above the control.
   Recessive on purpose - it explains, it does not shout. Somebody who already
   knows why can read past it to the input without it being in the way. */
/* Two classes, to out-specify `.required-field > span:first-child`'s weight
   without !important. The explanation is recessive by design - rendering it at
   the label's weight made the dialog look like two questions stacked instead of
   a question and its reason. */
/* The hint is the smallest thing in the field, under the control. The shared
   .field-hint is bold, which put it at the same weight as the question and
   flattened the three levels this dialog depends on: what is being asked, why,
   and how to answer. */
.required-field .field-hint {
  color: var(--muted, #6b7280);
  font-size: 0.76rem;
  font-weight: 400;
}

.required-field .required-field-why {
  border-left: 2px solid var(--line, #e5e7eb);
  color: var(--muted, #6b7280);
  font-size: 0.8rem;
  font-weight: 400;
  line-height: 1.5;
  padding-left: 10px;
}

.required-fields-list {
  display: grid;
  gap: 14px;
}

.required-field {
  display: grid;
  gap: 6px;
}

.required-field > span:first-child {
  font-size: 0.85rem;
  font-weight: 600;
}

.required-field input,
.required-field select {
  /* The owner picker brings its own input, so this has to reach it too - the
     three controls in one dialog looking like three different products is
     exactly what "render better input text look and feel" was about.
     background-COLOR: the shorthand would drop the select's chevron. */
  background-color: var(--surface, #fff);
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--radius-control, 8px);
  color: inherit;
  font: inherit;
  padding: 9px 32px 9px 11px;
  width: 100%;
}

.required-field input:focus-visible,
.required-field select:focus-visible {
  border-color: var(--accent, #2f6df6);
  outline: 2px solid color-mix(in srgb, var(--accent, #2f6df6) 35%, transparent);
  outline-offset: 1px;
}

/* Empty until a save is attempted with something still blank, so it holds no
   space and never reads as a warning that was already there. */
.required-fields-status:empty {
  display: none;
}

.required-fields-status {
  color: var(--danger-strong, #b42318);
  font-size: 0.8rem;
  margin: 0;
}

/* --- "Add a mitigation", the widest of the prompt dialogs ------------------
   It carries a paragraph of precedent, a free-text action, three full AI
   suggestions and four fields. In the 460px panel the suggestions wrapped to
   three lines each and the owner and status fields fell off the bottom of a
   laptop screen, so the dialog read as a narrow column of scroll. */
.mitigation-prompt-panel {
  gap: 14px;
  /* Never taller than the screen. Without this the panel simply grew, and on a
     laptop that put Save below the fold with nothing to scroll. */
  max-height: calc(100vh - 48px);
  max-width: min(920px, calc(100vw - 32px));
  overflow-y: auto;
  width: min(920px, calc(100vw - 32px));
}

/* Write on the left, pick a draft on the right. The columns are uneven on
   purpose: the action is what is being composed, the suggestions are a
   reference beside it. */
.mitigation-prompt-body {
  /* Stretch, not start: the textarea grows to the height of the suggestions
     beside it, so the space that was empty under it becomes room to write. */
  align-items: stretch;
  display: grid;
  gap: 14px;
  grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
}

.mitigation-prompt-body > .date-change-prompt-field {
  grid-template-rows: auto 1fr;
}

/* No suggestions to sit beside, so the action takes the whole width rather
   than leaving half the dialog blank. */
.mitigation-prompt-body.is-single {
  grid-template-columns: minmax(0, 1fr);
}

.mitigation-prompt-body .date-change-prompt-field {
  min-width: 0;
}

.mitigation-prompt-body textarea {
  min-height: 150px;
}

/* Three one-line answers on one row. They were three stacked full-width rows,
   which is most of why the dialog was so tall. */
.mitigation-prompt-meta {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(3, minmax(0, 1fr));
}

.mitigation-prompt-meta > label {
  min-width: 0;
}

/* The owner picker is written for the risk editor and brings its own label
   layout; inside this row it has to behave like the two selects beside it. */
.mitigation-prompt-meta .owner-picker-label {
  display: grid;
  gap: 5px;
}

.mitigation-prompt-meta .owner-picker-label > span {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 780;
}

/* Below this the two columns are narrower than the sentences in them, so both
   grids collapse rather than hyphenating a suggestion down a 180px column. */
@media (max-width: 760px) {
  .mitigation-prompt-body,
  .mitigation-prompt-meta {
    grid-template-columns: minmax(0, 1fr);
  }
}

.date-change-prompt-field {
  display: grid;
  gap: 5px;
}

/* display:grid above beats the browser's default [hidden]{display:none} (author
   styles win over UA styles) - needed here since promptMitigationMilestoneDateImpact
   toggles this field's hidden attribute to reveal the date input only after "Yes,
   it changes the date" is picked. */
.date-change-prompt-field[hidden] {
  display: none;
}

/* Same [hidden]-vs-author-styles issue as .date-change-prompt-field above, but for
   the Yes/No/Save-new-date buttons: the global `button { display: inline-flex }`
   rule (see the base button styles) beats the browser's default [hidden] on every
   <button> in the app, not just these - scoped here rather than fixed globally, in
   case something elsewhere already depends on toggling a button's own display some
   other way. */
.date-change-prompt-choice[hidden] {
  display: none;
}

.date-change-prompt-field span {
  color: var(--muted);
  font-size: 0.72rem;
  font-weight: 780;
}

.date-change-prompt-field textarea,
.date-change-prompt-field input[type="date"],
.date-change-prompt-field input[type="text"],
.date-change-prompt-field select {
  background-color: transparent;
  border: 1px solid var(--line);
  border-radius: var(--radius-control);
  color: inherit;
  font: inherit;
  max-width: 100%;
  padding: 8px 32px 8px 10px;
  width: 100%;
}

.date-change-prompt-field textarea {
  resize: vertical;
}

.date-change-prompt-field input[type="date"],
.date-change-prompt-field input[type="text"],
.date-change-prompt-field select {
  min-height: 42px;
}

.date-change-prompt-error {
  color: #a33a2a;
  font-size: 0.78rem;
  margin: 0;
}

.account-data-mode {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  display: grid;
  gap: 6px;
  padding: 12px 14px;
}

.account-data-mode label {
  align-items: center;
  display: flex;
  font-size: 0.82rem;
  gap: 6px;
}

.account-data-mode .admin-meta {
  margin: 2px 0 0;
}

/* "Your AI tools" - same card as Data & AI above. */
.account-ai-tools {
  background: var(--panel);
  border: 1px solid var(--line);
  border-radius: 10px;
  display: grid;
  gap: 8px;
  padding: 12px 14px;
}

.account-ai-tools label {
  align-items: center;
  display: flex;
  font-size: 0.82rem;
  gap: 6px;
}

.ai-tools-address {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin: 0;
}

.ai-tools-address code {
  overflow-wrap: anywhere;
}

.ai-tool-connections {
  display: grid;
  gap: 6px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.ai-tool-connections li {
  align-items: center;
  display: flex;
  gap: 10px;
  justify-content: space-between;
}

.ai-tool-connections li > span {
  display: grid;
}

.upgrade-limit-body {
  display: grid;
  gap: 14px;
}

.upgrade-limit-status {
  color: var(--muted);
  font-size: 0.78rem;
  margin: 0;
  min-height: 1em;
}

.upgrade-limit-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: flex-end;
}

.button-spinner {
  animation: button-spinner-spin 700ms linear infinite;
  border: 2px solid rgba(255, 255, 255, 0.35);
  border-radius: var(--radius-pill);
  border-top-color: #ffffff;
  display: inline-block;
  height: 14px;
  width: 14px;
}

.secondary-button .button-spinner {
  border-color: rgba(15, 107, 66, 0.25);
  border-top-color: var(--theruka-green, #0f6b42);
}

button.is-busy {
  cursor: wait;
}

@keyframes button-spinner-spin {
  to {
    transform: rotate(360deg);
  }
}

.mobile-nav-toggle {
  align-items: center;
  background: none;
  border: 1px solid var(--line);
  border-radius: 8px;
  cursor: pointer;
  display: none;
  flex: 0 0 auto;
  flex-direction: column;
  gap: 5px;
  height: 38px;
  justify-content: center;
  padding: 9px;
  transition: background 0.15s ease, border-color 0.15s ease;
  width: 38px;
}

.mobile-nav-toggle:hover {
  background: #f4f7fa;
  border-color: var(--line-strong);
}

.mobile-nav-toggle span {
  background: var(--ink);
  border-radius: 2px;
  display: block;
  height: 2px;
  transition: transform 0.22s ease, opacity 0.18s ease;
  width: 100%;
}

.mobile-nav-toggle[aria-expanded="true"] span:nth-child(1) {
  transform: translateY(7px) rotate(45deg);
}

.mobile-nav-toggle[aria-expanded="true"] span:nth-child(2) {
  opacity: 0;
  transform: scaleX(0);
}

.mobile-nav-toggle[aria-expanded="true"] span:nth-child(3) {
  transform: translateY(-7px) rotate(-45deg);
}

.mobile-nav-backdrop {
  background: rgba(15, 23, 32, 0.45);
  display: none;
  inset: 0;
  position: fixed;
  z-index: 35;
}

.mobile-nav-backdrop.open {
  display: block;
}

.dashboard-brand {
  align-items: center;
  display: flex;
  gap: 10px;
  min-width: 0;
}

/* The source PNG (theruka-logo.png/-dark.png) is a tall square: the mark
   (the crossed-line "A"/tent glyph) sits in the top ~44% and the "THERUKA (R)"
   wordmark is baked in below it - the brand now shows the mark only, no
   separate text label next to it (see the removed .dashboard-brand-text),
   so the wordmark portion of the image needs to stay cropped out. A
   wide-and-short box + object-fit:cover scales the (square) image to the
   box's width and crops vertically; object-position's 38% vertical anchor
   is tuned to land the crop right in the gap between the mark (ends ~65%
   down the source) and the wordmark (starts ~69%) - see the alpha-channel
   row scan used to derive these numbers before landing on this box ratio. */
.dashboard-brand-logo {
  background: none;
  /* The blue accent underline that used to sit here read as a stray rule
     floating under the mark rather than part of the brand - especially on a
     phone, where the top bar wraps and it ends up detached from everything
     else. The mark stands on its own. */
  border: 0;
  border-radius: 0;
  box-shadow: none;
  display: inline-flex;
  flex: 0 0 auto;
  overflow: visible;
  position: relative;
  height: 56px;
  width: 120px;
}

.dashboard-brand-logo img {
  object-fit: cover;
  object-position: center 36.5%;
  display: block;
  height: 100%;
  /* dashboard-premium.css (injected ahead of this file by dashboard-navigation.js)
     sets max-width/max-height: 26px here. A max-* constraint clamps the used size
     no matter which rule wins the cascade, so height/width: 100% below was being
     capped at 26x26 - the image never filled the box, object-fit: cover had
     nothing to crop, and the wordmark baked into the source PNG stayed visible
     inside a mostly-empty box. Reset the caps so the crop above actually runs. */
  max-height: none;
  max-width: none;
  width: 100%;
}
/* The brand is two <img> elements - the navy mark for a light ground and the
   green one for a dark ground - and exactly one of them may ever be on screen.
   This rule is the "off" half of that pair, and it has to live in a stylesheet
   the page LINKS rather than one it injects.

   It used to live only in dashboard-premium.css, which dashboard-navigation.js
   appends at the end of <body>. So between first paint and that script running,
   nothing hid the dark mark and BOTH rendered - 60px each, side by side, the
   navy lockup next to the green one. On a cold load that is a visible flash;
   with the injected file slow, blocked or the script erroring it is permanent,
   and it is what a first-time visitor to /demo.html was seeing. Measured by
   aborting the request for dashboard-premium.css: two visible images at
   x=14 and x=74 instead of one.

   dark-mode.css re-shows it (and hides the light one) under
   :root[data-theme="dark"], which outranks this by specificity, so the swap
   still works. dashboard-premium.css keeps its own copy - a duplicate is
   harmless, and deleting it there would make that file wrong on its own. */
.dashboard-brand-logo .theme-logo-dark {
  display: none;
}

@media (max-width: 900px) {
  .landing-nav {
    grid-template-columns: 1fr auto;
  }

  .landing-nav nav {
    display: none;
  }

  .landing-hero {
    height: 100vh;
    height: 100dvh;
    min-height: 0;
  }

  .landing-hero h1 {
    font-size: 3rem;
  }

  .landing-section h2 {
    font-size: 2.1rem;
  }

  .onboarding-brand h1 {
    font-size: 3rem;
  }

  .landing-product,
  .landing-demo,
  .landing-company,
  .onboarding-shell {
    grid-template-columns: 1fr;
  }

  .landing-company-logo {
    justify-self: start;
    max-width: 140px;
  }

  .landing-feature-grid,
  .demo-board-grid {
    grid-template-columns: 1fr;
  }

  .onboarding-panel {
    align-self: stretch;
    border-left: 0;
    border-radius: 0;
    border-right: 0;
    box-shadow: none;
    margin: 0;
  }

  .onboarding-shell {
    align-items: end;
    padding: 92px 0 0;
    min-height: 100vh;
    overflow: visible;
  }

  .onboarding-shell::before {
    background: linear-gradient(180deg, rgba(251, 247, 240, 0) 0%, rgba(251, 247, 240, 0.74) 58%, rgba(251, 247, 240, 0.98) 100%);
  }

  .onboarding-panel {
    grid-column: 1;
    justify-self: stretch;
    margin-top: 52vh;
    width: 100%;
  }
}

@media (max-width: 720px) {
  .onboarding-background-logo-link {
    height: 14%;
    left: 4%;
    top: 14%;
    width: 42%;
  }

  .onboarding-background-link {
    display: none;
  }
}

@media (max-width: 640px) {
  .pricing-plan-grid {
    grid-template-columns: 1fr;
  }
}

@page {
  margin: 16mm 14mm;
}

@media print {
  body {
    background: #ffffff;
  }

  .executive-report-meta {
    text-align: left;
  }

  .workspace-copilot,
  .project-portfolio,
  .dashboard-actions,
  .upload-window,
  .section-editor-window,
  .score-explanation,
  .pending-plan-review,
  .section-edit-button,
  .attention-item-actions {
    display: none !important;
  }

  .dashboard-shell,
  .executive-dashboard-shell {
    display: block;
  }

  .executive-workspace {
    display: block;
    min-height: auto;
  }

  .app-top-bar {
    border-bottom: 1px solid var(--line);
    padding: 0 0 12px;
  }

  .project-dashboard {
    overflow: visible;
    padding: 12px 0 0;
  }

  .site-footer {
    background: #ffffff;
    border-top: 1px solid var(--line);
    color: #52525b;
    margin-top: 18px;
  }

  .executive-project-hero,
  .plan-section,
  .executive-metrics div,
  .executive-metrics button {
    break-inside: avoid;
    box-shadow: none;
  }

  .executive-report-grid,
  .executive-plan-grid {
    grid-template-columns: 1fr 1fr;
  }
}

/* Simplified navigation shell: browsing portfolios/projects moved from the
   left rail into a top-bar switcher (portfolio name -> plan badge ->
   project name, Neon-console-style); the left rail itself is now a fixed
   Gantt/Risk Map/AI Features/People/Share/Billing nav instead of the old
   scrollable Portfolios/Projects lists. See primaryRailNav/top-switcher-group
   in dashboard.html and their wiring in dashboard.js. */

.top-switcher-group {
  align-items: center;
  display: flex;
  gap: 6px;
  min-width: 0;
}

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

.top-switcher-toggle {
  align-items: center;
  background: none;
  border: 0;
  border-radius: 6px;
  color: var(--ink);
  display: flex;
  font-weight: 800;
  gap: 6px;
  justify-content: flex-start;
  max-width: 220px;
  min-height: 34px;
  padding: 4px 4px;
}

.top-switcher-toggle:hover,
.top-switcher-toggle:focus-visible,
.top-switcher-toggle[aria-expanded="true"] {
  color: var(--accent-strong);
}

/* Neon-console-style double up/down arrow (a fixed "select" affordance,
   not a single chevron that rotates open/closed like the rail's other
   disclosure toggles). Two stacked CSS triangles instead of a glyph, to
   match this file's existing border-trick chevron rather than an icon font. */
.top-switcher-chevron {
  display: inline-flex;
  flex: 0 0 auto;
  flex-direction: column;
  gap: 3px;
}

.top-switcher-chevron::before,
.top-switcher-chevron::after {
  border-left: 3px solid transparent;
  border-right: 3px solid transparent;
  content: "";
  display: block;
  height: 0;
  width: 0;
}

.top-switcher-chevron::before {
  border-bottom: 4px solid var(--muted);
}

.top-switcher-chevron::after {
  border-top: 4px solid var(--muted);
}

.top-switcher-toggle:hover .top-switcher-chevron::before,
.top-switcher-toggle:focus-visible .top-switcher-chevron::before,
.top-switcher-toggle[aria-expanded="true"] .top-switcher-chevron::before {
  border-bottom-color: var(--accent-strong);
}

.top-switcher-toggle:hover .top-switcher-chevron::after,
.top-switcher-toggle:focus-visible .top-switcher-chevron::after,
.top-switcher-toggle[aria-expanded="true"] .top-switcher-chevron::after {
  border-top-color: var(--accent-strong);
}

.top-switcher-name {
  font-size: 0.88rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.top-switcher-hidden-count {
  display: none;
}

.top-switcher-intake {
  position: relative;
}

.top-switcher-intake-toggle {
  align-items: center;
  background: none;
  border: 1px solid var(--line);
  border-radius: 8px;
  color: var(--ink);
  display: flex;
  font-size: 1.05rem;
  font-weight: 800;
  height: 34px;
  justify-content: center;
  width: 34px;
}

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

.workspace-intake-menu {
  background: var(--panel-strong);
  border: 1px solid var(--line-strong, #d4d4d8);
  border-radius: 8px;
  box-shadow: 0 16px 34px rgba(24, 24, 27, 0.22);
  display: grid;
  gap: 2px;
  min-width: 170px;
  padding: 6px;
  position: absolute;
  right: 0;
  top: calc(100% + 6px);
  z-index: 20;
}

.workspace-intake-menu[hidden] {
  display: none;
}

.workspace-intake-menu button {
  background: none;
  border: 0;
  border-radius: 6px;
  color: var(--ink);
  font-size: 0.82rem;
  font-weight: 750;
  padding: 8px 10px;
  text-align: left;
}

.workspace-intake-menu button:hover,
.workspace-intake-menu button:focus-visible {
  background: color-mix(in srgb, var(--ink) 6%, transparent);
}

.primary-rail-nav {
  align-content: start;
  display: grid;
  gap: 4px;
  min-height: 0;
  overflow-y: auto;
  padding: 24px 10px 14px;
}

/* The two top-level categories ("Plan"/"Account") - a pill-shaped toggle,
   filled when its items are the ones currently showing in the flyout (see
   .primary-rail-nav-flyout below), plain otherwise. Mirrors the reference
   design's "Threads" expanded-category pill. */
.primary-rail-nav-group-toggle {
  align-items: center;
  background: none;
  border: 0;
  border-left: 3px solid transparent;
  border-radius: 0;
  box-shadow: none;
  color: var(--theruka-muted, #71817a);
  cursor: pointer;
  display: flex;
  font: inherit;
  font-size: 0.86rem;
  font-weight: 850;
  gap: 11px;
  padding: 11px 14px 11px 11px;
  text-align: left;
  width: 100%;
}

.primary-rail-nav-group-toggle .project-menu-icon {
  color: var(--theruka-muted, #71817a);
}

.primary-rail-nav-group-toggle .admin-pill {
  margin-left: auto;
}

.primary-rail-nav-group-toggle:hover,
.primary-rail-nav-group-toggle:focus-visible {
  color: var(--theruka-text, #121817);
}

/* A left accent line + colored text marks which category's items are
   showing in the flyout, instead of a filled rounded pill - a plain menu
   row, not a "card". */
.primary-rail-nav-group-toggle[aria-expanded="true"] {
  border-left-color: var(--accent-brand);
  color: var(--accent-brand);
}

/* The active category's items ("Gantt"/"Risk Map"/... or "Webhooks"/"Team"/...)
   - a second rail "column" that floats in front of the main content (see
   positionPrimaryRailFlyout, dashboard.js) instead of being a grid column
   that would shrink the workspace. Shows exactly one group at a time - see
   setPrimaryRailGroupOpen - and can be collapsed entirely (.collapsed, see
   setPrimaryRailFlyoutCollapsed) so it doesn't permanently block the content
   behind it: re-clicking its own open category toggle, clicking a nav item
   inside it, or clicking anywhere outside it all collapse it. */
/* Flush against the rail's own background/border (no shadow, no rounded
   corners, no left border) so it reads as one continuous surface with the
   category rail to its left instead of a floating card - only a right-edge
   divider line separates it from the content behind it. */
.primary-rail-nav-flyout {
  background: var(--bg);
  border: 0;
  border-right: 1px solid var(--theruka-line, #e4e9e6);
  border-radius: 0;
  box-shadow: none;
  display: grid;
  gap: 2px;
  overflow-y: auto;
  padding: 14px 10px;
  position: fixed;
  width: 208px;
  z-index: 45;
}

/* Desktop-only - collapsed is never applied on the sub-900px in-flow layout
   (see the max-width:900px override below), but scope it explicitly anyway
   so a stale class surviving a resize can't hide the mobile drawer's nav. */
@media (min-width: 901px) {
  .primary-rail-nav-flyout.collapsed {
    display: none;
  }
}

.primary-rail-nav-flyout-group[hidden] {
  display: none;
}

.primary-rail-nav-item {
  align-items: center;
  background: none;
  border: 0;
  border-radius: 14px;
  box-shadow: none;
  color: var(--theruka-muted, #71817a);
  display: flex;
  font-size: 0.82rem;
  font-weight: 800;
  gap: 11px;
  padding: 10px 12px;
  text-align: left;
  width: 100%;
}

.primary-rail-nav-item .project-menu-icon {
  color: var(--theruka-muted, #71817a);
}

/* rgba(0,0,0,...) here reads as a soft dark tint on the light rail
   background; dark-mode.css overrides these two rules back to the
   original rgba(255,255,255,...) light-on-dark treatment. */
.primary-rail-nav-item:hover,
.primary-rail-nav-item:focus-visible {
  background: rgba(18, 24, 23, 0.06);
  color: var(--theruka-text, #121817);
}

.primary-rail-nav-item.active {
  background: rgba(18, 24, 23, 0.08);
  color: var(--theruka-text, #121817);
}

@media (max-width: 900px) {
  .top-switcher-group {
    flex: 1;
    min-width: 0;
  }

  .top-switcher-toggle {
    max-width: 140px;
  }

  /* Below the mobile breakpoint the rail is already a full-screen off-canvas
     drawer (see .workspace-copilot in dashboard-responsive.css) - "in front
     of the content" doesn't apply there, so the flyout just renders in-flow
     right after the category toggles instead of as a second floating column. */
  .primary-rail-nav-flyout {
    border-radius: 0;
    border-right: 0;
    box-shadow: none;
    height: auto !important;
    margin-top: 8px;
    position: static;
    width: auto;
  }
}

/* Below ~560px there isn't room for [hamburger][logo][portfolio][/][project][+]
   on one line even with the 140px toggle cap above - the switcher group drops
   to its own row under the hamburger/logo instead of overflowing the header
   (matches .dashboard-actions's existing grid-column: 1 / -1 full-width-row
   pattern for this same header, just via flex-basis since this row is flex
   not grid). */
@media (max-width: 560px) {
  .app-top-bar {
    row-gap: 8px;
  }

  .top-switcher-group {
    flex-basis: 100%;
    gap: 4px;
  }

  .top-switcher-toggle {
    flex: 1 1 0;
    max-width: none;
    min-width: 0;
    padding: 6px 6px;
  }

  .top-switcher-sep {
    display: none;
  }

  .top-switcher-intake-toggle {
    flex: 0 0 auto;
  }
}

/* Scope badge (portfolio vs project hero) -----------------------------------
   The two hero views are deliberately the same layout - same gauges, same tile
   row, same accordions - so the only thing that ever said which altitude you
   were looking at was a small grey uppercase eyebrow reading "Active portfolio"
   or "Active project", which is easy to miss and easy to confuse. This gives
   each altitude its own coloured, icon-led pill instead, so the distinction
   registers before you start reading. Colours use the same hue pair as the
   rest of the app (blue accent for project, amber for portfolio) rather than
   introducing new ones. */
.scope-badge {
  align-items: center;
  border-radius: 999px;
  display: inline-flex;
  font-size: 0.68rem;
  font-weight: 900;
  gap: 6px;
  letter-spacing: 0.06em;
  line-height: 1;
  margin: 0 0 8px;
  padding: 5px 10px 5px 8px;
  text-transform: uppercase;
}

.scope-badge svg {
  height: 13px;
  width: 13px;
}

/* Doubled class (0,2,0) so these beat `.executive-project-hero p` (0,1,1),
   which sets the hero's muted body colour and would otherwise grey the badge
   out in light mode - the dark-mode overrides are already specific enough via
   their :root[data-theme] prefix, which is why only light mode showed it. */
.scope-badge.scope-badge-portfolio {
  background: rgba(180, 122, 8, 0.14);
  color: #8a5d05;
}

.scope-badge.scope-badge-project {
  background: rgba(37, 99, 235, 0.13);
  color: #1d4ed8;
}

/* Reports page: which of the two kinds this section is. Same pill as
   .scope-badge, its own two colours - see the rails in design-refresh.css for
   why they are not the project/portfolio blue and amber. */
.scope-badge.report-badge-live {
  background: rgba(15, 118, 110, 0.13);
  color: #0f766e;
}

.scope-badge.report-badge-frozen {
  background: rgba(109, 40, 217, 0.13);
  color: #6d28d9;
}

/* AI Features page: heading above each scope's group of panels, and the
   signpost list for the AI features triggered from elsewhere. */
/* Two scopes on one page. The band colours are the ones project and portfolio
   already wear on their own home pages, so this is a distinction somebody has
   already learned rather than a new key to memorise. */
.ai-scope {
  border-left: 3px solid;
  border-radius: 0 12px 12px 0;
  margin-bottom: 26px;
  padding: 14px 0 4px 16px;
}

.ai-scope-project {
  background: linear-gradient(90deg, rgba(20, 99, 90, 0.06), transparent 340px);
  border-left-color: var(--accent-brand);
}

.ai-scope-portfolio {
  background: linear-gradient(90deg, rgba(180, 122, 8, 0.07), transparent 340px);
  border-left-color: #b47a08;
}

.ai-scope-title {
  font-size: 1.05rem;
  margin: 6px 0 12px;
}

/* The panels inside a band keep their own card chrome, but the band already
   carries the left rule - a second one immediately inside it reads as a nested
   box rather than a group. */
.ai-scope > .plan-section:first-of-type {
  margin-top: 0;
}

.ai-elsewhere-list {
  display: grid;
  gap: 10px;
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
}

.ai-elsewhere-list li {
  align-items: center;
  border-top: 1px solid var(--line, #d4d4d8);
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: space-between;
  padding-top: 10px;
}

.ai-elsewhere-list li > div {
  display: grid;
  gap: 3px;
  min-width: 0;
}

.ai-elsewhere-list small {
  color: var(--muted, #71717a);
}

/* Notification dropdown pinned to the viewport below the 900px breakpoint -
   see positionNotificationDropdown in dashboard.js for why (left/top/width are
   set inline there). Anchoring it to the bell with right:0 only works while the
   bell sits at the right edge of a wide header. */
.notification-dropdown.pinned {
  max-height: min(60svh, 360px);
  position: fixed;
  right: auto;
  top: auto;
}

/* Risk list under the Risk Map (renderRiskMapRisksHtml). Mirrors the milestone
   table's shape so the two read as one family; the sub-900px stacking rule in
   dashboard-responsive.css covers this class too. */
.risk-map-risks-scroll {
  overflow-x: auto;
}

.risk-map-risk-table {
  border-collapse: collapse;
  font-size: 0.82rem;
  min-width: 100%;
  width: 100%;
}

.risk-map-risk-table th {
  border-bottom: 1px solid var(--line, #d4d4d8);
  color: var(--muted);
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  padding: 0 10px 8px 0;
  text-align: left;
  text-transform: uppercase;
  white-space: nowrap;
}

.risk-map-risk-table td {
  border-bottom: 1px solid var(--line, #e4e4e7);
  padding: 10px 10px 10px 0;
  vertical-align: top;
}

.risk-map-risk-table tbody tr:last-child td {
  border-bottom: 0;
}

.risk-map-risk-table td strong {
  font-weight: 700;
}

/* Rows open the risk for editing, so they need to look like they do. */
.risk-map-risk-row {
  cursor: pointer;
}

.risk-map-risk-row:hover,
.risk-map-risk-row:focus-within {
  background: rgba(37, 99, 235, 0.08);
}

.risk-map-risk-open {
  color: inherit;
  font: inherit;
  padding: 0;
  text-align: left;
}

.risk-map-risk-row:hover .risk-map-risk-open,
.risk-map-risk-open:focus-visible {
  color: var(--accent, #2563eb);
  text-decoration: underline;
}

/* Workspace-load failure overlay (renderWorkspaceLoadFailed) - shares the
   blocked-account overlay's shape; only the reassurance line is extra. */
.app-error-panel .app-error-reassurance {
  color: var(--muted);
  font-size: 0.82rem;
}

/* Executive Reports rail page (renderReportsSection in dashboard.js). The
   report history is a list of frozen snapshots, each with its own share links,
   so a row nests the existing .sharing-link-row markup rather than inventing a
   second link-row style. */
.executive-report-history-header {
  align-items: flex-start;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: space-between;
  margin-bottom: 16px;
}

.executive-report-history-header h3 {
  margin: 0 0 4px;
}

.executive-report-history-header p {
  color: var(--muted, #71717a);
  font-size: 0.88rem;
  margin: 0;
  max-width: 60ch;
}

.executive-report-history-row {
  border: 1px solid var(--line, #d4d4d8);
  margin-bottom: 14px;
}

/* "Open" is an <a> so it can be opened in a new tab / middle-clicked, but the
   element rules that give a button its box (min-height, padding, weight,
   cursor) all key off `button` and never reach an anchor - so it rendered as a
   short pill sitting off the baseline of the real buttons beside it. This
   restates that box for the anchor rather than turning it into a <button>,
   which would cost the new-tab behaviour. */
.executive-report-history-actions a.secondary-button {
  align-items: center;
  cursor: pointer;
  display: inline-flex;
  font-weight: 700;
  justify-content: center;
  min-height: 42px;
  padding: 0 16px;
  text-decoration: none;
}

.dashboard-app-shell .executive-report-history-actions a.secondary-button {
  font-weight: 500;
  padding: 0 18px;
}

/* Matches `.executive-dashboard-shell button` - without it the anchor stands
   4px taller than the buttons it sits beside and the row's baseline breaks. */
.executive-dashboard-shell .executive-report-history-actions a.secondary-button {
  font-size: 0.88rem;
  min-height: 38px;
}

.executive-report-history-heading {
  align-items: flex-start;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: space-between;
}

.executive-report-history-heading > div:first-child {
  display: grid;
  gap: 3px;
  min-width: 0;
}

.executive-report-history-heading small {
  color: var(--muted, #71717a);
}

.executive-report-history-actions {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.executive-report-history-totals {
  color: var(--muted, #71717a);
  font-size: 0.85rem;
  margin: 8px 0 12px;
}

.executive-report-form {
  display: grid;
  gap: 14px;
  margin-bottom: 18px;
}

.executive-report-fieldset {
  border: 1px solid var(--line, #d4d4d8);
  border-radius: var(--radius-tile, 14px);
  display: grid;
  gap: 8px;
  margin: 0;
  padding: 14px 16px;
}

.executive-report-fieldset legend {
  color: var(--muted, #71717a);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  padding: 0 6px;
  text-transform: uppercase;
}

.executive-report-fieldset label {
  align-items: center;
  cursor: pointer;
  display: flex;
  font-size: 0.9rem;
  gap: 10px;
}

.executive-report-fieldset input[type="checkbox"],
.executive-report-fieldset input[type="radio"] {
  flex: 0 0 auto;
  height: 16px;
  width: 16px;
}

/* Indented under the radio it belongs to, so "Selected portfolios" and its
   checkboxes read as one choice rather than two sibling controls. */
.executive-report-picker {
  border-left: 2px solid var(--line, #d4d4d8);
  display: grid;
  gap: 6px;
  margin-left: 8px;
  padding: 4px 0 4px 16px;
}

/* A class-level `display` beats the UA stylesheet's [hidden] rule, so the
   pickers would stay visible for every scope at once without this. */
.executive-report-picker[hidden] {
  display: none;
}

.executive-report-form .field-label {
  color: var(--muted, #71717a);
  font-size: 0.8rem;
}

/* Verdict on a risk row: whether the mitigation chosen for it turned out to be
   a good decision (see src/mitigationVerdicts.js). Reuses the score-band colours
   so it reads with the rest of the risk list rather than as a new vocabulary. */
.risk-verdict-badge {
  align-self: start;
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  font-size: 0.72rem;
  padding: 2px 10px;
  white-space: nowrap;
  width: fit-content;
}

/* The weekly check-in list. Answers sit on one row so working down the list is
   a sequence of taps, not a sequence of decisions about where to click. */
/* Title and the subject it is showing on one line, wrapping rather than
   crushing the name on a narrow screen. */
.check-in-header {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: space-between;
}

.check-in-header h3 {
  margin: 0;
}

/* The subject reads inside the sentence, so it is emphasis rather than a
   control: it names what the section is showing, and the way to change it is
   the project picker at the top of the page. Nothing with a border or a caret,
   which would invite a click that does nothing. */
.check-in-subject {
  color: var(--ink, #111827);
  font-weight: 600;
}

/* What this scope is not showing. Quiet, below the list, and only present when
   there is something out there. */
.check-in-elsewhere {
  color: var(--muted, #6b7280);
  font-size: 13px;
  margin: 14px 0 0;
}

.check-in-list {
  display: grid;
  gap: 12px;
  list-style: none;
  margin: 14px 0 0;
  padding: 0;
}

.check-in-item {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: var(--radius-tile, 10px);
  display: grid;
  gap: 8px;
  padding: 14px 16px;
}

.check-in-item-head {
  display: grid;
  gap: 2px;
}

.check-in-item-flags {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.check-in-item-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.check-in-item p {
  margin: 0;
}

/* The pre-share mirror. Notice-coloured rather than danger: none of this blocks
   the report, and styling it as an error would train people to dismiss it. */
/* The pre-flight dialog. Wider and scrollable, unlike the plain confirm panel it
   shares a class with - it carries a findings list plus generated questions, which a
   fixed-height yes/no box cannot hold. */
/* width, not just max-width. .confirm-panel sets width: min(420px, 100%), and a
   max-width alone never widened anything past it - so this dialog rendered at
   yes/no-box width no matter how much it had to say.
   Three rows: the title and the actions stay put, only the content scrolls. A
   panel that scrolled as one block took the heading and the Send button off
   screen exactly when there was most to read. */
.report-preflight-panel {
  display: grid;
  gap: 14px;
  grid-template-rows: auto minmax(0, 1fr) auto;
  max-height: min(86vh, 900px);
  max-width: 820px;
  overflow: hidden;
  text-align: left;
  width: min(820px, 100%);
}

.report-preflight-body {
  display: grid;
  gap: 12px;
  min-height: 0;
  overflow-y: auto;
  /* Room for the scrollbar so text never sits under it. */
  padding-right: 4px;
}

/* Findings and questions are the reason this dialog is big; give them the
   line-height of something meant to be read rather than glanced at. */
.report-preflight-findings li,
.report-questions-linked li,
.report-questions-unlinked li {
  line-height: 1.5;
}

.report-preflight-title {
  margin: 0;
}

/* "Questions this report doesn't answer" is a different kind of thing from the
   readiness findings above it - those are facts about the plan, these are what
   someone will ask you about it. Sharing the dialog's plain background made the
   two read as one long list, so this block is tinted and rule-marked: indigo,
   which no other state in the app uses (amber is a warning, red a deletion,
   green ready-to-show), so the colour carries the distinction on its own. */
.report-preflight-section {
  background: #f5f4ff;
  border: 1px solid #dcd8f7;
  border-left: 3px solid #6d5ce0;
  border-radius: 10px;
  display: grid;
  gap: 8px;
  padding: 12px 14px;
}

.report-preflight-section h4 {
  color: #4b3fb0;
  margin: 0;
}

/* The nested waiting/recovered notices inherit a grey/amber card that sits badly
   inside a tinted one - keep them in the section's own colour. */
.report-preflight-section .report-questions-progress {
  background: #ffffff;
  border-color: #dcd8f7;
}

.report-preflight-section .report-questions-recovered {
  background: #ffffff;
  border: 1px solid #dcd8f7;
}

/* The section is a grid, which stretched "What will they ask?" the full width of
   the dialog - at 820px that reads as a banner rather than a button. */
.report-preflight-section > button {
  justify-self: start;
}

.report-preflight-findings {
  display: grid;
  gap: 8px;
  margin: 0;
  padding-left: 18px;
}

/* Each question is a card rather than a bullet now, because each one carries a
   verdict - "here is where to go and look" or "nobody can tell you where to go
   and look" - and a verdict needs somewhere to sit. Amber for the first, red for
   the second: the two lists differ in what they ask of the author, so they have
   to be told apart before either is read. */
.report-questions-linked,
.report-questions-unlinked {
  display: grid;
  gap: 8px;
  list-style: none;
  margin: 0;
  padding-left: 0;
}

.report-questions-linked li,
.report-questions-unlinked li {
  border-radius: 8px;
  display: grid;
  gap: 6px;
  justify-items: start;
  padding: 10px 12px;
}

.report-questions-linked li {
  background: var(--warn-wash);
  border: 1px solid var(--warn-line);
  border-left: 3px solid #c08a1e;
}

/* A full red border, not a left rule: this is the one group with no way in, and
   it should read as a stop rather than as another category. */
.report-questions-unlinked li {
  background: var(--danger-wash);
  border: 2px solid #c0392b;
}

.report-questions-linked li .field-hint,
.report-questions-unlinked li .field-hint {
  display: block;
  margin-top: 2px;
}

.report-question-review {
  margin-top: 2px;
}

.report-preflight-warning {
  background: var(--notice-bg, rgba(208, 112, 44, 0.1));
  border: 1px solid var(--notice-line, rgba(208, 112, 44, 0.28));
  border-radius: 8px;
  margin: 0;
  padding: 8px 10px;
}

/* Three actions rather than two, and they should not be squeezed onto one line on a
   narrow screen. */
.report-preflight-actions {
  border-top: 1px solid var(--line, #e5e7eb);
  flex-wrap: wrap;
  justify-content: flex-end;
  padding-top: 14px;
}

@media (max-width: 720px) {
  .report-preflight-panel {
    max-height: 92vh;
  }
}

.report-readiness {
  background: var(--notice-bg, rgba(208, 112, 44, 0.1));
  border: 1px solid var(--notice-line, rgba(208, 112, 44, 0.28));
  border-radius: var(--radius-tile, 10px);
  display: grid;
  gap: 6px;
  margin: 14px 0 4px;
  padding: 14px 16px;
}

.report-readiness ul {
  display: grid;
  gap: 6px;
  margin: 0;
  padding-left: 18px;
}

.report-readiness li {
  font-size: 0.85rem;
}

.report-readiness-clean {
  background: rgba(21, 128, 61, 0.1);
  border-color: rgba(21, 128, 61, 0.28);
}

.report-readiness p {
  margin: 0;
}

/* "Somebody vouched for this, recently" - the difference between a plan that is
   stale (nobody's problem) and a milestone its owner has not confirmed in three
   weeks (somebody's problem). Bands reuse the score palette; "never" stays
   neutral rather than alarming, since an untouched plan is not yet a failure. */
.milestone-confirm-tag {
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  font-size: 0.68rem;
  margin-left: 8px;
  padding: 1px 8px;
  white-space: nowrap;
}

.milestone-confirm-tag.score-high {
  background: rgba(21, 128, 61, 0.12);
  border-color: rgba(21, 128, 61, 0.28);
}

.milestone-confirm-tag.score-mid {
  background: rgba(201, 155, 17, 0.14);
  border-color: rgba(201, 155, 17, 0.32);
}

.milestone-confirm-tag.score-low {
  background: rgba(185, 28, 28, 0.12);
  border-color: rgba(185, 28, 28, 0.28);
}

.milestone-confirm-never {
  color: var(--muted);
}

/* How sure the last check-in was about the DATE, beside the confirmation it came
   from - it is the same tap, and "confident" recorded five weeks ago is a stale
   opinion, so the two facts have to be read together.

   It borrows .milestone-confirm-tag's shape and the table's own three bands
   rather than introducing a second pill language for one field. Prefixed "Date:"
   in the markup because the band alone would read as another status, and status
   is the other question - this one is about the date, not the work. */
.milestone-confidence-tag {
  border: 1px solid var(--line);
  border-radius: var(--radius-pill);
  font-size: 0.68rem;
  margin-left: 6px;
  padding: 1px 8px;
  white-space: nowrap;
}

.milestone-confidence-tag.score-high {
  background: rgba(21, 128, 61, 0.12);
  border-color: rgba(21, 128, 61, 0.28);
}

.milestone-confidence-tag.score-mid {
  background: rgba(201, 155, 17, 0.14);
  border-color: rgba(201, 155, 17, 0.32);
}

.milestone-confidence-tag.score-low {
  background: rgba(185, 28, 28, 0.12);
  border-color: rgba(185, 28, 28, 0.28);
}

/* In the fork popover the badge trails the mitigation text inside a <p> rather
   than sitting in its own grid cell, so it needs the gap the grid was giving it. */
.milestone-fork-detail p .risk-verdict-badge,
.fork-detail-body p .risk-verdict-badge {
  margin-left: 8px;
}

/* Tinted like .milestone-table-status-pill so a failed mitigation is readable at
   a glance rather than only from the label text. Pending/inconclusive stay
   untinted on purpose - "we cannot say yet" is not a result. */
.risk-verdict-badge.score-high {
  background: rgba(21, 128, 61, 0.12);
  border-color: rgba(21, 128, 61, 0.28);
}

.risk-verdict-badge.score-mid {
  background: rgba(201, 155, 17, 0.14);
  border-color: rgba(201, 155, 17, 0.32);
}

.risk-verdict-badge.score-low {
  background: rgba(185, 28, 28, 0.12);
  border-color: rgba(185, 28, 28, 0.28);
}

/* An unconfirmed verdict is drawn as an unfinished one.
   -------------------------------------------------------------------------
   A "Mitigation failed" the sweep has flagged as needing attribution is a
   question, not a finding - see verdictBadgeHtml. A dashed border says
   provisional without introducing a fourth colour into a scale that already
   means something; the badge keeps its own band, because the verdict has not
   changed, only its certainty. */
.risk-verdict-badge.is-unconfirmed {
  border-style: dashed;
}

.risk-verdict-unconfirmed {
  font-size: 0.66rem;
  letter-spacing: 0.02em;
  margin-left: 4px;
  opacity: 0.72;
  text-transform: lowercase;
}

/* Owner picker - a required field that has to resolve to somebody on the
   project, so it suggests rather than accepting free text (see
   wireOwnerPickers). Deliberately styled like .comments-mention-dropdown: it is
   the same gesture in a different place, and the two should not look like two
   different mechanisms. */
.owner-picker {
  position: relative;
}

.owner-picker input {
  width: 100%;
}

.owner-picker-dropdown {
  background: var(--panel-strong, #ffffff);
  border: 1px solid var(--line, #e4e4e7);
  border-radius: 8px;
  box-shadow: var(--theruka-shadow, 0 12px 28px rgba(15, 23, 42, 0.16));
  left: 0;
  margin-top: 4px;
  max-height: 240px;
  overflow-y: auto;
  padding: 4px;
  position: absolute;
  right: 0;
  top: 100%;
  z-index: 30;
}

.owner-picker-option {
  background: none;
  border: 0;
  border-radius: 6px;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  gap: 1px;
  padding: 6px 8px;
  text-align: left;
  width: 100%;
}

.owner-picker-option strong {
  color: var(--ink, #18181b);
  font-size: 0.84rem;
}

.owner-picker-option span {
  color: var(--muted);
  font-size: 0.72rem;
}

.owner-picker-option:hover,
.owner-picker-option.active {
  background: var(--panel, #f4f4f5);
}

/* Separated from the matches above it: adding somebody is a different kind of
   action from picking somebody, and running them together invites a misclick. */
.owner-picker-add {
  border-top: 1px solid var(--line, #e4e4e7);
  margin-top: 4px;
  padding-top: 8px;
}

.owner-picker-add strong {
  color: var(--accent-brand, #14635a);
}

.owner-picker-empty {
  color: var(--muted);
  font-size: 0.76rem;
  margin: 0;
  padding: 8px;
}

.owner-picker-access-row {
  align-items: flex-start;
}

.owner-picker-seat-note {
  color: var(--muted);
  display: block;
  font-size: 0.72rem;
  font-style: normal;
  margin-top: 2px;
}

/* ---------------------------------------------------------------------------
   Gantt grid - the editable left-hand rows on the Gantt rail destination.
   The read-only summary card (.milestone-gantt-label on its own) is unchanged;
   these rules only apply inside .gantt-grid-list, so the Gantt modal and any
   read-only share keep the card they had.
   --------------------------------------------------------------------------- */

.section-hint {
  color: var(--muted);
  font-size: 0.78rem;
  line-height: 1.4;
  margin: 4px 0 0;
  max-width: 68ch;
}

/* The grid column has to hold three cells side by side, so it needs more room
   than the summary card's minmax(180px, 0.28fr). */
.gantt-grid-list .milestone-gantt-row {
  grid-template-columns: minmax(430px, 0.44fr) minmax(0, 1fr);
}

.gantt-grid-header {
  align-items: end;
  color: var(--muted);
  font-size: 0.66rem;
  font-weight: 780;
  letter-spacing: 0.04em;
  min-height: 0;
  text-transform: uppercase;
}

.gantt-grid-header .gantt-grid-fields {
  display: grid;
  gap: 6px;
  grid-template-columns: minmax(0, 2fr) minmax(130px, 1fr) minmax(130px, 1fr);
  padding: 0 10px;
}

.gantt-grid-label {
  align-content: start;
}

.gantt-grid-fields {
  display: grid;
  gap: 6px;
  grid-template-columns: minmax(0, 2fr) minmax(130px, 1fr) minmax(130px, 1fr);
}

.gantt-grid-field {
  display: grid;
  gap: 2px;
  min-width: 0;
}

/* Every cell is labelled for screen readers; sighted users read the column
   headings instead, so the per-cell label only appears once the columns stack
   on narrow screens (see the media query below). */
.gantt-grid-field-label {
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  height: 1px;
  overflow: hidden;
  position: absolute;
  white-space: nowrap;
  width: 1px;
}

.gantt-grid-input {
  background: #ffffff;
  border: 1px solid var(--line);
  border-radius: 6px;
  color: var(--ink);
  font: inherit;
  font-size: 0.76rem;
  min-width: 0;
  padding: 6px 8px;
  width: 100%;
}

.gantt-grid-input:hover {
  border-color: #a1a1aa;
}

.gantt-grid-input:focus {
  border-color: var(--accent, #2563eb);
  outline: none;
}

.gantt-grid-row-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.gantt-grid-row-actions button {
  font-size: 0.7rem;
  padding: 4px 10px;
}

/* Row controls appear when you are on the row, not before.
   ---------------------------------------------------------------------------
   A plan of thirty milestones drew sixty buttons, every one of them permanently
   on screen and none of them the thing being read. The row is the subject; its
   controls are what you reach for once you have found it.

   THREE RULES KEEP THIS FROM BECOMING AN ACCESSIBILITY BUG, and each is here
   because hiding a control is the easiest way to remove it by accident:

   - THE POINTER DECIDES HOW THEY ARE REVEALED, and there are two answers
     because there are two kinds of pointer. A mouse hovers, so hover reveals.
     A finger cannot hover at all - it either touches or it does not - so a
     touch screen gets an explicit ACTIVE ROW instead: the row you last touched
     shows its controls, and touching elsewhere puts them away. Relying on a tap
     to synthesise :hover is the trap that made the first version leave them
     permanently visible on a phone: it works in some mobile browsers, does
     nothing in others, and where it does work the state never clears.
   - `:focus-within` reveals them too, under both pointers, so tabbing through
     the row brings each control into view as it is reached and tapping a date
     field shows the row it belongs to. Without it a keyboard user focuses a
     button that is invisible.
   - visibility/opacity rather than `display: none`, so the row does not change
     height as the pointer crosses it and the buttons keep their place in the
     tab order. Hidden here means "not drawn yet", never "not there". */
@media (hover: hover) and (pointer: fine) {
  /* TWO ROW SHAPES, and missing the second is why a phone saw no change at all.
     The editable grid wraps its controls in .gantt-grid-row-actions; the
     READ-ONLY row - which is what narrow layouts render - carries a lone Edit
     button with no wrapper. Both are controls on a row, so both hide. */
  .milestone-gantt-row .gantt-grid-row-actions,
  .milestone-gantt-row .compact-edit-milestone {
    opacity: 0;
    transition: opacity 120ms ease;
    visibility: hidden;
  }

  .milestone-gantt-row:hover .gantt-grid-row-actions,
  .milestone-gantt-row:hover .compact-edit-milestone,
  .milestone-gantt-row:focus-within .gantt-grid-row-actions,
  .milestone-gantt-row:focus-within .compact-edit-milestone {
    opacity: 1;
    visibility: visible;
  }

  /* The blank row at either end is the exception: its only control is the one
     that commits the row, and a reader who has typed a milestone name into it
     is looking for exactly that button. */
  .gantt-add-row .gantt-grid-row-actions {
    opacity: 1;
    visibility: visible;
  }

  @media (prefers-reduced-motion: reduce) {
    .milestone-gantt-row .gantt-grid-row-actions,
    .milestone-gantt-row .compact-edit-milestone {
      transition: none;
    }
  }
}

/* The same idea for a finger, which cannot hover.
   ---------------------------------------------------------------------------
   The row you touched is the active one and shows its controls; touching
   another row moves them; touching away from the grid puts them away. The class
   is set by wireGanttGrid in dashboard.js and is honoured ONLY here, inside the
   coarse-pointer query - on a desktop it would otherwise leave a row's buttons
   on screen after the mouse had gone, which is the habit this whole rule exists
   to break.

   The first touch reveals rather than acts, and that falls out of the geometry
   rather than needing a guard: the controls are `visibility: hidden` until the
   row is active, so a finger landing where a button will be lands on the row
   instead. Nothing is pressed by accident on the way to seeing it. */
@media (hover: none), (pointer: coarse) {
  .milestone-gantt-row .gantt-grid-row-actions,
  .milestone-gantt-row .compact-edit-milestone {
    opacity: 0;
    transition: opacity 120ms ease;
    visibility: hidden;
  }

  .milestone-gantt-row.is-row-active .gantt-grid-row-actions,
  .milestone-gantt-row.is-row-active .compact-edit-milestone,
  .milestone-gantt-row:focus-within .gantt-grid-row-actions,
  .milestone-gantt-row:focus-within .compact-edit-milestone {
    opacity: 1;
    visibility: visible;
  }

  /* The blank add-row keeps its button for the same reason it does above: the
     only control on it is the one that commits the row just typed into. */
  .gantt-add-row .gantt-grid-row-actions {
    opacity: 1;
    visibility: visible;
  }

  @media (prefers-reduced-motion: reduce) {
    .milestone-gantt-row .gantt-grid-row-actions,
    .milestone-gantt-row .compact-edit-milestone {
      transition: none;
    }
  }
}

/* The same rule for the controls on a finding.
   ---------------------------------------------------------------------------
   Every row on "What needs you" and the Attention panel carries up to three -
   Mark done, + Risk, Edit - and the card shows three rows, so nine buttons were
   drawn permanently above a summary somebody opened the page to read. Show the
   finding; offer what to do about it when the reader is on it.

   THE TWO POINTERS GET DIFFERENT GEOMETRY, and that is the part worth reading.
   The timeline rows above hide their controls with `visibility` so the row
   keeps its height - right there, where the controls sit INLINE in a grid row.
   Here they are a block UNDER two lines of text, so reserving their height left
   a finger-deep hole in every row and a card three findings tall that looked
   broken. Measured in a screenshot; it is not visible from the rule.

   So on a mouse they move out of flow entirely, into a lane on the right the
   row reserves for them. Nothing reflows when the pointer crosses the list, and
   nothing is missing when it is elsewhere. */
@media (hover: hover) and (pointer: fine) {
  .needs-you li .attention-item-actions,
  .attention-section li .attention-item-actions {
    margin-top: 0;
    opacity: 0;
    position: absolute;
    right: 0;
    top: 10px;
    transition: opacity 120ms ease;
    visibility: hidden;
  }

  /* The lane this needs is declared in design-refresh.css, not here: that file
     loads after this one and sets these rows' padding with a SHORTHAND
     (`padding: 14px 0 14px 24px`), which resets padding-right to 0 whatever is
     written above it. Measured: the computed padding-right was 0px and the
     buttons sat on top of the sentence. */

  .needs-you li:hover .attention-item-actions,
  .needs-you li:focus-within .attention-item-actions,
  .attention-section li:hover .attention-item-actions,
  .attention-section li:focus-within .attention-item-actions {
    opacity: 1;
    visibility: visible;
  }

  @media (prefers-reduced-motion: reduce) {
    .needs-you li .attention-item-actions,
    .attention-section li .attention-item-actions {
      transition: none;
    }
  }
}

/* The finger's half, and it keeps the buttons IN FLOW.
   ---------------------------------------------------------------------------
   A phone has no 240px lane to give away, so the desktop geometry above would
   put three buttons on top of the sentence they belong to. `display: none`
   instead: the row sits tight until it is touched and opens when it is. The
   reflow that rules out on a mouse is fine here, because it happens in response
   to a deliberate tap rather than to a pointer passing over.

   `is-row-active` is set by wireAttentionRowReveal and is honoured ONLY inside
   this query - on a desktop it would leave a row's buttons on screen after the
   mouse had gone, which is the habit the rule exists to break. The first touch
   reveals rather than acts, because there is nothing at that point to press
   until the row is open. */
@media (hover: none), (pointer: coarse) {
  .needs-you li .attention-item-actions,
  .attention-section li .attention-item-actions {
    display: none;
  }

  .needs-you li.is-row-active .attention-item-actions,
  .needs-you li:focus-within .attention-item-actions,
  .attention-section li.is-row-active .attention-item-actions,
  .attention-section li:focus-within .attention-item-actions {
    display: flex;
  }
}

/* The same rule for the hero's Edit and Delete.
   These two are management, not work: they sit above every project and
   portfolio page, and Delete in particular is a permanent control for a thing
   somebody does roughly never. Only these two are grouped - the AI and Jira
   buttons injected into the same row are offers, and an offer nobody can see is
   not an offer. */
@media (hover: hover) and (pointer: fine) {
  .project-hero-actions .hero-manage-actions {
    display: flex;
    gap: 8px;
    opacity: 0;
    transition: opacity 120ms ease;
    visibility: hidden;
  }

  /* The whole hero panel, not just the row the buttons are in - "move onto the
     panel" is the gesture, and a target the width of two buttons is a target
     somebody has to aim at. All three hero rows (project, portfolio, first-run)
     sit inside .executive-project-hero. */
  .executive-project-hero:hover .hero-manage-actions,
  .executive-project-hero:focus-within .hero-manage-actions {
    opacity: 1;
    visibility: visible;
  }

  @media (prefers-reduced-motion: reduce) {
    .project-hero-actions .hero-manage-actions {
      transition: none;
    }
  }
}

.hero-manage-actions {
  display: flex;
  gap: 8px;
}

/* While a date change is waiting on its explanation prompt, the row it came
   from is dimmed - the value on screen is not saved yet, and cancelling puts
   it back. */
.gantt-row-pending {
  opacity: 0.55;
}

.gantt-add-row .milestone-gantt-label {
  border-style: dashed;
}

.gantt-add-hint {
  align-self: center;
  color: var(--muted);
  font-size: 0.72rem;
  line-height: 1.4;
  margin: 0;
}

.gantt-add-row {
  min-height: 0;
}

/* A draft row is a line you are still typing, not a record. It reads as part of
   the grid - same columns, same rhythm - but dashed and slightly recessive, so
   an unfinished line is never mistaken for a saved milestone. */
.gantt-draft-rows {
  display: contents;
}

.gantt-add-row.is-draft-incomplete .milestone-gantt-label {
  border-color: var(--line-strong);
}

.gantt-add-row[data-gantt-draft-row] .gantt-add-hint:empty {
  display: none;
}

@media (max-width: 1100px) {
  .gantt-grid-list .milestone-gantt-row {
    grid-template-columns: 1fr;
  }

  .gantt-grid-header {
    display: none;
  }

  /* With the header gone the columns have nothing to label them, so each cell
     shows its own label instead. */
  .gantt-grid-fields {
    grid-template-columns: 1fr;
  }

  .gantt-grid-field-label {
    clip: auto;
    clip-path: none;
    color: var(--muted);
    font-size: 0.62rem;
    font-weight: 780;
    height: auto;
    letter-spacing: 0.04em;
    overflow: visible;
    position: static;
    text-transform: uppercase;
    width: auto;
  }
}

/* ---------------------------------------------------------------------------
   Collapsing the rail on desktop.
   Below 901px the rail is an off-canvas sheet and the hamburger opens it (see
   design-refresh.css); at desktop widths it is a real grid column, so the same
   button collapses it and hands the width to the content instead.
   --------------------------------------------------------------------------- */

/* Its own control, sharing the hamburger's slot at the end of the top bar but
   never shown at the same time as it: the hamburger opens the drawer on mobile,
   this widens the workspace on desktop. Four arrows out is the conventional
   "expand"; it swaps to arrows in once there is nothing left to expand into. */
.workspace-expand-toggle {
  align-items: center;
  background: none;
  border: 1px solid var(--line);
  border-radius: 10px;
  box-shadow: none;
  color: var(--muted);
  cursor: pointer;
  display: none;
  flex: 0 0 auto;
  height: 38px;
  justify-content: center;
  margin-left: auto;
  min-height: 0;
  order: 9;
  padding: 0;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
  width: 38px;
}

.workspace-expand-toggle:hover {
  background: #f4f7fa;
  border-color: var(--line-strong);
  color: var(--ink);
}

.workspace-expand-icon {
  height: 18px;
  width: 18px;
}

/* One icon at a time: arrows out while there is a rail to reclaim, arrows in
   once the workspace already fills the width. */
.workspace-expand-toggle .collapse,
.workspace-expand-toggle[aria-pressed="true"] .expand {
  display: none;
}

.workspace-expand-toggle[aria-pressed="true"] .collapse {
  display: block;
}

.workspace-expand-toggle[aria-pressed="true"] {
  border-color: var(--line-strong);
  color: var(--ink);
}

@media (min-width: 901px) {
  .workspace-expand-toggle {
    display: inline-flex;
  }

  /* Collapsing the column to 0 rather than removing it keeps the grid a
     two-column grid, so nothing downstream has to know the rail is gone. */
  .dashboard-shell.executive-dashboard-shell.rail-collapsed {
    grid-template-columns: 0 minmax(0, 1fr);
  }

  .rail-collapsed .workspace-copilot {
    display: none;
  }
}

/* ---------------------------------------------------------------------------
   The milestone the check-in sent you here to look at.
   Marked rather than merely scrolled to: the check-in hands over one specific
   milestone that needs attention, and the Gantt is a wall of bars in which
   "which one was it again" is a real question once the scroll has finished.
   --------------------------------------------------------------------------- */

.milestone-gantt-row.gantt-row-focused {
  border-radius: 10px;
  outline: 2px solid var(--accent, #3f3f46);
  outline-offset: 4px;
  position: relative;
}

/* The outline and the badge are the whole signal, and both take their colour
   from --accent, which appearance-apply.js writes at runtime and dark-mode.css
   overrides. A hardcoded blue tint here would clash with whatever accent the
   workspace has actually chosen, so the row is marked, not repainted. */
.gantt-row-focused .milestone-gantt-label {
  border-color: var(--accent, #3f3f46);
}

/* Says why the row is marked, so the outline is not just decoration on a page
   somebody may have arrived at minutes ago. */
.gantt-row-focused::before {
  background: var(--accent, #3f3f46);
  border-radius: 999px;
  color: #ffffff;
  content: "From your check-in";
  font-size: 0.6rem;
  font-weight: 780;
  left: 0;
  letter-spacing: 0.04em;
  padding: 3px 8px;
  position: absolute;
  text-transform: uppercase;
  top: -12px;
  z-index: 2;
}

@media (prefers-reduced-motion: reduce) {
  .milestone-gantt-row.gantt-row-focused {
    outline-offset: 3px;
  }
}

/* ---------------------------------------------------------------------------
   Trend arrows and the trend line.
   Both only render when there is a recorded figure to compare against - a
   workspace in its first week shows neither, rather than a flat arrow that
   would be a claim about a comparison nobody made.
   --------------------------------------------------------------------------- */

.trend-badge {
  border-radius: 999px;
  display: inline-block;
  font-size: 0.62rem;
  font-weight: 780;
  letter-spacing: 0.01em;
  margin-left: 8px;
  padding: 2px 7px;
  vertical-align: middle;
  white-space: nowrap;
}

/* Green is "this got better", not "this went up" - fewer open risks and more
   confirmed milestones both improve, and they move in opposite directions
   numerically. The server's METRICS table is what decides which is which. */
.trend-good {
  background: #dcfce7;
  color: #166534;
}

.trend-bad {
  background: var(--danger-surface);
  color: #991b1b;
}

.trend-flat {
  background: #f4f4f5;
  color: var(--muted);
}

/* Sized to roughly the drawing box's own ratio (320x64) rather than stretched
   across whatever width is going: a sparkline blown out to 1200px flattens into
   a near-horizontal smear that says nothing. */
.trend-sparkline {
  background: var(--panel-strong, #ffffff);
  border: 1px solid var(--line);
  border-radius: 12px;
  color: var(--accent, #3f3f46);
  display: grid;
  gap: 8px;
  margin: 0 0 14px;
  max-width: 360px;
  padding: 14px 16px;
}

.trend-sparkline svg {
  display: block;
  height: auto;
  overflow: visible;
  width: 100%;
}

.trend-sparkline circle {
  fill: currentColor;
}

.trend-sparkline figcaption {
  align-items: baseline;
  color: var(--muted);
  display: flex;
  font-size: 0.64rem;
  font-weight: 780;
  gap: 10px;
  justify-content: space-between;
  letter-spacing: 0.03em;
  text-transform: uppercase;
}

/* The endpoints, because a line with no numbers on it says only "down". */
.trend-sparkline figcaption b {
  color: var(--ink);
  font-size: 0.78rem;
  letter-spacing: 0;
  white-space: nowrap;
}

@media (prefers-color-scheme: dark) {
  .trend-good {
    background: rgba(22, 101, 52, 0.35);
    color: #86efac;
  }

  .trend-bad {
    background: rgba(153, 27, 27, 0.35);
    color: #fca5a5;
  }
}

/* ---------------------------------------------------------------------------
   Workspace hits in the rail search.
   Their own block below the section list, because "take me to that page" and
   "take me to that milestone" are different questions and a milestone sorted
   between two menu items reads as a menu item.
   --------------------------------------------------------------------------- */

.rail-search-results {
  border-top: 1px solid var(--line);
  display: grid;
  gap: 2px;
  margin-top: 8px;
  padding-top: 8px;
}

.rail-search-results-heading {
  color: var(--muted);
  font-size: 0.6rem;
  font-weight: 780;
  letter-spacing: 0.06em;
  margin: 0 0 4px;
  padding: 0 10px;
  text-transform: uppercase;
}

.rail-search-result {
  align-items: flex-start;
  background: none;
  border: 0;
  border-radius: 8px;
  box-shadow: none;
  color: var(--ink);
  cursor: pointer;
  display: flex;
  gap: 9px;
  min-height: 0;
  padding: 7px 10px;
  text-align: left;
  width: 100%;
}

.rail-search-result:hover,
.rail-search-result:focus-visible {
  background: var(--panel-strong, #f4f4f5);
}

/* Fixed width and its own line box: the type glyphs are different widths, so
   without this each row's text started at a slightly different indent. */
.rail-search-result-icon {
  color: var(--muted);
  flex: 0 0 14px;
  font-size: 0.7rem;
  line-height: 1.4;
  text-align: center;
  width: 14px;
}

.rail-search-result span:last-child {
  display: grid;
  gap: 1px;
  min-width: 0;
}

.rail-search-result strong {
  font-size: 0.8rem;
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.rail-search-result small {
  color: var(--muted);
  font-size: 0.66rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* The flyout hides its non-matching contents while searching; the results block
   is the one thing that must stay visible in that state. */
.primary-rail-nav-flyout.searching .rail-search-results:not([hidden]) {
  display: grid;
}

/* ---------------------------------------------------------------------------
   Favourites in the rail, and the check-in row you came back to.
   --------------------------------------------------------------------------- */

/* A span with role="button", not a <button>: these rows are themselves buttons
   and a button inside a button is invalid markup browsers silently restructure. */
.rail-favorite-star {
  border-radius: 6px;
  color: var(--line);
  cursor: pointer;
  flex: 0 0 auto;
  font-size: 0.8rem;
  line-height: 1;
  margin-left: auto;
  opacity: 0;
  padding: 3px;
  transition: color 0.12s ease, opacity 0.12s ease;
}

/* Hidden until wanted: a column of grey stars down every menu row is visual
   noise for a thing most people set once. A favourited row keeps its star
   visible, since that is the state worth seeing at a glance. */
.primary-rail-nav-item:hover .rail-favorite-star,
.primary-rail-nav-item:focus-within .rail-favorite-star,
.rail-favorite-star:focus-visible,
.rail-favorite-star.is-favorite {
  opacity: 1;
}

.rail-favorite-star.is-favorite {
  color: #eab308;
}

.rail-favorite-star:hover {
  color: #ca8a04;
  opacity: 1;
}

.rail-favorites-empty {
  color: var(--muted);
  font-size: 0.72rem;
  line-height: 1.4;
  margin: 0;
  padding: 8px 10px;
}

/* Same signal as the Gantt's focused row, at list scale: the row the user left
   from, so coming back answers "which one was I on". */
.check-in-item-focused {
  outline: 2px solid var(--accent, #3f3f46);
  outline-offset: 3px;
  position: relative;
}

.check-in-item-focused::before {
  background: var(--accent, #3f3f46);
  border-radius: 999px;
  color: #ffffff;
  content: "You were here";
  font-size: 0.58rem;
  font-weight: 780;
  left: 14px;
  letter-spacing: 0.04em;
  padding: 3px 8px;
  position: absolute;
  text-transform: uppercase;
  top: -11px;
  z-index: 2;
}

/* ---------------------------------------------------------------------------
   Drafts in the reports list.
   A draft is the one row in this list nobody outside can open, so it says so
   rather than looking like a report that merely has not been shared yet.
   --------------------------------------------------------------------------- */

.report-draft-badge {
  background: var(--notice-bg, rgba(208, 112, 44, 0.14));
  border: 1px solid var(--notice-line, rgba(208, 112, 44, 0.3));
  border-radius: 999px;
  color: #92400e;
  font-size: 0.62rem;
  font-weight: 780;
  letter-spacing: 0.04em;
  margin-left: 8px;
  padding: 2px 8px;
  text-transform: uppercase;
  vertical-align: middle;
}

/* A left edge rather than a fill: the row still has to read as one of the list,
   not as a warning. Two classes because design-refresh.css loads after this file
   and flattens the row's borders - a single class loses that tie. */
.executive-report-history-row.executive-report-draft {
  border-left: 3px solid var(--notice-line, rgba(208, 112, 44, 0.5));
  padding-left: 14px;
}

@media (prefers-color-scheme: dark) {
  .report-draft-badge {
    color: #fcd34d;
  }
}

/* ---------------------------------------------------------------------------
   Check-in action buttons.
   These were three filled pills next to a bare .link-button - and every one of
   them computed border: 0px none, because design-refresh.css flattens button
   borders to pill fills. So the row had no edges at all, and the one button
   that also had no fill read as a piece of text rather than something to press.
   Worse in dark mode, where a transparent control on a near-black card has
   nothing but its label to be seen by.
   Scoped to this row rather than fixing .secondary-button globally: that class
   is used across the whole app and is not mine to redefine from here.
   --------------------------------------------------------------------------- */

.check-in-item-actions button {
  align-items: center;
  border: 1px solid var(--line);
  border-radius: 999px;
  box-shadow: none;
  cursor: pointer;
  display: inline-flex;
  font-size: 0.76rem;
  font-weight: 600;
  gap: 6px;
  justify-content: center;
  line-height: 1;
  min-height: 34px;
  /* Every button in the row gets the same padding, so they line up as a set
     rather than three of one size and one of another. */
  padding: 0 14px;
  transition: background 0.14s ease, border-color 0.14s ease, color 0.14s ease;
}

/* The three answers: a quiet filled chip. This is the routine action, pressed
   several times in a sitting. */
.check-in-item-actions .check-in-answer-button {
  background: var(--surface-soft, #f4f4f5);
  color: var(--ink);
}

.check-in-item-actions .check-in-answer-button:hover {
  background: #e4e4e7;
  border-color: #a1a1aa;
}

/* Going to the Gantt is a different kind of act - it leaves the page rather
   than answering anything - so it is outlined and accent-coloured instead of
   filled. Different from the answers, still unmistakably a button. */
.check-in-item-actions .check-in-go-button {
  background: transparent;
  /* Two classes: .check-in-item-actions button above is one class plus an
     element, which outranks a lone .check-in-go-button and was flattening this
     back to the generic line colour. */
  border-color: var(--theruka-green-strong, #0f6b42);
  color: var(--theruka-green-strong, #0f6b42);
}

.check-in-item-actions .check-in-go-button:hover {
  background: var(--theruka-green-soft, rgba(15, 107, 66, 0.1));
  border-color: var(--theruka-green-strong, #0f6b42);
}

.check-in-item-actions button:focus-visible {
  outline: 2px solid var(--accent, #3f3f46);
  outline-offset: 2px;
}

.check-in-item-actions button:disabled {
  cursor: not-allowed;
  opacity: 0.55;
}

/* ---------------------------------------------------------------------------
   The notification bell belongs in the corner.
   It sits inside .dashboard-actions, which had order: 3 and no auto margin, so
   it was pinned just after the portfolio/project switchers - in the middle of a
   wide top bar - while the expand toggle's own margin-left: auto pushed only
   itself to the right. The two now travel together at the right-hand end, bell
   first, expand toggle last.
   --------------------------------------------------------------------------- */

.app-top-bar .dashboard-actions {
  margin-left: auto;
  order: 8;
}

/* The expand toggle keeps the end of the row, so it no longer needs to claim the
   space itself - .dashboard-actions above does that now, and two auto margins
   would put a gap between the bell and the button rather than beside them. */
.app-top-bar .workspace-expand-toggle {
  margin-left: 0;
}

/* ---------------------------------------------------------------------------
   Gantt rows: banding, colour, drag handle.
   Rows read as a table, so the eye can follow one across to its bar. An explicit
   colour is a label the user chose; the alternating default is just structure,
   which is why "no colour" is stored as empty rather than as the stripe it
   happens to be showing.
   --------------------------------------------------------------------------- */

.gantt-grid-list .milestone-gantt-row {
  border-radius: 10px;
  padding: 6px;
  transition: background 0.12s ease;
}

/* One custom property per row, read by the row, its label card and its timeline
   track. Tinting only the <article> left the colour showing as a thin ring: both
   the label card (#fbfcfd) and the track (#f9fafb) paint their own opaque
   backgrounds over it, so the band stopped at their edges. All three take the
   tint now, and the row reads as one colour from the name to the end of the bar. */
.gantt-grid-list .milestone-gantt-row {
  --gantt-row-tint: transparent;
}

/* Rows separated by a hairline and a barely-there neutral zebra.
   
   What was here: eight user-selectable tints plus a blue/green alternation, all
   at 10-16% alpha over the label, the track AND the grid lines. On a plan of any
   length the page read as a coloured spreadsheet, and the tint competed with the
   only colour on this screen that means something - a bar that moved, a risk
   that is open, a date in conflict. Colour is now reserved for those.
   
   The zebra is neutral and faint on purpose: enough to follow a row across to
   its bar, not enough to be seen as a category. */
.gantt-grid-list .milestone-gantt-row + .milestone-gantt-row {
  border-top: 1px solid var(--line);
}

.gantt-grid-list .gantt-row-alt .milestone-gantt-label,
.gantt-grid-list .gantt-row-alt .milestone-gantt-track {
  background-color: color-mix(in srgb, var(--ink) 2.5%, transparent);
}

.gantt-grid-row-tools {
  align-items: center;
  display: flex;
  gap: 8px;
  justify-content: space-between;
}

.gantt-drag-handle {
  color: var(--muted);
  cursor: grab;
  font-size: 0.9rem;
  letter-spacing: -1px;
  line-height: 1;
  padding: 2px 4px;
  user-select: none;
}

.gantt-drag-handle:hover {
  color: var(--ink);
}

.gantt-row-dragging {
  cursor: grabbing;
  opacity: 0.5;
}

/* Which side of the row the drop lands on, drawn on the row being hovered rather
   than as a floating line - it needs no measurement and cannot drift. */
.gantt-row-drop-before {
  box-shadow: inset 0 3px 0 0 var(--accent, #3f3f46);
}

.gantt-row-drop-after {
  box-shadow: inset 0 -3px 0 0 var(--accent, #3f3f46);
}

/* .gantt-color-picker, .gantt-color-swatch, the nine swatch colours and
   .gantt-color-select stood here. All of it drew the per-row colour control,
   which is gone - see the note on the zebra above. */


/* The blank row at the top gets the same treatment as the one at the bottom, but
   a dashed top edge so it reads as the start of the list rather than a row that
   lost its content. */
.gantt-add-row-start {
  margin-bottom: 4px;
}

/* ---------------------------------------------------------------------------
   "Reading the report the way an executive would" - the waiting state.
   A sentence with no spinner and no elapsed time cannot be told apart from a
   stuck page, which is the one thing it must not look like.
   --------------------------------------------------------------------------- */

.report-questions-progress {
  align-items: flex-start;
  background: var(--surface-soft, #f4f4f5);
  border: 1px solid var(--line);
  border-radius: 10px;
  display: flex;
  gap: 12px;
  padding: 14px 16px;
}

.report-questions-progress > div {
  display: grid;
  gap: 4px;
}

.report-questions-progress .button-spinner {
  flex: 0 0 auto;
  margin-top: 3px;
}

/* Finding questions you do not remember saving is unsettling unless something
   says where they came from. */
.report-questions-recovered {
  background: var(--notice-bg, rgba(208, 112, 44, 0.1));
  border: 1px solid var(--notice-line, rgba(208, 112, 44, 0.28));
  border-radius: 8px;
  padding: 8px 10px;
}

/* ---------------------------------------------------------------------------
   Trial
   --------------------------------------------------------------------------- */

/* Sits between the top bar and the workspace, so the countdown is present on
   every section rather than only on the billing page someone has to go find. */
.trial-bar {
  align-items: center;
  background: #eef4ff;
  border-bottom: 1px solid #cddcf5;
  color: #1c3d70;
  display: flex;
  flex-wrap: wrap;
  font-size: 0.86rem;
  gap: 12px;
  justify-content: space-between;
  padding: 9px 18px;
}

/* The last day reads as a deadline rather than an FYI - it is the day the
   decision is actually due. */
.trial-bar.trial-bar-ending {
  background: #fff6e8;
  border-bottom-color: var(--warn-line);
  color: var(--warn);
}

/* `display: flex` above outranks the UA's `[hidden] { display: none }`, so
   without this the strip keeps its tinted band after the trial ends - a stripe
   of colour under the top bar with nothing in it. */
.trial-bar[hidden] {
  display: none;
}

.trial-bar-text {
  min-width: 0;
}

.trial-bar-actions {
  display: flex;
  flex: 0 0 auto;
  gap: 8px;
}

/* Its own button rather than .secondary-button: this strip has its own tinted
   background, and a white pill on top of it reads as a card, not an action. */
.trial-bar-button {
  background: #1c3d70;
  border: 0;
  border-radius: 999px;
  color: #ffffff;
  cursor: pointer;
  font-size: 0.84rem;
  min-height: 32px;
  padding: 0 14px;
}

.trial-bar.trial-bar-ending .trial-bar-button {
  background: var(--warn);
}

.trial-decision-panel,
.seat-selection-panel {
  max-width: 560px;
  text-align: left;
  width: min(560px, 100%);
}

.trial-decision-title,
.seat-selection-title {
  margin: 0 0 10px;
}

.trial-decision-body,
.seat-selection-body {
  line-height: 1.55;
  margin: 0 0 10px;
}

/* The one line that answers "have I just lost my team?" - it earns emphasis
   over the sentence that raised the question. */
.trial-decision-reassurance {
  background: var(--good-wash);
  border-left: 3px solid var(--good-line);
  border-radius: 8px;
  padding: 8px 10px;
}

.trial-decision-actions {
  justify-content: flex-end;
}

/* ---- Monthly or yearly ------------------------------------------------- */

.billing-cycle-panel {
  max-width: 560px;
  text-align: left;
  width: min(560px, 100%);
}

.billing-cycle-title {
  margin: 0 0 14px;
}

.billing-cycle-options {
  display: grid;
  gap: 10px;
  margin-bottom: 16px;
}

.billing-cycle-option {
  align-items: start;
  border: 1px solid var(--line);
  border-radius: 12px;
  cursor: pointer;
  display: grid;
  gap: 12px;
  grid-template-columns: auto minmax(0, 1fr);
  padding: 14px 16px;
}

.billing-cycle-option:hover {
  border-color: var(--good-line);
}

/* The cheaper option is the preselected one, and it says so structurally rather
   than only by being first: a tinted card with a green edge, so the eye lands on
   it before it reads either price. */
.billing-cycle-recommended {
  background: var(--good-wash);
  border-color: var(--good-line);
}

.billing-cycle-body {
  display: grid;
  gap: 3px;
}

.billing-cycle-head {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

.billing-cycle-badge {
  background: var(--good-line);
  border-radius: 999px;
  color: #ffffff;
  font-size: 0.68rem;
  font-style: normal;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding: 2px 8px;
  text-transform: uppercase;
}

.billing-cycle-price {
  font-size: 1.15rem;
  font-variant-numeric: tabular-nums;
  font-weight: 650;
  letter-spacing: -0.01em;
}

.billing-cycle-note {
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.5;
}

.billing-cycle-option input:disabled {
  cursor: not-allowed;
}

.seat-selection-list {
  display: grid;
  gap: 2px;
  list-style: none;
  margin: 0 0 10px;
  max-height: 42vh;
  overflow-y: auto;
  padding: 0;
}

.seat-selection-list label {
  align-items: center;
  border-radius: 8px;
  cursor: pointer;
  display: grid;
  gap: 10px;
  grid-template-columns: auto minmax(0, 1fr) auto;
  padding: 8px 10px;
}

.seat-selection-list label:hover {
  background: #f4f7fa;
}

/* Disabled means "the plan is full", not "this person is ineligible" - so it
   dims rather than looking broken. */
.seat-selection-list input:disabled + .seat-selection-email {
  opacity: 0.5;
}

.seat-selection-email {
  min-width: 0;
  overflow-wrap: anywhere;
}

.seat-selection-role {
  color: var(--muted, #71717a);
  font-size: 0.78rem;
}

.seat-selection-count {
  color: var(--muted, #71717a);
  font-size: 0.8rem;
  margin: 0 0 10px;
}

.subscription-trial-banner {
  background: #eef4ff;
  border: 1px solid #cddcf5;
  border-radius: 10px;
  color: #1c3d70;
  display: grid;
  gap: 6px;
  margin-bottom: 12px;
  padding: 12px 14px;
}

.subscription-trial-banner.ending {
  background: #fff6e8;
  border-color: var(--warn-line);
  color: var(--warn);
}

/* Not an error state - these people are held, not gone - but it is the one
   thing on this page that names something the owner has lost. */
.subscription-suspended-banner {
  background: var(--danger-wash);
  border: 1px solid var(--danger-line);
  border-radius: 10px;
  color: #7a2020;
  display: grid;
  gap: 6px;
  margin-bottom: 12px;
  padding: 12px 14px;
}

/* ---------------------------------------------------------------------------
   Delete account
   Last on the billing page and visibly apart from the plan controls - switching
   plan and deleting everything are not neighbours.
   --------------------------------------------------------------------------- */

/* One step short of the danger zone below it, and deliberately similar in
   shape: both are ways out. Amber rather than red because terminating is
   reversible - you can subscribe again and suspended seats come back - and
   giving the two the same colour would flatten that difference. */
.account-terminate-zone {
  background: var(--warn-wash);
  border: 1px solid #ecdfc4;
  border-radius: 10px;
  display: grid;
  gap: 8px;
  margin-top: 24px;
  padding: 14px 16px;
}

.account-terminate-zone header strong {
  color: #7a5820;
}

.account-terminate-zone p {
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.5;
  margin: 0;
  max-width: 62ch;
}

.account-terminate-zone button {
  justify-self: start;
}

.account-danger-zone {
  background: var(--danger-wash);
  border: 1px solid var(--danger-line);
  border-radius: 10px;
  display: grid;
  gap: 8px;
  margin-top: 24px;
  padding: 14px 16px;
}

.account-danger-zone header strong {
  color: #7a2020;
}

.account-danger-zone p {
  color: var(--muted);
  font-size: 0.85rem;
  line-height: 1.5;
  margin: 0;
  max-width: 62ch;
}

.account-danger-zone button {
  justify-self: start;
}

/* The delete dialog.
 *
 * The most destructive screen in the product, and it has to read as one without
 * shouting: the danger is carried by a single red rail down the steps and by the
 * final button, not by colouring the whole panel. Everything else stays quiet so
 * the text - which is the actual warning - is what gets read.
 *
 * It is also the longest dialog here, so it scrolls INSIDE itself rather than
 * growing past the viewport. On a laptop the confirm field and its buttons used
 * to be below the fold, which turns "read this and confirm" into "scroll the
 * page to find the button".
 */
.delete-account-panel {
  gap: 0;
  max-height: min(88vh, 780px);
  max-width: 560px;
  overflow-y: auto;
  text-align: left;
  width: min(560px, 100%);
}

.delete-account-title {
  font-size: 1.15rem;
  margin: 0 0 10px;
}

/* Which account is going. Shown only to somebody who is also on SOMEBODY ELSE'S
   account, where "delete account" is genuinely ambiguous from where they sit -
   they own a personal one and were granted another. Red and first, because it is
   the question they are actually asking. */
.delete-account-scope {
  background: rgba(180, 35, 24, 0.08);
  border: 1px solid rgba(180, 35, 24, 0.35);
  border-radius: 10px;
  color: #8a1b12;
  font-size: 0.86rem;
  line-height: 1.5;
  margin: 0 0 14px;
  padding: 11px 13px;
}

.delete-account-scope strong {
  color: var(--danger-strong);
}

.delete-account-body {
  color: var(--ink);
  font-size: 0.9rem;
  line-height: 1.55;
  margin: 0 0 14px;
}

/* The steps. A real ordered list with its own counters, because the sequence is
   load-bearing - step 1 failing stops the rest - and browser default markers set
   the number in body type where it reads as punctuation rather than as order. */
.delete-account-steps {
  border-left: 3px solid rgba(180, 35, 24, 0.28);
  counter-reset: delete-step;
  display: grid;
  gap: 12px;
  list-style: none;
  margin: 0 0 16px;
  padding: 2px 0 2px 16px;
}

.delete-account-steps li {
  counter-increment: delete-step;
  display: grid;
  font-size: 0.875rem;
  gap: 10px;
  grid-template-columns: 22px 1fr;
  line-height: 1.5;
}

.delete-account-steps li::before {
  align-items: center;
  background: rgba(180, 35, 24, 0.1);
  border-radius: 999px;
  color: var(--danger-strong);
  content: counter(delete-step);
  display: flex;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  height: 22px;
  justify-content: center;
  width: 22px;
}

/* The reassurance, deliberately not styled like the warnings above it. Somebody
   reading "permanently erased" four times needs this one to look like a
   different kind of sentence, or it reads as a fifth thing being destroyed. */
.delete-account-keeps {
  background: var(--panel);
  border-radius: 10px;
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.5;
  margin: 0 0 18px;
  padding: 10px 12px;
}

.delete-account-field {
  display: grid;
  gap: 8px;
  margin-bottom: 12px;
}

.delete-account-field span {
  color: var(--ink);
  font-size: 0.85rem;
}

/* The input had NO rules of its own and inherited only colour, so it rendered as
   a hairline box with no height and no padding - on the one screen where the
   thing being typed is the whole safeguard. */
.delete-account-field input {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--ink);
  font-family: inherit;
  font-size: 0.95rem;
  min-height: 44px;
  padding: 10px 12px;
  transition: border-color 0.15s ease, box-shadow 0.15s ease;
  width: 100%;
}

.delete-account-field input:focus-visible {
  border-color: var(--danger-strong);
  box-shadow: 0 0 0 3px rgba(180, 35, 24, 0.16);
  outline: none;
}

/* Matched, so the button below is now live. Confirmation before the click rather
   than after it - the typing is the safeguard, and a field that never
   acknowledges the match makes people re-read their own address instead of the
   warning.
 *
 * Driven by the same check that enables the button, NOT by :valid - the input is
 * type=email, so :valid would turn green for any well-formed address and tell
 * somebody they had typed their own when they had not. */
.delete-account-field input[data-confirm-match="yes"] {
  border-color: var(--good-line);
}

.delete-account-status:empty {
  display: none;
}

.delete-account-status {
  background: rgba(180, 35, 24, 0.08);
  border-radius: 8px;
  color: var(--danger-strong);
  font-size: 0.82rem;
  margin: 0 0 12px;
  padding: 8px 10px;
}

/* Full width and side by side, with the destructive one last - the reading order
   ends on the thing that cannot be undone rather than starting on it. */
.delete-account-panel .confirm-actions {
  display: grid;
  gap: 10px;
  grid-template-columns: 1fr 1fr;
}

.delete-account-panel .confirm-actions button {
  min-height: 42px;
  width: 100%;
}

/* Solid red once it is live, scoped to this panel rather than changing every
   delete button in the product. The shared .project-delete-button is a pale tint
   - right for removing one project from a list, wrong here, where "Keep my
   account" would otherwise be the louder of the two and the irreversible one
   would look like the lesser option. */
.delete-account-panel .confirm-actions button.project-delete-button:not([disabled]) {
  background: var(--danger-strong);
  border-color: var(--danger-strong);
  color: #ffffff;
  font-weight: 600;
}

.delete-account-panel .confirm-actions button.project-delete-button:not([disabled]):hover {
  background: #9a1e14;
  border-color: #9a1e14;
}

/* Inert rather than merely faded - a disabled destructive button that still
   looks pressable is one people click at, then wonder about. Still legible
   though: this is the label somebody reads to decide whether to type their
   address at all, so it cannot be greyed into illegibility. */
.delete-account-panel .confirm-actions button[disabled] {
  cursor: not-allowed;
  filter: grayscale(1);
  opacity: 0.6;
}

@media (max-width: 480px) {
  .delete-account-panel .confirm-actions {
    grid-template-columns: 1fr;
  }
}

/* The payment demand. Sits above everything, including other overlays, because
   it is the only modal in the product that must not be worked around - a
   z-index below a dialog somebody can still open would be a way past it. */
.payment-request-overlay {
  z-index: 10000;
}

.payment-request-panel {
  max-width: 520px;
}

/* ---- Ways out of a blocked account ------------------------------------- */

.payment-resolution-panel {
  max-width: 620px;
  text-align: left;
  width: min(620px, 100%);
}

.payment-resolution-title {
  margin: 0 0 8px;
}

.payment-resolution-body {
  line-height: 1.55;
  margin: 0 0 16px;
}

/* Flex column rather than a grid, and the cards refuse to shrink.
   A grid with a capped height compresses its auto rows to fit, and a stretched
   grid item then gets a row shorter than its own text - so the longest option
   ("move to Free", which names the team members who lose access) had its last
   line rendered outside the card and over the option below it. The cap has to
   stay, or four options plus their explanations run off a laptop screen; what
   changes is that overflow now scrolls the list instead of squashing a card. */
.payment-resolution-options {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 16px;
  max-height: 52vh;
  overflow-y: auto;
}

/* Rows, not a button bar: each option carries a sentence explaining what it
   costs the customer, and that sentence is the point of offering it.
   These are <button>s, so the app's base button rule applies - a dark gradient,
   white text, a shadow and a 40px floor. Every one of those has to be turned off
   explicitly or the cards render as white-on-white slabs.

   `flex: none` is load-bearing: without it a card shrinks to whatever height the
   capped list has left and its text spills out the bottom (see the list above).
   A card is as tall as what it has to say. */
.payment-resolution-option {
  background: var(--panel-strong, #ffffff);
  border: 1px solid var(--line);
  border-radius: 12px;
  box-shadow: none;
  color: var(--ink);
  cursor: pointer;
  display: block;
  flex: none;
  font: inherit;
  height: auto;
  min-height: 0;
  padding: 13px 15px;
  text-align: left;
  white-space: normal;
  width: 100%;
}

.payment-resolution-option:hover {
  background: var(--panel-strong, #ffffff);
  border-color: var(--good-line);
  box-shadow: none;
}

.payment-resolution-lead:hover {
  background: var(--good-wash);
}

.payment-resolution-lead {
  background: var(--good-wash);
  border-color: var(--good-line);
}

/* Deleting sits with the others rather than in a corner - it is a legitimate
   answer to "I cannot pay this" - but it never looks like the easy one. */
.payment-resolution-danger:hover {
  border-color: #c0392b;
}

.payment-resolution-label {
  color: var(--ink);
  display: block;
  font-weight: 640;
}

.payment-resolution-danger .payment-resolution-label {
  color: #a63a2f;
}

.payment-resolution-detail {
  color: var(--muted);
  display: block;
  font-size: 0.82rem;
  line-height: 1.5;
  margin-top: 3px;
}

/* Who can settle this. Never hidden behind the note - a member who does not know
   whose account it is has no way to act at all. */
.payment-request-owner {
  font-weight: 600;
  margin: 8px 0 0;
}

.payment-request-note {
  border-left: 3px solid var(--line);
  color: var(--muted);
  margin: 12px 0;
  padding: 4px 0 4px 12px;
}

.payment-request-error {
  color: var(--danger-strong, #b42318);
  font-size: 0.9rem;
  margin: 8px 0 0;
}

/* ---- A5 score bars ----------------------------------------------------- */
/* Replaces the semicircle gauges. A horizontal track reads a 0-100 value more
   accurately than an arc, stacks without fighting for space, and leaves the
   line free for the delta an arc could never carry. */
.score-bars {
  display: grid;
  gap: 12px;
  width: 100%;
}

.score-bar-row { display: grid; gap: 5px; }

.score-bar-top {
  align-items: baseline;
  display: flex;
  gap: 12px;
  justify-content: space-between;
}

.score-bar-label {
  color: var(--muted);
  font-size: 0.82rem;
}

.score-bar-value {
  font-size: 1.05rem;
  font-variant-numeric: tabular-nums;
  font-weight: 650;
  letter-spacing: -0.01em;
  white-space: nowrap;
}

/* The overall score leads: it is the one number somebody quotes in a meeting. */
.score-bar-lead .score-bar-label { color: var(--ink); font-weight: 600; }
.score-bar-lead .score-bar-value { font-size: 1.6rem; }
.score-bar-lead .score-bar-track { height: 10px; }

.score-bar-track {
  background: var(--line);
  border-radius: 999px;
  height: 7px;
  overflow: hidden;
}

.score-bar-fill {
  border-radius: 999px;
  display: block;
  height: 100%;
}
/* The same four accents the gauge used, so a bar is the colour that score has
   always been - the encoding changed, the meaning did not. All four bands must be
   listed: scoreBand() returns score-warn for 71-90, and an unlisted band renders a
   fill with no background, which reads as a score of zero. */
.score-bar-fill.score-low { background: #a63a2f; }
.score-bar-fill.score-mid { background: #9a6b05; }
.score-bar-fill.score-warn { background: #b65c1f; }
.score-bar-fill.score-high { background: #1f7a46; }

.score-bar-detail {
  color: var(--muted);
  font-size: 0.72rem;
}

/* ---- B5 overnight change strip ----------------------------------------- */
.change-strip {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 10px;
  margin: 16px 0 0;
  padding: 14px 16px;
}

.change-strip-head {
  align-items: baseline;
  display: flex;
  gap: 12px;
  justify-content: space-between;
  margin-bottom: 8px;
}
.change-strip-head h3 { font-size: 0.95rem; margin: 0; }
.change-strip-head span { color: var(--muted); font-size: 0.74rem; }

.change-strip-list { display: grid; gap: 2px; list-style: none; margin: 0; padding: 0; }

.change-strip-list li {
  align-items: baseline;
  border-top: 1px solid var(--line);
  display: grid;
  font-size: 0.82rem;
  gap: 10px;
  grid-template-columns: minmax(0, 1fr) auto auto;
  padding: 6px 0;
}
.change-strip-list li:first-child { border-top: none; }

.change-name { color: var(--ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.change-figures { color: var(--muted); font-size: 0.78rem; font-variant-numeric: tabular-nums; }
.change-figures b { color: var(--ink); font-weight: 600; }

.change-delta {
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  font-weight: 650;
  min-width: 2.5em;
  text-align: right;
}
.change-worse .change-delta { color: var(--danger-strong, #c0392b); }
.change-better .change-delta { color: var(--good-strong, #1a7f4b); }
.change-strip-more {
  margin: 8px 0 0;
  font-size: 0.78rem;
  color: var(--muted, #6b7280);
}

/* "What needs you" - the triage findings, on the opening screen instead of
   behind the Attention Needed tile's digit. Same card shape as the change strip
   below it, because they are two halves of one question and styling them
   differently would suggest otherwise. */
.needs-you {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: var(--radius-card);
  box-shadow: var(--shadow-resting);
  margin: 0 0 16px;
  padding: 18px 20px;
}

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

/* The heading and its count are one group now, so the Copilot button can sit
   opposite them rather than being spaced between them by the header's own
   justify-content. */
.needs-you-title {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

.needs-you-head h3 { font-size: 0.95rem; margin: 0; }
.needs-you-head span { color: var(--muted); font-size: 0.74rem; }

/* The button keeps the size every other button on the page has - the muted
   0.74rem above is for the count beside the heading, and it would otherwise
   inherit down onto the control. */
.needs-you-head button {
  font-size: 0.85rem;
}

/* The list is renderAttentionList's own markup, so it arrives with the panel's
   spacing. Here it is three rows under a header rather than a scrollable panel,
   so the outer list drops its own padding and lets the card provide it. */
.needs-you-list ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* .needs-you-more is gone with the footer it styled. The Copilot button it held
   moved up beside the heading - it is the destination this card exists to
   offer, and it was the last thing on the card, under three findings carrying
   three buttons each. */

/* Counts and scores, demoted out of the hero. It keeps the hero's two-column
   shape - the change is where it sits, not how it reads. */
.project-standing {
  margin: 0 0 16px;
}

/* Sits between the slip waterfall and the tracked-measure list, and its job is
   to say which is which. Without it the deltas read as part of the chart above
   them rather than as a second, weaker answer to the same question. */
.change-strip-caption {
  margin: 14px 0 6px;
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The waterfall is its own .plan-section on the Changes page - a card with its
   own padding and border. Nested inside the change strip it is already in one,
   so it drops the second frame and keeps only the chart. */
.change-strip .waterfall-section {
  background: none;
  border: 0;
  border-radius: 0;
  box-shadow: none;
  margin: 4px 0 0;
  padding: 0;
}

.change-strip .waterfall-header {
  display: none;
}

/* The welcome after a payment. It replaces a wall, so it is deliberately the
   opposite of one: dismissible, and it leads with what they now have. */
.billing-welcome-panel {
  max-width: 460px;
  text-align: left;
}

.billing-welcome-panel h2 {
  font-size: 1.35rem;
  margin: 0 0 10px;
}

.billing-welcome-panel p {
  line-height: 1.55;
  margin: 0 0 18px;
}

/* --- Connectors ------------------------------------------------------------
   The Jira panel. Scoped to .jira-* so nothing here reaches the rest of the
   page: the section reuses .entity-editor-form and .section-editor-actions, and
   the only things it needs of its own are the explainer list and the question
   rows, both of which are read rather than filled in. */

.jira-explainer {
  display: grid;
  gap: 14px;
  list-style: none;
  margin: 0 0 20px;
  padding: 0;
}

.jira-explainer li {
  display: grid;
  gap: 3px;
}

.jira-explainer strong {
  font-size: 0.95rem;
}

.jira-explainer span {
  color: var(--muted);
  line-height: 1.5;
}

/* Label above value rather than side by side: the values here are URLs and
   email addresses, which wrap badly into a narrow second column. */
.jira-connection-facts {
  display: grid;
  gap: 14px;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  margin: 0 0 16px;
}

.jira-connection-facts dt {
  color: var(--muted);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.jira-connection-facts dd {
  margin: 4px 0 0;
  overflow-wrap: anywhere;
}

/* Running the connector is a different kind of act from configuring it, so the
   two rows are separated rather than run together - and "Pull now" sits left,
   away from Disconnect, because they are one mis-click apart. */
.jira-run-actions {
  border-top: 1px solid var(--line);
  justify-content: flex-start;
  margin-top: 18px;
  padding-top: 16px;
}

.jira-questions-heading {
  margin-top: 26px;
}

.jira-question-list {
  display: grid;
  gap: 10px;
  list-style: none;
  margin: 0;
  padding: 0;
}

.jira-question-list li {
  align-items: center;
  /* Tokens, not literals: dark-mode.css redefines these, and a hardcoded pale
     grey here would be an unreadable white slab under the dark theme. */
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 10px;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: space-between;
  padding: 12px 14px;
}

/* Without this the button inherits the form-button width and fills the row,
   which reads as the primary action of the whole page rather than of one line. */
.jira-question-list li button {
  flex: 0 0 auto;
  width: auto;
}

.jira-return-notice {
  margin-bottom: 16px;
}

.jira-token-form,
.jira-oauth-alternative {
  border-top: 1px solid var(--line);
  margin-top: 20px;
  padding-top: 18px;
}

.jira-token-form h5 {
  margin: 0 0 4px;
}

/* The API-token form is the primary path (it works on every deployment and
   every Jira), so the OAuth alternative reads as the quieter option. */
.jira-oauth-alternative {
  display: grid;
  gap: 10px;
}

.jira-connector-list {
  display: grid;
  gap: 10px;
  list-style: none;
  margin: 0 0 18px;
  padding: 0;
}

.jira-connector-list li {
  align-items: center;
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 10px;
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: space-between;
  padding: 12px 14px;
}

.jira-connector-list li > span {
  display: grid;
  gap: 2px;
}

.jira-connector-list li small {
  color: var(--muted);
}

.jira-connector-list li button {
  flex: 0 0 auto;
  width: auto;
}

/* Collapsed once a connector exists: it is orientation for the first time, not
   something to scroll past on every later visit. */
.jira-explainer-details {
  margin: 0 0 4px;
}

.jira-explainer-details summary {
  cursor: pointer;
  padding: 6px 0;
}

.jira-explainer-details .jira-explainer {
  margin-top: 12px;
}

/* A failed pull. Deliberately loud: the schedule keeps firing when a connector
   breaks, so nothing else on the page would look wrong. */
.jira-failure {
  background: var(--panel-strong);
  border: 1px solid var(--danger-strong, #b91c1c);
  border-left-width: 4px;
  border-radius: 8px;
  display: grid;
  gap: 4px;
  margin: 0 0 16px;
  padding: 12px 14px;
}

.jira-failure strong {
  color: var(--danger-strong, #b91c1c);
}

.jira-failure span {
  color: var(--muted);
  /* Jira's own message, which can be long and is worth reading in full. */
  overflow-wrap: anywhere;
}

.jira-connector-list li.jira-connector-failing {
  border-color: var(--danger-strong, #b91c1c);
}

.jira-connector-list .jira-failure {
  margin: 8px 0 0;
}

/* Built to be screenshotted: plain labelled rows, generous contrast, nothing
   that depends on hover or scroll position to make sense in a still image. */
.jira-diagnostics {
  border-top: 1px solid var(--line);
  margin-top: 26px;
  padding-top: 16px;
}

.jira-diagnostics summary {
  cursor: pointer;
  font-weight: 600;
  padding: 6px 0;
}

.jira-diagnostics-setup {
  display: grid;
  gap: 8px 20px;
  grid-template-columns: repeat(auto-fit, minmax(220px, 1fr));
  margin: 12px 0 18px;
}

.jira-diagnostics-setup dt {
  color: var(--muted);
  font-size: 0.78rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
}

.jira-diagnostics-setup dd {
  margin: 2px 0 0;
  overflow-wrap: anywhere;
}

.jira-diagnostics-errors {
  display: grid;
  gap: 10px;
  list-style: none;
  margin: 0 0 16px;
  padding: 0;
}

.jira-diagnostics-errors li {
  background: var(--panel-strong);
  border: 1px solid var(--line);
  border-radius: 8px;
  padding: 10px 12px;
}

.jira-diagnostics-error-head {
  align-items: baseline;
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  font-size: 0.82rem;
  gap: 10px;
}

.jira-diagnostics-errors p {
  margin: 6px 0 0;
  overflow-wrap: anywhere;
}

/* --- Reports page ----------------------------------------------------------
   Two things that are easy to confuse - a live review and a frozen report -
   so each is a card of its own with its own list inside it, rather than two
   headings separated by a hairline. */

.reports-intro {
  margin: 0 0 20px;
  max-width: 70ch;
}

/* Deliberately NOT a nested card - design-refresh.css separates sections inside
   .project-dashboard with a top hairline and no border of their own, and one
   page inventing its own container would read as a bug rather than as emphasis.
   What makes the two legible instead is the eyebrow above each heading and the
   sub-list inside each. The extra air between them lives in design-refresh.css,
   which loads last and would otherwise win the tie on equal specificity. */

.reports-section-header {
  align-items: start;
  display: flex;
  flex-wrap: wrap;
  gap: 16px;
  justify-content: space-between;
}

.reports-section-header > div:first-child {
  max-width: 62ch;
}

.reports-section-header h3 {
  margin: 2px 0 6px;
}

.reports-section-header p {
  color: var(--muted);
  margin: 0;
}

.reports-section-actions {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* Each section's own list, fenced off from the section's description so it is
   clear which reports belong to which. */
.reports-subsection {
  border-top: 1px solid var(--line);
  margin-top: 20px;
  padding-top: 16px;
}

.reports-subsection h4 {
  margin: 0 0 4px;
}

.reports-subsection .admin-meta {
  margin: 0 0 12px;
  max-width: 70ch;
}

.reports-subsection .empty-state {
  margin: 0;
}

/* The workspace-seats half of the People page. It was a rail page of its own
   ("Seats & access") until the two were merged; a rule above it says the two
   halves are one subject rather than two stacked pages. */
.people-seats-section {
  border-top: 1px solid var(--line);
  display: grid;
  gap: 8px;
  margin-top: 28px;
  padding-top: 24px;
}

/* A refused check-in answer, said on the row that was answered. The toast at the
   top of the page clears itself after seven seconds and the answer buttons are
   often below the fold, so a refusal used to read as a dead button. */
.check-in-item-error {
  background: var(--danger-wash);
  border: 1px solid #e6b8b1;
  border-radius: 8px;
  color: #93301f;
  font-size: 0.8rem;
  line-height: 1.4;
  margin: 8px 0 0;
  padding: 8px 10px;
}

/* ---------------------------------------------------------------------------
   People: one roster, one row per person, one editor behind it

   The page used to carry two lists - this one and a "Who can sign in" section -
   which were the same people, since the roster already merges workspace members
   in. One list now, ten at a time, and a row opens everything about that person
   instead of crowding three link-buttons into it.
   ------------------------------------------------------------------------ */
.people-list {
  display: grid;
  gap: 8px;
  margin-top: 4px;
}

.people-list .people-row {
  align-items: center;
  background: var(--panel-strong, #ffffff);
  border: 1px solid var(--line);
  border-radius: 10px;
  color: var(--ink);
  cursor: pointer;
  display: flex;
  gap: 12px;
  justify-content: space-between;
  min-height: 0;
  padding: 12px 14px;
  text-align: left;
  transition: border-color 0.12s ease;
  width: 100%;
}

.people-list .people-row:hover,
.people-list .people-row:focus-visible {
  border-color: var(--accent, #2563eb);
}

.people-row-main {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.people-row-main strong {
  font-size: 0.92rem;
}

.people-row-main span {
  color: var(--muted);
  font-size: 0.78rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Whether somebody holds a seat is the one fact worth reading down the column
   rather than out of a sentence - it is what the plan is billed for. */
.people-row-access {
  background: var(--pill, #f4f4f5);
  border-radius: 999px;
  color: var(--muted);
  flex: 0 0 auto;
  font-size: 0.68rem;
  font-weight: 800;
  letter-spacing: 0.03em;
  padding: 4px 10px;
  text-transform: uppercase;
}

.people-row-access.has-login {
  background: var(--good-wash);
  color: #1f6b41;
}

.people-pagination {
  align-items: center;
  display: flex;
  gap: 12px;
  justify-content: flex-end;
  margin-top: 14px;
}

/* ---------------------------------------------------------------------------
   The scope bar: a filter that has to look like a filter.

   This first shipped in the page header beside "Add team member" - two controls
   in a row, one of which adds a person and one of which changes what you are
   looking at, distinguished only by the word "Showing" in small text. That
   reads as step one of adding somebody. It is its own bar now, above the list
   it governs, saying in words that it changes the view and adds nobody.
   ------------------------------------------------------------------------ */
.people-scope-bar {
  align-items: center;
  background: color-mix(in srgb, var(--ink) 3%, transparent);
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--radius-row, 12px);
  display: flex;
  flex-wrap: wrap;
  gap: 12px;
  justify-content: space-between;
  margin: 14px 0 12px;
  padding: 12px 14px;
}

.people-scope-copy {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.people-scope-heading {
  font-size: 0.9rem;
  font-weight: 600;
  margin: 0;
}

.people-scope-hint {
  color: var(--muted, #5b6470);
  font-size: 0.78rem;
  margin: 0;
}

.people-scope select {
  background-color: var(--surface, #fff);
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--radius-control, 8px);
  color: inherit;
  font: inherit;
  max-width: 260px;
  min-height: 36px;
  padding: 0 32px 0 10px;
}

.people-scope select:focus-visible {
  outline: 2px solid var(--accent, #2563eb);
  outline-offset: 1px;
}

/* The right-hand end of a person's row: what they can see, and - only when the
   reader may actually change it - what clicking does. */
.people-row-tail {
  align-items: center;
  display: flex;
  flex-shrink: 0;
  gap: 10px;
}

.people-row-edit {
  color: var(--accent, #2563eb);
  font-size: 0.78rem;
  font-weight: 600;
}

/* A row nobody may edit is not pressable, and must not look it. The disabled
   attribute already blocks the click; this is the half a reader can see. */
.people-list .people-row[disabled] {
  cursor: default;
  opacity: 0.85;
}

/* ---------------------------------------------------------------------------
   Dialog furniture, shared by the two People dialogs.

   Both were a bare <h3> over unstyled inputs and a native select. The fields
   below give them one type scale, one control style, and room to say what a
   field is for - which the email field in particular needed, since whether you
   fill it in decides whether a seat is used.
   ------------------------------------------------------------------------ */
.dialog-head {
  display: grid;
  gap: 4px;
  margin-bottom: 16px;
}

.dialog-eyebrow {
  color: var(--muted, #5b6470);
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  margin: 0;
  text-transform: uppercase;
}

.dialog-title {
  font-size: 1.15rem;
  font-weight: 600;
  line-height: 1.25;
  margin: 0;
  text-wrap: balance;
}

.dialog-lede {
  color: var(--muted, #5b6470);
  font-size: 0.85rem;
  line-height: 1.5;
  margin: 0;
}

/* The dialog's fields had no container spacing at all - each .field managed its
   own internal gap, so a field's hint text sat flush against the next field's
   label and read as belonging to it. */
.person-editor-fields {
  display: grid;
  gap: 16px;
}

.field {
  display: grid;
  gap: 5px;
  min-width: 0;
}

.field-label {
  font-size: 0.78rem;
  font-weight: 600;
}

.field-hint {
  color: var(--muted, #5b6470);
  font-size: 0.75rem;
}

.field-input {
  background: var(--surface, #fff);
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--radius-control, 8px);
  color: inherit;
  font: inherit;
  min-height: 38px;
  padding: 0 11px;
  width: 100%;
}

.field-input:focus-visible {
  border-color: var(--accent, #2563eb);
  outline: 2px solid var(--accent, #2563eb);
  outline-offset: -1px;
}

.field-input::placeholder {
  color: var(--muted, #9aa3af);
}

/* A native select keeps the browser's own arrow, which sits at a different size
   and inset in every browser and next to a text input reads as a different
   family of control. Suppressing it and drawing one chevron gives the two the
   same silhouette. Padding-right keeps a long option clear of it. */
.field-select {
  display: block;
  position: relative;
}

/* The chevron itself is global now (see the form-control block above), so this
   keeps only the room for it. The ::after that used to draw one here was removed
   rather than kept: two mechanisms drawing the same arrow means two arrows. */
.field-select select {
  padding-right: 34px;
}

.people-seat-limit {
  background: var(--notice-bg, #fdf1e7);
  border: 1px solid var(--notice-line, #e6c9ab);
  border-radius: 10px;
  display: grid;
  gap: 8px;
  justify-items: start;
  margin: 12px 0 4px;
  padding: 12px 14px;
}

.people-seat-limit p {
  margin: 0;
}

/* display:grid above beats the browser's own [hidden]{display:none}, so without
   this the notice drew as an empty tinted bar whenever seats were fine. */
.people-seat-limit[hidden] {
  display: none;
}

/* The add and edit dialogs are the same shape - the fields pair up two to a row
   so the dialog is read across rather than scrolled down. */
.person-editor-panel {
  max-width: min(620px, calc(100vw - 32px));
  width: min(620px, calc(100vw - 32px));
}

.person-editor-head {
  align-items: start;
  display: flex;
  gap: 12px;
  justify-content: space-between;
}

.person-editor-head h3 {
  margin: 0;
}

.person-editor-grid {
  display: grid;
  gap: 12px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.person-editor-wide {
  grid-column: 1 / -1;
}

/* Remove sits away from Cancel/Save, so the destructive one is never the button
   next to the one somebody meant to press. */
.person-editor-actions [data-person-remove] {
  margin-right: auto;
}

@media (max-width: 620px) {
  .person-editor-grid {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* Buying seats: a quantity and a button whose label carries the total, so the
   price of the decision is on the button rather than worked out afterwards. */
.seat-buy-row {
  align-items: end;
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
}

/* A display value beats the browser's own [hidden] { display: none }, so a
   hidden row draws as an empty gap unless this says otherwise. */
.seat-buy-row[hidden] {
  display: none;
}

.seat-buy-row label {
  display: grid;
  gap: 4px;
}

.seat-buy-row label span {
  color: var(--muted);
  font-size: 0.7rem;
  font-weight: 800;
}

.seat-buy-row input {
  border: 1px solid var(--line);
  border-radius: var(--radius-control, 8px);
  color: inherit;
  font: inherit;
  min-height: 34px;
  padding: 0 10px;
  width: 88px;
}

/* --- "Where do I start?" -------------------------------------------------
   A short ranked list where each row is a whole button: the row IS the action,
   so there is no hunting for a small link inside a paragraph. */

/* The shared .confirm-panel is a plain grid with no height cap - fine for a
   two-line confirmation, not for a list plus an explanation. Without this the
   panel grows past the viewport at both ends and takes its own Close button off
   screen with it. Column + hidden overflow, with the list as the part that
   scrolls. */
.where-to-start-panel {
  display: flex;
  flex-direction: column;
  max-height: min(85vh, 100%);
  max-width: 620px;
  overflow: hidden;
  width: min(620px, 100%);
}

.where-to-start-panel > .person-editor-head,
.where-to-start-panel > .confirm-message,
.where-to-start-panel > .confirm-actions {
  flex: 0 0 auto;
}

/* Everything the reader reads, as one scrolling column between a pinned header
   and a pinned actions row.

   min-height: 0 is the load-bearing half. A flex item's default min-height is
   auto, which means "never shrink below your content" - so without it this
   wrapper reports the full height of the AI answer inside it, the panel's cap
   is exceeded rather than respected, and overflow:hidden above simply cuts the
   buttons off instead of letting anything scroll. That is the exact shape of
   the defect this replaced.

   overscroll-behavior keeps a flick at the end of the answer from carrying on
   into the page behind the modal. */
.copilot-body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  gap: 10px;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
}

/* A shadow only while there IS more below, so the panel says so rather than
   ending in a hard crop that reads as the end of the text. Pure CSS: the
   gradient is painted against the scroller's own background and scrolls out of
   the way once the reader reaches the bottom. */
.copilot-body {
  background:
    linear-gradient(var(--panel, #fff) 30%, rgba(255, 255, 255, 0)) top / 100% 24px no-repeat,
    linear-gradient(rgba(255, 255, 255, 0), var(--panel, #fff) 70%) bottom / 100% 24px no-repeat,
    radial-gradient(farthest-side at 50% 0, rgba(15, 23, 42, 0.14), rgba(0, 0, 0, 0)) top / 100% 10px no-repeat,
    radial-gradient(farthest-side at 50% 100%, rgba(15, 23, 42, 0.14), rgba(0, 0, 0, 0)) bottom / 100% 10px no-repeat;
  background-attachment: local, local, scroll, scroll;
}

/* The list gives up its height first: the explanation underneath is short and
   fixed, and shrinking that instead would hide the very thing just paid for.

   Flex column, NOT grid, and every row pinned to its own height. As a grid this
   compressed instead of scrolling: the panel gives the list a definite height,
   auto grid rows are flexible against a definite height, so six rows split the
   space evenly - measured at 52.8px each on a 390px screen - and each row's
   text ran out of the bottom of its own box onto the row below. A scrolling
   list must never negotiate its rows' heights; it has overflow for that. */
.where-to-start-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 0;
  overflow-y: auto;
}

.where-to-start-row {
  flex: 0 0 auto;
}

.where-to-start-row {
  align-items: start;
  background: #ffffff;
  border: 1px solid var(--line);
  border-left: 4px solid var(--line);
  border-radius: 10px;
  color: inherit;
  display: grid;
  gap: 12px;
  /* Flag, the text, then the action - so the eye reads severity first and
     lands on what to do last, which is the order somebody triages in. */
  grid-template-columns: auto minmax(0, 1fr) auto;
  /* The global button rule is sized for a control, not a block of wrapped
     text - it would hold a two-line row at one line's height. */
  height: auto;
  padding: 12px 14px;
  text-align: left;
  width: 100%;
}

.where-to-start-row:hover {
  background: #fafafa;
}

/* The stripe carries the severity, not the whole row: a list of four filled
   red panels reads as noise, and everything looking urgent is the same as
   nothing looking urgent. */
.where-to-start-critical { border-left-color: #b91c1c; }
.where-to-start-high { border-left-color: #d97706; }
.where-to-start-medium { border-left-color: #ca8a04; }
.where-to-start-low { border-left-color: #a1a1aa; }

.where-to-start-flag {
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.02em;
  padding-top: 2px;
  text-transform: uppercase;
  white-space: nowrap;
}

.where-to-start-critical .where-to-start-flag { color: #b91c1c; }
.where-to-start-high .where-to-start-flag { color: #b45309; }
.where-to-start-medium .where-to-start-flag { color: #a16207; }
.where-to-start-low .where-to-start-flag { color: var(--muted); }

.where-to-start-body {
  display: grid;
  gap: 2px;
  min-width: 0;
}

/* Milestone names here are whatever the imported plan called them, which in
   real workspaces runs to a full sentence. Let them wrap anywhere rather than
   push the row sideways. */
.where-to-start-body strong {
  overflow-wrap: anywhere;
}

.where-to-start-fact,
.where-to-start-project {
  color: var(--muted);
  font-size: 0.85rem;
}

.where-to-start-project {
  font-size: 0.75rem;
}

.where-to-start-action {
  align-self: center;
  color: var(--accent, #0f6b42);
  font-size: 0.8rem;
  font-weight: 600;
  white-space: nowrap;
}

@media (max-width: 620px) {
  /* One column on a phone. Keeping the flag in its own column costs the title
     a third of a 390px screen and leaves it wrapping every three words, so the
     severity becomes an eyebrow above the name instead - same reading order,
     top to bottom rather than left to right. */
  .where-to-start-row {
    gap: 6px;
    grid-template-columns: minmax(0, 1fr);
  }

  .where-to-start-action {
    align-self: start;
    justify-self: start;
  }
}

/* The AI reading of the findings. Visually secondary to the list above it:
   the facts are the product, this is commentary on them. */
.where-to-start-explain {
  background: #f8fafc;
  border: 1px solid var(--line);
  border-radius: 10px;
  display: grid;
  /* Sizes to its content and scrolls nothing of its own.

     It used to be flex: 0 0 auto with a max-height and its own overflow-y. The
     "never gives up height" it was defending was against a ranked list that sat
     above it in this panel and no longer exists - so the rule stopped protecting
     anything and started guaranteeing that a long answer could not fit, on a
     panel where nothing else could shrink either. And a scroller nested in a
     scroller means two scrollbars for one paragraph, which the note this
     replaced had already complained about once.

     .copilot-body above owns the scrolling now. This box just states its size
     and lets the column it sits in do the rest. */
  flex: 0 0 auto;
  gap: 10px;
  padding: 12px 14px;
}

/* The box is a grid, so the gap is the spacing - the paragraphs' own margins
   stack on top of it and were pushing the headline, the reading and the "Start
   with" line about thirty pixels apart, which reads as three unrelated notes
   rather than one answer. One rule owns the rhythm. */
.where-to-start-explain > p {
  margin: 0;
}

.where-to-start-explain[hidden] {
  display: none;
}

.where-to-start-explain-headline {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  font-weight: 600;
  gap: 8px;
}

/* The generated block reads as generated, not just labelled.
   A badge alone is easy to miss in a panel this dense, so the whole answer sits
   against a tinted ground with a rule down its edge - the computed findings
   above it keep the plain surface. Somebody skimming can tell which is the
   product's arithmetic and which is a model's reading of it without stopping to
   look for a two-letter tag. */
.where-to-start-explain {
  border-left: 3px solid var(--accent-brand, #14635a);
}

.where-to-start-explain-reading,
.where-to-start-explain-move {
  color: var(--muted);
  font-size: 0.88rem;
}

.where-to-start-explain-move strong {
  color: var(--ink);
}

/* A list inside theruKO's answer (causes, first moves) - same voice as the
   paragraphs around it. */
.ai-answer-list {
  color: var(--muted);
  display: grid;
  font-size: 0.88rem;
  gap: 4px;
  margin: 0 0 8px;
  padding-left: 18px;
}

/* ---------------------------------------------------------------------------
   The reading: where an AI answer lives now.

   A drawer down the right-hand side rather than a block inside the Copilot.
   The answer ends by telling somebody what to do first, and inside a modal the
   only way to act on it was to dismiss it - so the reading is a surface of its
   own that outlives the panel that asked for it.

   NO BACKDROP, and that is the whole point rather than an omission: the page
   behind stays live, because the reader is meant to work while this is open.
   Nothing here traps focus for the same reason.

   z-index 110 puts it under every dialog layer (section editor 120, date prompt
   130, confirm 150) so that work opened FROM the reading lands in front of it,
   and over ordinary page furniture so it is not lost behind a sticky header.
   --------------------------------------------------------------------- */
.ai-drawer {
  background: var(--panel, #fff);
  border-left: 1px solid var(--line);
  bottom: 0;
  box-shadow: -18px 0 48px rgba(15, 23, 42, 0.12);
  display: flex;
  flex-direction: column;
  max-width: 100%;
  position: fixed;
  right: 0;
  top: 0;
  /* Set on :root by the resize handle and restored from localStorage. The
     literal is the fallback for a first visit and for a browser that refuses
     site data. */
  width: var(--ai-drawer-width, 400px);
  z-index: 110;
}

/* The drag edge. Sits over the column's left border rather than beside it, so
   it costs no width; widened well past the 1px line because a 1px grab target
   is one nobody can hit. */
.ai-drawer-resizer {
  bottom: 0;
  cursor: col-resize;
  left: -3px;
  position: absolute;
  top: 0;
  width: 9px;
  z-index: 2;
}

.ai-drawer-resizer::after {
  background: var(--line);
  bottom: 0;
  content: "";
  left: 3px;
  opacity: 0;
  position: absolute;
  top: 0;
  transition: opacity 0.15s ease;
  width: 3px;
}

.ai-drawer-resizer:hover::after,
.ai-drawer-resizer:focus-visible::after,
body.is-resizing-ai-drawer .ai-drawer-resizer::after {
  background: var(--accent-brand);
  opacity: 1;
}

.ai-drawer-resizer:focus-visible {
  outline: none;
}

/* While dragging: no text selection anywhere, and the pointer keeps its resize
   shape even once it has outrun the handle. */
body.is-resizing-ai-drawer {
  cursor: col-resize;
  user-select: none;
}

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

.ai-drawer-head {
  align-items: center;
  border-bottom: 1px solid var(--line);
  display: flex;
  flex: 0 0 auto;
  gap: 12px;
  justify-content: space-between;
  padding: 16px 18px;
}

/* NOT UPPERCASED, and not letter-spaced like an eyebrow.
   ---------------------------------------------------------------------------
   This was a micro-label describing a panel ("WHAT AI MAKES OF THIS"), and
   uppercase suited that. It now carries a NAME whose capitalisation is the
   point: text-transform rendered theruKO as THERUKO, which is a different word
   and not the product's. A name is set as it is written. */
.ai-drawer-title {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  font-size: 0.92rem;
  font-weight: 600;
  gap: 8px;
  letter-spacing: 0;
  margin: 0;
}

.ai-drawer-head-actions {
  display: flex;
  flex: 0 0 auto;
  gap: 2px;
}

.ai-drawer-collapse {
  background: none;
  border: 0;
  border-radius: 8px;
  color: var(--muted);
  cursor: pointer;
  font-size: 1.4rem;
  line-height: 1;
  padding: 4px 10px;
}

.ai-drawer-collapse:hover,
.ai-drawer-collapse:focus-visible {
  background: var(--panel-strong);
  color: var(--ink);
}

/* Collapsed is still OPEN - the reading is kept and the column is out of the
   way, which is why it goes off the edge rather than being hidden. A hidden
   panel and a collapsed one look identical to a reader; only the tab beside it
   says which this is. */
.ai-drawer.is-collapsed {
  pointer-events: none;
  transform: translateX(100%);
  visibility: hidden;
}

@media (prefers-reduced-motion: no-preference) {
  .ai-drawer {
    transition: transform 180ms ease;
  }
}

/* The handle a collapsed column leaves behind.
   A column that collapsed to nothing would be one the reader could only get
   back by paying for another reading, which is the opposite of what collapsing
   is for. Vertical, against the right edge, out of the way of the content and
   impossible to mistake for part of it. */
.ai-drawer-tab {
  background: var(--panel, #fff);
  border: 1px solid var(--line);
  border-radius: 10px 0 0 10px;
  border-right: 0;
  box-shadow: -8px 0 24px rgba(15, 23, 42, 0.1);
  color: var(--muted);
  cursor: pointer;
  font-size: 0.76rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  padding: 16px 8px;
  position: fixed;
  right: 0;
  text-transform: uppercase;
  top: 50%;
  transform: translateY(-50%);
  writing-mode: vertical-rl;
  z-index: 110;
}

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

.ai-drawer-tab:hover,
.ai-drawer-tab:focus-visible {
  color: var(--ink);
}

/* What moved in Jira, listed under the sentence that explains it.
   Tight and quiet: this is evidence for a decision, not the decision. */
.ai-jira-changes {
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
}

.ai-jira-changes li {
  border-top: 1px solid var(--line);
  display: flex;
  flex-direction: column;
  font-size: 0.85rem;
  gap: 2px;
  padding: 8px 0;
}

.ai-jira-changes li span {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.ai-jira-groups {
  display: flex;
  flex-direction: column;
  gap: 14px;
  margin-top: 12px;
}

.ai-jira-group {
  border: 1px solid var(--line);
  border-radius: 10px;
  padding: 12px 14px;
}

.ai-jira-group.is-accepted {
  /* Done, and visibly so. A group that looks identical after it has been
     recorded invites a second press and a duplicate risk. */
  background: var(--panel-strong);
  opacity: 0.75;
}

.ai-jira-group-reason {
  font-weight: 600;
  margin: 0 0 8px;
}

.ai-jira-group-meta {
  align-items: center;
  color: var(--muted);
  display: flex;
  flex-wrap: wrap;
  font-size: 0.8rem;
  gap: 8px;
  margin: 0;
}

/* How old a generated panel is. The reading's own age and staleness moved onto
   its entry header (.ai-reading-when / .ai-reading-stale) once the column kept
   a history, because the entry markup is stored and anything computed inside it
   is frozen at write time. This is the recommendations panel's line. */
.ai-drawer-age {
  border-top: 1px solid var(--line);
  color: var(--muted);
  font-size: 0.8rem;
  margin: 14px 0 0;
  padding-top: 10px;
}

/* Modals stop at the column's edge.
   ---------------------------------------------------------------------------
   The column exists so somebody can act on what the AI said while still seeing
   it. Every dialog here is inset: 0 with a translucent backdrop, so the moment
   one opened the column went behind a dimming sheet, stopped taking clicks, and
   the first click aimed at it landed on the backdrop and dismissed the dialog -
   the exact loop the column was built to end. Following the advice hid the
   advice.

   Narrowing the backdrop rather than raising the column is deliberate. Lifting
   .ai-drawer above 150 would float it OVER a confirm dialog, where it reads as
   part of the question being asked. Stopping the sheet at the column's edge
   instead leaves the rule as it should be read: the page dims and answers a
   click, the column does neither.

   A .confirm-overlay centres its panel with flex, so the dialog re-centres in
   what is left - which is right, since that is the part of the screen it is
   actually covering. */
body.has-ai-drawer .section-editor-window,
body.has-ai-drawer .confirm-overlay {
  right: var(--ai-drawer-width, 400px);
}

/* And the PANEL inside it, not only the sheet behind it.
   -------------------------------------------------------------------------
   Narrowing the backdrop alone was not enough, and the gap is the kind that
   looks fixed in a screenshot taken one inch to the left: .section-editor-panel
   sizes itself off the VIEWPORT (`width: min(920px, calc(100vw - 56px))`,
   `max-width: 96vw`), so with the sheet stopping at the column the panel simply
   overhung it - measured at 1120px wide inside a 1040px window, 104px of dialog
   sitting on top of the reading it was opened from.

   The cap is in VIEWPORT units, matching how the rules it is capping already
   think. A percentage was tried first and resolved against 1120px rather than
   the 1040px window - the panel's containing block is not the sheet, so `100%`
   answered a question about the wrong box and the overhang survived at 1128px.
   Subtracting the column from the viewport asks the only question that matters
   here, and covers every variant - the wide one the Gantt and milestone-focus
   modals use, and any size somebody has dragged the panel to - without
   restating a single one of their widths. */
/* FLOORED, because a subtraction can go negative and a max-width cannot argue.
   -------------------------------------------------------------------------
   Measured on a 390px phone with the column open: this resolved to
   `calc(390px - 400px - 32px)`, which is -42px, which clamps to 0 - and the
   dialog rendered 46px wide with one word per line. A reader pressing "Take me
   to it" got a white sliver.

   max() means the cap can never be tighter than a dialog can be read at, at
   any viewport and at any width somebody has dragged the column to. The
   subtraction still wins wherever it is the smaller, sensible answer. */
body.has-ai-drawer .section-editor-panel,
body.has-ai-drawer .confirm-overlay .confirm-panel {
  max-width: max(280px, calc(100vw - var(--ai-drawer-width, 400px) - 32px));
}

@media (max-width: 720px) {
  /* The column is a bottom sheet at this width, so there is no left-hand strip
     to preserve - a 400px inset would leave a dialog 30px wide. */
  body.has-ai-drawer .section-editor-window,
  body.has-ai-drawer .confirm-overlay {
    right: 0;
  }

  /* AND THE PANEL, which the rule above never covered - that was the whole
     bug. The sheet was un-inset here from the day this was written, but the
     panel's max-width kept subtracting a 400px column that is not beside it at
     this width: at 390px the dialog was capped to nothing while the backdrop
     behind it was correctly full-width. Fixed in a screenshot one inch to the
     left, invisible in the one that mattered. */
  body.has-ai-drawer .section-editor-panel,
  body.has-ai-drawer .confirm-overlay .confirm-panel {
    max-width: calc(100vw - 32px);
  }
}

/* Under a genuine confirmation the column stays READABLE but stops ACTING.
   A confirm is a momentary yes/no, and somebody who navigated away from the
   column mid-question would be answering it about a screen they had just left.
   Scrolling, switching tabs and reading all keep working; only the controls
   that would move them are held. The quick-update editor is deliberately not
   included - it is a panel, not a question, and working beside it is the whole
   point. */
body:has(.confirm-overlay) .ai-drawer-action,
body:has(.confirm-overlay) .ai-drawer-body [data-take-action-type] {
  opacity: 0.5;
  pointer-events: none;
}

/* The tab strip. Hidden entirely when there is only one panel: a row of one is
   furniture that teaches nothing, and the column simply shows the thing. */
.ai-drawer-tabs {
  border-bottom: 1px solid var(--line);
  display: flex;
  flex: 0 0 auto;
  gap: 4px;
  overflow-x: auto;
  padding: 8px 12px 0;
}

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

.ai-drawer-tab-button {
  background: none;
  border: 0;
  /* The underline is the selected state, so it is on every tab and only
     coloured on the active one - a border appearing on selection would shift
     the others by 2px every time somebody switched. */
  border-bottom: 2px solid transparent;
  color: var(--muted);
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 0.85rem;
  font-weight: 600;
  padding: 8px 10px;
  white-space: nowrap;
}

.ai-drawer-tab-button:hover,
.ai-drawer-tab-button:focus-visible {
  color: var(--ink);
}

.ai-drawer-tab-button.is-active {
  border-bottom-color: var(--accent, #0f6b42);
  color: var(--ink);
}

/* One reading in the history.
   Folded to its stamp unless it is the open one, so five answers read as a
   record somebody scans rather than five paragraphs nobody does. */
.ai-reading-entry {
  border-bottom: 1px solid var(--line);
  padding: 10px 0;
}

.ai-reading-entry:first-child {
  padding-top: 0;
}

.ai-reading-entry:last-child {
  border-bottom: 0;
}

.ai-reading-entry-head {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
}

.ai-reading-toggle {
  align-items: baseline;
  background: none;
  border: 0;
  /* The global button rule gives every button a radius and a lift. Both are
     wrong on a line of provenance: they drew a faint rounded card under each
     stamp, so the record read as a row of empty buttons rather than as dates. */
  border-radius: 0;
  box-shadow: none;
  color: var(--muted);
  cursor: pointer;
  display: flex;
  /* ONE LINE. The same global rule sets a 42px minimum height and bold type,
     which is a filled button's shape - on a list of one-liners it doubled the
     height of every folded entry. */
  font-size: 0.8rem;
  font-weight: 400;
  gap: 8px;
  min-height: 0;
  padding: 2px 0;
  text-align: left;
  width: 100%;
}

/* Which way it opens, drawn rather than typed so it cannot fall back to a
   missing glyph. Turns when open, so the state reads without aria. */
.ai-reading-toggle::before {
  align-self: center;
  border-bottom: 1.5px solid currentColor;
  border-right: 1.5px solid currentColor;
  content: "";
  flex: none;
  height: 5px;
  margin: 0 3px 0 1px;
  transform: rotate(-45deg);
  transition: transform 120ms ease;
  width: 5px;
}

.ai-reading-toggle[aria-expanded="true"]::before {
  transform: rotate(45deg);
}

@media (prefers-reduced-motion: reduce) {
  .ai-reading-toggle::before {
    transition: none;
  }
}

.ai-reading-toggle:hover,
.ai-reading-toggle:focus-visible {
  color: var(--ink);
}

/* What the entry was - the one line the fold keeps. Ellipsised rather than
   wrapped, because a history that wraps is a history of uneven paragraphs;
   the whole sentence is the button's title, and one press opens it. */
.ai-reading-summary {
  color: var(--ink);
  flex: 1 1 auto;
  font-size: 0.86rem;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* Your half of the exchange, told apart from the AI's at a glance - the
   column holds both, and a list of one-liners loses the bubble that did it. */
.ai-reading-who {
  color: var(--muted);
  font-weight: 600;
  margin-right: 6px;
}

.ai-reading-when {
  color: var(--muted);
  flex: none;
  font-size: 0.75rem;
}

/* The open entry's header is when and where; the folded one's is what. So
   open, the toggle shrinks to its time and the stamp sits beside it. */
.ai-reading-entry.is-open .ai-reading-toggle {
  flex: 0 1 auto;
  width: auto;
}

.ai-reading-open {
  min-width: 0;
  padding-left: 17px;
}

/* Every box between the column and a one-line row has to be allowed to be
   narrower than its content. A flex or grid child defaults to min-width:auto,
   which is the width of its longest unbroken line - so a nowrap row sized its
   own container, the container sized the next, and the text ran off the side
   of the column instead of ending in an ellipsis. */
.ai-reading-entry,
.ai-reading-entry-head,
.ai-reading-body,
.ai-applied-list,
.ai-applied-list li,
.ai-quick-wins,
.ai-quick-wins-list,
.ai-quick-wins-list li {
  max-width: 100%;
  min-width: 0;
}

/* What an update changed, one line each, each a way to it. */
.ai-applied-list {
  display: flex;
  flex-direction: column;
  gap: 1px;
  list-style: none;
  margin: 10px 0 0;
  padding: 0;
}

.ai-applied-item {
  align-items: baseline;
  background: none;
  border: 0;
  border-radius: 6px;
  box-shadow: none;
  color: inherit;
  cursor: pointer;
  display: flex;
  font-weight: 400;
  gap: 8px;
  min-height: 0;
  padding: 5px 6px;
  text-align: left;
  width: 100%;
}

.ai-applied-item:not(.is-static):not(.is-gone):hover,
.ai-applied-item:not(.is-static):not(.is-gone):focus-visible {
  background: color-mix(in srgb, var(--accent-brand) 10%, transparent);
  outline: none;
}

/* A chevron only on the ones that go somewhere, so a removal - which has
   nowhere left to go - does not look like a link that is broken. */
button.ai-applied-item:not(.is-gone)::after {
  color: var(--muted);
  content: "\203A";
  flex: none;
  margin-left: auto;
}

.ai-applied-name {
  color: var(--ink);
  flex: 0 1 auto;
  font-size: 0.86rem;
  font-weight: 600;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.ai-applied-fact {
  color: var(--muted);
  flex: none;
  font-size: 0.78rem;
}

.ai-applied-item.is-static {
  cursor: default;
}

.ai-applied-item.is-gone {
  cursor: default;
}

.ai-applied-item.is-gone .ai-applied-name {
  color: var(--muted);
  text-decoration: line-through;
}

/* --warn, not a --warn-strong invented here: this palette has no such token, so
   the fallback would apply and the rule would stop tracking the brand the
   moment somebody changed it. That has happened in this stylesheet before. */
.ai-reading-stale {
  color: var(--warn);
}

/* What a background job actually changed, itemised.
   A row is the thing that moved and what it moved to, so the reader can see the
   model's work rather than a sentence claiming there was some. */
.ai-job-reading-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  list-style: none;
  margin: 12px 0 0;
  padding: 0;
}

.ai-job-reading-list li {
  border-left: 2px solid var(--line);
  display: flex;
  flex-direction: column;
  gap: 2px;
  padding-left: 10px;
}

/* --ink, not a --text invented here. This stylesheet has no --text token, so
   the declaration would silently fall back to the inherited colour and stop
   tracking the theme - which happened in this file more than once. The last
   of those references were mapped to --ink, and a test now fails on any
   var(--x) whose token is defined nowhere (no-invented-colour-tokens). */
.ai-job-reading-item {
  color: var(--ink);
  font-size: 0.86rem;
  line-height: 1.35;
}

.ai-job-reading-label {
  color: var(--muted);
  font-size: 0.78rem;
}

/* What the column no longer holds. A quiet line under the record rather than a
   warning: nothing has gone wrong, this is how long it keeps things, and the
   reader is better off knowing the rule than wondering where an answer went. */
.ai-reading-forgotten {
  border-top: 1px solid var(--line);
  margin-top: 14px;
  padding-top: 10px;
}

/* The link back. Quiet, because it is provenance rather than an instruction -
   but a button, because it goes somewhere. */
.ai-reading-scope {
  background: none;
  border: 0;
  border-bottom: 1px dashed var(--line-strong, var(--line));
  border-radius: 0;
  box-shadow: none;
  color: var(--muted);
  cursor: pointer;
  font-size: 0.78rem;
  /* The global button rule's bold and 42px minimum turned a line of
     provenance into the loudest thing in the entry, with a gap under it. */
  font-weight: 400;
  min-height: 0;
  padding: 0 0 1px;
}

.ai-reading-scope:hover,
.ai-reading-scope:focus-visible {
  border-bottom-style: solid;
  color: var(--accent, #0f6b42);
}

/* Already here. Marked rather than hidden, so the connection between an answer
   and the screen it was about is passive - nothing to work out, and no click
   needed to discover it goes nowhere new. */
.ai-reading-scope.is-here {
  border-bottom-color: transparent;
  color: var(--accent, #0f6b42);
  cursor: default;
}

.ai-reading-body {
  margin-top: 8px;
}

.ai-drawer-panel-lead {
  color: var(--muted);
  font-size: 0.8rem;
  letter-spacing: 0.04em;
  margin: 0 0 10px;
  text-transform: uppercase;
}

.ai-drawer-panel-summary {
  font-weight: 600;
  margin: 0 0 12px;
}

/* The recommendation cards were built for a wide page and land here in a 400px
   column, so the priority chip stops sitting beside the text and sits above it.
   Same markup, same classes - only the shape of the box changed. */
.ai-drawer-body .ai-recommendation-card {
  display: block;
}

.ai-drawer-body .ai-recommendation-card .recommendation-priority {
  margin-bottom: 6px;
}

/* The scroller, for the same reason the Copilot has one: an answer can be any
   length and the head must not be pushed off the top of it. min-height: 0
   because a flex item refuses to shrink below its content without it. */
.ai-drawer-body {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  padding: 18px;
}

/* The move the answer named, as something to press. The model already picks the
   finding and the server resolves it to a real id, so a reader should not have
   to translate a sentence back into a click of their own. */
.ai-drawer-action {
  margin-top: 4px;
  width: 100%;
}

/* While the text is still arriving. A caret on the last line, so a paused
   stream reads as "still writing" rather than as a finished short answer. */
.where-to-start-explain.is-writing .where-to-start-explain-reading::after,
.where-to-start-explain.is-writing .where-to-start-explain-headline:only-child::after {
  animation: ai-caret 1.1s steps(1, end) infinite;
  content: "";
  display: inline-block;
  height: 1em;
  margin-left: 2px;
  vertical-align: -0.15em;
  width: 2px;
}

@keyframes ai-caret {
  0%, 49% { background: var(--accent, #0f6b42); }
  50%, 100% { background: transparent; }
}

@media (prefers-reduced-motion: reduce) {
  .where-to-start-explain.is-writing .where-to-start-explain-reading::after,
  .where-to-start-explain.is-writing .where-to-start-explain-headline:only-child::after {
    animation: none;
    background: var(--accent, #0f6b42);
  }
}

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

/* The page makes room rather than being covered.
   ---------------------------------------------------------------------------
   An overlay drawer would sit ON the right-hand side of the content - which on
   this app means on the Gantt, the risk map and the right-hand column of every
   table. The reading is meant to be consulted WHILE working, so the work has to
   remain visible: the shell gives up the width instead.

   Padding rather than a width, so nothing inside has to know: the shell's own
   children keep their layout and simply have less room. */
body.has-ai-drawer .dashboard-app-shell {
  padding-right: var(--ai-drawer-width, 400px);
}

/* A phone has no room for a column beside the work, so it becomes a sheet at
   the bottom - still not a modal, still leaving the page live above it. The
   shell takes its room back: at this width the sheet is over the content, and
   400px of padding on a 430px screen would leave a column 30px wide. */
@media (max-width: 720px) {
  body.has-ai-drawer .dashboard-app-shell {
    padding-right: 0;
  }
}

@media (max-width: 720px) {
  .ai-drawer {
    border-left: 0;
    border-top: 1px solid var(--line);
    box-shadow: 0 -18px 48px rgba(15, 23, 42, 0.16);
    left: 0;
    max-height: 70vh;
    top: auto;
    width: auto;
  }
}

/* ---------------------------------------------------------------------------
   Waking the database.

   Shown only when a request passes SLOW_REQUEST_NOTICE_MS (see dashboard.js),
   which on a warm database is never. Deliberately plain: this is a status, not
   a decision, so it carries no close button and no backdrop dismissal - there
   is nothing to choose yet, and an exit offered here would be taken half a
   second before the answer lands.
   --------------------------------------------------------------------- */
.cold-start-overlay {
  /* Above the dialogs (150) because it reports on the request those dialogs are
     waiting for, and below the system notice (10000) which reports refusals. */
  z-index: 200;
}

.cold-start-panel {
  align-items: center;
  display: flex;
  flex-direction: column;
  gap: 12px;
  max-width: min(420px, 100%);
  text-align: center;
}

.cold-start-panel h3 {
  margin: 0;
}

.cold-start-spinner {
  animation: cold-start-spin 900ms linear infinite;
  border: 3px solid var(--line, #d4d4d8);
  border-radius: 50%;
  border-top-color: var(--accent, #0f6b42);
  height: 28px;
  width: 28px;
}

@keyframes cold-start-spin {
  to { transform: rotate(360deg); }
}

/* A spinner that cannot stop is worse than none for anyone who finds motion
   uncomfortable - the text says everything the animation does. */
@media (prefers-reduced-motion: reduce) {
  .cold-start-spinner {
    animation: none;
    border-top-color: var(--line, #d4d4d8);
  }
}
/* ---------------------------------------------------------------------------
   Seat billing periods: two options, one line each.

   This was a <select>, then a four-column comparison table (period / price /
   you pay / you save). The table was the wrong amount of furniture for the
   decision: three of its columns restated things stated elsewhere - the total
   is on the buy button, and "saves $X a year" is the same fact as the Save N%
   badge. What is left is the one figure the two periods actually differ on,
   normalised so they can be compared at all: cost per seat per month.

   Two cards side by side, radio-backed. See seatCycleChoiceHtml.
   ------------------------------------------------------------------------ */
.seat-cycle-options {
  display: grid;
  gap: 8px;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  margin: 0 0 12px;
}

.seat-cycle-option {
  align-items: flex-start;
  background: var(--surface, #fff);
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--radius-control, 10px);
  cursor: pointer;
  display: flex;
  gap: 8px;
  padding: 10px 12px;
  transition: border-color 0.12s ease, box-shadow 0.12s ease;
}

.seat-cycle-option:hover {
  border-color: var(--accent, #2563eb);
}

/* The selected period is the one the button is about to charge for, so it is
   marked on the card itself rather than only by the radio dot. :has is what
   makes that possible without a JS class toggle; browsers without it still get
   the radio, which is the accessible state either way. */
.seat-cycle-option:has(input:checked) {
  border-color: var(--accent, #2563eb);
  box-shadow: inset 0 0 0 1px var(--accent, #2563eb);
}

.seat-cycle-option:focus-within {
  outline: 2px solid var(--accent, #2563eb);
  outline-offset: 2px;
}

.seat-cycle-option input {
  margin: 3px 0 0;
}

.seat-cycle-option-body {
  display: grid;
  gap: 2px;
  min-width: 0;
}

.seat-cycle-option-head {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.seat-cycle-option-label {
  font-weight: 600;
}

.seat-cycle-option-price {
  /* Digits line up between the two cards, which is the whole point of showing
     the periods next to each other. */
  font-variant-numeric: tabular-nums;
  font-size: 1.15rem;
  line-height: 1.2;
  white-space: nowrap;
}

.seat-cycle-unit {
  color: var(--muted, #6b7280);
  font-size: 0.75rem;
  font-weight: 400;
  margin-left: 2px;
}

.seat-cycle-option-billed {
  color: var(--muted, #6b7280);
  font-size: 0.75rem;
}

.seat-cycle-badge {
  background: var(--accent-soft, rgba(37, 99, 235, 0.12));
  border-radius: 999px;
  color: var(--accent, #2563eb);
  font-size: 0.68rem;
  font-style: normal;
  font-weight: 600;
  padding: 0.1rem 0.4rem;
  white-space: nowrap;
}

/* Narrow screens: stacked, because two price cards squeezed side by side stop
   being readable long before they stop fitting. */
@media (max-width: 520px) {
  .seat-cycle-options {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ---------------------------------------------------------------------------
   Buying seats, as a place money changes hands.

   The purchase used to be a bare number input and a secondary button inside the
   same tinted notice as the seat count, so it read as part of a status message.
   It is its own bounded panel now, with a heading, the primary button, and a
   line saying a checkout opens - so nobody presses it without knowing it
   charges. See renderSeatLimitNotice.
   ------------------------------------------------------------------------ */
.seat-purchase {
  background: var(--surface, #fff);
  border: 1px solid var(--line, #e5e7eb);
  border-radius: var(--radius-row, 12px);
  display: grid;
  gap: 4px;
  margin-top: 12px;
  padding: 14px;
}

/* A grid display beats the browser's own [hidden] { display: none }, so without
   this the add-a-member dialog draws an empty bordered box whenever the seat
   purchase is not needed - the same collision the seat limit notice hit. */
.seat-purchase[hidden] {
  display: none;
}

.seat-purchase-heading {
  font-size: 0.95rem;
  font-weight: 600;
  margin: 0 0 8px;
}

.seat-purchase-note {
  color: var(--muted, #6b7280);
  font-size: 0.78rem;
  margin: 6px 0 0;
}

.seat-buy-button {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* ---------------------------------------------------------------------------
   The account access picker.

   Three choices, and each one's sub-line says whether a project created later
   is included - which is the entire difference between them, and the thing a
   dropdown of scope names would hide. See accessPickerHtml.
   ------------------------------------------------------------------------ */
.access-scope {
  border: 1px solid var(--line, #e5e7eb);
  border-radius: 0.5rem;
  padding: 0.85rem 1rem 1rem;
  margin: 0;
}

.access-scope legend {
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--muted, #6b7280);
  padding: 0 0.35rem;
}

.access-choice {
  display: flex;
  align-items: flex-start;
  gap: 0.6rem;
  padding: 0.5rem 0;
  cursor: pointer;
}

.access-choice span {
  display: flex;
  flex-direction: column;
  gap: 0.1rem;
}

/* The line that answers "will they see a project added next week". Quieter than
   the label but never hidden - it is the actual decision being made. */
.access-choice em {
  font-style: normal;
  font-size: 0.8rem;
  color: var(--muted, #6b7280);
}

/* Shown only under its own selected radio: three open checkbox lists is a wall,
   and ticking boxes beneath an unselected heading looks like it counts. */
.access-list {
  display: flex;
  flex-direction: column;
  gap: 0.3rem;
  margin: 0 0 0.5rem 1.6rem;
  padding: 0.5rem 0.6rem;
  border-left: 2px solid var(--line, #e5e7eb);
  max-height: 12rem;
  overflow-y: auto;
}

.access-option {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  font-size: 0.9rem;
  cursor: pointer;
}

/* MUST come after .access-list's own display rule.
 *
 * `hidden` is only `display: none` from the user-agent stylesheet, so any class
 * that sets display - like .access-list above - silently beats it. The JS was
 * correctly setting el.hidden and both lists stayed on screen anyway, which
 * looked exactly like the show/hide logic being broken. Caught by screenshotting
 * the result rather than by asserting on the property, which was true. */
.access-list[hidden] {
  display: none;
}

/* Purchase history on Billing & Data.
   A customer's own record of what they paid for - the reference column is the
   string Lemon Squeezy shows on their statement, so it stays readable and
   selectable rather than being truncated to fit. */
.billing-purchases {
  margin-top: 18px;
}

.purchase-history {
  width: 100%;
  border-collapse: collapse;
  margin-top: 10px;
  font-size: 0.92rem;
}

.purchase-history th {
  text-align: left;
  font-weight: 500;
  color: var(--muted);
  padding: 6px 10px 6px 0;
  border-bottom: 1px solid var(--line);
}

.purchase-history td {
  padding: 8px 10px 8px 0;
  border-bottom: 1px solid var(--line);
  vertical-align: top;
}

/* A refunded line, recessed rather than struck through: the money moved twice
   and both movements are real, so this is history rather than a mistake. Struck
   text would read as "this never happened", which is exactly the thing a card
   statement disagrees with. Colour is not the only signal - the Status column
   says "Refunded" in words. */
.purchase-history tr.purchase-refunded td {
  color: var(--muted);
}

.purchase-reference {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 0.82rem;
  overflow-wrap: anywhere;
  color: var(--muted);
}

/* The circular-dependency dialog.
   ---------------------------------------------------------------------------
   A refused save used to be one line of status text naming no milestone. This
   is the loop itself, so the numbered chain is the content rather than
   decoration: each step is a milestone, and the last one closes back to the
   first. */
.cycle-dialog-lead { margin: 0 0 14px; }

.cycle-chain {
  display: flex;
  flex-direction: column;
  gap: 10px;
  list-style: none;
  margin: 0 0 16px;
  padding: 0;
}

.cycle-chain li {
  border-left: 3px solid var(--danger-line);
  display: grid;
  gap: 2px 10px;
  grid-template-columns: 1fr auto;
  padding: 8px 0 8px 12px;
}

.cycle-step {
  align-items: baseline;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* The project only matters when the loop leaves this one, but showing it always
   costs a word and saves the reader working out which "Cutover" this is. */
.cycle-step em {
  color: var(--muted);
  font-size: 0.78rem;
  font-style: normal;
}

.cycle-waits {
  color: var(--muted);
  font-size: 0.85rem;
  grid-column: 1;
}

.cycle-chain button { align-self: start; grid-column: 2; grid-row: 1; }

/* Not a button: leaving the link unset is what happens if you do nothing, so
   this should not look like the thing to click. */
.cycle-pending {
  align-self: start;
  color: var(--muted);
  font-size: 0.78rem;
  grid-column: 2;
  grid-row: 1;
}

.cycle-dialog-note {
  background: var(--panel);
  border-radius: var(--radius-control);
  color: var(--muted);
  font-size: 0.85rem;
  margin: 0 0 16px;
  padding: 12px 14px;
}

.cycle-dialog-actions { align-items: center; display: flex; gap: 12px; justify-content: flex-end; }

/* ---------------------------------------------------------------------------
   The Copilot's single step.

   Shares the severity stripe and type with the rows it replaced, so the two
   read as the same product - what changes is that there is ONE of them and it
   is not itself a button. The action lives in the action row underneath, where
   a reader looks for it, rather than being hidden in a click on the text.
   ------------------------------------------------------------------------ */
.copilot-step {
  align-items: start;
  background: var(--surface, #ffffff);
  border: 1px solid var(--line);
  border-left: 4px solid var(--line);
  border-radius: 10px;
  display: grid;
  gap: 12px;
  grid-template-columns: auto minmax(0, 1fr);
  margin: 0 0 4px;
  padding: 14px 16px;
}

.copilot-step-critical { border-left-color: #b91c1c; }
.copilot-step-high { border-left-color: #d97706; }
.copilot-step-medium { border-left-color: #ca8a04; }
.copilot-step-low { border-left-color: #a1a1aa; }

.copilot-step-critical .where-to-start-flag { color: #b91c1c; }
.copilot-step-high .where-to-start-flag { color: #b45309; }
.copilot-step-medium .where-to-start-flag { color: #a16207; }
.copilot-step-low .where-to-start-flag { color: var(--muted); }

.copilot-step-body {
  display: grid;
  gap: 3px;
  min-width: 0;
}

.copilot-step-body strong {
  font-size: 1.02rem;
  overflow-wrap: anywhere;
}

/* What continuity adds, and it is set apart from the fact above it on purpose:
   the fact is what is true, this is what we remember about it, and running them
   together would read as one longer sentence about the milestone. */
.copilot-step-history {
  color: var(--muted);
  font-size: 0.78rem;
  font-style: italic;
  padding-top: 2px;
}

/* Take me there first and largest; everything else is a way out. The row wraps
   rather than compressing, since on a narrow screen four controls in one line
   become four unreadable ones. */
.copilot-actions {
  flex-wrap: wrap;
}

/* Why the Copilot appeared, when nobody asked it to. Set above the subtitle and
   given the accent rather than the muted grey the subtitle wears: it is the
   answer to "what just happened", and a reader who cannot find that answer
   quickly learns to close this without reading any of it. */
.copilot-because {
  color: var(--accent, #0f6b42);
  font-size: 0.9rem;
  font-weight: 600;
  margin: 0 0 2px;
}

/* ---------------------------------------------------------------------------
   The calibration finding: the one thing here that is about the reader.

   Set apart from the plan lines below it, because it is a different KIND of
   claim - everything else in this panel is about what needs doing, and this is
   an observation about how this team's dates behave, counted from their own
   history. A rule and a tinted ground rather than a card: it should read as an
   aside in the same conversation, not as a second panel competing with the step
   the Copilot is asking about.

   No AI badge, deliberately. The badge means "this calls a model"; this is
   arithmetic over rows the workspace already holds, and the basis line says so
   in words. Inventing a second badge for one feature would add a visual
   language nothing else in the product speaks.
   ------------------------------------------------------------------------ */
.copilot-calibration {
  /* --accent-soft, not an invented --panel-soft. The first draft of this rule
     named a variable that does not exist anywhere in the stylesheet, which does
     not fail - it silently falls through to the literal beside it, so the block
     stops tracking the palette while looking perfectly fine in the one theme it
     was written in. The test below this file's siblings now checks that every
     custom property a calibration rule reads is actually defined. */
  background: var(--accent-soft, #e8edf2);
  border-left: 3px solid var(--accent, #0f6b42);
  border-radius: 0 6px 6px 0;
  padding: 12px 14px;
  margin: 0 0 14px;
}

.copilot-calibration-eyebrow {
  color: var(--accent, #0f6b42);
  font-size: 0.72rem;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  margin: 0 0 4px;
}

.copilot-calibration-headline {
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.35;
  margin: 0 0 4px;
}

.copilot-calibration-detail {
  font-size: 0.9rem;
  line-height: 1.45;
  margin: 0 0 6px;
}

/* The answer to "how do you know that?", which is the first thing a sceptical
   reader looks for. Quiet, because it is a footnote rather than the claim - but
   present, because a number that cannot say what it counted is a number people
   stop trusting the moment it surprises them. */
.copilot-calibration-basis {
  color: var(--muted, #5f6b66);
  font-size: 0.78rem;
  line-height: 1.4;
  margin: 0 0 6px;
}

.copilot-calibration-dismiss {
  font-size: 0.78rem;
}

/* ---------------------------------------------------------------------------
   The Copilot's summary lines: the other things worth a look.

   Text rows rather than cards or buttons-that-look-like-buttons. They sit above
   the one thing being asked about, and anything with more visual weight than
   this would compete with it - the step is the point, these are the alternatives
   to it.
   ------------------------------------------------------------------------ */
.copilot-summary {
  border-top: 1px solid var(--line);
  display: grid;
  gap: 0;
  list-style: none;
  margin: 0 0 12px;
  padding: 0;
}

.copilot-summary li {
  border-bottom: 1px solid var(--line);
}

.copilot-summary button {
  align-items: baseline;
  background: none;
  border: 0;
  border-radius: 0;
  color: inherit;
  display: flex;
  gap: 12px;
  height: auto;
  justify-content: space-between;
  padding: 9px 2px;
  text-align: left;
  width: 100%;
}

.copilot-summary button:hover .copilot-summary-label {
  text-decoration: underline;
}

.copilot-summary-label {
  font-size: 0.88rem;
}

/* The number is the reason to click, so it is set apart rather than run into
   the label - but muted, because the label is what says what it is about. */
.copilot-summary-detail {
  color: var(--muted);
  font-size: 0.78rem;
  white-space: nowrap;
}

.copilot-lane-back {
  font-size: 0.78rem;
  margin: 0 0 10px;
}

/* The support view banner.
   ---------------------------------------------------------------------------
   Deliberately the loudest thing the app can draw. An admin reading somebody
   else's workspace has no other visible signal that this is not their own - the
   URL looks ordinary and the refused writes only speak when something is
   attempted - so this is fixed to the top of the window, high contrast, and
   pushes the page down rather than floating over it. A banner that covers the
   first thing on the page is a banner people learn to close. */
.support-banner {
  align-items: center;
  background: #7f1d1d;
  color: #fff;
  display: flex;
  flex-wrap: wrap;
  font-size: 0.82rem;
  gap: 10px;
  left: 0;
  padding: 8px 16px;
  position: fixed;
  right: 0;
  top: 0;
  z-index: 10000;
}

.support-banner-tag {
  background: rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 2px 10px;
  text-transform: uppercase;
}

.support-banner-text {
  flex: 1;
  min-width: 220px;
}

.support-banner-exit {
  background: #fff;
  border-radius: 999px;
  color: #7f1d1d;
  font-weight: 600;
  padding: 4px 12px;
  text-decoration: none;
  white-space: nowrap;
}

/* The strip is fixed, so the page needs the room back or the banner sits on top
   of the app's own header. */
body.has-support-banner {
  padding-top: 42px;
}

/* The notes composer's box. The Home card that used to hold it - and the
   classes that drew it - is gone; theruKO's paste control opens the panel. */
.plan-notes-input {
  border: 1px solid var(--line);
  border-radius: 8px;
  font: inherit;
  margin-top: 10px;
  padding: 10px 12px;
  resize: vertical;
  width: 100%;
}

/* The box in the panel, where the height is the point rather than the problem.
   A 45-minute transcript is ~37,000 characters; watching that land in four
   lines tells the reader nothing about whether they pasted the right thing.
   min-height rather than rows alone so a narrow window cannot shrink it back
   to the shape this change was made to get rid of. */
.plan-notes-input-tall {
  min-height: 300px;
}

/* The sentence from the reader's own notes that produced a change. Quoted
   rather than paraphrased: the whole claim this screen makes is "your text said
   this", and a paraphrase is not evidence. */
.plan-notes-quote {
  border-left: 3px solid var(--line-strong, #cbd5e1);
  color: #4a5b57;
  font-style: italic;
  margin: 8px 0 0;
  padding-left: 10px;
}

/* The reader's own sentence, and it must not look like the app's prose.
   ---------------------------------------------------------------------------
   Set upright rather than italic, against a tinted ground, because this is the
   one line on the screen that is a quotation and every other line is the
   product talking. The italic above stays on the AI's paraphrase, so the two are
   told apart by more than the words - which is the whole point of quoting
   verbatim in the first place. Quote marks are drawn in CSS rather than typed
   into the string, so the text stays exactly what was pasted. */
.plan-notes-quote-verbatim {
  background: var(--surface-soft, #f6f8f7);
  border-left-color: var(--accent-brand);
  border-radius: 0 8px 8px 0;
  font-style: normal;
  padding: 8px 12px 8px 12px;
}

.plan-notes-quote-verbatim::before {
  content: "\201C";
}

.plan-notes-quote-verbatim::after {
  content: "\201D";
}

/* What else moves, stated as a consequence rather than a warning: nothing here
   is wrong, it is simply larger than one milestone. */
.plan-notes-knockon {
  background: #fffbeb;
  border: 1px solid #fde68a;
  border-radius: 8px;
  color: #713f12;
  font-size: 0.82rem;
  margin: 10px 0 0;
  padding: 10px 12px;
}

/* Inline, because one of these sits INSIDE the list of names - "Go-live gate
   (Payments API v2)" - and a block rule here broke that line in two and left
   the sentence's full stop stranded at the start of the next one. Found by
   screenshotting the step rather than by reading the rule, which is the only
   way this kind of thing shows up. */
.plan-notes-knockon .admin-meta {
  color: inherit;
  opacity: 0.8;
}

/* The one that really is its own line: the promise that nothing downstream is
   being rewritten. It is the reassurance, so it wants separating from the list
   it reassures about. */
.plan-notes-knockon-note {
  display: block;
  margin-top: 6px;
}

/* How sure the model was. Quiet by default, because it appears on most steps
   and a loud badge everywhere is furniture the reader stops seeing. */
.plan-notes-confidence {
  color: var(--muted, #64748b);
  font-size: 0.8rem;
  margin: 8px 0 0;
}

/* The one that is not quiet: a guess the reader should check before accepting.
   Left rule rather than a filled box, so it reads as a margin note on the quote
   above it rather than as a second warning competing with the knock-on. */
.plan-notes-confidence-low {
  border-left: 3px solid #d97706;
  color: #92400e;
  padding-left: 10px;
}

/* Where the reader stands against the limit. Quiet by default - it sits under
   the box on every keystroke, and a loud counter is one people stop seeing
   before it has anything urgent to say. */
.plan-notes-count {
  color: var(--muted, #64748b);
  font-size: 0.78rem;
  font-variant-numeric: tabular-nums;
  margin: 6px 0 0;
  min-height: 1em;
}

/* Longer than one read: a change in what happens, not a fault. Amber rather
   than red, and no border - it is information the reader wants before pressing
   the button, not something to fix. */
.plan-notes-count-condense {
  color: #92400e;
}

/* Over the limit, where the button is off and something has to be done. */
.plan-notes-count-over {
  color: #b42318;
  font-weight: 600;
}

/* The last thing these notes did, and the way back out of it. Quiet: it is a
   footnote under the box until somebody wants it, not a second call to action
   competing with "Read my notes". */
.plan-notes-last {
  align-items: baseline;
  border-top: 1px solid var(--line, #e2e8f0);
  color: var(--muted, #64748b);
  display: flex;
  flex-wrap: wrap;
  font-size: 0.82rem;
  gap: 8px;
  justify-content: space-between;
  margin-top: 12px;
  padding-top: 10px;
}

/* Shown on the choice, never after it - a warning that appears once the
   decision is made is a receipt rather than a warning. */
.plan-notes-warning {
  background: #fef3f2;
  border: 1px solid #fecdca;
  border-radius: 8px;
  color: #7f1d1d;
  font-size: 0.82rem;
  margin: 10px 0 0;
  padding: 10px 12px;
}

.plan-notes-dates {
  display: grid;
  gap: 10px;
  grid-template-columns: 1fr 1fr;
}

.plan-notes-why {
  border: 1px solid var(--line);
  border-radius: 8px;
  margin: 12px 0;
  padding: 10px 12px;
}

.plan-notes-why legend {
  font-size: 0.78rem;
  font-weight: 600;
  padding: 0 4px;
}

.plan-notes-why textarea,
.plan-notes-why input[type="text"] {
  border: 1px solid var(--line);
  border-radius: 6px;
  font: inherit;
  margin: 4px 0 10px 26px;
  padding: 6px 8px;
  width: calc(100% - 26px);
}

.plan-notes-mitigations {
  margin: 4px 0 0;
  padding-left: 18px;
}
