/**
 * NPD Monitor — styles.
 *
 * Palette, type and layout follow the other NP Digital internal dashboards
 * (mkt-dashboard-npd): orange #ff6b2b on near-black, Geomanist for text and
 * JetBrains Mono for anything numeric or label-like. Two internal tools for
 * the same team should not look like two different products.
 *
 * That dashboard is dark-only. This one supports both, because the people
 * reading it are not all sitting in a dark room — so every colour is a token
 * defined once per theme and nothing below hardcodes one. The theme is chosen
 * by a `data-theme` attribute set on <html> before first paint.
 */

/* ── Dark: the brand default ───────────────────────────────────────────── */
:root,
:root[data-theme="dark"] {
  --bg:       #080808;
  --surface:  #111111;
  --surface2: #1a1a1a;
  --border:   #242424;
  --border2:  #2e2e2e;

  --accent:   #ff6b2b;   /* NP Digital orange */
  --info:     #00c4ff;
  --err:      #ff4444;
  --warn:     #ffb347;
  --ok:       #35d07f;

  --text:     #f5f5f5;
  --muted:    #888888;
  --muted2:   #555555;

  --pill-ok-bg:   #12291d;  --pill-ok-fg:   #4ade80;
  --pill-warn-bg: #33260c;  --pill-warn-fg: #ffb347;
  --pill-err-bg:  #331414;  --pill-err-fg:  #ff6b6b;
  --pill-neu-bg:  #1f1f1f;  --pill-neu-fg:  #9099a6;

  --shadow: 0 1px 3px rgba(0,0,0,.4);
}

/* ── Light ─────────────────────────────────────────────────────────────── */
:root[data-theme="light"] {
  --bg:       #f6f7f9;
  --surface:  #ffffff;
  --surface2: #f0f2f5;
  --border:   #e3e6ea;
  --border2:  #d8dce2;

  --accent:   #e8551a;   /* the same orange, darkened to stay legible on white */
  --info:     #0284c7;
  --err:      #dc2626;
  --warn:     #b45309;
  --ok:       #15803d;

  --text:     #1b1f24;
  --muted:    #6b7280;
  --muted2:   #9099a6;

  --pill-ok-bg:   #dcfce7;  --pill-ok-fg:   #15803d;
  --pill-warn-bg: #fef3c7;  --pill-warn-fg: #b45309;
  --pill-err-bg:  #fee2e2;  --pill-err-fg:  #b91c1c;
  --pill-neu-bg:  #eef1f4;  --pill-neu-fg:  #4b5563;

  --shadow: 0 1px 2px rgba(0,0,0,.06);
}

* { box-sizing: border-box; margin: 0; padding: 0; }

/* The app swaps views with the `hidden` attribute, which the browser
 * implements as `[hidden] { display: none }` — a rule weak enough that any
 * class selector declaring `display` silently defeats it. `.login-wrap` below
 * sets `display: grid`, which once kept the login form on screen after a
 * successful sign-in and made a working login look broken. */
[hidden] { display: none !important; }

body {
  min-height: 100vh;
  background: var(--bg);
  color: var(--text);
  font: 15px/1.55 'Geomanist', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

/* Monospace for numbers and labels. Tabular figures matter in tables:
 * without them a column of numbers does not line up. */
.mono, table td, .pill, .field label, .sb-title, th {
  font-family: 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, monospace;
  font-variant-numeric: tabular-nums;
}

.muted  { color: var(--muted); }
.spacer { flex: 1; }
.error  { color: var(--err); font-size: 14px; }

a { color: var(--accent); }

/* ── Login ─────────────────────────────────────────────────────────────── */
.login-wrap {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 24px;
}

.login-box {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  padding: 44px 40px;
  width: 100%;
  max-width: 380px;
}

.login-logo-wrap { display: flex; align-items: center; gap: 10px; margin-bottom: 26px; }
.brand-logo      { height: 30px; width: auto; display: block; object-fit: contain; }

.login-title { font-size: 27px; font-weight: 800; letter-spacing: -.01em; }
.login-sub   { font-size: 13px; color: var(--muted); margin: 4px 0 30px; }

.field { margin-bottom: 16px; }
.field label {
  display: block;
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted2);
  margin-bottom: 7px;
}

input, select {
  width: 100%;
  background: var(--bg);
  border: 1px solid var(--border2);
  border-radius: 8px;
  color: var(--text);
  padding: 11px 14px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 13px;
  outline: none;
  transition: border-color .2s;
}
input:focus, select:focus { border-color: var(--accent); }

.login-err {
  margin-top: 14px;
  color: var(--err);
  font-size: 12px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  text-align: center;
}

/* ── Buttons ───────────────────────────────────────────────────────────── */
.btn-primary {
  width: 100%;
  background: var(--accent);
  color: #fff;
  border: none;
  border-radius: 8px;
  padding: 12px;
  font-family: inherit;
  font-weight: 700;
  font-size: 14px;
  cursor: pointer;
  transition: opacity .15s, transform .1s;
  margin-top: 4px;
}
.btn-primary:hover:not(:disabled) { opacity: .88; transform: translateY(-1px); }
.btn-primary:disabled             { opacity: .35; cursor: not-allowed; transform: none; }

