/* Reveo Diktofon — the shell and the shared primitives.
 *
 * Every colour, face, radius and shadow below is a custom property from
 * reveo-tokens.css. That file is Reveo's design system copied token for token
 * from reveo-suhbat; NOTHING here defines a colour of its own, because a
 * product-local hex is exactly how two Reveo products drift apart until
 * somebody moving between call.reveo.uz and diktofon.reveo.uz thinks they
 * changed vendor. If a value is missing, it gets added to kit.css upstream and
 * re-synced — not invented here.
 *
 * The @import is a safety net: index.html also links the token sheet, before
 * this file, so it is fetched in parallel. The import means app.css still
 * resolves if that link is ever dropped.
 *
 * The CHROME is not here either. reveo-shell.css is Reveo's rail, brand bar and
 * topbar, copied from reveo-suhbat and loaded immediately before this file;
 * what lives below is only what this product adds on top of it, and it is
 * ordered after the shell so it can adjust it. Do not restate a shell rule
 * here — a second copy is what makes the next re-sync silently a no-op.
 *
 * The four surfaces live in pages.css, which loads after this file and depends
 * on it. No preprocessor and no CDN beyond the two webfonts: this is an
 * internal tool, read for eight hours a day by people looking for one
 * recording, so it is tuned for density and legibility over decoration. */

@import url('/reveo-tokens.css');

:root {
  /* Product shape, on top of the system's. The token sheet's --radius is 20px,
   * which is right for a marketing card and too round for a dense table of one
   * recording per row; the smaller two are the same scale, chosen per element
   * rather than redefined. */

  /* The offset a `position: sticky` heading INSIDE the scroll area has to clear.
   * It is zero, and the zero is the point: the topbar used to scroll with the
   * document, so a sticky column header had to stop 56px down or it slid under
   * the bar. In Reveo's shell the topbar is outside the scrolling element
   * entirely — `main` scrolls beneath a fixed 72px bar — so a sticky header's
   * containing block already starts below it, and any offset here is a gap
   * between the bar and the header with rows visible through it. Kept as a
   * variable, and not deleted, because pages.css spends it in several places
   * and that file belongs to another surface. */
  --topbar-h: 0px;
}

* {
  box-sizing: border-box;
}

/* The UA rule for [hidden] is `display: none`, which any class setting a
 * display value outranks — a hidden .btn (inline-flex) stays on screen. This
 * makes `el.hidden = true` mean what it says everywhere. */
[hidden] {
  display: none !important;
}

html,
body {
  margin: 0;
  padding: 0;
}

body {
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-b);
  font-size: var(--t-body);
  line-height: 1.45;
  -webkit-font-smoothing: antialiased;
}

h1,
h2,
h3 {
  font-family: var(--font-d);
  letter-spacing: -0.015em;
}

a {
  color: var(--brand-ink);
  text-decoration: none;
}
a:hover {
  text-decoration: underline;
}

/* Numbers you compare down a column get tabular figures. Nothing in this
 * product is set in mono because nothing is over in Suhbat either — except
 * identifiers you read left to right rather than magnitudes you compare, which
 * here means filenames and hashes. */
.num {
  font-variant-numeric: tabular-nums;
}
.mono {
  font-family: var(--font-m);
  font-size: 0.94em;
}

/* ── The shell, where this product joins Reveo's ─────────────────────────── */

/* The pane is `height: 100vh; overflow: hidden`, so something inside it has to
 * be the scroll container. That is `main`: the topbar then stays put while the
 * page moves under it, which is the whole reason the title lives up there. */
main.view {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

/* Signed out there is no nav to show and nothing to title, so the chrome goes
 * rather than standing empty around a sign-in card. `data-auth` is set on
 * <html> before the first module runs, so the rail is never painted for
 * somebody who is about to be shown the form. `pending` is the state before
 * the session call has answered — also chrome-less, because guessing wrong
 * flashes a full nav at a stranger. */
/* The grid is collapsed to one column directly rather than by zeroing --rail,
 * because the responsive block further down re-sets --rail on `.shell` and
 * would otherwise leave a 68px empty gutter beside the sign-in card. */
:root[data-auth='out'] .shell,
:root[data-auth='pending'] .shell {
  grid-template-columns: 1fr;
}
:root[data-auth='out'] .rail,
:root[data-auth='out'] .tb,
:root[data-auth='pending'] .rail,
:root[data-auth='pending'] .tb {
  display: none;
}

/* The right end of the topbar: who is signed in, whether the live stream is
 * up, and the way out. */
.tb-right {
  display: flex;
  align-items: center;
  gap: 10px;
  min-width: 0;
  color: var(--ink-2);
}
.whoami {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  max-width: 28ch;
}

/* ── The live indicator ──────────────────────────────────────────────────── */

/* Quiet on purpose. A stream that is working is not news and must not compete
 * with the page; a stream that has dropped is, because every row on screen is
 * then a snapshot of some earlier moment and nothing will say so on its own. */
.live {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: var(--t-small);
  color: var(--ink-3);
  white-space: nowrap;
}
.live-dot {
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--brand);
  flex: none;
}
.live[data-state='reconnecting'] {
  color: var(--amber);
}
.live[data-state='reconnecting'] .live-dot {
  background: var(--amber);
  animation: live-pulse 1.4s ease-in-out infinite;
}
.live[data-state='offline'] {
  color: var(--red-ink);
}
.live[data-state='offline'] .live-dot {
  background: var(--red);
}
/* Connected is a dot and no words: the label would be furniture on every page
 * load, and the state that needs a sentence is the one that has none. */
.live[data-state='live'] .live-label {
  display: none;
}

@keyframes live-pulse {
  0%,
  100% {
    opacity: 1;
  }
  50% {
    opacity: 0.25;
  }
}

/* ── Layout ──────────────────────────────────────────────────────────────── */

/* Content is centred with padding rather than `max-width` + `margin: auto`,
 * because this element is also the scroll container: a max-width on it would
 * park the scrollbar 1240px from the left edge instead of at the edge of the
 * pane, which looks like a rendering bug at wide viewports. */
.view {
  padding: 22px max(24px, calc((100% - 1240px) / 2)) 72px;
}

/* The title row inside the bar: the page name, and the count the view was
 * showing beside it. `.tb-title h1` and `.tb-title p` are reveo-shell.css's
 * and still match through this wrapper — it only puts the count on the same
 * baseline as the name instead of under it. */
.tb-heading {
  display: flex;
  align-items: baseline;
  gap: 9px;
  min-width: 0;
}
/* The slot itself lays nothing out: `display: contents` puts the view's count
 * element directly into the heading row, so the rules below can style THAT
 * element — the one the view keeps writing to — rather than a wrapper that
 * would draw a padded pill around an empty string while a list is loading. */
.tb-count {
  display: contents;
}
/* Sized and coloured like the rail's own count in reveo-shell.css, because it
 * is the same kind of figure: a quiet number attached to a label, never
 * competing with the label. Empty is invisible rather than a grey lozenge,
 * which reads as a badge that failed to load. */
.tb-heading .count {
  font-family: var(--font-b);
  font-variant-numeric: tabular-nums;
  font-size: 12px;
  font-weight: 700;
  color: var(--ink-2);
  background: var(--surface-hover);
  border-radius: var(--pill);
  padding: 2px 8px;
  flex: none;
}
.tb-heading .count:empty {
  display: none;
}

/* What is LEFT of a view's own heading row after the router has lifted its
 * <h1> and its count into the topbar: the upload view's back link, and
 * whatever a future view puts there. Those are page furniture; the title and
 * the number beside it are facts about where you are and belong in the bar. A
 * row that ends up holding nothing at all takes no space — see `:empty`. */
.page-head {
  display: flex;
  align-items: baseline;
  gap: 12px;
  margin: 0 0 16px;
  color: var(--ink-3);
  font-size: var(--t-small);
}
.page-head:empty {
  display: none;
  margin: 0;
}
/* Still styled, because a view is free to render a heading the router has not
 * been taught to lift, and it must not then be set in body copy. */
.page-head h1 {
  font-size: var(--t-h1);
  font-weight: 600;
  margin: 0;
  color: var(--ink);
}
.page-head .count {
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.page-head .spacer {
  margin-left: auto;
}

.loading {
  color: var(--ink-3);
  padding: 24px 16px;
}

.noscript {
  padding: 24px;
  color: var(--ink-2);
  font-family: var(--font-b);
}

/* ── Controls ────────────────────────────────────────────────────────────── */

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

.input,
.select {
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-xs);
  padding: 7px 10px;
  min-width: 0;
  transition: border-color 0.16s var(--ease), box-shadow 0.16s var(--ease);
}
.input:hover,
.select:hover {
  border-color: var(--line-strong);
}
.input:focus,
.select:focus {
  border-color: var(--brand);
  outline: none;
  box-shadow: 0 0 0 3px var(--brand-soft);
}
.btn:focus-visible,
.row:focus-visible,
.input:focus-visible,
.select:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 1px;
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  /* «Chiqish» wrapping to two lines inside a fixed-height top bar broke the
   * row at 390px. Nothing in this bar wraps. */
  white-space: nowrap;
  background: var(--surface);
  border: 1px solid var(--line-2);
  border-radius: var(--radius-xs);
  padding: 7px 12px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  transition: background 0.16s var(--ease), border-color 0.16s var(--ease),
    box-shadow 0.16s var(--ease);
}
.btn:hover:not(:disabled) {
  background: var(--surface-hover);
  border-color: var(--line-strong);
}
.btn:active:not(:disabled) {
  transform: translateY(0.5px);
}
.btn:disabled {
  opacity: 0.5;
  cursor: default;
}
.btn-primary {
  background: var(--brand);
  border-color: var(--brand);
  color: var(--on-brand);
  box-shadow: var(--shadow-xs);
}
.btn-primary:hover:not(:disabled) {
  background: var(--brand-ink);
  border-color: var(--brand-ink);
}
.btn-danger {
  color: var(--red-ink);
  border-color: var(--line-2);
}
.btn-danger:hover:not(:disabled) {
  background: var(--red-soft);
  border-color: var(--red);
}
.btn-sm {
  padding: 4px 9px;
  font-size: var(--t-small);
}

