/* The Signal Console's own surfaces.
 *
 * SCOPED, DELIBERATELY. Every selector here is either a `.cn-` class or is
 * nested under one. Nothing in this file can reach the storefront, the report,
 * the account pages or any shared component, which is the property that makes
 * it safe to load a second stylesheet at all. styles.css and signal.css are
 * untouched by this work.
 *
 * IT INVENTS NO SCALE. Every value below comes from the tokens dashboard.html
 * already declares: --t-* for type, --s-* for space, --r-* for radius,
 * --signal / --hairline / --ink / --muted / --up / --down for colour. The
 * design manifesto says this page is the reference implementation, so a
 * stylesheet standing beside it with its own sizes would be the migration
 * going backwards. Where a new token is defined it is derived from an existing
 * one and says so.
 *
 * The one thing this file adds to the vocabulary is STATE colour: a
 * connection that is live, expired or absent, and a measurement that is
 * stable, volatile or absent. Those are semantic and they are kept apart from
 * the accent, because purple already means "the customer" on every chart on
 * this page and must not start meaning "good".
 */

:root {
  /* Semantic state. Green and orange already exist as --up and --down; these
     name the third and fourth states those two do not cover. */
  --cn-flat: #8A87A8;          /* nothing to say, drawn from --muted */
  --cn-warn-bg: #FDF6EC;
  --cn-warn-ink: #8A5B12;
  --cn-live-bg: #EAF6F0;
  --cn-live-ink: #1F6B4A;
  --cn-off-bg: #F4F3F8;
  --cn-off-ink: #6B6885;
  --cn-cell-0: #F6F5FA;        /* an empty matrix cell: present, unfilled */
}

/* ==========================================================================
   SECTIONS
   Only one is ever on screen. Hidden with [hidden] rather than a class, so a
   section that never gets wired is invisible by default rather than flashing.
   ========================================================================== */
.cn-section[hidden] { display: none !important; }
/* THE MARKETING SITE'S SECTION PADDING IS NOT OURS.
   styles.css carries a global `section { padding: var(--space-5) 0 }` for the
   landing pages, and --space-5 is clamp(4rem, 3rem + 4vw, 8rem). These panels
   are <section> elements because they are semantically sections of an
   application, so they inherited it: measured at a 1280px viewport, 99.2px of
   empty band above every console section and the same again below it. Every
   section but Overview opened on a blank screen with the heading pushed under
   the fold, which is what the founder saw on 2026-09-07 and called "space
   ข้างบน ... เป็นเกือบทุกหน้าเลย". Overview escaped it only because #dashPanel
   is a div.
   The report page solved this same leak twice already, in signal.css for
   .rp-sec and again inside report.html, and both wrote down why. This is the
   third place, and the last one that had it. */
.cn-section { padding: 0; }
.cn-section { animation: cn-fade .18s ease-out; }
@keyframes cn-fade { from { opacity: .4; } to { opacity: 1; } }
@media (prefers-reduced-motion: reduce) { .cn-section { animation: none; } }

.cn-head { margin: 0 0 var(--s-4); }
.cn-head h2 {
  font-size: var(--t-title); font-weight: var(--w-strong); color: var(--ink);
  margin: 0 0 var(--s-1); letter-spacing: -.01em;
}
.cn-head p { font-size: var(--t-body); color: var(--muted); margin: 0; max-width: 68ch; line-height: 1.55; }

/* ==========================================================================
   THE BUSINESS SWITCHER
   Lives in the app bar. A button that opens a list, not a native select: the
   rows carry two lines and two status dots, which a select cannot draw.
   ========================================================================== */
.cn-switch { position: relative; min-width: 0; }
.cn-switch-btn {
  display: flex; align-items: center; gap: var(--s-2);
  background: none; border: 0; padding: 4px 6px; margin-left: -6px;
  border-radius: var(--r-control); cursor: pointer; text-align: left;
  max-width: 100%; font-family: var(--dash-font);
}
.cn-switch-btn:hover { background: var(--paper-dim, #F4F3F9); }
.cn-switch-btn:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }
.cn-switch-name {
  display: block; font-size: var(--t-lead); font-weight: var(--w-strong); color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 46vw;
}
.cn-switch-dom {
  display: block; font-size: var(--t-meta); color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 46vw;
}
/* THE PAGE'S OWN CHEVRON, not a sixth one.
   This file first drew a small solid triangle glyph. The page had five
   different marks doing that single job and unified them behind --chev
   precisely so a new control could not add another; a stroked chevron carries
   far less ink than a solid triangle at the same width, which is why the two
   do not read as the same mark sitting next to each other. Same mask, same
   size token, same colour as every other control that opens something. */
.cn-switch-car {
  display: inline-block; font-size: 0; flex: none;
  width: var(--chev-size); height: var(--chev-size);
  color: var(--muted); background-color: currentColor;
  -webkit-mask: var(--chev) center / var(--chev-size) var(--chev-size) no-repeat;
  mask: var(--chev) center / var(--chev-size) var(--chev-size) no-repeat;
}
.cn-switch-btn:hover .cn-switch-car { color: var(--ink); }
@media (forced-colors: active) { .cn-switch-car { background-color: CanvasText; } }

.cn-menu {
  position: absolute; top: calc(100% + 6px); left: 0; z-index: 60;
  min-width: 320px; max-width: min(420px, 92vw); max-height: 70vh; overflow: auto;
  background: #fff; border: 1px solid var(--hairline); border-radius: var(--r-surface);
  /* The one shadow this file spends. The manifesto reserves elevation for the
     transient layer, and a menu that closes on the next click is exactly that. */
  box-shadow: 0 12px 32px rgba(24, 20, 60, .13);
  padding: var(--s-2);
}
.cn-menu[hidden] { display: none; }
.cn-menu-label {
  font-size: var(--t-micro); text-transform: uppercase; letter-spacing: .07em;
  color: var(--muted); font-weight: var(--w-medium); padding: var(--s-2) var(--s-3) var(--s-1);
}
.cn-menu-item {
  display: flex; align-items: flex-start; gap: var(--s-3); width: 100%;
  background: none; border: 0; text-align: left; cursor: pointer;
  padding: var(--s-2) var(--s-3); border-radius: var(--r-control);
  font-family: var(--dash-font); color: var(--ink); text-decoration: none;
}
.cn-menu-item:hover { background: var(--dash-bg); }
.cn-menu-item:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; }
.cn-menu-item.is-on { background: var(--signal-tint); }
.cn-menu-item .mi-body { min-width: 0; flex: 1; }
.cn-menu-item .mi-name {
  display: block; font-size: var(--t-body); font-weight: var(--w-medium);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cn-menu-item .mi-meta {
  display: block; font-size: var(--t-meta); color: var(--muted); margin-top: 1px;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.cn-menu-item .mi-tick { flex: none; color: var(--signal); font-size: 12px; width: 12px; }
.cn-menu-sep { height: 1px; background: var(--hairline); margin: var(--s-2) var(--s-1); }

/* ==========================================================================
   STATUS PILLS
   Form as well as number: a reader scanning a list should see what needs
   attention without reading a single figure.
   ========================================================================== */
.cn-pill {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--t-micro); font-weight: var(--w-medium); letter-spacing: .05em;
  text-transform: uppercase; padding: 3px 7px; border-radius: var(--r-tag);
  white-space: nowrap; vertical-align: middle;
}
.cn-pill.live { background: var(--cn-live-bg); color: var(--cn-live-ink); }
.cn-pill.warn { background: var(--cn-warn-bg); color: var(--cn-warn-ink); }
.cn-pill.off  { background: var(--cn-off-bg);  color: var(--cn-off-ink); }
.cn-pill.acc  { background: var(--signal-tint); color: var(--signal); }
.cn-dot { width: 6px; height: 6px; border-radius: 50%; flex: none; }
.cn-dot.live { background: var(--up); }
.cn-dot.warn { background: var(--down); }
.cn-dot.off  { background: #C9C7D8; }

/* ==========================================================================
   ONBOARDING
   Three cards over the real console. The console behind is the same blurred
   stage the first-run panel uses, so what somebody is being taught about is
   visible while they are taught it.
   ========================================================================== */
.cn-ob-backdrop {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(28, 24, 58, .34);
  backdrop-filter: blur(2px);
  display: flex; align-items: center; justify-content: center; padding: var(--s-5);
}
.cn-ob-backdrop[hidden] { display: none; }
.cn-ob {
  background: #fff; border-radius: var(--r-surface); width: min(620px, 100%);
  max-height: 90vh; overflow: auto;
  box-shadow: 0 24px 64px rgba(24, 20, 60, .22);
  padding: var(--s-6);
}
.cn-ob-top { display: flex; align-items: center; justify-content: space-between; margin-bottom: var(--s-5); }
.cn-ob-steps { display: flex; gap: 6px; align-items: center; }
.cn-ob-step { width: 26px; height: 3px; border-radius: 2px; background: var(--hairline); }
.cn-ob-step.is-on { background: var(--signal); }
.cn-ob-skip {
  background: none; border: 0; cursor: pointer; font-family: var(--dash-font);
  font-size: var(--t-meta); color: var(--muted); padding: 4px 6px; border-radius: var(--r-control);
}
.cn-ob-skip:hover { color: var(--ink); }
.cn-ob-skip:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }
.cn-ob h2 {
  font-size: var(--t-title); font-weight: var(--w-strong); color: var(--ink);
  margin: 0 0 var(--s-3); letter-spacing: -.012em; line-height: 1.2;
}
.cn-ob p { font-size: var(--t-lead); line-height: 1.6; color: var(--muted); margin: 0 0 var(--s-5); max-width: 56ch; }
.cn-ob-art {
  background: var(--dash-bg); border: 1px solid var(--hairline); border-radius: var(--r-surface);
  padding: var(--s-5); margin-bottom: var(--s-5);
}
.cn-ob-foot { display: flex; align-items: center; justify-content: space-between; gap: var(--s-3); }
.cn-ob-back {
  background: none; border: 0; font-family: var(--dash-font); font-size: var(--t-body);
  color: var(--muted); cursor: pointer; padding: 6px 8px; border-radius: var(--r-control);
}
.cn-ob-back:hover { color: var(--ink); }
.cn-ob-back:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }
.cn-ob-back[hidden] { visibility: hidden; display: block; }