.btn-sm, .btn-ghost {
  background: transparent;
  border: 1px solid var(--border2);
  border-radius: 7px;
  color: var(--muted);
  padding: 7px 11px;
  font-family: inherit;
  font-weight: 600;
  font-size: 12px;
  cursor: pointer;
  transition: border-color .2s, color .2s;
}
.btn-sm { width: 100%; text-align: center; }
.btn-sm:hover, .btn-ghost:hover { border-color: var(--accent); color: var(--accent); }
.btn-ghost:disabled { opacity: .4; cursor: default; border-color: var(--border); color: var(--muted2); }

.btn-danger:hover { border-color: var(--err); color: var(--err); }

.btn-icon {
  background: transparent;
  border: 1px solid var(--border2);
  border-radius: 7px;
  color: var(--muted);
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  flex-shrink: 0;
  transition: border-color .2s, color .2s;
}
.btn-icon:hover { border-color: var(--accent); color: var(--accent); }

/* ── Layout ────────────────────────────────────────────────────────────── */
.layout {
  display: grid;
  grid-template-columns: 240px 1fr;
  min-height: 100vh;
}

aside {
  background: var(--surface);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  position: sticky;
  top: 0;
  height: 100vh;
}

.sb-head  { padding: 22px 22px 18px; border-bottom: 1px solid var(--border); }
.sb-logo  { display: flex; align-items: center; gap: 9px; margin-bottom: 9px; }
.sb-title {
  font-size: 10px;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--muted2);
}

#tabs { display: flex; flex-direction: column; gap: 2px; padding: 14px 12px; flex: 1; overflow-y: auto; }

/* Two lines: the label, and the source it draws from. Vertical padding is
 * tighter than a single-line button would want — ten two-line buttons plus the
 * header and footer otherwise overflow a 900px-tall window, and #tabs scrolls
 * rather than the sidebar growing. */
#tabs button {
  font: inherit;
  display: flex;
  flex-direction: column;
  gap: 1px;
  line-height: 1.25;
  font-size: 13.5px;
  font-weight: 500;
  text-align: left;
  padding: 7px 12px;
  border: 0;
  border-radius: 7px;
  background: transparent;
  color: var(--muted);
  cursor: pointer;
  transition: background .15s, color .15s;
}
#tabs button:hover { background: var(--surface2); color: var(--text); }

/* The question each group of tabs answers.
 *
 * Same 10px mono small-caps as .sb-title above it, so the sidebar has one
 * vocabulary for "this is a label, not a thing you click". Deliberately quiet:
 * these are signposts read once while learning the dashboard, not every visit.
 *
 * Kept tight on purpose. The buttons are already two lines each and the note on
 * #tabs above records that ten of them plus the header and footer overflow a
 * 900px-tall window; six headings would push that further, so the margins here
 * are the smallest that still separate the groups. */
.tab-group {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  letter-spacing: .13em;
  text-transform: uppercase;
  /* Full text colour, which is the third value tried here and the first that
   * works. The headings shipped at --muted2, the same treatment as .tab-src
   * directly below them, so a heading and a data source were one kind of
   * thing. Moving to --muted fixed the contrast against the background and
   * not the actual problem: #tabs button is ALSO --muted, so the heading was
   * the same colour as the items under it, separated only by weight and size.
   *
   * At --text it is louder than the labels beneath, which sounds like an
   * inverted hierarchy and is not: 10px uppercase mono against 13.5px
   * sentence-case sans reads as a different kind of type, not a more
   * important item. This is the ordinary small-caps section header.
   *
   * Deliberately still not the accent colour. The active tab is marked with
   * orange text and an orange left rule, and six orange headings would leave
   * that competing for the one meaning it carries — and on the light theme
   * the accent is darkened for white and lands at 3.66:1, worse at this size
   * than the grey it would replace. */
  font-weight: 700;
  font-size: 10px;
  color: var(--text);
  padding: 11px 12px 0;
  margin: 3px 0 5px;
  /* A hairline does the grouping that colour should not have to. Paid for out
   * of the margin it replaces, so the sidebar is no taller than before — the
   * note on #tabs above records how little room there is. */
  border-top: 1px solid var(--border);
}
/* The first heading follows Overview, which is already separated from it. */
.tab-group:first-of-type { border-top: 0; padding-top: 4px; margin-top: 6px; }

/* Matches the .src idiom used under card headings, so "where does this come
 * from?" is answered the same way everywhere. */
.tab-src {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 9.5px;
  letter-spacing: .07em;
  text-transform: uppercase;
  color: var(--muted2);
  font-weight: 500;
}

/* The active tab carries the brand colour and a left edge, so which screen
 * you are on is legible without reading the label. */
#tabs button[aria-current="true"] {
  background: var(--surface2);
  color: var(--accent);
  font-weight: 700;
  box-shadow: inset 2px 0 0 var(--accent);
}

/* The source line must not inherit the accent colour or the bold weight from
 * the rule above — it would compete with the label it is subordinate to. */
#tabs button[aria-current="true"] .tab-src {
  color: var(--muted);
  font-weight: 500;
}