/* ── Chips ───────────────────────────────────────────────────────────────── */

/* Suhbat's chip: raised on --shadow-xs, no border, and the colour always paired
 * with the word so the pill is never the only thing saying it. A border AND a
 * tint was one outline too many on a pill five characters wide.
 *
 * The modifiers are compound classes and not a second hyphenated name, because
 * `.chip.good` is the spelling the sibling product already ships. The tone
 * names the callers pass are unchanged and map to these in `dom.js`; see the
 * note there for why the mapping lives in one place. */
.chip {
  box-shadow: var(--shadow-xs);
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 8px;
  border-radius: var(--pill);
  font-size: 11.5px;
  font-weight: 600;
  white-space: nowrap;
}
.chip-dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
.chip.good {
  background: var(--green-soft);
  color: var(--green);
}
.chip.warn {
  background: var(--amber-soft);
  color: var(--amber);
}
.chip.bad {
  background: var(--red-soft);
  color: var(--red-ink);
}
/* The neutral chip drops the shadow, Suhbat's rule and Suhbat's reason: a pill
 * that says «kutmoqda» is reporting that nothing has happened, and lifting it
 * off the page for that is the page raising its voice over no news. */
.chip.mute {
  background: var(--surface-hover);
  color: var(--ink-3);
  box-shadow: none;
}
.chip.accent {
  background: var(--brand-soft);
  color: var(--brand-ink);
}
/* No counterpart in Suhbat, which has no blue chip. It stays because this
 * product has a state Suhbat does not: audio still moving between a Mac and
 * here is neither good news nor bad, and tinting it green or amber would answer
 * a question the transfer has not finished asking. */
.chip.info {
  background: var(--blue-soft);
  color: var(--blue);
}
.chip-stack {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
}

/* ── Progress ────────────────────────────────────────────────────────────── */

/* One bar shape, used on a list row and on the detail page. The fill is
 * transitioned rather than snapped because a chunk landing every second or two
 * otherwise reads as a bar that is broken and jumping. */
.progress {
  height: 4px;
  border-radius: var(--pill);
  background: var(--line-2);
  overflow: hidden;
}
.progress > i {
  display: block;
  height: 100%;
  width: 0;
  border-radius: inherit;
  background: var(--brand);
  transition: width 0.35s var(--ease);
}
/* A transfer with no declared total cannot honestly claim a percentage, so it
 * says «something is moving» instead of guessing one. */
.progress[data-indeterminate='true'] > i {
  width: 35%;
  animation: progress-slide 1.3s ease-in-out infinite;
}

@keyframes progress-slide {
  0% {
    transform: translateX(-100%);
  }
  100% {
    transform: translateX(320%);
  }
}

/* ── Empty, error and banner states ──────────────────────────────────────── */

.empty {
  padding: 44px 20px;
  text-align: center;
  color: var(--ink-2);
}
.empty h2 {
  font-size: var(--t-h2);
  font-weight: 600;
  color: var(--ink);
  margin: 0 0 6px;
}
.empty p {
  margin: 0 auto;
  max-width: 56ch;
}

.banner {
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 10px 13px;
  border-radius: var(--radius-xs);
  border: 1px solid var(--line);
  background: var(--surface-2);
  margin-bottom: 14px;
}
.banner-danger {
  background: var(--red-soft);
  border-color: transparent;
  color: var(--red-ink);
}
.banner-warn {
  background: var(--amber-soft);
  border-color: transparent;
  color: var(--amber);
}
.banner-ok {
  background: var(--green-soft);
  border-color: transparent;
  color: var(--green);
}
.banner .close {
  margin-left: auto;
  background: none;
  border: none;
  cursor: pointer;
  color: inherit;
  opacity: 0.7;
  padding: 0 2px;
}
.banner .close:hover {
  opacity: 1;
}

/* ── Toasts ──────────────────────────────────────────────────────────────── */

/* Bottom-left, not top-right: the top-right corner of this page is the
 * identity and the sign-out button, and a stack of notices over it during a
 * busy sync covers the one control somebody may be reaching for. */
.toasts {
  position: fixed;
  left: 16px;
  bottom: 16px;
  z-index: 60;
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-width: min(380px, calc(100vw - 32px));
  pointer-events: none;
}

.toast {
  pointer-events: auto;
  display: flex;
  gap: 10px;
  align-items: flex-start;
  padding: 11px 12px;
  border-radius: var(--radius-sm);
  background: var(--surface);
  border: 1px solid var(--line);
  box-shadow: var(--shadow-lg);
  animation: toast-in 0.24s var(--ease);
}
.toast-title {
  font-family: var(--font-d);
  font-weight: 600;
  color: var(--ink);
}
.toast-body {
  min-width: 0;
}
.toast-detail {
  color: var(--ink-2);
  font-size: var(--t-small);
  margin-top: 2px;
}
.toast-icon {
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-top: 7px;
  flex: none;
  background: var(--ink-4);
}
.toast[data-tone='ok'] .toast-icon {
  background: var(--brand);
}
.toast[data-tone='warn'] .toast-icon {
  background: var(--amber);
}
.toast[data-tone='danger'] .toast-icon {
  background: var(--red);
}
.toast .close {
  margin-left: auto;
  background: none;
  border: none;
  cursor: pointer;
  color: var(--ink-3);
  padding: 0 2px;
  line-height: 1;
}
.toast .close:hover {
  color: var(--ink);
}

@keyframes toast-in {
  from {
    opacity: 0;
    transform: translateY(8px);
  }
  to {
    opacity: 1;
    transform: none;
  }
}

/* ── Responsive: the shell ───────────────────────────────────────────────── */

/* Below this width a 266px rail is a third of the screen spent on navigation,
 * so it collapses to icons whether or not the reader asked for it. The rules
 * mirror reveo-shell.css's `[data-rail='collapsed']` block rather than editing
 * it: that file is a copy of another repo's and gets re-synced wholesale. */
@media (max-width: 900px) {
  .shell {
    --rail: 68px;
  }
  .brand {
    padding: 0;
    justify-content: center;
  }
  .brand .name,
  .nav button .lbl,
  .nav .count,
  .rail-collapse {
    display: none;
  }
  .nav button {
    justify-content: center;
    padding: 0;
  }
  .tb {
    padding: 0 16px;
    gap: 12px;
  }
  .view {
    padding: 16px 14px 56px;
  }
}

@media (max-width: 720px) {
  /* The identity is the first thing to go: the reader already knows who they
   * are, and the admin-only controls are on the page itself either way. The
   * live dot stays, because a dropped stream is a fact about the page. The
   * page subtitle goes with it — at this width the title alone is the answer
   * to «where am I» and the second line only squeezes it. */
  .whoami,
  .tb-right .chip.accent,
  .tb-title p {
    display: none;
  }
  .toasts {
    left: 10px;
    right: 10px;
    bottom: 10px;
    max-width: none;
  }
}

/* Respect a reader who has asked for less motion; nothing here is essential. */
@media (prefers-reduced-motion: reduce) {
  * {
    transition: none !important;
    animation: none !important;
  }
}

/* ── «Dastur»: the Mac agent's page ──────────────────────────────────────── */

/* Owned by js/views/app-page.js. It is prose plus one command, so it is laid
 * out as a document and not as a dashboard: the install column carries the
 * reading order (what it is → the line → the steps → the warnings) and the
 * narrow column beside it answers «is my Mac still there» and «it is not
 * working». On a narrow screen the two stack in that same order. */

.prog {
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) minmax(0, 1fr);
  gap: 18px;
  align-items: start;
}
.prog-main,
.prog-side {
  display: grid;
  gap: 18px;
  min-width: 0;
}

.prog-card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  padding: 18px 20px 20px;
  min-width: 0;
}
.prog-card > h2 {
  font-family: var(--font-d);
  font-size: var(--t-h2);
  font-weight: 600;
  margin: 0 0 10px;
}
.prog-sub {
  font-size: var(--t-body);
  font-weight: 600;
  margin: 18px 0 6px;
}
.prog-card > .prog-sub:first-of-type {
  margin-top: 14px;
}
.prog-card p {
  margin: 0 0 8px;
  color: var(--ink-2);
  line-height: 1.55;
}
.prog-lede {
  font-size: 15px;
  color: var(--ink-2);
}

/* A secondary line: the thing you read only if the sentence above it did not
 * answer you. Smaller and greyer, never a different colour. */
.prog-hint {
  font-size: var(--t-small);
  color: var(--ink-3);
  line-height: 1.5;
}
.prog-hint-warn {
  color: var(--amber);
}

/* The command. Monospace here and nowhere else on the page, because this is a
 * string somebody retypes character for character if the copy button fails —
 * the one place where telling l from 1 is the point. It scrolls inside its own
 * box rather than wrapping: a wrapped shell line invites a paste that has lost
 * its pipe. */