/* ---------------------------------------------------------------------------
   THE CARDS' OWN MOTION.

   Every animation below is the thing being explained, performing itself. The
   design manifesto lists "decorative motion" under what this product must
   never become, and that rule is kept by making none of this decorative: the
   grid filling to eighteen of sixty-four IS the denominator argument, the
   chain drawing in order IS the data flow, and the bar moving from one in
   eight to six in eight IS the promise that you can tell whether it worked.

   All of it is skipped under prefers-reduced-motion, and the script paints the
   finished state instead, so nobody who asked for less is shown less.
   --------------------------------------------------------------------------- */
.cn-ob-eyebrow {
  font-size: var(--t-micro); text-transform: uppercase; letter-spacing: .09em;
  color: var(--signal); font-weight: var(--w-medium); margin-bottom: var(--s-2);
}
.cn-ob-hi {
  font-size: var(--t-body); color: var(--muted); margin: 0 0 var(--s-3);
  animation: cn-rise .5s cubic-bezier(.2,.7,.3,1) both;
}
.cn-ob-body { animation: cn-card-in .34s cubic-bezier(.2,.7,.3,1) both; }
.cn-ob-body.from-left { animation-name: cn-card-in-back; }
@keyframes cn-card-in { from { opacity: 0; transform: translateX(14px); } to { opacity: 1; transform: none; } }
@keyframes cn-card-in-back { from { opacity: 0; transform: translateX(-14px); } to { opacity: 1; transform: none; } }
@keyframes cn-rise { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: none; } }

/* The step rail. A finished step stays filled, so progress reads as ground
   covered rather than as a dot that moved. */
.cn-ob-step { transition: background .3s ease, width .3s ease; }
.cn-ob-step.is-done { background: var(--signal); opacity: .45; }
.cn-ob-step.is-on { background: var(--signal); width: 34px; }

/* Card 1: sixty-four answers, eighteen of them yours. The empty cells are the
   point, so they are as visible as the filled ones. */
/* EIGHT BY EIGHT, not sixteen by four. Sixty-four cells in a strip reads as a
   bar; in a square it reads as a quantity, which is the only reason the grid
   is here. Measured by looking at both. */
.cn-grid-art {
  display: grid; grid-template-columns: repeat(8, 1fr); gap: 4px;
  margin-bottom: var(--s-4); max-width: 296px;
}
.cn-grid-art i {
  display: block; padding-top: 100%; border-radius: 2px; background: var(--cn-cell-0);
  opacity: 0; transform: scale(.72);
  transition: opacity .22s ease, transform .22s cubic-bezier(.2,1.5,.4,1), background .22s ease;
}
.cn-grid-art i.in { opacity: 1; transform: none; }
.cn-grid-art i.on { background: var(--signal); }
.cn-art-line { display: flex; align-items: baseline; gap: var(--s-2); flex-wrap: wrap; }
.cn-art-n {
  font-size: var(--t-display); font-weight: var(--w-strong); color: var(--ink);
  font-variant-numeric: tabular-nums; letter-spacing: -.01em;
}
.cn-art-n b { font-weight: var(--w-strong); }
.cn-art-k { font-size: var(--t-meta); color: var(--muted); }

/* Card 2: the chain, arriving in the order the data moves. */
.cn-chain { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; }
.cn-chain span {
  font-size: var(--t-meta); font-weight: var(--w-medium); color: var(--ink);
  background: #fff; border: 1px solid var(--hairline); border-radius: var(--r-control);
  padding: 7px 10px; white-space: nowrap;
  opacity: 0; transform: translateY(5px); transition: opacity .3s ease, transform .3s cubic-bezier(.2,.8,.3,1);
}
.cn-chain span.in { opacity: 1; transform: none; }
.cn-chain-link {
  display: block; height: 1px; width: 0; background: var(--hairline);
  transition: width .26s ease;
}
.cn-chain-link.in { width: 16px; }

/* Card 3: the move. One question, before and after. */
.cn-move { display: grid; gap: var(--s-2); }
.cn-move-q { font-size: var(--t-body); color: var(--ink); margin-bottom: var(--s-1); }
.cn-move-row { display: flex; align-items: center; gap: var(--s-3); }
.cn-move-k { font-size: var(--t-meta); color: var(--muted); width: 46px; flex: none; }
.cn-move-track { flex: 1; height: 8px; border-radius: var(--r-pill); background: var(--cn-cell-0); overflow: hidden; }
.cn-move-track i { display: block; height: 100%; border-radius: var(--r-pill); background: var(--signal); }
.cn-move-track i.grow { width: 12.5%; transition: width .9s cubic-bezier(.2,.8,.25,1); }
.cn-move-v {
  font-size: var(--t-body); font-weight: var(--w-strong); color: var(--ink);
  font-variant-numeric: tabular-nums; width: 44px; text-align: right; flex: none;
}

/* The primary action sits in the same place on every card. space-between plus
   a hidden placeholder was supposed to do that and did not on the first card,
   where there is no Back: the button landed on the left and moved right on
   card two, so the one control a reader is aiming for was the one thing that
   jumped. margin-left:auto does not care whether the placeholder is there. */
.cn-ob-next { transition: transform .12s ease; margin-left: auto; }
.cn-ob-next:active { transform: translateY(1px); }

@media (prefers-reduced-motion: reduce) {
  .cn-ob-body, .cn-ob-hi { animation: none; }
  .cn-grid-art i, .cn-chain span, .cn-chain-link, .cn-move-track i, .cn-ob-step, .cn-ob-next {
    transition: none;
  }
  .cn-grid-art i { opacity: 1; transform: none; }
  .cn-chain span { opacity: 1; transform: none; }
}

/* ==========================================================================
   SETUP CHECKLIST
   Collapses to one line when the required items are done, because a permanent
   checklist on a finished console is furniture nobody asked for.
   ========================================================================== */