.sb-foot { padding: 14px 16px; border-top: 1px solid var(--border); }
.sb-user {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 9px;
  font-size: 12px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
}
.sb-user span { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

main { padding: 26px 28px; max-width: 1280px; }

@media (max-width: 820px) {
  .layout { grid-template-columns: 1fr; }
  aside   { position: static; height: auto; }
  #tabs   { flex-direction: row; flex-wrap: wrap; align-items: baseline; }
  /* A heading in a wrapped row would sit inline between two buttons and read as
   * one of them. Full width forces the break that the column layout gets free. */
  .tab-group { flex: 0 0 100%; padding: 10px 12px 0; margin: 2px 0 2px; }
  .tab-group:first-of-type { padding-top: 2px; margin-top: 2px; }
  /* Wrapped into a row, a two-line button can be squeezed to the width of its
   * longest word and break the source line across three lines. */
  #tabs button { min-width: 150px; }
  #tabs button[aria-current="true"] { box-shadow: inset 0 -2px 0 var(--accent); }
  main    { padding: 18px 14px; }
}

/* ── Section headings ──────────────────────────────────────────────────── */
/*
 * Names the question a run of cards answers, matching the groups in the
 * sidebar so the two layouts teach each other.
 *
 * Bigger than a card's own h2 and set apart by space rather than by a border
 * or a background: the cards below already carry borders, and another one here
 * would read as a box containing them rather than a label above them.
 */
.section-head {
  margin: 34px 0 14px;
}
.section-head:first-child { margin-top: 0; }
.section-head h2 {
  font-size: 19px;
  font-weight: 800;
  letter-spacing: -.02em;
  margin: 0;
}
.section-head p {
  margin: 5px 0 0;
  font-size: 13.5px;
  max-width: 78ch;
}

/* ── Cards ─────────────────────────────────────────────────────────────── */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 20px;
  box-shadow: var(--shadow);
}
.card + .card { margin-top: 16px; }

.card-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  flex-wrap: wrap;
}
.card-head h2 { margin: 0; }

h2 { font-size: 16px; font-weight: 700; margin: 0 0 4px; letter-spacing: -.01em; }
h2 + .muted { margin: 0 0 16px; font-size: 13.5px; }

code {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: .88em;
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 1px 5px;
}

/* A fixed height is required: Chart.js with maintainAspectRatio false grows to
 * fill its parent, and a parent sized by its content grows to fit the canvas —
 * the two chase each other and the canvas expands on every resize event. */
.chart-wrap { position: relative; height: 260px; margin-top: 14px; }

/* Overview stacks two charts in one card alongside four other blocks, so they
 * are shorter there. A variant rather than a change to .chart-wrap, which the
 * detail tabs want at full height. */
.chart-short { height: 180px; }

select { width: auto; padding: 6px 10px; font-size: 12px; }

/* ── Table ─────────────────────────────────────────────────────────────── */
.table-wrap { overflow-x: auto; }

table { width: 100%; border-collapse: collapse; font-size: 13px; }

th, td {
  text-align: left;
  padding: 9px 12px;
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

th {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: .1em;
  color: var(--muted2);
  font-weight: 600;
}

tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover td      { background: var(--surface2); }

/* A URL Google will not index is the reason the index table exists, and one
 * red pill in a column of green ones is easy to scroll past. The row itself
 * changes, so the problem is visible before anything is read. */
tr.flagged td, tr.flagged:hover td {
  background: var(--pill-err-bg);
  box-shadow: inset 2px 0 0 var(--err);
}

/* ── Status pill ───────────────────────────────────────────────────────── */
.pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 99px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .02em;
}

.pill-ok      { background: var(--pill-ok-bg);   color: var(--pill-ok-fg); }
.pill-warn    { background: var(--pill-warn-bg); color: var(--pill-warn-fg); }
.pill-err     { background: var(--pill-err-bg);  color: var(--pill-err-fg); }
.pill-neutral { background: var(--pill-neu-bg);  color: var(--pill-neu-fg); }

/* ── Banner ────────────────────────────────────────────────────────────── */
.banner {
  border: 1px solid var(--err);
  background: var(--pill-err-bg);
  color: var(--pill-err-fg);
  border-radius: 8px;
  padding: 11px 14px;
  font-size: 13px;
  margin-bottom: 14px;
}
.banner.warn { border-color: var(--warn); background: var(--pill-warn-bg); color: var(--pill-warn-fg); }
/* A banner reporting that something is now fixed. It stays a banner rather than
 * becoming plain prose because it is still explaining a gap in the numbers
 * below — but red would keep raising an alarm about a problem that is over. */
.banner.ok { border-color: var(--ok); background: var(--pill-ok-bg); color: var(--pill-ok-fg); }
.banner code { background: transparent; border-color: currentColor; }

/* ── Forms inside cards (user management) ──────────────────────────────── */
.form-row {
  display: flex;
  gap: 10px;
  flex-wrap: wrap;
  align-items: flex-end;
  margin-top: 14px;
}
.form-row .field       { margin-bottom: 0; flex: 1 1 160px; }
.form-row .btn-primary { width: auto; padding: 11px 20px; margin-top: 0; }