.prog-cmd {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--line-2);
  border-radius: var(--radius-xs);
  background: var(--surface-2);
  overflow: hidden;
}
.prog-cmd code {
  flex: 1;
  min-width: 0;
  overflow-x: auto;
  white-space: pre;
  font-family: var(--font-m);
  font-size: 13px;
  line-height: 1.4;
  color: var(--ink);
  padding: 11px 13px;
}
.prog-cmd .prog-copy {
  flex: none;
  border: 0;
  border-left: 1px solid var(--line-2);
  border-radius: 0;
  background: var(--surface);
  /* Fixed width so «Nusxa olish» → «Nusxa olindi» does not shift the box it
   * sits in at the moment somebody is looking at it. */
  min-width: 108px;
  justify-content: center;
}

.prog-steps {
  list-style: none;
  counter-reset: prog-step;
  margin: 14px 0 0;
  padding: 0;
  display: grid;
  gap: 11px;
}
.prog-steps li {
  counter-increment: prog-step;
  display: grid;
  grid-template-columns: 23px minmax(0, 1fr);
  gap: 11px;
  align-items: start;
  color: var(--ink-2);
  line-height: 1.5;
}
.prog-steps li::before {
  content: counter(prog-step);
  display: grid;
  place-items: center;
  width: 23px;
  height: 23px;
  border-radius: var(--pill);
  background: var(--brand-soft);
  color: var(--brand-deep);
  font-size: var(--t-small);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

/* A boxed aside. `data-tone='warn'` is the Apple-signing one: it is the single
 * thing on this page a reader is punished for not having read, so it is the
 * only block that carries colour. */
.prog-note {
  margin-top: 18px;
  padding: 13px 15px;
  border: 1px solid var(--line);
  border-radius: var(--radius-xs);
  background: var(--surface-2);
}
.prog-note[data-tone='warn'] {
  border-color: var(--amber);
  background: var(--amber-soft);
}
.prog-note .prog-sub {
  margin-top: 0;
}
.prog-note p:last-of-type {
  margin-bottom: 0;
}
.prog-note .btn {
  margin-top: 10px;
}

/* One Mac per row: who it is on the left, when it last spoke on the right. The
 * time and its chip stay together — a chip that says «kutmoqda» means nothing
 * without the timestamp it is a reading of. */
.prog-macs {
  margin-top: 2px;
}
.prog-mac {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 14px;
  padding: 11px 0;
  border-top: 1px solid var(--line);
}
.prog-mac:first-child {
  border-top: 0;
  padding-top: 2px;
}
.prog-mac-id {
  min-width: 0;
}
.prog-mac-name {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.prog-mac-sub {
  font-size: var(--t-small);
  color: var(--ink-3);
  margin-top: 2px;
}
.prog-mac-seen {
  flex: none;
  text-align: right;
}
.prog-mac-when {
  font-size: var(--t-small);
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.prog-mac-chip {
  margin-top: 4px;
}
.prog-macs .prog-hint {
  margin: 12px 0 0;
}

.prog-help {
  display: grid;
  gap: 14px;
}
.prog-help-item h3 {
  font-size: var(--t-body);
  font-weight: 600;
  margin: 0 0 3px;
}
.prog-help-item p {
  margin: 0;
  font-size: var(--t-small);
  color: var(--ink-2);
  line-height: 1.55;
}
.prog-card > .prog-help + .btn {
  margin-top: 16px;
}

/* Two columns need roughly 1100px before the right-hand one stops being a
 * gutter with words in it. Below that the cards stack in reading order, which
 * is the order the markup is already in. */
@media (max-width: 1100px) {
  .prog {
    grid-template-columns: minmax(0, 1fr);
  }
}

/* ══ The pipeline blocks, and the home page's chrome ═════════════════════════
 *
 * Prefixed `db-`, and this block is now TWO things.
 *
 * The chrome — `.db-head`, `.db-periods`, `.db-period`, `.db-refresh`,
 * `.db-note`, `.db-hold`, `.db-retry`, `.db-onboard-actions` — still belongs to
 * js/views/dashboard.js and is shared with the `dbi-` block further down.
 *
 * Everything else here dresses the PIPELINE blocks in
 * js/views/dashboard.parts.js: the stat cards, the alert list, the volume
 * chart, the recent list. Those blocks left the home page — recordings,
 * minutes, coverage and in-transit are telemetry about our own plumbing, and
 * they belong where somebody debugging a Mac looks. The rules stay so the
 * blocks keep their appearance when «Qurilmalar» and «Dastur» mount them (see
 * NEEDS WIRING); nothing on «Bosh sahifa» draws `.kpi` or `.db-chart` any
 * more.
 *
 * The visual argument the page makes: a figure and the count behind it are two
 * different kinds of statement, and they must never be set alike. The figure is
 * Space Grotesk at display size; the count under it is body copy in --ink-3. A
 * reader glancing at the page has to be able to tell in one look which of the
 * two they are reading, because half the numbers here are honest only WITH the
 * count attached. */

/* ── The head row: period switch and the «you are behind» pill ───────────── */

/* `.page-head` is baseline-aligned for a heading and a count. This row holds
 * controls instead, and after the router lifts the <h1> into the topbar it
 * holds nothing else — so it centres them and is allowed to wrap. */
.db-head {
  align-items: center;
  flex-wrap: wrap;
  row-gap: 10px;
}

.db-periods {
  display: inline-flex;
  align-items: stretch;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--pill);
  padding: 3px;
  gap: 2px;
}
.db-period {
  all: unset;
  box-sizing: border-box;
  padding: 6px 14px;
  border-radius: var(--pill);
  font-size: 13px;
  font-weight: 600;
  line-height: 1;
  color: var(--ink-3);
  cursor: pointer;
  transition:
    background 0.18s var(--ease),
    color 0.18s var(--ease);
}
.db-period:hover {
  color: var(--ink);
  background: var(--surface-hover);
}
.db-period[aria-pressed='true'] {
  background: var(--surface);
  color: var(--brand-deep);
  box-shadow: var(--shadow-xs);
}
.db-period:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 1px;
}

/* The refresh pill is hidden until a live event says the figures are behind, so
 * it is an event and not furniture — it earns the accent it carries. */
.db-refresh {
  border-color: var(--brand);
  color: var(--brand-deep);
  background: var(--brand-soft);
}

/* ── The headline figures ────────────────────────────────────────────────── */

/* Suhbat's KPI band, and it is a band for the reason it is one there: four
 * figures about one period are a single statement, and four shadowed cards with
 * a brand hairline each were four statements sitting next to each other. The
 * 1px gap over a --line background is the separator, so the rule between two
 * figures is the same rule that runs under every table on the page.
 *
 * `four` is a class and not an inline grid-template, Suhbat's note and Suhbat's
 * bug: an inline style beats the media queries below it, and the band then
 * stays four columns wide on a phone. */
.kpis {
  display: grid;
  grid-template-columns: 1.5fr 1fr 1fr 1fr 1fr;
  gap: 1px;
  background: var(--line);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
  /* Suhbat leaves the top to the UA's 1em on a <dl>. Stated here instead,
   * because the detached cards this replaced had none and the row above them,
   * `.db-head`, is what sets the distance: inheriting a browser default would
   * move the band down by sixteen pixels for no reason a reader could see. */
  margin: 0 0 22px;
}
.kpis.four {
  grid-template-columns: 1.5fr 1fr 1fr 1fr;
}

.kpi {
  background: var(--surface);
  padding: 15px 17px;
  min-width: 0;
}

.kpi dt {
  margin: 0 0 6px;
  font-size: var(--t-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.1em;
  font-weight: 700;
  color: var(--ink-3);
}

/* The figure. Space Grotesk, because that is what carries a headline number in
 * every other Reveo surface, and nothing here is set in mono: these are
 * magnitudes you compare down a row, not identifiers you read left to right.
 * The size is Suhbat's 26px and not a token, because the band's lead figure is
 * deliberately larger than the rest and the two sizes only work as a pair. */
.kpi dd {
  margin: 0;
  font-family: var(--font-d);
  font-weight: 600;
  font-size: 26px;
  font-variant-numeric: tabular-nums;
  letter-spacing: -0.02em;
}
.kpi:first-child dd {
  font-size: 30px;
}

/* A figure that is being withheld does NOT get the figure's typography. It is
 * set in body copy, at body size, in the muted ink. The visual difference is
 * the point, because a suppressed value dressed as a value reads as one.
 *
 * `.kpi dd.kpi-hold` and not `.kpi-hold`, and the extra words are the rule.
 * The class is on the <dd> ITSELF, so it is competing with `.kpi dd` (0,1,1)
 * and `.kpi:first-child dd` (0,2,1) for the same declarations on the same
 * element. As a bare class (0,1,0) it lost both, and a suppressed figure
 * quietly regained the 26px Space Grotesk of a real one, which is a
 * data-honesty bug and not a cosmetic one: «hali kam ma'lumot» dressed as a
 * headline number reads as a measurement. At (0,2,1) it beats `.kpi dd`
 * outright and ties `.kpi:first-child dd`, which it then wins on source order
 * by sitting below it. No `!important`: specificity is the mechanism that is
 * supposed to decide this, and reaching past it hides the next collision
 * instead of losing it loudly. */
.kpi dd.kpi-hold {
  font-family: var(--font-b);
  font-size: var(--t-body);
  font-weight: 600;
  letter-spacing: 0;
  color: var(--ink-3);
}

/* The count the figure came from. Always present, never the same weight. Inside
 * the <dd> rather than beside it, so the band stays a description list a screen
 * reader can walk; `.kpi .sub` reaches it either way. */
.kpi .sub {
  display: block;
  font-family: var(--font-b);
  font-weight: 400;
  font-size: 13px;
  color: var(--ink-3);
  margin-top: 7px;
}

/* What a suppressed figure is waiting for, said in its own line so it cannot be
 * mistaken for the count of something that was computed. */
.kpi-wait {
  display: block;
  font-family: var(--font-b);
  font-weight: 400;
  font-size: var(--t-small);
  color: var(--ink-4);
  margin-top: 5px;
}

/* ── «E’tibor bering» ────────────────────────────────────────────────────── */

/* Nothing wrong is one line, not a panel. A bordered card with a heading and
 * forty pixels of padding around «hammasi joyida» is the page announcing the
 * absence of news, which is how a reader learns to skip the section entirely on
 * the day it does have something to say. */
.db-clear {
  display: flex;
  align-items: center;
  gap: 9px;
  margin: 0 0 18px;
  padding: 9px 14px;
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: var(--t-small);
}
.db-clear-dot {
  width: 7px;
  height: 7px;
  border-radius: var(--pill);
  background: var(--green);
  flex: none;
}

.db-alerts {
  margin-bottom: 18px;
}

.db-alert {
  display: grid;
  grid-template-columns: 8px minmax(0, 1fr) auto;
  align-items: start;
  gap: 12px;
  padding: 13px 14px;
  border-bottom: 1px solid var(--line);
}
.db-alert:last-child {
  border-bottom: 0;
}

/* The tone is a dot and a tint, never a whole red card. Five red cards in a
 * column is a page that looks broken rather than a list of five things to do. */
.db-alert-dot {
  width: 8px;
  height: 8px;
  border-radius: var(--pill);
  margin-top: 6px;
  flex: none;
  background: var(--ink-4);
}
.db-alert[data-tone='danger'] .db-alert-dot {
  background: var(--red);
}
.db-alert[data-tone='warn'] .db-alert-dot {
  background: var(--amber);
}
.db-alert[data-tone='danger'] {
  background: linear-gradient(90deg, var(--red-soft), transparent 42%);
}
.db-alert[data-tone='warn'] {
  background: linear-gradient(90deg, var(--amber-soft), transparent 42%);
}

.db-alert-text {
  min-width: 0;
}
.db-alert-title {
  margin: 0;
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.db-alert-detail {
  margin: 4px 0 0;
  font-size: var(--t-small);
  color: var(--ink-2);
  max-width: 78ch;
}
.db-alert-go {
  flex: none;
  white-space: nowrap;
}

/* ── The volume trend ────────────────────────────────────────────────────── */

.db-trend {
  margin-bottom: 18px;
}

/* A heading's supporting line, and the caption under the chart. Small, muted,
 * and never carrying a figure the page has not already stated in full. */
.db-note {
  font-size: var(--t-small);
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}

/* What a block renders INSTEAD of a chart when it will not draw one. It states
 * the reason rather than leaving a blank rectangle, which reads as a chart that
 * failed to load. */
.db-hold {
  padding: 26px 16px;
  text-align: center;
  color: var(--ink-2);
}
.db-hold p {
  margin: 0 auto;
  max-width: 62ch;
}
.db-hold p + p {
  margin-top: 6px;
}
.db-hold-line {
  margin: 0;
  padding: 18px 14px;
  color: var(--ink-3);
  font-size: var(--t-small);
}

.db-chart {
  display: flex;
  align-items: flex-end;
  gap: 4px;
  height: 132px;
  padding: 14px 14px 0;
}
.db-chart[data-dense='true'] {
  gap: 2px;
}

/* Every day is a full-height track, so an empty day is a visible zero rather
 * than a gap. The zeroes are data: four uploads on four consecutive days and
 * four uploads across a month are different facts and must not draw alike. */
.db-bar {
  flex: 1 1 0;
  min-width: 3px;
  height: 100%;
  display: flex;
  align-items: flex-end;
  border-radius: 5px;
  background: var(--surface-hover);
}
.db-bar > i {
  display: block;
  width: 100%;
  min-height: 2px;
  border-radius: 5px;
  background: var(--brand);
  transition: background 0.18s var(--ease);
}
.db-bar[data-empty='true'] > i {
  min-height: 0;
}
/* The first and last column of a rolling window cover only part of a day, so
 * their HEIGHT is not comparable to their neighbours' even though their count
 * is true. Faded, and the chart says why underneath — a bar drawn identically
 * would open every chart with a slump that is only the clock. */
.db-bar[data-partial='true'] > i {
  opacity: 0.42;
}
.db-bar:hover > i {
  background: var(--brand-ink);
}

.db-chart-foot {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin: 10px 0 0;
  padding: 0 14px 12px;
  font-size: var(--t-small);
  color: var(--ink-3);
}

/* The sentence that makes the faded end columns readable. Under the axis and
 * not in a tooltip: a caveat only a hover reveals is a caveat nobody reads. */
.db-chart-note {
  margin: 0;
  padding: 0 14px 14px;
  max-width: 62ch;
  font-size: var(--t-small);
  line-height: 1.5;
  color: var(--ink-3);
}

/* ── The recent recordings ───────────────────────────────────────────────── */

.db-recent {
  margin-bottom: 18px;
}

.db-rec {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  align-items: center;
  column-gap: 14px;
  row-gap: 8px;
  padding: 12px 14px;
  border-bottom: 1px solid var(--line);
  /* NOT decoration, and not optional. The filename below carries `.row-link`,
   * whose `::after { position: absolute; inset: 0 }` in pages.css gives the
   * whole row a click target. An absolute box needs a POSITIONED ancestor, and
   * without this line there is none the whole way up — `.db-rec-main`,
   * `.db-recent`, the card, `main#view`, `.pane`, `.shell` and `body` are all
   * static — so the overlay falls back to the initial containing block and
   * covers the entire VIEWPORT.
   *
   * Measured, not theorised: on the dashboard every rail item, and the rail's
   * collapse button, reported `.row-link` as the topmost element, and clicking
   * where «Qurilmalar» is drawn navigated to the last recording in this list
   * instead. The landing page had no working navigation at all, and nothing
   * threw — the page looks perfect in a screenshot. */
  position: relative;
}
.db-rec:last-child {
  border-bottom: 0;
}
/* Listed but not here yet. The same tint the list uses for an ON_DEVICE row, so
 * the two pages agree about what «readable but not playable» looks like. */
.db-rec-ondevice {
  background: var(--surface-2);
}

.db-rec-main {
  min-width: 0;
}
.db-rec-name {
  display: block;
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.db-rec-meta {
  display: flex;
  flex-wrap: wrap;
  gap: 3px 12px;
  margin-top: 3px;
  font-size: var(--t-small);
  color: var(--ink-3);
}
.db-rec-state {
  justify-self: end;
}

/* The player spans both columns on its own line: a 44px-tall native control
 * squeezed into a table cell is unusable at every width, and this is the one
 * thing on the row somebody is here to press. */
.db-rec-audio,
.db-rec-wait {
  grid-column: 1 / -1;
}
.db-rec-audio {
  width: 100%;
  height: 34px;
  /* Above the `.row-link` overlay, or pressing play opens the recording instead
   * of playing it. `.db-rec` is `position: relative` (see the note above), so
   * the filename's `::after { inset: 0 }` covers the WHOLE row — including the
   * player, which sits on its own line further down it — and an absolutely
   * positioned box paints over its in-flow siblings whatever the source order
   * says. Nothing throws, the controls are drawn correctly, and every click on
   * them navigates away. pages.css has carried the same two lines on
   * `.row-fetch` since the same overlay ate the «fetch now» button; this row's
   * player is the one thing the block exists for. */
  position: relative;
  z-index: 1;
}
.db-rec-wait {
  margin: 0;
  font-size: var(--t-small);
  color: var(--ink-3);
}

/* ── Odds and ends ───────────────────────────────────────────────────────── */

.db-retry {
  margin: 0;
  text-align: center;
}
.db-onboard-actions {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 10px;
  justify-content: center;
}

@media (max-width: 1100px) {
  /* Four figures across stop being readable well before the rail collapses:
   * «Yozib olingan vaqt» wraps to three lines at 220px and the row loses its
   * scan line. Two-up keeps every cell wide enough for its own count. */
  .kpis,
  .kpis.four {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
}

@media (max-width: 720px) {
  .kpis,
  .kpis.four {
    grid-template-columns: minmax(0, 1fr);
  }
  .db-head .db-periods {
    order: 3;
    width: 100%;
    justify-content: space-between;
  }
  .db-alert {
    grid-template-columns: 8px minmax(0, 1fr);
  }
  /* The action moves under the text rather than squeezing it: a «Qurilmalar»
   * button holding a column at 375px leaves the sentence four words wide. */
  .db-alert-go {
    grid-column: 2;
    justify-self: start;
  }
  .db-rec {
    grid-template-columns: minmax(0, 1fr);
  }
  .db-rec-state {
    justify-self: start;
  }
  .db-chart {
    height: 104px;
  }
}

/* ══ «Bosh sahifa», what was SAID ═════════════════════════════════════════════
 *
 * Owned by js/views/dashboard.js and js/views/dashboard.insights.js. Prefixed
 * `dbi-` and kept apart from the `db-` block above, which now dresses the
 * PIPELINE blocks — the stat cards, the volume chart, the alert list — as they
 * move to «Qurilmalar» and «Dastur». The two prefixes share only the page
 * chrome: the period switch, the refresh pill, `.db-note` and `.db-hold`.
 *
 * The visual argument this half makes: THE QUOTE IS THE ITEM. Everything above
 * a quote is somebody's paraphrase of it — the model's, ours — and the only
 * thing on the page a reader can check is the sentence that was actually said.
 * So the quote is not a footnote under the claim: it is set at body size with a
 * brand rule down its left edge, and the paraphrase above it is the smaller,
 * quieter line. A layout that whispered the evidence and shouted the claim
 * would be the exact shape of the failure this feature exists to prevent.
 *
 * A SUGGESTION never wears the styling of a fact: two columns, the later quote
 * on a tinted ground, an eyebrow that says «taklif», and a button. Nothing here
 * is green until a person has pressed it. */

/* ── The sections ────────────────────────────────────────────────────────── */

/* No margin: the page is `.dash`, Suhbat's grid, and IT owns the rhythm. A
 * margin here would be added to the grid gap and space one block at 36px while
 * its neighbours sit at 18px. */
.dbi-panel {
  margin-bottom: 0;
}

/* A sentence that frames a whole section — what «javobsiz» can and cannot
 * mean, what a suggestion is not. Set as prose, above the rows it qualifies,
 * because a caveat only a hover reveals is a caveat nobody reads. */
.dbi-lede {
  margin: 6px 14px 10px;
  max-width: 78ch;
  font-size: var(--t-small);
  line-height: 1.55;
  color: var(--ink-2);
}

.dbi-item {
  padding: 13px 14px;
  border-bottom: 1px solid var(--line);
}
.dbi-item:last-child {
  border-bottom: 0;
}

.dbi-item-head {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin-bottom: 6px;
}
.dbi-item-head .spacer {
  margin-left: auto;
}

/* The model's phrasing of the item. Deliberately quieter than the quote under
 * it: this is the paraphrase, and the quote is the evidence. */
.dbi-text {
  margin: 0;
  font-size: var(--t-small);
  line-height: 1.5;
  color: var(--ink-2);
  max-width: 84ch;
}

/* THE EVIDENCE. Body size, near-full ink, a brand rule down the edge — the
 * thing a reader's eye should land on, because it is the only line on the page
 * somebody can check against the recording. */
.dbi-quote {
  margin: 7px 0 0;
  padding: 2px 0 2px 12px;
  border-left: 2px solid var(--brand-soft-2);
  font-size: var(--t-body);
  line-height: 1.55;
  color: var(--ink);
  overflow-wrap: anywhere;
  max-width: 84ch;
}

.dbi-source {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin-top: 7px;
  font-size: var(--t-small);
  color: var(--ink-3);
}

/* The timestamp, as a link into the recording at that second. Mono because it
 * is an identifier you read left to right — a place in a file — and not a
 * magnitude compared down a column. */
.dbi-at {
  font-family: var(--font-m);
  font-size: 12px;
  color: var(--brand-ink);
  text-decoration: none;
  padding: 1px 6px;
  border-radius: var(--radius-xs);
  background: var(--brand-soft);
}
.dbi-at:hover {
  background: var(--brand-soft-2);
  text-decoration: underline;
}

.dbi-source-name {
  color: var(--ink-2);
  text-decoration: none;
  max-width: 32ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dbi-source-name:hover {
  color: var(--brand-ink);
  text-decoration: underline;
}
.dbi-source-when {
  font-variant-numeric: tabular-nums;
}

/* The recorder's clock disagrees with the filesystem's. Shown, never hidden:
 * a recording filed two months from where it belongs is worse than one
 * labelled uncertain — and it is why the commitment above may have no
 * deadline at all. */
.dbi-suspect {
  color: var(--amber);
  font-weight: 600;
}

/* ── Overdue ─────────────────────────────────────────────────────────────── */

.dbi-due {
  font-weight: 700;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.dbi-late {
  color: var(--red-ink);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}

.dbi-owner {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  font-size: var(--t-small);
  color: var(--ink-2);
}
/* The same three hues the transcript gives speakers A, B and C, so the person
 * on the hook here is the same colour as the turn they said it in. */
.dbi-speaker {
  width: 20px;
  height: 20px;
  border-radius: 6px;
  display: grid;
  place-items: center;
  font-size: 11px;
  font-weight: 700;
  color: var(--on-brand);
  background: var(--ink-4);
  flex: none;
}
.dbi-speaker[data-speaker='A'] {
  background: var(--brand);
}
.dbi-speaker[data-speaker='B'] {
  background: var(--violet);
}
.dbi-speaker[data-speaker='C'] {
  background: var(--tg);
}
/* No name was spoken, and the page says so rather than turning a label into a
 * person somebody will go and chase. */
.dbi-owner-anon {
  color: var(--ink-3);
}

.dbi-hint {
  margin: 7px 0 0;
  font-size: var(--t-small);
  color: var(--ink-3);
}
/* What the list above deliberately does NOT contain, and why. Under the rows,
 * inside the panel, because a count of excluded items in a different block
 * reads as a second finding. */
.dbi-tail {
  margin: 0;
  padding: 11px 14px 12px;
  border-top: 1px solid var(--line);
  background: var(--surface-2);
  font-size: var(--t-small);
  line-height: 1.55;
  color: var(--ink-3);
}
.dbi-tail-head {
  margin: 0 0 7px;
}
/* An open promise that can never be late, and WHY — «muddat aytilmagan», or a
 * recorder whose clock is wrong and whose «ertaga» we refuse to resolve. One
 * line each: it is a footnote to the overdue list, not a second list. */
.dbi-tail-item {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 3px 9px;
  padding: 4px 0;
}
.dbi-tail-why {
  font-weight: 700;
  color: var(--amber);
  flex: none;
}
.dbi-tail-text {
  color: var(--ink-2);
  min-width: 0;
}

.dbi-empty {
  margin: 0;
  padding: 16px 14px;
  color: var(--ink-3);
  font-size: var(--t-small);
  line-height: 1.55;
  max-width: 78ch;
}

.dbi-error {
  margin: 9px 0 0;
  font-size: var(--t-small);
  color: var(--red-ink);
}

/* ── The one rate on the page ────────────────────────────────────────────── */

/* Set as a line and not a card, deliberately. It is a derived figure on a
 * page of counts, it is usually withheld, and a card would promise a headline
 * the sample cannot pay for. */
.dbi-rate {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: 4px 10px;
  margin: 0;
  padding: 9px 14px;
  border: 1px dashed var(--line-2);
  border-radius: var(--radius-sm);
}
.dbi-rate-value {
  font-weight: 700;
  color: var(--ink);
}
/* A withheld figure does not get the figure's typography. The visual
 * difference is the point: a suppressed value dressed as a value reads as one. */
.dbi-rate-hold {
  font-weight: 600;
  color: var(--ink-3);
}

/* ── A follow-through suggestion ─────────────────────────────────────────── */

.dbi-suggestion {
  background: var(--violet-soft);
}

/* Both quotes, side by side. The pair IS the interface: a human confirms by
 * reading the promise and the later line together, and a layout that showed
 * one of them behind a click would be asking for a decision without the
 * evidence it depends on. */
.dbi-pair {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 14px;
}
.dbi-side {
  min-width: 0;
}
.dbi-side-later {
  padding-left: 14px;
  border-left: 1px solid var(--line-2);
}
.dbi-side-label {
  margin: 0 0 6px;
  font-size: var(--t-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 700;
  color: var(--ink-3);
}
/* The later quote carries the violet rule, so the two sides of the pair are
 * never mistaken for one another at a glance. */
.dbi-side-later .dbi-quote {
  border-left-color: var(--violet);
}

.dbi-confirm {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px;
  margin-top: 12px;
  padding-top: 11px;
  border-top: 1px solid var(--line);
}

/* «Bu mos emas» — the third button, and the quietest on purpose.
 *
 * It sits beside two buttons that CLOSE a promise, and it is the only one that
 * does not: the promise stays exactly where it was and only the model's
 * evidence goes. Given equal weight it would read as a third outcome, which is
 * the one thing it is not. Borderless until hover, so it is plainly available
 * without competing with the decision beside it. */
.dbi-dismiss {
  background: transparent;
  border-color: transparent;
  color: var(--ink-3);
  font-weight: 500;
}
.dbi-dismiss:hover:not(:disabled) {
  background: var(--surface-hover);
  border-color: var(--line-2);
  color: var(--ink);
}

/* ── Mentions ────────────────────────────────────────────────────────────── */

.dbi-mention-group {
  padding: 11px 14px;
  border-bottom: 1px solid var(--line);
}
.dbi-mention-group:last-child {
  border-bottom: 0;
}
.dbi-mention-label {
  margin: 0 0 8px;
  font-size: var(--t-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-weight: 700;
  color: var(--ink-3);
}
.dbi-mentions {
  display: flex;
  flex-wrap: wrap;
  gap: 7px;
}

/* Every chip is a search link. The tally is not the point — the point is that
 * «Bioflex» finally names something the list can be filtered by, on a product
 * whose rows are otherwise called 20260905155901.WAV. */
.dbi-mention {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 5px 8px 5px 10px;
  border: 1px solid var(--line-2);
  border-radius: var(--pill);
  background: var(--surface);
  color: var(--ink);
  font-size: var(--t-small);
  font-weight: 600;
  text-decoration: none;
  transition:
    border-color 0.18s var(--ease),
    background 0.18s var(--ease);
}
.dbi-mention:hover {
  border-color: var(--brand);
  background: var(--brand-soft);
  color: var(--brand-deep);
}
.dbi-mention-value {
  max-width: 26ch;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.dbi-mention-count {
  padding: 1px 6px;
  border-radius: var(--pill);
  background: var(--surface-hover);
  color: var(--ink-3);
  font-size: 11.5px;
  font-variant-numeric: tabular-nums;
}
/* Two spellings of one client were folded into this chip. Shown, because a
 * judgement made silently in a group-by is one the reader can neither see nor
 * undo — the database stores mentions unnormalised for exactly that reason. */
.dbi-mention-variants {
  color: var(--ink-4);
  font-weight: 500;
  font-size: 11.5px;
}

/* ── Meetings with no follow-up ──────────────────────────────────────────── */

.dbi-quiet .dbi-item-head {
  margin-bottom: 0;
}
/* The summary is the one thing on this page with no quote behind it, so it is
 * support under a recording somebody can open and never a finding of its own. */
.dbi-summary {
  margin: 6px 0 0;
  font-size: var(--t-small);
  line-height: 1.55;
  color: var(--ink-2);
  max-width: 84ch;
}

/* ── «Not yet read» ──────────────────────────────────────────────────────── */

.dbi-hold-title {
  font-family: var(--font-d);
  font-size: var(--t-h2);
  font-weight: 600;
  color: var(--ink);
  margin: 0 0 6px;
}
.dbi-hold-action {
  margin-top: 14px;
}

@media (max-width: 860px) {
  /* The pair stacks rather than shrinking: two quotes at 200px each are two
   * columns of one word, and the pair is the whole reason the block exists. */
  .dbi-pair {
    grid-template-columns: minmax(0, 1fr);
  }
  .dbi-side-later {
    padding-left: 0;
    padding-top: 12px;
    border-left: 0;
    border-top: 1px solid var(--line-2);
  }
}

@media (max-width: 720px) {
  .dbi-item-head .spacer {
    /* At this width the owner would be pushed onto its own line with a gap the
     * size of the card above it. Wrapping in place reads better. */
    display: none;
  }
}

/* ══ «Tahlil» — the analytics surface ═════════════════════════════════════════

   Everything below is prefixed `an-` (the page) or `chart-` (the SVG the page
   draws), so this block can be read, moved or removed as one thing while other
   surfaces are being built in the same file.

   The charts carry no colour of their own: `charts.js` emits class names and
   `data-tone`, and the palette resolves here out of reveo-tokens.css. That is
   what makes dark mode free — an SVG with `fill="#07B17F"` baked in would be
   the one element on the page that never heard about the theme. */

/* ── The period control ──────────────────────────────────────────────────── */

/* One control, at the top, governing every card. Two period pickers on one
   page is how the same label prints two different numbers four blocks apart. */
.an-toolbar {
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
  margin-bottom: 10px;
}
.an-toolbar-spacer {
  flex: 1;
}
.an-period {
  display: inline-flex;
  padding: 3px;
  gap: 2px;
  background: var(--surface-2);
  border: 1px solid var(--line);
  border-radius: var(--pill);
}
.an-period button {
  appearance: none;
  border: 0;
  background: transparent;
  color: var(--ink-2);
  font: inherit;
  font-size: var(--t-small);
  font-weight: 600;
  padding: 6px 14px;
  border-radius: var(--pill);
  cursor: pointer;
  transition: background 0.16s var(--ease), color 0.16s var(--ease);
}
.an-period button:hover {
  color: var(--ink);
  background: var(--surface-hover);
}
.an-period button[aria-pressed='true'] {
  background: var(--surface);
  color: var(--brand-deep);
  box-shadow: var(--shadow-xs);
}
.an-period button:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 1px;
}

.an-updated {
  font-size: var(--t-small);
  color: var(--ink-3);
}

/* The window, in words, once. Every card reads the same one. */
.an-caption {
  margin: 0 0 16px;
  font-size: var(--t-small);
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.an-caption-note {
  display: block;
  color: var(--ink-4);
}

/* ── The card grid ───────────────────────────────────────────────────────── */

/* One column until there is honestly room for two. `minmax(0, 1fr)` and not
   `1fr`: a grid track's default minimum is its content, so a wide chart would
   push the column past the viewport instead of shrinking inside it. */
.an-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: 14px;
  padding-bottom: 8px;
}

@media (min-width: 1080px) {
  .an-grid {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .an-card-wide {
    grid-column: span 2;
  }
}

@media (min-width: 1600px) {
  .an-grid {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.an-card {
  min-width: 0;
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  box-shadow: var(--shadow-sm);
  display: flex;
  flex-direction: column;
}
.an-card-head {
  padding: 12px 16px 10px;
  border-bottom: 1px solid var(--line);
  background: var(--surface-2);
  border-radius: var(--radius-sm) var(--radius-sm) 0 0;
}
.an-card-head h2 {
  margin: 0;
  font-size: var(--t-h2);
  font-weight: 600;
}
/* `.cs` is kit.css's name for a card heading's supporting line; the same class
   does the same job in Suhbat. */
.an-card-head .cs {
  margin: 3px 0 0;
  font-size: var(--t-small);
  color: var(--ink-3);
  line-height: 1.4;
}
.an-card-body {
  padding: 14px 16px 16px;
  display: flex;
  flex-direction: column;
  gap: 12px;
  min-width: 0;
}

/* A heading inside a card — «Qurilmalar», «Odamlar». */
.an-sub {
  margin: 2px 0 0;
  font-size: var(--t-small);
  font-family: var(--font-b);
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-3);
}

/* ── A figure, its count and its delta ───────────────────────────────────── */

.an-figures {
  display: flex;
  flex-wrap: wrap;
  gap: 14px 30px;
}
.an-fig {
  min-width: 124px;
}
.an-fig-label {
  font-size: var(--t-eyebrow);
  text-transform: uppercase;
  letter-spacing: 0.05em;
  font-weight: 600;
  color: var(--ink-3);
}
/* Space Grotesk at 600, like every headline number in Suhbat. Nothing on this
   page is mono: a magnitude you compare down a column takes tabular figures in
   the body face, and mono is kept for identifiers you read left to right. */
.an-fig-value {
  font-family: var(--font-d);
  font-weight: 600;
  font-size: 26px;
  line-height: 1.15;
  letter-spacing: -0.02em;
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}
/* A suppressed figure is a dash, and it is visibly a dash: the reader must be
   able to tell «we will not say» from «it is zero» at a glance. */
.an-fig-empty {
  color: var(--ink-4);
}
/* The count the figure above was computed from. Never the card's count — see
   the note on `figure()` in analytics.parts.js. */
.an-fig-n {
  margin-top: 3px;
  font-size: var(--t-small);
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
.an-delta {
  margin-top: 3px;
  font-size: var(--t-small);
  color: var(--ink-3);
  font-variant-numeric: tabular-nums;
}
/* Direction is shown, not judged. More recording is not automatically good and
   less is not automatically bad, so the arrow says which way and the colour
   stays out of it. */
.an-delta[data-dir='up']::before {
  content: '↑ ';
  color: var(--ink-2);
}
.an-delta[data-dir='down']::before {
  content: '↓ ';
  color: var(--ink-2);
}

/* ── The honest empty state ──────────────────────────────────────────────── */

/* Dashed, so it never reads as a card with a number missing from it: this is a
   thing that is deliberately not being shown yet, and it says how far off it
   is. */
.an-wait {
  border: 1px dashed var(--line-2);
  border-radius: var(--radius-xs);
  padding: 12px 14px;
  background: var(--surface-2);
}
.an-wait-head {
  margin: 0;
  font-weight: 600;
  font-size: var(--t-small);
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.an-wait-bar {
  margin: 8px 0;
  height: 4px;
  border-radius: var(--pill);
  background: var(--line);
  overflow: hidden;
}
.an-wait-bar > i {
  display: block;
  height: 100%;
  border-radius: var(--pill);
  background: var(--ink-4);
}
.an-wait-why {
  margin: 0;
  font-size: var(--t-small);
  color: var(--ink-3);
  line-height: 1.45;
}

/* ── Legends, count lists, notes, issues ─────────────────────────────────── */

.an-legend,
.an-counts,
.an-issues {
  list-style: none;
  margin: 0;
  padding: 0;
}
.an-legend li {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 3px 0;
  font-size: var(--t-small);
}
.an-swatch {
  width: 10px;
  height: 10px;
  border-radius: 3px;
  flex: none;
  background: var(--tone, var(--brand));
}
.an-legend-name {
  color: var(--ink-2);
}
.an-legend-n {
  margin-left: auto;
  color: var(--ink);
  font-weight: 600;
}

.an-counts li {
  display: flex;
  justify-content: space-between;
  gap: 12px;
  padding: 4px 0;
  font-size: var(--t-small);
  color: var(--ink-2);
  border-bottom: 1px solid var(--line);
}
.an-counts li:last-child {
  border-bottom: 0;
}

.an-note {
  margin: 0;
  font-size: var(--t-small);
  color: var(--ink-3);
  line-height: 1.45;
}
.an-note[data-tone='ok'] {
  color: var(--green);
}

/* One broken thing per row, each with its next step in the sentence. The bar
   down the left is the only colour: a list of six red rows reads as a system on
   fire, and «the model heard no speech» is not a fire. */
.an-issues li {
  padding: 7px 0 7px 12px;
  border-left: 3px solid var(--line-2);
  margin-bottom: 6px;
  font-size: var(--t-small);
  color: var(--ink-2);
  line-height: 1.45;
}
.an-issues li:last-child {
  margin-bottom: 0;
}
.an-issues li[data-tone='warn'] {
  border-left-color: var(--amber);
}
.an-issues li[data-tone='danger'] {
  border-left-color: var(--red);
}

/* ── The charts ──────────────────────────────────────────────────────────── */

/* `min-width: 0` so a chart inside a flex or grid column shrinks with it
   instead of setting the column's width from its own SVG. */
.chart {
  width: 100%;
  min-width: 0;
}
.chart svg {
  display: block;
  overflow: visible;
}

.chart-grid {
  stroke: var(--line);
  stroke-width: 1;
}
.chart-axis,
.chart-tick {
  fill: var(--ink-4);
  font-family: var(--font-b);
  font-size: 10.5px;
  font-variant-numeric: tabular-nums;
}
.chart-label {
  fill: var(--ink-2);
  font-family: var(--font-b);
  font-size: 12px;
}
.chart-value {
  fill: var(--ink);
  font-family: var(--font-b);
  font-size: 12px;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}

.chart-track {
  fill: var(--bg);
}
.chart-col {
  fill: var(--brand);
}
.chart-col:hover {
  fill: var(--brand-ink);
}
/* A day with nothing in it is drawn as a flat stub, not left blank: an absent
   column reads as «no data for that day», and «nobody recorded» is data. */
.chart-col-zero {
  fill: var(--line-2);
}
/* Same rule as `.db-bar[data-partial]`: a part-day column keeps its true count
   and loses the claim that its height means what the full days' heights mean. */
.chart-col-partial {
  opacity: 0.42;
}

.chart-bar,
.chart-seg {
  fill: var(--tone, var(--brand));
}

/* The tone map, in one place, for both the SVG shapes and the legend swatch
   that names them. `:is()` keeps `[data-tone]` from becoming a page-wide
   selector — `.toast` already uses that attribute for something else. */
:is(.chart-bar, .chart-seg, .an-swatch)[data-tone='brand'] {
  --tone: var(--brand);
}
:is(.chart-bar, .chart-seg, .an-swatch)[data-tone='green'] {
  --tone: var(--green);
}
:is(.chart-bar, .chart-seg, .an-swatch)[data-tone='blue'] {
  --tone: var(--blue);
}
:is(.chart-bar, .chart-seg, .an-swatch)[data-tone='violet'] {
  --tone: var(--violet);
}
:is(.chart-bar, .chart-seg, .an-swatch)[data-tone='amber'] {
  --tone: var(--amber);
}
:is(.chart-bar, .chart-seg, .an-swatch)[data-tone='red'] {
  --tone: var(--red);
}
:is(.chart-bar, .chart-seg, .an-swatch)[data-tone='neutral'] {
  --tone: var(--ink-4);
}

@media (max-width: 720px) {
  /* The refresh control and its timestamp are conveniences; the period buttons
     are the page. At this width they get the whole row. */
  .an-toolbar {
    gap: 8px;
  }
  .an-period {
    width: 100%;
    justify-content: space-between;
  }
  .an-figures {
    gap: 12px 20px;
  }
  .an-fig-value {
    font-size: 22px;
  }
}

/* ══ «Bosh sahifa», Suhbat's composition ══════════════════════════════════════

   Not a Suhbat-flavoured tweak of Diktofon's own blocks — Suhbat's dashboard
   COMPOSITION, with Diktofon's data poured into it. The rules below are copied
   from reveo-suhbat/public/app.css declaration for declaration, comments and
   all, because the comments are where the reasoning lives and a re-derived
   value drifts the moment somebody tidies it. Where Diktofon needs something
   Suhbat has no equivalent for, the rule says which Suhbat primitive it was
   built from.

   Tokens only, not one literal colour: reveo-tokens.css is byte-identical to
   Suhbat's, so every tint below already exists in both themes and the dark
   block has nothing to override. */

/* ── Suhbat's card ───────────────────────────────────────────────────────── */

/* The box. `.panel` in pages.css is this same rule under Diktofon's older name
   and still draws every other page; the dashboard uses THIS name, because the
   blocks below (`.attention`, `.qstrip`, `.hero-tiles`) are Suhbat's and are
   written against `.card > header` and against the card's `overflow: hidden`,
   which is what makes their internal 1px grid gaps read as clean hairlines all
   the way to the edge. */
.card {
  background: var(--surface);
  border: 1px solid var(--line);
  border-radius: var(--radius-sm);
  overflow: hidden;
}
.card > header {
  padding: 13px 17px;
  border-bottom: 1px solid var(--line);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 12px;
}
.card > header h2 {
  font-size: var(--t-h2);
  font-weight: 600;
  margin: 0;
}
.card > header .hint {
  font-size: var(--t-small);
  color: var(--ink-3);
}
/* A hint that is also the way to the page this figure is really about. Suhbat's
   hints are all spans; this one is a link because the pipeline lives on
   «Qurilmalar» and a caption that names a page you cannot reach from it is a
   caption that sends people looking. */
a.hint {
  text-decoration: none;
}
a.hint:hover {
  color: var(--brand-ink);
  text-decoration: underline;
}
.card > header .spacer {
  margin-left: auto;
}
.card .body {
  padding: 15px 17px;
}

/* The dashboard's own vertical rhythm. The blocks are not equal weight — the
   verdict is the page — so the gap is one value and the emphasis comes from
   size, not from spacing that grows to signal importance. */
.dash {
  display: grid;
  gap: 18px;
}

/* ── The hero ────────────────────────────────────────────────────────────── */

/* Left half says what happened, right half answers the four standing
   questions. The split is not 50/50: the sentence is the reason the card
   exists and the tiles are reference, so the reading order is enforced by
   width as well as by position. */
.hero {
  display: grid;
  grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr);
  align-items: stretch;
}
.hero-say {
  padding: 24px 26px 26px;
  min-width: 0;
  display: flex;
  flex-direction: column;
}

.hero-badges {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  margin-bottom: 18px;
}

/* The filled pill. It is the only filled element on the page above the fold,
   which is what makes the card read as a header rather than a fourth panel. */
.pill {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 11px 5px 9px;
  border-radius: var(--pill);
  background: var(--brand);
  color: var(--on-brand);
  font-size: var(--t-eyebrow);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
}
.pill svg {
  width: 13px;
  height: 13px;
  flex: none;
}

/* The verdict itself. Space Grotesk at lede size and a tight measure: it has
   to survive being read at a glance from a metre away, which is how a manager
   reads a dashboard between two meetings. */
.hero-line {
  margin: 0;
  font-family: var(--font-d);
  font-weight: 600;
  font-size: clamp(21px, 1.5vw + 12px, 31px);
  line-height: 1.28;
  letter-spacing: -0.025em;
  max-width: 30ch;
  text-wrap: balance;
}

.hero-support {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  margin: 16px 0 0;
  font-size: 13.5px;
  color: var(--ink-2);
  font-variant-numeric: tabular-nums;
}
.hero-support span + span::before {
  content: '·';
  color: var(--ink-4);
  margin: 0 9px;
}

/* Pushed to the bottom of the column so the sentence keeps the optical centre
   however many drivers there were. */
.hero-why {
  margin-top: auto;
  padding-top: 22px;
}
.hero-why .eyebrow {
  display: block;
  margin-bottom: 9px;
  font-size: var(--t-eyebrow);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.11em;
  color: var(--ink-3);
}
.wchips {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}

/* One driver. The tint is the verdict's own reasoning made visible: green
   pushed the sentence up, red pushed it down, and a neutral chip was weighed
   and found not to move it. There is deliberately no `.wchip.flat` rule —
   a flat driver falls through to this base and reads as outlined and grey. */
.wchip {
  display: inline-flex;
  align-items: baseline;
  gap: 8px;
  padding: 7px 12px;
  border: 1px solid var(--line);
  border-radius: var(--pill);
  background: var(--surface);
  font-size: var(--t-small);
  color: var(--ink-2);
}
.wchip b {
  font-weight: 600;
  color: var(--ink-2);
}
.wchip .wval {
  font-family: var(--font-d);
  font-weight: 600;
  font-size: 14px;
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  color: var(--ink);
}
.wchip.good {
  border-color: transparent;
  background: var(--green-soft);
}
.wchip.good b,
.wchip.good .wval {
  color: var(--green);
}
.wchip.bad {
  border-color: transparent;
  background: var(--red-soft);
}
.wchip.bad b,
.wchip.bad .wval {
  color: var(--red-ink);
}
.wchip .n {
  border: 0;
  padding: 0;
  background: none;
}

/* The `n` beside a figure: what it was counted over. Suhbat's rule verbatim —
   this product has the same problem, that half its numbers are only honest
   with their population attached. */
.n {
  font-family: var(--font-b);
  font-variant-numeric: tabular-nums;
  font-size: 10.5px;
  font-weight: 500;
  color: var(--ink-3);
  letter-spacing: 0;
  white-space: nowrap;
}
.chip .n {
  color: inherit;
  opacity: 0.75;
}

/* ── The hero tiles ──────────────────────────────────────────────────────── */

/* THE HAIRLINES ARE FAKE BORDERS: `gap: 1px` over a `--line` background with
   opaque `--surface` children. The 2x2 block is one continuous grid of white
   squares on a grey ground, and the card's own overflow:hidden plus this
   border-left seal it against the sentence column. No border on the tile. */
.hero-tiles {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1px;
  background: var(--line);
  border-left: 1px solid var(--line);
}
.htile {
  background: var(--surface);
  padding: 18px 20px;
  display: flex;
  gap: 13px;
  min-width: 0;
}
.htile .tbody {
  min-width: 0;
  display: flex;
  flex-direction: column;
}

/* The tinted square. Same radius family as the cards, one step down, so the
   icons read as part of the surface rather than as stickers on it.

   The tone is HARD-CODED PER METRIC, never derived from the data: a kind of
   item wears the same hue wherever it appears, so «va'da» is green on the tile
   and green in the strip and the reader learns it once. */
.tico {
  width: 34px;
  height: 34px;
  flex: none;
  border-radius: var(--radius-xs);
  display: grid;
  place-items: center;
  /* NOT in Suhbat's copy of this rule, and they are load bearing HERE.
   *
   * Suhbat scopes its rail block as `.rail .brand`; the copy of reveo-shell.css
   * this product ships declares a BARE `.brand`, which carries `padding: 0 20px`
   * and a `border-bottom`. `.tico.brand` therefore inherited both, and the one
   * green tile on the page rendered 40px wide with a hairline under it while
   * its four neighbours were 34px squares. Nothing threw and nothing logged;
   * it was caught by measuring the rendered box, not by reading the rule.
   *
   * Zeroed here rather than on `.tico.brand`, because the next tone that
   * collides with a shell class would repeat the same afternoon: a tinted
   * square has no padding and no border, whatever else claims its name.
   * reveo-shell.css is synced wholesale from the workspace and is not ours to
   * scope. */
  padding: 0;
  border: 0;
}
.tico svg {
  width: 17px;
  height: 17px;
  display: block;
}
.tico.brand {
  background: var(--brand-soft);
  color: var(--brand-ink);
}
.tico.violet {
  background: var(--violet-soft);
  color: var(--violet);
}
.tico.blue {
  background: var(--blue-soft);
  color: var(--blue);
}
.tico.amber {
  background: var(--amber-soft);
  color: var(--amber);
}
.tico.red {
  background: var(--red-soft);
  color: var(--red-ink);
}
.tico.green {
  background: var(--green-soft);
  color: var(--green);
}

.tlabel {
  display: flex;
  align-items: center;
  gap: 6px;
  font-size: var(--t-eyebrow);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.09em;
  color: var(--ink-3);
}
.tvalue {
  margin-top: 7px;
  font-family: var(--font-d);
  font-weight: 600;
  font-size: 26px;
  line-height: 1.1;
  letter-spacing: -0.025em;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
.tvalue small {
  font-family: var(--font-b);
  font-weight: 500;
  font-size: 13px;
  color: var(--ink-3);
}
.tsub {
  margin-top: 7px;
  font-size: 12.5px;
  color: var(--ink-3);
}

/* The affordance that says a figure has a rule behind it. Focusable, because
   a tooltip only a mouse can reach is a tooltip half the readers never see.

   Suhbat drives this from `data-tip` and a tooltip in charts.js; this product
   has no such tooltip, so the same mark carries a native `title`. Same class,
   same geometry, same states — only the delivery differs. */
.info {
  width: 14px;
  height: 14px;
  flex: none;
  border-radius: 50%;
  display: grid;
  place-items: center;
  background: var(--surface-hover);
  color: var(--ink-3);
  font-family: var(--font-b);
  font-size: 9.5px;
  font-weight: 700;
  text-transform: none;
  letter-spacing: 0;
  cursor: help;
}
.info:hover {
  background: var(--brand-soft);
  color: var(--brand-ink);
}
.info:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
}

/* ── The pipeline strip ──────────────────────────────────────────────────── */

/* Suhbat's quality strip, five across, same hairline trick as the hero tiles.
   Selectors are COMPOUND (`.qcard .qstrip`) exactly as they are there: a bare
   `.qstrip` rule would not win against them if the kit is ever re-synced. */
.qcard .qstrip {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 1px;
  background: var(--line);
}
.qtile {
  background: var(--surface);
  padding: 18px 20px 20px;
  display: flex;
  flex-direction: column;
  min-width: 0;
}
/* Stacked here, beside the figure in the hero. That inversion is the only
   structural difference between `.htile` and `.qtile`. */
.qtile .tico {
  margin-bottom: 14px;
}
.qvalue {
  font-family: var(--font-d);
  font-weight: 600;
  font-size: 25px;
  line-height: 1.1;
  letter-spacing: -0.025em;
  font-variant-numeric: tabular-nums;
  display: flex;
  align-items: baseline;
  gap: 8px;
  flex-wrap: wrap;
}
.qvalue small {
  font-family: var(--font-b);
  font-weight: 500;
  font-size: 13px;
  color: var(--ink-3);
}
.qlabel {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-top: 9px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-2);
}

/* ── The door ────────────────────────────────────────────────────────────── */

/* THE WHOLE TILE IS THE DOOR. A figure a reader cannot open is one they have
   to take on faith. Suhbat's tiles open a dialog; ours scroll to the section
   the figure was counted from, which is the behaviour the four count cards
   already had — the affordance is the same and so is the marking. */
.can-open {
  cursor: pointer;
  position: relative;
  transition:
    background 0.14s var(--ease),
    box-shadow 0.14s var(--ease),
    transform 0.14s var(--ease);
}
.can-open:hover {
  background: var(--surface-hover);
  box-shadow: var(--shadow-sm);
  /* A lift small enough not to move the grid under the pointer, large enough
     to answer «is this a button» before the click. */
  transform: translateY(-1px);
  z-index: 1;
}
.can-open:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: -2px;
  z-index: 2;
}
/* The chevron at rest, so an openable cell is legible in a screenshot and to
   a reader who never hovers. */
.cell-open {
  position: absolute;
  top: 12px;
  right: 12px;
  width: 14px;
  height: 14px;
  color: var(--ink-4);
  opacity: 0.5;
  transition:
    opacity 0.14s var(--ease),
    color 0.14s var(--ease);
  pointer-events: none;
}
.cell-open svg {
  width: 100%;
  height: 100%;
  display: block;
}
.can-open:hover .cell-open {
  opacity: 1;
  color: var(--brand);
}
/* Room for the chevron: without it a long label («Javobsiz savollar») runs
   under the mark. */
.qtile.can-open .qlabel,
.htile.can-open .tlabel {
  padding-right: 18px;
}

/* ── «E'tibor bering» ────────────────────────────────────────────────────── */

.attention {
  list-style: none;
  margin: 0;
  padding: 0;
}
.att {
  display: grid;
  grid-template-columns: 26px minmax(0, 1fr);
  gap: 14px;
  padding: 15px 18px;
  border-bottom: 1px solid var(--line);
}
.att:last-child {
  border-bottom: 0;
}
.att h3 {
  font-size: 14.5px;
  display: flex;
  align-items: center;
  gap: 9px;
  flex-wrap: wrap;
  margin: 0;
}
.att p {
  margin: 5px 0 0;
  font-size: 13.5px;
  line-height: 1.55;
  color: var(--ink-2);
}
/* The severity lives ONLY on this disc and on the chip inside the h3. The row
   background never changes: four tinted rows read as a broken page rather
   than as four things to read. */
.att .att-mark {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border-radius: 50%;
}
.att .att-mark svg {
  width: 15px;
  height: 15px;
}
.att.high .att-mark {
  background: var(--red-soft);
  color: var(--red-ink);
}
.att.medium .att-mark {
  background: var(--amber-soft);
  color: var(--amber);
}
/* The finding is a sentence, so what it links to is a link, not a whole row
   pretending to be one: a focusable <li> announces as a list item and gives a
   keyboard user nothing to go on. */
.att-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px 16px;
  margin-top: 8px;
  font-size: var(--t-small);
  color: var(--ink-3);
}
.att-foot:empty {
  display: none;
}
.att-foot .linky {
  font-size: var(--t-small);
  color: var(--brand-ink);
  font-weight: 600;
}
.linky {
  all: unset;
  cursor: pointer;
  color: var(--brand);
  font-weight: 600;
  border-bottom: 1px solid transparent;
}
.linky:hover {
  color: var(--brand-ink);
  border-bottom-color: var(--brand);
}
.linky:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: 3px;
}
/* ── Narrow ──────────────────────────────────────────────────────────────── */

/* The hero splits before the tiles do: a 30ch sentence beside a 2x2 grid stops
   working around 1100px, and stacking the two keeps both readable long before
   the tiles themselves have to become one column. */
@media (max-width: 1180px) {
  .hero {
    grid-template-columns: minmax(0, 1fr);
  }
  .hero-tiles {
    border-left: 0;
    border-top: 1px solid var(--line);
  }
  .qcard .qstrip {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}
@media (max-width: 760px) {
  .hero-say {
    padding: 20px 18px;
  }
  .hero-tiles {
    grid-template-columns: minmax(0, 1fr);
  }
  .qcard .qstrip {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .hero-line {
    max-width: none;
  }
}
@media (max-width: 700px) {
  .att {
    grid-template-columns: 1fr;
    gap: 10px;
  }
}
@media (max-width: 460px) {
  .qcard .qstrip {
    grid-template-columns: minmax(0, 1fr);
  }
}