.cn-setup {
  border: 1px solid var(--hairline); border-radius: var(--r-surface);
  background: #fff; padding: var(--s-4); margin-bottom: var(--s-4);
}
.cn-setup-h { display: flex; align-items: center; gap: var(--s-3); }
.cn-setup-h h3 {
  font-size: var(--t-lead); font-weight: var(--w-medium); color: var(--ink); margin: 0; flex: 1;
}
.cn-setup-toggle {
  background: none; border: 0; cursor: pointer; font-family: var(--dash-font);
  font-size: var(--t-meta); color: var(--signal); font-weight: var(--w-medium);
  padding: 4px 6px; border-radius: var(--r-control);
}
.cn-setup-toggle:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }
.cn-setup-list { list-style: none; margin: var(--s-3) 0 0; padding: 0; display: grid; gap: var(--s-2); }
.cn-setup-list li { display: flex; align-items: flex-start; gap: var(--s-3); font-size: var(--t-body); }
.cn-setup-mark { flex: none; width: 16px; text-align: center; font-size: 12px; line-height: 1.5; }
.cn-setup-mark.done { color: var(--up); }
.cn-setup-mark.todo { color: #C9C7D8; }
.cn-setup-txt { min-width: 0; }
.cn-setup-txt b { font-weight: var(--w-medium); color: var(--ink); }
.cn-setup-txt span { display: block; color: var(--muted); font-size: var(--t-meta); margin-top: 1px; }
.cn-setup-act {
  background: none; border: 0; padding: 0; margin-top: 2px; cursor: pointer;
  font-family: var(--dash-font); font-size: var(--t-meta); font-weight: var(--w-medium);
  color: var(--signal); text-decoration: none; display: inline-block;
}
.cn-setup-act:hover { text-decoration: underline; }

/* A FREE TRIAL WAITING FOR ITS BUSINESS. Drawn from the setup panel's own
   parts, because it is the same kind of object: a step the account takes before
   the console can do its work. Only the choice row and the action strip are new. */
.cn-trial .cn-note { margin-top: var(--s-2); }
.cn-trial-opt { display: flex; align-items: center; gap: var(--s-3); cursor: pointer; padding: var(--s-1) 0; }
.cn-trial-opt input { flex: none; margin: 0; accent-color: var(--signal); }
.cn-trial-q { font-size: var(--t-body); color: var(--ink); margin: var(--s-3) 0 0; line-height: 1.6; }
.cn-trial-q b { font-weight: var(--w-medium); }
.cn-trial-acts { display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap; margin-top: var(--s-4); }
.cn-trial .dash-btn[disabled] { opacity: 0.5; cursor: default; }
@media (max-width: 600px) {
  .cn-trial-opt { padding: var(--s-2) 0; }
  .cn-trial-acts .dash-btn { width: 100%; justify-content: center; min-height: 44px; }
}

/* ==========================================================================
   THE HEADLINE MEASUREMENT
   The numerator, the denominator and the period, in that order and at that
   emphasis. The percentage is second because it is derived from the two
   figures above it, and a page that leads with the derived number teaches the
   reader to stop looking for the evidence.
   ========================================================================== */
.cn-exec {
  display: grid; grid-template-columns: minmax(280px, 1.15fr) minmax(240px, 1fr);
  gap: var(--s-5); align-items: start;
  border: 1px solid var(--hairline); border-radius: var(--r-surface);
  background: #fff; padding: var(--s-5); margin-bottom: var(--s-4);
}
@media (max-width: 780px) { .cn-exec { grid-template-columns: 1fr; gap: var(--s-4); } }
.cn-exec-label {
  font-size: var(--t-micro); text-transform: uppercase; letter-spacing: .07em;
  color: var(--muted); font-weight: var(--w-medium); margin-bottom: var(--s-3);
}
.cn-frac { display: flex; align-items: baseline; gap: var(--s-2); flex-wrap: wrap; }
.cn-frac-n {
  font-size: 34px; font-weight: var(--w-strong); color: var(--ink);
  font-variant-numeric: tabular-nums; letter-spacing: -.02em; line-height: 1.05;
}
.cn-frac-of { font-size: var(--t-lead); color: var(--muted); }
.cn-frac-say { font-size: var(--t-body); color: var(--muted); margin-top: var(--s-2); line-height: 1.5; }
.cn-rate-row { display: flex; align-items: baseline; gap: var(--s-3); margin-top: var(--s-3); flex-wrap: wrap; }
.cn-rate { font-size: var(--t-display); font-weight: var(--w-strong); color: var(--ink); font-variant-numeric: tabular-nums; }
.cn-delta { font-size: var(--t-body); font-weight: var(--w-medium); font-variant-numeric: tabular-nums; }
.cn-delta.up { color: var(--up); }
.cn-delta.down { color: var(--down); }
.cn-delta.flat { color: var(--cn-flat); }

/* Per-source chips. Every one carries its own fraction: a source with four
   answers and a source with forty look identical as percentages. */
.cn-chips { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-top: var(--s-4); }
.cn-chip {
  border: 1px solid var(--hairline); border-radius: var(--r-control);
  padding: 7px 10px; background: #fff; min-width: 0;
}
.cn-chip b { display: block; font-size: var(--t-meta); font-weight: var(--w-medium); color: var(--ink); }
.cn-chip span { display: block; font-size: var(--t-meta); color: var(--muted); font-variant-numeric: tabular-nums; margin-top: 2px; }

/* Measurement health: what the metric is made of, stated so the rate above
   can be checked rather than believed. */
.cn-health { border-left: 1px solid var(--hairline); padding-left: var(--s-5); }
@media (max-width: 780px) { .cn-health { border-left: 0; padding-left: 0; border-top: 1px solid var(--hairline); padding-top: var(--s-4); } }
.cn-health dl { margin: 0; display: grid; grid-template-columns: 1fr auto; gap: var(--s-2) var(--s-3); }
.cn-health dt { font-size: var(--t-body); color: var(--muted); }
.cn-health dd {
  margin: 0; font-size: var(--t-body); font-weight: var(--w-medium); color: var(--ink);
  font-variant-numeric: tabular-nums; text-align: right;
}

/* ==========================================================================
   CARDS AND TABLES
   ========================================================================== */
.cn-card {
  border: 1px solid var(--hairline); border-radius: var(--r-surface);
  background: #fff; padding: var(--s-4); margin-bottom: var(--s-4);
}
.cn-card-h { display: flex; align-items: baseline; gap: var(--s-3); margin-bottom: var(--s-3); flex-wrap: wrap; }
.cn-card-h h3 {
  font-size: var(--t-lead); font-weight: var(--w-medium); color: var(--ink); margin: 0; flex: 1;
  /* A CARD TITLE NEVER BREAKS MID-WORD.
     styles.css's reset sets `overflow-wrap: anywhere` on every h1/h2/h3/p/
     li/span/a/div, and `anywhere` (unlike `break-word`) counts soft-wrap
     opportunities in the MIN-CONTENT size. So `flex: 1` here, which is
     `1 1 0%` with `min-width: auto`, resolved to a minimum of roughly nothing
     instead of to the longest word: measured at a 390px viewport the title
     was 82.7px wide and three lines tall, reading "Recommen / dation /
     consistency". signal.css met the same reset twice already and answered it
     the same way (`.pricing-compare td`). */
  overflow-wrap: normal;
}
.cn-card-h .meta { font-size: var(--t-meta); color: var(--muted); }
/* On a phone the head is a title with a caption under it, not two columns.
   The wrap was already allowed; what was missing was a basis that reaches the
   end of the line, so the caption had somewhere to go other than beside a
   title squeezed to a third of its width. */
@media (max-width: 640px) { .cn-card-h h3 { flex-basis: 100%; } }
.cn-note { font-size: var(--t-meta); color: var(--muted); line-height: 1.6; margin: var(--s-3) 0 0; }

/* THE RULE THE CARD STATES, WHICH IS NOT THE SAME KIND OF LINE AS THE NOTE
   ABOVE IT. The note explains a number that is on screen; this one says what
   the customer may do and until when. Set apart by a rule above it and a
   little more ink, so it is read rather than skimmed past as more small print,
   and no more than that: the manifesto lists decorative emphasis under things
   this product must never become.
   The closed state is amber and not red. Nothing is wrong, nothing is lost,
   and a measurement is about to run: that is a hold, and red would read as a
   fault the customer has to fix. */
.cn-rule {
  color: var(--ink); border-top: 1px solid var(--hairline);
  padding-top: var(--s-3); margin-top: var(--s-3);
}
.cn-rule.is-closed { color: var(--cn-warn-ink); }

/* THE INDEX COLUMN. Narrow, tabular and quiet: it is a handle for talking
   about a row ("question 3 never names us"), not a figure to be read. */
.cn-tbl th.n, .cn-tbl td.n {
  width: 1%; white-space: nowrap; text-align: right;
  font-variant-numeric: tabular-nums; color: var(--muted); padding-right: var(--s-2);
}

/* A QUESTION IN THE SET THAT NOTHING HAS ANSWERED YET. Held back rather than
   greyed out: it is not disabled, it is waiting, and it runs next time. */
.cn-unmeasured td.q { color: var(--ink); }
.cn-unmeasured { background: var(--cn-cell-0); }

/* A QUESTION MEASURED SINCE THIS REPORT WAS WRITTEN. The figures are ordinary
   figures, so the row is an ordinary row; the only addition is a line under
   the question naming the morning they came from, because a reader comparing
   two rows has to be able to see that they are two different mornings. */
.cn-seen-when { margin-top: 4px; font-size: var(--t-meta); line-height: 1.4; }
.cn-seen-when .cn-inline-btn { font-size: var(--t-meta); }

/* TICKING PROPOSALS, THE WAY MAIL DOES IT.
   The row becomes a box and a body so the tick sits in front of the question
   rather than above it, which is the whole point of the shape: the eye runs
   down a column of boxes. */
.cn-prop-pick { display: flex; align-items: center; padding-top: 2px; cursor: pointer; }
.cn-prop-pick input { width: 16px; height: 16px; accent-color: var(--signal); cursor: pointer; }
.cn-props-bulk {
  display: flex; align-items: center; gap: var(--s-3); flex-wrap: wrap;
  padding: var(--s-2) 0 var(--s-3); border-bottom: 1px solid var(--hairline); margin-bottom: var(--s-3);
}
.cn-props-n { font-size: var(--t-meta); color: var(--muted); min-width: 8ch; }
.cn-props-bulk-do { margin-left: auto; display: flex; align-items: center; gap: var(--s-3); }
@media (max-width: 720px) { .cn-props-bulk-do { margin-left: 0; } }
/* Present for a screen reader, absent for everyone else: the box needs a name
   and the column does not need it printed fifteen times. */
.cn-sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

/* ADDING, DIRECTLY UNDER THE LAST QUESTION.
   Outside the scrolling table on purpose: a control inside it is as wide as
   the table, so on a phone its middle is off the screen and the pointer cannot
   reach it. Here it fits the card at any width and still reads as the next
   row, by the + and the same left inset the index column has. */
.cn-addrow { display: flex; gap: var(--s-3); align-items: flex-start; margin-top: var(--s-2); }
.cn-addrow-n { color: var(--muted); font-variant-numeric: tabular-nums; padding-top: 9px; }
.cn-addrow-b { flex: 1; min-width: 0; }
.cn-add-open {
  font: inherit; font-size: var(--t-meta); color: var(--signal, #5B4FE0);
  background: none; border: 1px dashed var(--hairline); border-radius: var(--r-control, 8px);
  padding: var(--s-2) var(--s-3); cursor: pointer; width: 100%; text-align: left;
}
.cn-add-open:hover { border-color: var(--signal, #5B4FE0); }
.cn-add-box textarea {
  width: 100%; box-sizing: border-box; font: inherit; font-size: var(--t-body);
  border: 1px solid var(--hairline); border-radius: var(--r-control, 8px);
  padding: var(--s-2) var(--s-3); resize: vertical;
}
.cn-add-box textarea:focus { outline: none; border-color: var(--signal, #5B4FE0); }
.cn-add-acts { display: flex; align-items: center; gap: var(--s-3); margin-top: var(--s-2); flex-wrap: wrap; }
.cn-add-say { font-size: var(--t-meta); color: var(--muted); }
.cn-add-say.bad { color: var(--cn-warn-ink); }

/* Removing a question: the row asks, and waits for an answer. No timeout, for
   the reason the handler explains. The refusal, if there is one, is said in
   the row rather than in a line under a table that is taller than the screen. */
.cn-drop-ask { color: var(--ink); }
.cn-drop-say { margin-top: var(--s-2); font-size: var(--t-meta); color: var(--cn-warn-ink); }

/* A HISTORY IS A COLUMN, NOT A TOOLBAR. One row per measurement, newest
   first, one of them open. A row of pills grew without bound and read as a
   filter; a customer with two hundred measurements wants a list of them with
   one open, and the row carries what the measurement SAYS so the column is
   worth reading before anything is expanded. */
.cn-mlist { display: flex; flex-direction: column; }
.cn-mrow {
  display: flex; align-items: center; gap: var(--s-3); width: 100%;
  font: inherit; text-align: left; background: none; cursor: pointer;
  border: 0; border-top: 1px solid var(--hairline); padding: var(--s-3) 0;
}
.cn-mlist > .cn-mrow:first-child { border-top: 0; }
.cn-mrow:hover .cn-mrow-d { color: var(--signal); }
.cn-mrow.is-on .cn-mrow-d { color: var(--ink); font-weight: var(--w-medium); }
.cn-mrow:focus-visible { outline: 2px solid var(--signal); outline-offset: -2px; }
/* A measurement whose raw answers have since been replaced. It is a real row
   in a real history and it is not pressable, so it does not pretend to be:
   no pointer, no hover, and the date stays quiet. */
.cn-mrow.is-flat { cursor: default; }
.cn-mrow.is-flat .cn-mrow-d { color: var(--muted); }
.cn-mrow.is-flat:hover .cn-mrow-d { color: var(--muted); }
.cn-mrow-d { font-size: var(--t-body); color: var(--ink); }
.cn-mrow-m { flex: 1; font-size: var(--t-meta); color: var(--muted); }
.cn-mrow-x { font-size: var(--t-body); color: var(--muted); width: 1em; text-align: center; }
.cn-mrow-body { padding: 0 0 var(--s-4); }


/* And the button that sits in a card head beside the title. .cn-card-h aligns
   on the baseline, which is right for a heading and a meta line and wrong for
   a control: it hangs the button's text on the title's baseline and its box
   below the row. Aligned to centre only where a button is actually present. */
.cn-card-h:has(.dash-btn) { align-items: center; }

.cn-scroll { overflow-x: auto; }
.cn-tbl { border-collapse: collapse; width: 100%; font-size: var(--t-body); }
.cn-tbl th, .cn-tbl td { text-align: left; padding: var(--s-2) var(--s-3); border-bottom: 1px solid var(--hairline); vertical-align: top; }
.cn-tbl th {
  font-size: var(--t-micro); text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); font-weight: var(--w-medium); white-space: nowrap;
}
.cn-tbl tr:last-child td { border-bottom: 0; }
.cn-tbl td.num, .cn-tbl th.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.cn-tbl .q { min-width: 260px; max-width: 460px; }
.cn-you td { background: var(--signal-tint); }

/* THE SELECT COLUMN. Narrow, top-aligned with the name rather than centred in
   a two-line cell, and a real checkbox: a styled div would lose the keyboard,
   the screen reader and the indeterminate state, all three of which this
   header cell needs. */
.cn-tbl th.chk, .cn-tbl td.chk { width: 1%; padding-right: 0; white-space: nowrap; }
.cn-tbl td.chk input, .cn-tbl th.chk input {
  width: 15px; height: 15px; margin: 2px 0 0; accent-color: var(--signal); cursor: pointer;
}
.cn-tbl th.chk input { margin-top: 0; }

/* The bulk strip. Inside the card and above the table, so it reads as a state
   of this list rather than as a toolbar the page always had. It is `hidden`
   until something is ticked; see wireBulk for why it is absent and not
   disabled. */
.cn-bulk {
  display: flex; align-items: center; justify-content: space-between; gap: var(--s-3);
  flex-wrap: wrap; padding: var(--s-2) var(--s-3);
  border-bottom: 1px solid var(--hairline); background: var(--signal-tint);
}
.cn-bulk-say { font-size: var(--t-meta); color: var(--ink); font-weight: var(--w-medium); }
.cn-bulk-do { display: flex; align-items: center; gap: var(--s-2); }
.cn-you .nm { font-weight: var(--w-strong); }

/* The matrix. A cell is a button because it opens the answers behind it, and
   it has to look pressable for that to be discoverable. */
.cn-cell {
  display: block; width: 100%; border: 0; border-radius: var(--r-tag); cursor: pointer;
  font-family: var(--dash-font); padding: 6px 8px; text-align: center;
  background: var(--cn-cell-0); color: var(--ink); line-height: 1.25;
}
.cn-cell:hover { outline: 1px solid var(--signal); }
.cn-cell:focus-visible { outline: 2px solid var(--signal); outline-offset: 1px; }
.cn-cell b { display: block; font-size: var(--t-body); font-weight: var(--w-strong); font-variant-numeric: tabular-nums; }
.cn-cell span { display: block; font-size: var(--t-micro); color: var(--muted); font-variant-numeric: tabular-nums; }
.cn-cell[data-empty="1"] { cursor: default; }
.cn-cell[data-empty="1"]:hover { outline: 0; }

/* Consistency bands. */
.cn-bands { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: var(--s-3); }
.cn-band { border: 1px solid var(--hairline); border-radius: var(--r-surface); padding: var(--s-3) var(--s-4); }
.cn-band-h { display: flex; align-items: center; gap: var(--s-2); margin-bottom: var(--s-2); }
.cn-band-h b { font-size: var(--t-body); font-weight: var(--w-medium); color: var(--ink); }
.cn-band-n { font-size: var(--t-display); font-weight: var(--w-strong); color: var(--ink); font-variant-numeric: tabular-nums; line-height: 1.1; }
.cn-band ul { list-style: none; margin: var(--s-2) 0 0; padding: 0; display: grid; gap: 6px; }
.cn-band li { font-size: var(--t-meta); color: var(--muted); line-height: 1.45; }
.cn-band li b { color: var(--ink); font-variant-numeric: tabular-nums; }

/* Quadrants. */
.cn-quads { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: var(--s-3); }
.cn-quad { border: 1px solid var(--hairline); border-radius: var(--r-surface); padding: var(--s-4); }
.cn-quad.priority { border-color: #E8CDA8; background: var(--cn-warn-bg); }
.cn-quad h4 { font-size: var(--t-body); font-weight: var(--w-medium); color: var(--ink); margin: 0 0 var(--s-1); }
.cn-quad .why { font-size: var(--t-meta); color: var(--muted); margin: 0 0 var(--s-3); line-height: 1.5; }
.cn-quad-n { font-size: var(--t-display); font-weight: var(--w-strong); font-variant-numeric: tabular-nums; color: var(--ink); }

/* Action cards: an action is only worth reading with the evidence beside it. */
.cn-action { border: 1px solid var(--hairline); border-radius: var(--r-surface); padding: var(--s-4); margin-bottom: var(--s-3); background: #fff; }
.cn-action-top { display: flex; align-items: baseline; gap: var(--s-3); margin-bottom: var(--s-2); }
.cn-action-rank {
  font-size: var(--t-micro); text-transform: uppercase; letter-spacing: .07em;
  color: var(--muted); font-weight: var(--w-medium); white-space: nowrap;
}
.cn-action h4 { font-size: var(--t-lead); font-weight: var(--w-medium); color: var(--ink); margin: 0; flex: 1; }
.cn-ev { list-style: none; margin: var(--s-2) 0 var(--s-3); padding: 0; display: grid; gap: 5px; }
.cn-ev li { font-size: var(--t-body); color: var(--muted); line-height: 1.55; }
.cn-ev li b { color: var(--ink); font-variant-numeric: tabular-nums; font-weight: var(--w-medium); }

/* ==========================================================================
   THE EVIDENCE DRAWER
   Raw answers, reachable from any number. It slides from the right on a wide
   screen and covers the page on a narrow one.
   ========================================================================== */
.cn-drawer-scrim { position: fixed; inset: 0; z-index: 190; background: rgba(28, 24, 58, .28); }
.cn-drawer-scrim[hidden] { display: none; }
.cn-drawer {
  position: fixed; top: 0; right: 0; bottom: 0; z-index: 191;
  width: min(680px, 100vw); background: #fff; display: flex; flex-direction: column;
  box-shadow: -14px 0 40px rgba(24, 20, 60, .16);
}
.cn-drawer[hidden] { display: none; }
.cn-drawer-h {
  padding: var(--s-4) var(--s-5); border-bottom: 1px solid var(--hairline);
  display: flex; align-items: flex-start; gap: var(--s-3);
}
.cn-drawer-h .t { flex: 1; min-width: 0; }
.cn-drawer-h h3 { font-size: var(--t-lead); font-weight: var(--w-medium); color: var(--ink); margin: 0 0 3px; line-height: 1.4; }
.cn-drawer-h .meta { font-size: var(--t-meta); color: var(--muted); font-variant-numeric: tabular-nums; }
.cn-drawer-x {
  flex: none; background: none; border: 0; cursor: pointer; font-size: 18px; line-height: 1;
  color: var(--muted); padding: 4px 8px; border-radius: var(--r-control);
}
.cn-drawer-x:hover { color: var(--ink); background: var(--dash-bg); }
.cn-drawer-x:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }
.cn-drawer-b { flex: 1; overflow: auto; padding: var(--s-4) var(--s-5) var(--s-6); }

.cn-run { border: 1px solid var(--hairline); border-radius: var(--r-surface); padding: var(--s-3) var(--s-4); margin-bottom: var(--s-3); }
.cn-run-h { display: flex; align-items: center; gap: var(--s-2); flex-wrap: wrap; margin-bottom: var(--s-2); }
.cn-run-n { font-size: var(--t-micro); text-transform: uppercase; letter-spacing: .06em; color: var(--muted); font-weight: var(--w-medium); }
.cn-answer {
  font-size: var(--t-body); line-height: 1.65; color: var(--ink); white-space: pre-wrap;
  margin: 0; max-height: 320px; overflow: auto;
}
.cn-answer.quiet { color: var(--muted); font-style: italic; }
.cn-run-foot { margin-top: var(--s-2); padding-top: var(--s-2); border-top: 1px solid var(--hairline); font-size: var(--t-meta); color: var(--muted); line-height: 1.55; }
.cn-run-foot b { color: var(--ink); font-weight: var(--w-medium); }
.cn-cites { display: grid; gap: 3px; margin-top: 4px; }
.cn-cites a { font-size: var(--t-meta); color: var(--signal); text-decoration: none; overflow-wrap: anywhere; }
.cn-cites a:hover { text-decoration: underline; }

/* ==========================================================================
   EMPTY, LOADING AND CONNECTION STATES
   Skeletons rather than spinners, so the layout does not jump when data lands.
   ========================================================================== */
.cn-empty {
  border: 1px dashed var(--hairline); border-radius: var(--r-surface);
  padding: var(--s-6) var(--s-5); text-align: center; background: var(--dash-bg);
}
.cn-empty h3 { font-size: var(--t-lead); font-weight: var(--w-medium); color: var(--ink); margin: 0 0 var(--s-2); }
.cn-empty p { font-size: var(--t-body); color: var(--muted); margin: 0 auto var(--s-4); max-width: 52ch; line-height: 1.6; }
.cn-empty p:last-child { margin-bottom: 0; }

.cn-skel { display: grid; gap: var(--s-2); }
.cn-skel i {
  display: block; height: 12px; border-radius: var(--r-tag);
  background: linear-gradient(90deg, #F2F1F7 25%, #E9E7F2 37%, #F2F1F7 63%);
  background-size: 400% 100%; animation: cn-shimmer 1.3s ease-in-out infinite;
}
.cn-skel i:nth-child(2) { width: 82%; }
.cn-skel i:nth-child(3) { width: 64%; }
@keyframes cn-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: 0 50%; } }
@media (prefers-reduced-motion: reduce) { .cn-skel i { animation: none; } }

/* ==========================================================================
   CONTROLS
   Built on the page's existing .dash-btn wherever one will do. These are the
   two shapes it does not have.
   ========================================================================== */
.cn-inline-btn {
  background: none; border: 0; padding: 0; cursor: pointer; font-family: var(--dash-font);
  font-size: var(--t-meta); font-weight: var(--w-medium); color: var(--signal); text-decoration: none;
}
.cn-inline-btn:hover { text-decoration: underline; }
.cn-inline-btn:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }
.cn-inline-btn[disabled] { color: var(--muted); cursor: default; text-decoration: none; }

.cn-field {
  font-family: var(--dash-font); font-size: var(--t-body); color: var(--ink);
  padding: var(--s-2) var(--s-3); border: 1px solid var(--hairline);
  border-radius: var(--r-control); background: #fff; min-width: 0;
}
.cn-field:focus { outline: none; border-color: var(--signal); box-shadow: 0 0 0 3px rgba(91, 79, 224, .16); }
.cn-row { display: flex; gap: var(--s-2); flex-wrap: wrap; align-items: center; }
.cn-row .grow { flex: 1; min-width: 200px; }

/* Suggestions. Draft and accepted must never look alike: the whole safety
   property of this feature is that a proposal is visibly not a measurement. */
.cn-sug { border: 1px solid var(--hairline); border-radius: var(--r-surface); padding: var(--s-3) var(--s-4); margin-bottom: var(--s-2); }
.cn-sug.is-accepted { border-color: #BFD8CB; background: var(--cn-live-bg); }
.cn-sug.is-dismissed { opacity: .55; }
.cn-sug-t { font-size: var(--t-body); color: var(--ink); line-height: 1.5; margin: 0 0 var(--s-2); }
.cn-sug-ev { font-size: var(--t-meta); color: var(--muted); line-height: 1.55; margin: 0 0 var(--s-2); }
.cn-sug-ev b { color: var(--ink); font-variant-numeric: tabular-nums; font-weight: var(--w-medium); }
.cn-sug-acts { display: flex; gap: var(--s-4); flex-wrap: wrap; align-items: center; }

/* ==========================================================================
   RAIL: the console's own sections, above the page links that already exist.
   ========================================================================== */
.cn-rail-label {
  font-size: var(--t-micro); text-transform: uppercase; letter-spacing: .07em;
  color: var(--muted); font-weight: var(--w-medium); padding: 0 0 var(--s-2) 12px;
}

/* ==========================================================================
   NARROW SCREENS
   The console keeps every action on a phone. Tables scroll inside their own
   container so the page body never does.
   ========================================================================== */
@media (max-width: 640px) {
  .cn-ob { padding: var(--s-5) var(--s-4); }
  .cn-ob h2 { font-size: 22px; }
  .cn-grid-art { max-width: none; }
  .cn-exec { padding: var(--s-4); }
  .cn-frac-n { font-size: 28px; }
  .cn-menu { min-width: 0; width: min(340px, 90vw); }
  /* The switcher has a full row of its own at this width (the shell's bar
     wraps), so the name may use it rather than being cut at 40% of a viewport
     it no longer shares. */
  .cn-switch-name, .cn-switch-dom { max-width: 78vw; }
  .cn-drawer { width: 100vw; }
}

/* RENAME, beside the name it renames. A text control rather than a button
   shape: the row is a link to a dashboard and a second solid button in the
   same cell would compete with it for the eye. */
.cn-rename {
  font: inherit; font-size: var(--t-meta); color: var(--muted);
  background: none; border: 0; padding: 0 2px; margin-left: 6px; cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px;
}
.cn-rename:hover { color: var(--signal); }
.cn-rename:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; border-radius: 2px; }
.cn-rename[disabled] { opacity: .5; cursor: default; text-decoration: none; }

/* The switcher row: a link and its rename, side by side, because a button may
   not live inside an anchor.

   THE RENAME IS ON EVERY ROW, ALWAYS. It used to be `opacity: 0` until the row
   was hovered, and that was wrong three ways. A touch screen has no hover, so
   on every phone and tablet the control was unreachable while still sitting in
   the tab order, invisible and clickable at once. A control nobody can see is
   a control nobody knows exists: the founder reported the menu as offering a
   rename "for one business", which was simply the row the pointer was resting
   on. And the table on the Businesses screen shows its rename unconditionally,
   so the same action appeared and disappeared depending on where it was met.

   The original worry was real and is answered differently: the menu is mostly
   used for CHOOSING, so the rename must not read as a second button competing
   with the row. It stays quiet by being muted and unadorned until the row is
   engaged, rather than by being absent. */
.cn-menu-row { display: flex; align-items: center; gap: 4px; }
.cn-menu-row > .cn-menu-item { flex: 1; min-width: 0; }
.cn-menu-row > .cn-rename { flex: none; margin-right: var(--s-2); text-decoration: none; }
/* The underline is the affordance, and it arrives when the row is engaged.
   Colour still changes only on the rename itself, so pointing at the row does
   not make the rename look like the thing you are about to press. */
.cn-menu-row:hover > .cn-rename,
.cn-menu-row:focus-within > .cn-rename { text-decoration: underline; }
.cn-remove {
  font: inherit; font-size: var(--t-meta); color: var(--muted);
  background: none; border: 0; padding: 0 2px; margin-left: 8px; cursor: pointer;
  text-decoration: underline; text-underline-offset: 2px;
}
.cn-remove:hover { color: var(--down); }
.cn-remove:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; border-radius: 2px; }
.cn-remove[disabled] { opacity: .5; cursor: default; text-decoration: none; }

/* ==========================================================================
   THE QUESTION WORKSPACE
   Above the measured matrix, and deliberately a different shape from it. The
   matrix is a result and reads as a table. This is work in progress and reads
   as a list of things waiting for a decision.
   ========================================================================== */

/* Where you stand, in one line. Four facts, no chrome around them, because a
   status strip that looks like four cards competes with the cards below it. */
.cn-standing {
  display: flex; flex-wrap: wrap; gap: var(--s-2) var(--s-6);
  padding: var(--s-3) 0 var(--s-4); border-bottom: 1px solid var(--hairline);
  margin-bottom: var(--s-5);
}
.cn-standing div { display: flex; flex-direction: column; gap: 2px; }
.cn-standing dt {
  font-size: var(--t-micro); letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted); font-weight: var(--w-medium); margin: 0;
}
.cn-standing dd { font-size: var(--t-lead); color: var(--ink); font-weight: var(--w-medium); margin: 0; }
.cn-standing dd small { font-size: var(--t-meta); color: var(--muted); font-weight: 400; }

/* THE THREE ROUTES. Equal width on purpose: none of them is the recommended
   one, they answer different questions, and a customer with no Search Console
   should not read their best option as the greyed-out one. */
.cn-routes { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--s-3); }
@media (max-width: 860px) { .cn-routes { grid-template-columns: 1fr; } }
.cn-route {
  border: 1px solid var(--hairline); border-radius: var(--r-surface);
  padding: var(--s-4); display: flex; flex-direction: column; gap: var(--s-2);
  background: #fff;
}
.cn-route h4 { font-size: var(--t-body); font-weight: var(--w-medium); color: var(--ink); margin: 0; }
.cn-route p { font-size: var(--t-meta); color: var(--muted); margin: 0; line-height: 1.55; }
/* The control sits at the bottom of every card whatever the copy above it
   runs to, so three cards of different lengths still line their buttons up. */
.cn-route .cn-route-do { margin-top: auto; padding-top: var(--s-2); }

.cn-paste {
  width: 100%; box-sizing: border-box; min-height: 128px; resize: vertical;
  font: inherit; font-size: var(--t-meta); line-height: 1.55; color: var(--ink);
  border: 1px solid var(--hairline); border-radius: var(--r-control);
  padding: var(--s-3); background: var(--dash-bg);
}
.cn-paste:focus-visible { outline: 2px solid var(--signal); outline-offset: 1px; }

/* The prompt we hand them, shown as the thing it is: text to be copied, not
   copy to be read. Monospace and a scroll box rather than a paragraph. */
.cn-prompt {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12px; line-height: 1.6; white-space: pre-wrap; word-break: break-word;
  background: var(--dash-bg); border: 1px solid var(--hairline);
  border-radius: var(--r-control); padding: var(--s-3);
  max-height: 200px; overflow-y: auto; color: var(--ink); margin: 0;
}

/* A PROPOSAL. Not a card: a row with its evidence under it, because twenty of
   these must stay readable and twenty cards would not. */
/* TICK, SUBJECT, DECISIONS, ON ONE LINE.
   ------------------------------------------------------------------
   Founder, 2026-09-08, twice. First: everything is piled on the left of the
   table. Then, after a fix that did not take: now you have left a gap in the
   middle.

   The first attempt added a flex row THREE HUNDRED AND SEVENTY LINES ABOVE
   this rule, which was still `display: grid`. Later wins, so the row was never
   a row: the tick stacked over the question and the actions dropped to their
   own line, pushed right by a margin that survived the override. It looked
   deliberate and was an accident, and the geometry test passed because it
   measured where things ended up rather than whether they shared a line.

   So the layout lives here, where the rules for this component already were,
   and there is one .cn-prop rule again. The subject takes the width that is
   actually there rather than a capped measure: capping it was what opened the
   gap the founder saw the second time. */
.cn-prop {
  display: flex; align-items: center; gap: var(--s-4);
  padding: var(--s-3) 0; border-top: 1px solid var(--hairline);
}
.cn-prop:first-child { border-top: 0; }
/* Four columns: tick, question, where it came from, what you can do about it.
   The provenance is a column rather than a second line under the question,
   which is what closes the middle: two blocks at opposite edges of a card this
   wide will always have a hole between them, and the way to fill it is with
   the content that already exists rather than by moving them again. */
.cn-prop-body { flex: 1 1 auto; min-width: 0; }
.cn-prop-t { font-size: var(--t-body); color: var(--ink); line-height: 1.5; }
.cn-prop-meta {
  flex: 0 1 auto; min-width: 0; display: flex; align-items: center; gap: var(--s-2);
  font-size: var(--t-meta); color: var(--muted);
}
/* The reason lives on the badge's title now, so this column is the label and
   nothing else. It was the same sentence repeated down the whole page, which
   is a column of noise standing in for a column of information. */
.cn-src[title] { cursor: help; }

/* A settings label that carries an explanation says so, quietly. A dotted
   underline is the one convention a reader already knows for "there is more
   here", and it costs no colour and no icon. */
.cn-health dt[title] { text-decoration: underline dotted var(--hairline); text-underline-offset: 3px; cursor: help; }
.cn-health dt[title]:hover { text-decoration-color: var(--muted); }
.cn-prop-do { flex: none; display: flex; flex-wrap: wrap; gap: var(--s-3); align-items: center; }
/* Narrow: three lines, in reading order, because four columns in 390px is
   four things fighting for one width. */
@media (max-width: 900px) {
  .cn-prop { flex-wrap: wrap; align-items: flex-start; gap: var(--s-2) var(--s-3); }
  .cn-prop-body { flex-basis: 100%; order: 1; padding-left: 28px; margin-top: -22px; }
  .cn-prop-meta { order: 2; padding-left: 28px; flex-basis: 100%; }
  .cn-prop-do { order: 3; width: 100%; padding-left: 28px; }
}
.cn-prop.is-done { opacity: .55; }
.cn-prop-edit {
  width: 100%; box-sizing: border-box; font: inherit; font-size: var(--t-body);
  border: 1px solid var(--signal); border-radius: var(--r-control);
  padding: 8px var(--s-3); color: var(--ink); background: #fff;
}

/* Where a proposal came from, as a word rather than a colour, so the four
   sources stay distinguishable to a reader who cannot separate the hues. */
.cn-src {
  font-size: var(--t-micro); letter-spacing: .06em; text-transform: uppercase;
  font-weight: var(--w-medium); color: var(--muted);
  background: var(--dash-bg); border: 1px solid var(--hairline);
  border-radius: var(--r-tag); padding: 2px 7px; white-space: nowrap;
}
.cn-src.own { color: var(--signal); border-color: var(--signal-tint); background: var(--signal-tint); }

/* Counts on the filter row, so "12 waiting" is legible before anything is
   clicked and the customer knows the list is not everything they have. */
.cn-props-filter { display: flex; flex-wrap: wrap; gap: var(--s-2); margin-bottom: var(--s-2); }
.cn-props-filter button {
  font: inherit; font-size: var(--t-meta); color: var(--muted); cursor: pointer;
  background: none; border: 1px solid var(--hairline); border-radius: var(--r-pill);
  padding: 3px 12px;
}
.cn-props-filter button.is-on { color: var(--ink); border-color: var(--signal); background: var(--signal-tint); }
.cn-props-filter button:focus-visible { outline: 2px solid var(--signal); outline-offset: 2px; }

.cn-say { font-size: var(--t-meta); color: var(--muted); margin: var(--s-2) 0 0; min-height: 1.2em; }
.cn-say.bad { color: var(--down); }

/* THE CATEGORY BAND. Context for every question below it, so it sits above
   them and reads as one sentence rather than as a tile of statistics. Not a
   card: a card would make it look like a fourth thing to do, and it is not a
   thing to do, it is the weather. */
.cn-cat {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px;
  padding: var(--s-3) var(--s-4); margin-bottom: var(--s-4);
  border: 1px solid var(--hairline); border-radius: var(--r-surface);
  background: var(--dash-bg); font-size: var(--t-body); line-height: 1.55;
}
.cn-cat-say { color: var(--ink); }
.cn-cat-say b { font-weight: var(--w-medium); }
.cn-cat-why { flex-basis: 100%; font-size: var(--t-meta); color: var(--muted); margin: 0; }
/* The month's allowance. Its own row, above the source line, because it is a
   fact about their account rather than about the data: how many category
   reads are left and when they come back. Reads as meta, not as a warning,
   even at zero: a spent allowance is a date to wait for, not a fault. */
.cn-cat-meter { flex-basis: 100%; font-size: var(--t-meta); color: var(--muted); margin: 0; }
/* The card's own result line. .cn-say already carries a reserved height so a
   message appearing does not shove the source line down the page. */
.cn-cat #cnCatSay { flex-basis: 100%; margin: 0; }
/* The source line. Always present, always says it is an index, because the
   one claim this data cannot support is the one everybody wants to make. */
.cn-cat-src { flex-basis: 100%; font-size: var(--t-micro); color: var(--muted); font-family: var(--dash-font); margin: 2px 0 0; }
.cn-cat-do { margin-left: auto; display: flex; gap: var(--s-2); align-items: center; }
.cn-cat-topic {
  font: inherit; font-size: var(--t-meta); color: var(--ink); min-width: 180px;
  border: 1px solid var(--hairline); border-radius: var(--r-control);
  padding: 5px var(--s-3); background: #fff;
}
.cn-cat-topic:focus-visible { outline: 2px solid var(--signal); outline-offset: 1px; }
.cn-dir { font-weight: var(--w-medium); }
.cn-dir.rising { color: var(--up); }
.cn-dir.falling { color: var(--down); }
.cn-dir.steady, .cn-dir.unknown { color: var(--muted); }

/* =====================================================================
   THE POINTER: onboarding that is not a dialog.
   ---------------------------------------------------------------------
   Founder, 2026-09-06: there should be some form of onboarding, and it does
   not have to always be a pop-up. Maybe the first time. After they have seen
   those steps, in-platform animation to lead their eyes to click certain
   things, with text showing why they should click it.

   So the modal stays for the first visit and this is what comes after it.
   Three things make it a pointer rather than a second dialog:

   IT POINTS AT A REAL CONTROL. The halo is drawn over an element that is on
   the screen right now, and it moves with it. Nothing is dimmed and nothing is
   trapped; the page underneath stays fully usable, which is the whole
   difference between a hint and an interruption.

   IT SAYS WHY, NOT WHAT. A label that reads "Buyer Questions" beside a link
   that says Buyer Questions has told nobody anything. The reason is the
   content.

   IT IS EARNED BY STATE. Each hint is derived from the setup checklist the
   server already computes, so it only ever appears while the thing it points
   at is genuinely undone, and it leaves the moment it is done. A tour that
   runs on a schedule instead of on state is furniture.
   ===================================================================== */
/* AN OUTLINE, NOT AN ELEVATION. The halo marks a control in place; it does not
   float above the page, and console-views.test.js reserves box-shadow for the
   layers that genuinely do. So the ring is a pseudo-element that scales and
   fades, which is also the cheaper thing to animate. */
.cn-coach-halo {
  position: fixed; z-index: 60; pointer-events: none;
  border-radius: 10px; border: 2px solid var(--signal);
}
.cn-coach-halo:after {
  content: ""; position: absolute; inset: -4px;
  border-radius: 13px; border: 2px solid var(--signal); opacity: .45;
  animation: cnCoachPulse 2.4s cubic-bezier(.4, 0, .2, 1) infinite;
}
@keyframes cnCoachPulse {
  0%   { transform: scale(1); opacity: .45 }
  70%  { opacity: 0 }
  100% { transform: scale(1.16); opacity: 0 }
}
.cn-coach {
  position: fixed; z-index: 61; max-width: 300px;
  background: var(--ink); color: #fff; border-radius: var(--r-surface);
  padding: 12px 14px 11px; box-shadow: 0 12px 34px rgba(20, 18, 31, .28);
  font-size: var(--t-meta); line-height: 1.55;
  animation: cnCoachIn .28s cubic-bezier(.2, .7, .3, 1) both;
}
@keyframes cnCoachIn { from { opacity: 0; transform: translateX(-6px) } to { opacity: 1; transform: none } }
/* The nib, so the card reads as attached to the thing rather than floating
   near it. */
.cn-coach:after {
  content: ""; position: absolute; left: -5px; top: 18px;
  width: 10px; height: 10px; background: var(--ink); transform: rotate(45deg);
}
.cn-coach b { display: block; font-weight: var(--w-medium); margin-bottom: 4px; color: #fff; }
.cn-coach p { margin: 0; color: rgba(255, 255, 255, .82); }
.cn-coach-do { display: flex; gap: 10px; align-items: center; margin-top: 10px; }
.cn-coach-do button {
  font: inherit; font-size: var(--t-meta); cursor: pointer; border-radius: 7px;
  padding: 5px 11px; border: 0; background: var(--signal); color: #fff; font-weight: var(--w-medium);
}
/* THREE CONTROLS, THREE WEIGHTS, because they are not three of the same
   thing. Take me there is the offer and keeps the accent. Next moves through
   the queue and writes nothing, so it is a quiet button rather than a link:
   two underlines side by side would read as two equal ways of saying no.
   Not now is the only one that decides anything, and it stays the faintest,
   because a permanent no should take slightly more intent than a glance. */
/* MEASURED, NOT PICKED. .13 white on the card's #0A2540 came out at 1.48:1,
   and this repository has already paid for a control edge that looked fine in
   a screenshot and measured 1.98:1 where 3.0 was needed. Swept the alpha in
   the browser: the fill rises against the card as the label falls against the
   fill, and .36 with a pure white label is the value where both clear, at
   3.21:1 and 4.85:1. Hover is a ring rather than more white, because a
   lighter fill takes the label back down: at .44 it is 3.79:1, and a hover
   state is still a state. */
.cn-coach-do .cn-coach-next {
  background: rgba(255, 255, 255, .36); color: #fff;
  font-weight: var(--w-body);
}
/* An OUTLINE and not an inset box-shadow, which would have looked identical
   and spent one of the five shadows the manifesto reserves for things that
   float. A ring around a control in place is not elevation, the same reason
   .cn-coach-halo is an outline too. */
.cn-coach-do .cn-coach-next:hover { outline: 1px solid rgba(255, 255, 255, .85); outline-offset: -1px; }
.cn-coach-do .cn-coach-skip { background: none; color: rgba(255, 255, 255, .6); padding: 5px 2px; text-decoration: underline; }
/* Where you are in the queue, next to the title rather than in the row of
   controls: it is a label, and a row of three buttons plus a number reads as
   four things to decide between. */
.cn-coach-n {
  float: right; font-weight: var(--w-body); font-size: var(--t-micro);
  color: rgba(255, 255, 255, .5); letter-spacing: .02em; margin-left: 8px;
  font-variant-numeric: tabular-nums;
}
@media (prefers-reduced-motion: reduce) {
  .cn-coach-halo { animation: none }
  .cn-coach-halo:after { animation: none; opacity: .3 }
  .cn-coach { animation: none }
}
/* Below the rail's own breakpoint the rail is a drawer, so a halo pinned to a
   link inside it would point at something nobody can see. */
@media (max-width: 900px) { .cn-coach-halo, .cn-coach { display: none } }

/* ==========================================================================
   SOURCES: ONE TABLE, MANY VIEWS
   The same seventeen answers read two ways. "All answers" is the table this
   section has always shown; picking a competitor re-scopes every count to the
   answers that named them, and the denominator moves with it. A count left
   describing a set that is no longer on screen is the one thing this screen
   must never do, so the denominator is printed on every row rather than
   assumed from the header.
   ========================================================================== */
.cn-views { display: flex; flex-wrap: wrap; gap: var(--s-2); margin: var(--s-3) 0 var(--s-4); }
.cn-view {
  font: inherit; font-size: var(--t-meta); line-height: 1.2;
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 6px 10px; border: 1px solid var(--hairline); border-radius: var(--r-control);
  background: #fff; color: var(--ink); cursor: pointer;
}
.cn-view:hover { border-color: var(--signal); }
.cn-view:focus-visible { outline: 2px solid var(--signal); outline-offset: 1px; }
.cn-view .n { color: var(--muted); font-variant-numeric: tabular-nums; }
.cn-view[aria-pressed="true"] { background: var(--signal-tint); border-color: var(--signal); color: var(--signal); font-weight: var(--w-medium); }
.cn-view[aria-pressed="true"] .n { color: var(--signal); }
/* Your own name is not a competitor, and the two must not read as one row of
   equivalent chips. */
.cn-view.is-you[aria-pressed="true"] { background: var(--signal); border-color: var(--signal); color: #fff; }
.cn-view.is-you[aria-pressed="true"] .n { color: rgba(255,255,255,.82); }

/* A source opens to the pages behind it. The domain answers which door the
   assistant walked through; the path is the thing somebody can actually go
   and read. */
.cn-src-row { cursor: pointer; }
.cn-src-row .cn-caret { display: inline-block; color: var(--muted); transition: transform .12s ease; font-size: var(--t-meta); }
.cn-src-row[aria-expanded="true"] .cn-caret { transform: rotate(90deg); }
.cn-src-row[aria-expanded="true"] td { border-bottom-color: transparent; }
.cn-pages td { background: var(--dash-bg); padding-top: var(--s-2); padding-bottom: var(--s-3); }
.cn-page { display: grid; grid-template-columns: 1fr auto; gap: 2px var(--s-3); padding: var(--s-2) 0; border-top: 1px solid var(--hairline); align-items: baseline; }
.cn-page:first-child { border-top: 0; }
.cn-page a { font-size: var(--t-meta); color: var(--ink); text-decoration: none; word-break: break-all; }
.cn-page a:hover { text-decoration: underline; text-decoration-color: var(--signal); }
.cn-page .pth { grid-column: 1; font-size: var(--t-micro); color: var(--muted); word-break: break-all; }
.cn-page .qty { grid-column: 2; grid-row: 1 / span 2; font-size: var(--t-meta); color: var(--muted); font-variant-numeric: tabular-nums; white-space: nowrap; }
.cn-page-more { font-size: var(--t-micro); color: var(--muted); padding-top: var(--s-2); }
@media (prefers-reduced-motion: reduce) { .cn-src-row .cn-caret { transition: none; } }

/* ==========================================================================
   PEOPLE
   Who else can reach this business. Drawn only for an owner or an admin, so
   every row here belongs to somebody who may also change it.
   ========================================================================== */
.cn-people-list { list-style: none; margin: 0 0 var(--s-4); padding: 0; }
.cn-person {
  display: flex; align-items: baseline; gap: var(--s-3);
  padding: var(--s-3) 0; border-bottom: 1px solid var(--hairline);
  /* WRAPS AT ANY WIDTH, not only below a viewport breakpoint. The meta on the
     right is longer than it looks ("invited, has not signed in yet") and it was
     squeezing the address down to one syllable per line. A media query did not
     fix it because what is narrow here is the COLUMN, not the window: the same
     squeeze happens on a wide screen with a narrow console. */
  flex-wrap: wrap;
}
.cn-person:last-child { border-bottom: 0; }
/* The address is the identity here, so it gets a floor it cannot be squeezed
   below and takes a line of its own when the rest will not fit beside it. It
   wraps rather than truncating, because half an email names nobody. */
.cn-person-who { flex: 1 1 14rem; min-width: 0; overflow-wrap: anywhere; color: var(--ink); font-size: var(--t-body); }
.cn-person-lvl { flex: 0 0 auto; font-size: var(--t-meta); color: var(--muted); }
/* An invitation nobody has picked up. Muted rather than coloured: it is a
   state, not a problem, and an owner who sees red assumes something failed. */
.cn-person-state { flex: 0 0 auto; font-size: var(--t-meta); color: var(--cn-flat); font-style: italic; }
.cn-person-x {
  flex: 0 0 auto; border: 0; background: none; padding: 0;
  font-size: var(--t-meta); color: var(--muted); cursor: pointer; text-decoration: underline;
}
.cn-person-x:hover { color: var(--ink); }

.cn-person-add { display: flex; flex-wrap: wrap; gap: var(--s-2); align-items: center; }
.cn-person-add input, .cn-person-add select {
  font: inherit; font-size: var(--t-body); color: var(--ink);
  border: 1px solid var(--hairline); border-radius: var(--r-control, 8px);
  padding: var(--s-2) var(--s-3); background: #fff;
}
/* The address is the long field and the level is the short one, so the row
   collapses to one column on a phone rather than squeezing both. */
.cn-person-add input { flex: 1 1 16rem; min-width: 0; }
.cn-person-add select { flex: 0 0 auto; }
@media (max-width: 560px) {
  .cn-person-add { flex-direction: column; align-items: stretch; }
  .cn-person-add input, .cn-person-add select, .cn-person-add .dash-btn { width: 100%; }
  /* FLEX-BASIS IS THE HEIGHT ONCE THE DIRECTION IS COLUMN, and the rule above
     sets `flex: 1 1 16rem` on the input for the row layout. Turning the
     container to a column without resetting it made the email field 256px tall,
     seven times the select beside it. Measured in Chrome at 390px, 2026-09-13.
     `width: 100%` above cannot save it: width is the cross axis here. */
  .cn-person-add input { flex: 0 0 auto; }
}
.cn-note-err { color: var(--down, #b4232a); }