/* ── Freshness indicator ───────────────────────────────────────────────── */
.freshness {
  display: flex;
  align-items: center;
  gap: 7px;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10px;
  color: var(--muted2);
  margin-bottom: 10px;
}

.freshness .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--muted2);
  flex-shrink: 0;
}
.freshness.ok   .dot { background: var(--ok); }
.freshness.warn .dot { background: var(--warn); }
.freshness.err  .dot { background: var(--err); }
.freshness.warn, .freshness.err { color: var(--text); }

/* The data source under a card heading. Small and quiet — it answers "where
 * does this number come from?" without competing with the heading. */
.src {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--muted2);
  margin: 3px 0 12px;
}
.src + .muted { margin-top: -6px; }

/* The full URL under a page's name. Three of the tracked pages are called
 * "Home", so the name alone cannot identify a row — the URL is what tells them
 * apart, and it is the thing someone would copy to go and look. */
.cell-url {
  font-size: 11px;
  margin-top: 2px;
  word-break: break-all;
}

/* A note under a chart saying the source ran out before the range did. Sits
 * tight to the chart it belongs to, so it cannot be read as applying to the
 * next one down. */
.range-note {
  margin-top: 8px;
  font-size: 12px;
  border-left: 2px solid var(--border);
  padding-left: 10px;
}

/* The per-metric legend on the Overview site cards. Every metric there covers a
 * different period, and the list is the only honest way to say so — as prose it
 * was one sentence nobody finished reading. */
.legend {
  margin: 0 0 14px;
  padding-left: 18px;
  line-height: 1.7;
}
.legend li { margin: 0; }

/* ── Overview ──────────────────────────────────────────────────────────── */
/* One alert per row, colour-coded by severity down the left edge. The bar is
 * a border rather than a filled background: a screen of eight filled red rows
 * reads as a catastrophe even when most of them are informational. */
.alert {
  display: flex;
  gap: 12px;
  align-items: baseline;
  padding: 11px 14px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--muted2);
  border-radius: 8px;
  background: var(--surface2);
  font-size: 13px;
}
.alert + .alert { margin-top: 8px; }
.alert-critical { border-left-color: var(--err); }
.alert-warning  { border-left-color: var(--warn); }
.alert-info     { border-left-color: var(--info); }

.alert-body   { flex: 1; min-width: 0; }
.alert-title  { font-weight: 600; }
.alert-detail { color: var(--muted); font-size: 12.5px; margin-top: 2px; }
.alert-site {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 10px;
  letter-spacing: .06em;
  color: var(--muted2);
  white-space: nowrap;
}

/* auto-fit rather than a fixed column count: the site list is driven by the
 * database, and a hardcoded grid breaks the day a fifth site is added. */
.site-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(230px, 1fr));
  gap: 12px;
  margin-top: 14px;
}

.site-card {
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 14px 16px;
  background: var(--surface2);
}
.site-card h3 {
  font-size: 13.5px;
  font-weight: 700;
  margin: 0 0 12px;
  letter-spacing: -.01em;
  overflow-wrap: anywhere;
}

.metric { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; }
.metric + .metric { margin-top: 9px; }
.metric-label {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: .09em;
  color: var(--muted2);
  font-family: 'JetBrains Mono', ui-monospace, monospace;
}
.metric-value { font-size: 14px; font-weight: 600; font-variant-numeric: tabular-nums; }

/* A change is not good or bad on its own — fewer visits is bad, and this is
 * only ever used for visits and clicks, so up is green. Anything with the
 * opposite polarity must not reuse this. */
.delta { font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums; }
.delta-up   { color: var(--ok); }
.delta-down { color: var(--err); }
.delta-flat { color: var(--muted2); }

/* ── Collapsed alert groups ─────────────────────────────────────────────── */
/* Styled to match .alert so the closed state reads as another row in the list
 * rather than as a separate control. */
.alert-group + .alert-group,
.alert + .alert-group { margin-top: 8px; }

.alert-group summary {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 11px 14px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--muted2);
  border-radius: 8px;
  background: var(--surface2);
  font-size: 13px;
  cursor: pointer;
  list-style: none;
  user-select: none;
}
.alert-group summary::-webkit-details-marker { display: none; }

/* The disclosure triangle, drawn rather than inherited, so it rotates and
 * matches the palette in both themes. */
.alert-group summary::after {
  content: '';
  margin-left: auto;
  width: 6px;
  height: 6px;
  border-right: 1.5px solid var(--muted2);
  border-bottom: 1.5px solid var(--muted2);
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform .15s;
}
.alert-group[open] summary::after { transform: rotate(-135deg) translate(-2px, -2px); }

.alert-group summary:hover { border-color: var(--border2); }
.alert-group[open] summary { border-radius: 8px 8px 0 0; }

.alert-group-body {
  border: 1px solid var(--border);
  border-top: 0;
  border-radius: 0 0 8px 8px;
  padding: 8px;
}
.alert-group-body .alert { border: 0; background: transparent; padding: 8px 6px; }
.alert-group-body .alert + .alert { margin-top: 0; border-top: 1px solid var(--border); }

/* ── Form-test history strip ────────────────────────────────────────────── */
.run-rows { margin-top: 16px; }

.run-row {
  display: flex;
  align-items: center;
  gap: 14px;
  padding: 9px 0;
  border-bottom: 1px solid var(--border);
}
.run-row:last-child { border-bottom: none; }

.run-site { flex: 0 0 190px; font-size: 13px; font-weight: 600; overflow-wrap: anywhere; }

/* Inside an Overview card the strip shares the page with four other blocks,
 * so it gets a narrower name column and shorter squares. A modifier rather
 * than an override of .run-row, so the Form tests tab keeps its full size. */
.run-row-compact { padding: 7px 0; }
.run-row-compact .run-site { flex: 0 0 150px; font-size: 12.5px; }
.run-row-compact .run-cell { height: 16px; width: 9px; }

/* Wraps rather than scrolls: a run that fell off the right edge is a run
 * nobody checked, and the whole point of the strip is that it is all visible. */
.run-strip { flex: 1; display: flex; flex-wrap: wrap; gap: 3px; }

.run-cell {
  width: 11px;
  height: 22px;
  border-radius: 2px;
  background: var(--muted2);
  flex-shrink: 0;
}
.run-ok  { background: var(--ok); }
.run-bad { background: var(--err); }
/* A failure an admin marked as the test bot's fault: present, but neither red nor green. */
.run-ignored { background: repeating-linear-gradient(135deg, var(--muted2) 0 2px, transparent 2px 5px); box-shadow: inset 0 0 0 1px var(--muted2); }
.ft-fields { display: flex; flex-wrap: wrap; gap: 14px; align-items: flex-end; margin-top: 14px; }
.ft-reason { flex: 1; min-width: 260px; }
.ft-why { max-width: 360px; white-space: normal; overflow-wrap: anywhere; }
.ft-reason input { width: 100%; }

.run-tally {
  flex: 0 0 auto;
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px;
  font-weight: 600;
}

/* ── Dev Updates calendar (Notion) ──────────────────────────────────────── */
/* A month grid rather than a list, because the shape is the information: four
 * cards stacked on one Friday is a fact you read without reading anything. */

/* Three selects plus the arrows do not fit a narrow header — wrapping keeps
 * them usable instead of pushing the nav off the card. */
.controls { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; justify-content: flex-end; }
.controls select { max-width: 200px; }

.cal-nav { display: flex; gap: 4px; }
/* .btn-sm is full-width by default (it is a sidebar button elsewhere), which
 * would stretch each arrow across the header. */
.cal-nav .btn-sm { width: auto; padding: 6px 10px; }
.cal-nav .btn-sm:disabled { opacity: .35; cursor: default; border-color: var(--border); color: var(--muted2); }

.cal-month {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 12px;
  color: var(--muted);
  margin: 12px 0 8px;
}

.cal-grid {
  display: grid;
  grid-template-columns: repeat(7, minmax(0, 1fr));
  gap: 6px;
}
.cal-head { margin-bottom: 6px; }

.cal-wd {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px;
  color: var(--muted2);
  text-transform: uppercase;
  letter-spacing: .04em;
  padding-left: 2px;
}

.cal-day {
  min-height: 96px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface2);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}
/* Empty leading cells and quiet days recede — the eye should land on the days
 * something actually shipped. */
.cal-day.empty { background: transparent; border-color: transparent; }
.cal-day.quiet { background: transparent; }
.cal-day.today { border-color: var(--accent); }

.cal-daynum {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 11px;
  color: var(--muted);
  margin-bottom: 2px;
}
.cal-day.today .cal-daynum { color: var(--accent); font-weight: 600; }

.cal-card {
  display: block;
  width: 100%;
  text-align: left;
  border: 1px solid var(--border2);
  border-radius: 6px;
  background: var(--surface);
  color: var(--text);
  padding: 6px 7px;
  cursor: pointer;
  font: inherit;
  overflow: hidden;
}
.cal-card:hover { border-color: var(--accent); }

.cal-card-title {
  display: block;
  font-size: 12px;
  font-weight: 600;
  line-height: 1.3;
  /* Two lines, then ellipsis: a long title must not push a day cell taller
   * than its neighbours and break the grid the reader is scanning. */
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.cal-chips { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 4px; min-width: 0; }

.cal-chip {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 9.5px;
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 1px 4px;
  color: var(--muted);
  white-space: nowrap;
  /* A long domain — summitmembers.neilpatel.com is 27 characters — is wider
   * than a day cell, and nowrap alone let it run straight out through the card
   * border. Truncate instead: the full value is in the opened card. */
  max-width: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Stacked, not side by side: the author and the division were competing for one
 * narrow line and both ended in ellipsis, so neither could be read. Notion
 * stacks them for the same reason. */
.cal-card-meta {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 4px;
  margin-top: 5px;
  font-size: 10.5px;
  color: var(--muted2);
  min-width: 0;
}
.cal-who {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

.cal-divs { display: flex; flex-wrap: wrap; gap: 3px; max-width: 100%; }

/* A pill, matching how Notion renders a multi-select value. One per division
 * rather than the joined string, so "DE, FR, NL" reads as three tags. */
.cal-div {
  background: var(--pill-neu-bg);
  color: var(--pill-neu-fg);
  border-radius: 3px;
  padding: 1px 5px;
  font-size: 10px;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  max-width: 100%;
}

/* ── Flag glyphs ────────────────────────────────────────────────────────────
 * Windows renders no country flags at all: Segoe UI Emoji draws the
 * regional-indicator pair as two boxed letters, on purpose, so "FR 🇫🇷" from
 * Notion arrives on screen as "FR fr" for the whole team.
 *
 * The fix is this font — but scoped. Declared through the normal Google Fonts
 * stylesheet it also answered for digits, because Noto Color Emoji carries the
 * keycap numerals, and "August 2026 (36)" rendered as spaced-out emoji digits
 * in the month picker. `unicode-range` confines it to the regional indicators
 * (U+1F1E6–U+1F1FF) and nothing else, so it can be listed first anywhere
 * without touching text or numbers.
 *
 * The file is Twemoji's flag set, 76 KB, vendored next to chart.js. Google's
 * Noto Color Emoji would have worked too and was tried first — but it ships as
 * a single 25 MB bitmap TTF that cannot be range-subsetted, and unicode-range
 * does not help when the page really does contain a flag: the browser downloads
 * all 25 MB. This one holds flags and nothing else. */
@font-face {
  font-family: 'FlagEmoji';
  font-style: normal;
  font-weight: 400;
  font-display: swap;
  src: url(/vendor/TwemojiCountryFlags.woff2) format('woff2');
  unicode-range: U+1F1E6-1F1FF;
}

.cal-div,
.cal-card-meta,
.dlg-val,
.controls select,
.controls option {
  font-family: 'FlagEmoji', 'Geomanist', -apple-system, BlinkMacSystemFont, sans-serif;
}

/* ── The opened card ────────────────────────────────────────────────────── */
.cal-dialog {
  /* `* { margin: 0 }` at the top of this file wipes the browser's own
   * `dialog { margin: auto }`, which is the ONLY thing centring a modal
   * <dialog>. Without this line it renders pinned to the top-left corner and
   * looks like a positioning bug rather than an inherited reset. */
  margin: auto;
  border: 1px solid var(--border2);
  border-radius: 12px;
  background: var(--surface);
  color: var(--text);
  padding: 24px;
  max-width: 640px;
  width: calc(100% - 32px);
  max-height: 82vh;
  overflow-y: auto;
  box-shadow: 0 8px 32px rgba(0,0,0,.35);
}
.cal-dialog::backdrop { background: rgba(0,0,0,.55); }

.dlg-title { font-size: 20px; font-weight: 700; margin: 0 0 16px; letter-spacing: -.01em; }

.dlg-props { display: flex; flex-direction: column; gap: 7px; margin-bottom: 18px; }
.dlg-row { display: flex; gap: 12px; align-items: baseline; }
.dlg-key {
  flex: 0 0 128px;
  font-size: 12px;
  color: var(--muted);
}
.dlg-val { flex: 1; font-size: 13px; display: flex; flex-wrap: wrap; gap: 4px; align-items: center; }

.dlg-body {
  font-size: 13.5px;
  line-height: 1.6;
  border-top: 1px solid var(--border);
  padding-top: 16px;
}
/* Notion's code annotation is how these cards carry lists of short codes — the
 * 58 geolocations on one of them. They have to stay readable as chips rather
 * than run together as prose. */
.dlg-body code { font-size: 11.5px; color: var(--accent); }
.dlg-body a { color: var(--info); }

.dlg-close { width: auto; margin-top: 20px; padding: 6px 14px; }
.dlg-link {
  display: inline-block;
  margin-top: 16px;
  margin-right: 12px;
  font-size: 12.5px;
  color: var(--info);
}

@media (max-width: 900px) {
  /* Seven columns of cards do not survive a phone. The grid becomes a single
   * column of days, which is the same information read top to bottom. */
  .cal-grid { grid-template-columns: 1fr; }
  .cal-head { display: none; }
  .cal-day.empty { display: none; }
  .cal-day.quiet { display: none; }
  .cal-day { min-height: 0; }
}

/* The Dev Updates card behind an alert.
 *
 * The card's own Status carries the colour — green when the team marked it
 * Done — while the alert around it stays red. That split is the whole point:
 * green says "someone has been here", red says "the last run still failed".
 * Letting the green spread to the alert would have the dashboard repeat a
 * claim it cannot check, and only a passing run can make that claim. */
.alert-link {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  margin-top: 6px;
  font-size: 12px;
  color: var(--muted);
  max-width: 100%;
  /* A button, not a link: it navigates inside the dashboard rather than out to
   * notion.so, so it must not look like it leaves the page. */
  font-family: inherit;
  background: none;
  border: 0;
  padding: 0;
  text-align: left;
  cursor: pointer;
}
.alert-link:hover { color: var(--accent); }
.alert-link:hover .alert-link-title { text-decoration: underline; }

/* Green once the team has marked the card finished — and only here. The alert
 * around it stays red: green says "someone has been here", red says "the last
 * run still failed", and those are two different facts. */
.alert-link-done { color: var(--pill-ok-fg); }
.alert-link-done .alert-link-tag { border-color: var(--pill-ok-fg); color: var(--pill-ok-fg); }

.alert-link-title { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
.alert-link-go    { flex: 0 0 auto; }

/* Smaller than a severity pill — it is a status on a link, not a verdict on
 * the row. */
.alert-link .pill { font-size: 9.5px; padding: 1px 6px; flex: 0 0 auto; }

.alert-link-tag {
  font-family: 'JetBrains Mono', ui-monospace, monospace;
  font-size: 9.5px;
  text-transform: uppercase;
  letter-spacing: .04em;
  background: var(--surface2);
  border: 1px solid var(--border);
  border-radius: 3px;
  padding: 1px 5px;
  flex: 0 0 auto;
}

/* ── Marketing: funnel, forecast and sessions ──────────────────────────────
 * Everything here is prefixed mk- and drawn by public/marketing.js.
 *
 * The series colours are an eight-slot categorical palette validated for
 * colour-blind separation on both surfaces; the order is the safety mechanism,
 * so the slots are used in this order and never re-sorted. A colour belongs to
 * the thing it stands for (a division, a source), not to its rank, so a filter
 * that removes one series does not repaint the others. The sequential blues are
 * for the funnel stages, lightest to darkest.
 */
:root,
:root[data-theme="dark"] {
  --mk-s1: #3987e5; --mk-s2: #d95926; --mk-s3: #199e70; --mk-s4: #c98500;
  --mk-s5: #d55181; --mk-s6: #008300; --mk-s7: #9085e9; --mk-other: #6b7280;
  --mk-o1: #86b6ef; --mk-o2: #5598e7; --mk-o3: #2a78d6; --mk-o4: #256abf; --mk-o5: #1c5cab;
  --mk-up: #4ade80; --mk-down: #ff6b6b; --mk-wash: rgba(255, 255, 255, .05);
}
:root[data-theme="light"] {
  --mk-s1: #2a78d6; --mk-s2: #eb6834; --mk-s3: #1baf7a; --mk-s4: #eda100;
  --mk-s5: #e87ba4; --mk-s6: #008300; --mk-s7: #6250d6; --mk-other: #9aa0a8;
  --mk-up: #15803d; --mk-down: #dc2626; --mk-wash: rgba(0, 0, 0, .04);
}

.controls .btn-sm, .card-head .btn-sm { width: auto; }
select:disabled { opacity: .45; cursor: not-allowed; }

.mk-filters { display: flex; flex-wrap: wrap; gap: 10px 16px; align-items: flex-end; padding: 12px 16px; margin-bottom: 16px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 12px; box-shadow: var(--shadow); position: sticky; top: 8px; z-index: 20; }
.mk-f { display: flex; flex-direction: column; gap: 4px; }
.mk-f > span { font-family: 'JetBrains Mono', ui-monospace, monospace; font-size: 10px; letter-spacing: .1em; text-transform: uppercase; color: var(--muted2); }
.mk-chk { white-space: nowrap; display: flex; gap: 7px; align-items: center; font-size: 12px; color: var(--muted); min-height: 30px; cursor: pointer; }
/* The monitor styles every <input> as a full-width text field; a checkbox has to opt out. */
.mk-chk input[type="checkbox"], .mk-dd-panel input[type="checkbox"] { width: 15px; height: 15px; padding: 0; margin: 0; flex: none; background: none; border-radius: 3px; accent-color: var(--accent); }

.mk-dd { position: relative; }
.mk-dd-btn { background: var(--surface2); color: var(--text); border: 1px solid var(--border2); border-radius: 7px; padding: 6px 10px; font: inherit; font-size: 12px; cursor: pointer; min-width: 150px; text-align: left; }
.mk-dd-panel { position: absolute; top: calc(100% + 4px); left: 0; min-width: 230px; max-height: 340px; overflow: auto; background: var(--surface); border: 1px solid var(--border2); border-radius: 8px; padding: 6px; box-shadow: 0 8px 24px rgba(0, 0, 0, .35); display: none; z-index: 30; }
.mk-dd.open .mk-dd-panel { display: block; }
.mk-dd-panel label { display: flex; gap: 8px; align-items: center; padding: 5px 8px; border-radius: 5px; cursor: pointer; font-size: 12px; white-space: nowrap; }
.mk-dd-panel label:hover { background: var(--mk-wash); }
.mk-dd-panel label i { width: 10px; height: 10px; border-radius: 2px; flex: none; }
.mk-dd-panel hr { border: 0; border-top: 1px solid var(--border); margin: 4px 0; }

.mk-seg { display: inline-flex; border: 1px solid var(--border2); border-radius: 7px; overflow: hidden; }
.mk-seg button { background: none; border: 0; color: var(--muted); padding: 6px 11px; font: inherit; font-size: 12px; font-weight: 600; cursor: pointer; }
.mk-seg button[aria-pressed="true"] { background: var(--surface2); color: var(--text); box-shadow: inset 0 -2px 0 var(--accent); }
.mk-dd-btn:focus-visible, .mk-seg button:focus-visible, .mk-band:focus-visible, .mk-t th[tabindex]:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

.mk-win { font-size: 12px; }
.mk-note { font-size: 12px; margin: 10px 0 0; max-width: 110ch; }
.mk-tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(148px, 1fr)); gap: 10px; margin: 14px 0; }
.mk-tile { background: var(--surface2); border: 1px solid var(--border); border-radius: 10px; padding: 12px 14px; min-height: 112px; }
.mk-tile .mk-k { font-size: 12px; color: var(--muted); }
.mk-tile .mk-v { font-size: 28px; font-weight: 700; line-height: 1.15; margin: 2px 0; letter-spacing: -.02em; }
.mk-tile .mk-d { font-size: 12px; color: var(--muted); display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.mk-tile svg { display: block; margin-top: 6px; }
.mk-tile .mk-by { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--border); display: grid; gap: 4px; }
.mk-tile .mk-by-row { display: grid; grid-template-columns: 10px minmax(0, 1fr) auto auto; gap: 8px; align-items: baseline; font-size: 12px; }
.mk-tile .mk-by-row i { width: 8px; height: 8px; border-radius: 2px; align-self: center; }
.mk-tile .mk-by-n { color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mk-tile .mk-by-row span:last-child { min-width: 64px; text-align: right; }
.mk-up { color: var(--mk-up); }
.mk-down { color: var(--mk-down); }

.mk-legend { display: flex; flex-wrap: wrap; gap: 6px 16px; margin: 10px 0 2px; font-size: 12px; color: var(--muted); }
.mk-legend i { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 6px; vertical-align: -1px; }
.mk-chart { width: 100%; margin-top: 6px; }
.mk-chart svg { display: block; width: 100%; height: auto; overflow: visible; }
.mk-chart text { font-family: inherit; fill: var(--muted); font-size: 11px; }
.mk-chart .mk-val { fill: var(--text); font-weight: 600; }
.mk-chart .mk-grid { stroke: var(--border); stroke-width: 1; }
.mk-chart .mk-band { outline: none; }
.mk-chart .mk-band:hover .mk-hit, .mk-chart .mk-band:focus .mk-hit { fill: var(--mk-wash); }
.mk-chart .mk-bar { transition: opacity .12s; }
.mk-pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(480px, 1fr)); gap: 16px; }
.mk-ctitle { font-size: 13px; font-weight: 600; margin: 10px 0 0; }

.mk-t th, .mk-t td { text-align: right; }
.mk-t th:first-child, .mk-t td:first-child { text-align: left; }
.mk-t th[tabindex] { cursor: pointer; }
.mk-t th[aria-sort] { color: var(--text); box-shadow: inset 0 -2px 0 var(--accent); }
.mk-t thead th { position: sticky; top: 0; background: var(--surface); z-index: 1; }
.mk-t tr.mk-total td { font-weight: 700; background: var(--surface2); }
/* A thin bar under the sorted column, on a visible track: a full bar is the largest row. */
.mk-t td.mk-db { background: linear-gradient(to right, var(--mk-s1) var(--w, 0%), var(--border2) var(--w, 0%)) left bottom / 100% 3px no-repeat; }
.mk-t td.mk-na { color: var(--muted2); }
.mk-sw { display: inline-block; width: 10px; height: 10px; border-radius: 2px; margin-right: 8px; vertical-align: -1px; }
.mk-scroll { max-height: 62vh; overflow: auto; border: 1px solid var(--border); border-radius: 8px; }
.mk-twin { margin-top: 10px; display: none; }
.mk-twin.on { display: block; }

.mk-funnel { display: grid; grid-template-columns: 130px 1fr; gap: 6px 12px; align-items: center; font-size: 13px; margin-top: 6px; }
.mk-funnel-cell { display: flex; align-items: center; gap: 10px; min-width: 0; }
.mk-bar-h { height: 22px; border-radius: 0 4px 4px 0; min-width: 2px; }

.mk-tip { position: fixed; z-index: 99; pointer-events: none; background: var(--surface); border: 1px solid var(--border2); border-radius: 8px; padding: 8px 11px; font-size: 12px; box-shadow: 0 8px 24px rgba(0, 0, 0, .4); display: none; max-width: 280px; }
.mk-tip-title { color: var(--muted); margin-bottom: 4px; }
.mk-tip-row { display: flex; gap: 8px; align-items: center; justify-content: space-between; }
.mk-tip-row > span:first-child { display: flex; gap: 7px; align-items: center; color: var(--muted); }
.mk-tip-row b { color: var(--text); font-size: 13px; }
.mk-tip-key { width: 12px; height: 3px; display: inline-block; border-radius: 2px; }

.mk-defs { margin-top: 18px; font-size: 12px; color: var(--muted); }
.mk-defs summary { cursor: pointer; color: var(--text); }
.mk-defs ul { margin: 8px 0 0; padding-left: 18px; }
.mk-defs li { margin: 3px 0; }

/* The sessions card sits in a wrapper after another card, where .card + .card cannot reach it. */
#mk-ov-found { margin-top: 16px; }

/* A grid item otherwise grows to its widest table and the page scrolls sideways. */
.layout > main { min-width: 0; }
@media (max-width: 700px) { .mk-funnel { grid-template-columns: 110px 1fr; } }
