/* ABD-755: the TJM Labs brand (guidelines, November 2025). Beige for warmth, purple
   for the accent, grey for text; Inter for the interface, DM Serif Display for headings.
   Colours that carry meaning (ok / warn / err) are not brand colours and stay semantic,
   darkened so they read on a light page. */
:root {
  color-scheme: light;

  --bg:            #F7F5F2;
  --surface:       #FFFFFF;
  --surface-2:     #F3F0EB;
  --surface-3:     #ECE7E0;
  --border:        #E2DDD5;
  --border-strong: #D3CCC1;

  --fg:           #2E2E2E;
  --fg-secondary: #525252;
  --fg-muted:     #6B665F;
  --fg-faint:     #A8A29A;

  --brand:        #5C288F;
  --brand-strong: #4A1F75;
  --brand-dim:    rgba(92, 40, 143, 0.08);
  --brand-ring:   rgba(92, 40, 143, 0.28);
  --lavender:     #F1ECFF;
  --lilac:        #BBA2FE;
  /* Text on a solid brand fill: buttons, the unlimited and admin pills, a chosen chip. */
  --on-brand:       #FFFFFF;
  --on-brand-muted: #F1ECFF;

  --ok:    #15803D;  --ok-bg:    rgba(21, 128, 61, 0.08);   --ok-bd:    rgba(21, 128, 61, 0.28);
  --warn:  #A16207;  --warn-bg:  rgba(161, 98, 7, 0.09);    --warn-bd:  rgba(161, 98, 7, 0.30);
  --err:   #B91C1C;  --err-bg:   rgba(185, 28, 28, 0.07);   --err-bd:   rgba(185, 28, 28, 0.28);
  /* Gold marks the owner pill and a followed key. It is not a status colour. */
  --gold:  #A16207;  --gold-bg:  rgba(161, 98, 7, 0.10);    --gold-bd:  rgba(161, 98, 7, 0.30);
  --star:  #CA8A04;
  --neutral-bd: rgba(148, 163, 184, 0.28);
  --backdrop:   rgba(46, 46, 46, 0.45);

  --shadow-sm: 0 1px 2px rgba(46, 46, 46, 0.06);
  --shadow:    0 4px 12px rgba(46, 46, 46, 0.08);

  --serif: 'DM Serif Display', Georgia, 'Times New Roman', serif;

  --r-sm: 6px;
  --r:    8px;
  --r-lg: 12px;
}

/* ABD-775: the dark theme. Each person picks Light or Dark in the sidebar, and theme.js
   puts the choice on <html data-theme> before the first paint; a browser with no stored
   choice follows the device's own setting. These are the colours the admin had before
   ABD-755, with a dark value for every colour token ABD-755 added, and one change:
   --fg-muted was #7A7A85, which measures 4.36:1 on --surface and 3.72:1 on --surface-3,
   under the 4.5:1 that small text needs. #8A8A96 measures 4.62:1 or better on all four
   surfaces. Type, radius and spacing are the same in both themes, so only colour
   tokens are repeated here, and
   tests/test_abd775_theme_selector.py fails when a colour token above has no value below
   or when a rule further down carries a colour of its own. */
:root[data-theme="dark"] {
  color-scheme: dark;

  --bg:            #0B0B0F;
  --surface:       #131318;
  --surface-2:     #1A1A21;
  --surface-3:     #22222B;
  --border:        #26262E;
  --border-strong: #33333D;

  --fg:           #FAFAFA;
  --fg-secondary: #C4C4CC;
  --fg-muted:     #8A8A96;
  --fg-faint:     #4A4A55;

  --brand:        #8B5CF6;
  --brand-strong: #A78BFA;
  --brand-dim:    rgba(139, 92, 246, 0.12);
  --brand-ring:   rgba(139, 92, 246, 0.35);
  --lavender:     rgba(139, 92, 246, 0.12);
  --lilac:        rgba(139, 92, 246, 0.55);
  --on-brand:       #FFFFFF;
  --on-brand-muted: #FFFFFF;

  --ok:    #34D399;  --ok-bg:    rgba(52, 211, 153, 0.10);  --ok-bd:    rgba(52, 211, 153, 0.30);
  --warn:  #FBBF24;  --warn-bg:  rgba(251, 191, 36, 0.10);  --warn-bd:  rgba(251, 191, 36, 0.30);
  --err:   #F87171;  --err-bg:   rgba(248, 113, 113, 0.10); --err-bd:   rgba(248, 113, 113, 0.30);
  --gold:  #EAB308;  --gold-bg:  rgba(234, 179, 8, 0.14);   --gold-bd:  rgba(234, 179, 8, 0.35);
  --star:  #EAB308;
  --neutral-bd: rgba(148, 163, 184, 0.28);
  --backdrop:   rgba(0, 0, 0, 0.55);

  --shadow-sm: 0 1px 0 rgba(255, 255, 255, 0.02) inset, 0 1px 2px rgba(0, 0, 0, 0.4);
  --shadow:    0 1px 0 rgba(255, 255, 255, 0.03) inset, 0 4px 12px rgba(0, 0, 0, 0.45);
}
* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; height: 100%; }
body {
  font: 14px/1.55 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  background: var(--bg);
  color: var(--fg);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
a { color: var(--brand-strong); text-decoration: none; }
a:hover { text-decoration: underline; }
::selection { background: var(--brand-dim); color: var(--fg); }

/* ----- Shell: sidebar + main ----- */
.app { display: grid; grid-template-columns: 240px 1fr; min-height: 100vh; }
.sidebar {
  position: sticky;
  top: 0;
  align-self: start;
  height: 100vh;
  /* ABD-775: the sidebar is exactly one screen tall and stuck to the top, so anything
     that does not fit is unreachable, not merely below the fold. An admin's 12 items plus
     the foot need 698px: measured in Chromium, Sign out was cut off in a window 680px
     tall. Scroll inside it. */
  overflow-y: auto;
  background: var(--surface);
  border-right: 1px solid var(--border);
  padding: 24px 16px 20px;
  display: flex;
  flex-direction: column;
  gap: 24px;
}
.sidebar-brand { display: block; padding: 0 8px 16px; border-bottom: 1px solid var(--border); }
.sidebar-brand:hover { text-decoration: none; }
.sidebar-brand .name {
  font-family: var(--serif);
  font-size: 22px;
  font-weight: 400;
  color: var(--fg);
  letter-spacing: 0;
  display: flex;
  align-items: center;
  gap: 10px;
}
.sidebar-brand .mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  border-radius: 7px;
  flex-shrink: 0;
}
.sidebar-brand .sub {
  margin-top: 8px;
  font-size: 10.5px;
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.10em;
  font-weight: 500;
  padding-left: 38px;
}
.sidebar-brand .sub .tag {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--surface-2);
  color: var(--fg-secondary);
  font-size: 9.5px;
  letter-spacing: 0.08em;
  margin-left: 4px;
}
.sidebar-nav { display: flex; flex-direction: column; gap: 2px; position: relative; }
.navitem {
  position: relative;
  display: flex;
  align-items: center;
  gap: 10px;
  height: 36px;
  padding: 0 12px;
  border-radius: var(--r-sm);
  color: var(--fg-secondary);
  font-size: 13px;
  font-weight: 500;
  text-decoration: none;
  transition: background 0.1s ease, color 0.1s ease;
}
.navitem:hover { background: var(--surface-2); color: var(--fg); text-decoration: none; }
/* A nav entry for a surface this deployment does not serve. Shown rather than hidden,
   for ABD-330's reason - an invisible feature is one nobody can ask for - but not a
   link, because a link to a 404 is a control that does nothing. Only a stack that sets
   DRUGBASE_QUERY_CONSOLE=off renders this; production never does. */
.navitem-absent { opacity: .45; cursor: default; }
.navitem-absent:hover { background: none; }
.navitem.active {
  background: var(--brand-dim);
  color: var(--fg);
}
.navitem.active::before {
  content: "";
  position: absolute;
  left: 0;
  top: 6px;
  bottom: 6px;
  width: 2px;
  background: var(--brand);
  border-radius: 2px;
}
.navitem .dot {
  width: 6px;
  height: 6px;
  border-radius: 50%;
  background: var(--fg-faint);
  flex-shrink: 0;
}
.navitem.active .dot { background: var(--brand-strong); box-shadow: 0 0 0 3px var(--brand-ring); }
.sidebar-foot { margin-top: auto; padding-top: 16px; border-top: 1px solid var(--border); }
/* ABD-775: the Light / Dark switch. It is rendered hidden and theme.js reveals it, so a
   browser that did not run the script shows no control that does nothing. The selectors
   carry `button` because the bare `button` rules further down would otherwise win on
   hover. */
.theme-switch {
  display: flex; gap: 2px; padding: 2px; margin-bottom: 10px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: var(--r);
}
.theme-switch[hidden] { display: none; }
button.theme-opt {
  flex: 1; height: 28px; padding: 0 10px; border: 0; border-radius: var(--r-sm);
  background: transparent; color: var(--fg-secondary); font-size: 12px; font-weight: 500;
}
button.theme-opt:hover { background: transparent; color: var(--fg); }
button.theme-opt[aria-pressed="true"],
button.theme-opt[aria-pressed="true"]:hover {
  background: var(--surface); color: var(--fg); box-shadow: var(--shadow-sm);
}
button.theme-opt:focus-visible { box-shadow: 0 0 0 3px var(--brand-ring); }

/* Curation sub-nav strip (ABD-105): horizontal sub-tabs under the page header. */
.subnav {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin: 0 0 20px;
  padding-bottom: 12px;
  border-bottom: 1px solid var(--border);
}
.subtab {
  display: inline-flex;
  align-items: center;
  height: 30px;
  padding: 0 12px;
  border-radius: var(--r-sm);
  color: var(--fg-secondary);
  font-size: 13px;
  font-weight: 500;
  text-decoration: none;
  transition: background 0.1s ease, color 0.1s ease;
}
.subtab:hover { background: var(--surface-2); color: var(--fg); text-decoration: none; }
.subtab.active { background: var(--brand-dim); color: var(--brand-strong); }

/* Data Quality tab bars (ABD-111): label | filled track | value. Pure CSS, no JS. */
.qrow { display: flex; align-items: center; gap: 12px; padding: 4px 0; font-size: 13px; }
.qlabel { flex: 0 0 200px; color: var(--fg-secondary); overflow-wrap: anywhere; }
.qtrack { flex: 1 1 auto; height: 10px; background: var(--surface-2); border-radius: 5px; overflow: hidden; }
.qfill { height: 100%; border-radius: 5px; min-width: 2px; }
.qval { flex: 0 0 76px; text-align: right; color: var(--fg-muted); font-size: 12px; font-variant-numeric: tabular-nums; }

/* Trend sparklines (ABD-113): a labelled header row above each inline-SVG line. */
.trend-h { display: flex; justify-content: space-between; align-items: baseline; margin-bottom: 4px; font-size: 13px; color: var(--fg-secondary); }
.trend-v { color: var(--fg-muted); font-size: 12px; font-variant-numeric: tabular-nums; }

.main { padding: 40px 40px 64px; min-width: 0; }
.container { max-width: 880px; margin: 0 auto; }

/* ----- Auth shell (sign-in) ----- */
.auth-shell {
  min-height: 100vh;
  display: grid;
  place-items: center;
  padding: 24px;
  background:
    radial-gradient(900px 500px at 50% -10%, var(--lavender), transparent 60%),
    var(--bg);
}
.auth-card {
  width: 100%;
  max-width: 400px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 32px;
  box-shadow: var(--shadow);
}
.auth-brand { text-align: center; margin-bottom: 28px; }
.auth-brand .mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  margin-bottom: 14px;
}
.auth-brand .name { font-family: var(--serif); font-size: 28px; font-weight: 400; color: var(--fg); letter-spacing: 0; }
.auth-brand .sub {
  margin-top: 4px;
  font-size: 11px;
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.10em;
  font-weight: 500;
}
.auth-brand .sub .tag {
  display: inline-block;
  padding: 1px 6px;
  border-radius: 4px;
  background: var(--surface-2);
  color: var(--fg-secondary);
  font-size: 9.5px;
  letter-spacing: 0.08em;
  margin-left: 4px;
}
.auth-card .field { display: flex; flex-direction: column; gap: 6px; margin-bottom: 16px; }
.auth-card .field label { font-size: 12px; color: var(--fg-muted); font-weight: 500; }
.auth-card .btn { width: 100%; justify-content: center; }
.auth-foot { margin-top: 24px; text-align: center; font-size: 12px; color: var(--fg-muted); }

/* "Sign in with Google" - primary CTA, styled to the drugbase dark/purple theme
   (Google's dark button variant) rather than the stock white button: an elevated
   dark surface with a brand-purple border + hover glow. The colored G is kept per
   Google branding (recognisable + compliant on a dark surface). */
.btn-google {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 12px;
  width: 100%;
  height: 44px;
  margin-bottom: 20px;
  background: var(--surface-2);
  color: var(--fg);
  border: 1px solid var(--brand-ring);
  border-radius: var(--r-md, 10px);
  font-family: inherit;
  font-size: 14px;
  font-weight: 600;
  letter-spacing: .1px;
  transition: background .15s ease, border-color .15s ease, box-shadow .15s ease;
}
.btn-google:hover {
  background: var(--surface-3);
  border-color: var(--brand-strong);
  box-shadow: 0 0 0 3px var(--brand-dim);
  text-decoration: none;
}
.btn-google:active { background: var(--surface-2); }
.btn-google svg { flex: 0 0 18px; display: block; }
/* Divider between the Google button and the break-glass token form. */
.auth-or {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 20px;
  color: var(--fg-muted);
  font-size: 12px;
}
.auth-or::before,
.auth-or::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--border);
}
/* Small "break-glass" tag beside the Access token label. */
.field label .hint {
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 999px;
  background: var(--surface-3);
  color: var(--fg-muted);
  font-size: 10px;
  font-weight: 500;
  text-transform: uppercase;
  letter-spacing: .4px;
  vertical-align: middle;
}

/* ----- Typography ----- */
h1 {
  margin: 0 0 6px;
  font-family: var(--serif);
  font-size: 30px;
  font-weight: 400;
  line-height: 1.15;
  color: var(--fg);
  letter-spacing: 0;
}
h1 code {
  font-size: 20px;
  background: var(--surface-2);
  color: var(--fg);
  border: 1px solid var(--border);
  padding: 1px 8px;
  border-radius: var(--r-sm);
}
.page-sub { color: var(--fg-muted); margin: 0 0 28px; font-size: 14px; }
.eyebrow {
  font-size: 11px;
  font-weight: 600;
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
}

/* ----- Card ----- */
.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 20px 24px;
  margin-bottom: 20px;
  box-shadow: var(--shadow-sm);
}
.card-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin: -4px 0 16px;
}
.card-head h2 { margin: 0; font-size: 11px; font-weight: 600; color: var(--fg-muted); text-transform: uppercase; letter-spacing: 0.08em; }
/* Base .meta: small muted text. Used by the keys status line + pager indicator
   (injected by admin.js OUTSIDE .card-head), which were previously unstyled. */
.meta { font-size: 12px; color: var(--fg-muted); }
.card-head .meta { font-size: 12px; color: var(--fg-muted); }

/* ----- Table ----- */
table.tbl { width: 100%; border-collapse: collapse; }
table.tbl th, table.tbl td {
  text-align: left;
  padding: 10px 10px;
  font-size: 13px;
  vertical-align: middle;
}
/* Aggressive wrapping is OPT-IN per table (e.g. the Noise tab's long IPs/paths).
   It must NOT be global: on the Keys table it broke short tokens mid-word - the
   tier pill rendered "STANDAR/D", fingerprints + timestamps split across 2 lines. */
table.tbl--wrap th, table.tbl--wrap td { overflow-wrap: anywhere; }
table.tbl th {
  color: var(--fg-muted);
  font-weight: 600;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  border-bottom: 1px solid var(--border);
  padding-bottom: 10px;
}
table.tbl td { border-bottom: 1px solid var(--border); }
table.tbl tbody tr:last-child td { border-bottom: 0; }
table.tbl tbody tr { transition: background 0.08s ease; }
table.tbl tbody tr:hover { background: var(--surface-2); }
table.tbl .num { text-align: right; font-variant-numeric: tabular-nums; }
table.tbl .actions { text-align: right; width: 1%; white-space: nowrap; }
/* Space the controls in an actions cell so a role <select>+Set form and a Delete form
   don't run together ("admin Set Delete" crammed). Each form's own controls get a small
   gap; adjacent forms get a larger one. */
table.tbl .actions form.inline { display: inline-flex; align-items: center; gap: 6px; vertical-align: middle; }
table.tbl .actions form.inline + form.inline { margin-left: 12px; }
table.tbl .actions form.inline select { min-width: 96px; }
/* Timestamp cell: muted + one line (was splitting "...18" / "GMT+0" across rows). */
table.tbl .cell-created { color: var(--fg-muted); white-space: nowrap; }

/* Reusable responsive-table utilities (ABD-258): rather than ever scroll a table
   horizontally, drop a low-priority column below a breakpoint. Put the class on BOTH
   the <th> and every matching <td>. `.nowrap` keeps compact cells (timestamps) on one
   line; `.wrap` lets a long free-text cell wrap instead of forcing width. */
table.tbl td.nowrap, table.tbl th.nowrap { white-space: nowrap; }
table.tbl td.wrap { white-space: normal; overflow-wrap: anywhere; }
table.tbl .follow-col { width: 30px; text-align: center; padding-left: 4px; padding-right: 0; }
table.tbl .follow-col .btn--sm { padding: 2px 5px; }
@media (max-width: 1150px) { table.tbl .col-hide-1150 { display: none; } }
@media (max-width: 1024px) { table.tbl .col-hide-1024 { display: none; } }
/* Below 640px the table stops being a table (see "Phone layout" at the end of this
   file), so a column hidden here is hidden in the CARD too - use it only for a column
   that is noise on a phone, never for one that carries the row's meaning. */
@media (max-width: 640px)  { table.tbl .col-hide-640  { display: none; } }

/* ----- Keys table (ABD-252): fit all columns with NO horizontal scroll -----
   The label column is the ONLY flexible one - it wraps so the table shrinks to the
   container instead of forcing a horizontal scroll; the fingerprint + timestamps stay
   on one line (compact fonts) and the owner input flexes. Auto layout so nothing clips.
   Columns: 1 Label, 2 Owner, 3 Tier, 4 Fingerprint, 5 Created, 6 Last seen, 7 actions. */
table.tbl--keys th, table.tbl--keys td { padding-left: 6px; padding-right: 6px; }
/* Label (1st col) is the flex column: wrap long labels rather than scroll. Break at
   hyphens (break-word) so a long "A-B-C-Pharmacy" splits cleanly instead of mid-token.
   The extra .tbl class outranks the `td code { white-space: nowrap }` rule below. */
table.tbl.tbl--keys td:nth-child(1) code { white-space: normal; overflow-wrap: break-word; }
table.tbl--keys .owner-cell { width: 15%; max-width: 12rem; }  /* cap owner so the label keeps room */
table.tbl--keys .owner-cell form { display: flex; gap: 4px; align-items: center; }
table.tbl--keys .owner-cell input { font-size: 12px; }
table.tbl--keys .cell-created, table.tbl--keys td:nth-child(6) { font-size: 11.5px; }  /* timestamps */
table.tbl--keys td:nth-child(4) code { font-size: 11px; }   /* fingerprint: compact, one line */
/* Below ~1150px the sidebar leaves too little room for all columns, so drop the
   lowest-priority one (Created) rather than ever scroll horizontally - "Last seen"
   (which drives the dormant surface) and the rest stay. Progressive disclosure > a
   hidden horizontal scrollbar. */
@media (max-width: 1150px) {
  table.tbl--keys th:nth-child(5), table.tbl--keys td:nth-child(5) { display: none; }  /* Created */
}
@media (max-width: 1024px) {
  table.tbl--keys th:nth-child(4), table.tbl--keys td:nth-child(4) { display: none; }  /* Fingerprint (label still IDs the key) */
}

/* ----- Audit table (ABD-449): FIXED layout, so content cannot move a column -----
   This table used the Noise tab's `.tbl--wrap`, which puts `overflow-wrap: anywhere` on
   every th and td. Under AUTO layout that is ruinous: `anywhere` (unlike `break-word`)
   drops a column's MIN-CONTENT width to one character, so the browser was free to
   squeeze any of the seven to nothing and hand the slack to whichever was longest.
   Measured in Chromium before the fix - at 900px Actor was 41px, Target 44px and IP
   43px, the headers rendered "AC/TO/R" and "TA/RG/ET", and one row was 204px tall
   because every value was a column of single characters.

   The first fix put min/max widths on the columns and left auto layout in place. That
   was wrong in the other direction, and only a browser showed it: min-width is a HINT
   to auto layout, not a floor, and the sum of the columns' content widths still sets
   the table's width. One audit line with an IPv6 peer - `2001:0db8:...:7334 (fwd ...)`,
   a shape `client_ip()` produces and prod can serve today - measured a 295px IP column
   and a 1210px table inside an 1180px container, i.e. the table was CLIPPED at every
   width including 1920, and by 330px at 1200.

   `table-layout: fixed` removes the whole class. Column widths come from the header row
   alone and content is not consulted, so:
     - the table is always exactly its container's width. It cannot clip, at any width,
       for any content - which is the ABD-258 rule ("never scroll a data table") made
       structural instead of emergent;
     - no column can be squeezed by a neighbour, so no value or header can shred;
     - `overflow-wrap: anywhere` becomes SAFE, because under fixed layout min-content no
       longer influences any column. That is why it is granted freely below to every
       cell that can hold an unbreakable string.
   The percentages must sum to 100. When Source and IP hide at 1150 the remainder is
   redistributed in proportion, which is what keeps the five-column band honest. */
table.tbl--audit { table-layout: fixed; width: 100%; }
table.tbl--audit th, table.tbl--audit td { padding-left: 8px; padding-right: 8px; }
/* Every header here is one short word, so wrapping one only makes it taller for nothing. */
table.tbl--audit th { white-space: nowrap; }
/* Sized from the widest REAL content each column holds, not by eye. `Action` is the
   binding one: it renders a pill, `.pill` may never wrap (a wrapped "STANDAR/D" is why
   that rule exists), and a nowrap box wider than its fixed column paints OUTSIDE the
   cell - so the share has to cover the longest action name the tree actually writes.
   Measured: `account.query_access` is 185px and `curation.approve` 150px at 13px Inter. */
table.tbl--audit .col-when    { width: 11%; }
table.tbl--audit .col-src     { width: 9%; }
table.tbl--audit .col-actor   { width: 15%; }
table.tbl--audit .col-action  { width: 18%; }
table.tbl--audit .col-target  { width: 14%; }
table.tbl--audit .col-details { width: 22%; }
table.tbl--audit .col-ip      { width: 11%; }
/* The two pills on this table allowed to wrap, and each only at a break its content
   already contains: the action pill at the dot `_action_html` marks with a `<wbr>`, and
   the actor-class pill at the space in "shared login" / "no actor". `white-space: normal`
   grants nothing else - there is deliberately no `overflow-wrap` here - so the rule a
   pill exists to keep, that it must never split a WORD ("STANDAR/D"), is intact.
   Without this, a pill wider than its fixed column paints across the column beside it:
   measured `account.query_access` at 185px against 155px, and the `ops` + "shared login"
   pair at 117px against 109px.

   The other two declarations are not optional, and a review round found both missing:
     - `overflow-wrap` is an INHERITED property, so these pills were picking up the
       `anywhere` their cell is granted below and splitting mid-word - "AUTOMATE/D",
       exactly the failure `.pill { white-space: nowrap }` exists to prevent, reached by
       the one route a rule that merely omits `overflow-wrap` cannot block. It has to be
       set back to `normal` explicitly.
     - `.pill` is `height: 22px`, a fixed height chosen when no pill could wrap. A pill
       that wraps to two lines inside a 22px capsule paints its second line outside its
       own badge. `height: auto` with the 22px kept as a floor leaves every one-line pill
       on the dashboard pixel-identical and lets these two grow. */
table.tbl--audit .col-action .pill,
table.tbl--audit .col-actor .pill {
  white-space: normal;
  overflow-wrap: normal;
  height: auto;
  min-height: 22px;
}
/* The timestamp may wrap between the date and the time. It used to be pinned to one line
   (`.cell-created`), which is right under auto layout - a nowrap cell WIDENS its column
   there - and wrong under fixed, where the column is 11% whatever happens and a nowrap
   value simply paints outside it. It has a space to break at, so it breaks cleanly. */
table.tbl--audit td.col-when { white-space: normal; }
/* Everything that can hold a string with no break opportunity: a fingerprint, an IPv6
   address, a pharmacy label, a SQL statement. Safe here only because the layout is
   fixed - under auto this is the bug at the top of this comment.
   `table.tbl.tbl--audit td.<col> code` is three classes on purpose: these chips compete
   with `table.tbl:not(.tbl--wrap) td code { white-space: nowrap }`, which is (0,2,3)
   because `:not()` contributes its argument, so a two-class selector LOSES to it and
   every chip stays on one line. */
table.tbl--audit .col-actor,
table.tbl--audit .col-target,
table.tbl--audit .col-details,
table.tbl--audit .col-ip { overflow-wrap: anywhere; }
table.tbl.tbl--audit td.col-actor code,
table.tbl.tbl--audit td.col-target code,
table.tbl.tbl--audit td.col-details code,
table.tbl.tbl--audit td.col-ip code { white-space: normal; overflow-wrap: anywhere; }
/* One element around the whole `k=v k=v` run, and around the actor's handle + class pill
   + identity line, because below 640px the cell becomes a grid and grid turns every
   in-flow CHILD into its own item - a bare run laid the keys and their chips out down
   the card's label/value columns. `inline` keeps these inert in the table view; they
   exist to be a single grid item in the card. */
.detail-pairs, .actor-cell { display: inline; }
/* An absent value is spelled out instead of being rendered as an empty <code> chip,
   which read as a rendering fault. It borrows the chip's padding so `owner=(none)` sits
   where `owner= someone` would, and the faint italic keeps it legible as a note ABOUT
   the value rather than as the value. */
.detail-none { padding: 2px 6px; color: var(--fg-muted); font-style: italic; }

/* ----- Inline code chips ----- */
code {
  background: var(--surface-2);
  color: var(--fg-secondary);
  padding: 2px 6px;
  border-radius: var(--r-sm);
  font-family: 'Inconsolata', ui-monospace, monospace;
  font-size: 12.5px;
  border: 1px solid var(--border);
}
td code { border-color: transparent; background: var(--surface-2); }
/* Code chips that are short identifiers (key label, fingerprint) stay on one line;
   the ellipsis in a fingerprint (9bef0a1f…1341c6ab) is otherwise a wrap point.
   The Noise tables (.tbl--wrap) are exempt - their IPs/paths SHOULD wrap. */
table.tbl:not(.tbl--wrap) td code { white-space: nowrap; }

/* ----- Form controls ----- */
input, select, textarea {
  font: inherit;
  height: 36px;
  padding: 0 12px;
  border-radius: var(--r-sm);
  border: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--fg);
  transition: border-color 0.1s ease, box-shadow 0.1s ease, background 0.1s ease;
}
input::placeholder, textarea::placeholder { color: var(--fg-faint); }
input:hover, select:hover, textarea:hover { border-color: var(--border-strong); }
input:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-ring);
}
select {
  appearance: none;
  -webkit-appearance: none;
  background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'><path fill='%237A7A85' d='M2.5 4.5 6 8l3.5-3.5z'/></svg>");
  background-repeat: no-repeat;
  background-position: right 10px center;
  padding-right: 30px;
}

/* ----- Buttons ----- */
.btn, button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 6px;
  height: 36px;
  padding: 0 14px;
  border-radius: var(--r-sm);
  border: 1px solid transparent;
  background: var(--brand);
  color: var(--on-brand);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
  text-decoration: none;
  transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease, box-shadow 0.12s ease, transform 0.06s ease;
}
.btn:hover, button:hover { background: var(--brand-strong); text-decoration: none; }
.btn:active, button:active { transform: translateY(1px); }
.btn:focus-visible, button:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px var(--brand-ring);
}
.btn--ghost, button.ghost {
  background: transparent;
  color: var(--fg-secondary);
  border-color: var(--border);
  font-weight: 500;
}
.btn--ghost:hover, button.ghost:hover {
  background: var(--surface-2);
  border-color: var(--border-strong);
  color: var(--fg);
}
.btn--danger, button.danger {
  background: transparent;
  color: var(--err);
  border-color: var(--err-bd);
  font-weight: 500;
}
.btn--danger:hover, button.danger:hover {
  background: var(--err-bg);
  border-color: var(--err);
  color: var(--err);
}
.btn--sm { height: 30px; padding: 0 10px; font-size: 12px; }

/* ----- Forms layout ----- */
.row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
/* Vertical form layout (Add-drug form): stack rows/fields with breathing room. */
.stack { display: flex; flex-direction: column; gap: 16px; align-items: stretch; }
.stack .field > input, .stack .field > select { width: 100%; }
.field { display: flex; flex-direction: column; gap: 6px; }
.field > label { font-size: 12px; color: var(--fg-muted); font-weight: 500; }
form.inline { display: inline; }

/* ----- Feedback form (ABD-270): a single narrow card, fields stacked full width ----- */
.page-head { margin-bottom: 10px; }
.fb-form { max-width: 640px; display: flex; flex-direction: column; gap: 18px; }
.fb-form .field { gap: 7px; }
.fb-form .field > input,
.fb-form .field > select,
.fb-form .field > textarea { width: 100%; }
.fb-form textarea { resize: vertical; min-height: 130px; line-height: 1.5; }
.fb-form input[type="file"] { padding: 8px 10px; font-size: 13px; cursor: pointer; }
.field-hint { margin: 2px 0 0; font-size: 12px; color: var(--fg-faint); }
.fb-actions { display: flex; gap: 10px; align-items: center; }
/* Sleek upload drop-zone: click / drag-drop / paste, with removable thumbnails. The
   native file input is visually hidden (kept keyboard-focusable), and the styled label
   is the drop target + click surface. */
.fb-file-input { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }
.fb-dropzone { display: block; border: 1.5px dashed var(--border-strong); border-radius: var(--r-sm);
  background: var(--surface-2); padding: 22px 16px; cursor: pointer; text-align: center;
  transition: border-color .12s ease, background .12s ease; }
.fb-dropzone:hover { border-color: var(--brand); }
.fb-dropzone:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); }
.fb-dropzone.dragover { border-color: var(--brand); background: var(--brand-dim); }
.fb-dropzone-inner { display: flex; flex-direction: column; align-items: center; gap: 4px; pointer-events: none; }
.fb-dropzone-icon { color: var(--fg-muted); margin-bottom: 2px; }
.fb-dropzone-text { font-size: 13px; color: var(--fg); }
.fb-dropzone-text strong { color: var(--brand); font-weight: 600; }
.fb-dropzone-sub { font-size: 12px; color: var(--fg-faint); }
.fb-paste-preview { display: flex; flex-wrap: wrap; gap: 10px; }
.fb-paste-preview:not(:empty) { margin-top: 10px; }
.fb-thumb-wrap { position: relative; display: inline-flex; }
.fb-paste-thumb { max-height: 88px; max-width: 140px; border: 1px solid var(--border-strong);
  border-radius: var(--r-sm); display: block; }
.fb-thumb-x { position: absolute; top: -7px; right: -7px; width: 20px; height: 20px; border-radius: 999px;
  border: 1px solid var(--border-strong); background: var(--surface-2); color: var(--fg-muted);
  font-size: 14px; line-height: 1; cursor: pointer; display: flex; align-items: center;
  justify-content: center; padding: 0; }
.fb-thumb-x:hover { background: var(--err-bg); color: var(--err); border-color: var(--err-bd); }

/* ----- Feedback inbox (ABD-271): owner-only triage, card list (no wide table) ----- */
.fb-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; flex-wrap: wrap; }
.fb-chips { display: flex; flex-wrap: wrap; gap: 8px; margin: 2px 0 14px; }
.fb-chip { display: inline-flex; align-items: center; gap: 6px; padding: 5px 12px; border-radius: 999px;
  border: 1px solid var(--border-strong); background: var(--surface-2); color: var(--fg-muted);
  font-size: 13px; text-decoration: none; }
.fb-chip:hover { border-color: var(--brand); color: var(--fg); }
.fb-chip.active { background: var(--brand-dim); color: var(--brand); border-color: var(--brand-ring); }
.fb-chip-n { font-size: 11px; opacity: 0.75; }
.fb-filters { display: flex; gap: 10px; align-items: center; margin-bottom: 18px; flex-wrap: wrap; }
.fb-filters input[type="search"] { min-width: 240px; }
.fb-list { display: flex; flex-direction: column; gap: 14px; }
.fb-card { padding: 16px 18px; display: flex; flex-direction: column; gap: 12px; }
.fb-card-head { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
.fb-card-meta { font-size: 12px; color: var(--fg-muted); }
.fb-card-msg { white-space: pre-wrap; overflow-wrap: anywhere; line-height: 1.55; }
.fb-thumbs { display: flex; flex-wrap: wrap; gap: 10px; }
.fb-thumb { display: block; border: 1px solid var(--border-strong); border-radius: var(--r-sm); overflow: hidden; }
.fb-thumb img { display: block; max-height: 160px; max-width: 240px; }
.fb-card-actions { display: flex; flex-wrap: wrap; gap: 16px; align-items: flex-start;
  border-top: 1px solid var(--border-strong); padding-top: 12px; }
.fb-status-form { display: inline-flex; gap: 8px; align-items: center; }
.fb-note-form { display: flex; flex-direction: column; gap: 6px; flex: 1; min-width: 260px; }
.fb-note-form textarea { width: 100%; resize: vertical; min-height: 46px; }
.fb-note-form button { align-self: flex-start; }
.fb-dup { color: var(--warn); }
.fb-sub, .fb-frompage { color: var(--fg-muted); }
.pill.fb-kind-bug { background: var(--warn-bg); color: var(--warn); border-color: var(--warn-bd); }
.pill.fb-kind-request { background: var(--brand-dim); color: var(--brand); border-color: var(--brand-ring); }
.pill.fb-st-new { background: var(--brand-dim); color: var(--brand); border-color: var(--brand-ring); }
.pill.fb-st-triaged { background: var(--warn-bg); color: var(--warn); border-color: var(--warn-bd); }
.pill.fb-st-promoted { background: var(--ok-bg); color: var(--ok); border-color: var(--ok-bd); }
.pill.fb-st-dismissed { background: var(--surface-2); color: var(--fg-faint); border-color: var(--border-strong); }
.fb-promote-cell { display: inline-flex; align-items: center; }
.fb-inlinear { color: var(--ok); border-color: var(--ok-bd); }
.fb-delete { color: var(--err); }
.fb-delete:hover { border-color: var(--err); }

/* ----- Feedback history + "Add new feedback" dialog (ABD-469) -----
   admin.js opens the dialog with showModal(). Without JS, or before the upgrade runs,
   a dialog the server rendered `open` is an ordinary card in the page flow, which is
   what the :not(:modal) rule styles. */
.fb-head-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.fb-dialog { width: min(640px, calc(100vw - 24px)); max-height: calc(100dvh - 24px); overflow: auto;
  padding: 20px 22px; border: 1px solid var(--border-strong); border-radius: var(--r-lg);
  background: var(--surface); color: var(--fg); box-shadow: var(--shadow); }
.fb-dialog::backdrop { background: var(--backdrop); }
.fb-dialog[open]:not(:modal) { position: static; width: auto; max-height: none; margin: 0 0 18px;
  box-shadow: none; }
.fb-dialog > h2 { margin: 0 0 4px; font-size: 18px; }
.fb-dialog .fb-actions { display: flex; flex-wrap: wrap; gap: 8px; justify-content: flex-end; }
/* Busy/disabled submit buttons (admin.js data-busy-label): read as inactive while the
   request is in flight, so a double-click can't fire a second POST. */
.btn:disabled, .btn[disabled] { opacity: 0.6; cursor: default; }
/* Higher specificity than .btn:disabled so the busy cursor actually wins; no
   pointer-events:none - a native disabled button already can't be activated. */
.btn[aria-busy="true"] { cursor: progress; }

/* ----- Pills ----- */
.pill {
  /* Pills sit inside headings (the audit "chain verified" badges are in an <h1>), so
     they must not inherit the serif heading face. */
  font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
  display: inline-flex;
  align-items: center;
  height: 22px;
  padding: 0 10px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  border: 1px solid transparent;
  white-space: nowrap;   /* a pill label must never wrap (e.g. "STANDARD") */
}
.pill.standard {
  background: var(--brand-dim);
  color: var(--brand-strong);
  border-color: var(--brand-ring);
}
.pill.unlimited {
  background: var(--brand);
  color: var(--on-brand);
}
/* ABD-437: an actor that is NOT a person. A shared credential and an automated
   writer both landed in the same column as a named account and rendered identically,
   so an anonymous action read exactly like an accountable one. Amber for the shared
   login (a human is behind it, just not a knowable one); grey for automated (nobody
   decided anything); grey for a missing actor. */
.pill.actor-shared {
  background: var(--warn-bg);
  color: var(--warn);
  border-color: var(--warn-bd);
}
.pill.actor-automated,
.pill.actor-credential,
.pill.actor-unknown {
  background: var(--surface-2);
  color: var(--fg-faint);
  border-color: var(--border-strong);
}
/* ABD-436: a submission carrying a GCN the database has never seen. Red, because
   the reviewer must go and check it rather than glance at it - a GCN cannot be
   derived, so an unrecognised one is either a new drug family or a fabrication. */
.pill.unknown-gcn {
  background: var(--err-bg);
  color: var(--err);
  border-color: var(--err-bd);
}
/* Writer = a different capability (write-only) from standard/unlimited reads, so
   give it its own colour (amber) - it was indistinguishable from standard before. */
.pill.writer {
  background: var(--warn-bg);
  color: var(--warn);
  border-color: var(--warn-bd);
}
/* Account roles (ENC2-6): admin = solid brand, reviewer = dim/outlined. */
.pill.admin {
  background: var(--brand);
  color: var(--on-brand);
}
.pill.reviewer {
  background: var(--brand-dim);
  color: var(--brand-strong);
  border-color: var(--brand-ring);
}
.pill.owner {
  background: var(--gold-bg);
  color: var(--gold);
  border-color: var(--gold-bd);
}
.pill.member {
  background: var(--surface-2);
  color: var(--fg-secondary);
  border-color: var(--neutral-bd);
}

/* ----- Flash / inline notices ----- */
.flash {
  padding: 12px 14px;
  border-radius: var(--r);
  margin-bottom: 20px;
  font-size: 13px;
  border: 1px solid;
  line-height: 1.5;
}
.flash.ok { background: var(--ok-bg); border-color: var(--ok-bd); color: var(--ok); }
.flash.err { background: var(--err-bg); border-color: var(--err-bd); color: var(--err); }
.flash.warn { background: var(--warn-bg); border-color: var(--warn-bd); color: var(--warn); }
.flash strong { color: inherit; }

/* ----- KPI strip (Usage page headline) ----- */
.kpi-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 16px;
}
.kpi {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: 16px 18px;
}
.kpi .label {
  font-size: 11px;
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  font-weight: 600;
}
.kpi .value {
  margin-top: 8px;
  font-size: 26px;
  font-weight: 600;
  color: var(--fg);
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}
.kpi .delta { margin-top: 4px; font-size: 12px; color: var(--fg-muted); }
.kpi.ok   .value { color: var(--ok); }
.kpi.warn .value { color: var(--warn); }
.kpi.err  .value { color: var(--err); }

/* ----- Card row: side-by-side when there's room for two readable tables,
   otherwise each stacks full-width (no horizontal scroll). auto-fit +
   minmax(min(560px,100%),1fr): >=~1140px -> 2 columns; narrower -> 1 full-width
   column; the min() guard means a column is never wider than the container, so
   phones don't scroll the page either. ----- */
.grid-2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(560px, 100%), 1fr)); gap: 20px; }

/* ----- Key display (issue-success page) ----- */
.key-display {
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--fg);
  padding: 16px 18px;
  border-radius: var(--r);
  font-family: 'Inconsolata', ui-monospace, monospace;
  font-size: 13.5px;
  word-break: break-all;
  user-select: all;
  cursor: copy;
  border: 1px solid var(--border);
}

/* ----- Foot / helper text ----- */
.foot { color: var(--fg-muted); font-size: 12.5px; margin-top: 28px; line-height: 1.65; }
.foot a { color: var(--brand-strong); }

/* ----- Empty state ----- */
.empty {
  text-align: center;
  padding: 36px 20px;
  color: var(--fg-muted);
  font-size: 13px;
}

/* ----- Help tooltip ([data-tip], positioned via JS) (ABD-227) ----- */
.help-tip {
  position: fixed;
  z-index: 60;
  pointer-events: none;
  max-width: 320px;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  padding: 9px 12px;
  font-size: 12.5px;
  line-height: 1.45;
  color: var(--fg);
  box-shadow: var(--shadow);
  opacity: 0;
  transition: opacity 0.08s ease;
  /* Pills/labels are uppercase + tracked; the help text must read as normal prose. */
  text-transform: none;
  letter-spacing: normal;
  font-weight: 400;
  white-space: normal;
}
/* Affordance: a non-interactive element that carries help gets the help cursor and a
   faint dotted underline so users know it explains itself. Interactive controls
   (links, buttons, inputs) keep their own cursor. */
[data-tip]:not(a):not(button):not(input):not(select):not(textarea) { cursor: help; }
/* A jargon pill (gcn, ndc, abstained, ...) is the thing a non-expert most needs
   flagged as explainable, so give those a faint dotted underline. KPI tiles already
   have descriptive labels, so they get the help cursor only, no extra chrome. */
.pill[data-tip] { text-decoration: underline dotted currentColor; text-underline-offset: 2px; }

/* ----- Chart tooltip (positioned via JS) ----- */
.chart-tip {
  position: fixed;
  z-index: 50;
  pointer-events: none;
  background: var(--surface-2);
  border: 1px solid var(--border-strong);
  border-radius: var(--r-sm);
  padding: 8px 10px;
  font-size: 12px;
  box-shadow: var(--shadow);
  opacity: 0;
  transition: opacity 0.08s ease;
  min-width: 130px;
}
.chart-tip .t-label {
  color: var(--fg-muted);
  text-transform: uppercase;
  letter-spacing: 0.06em;
  font-size: 10px;
  font-weight: 600;
}
.chart-tip .t-value {
  color: var(--fg);
  font-weight: 600;
  font-size: 14px;
  margin-top: 2px;
  font-variant-numeric: tabular-nums;
}

/* ----- Chart bar hover affordance ----- */
svg[data-chart="bar"] rect[data-label] {
  transition: opacity 0.08s ease;
  cursor: pointer;
}

/* ----- Sortable tables ----- */
table.tbl th.sortable {
  cursor: pointer;
  user-select: none;
  position: relative;
  padding-right: 18px;
  transition: color 0.08s ease;
}
table.tbl th.sortable:hover { color: var(--fg-secondary); }
table.tbl th.sortable:focus-visible {
  outline: none;
  color: var(--fg);
  box-shadow: inset 0 0 0 2px var(--brand-ring);
  border-radius: var(--r-sm);
}
table.tbl th.sortable::after {
  content: "";
  position: absolute;
  right: 6px;
  top: 50%;
  transform: translateY(-50%);
  width: 0;
  height: 0;
  border-left: 3px solid transparent;
  border-right: 3px solid transparent;
  border-top: 3px solid var(--fg-faint);
  opacity: 0.5;
}
table.tbl th.sortable:hover::after { opacity: 1; border-top-color: var(--fg-muted); }
table.tbl th.sort-asc::after  { border-top: 0; border-bottom: 3px solid var(--brand-strong); opacity: 1; }
table.tbl th.sort-desc::after { border-top-color: var(--brand-strong); opacity: 1; }

/* ----- Responsive: collapse sidebar below 768px ----- */
/* ----- Responsive / data tables (ENC2-7 UI pass) ----- */
/* Wrap data tables so a table wider than its card scrolls horizontally inside
   the card instead of shoving the whole page sideways on narrow screens. */
.tbl-wrap { overflow-x: auto; }
.tbl-wrap > table.tbl { min-width: 560px; }

/* Wider content column for the review queue (it has the most columns). */
.container.wide { max-width: 1180px; }

/* Submissions review table: short columns stay on one line so the Drug name -
   the field that actually matters - keeps the remaining width and wraps to a
   line or two instead of one word per line. */
table.tbl--review { min-width: 760px; }
/* Tighter padding + top alignment for the dense, multi-line review table.
   `table.tbl--review` matches the original `table.tbl td` specificity, and
   comes later in the file, so these win. */
table.tbl--review th, table.tbl--review td { white-space: nowrap; padding-left: 8px; padding-right: 8px; vertical-align: top; }
.tbl--review th.col-drug, .tbl--review td.col-drug { white-space: normal; min-width: 220px; }
/* Compact, vertically-stacked review control so the Approve/Reject buttons (the
   primary action) always stay visible instead of being pushed off the right. */
.tbl--review .review-form { display: flex; flex-direction: column; gap: 6px; align-items: stretch; width: 150px; }
.tbl--review .review-actions { display: flex; gap: 6px; }
.tbl--review .review-actions .btn--sm { flex: 1; }
.tbl--review .note-input { width: 100%; }

@media (max-width: 768px) {
  /* minmax(0, 1fr) lets the single column shrink below its content's min-width,
     so a wide table (which scrolls inside its own .tbl-wrap) can't force the
     whole page to scroll sideways. */
  .app { grid-template-columns: minmax(0, 1fr); }
  .sidebar {
    position: static;
    height: auto;
    flex-direction: row;
    align-items: center;
    gap: 16px;
    padding: 14px 20px;
    flex-wrap: wrap;
  }
  /* Wrap the nav chips to a second row instead of overflowing the viewport. */
  .sidebar-nav { flex-direction: row; flex-wrap: wrap; gap: 4px; flex: 1; min-width: 0; }
  .navitem { height: 32px; }
  .navitem.active::before { display: none; }
  .sidebar-foot { margin: 0; padding: 0; border: 0; display: flex; align-items: center; gap: 8px; }
  .theme-switch { margin-bottom: 0; }
  .main { padding: 24px 20px 48px; }
  .kpi-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-2 { grid-template-columns: minmax(0, 1fr); }
}

/* ----- Phone layout (ABD-380): a table stops being a table below 640px -----

   The column-priority utilities above (`.col-hide-1150` / `-1024`) are the right answer
   down to about a tablet: drop the least useful column rather than scroll. They run out
   at phone width, because a 7-column table (Keys, Submissions) has no 5 columns you can
   drop and still have a row that means anything. `.tbl-wrap > table.tbl` also carries a
   `min-width: 560px`, so on a 390px screen every table is guaranteed to scroll sideways.

   So below 640px each row becomes a CARD: one label/value line per cell, nothing hidden,
   nothing scrolled. The label comes from the row's own <th>, stamped onto each <td> as
   `data-label` by admin.js - deriving it from the live header is what stops it drifting
   from the column it names, which a hand-written copy in the Python f-strings would.

   Opt OUT with `.tbl--flat` on a table that is already narrow enough to read as a grid
   (a 2-column metric/value summary reads worse as a stack of cards than as a table). */
@media (max-width: 640px) {
  /* The wrapper exists to scroll a wide table. Cards are never wide, so both the scroll
     and the min-width that forces it have to go, or the page still moves sideways. */
  .tbl-wrap { overflow-x: visible; }
  .tbl-wrap > table.tbl { min-width: 0; }
  table.tbl--review { min-width: 0; }

  /* The header row is hidden from SIGHT, not from assistive technology. `display: none`
     here removed all 7 `columnheader` nodes from the accessibility tree (measured via
     CDP at 390px), leaving a screen reader with 14 cells and no column names. The visible
     label comes from `td::before`, and Chromium does fold that generated content into the
     cell's accessible name ("LABEL claude-session-testing") - but that is engine
     behaviour, not a contract, and it gives no header ASSOCIATION for table navigation.
     So the real header stays in the tree, using the same technique as `.fb-file-input`. */
  table.tbl:not(.tbl--flat) thead {
    position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
  }
  table.tbl:not(.tbl--flat),
  table.tbl:not(.tbl--flat) tbody,
  table.tbl:not(.tbl--flat) tr,
  table.tbl:not(.tbl--flat) td { display: block; width: auto; }

  table.tbl:not(.tbl--flat) tbody tr {
    border: 1px solid var(--border);
    border-radius: var(--r);
    background: var(--surface);
    margin-bottom: 10px;
    padding: 2px 0;
  }
  table.tbl:not(.tbl--flat) tbody tr:last-child { margin-bottom: 0; }

  /* Two columns: the label keeps a fixed share so values line up down the card, and
     minmax(0, ...) lets a long NDC or path shrink instead of widening the row. 7rem is
     measured, not chosen: the card is ~300px wide inside its padding at 390px, so a wider
     label column leaves too little for a value that is itself a control (the Keys owner
     input + Set button). */
  table.tbl:not(.tbl--flat) td {
    display: grid;
    grid-template-columns: minmax(0, 7rem) minmax(0, 1fr);
    gap: 12px;
    align-items: baseline;
    border-bottom: 0;
    padding: 8px 12px;
    white-space: normal;
    overflow-wrap: anywhere;
  }
  table.tbl:not(.tbl--flat) td::before {
    content: attr(data-label);
    color: var(--fg-muted);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.06em;
  }
  /* A header with no text (the follow star) and a spanning empty-state row ("no rows
     yet") have no column name to show, so they get the full width instead of a blank
     label column. `[colspan]` also stops admin.js labelling a cell that is not in a
     single column. */
  table.tbl:not(.tbl--flat) td[data-label=""],
  table.tbl:not(.tbl--flat) td[colspan] { display: block; }
  table.tbl:not(.tbl--flat) td[data-label=""]::before,
  table.tbl:not(.tbl--flat) td[colspan]::before { content: none; }

  /* `.col-hide-640` has to be re-stated here to outrank the card rule above. The plain
     `table.tbl .col-hide-640` (0-2-1) declared with the other col-hide utilities loses to
     `table.tbl:not(.tbl--flat) td` (0-2-2), which is later in the file, so it computed
     `display: grid` and hid nothing - a utility that silently did nothing at the only
     width it exists for. Verified in a browser, not by reading the cascade. */
  table.tbl:not(.tbl--flat) th.col-hide-640,
  table.tbl:not(.tbl--flat) td.col-hide-640 { display: none; }

  /* The Keys table drops Fingerprint + Created at 1024/1150 to avoid a horizontal
     scroll. A card has no horizontal axis to run out of, and their CELLS are shown here
     regardless (the card rule outranks those nth-child hides), so restore their HEADERS
     too. Otherwise the row carries two cells whose columnheader is missing from the
     accessibility tree - measured as 5 of 7 before this. */
  table.tbl--keys th:nth-child(4),
  table.tbl--keys th:nth-child(5) { display: table-cell; }

  /* The SAME defect, for the generic utilities rather than the keys table's nth-child
     hides (ABD-381). `.col-hide-1150` / `-1024` hide a th AND its td above 640px; in a
     card the td comes back (the card rule outranks them) and the th does not, so
     Accounts' "Token" and Misses' "First seen" render with no columnheader. Measured in a
     browser at 390px on a generic card table: td `grid`, th `none`. The rule above fixed
     exactly this for `.tbl--keys` and stopped at the table in front of it. */
  table.tbl:not(.tbl--flat) th.col-hide-1150,
  table.tbl:not(.tbl--flat) th.col-hide-1024 { display: table-cell; }

  /* Right-alignment and one-line clamping are table affordances; inside a card they
     just push the value away from its label or clip it. */
  table.tbl:not(.tbl--flat) td.num,
  table.tbl:not(.tbl--flat) td.actions { text-align: left; width: auto; white-space: normal; }
  table.tbl:not(.tbl--flat) td.nowrap { white-space: normal; }
  /* NOT `:not(.tbl--flat)` (ABD-381). A flat table stays a real table at phone width, and
     this block sets `.tbl-wrap { overflow-x: visible }` - so it lost the wrapper scroll
     that used to absorb a long value while KEEPING `white-space: nowrap` from line 427.
     Measured at 390px on the Follow-a-key table: a 42-char label (prod's longest today)
     paints 23px outside its card, and at 50 chars the whole PAGE scrolls sideways - the
     exact failure ABD-380 exists to remove, reintroduced by ABD-380 on another table.
     `overflow-wrap` covers the rest: a key fingerprint has no space to break at, so
     `white-space: normal` alone would not save it. */
  table.tbl:not(.tbl--wrap) td code { white-space: normal; overflow-wrap: anywhere; }
  table.tbl:not(.tbl--flat) .actions form.inline + form.inline { margin-left: 0; }

  /* The review control was pinned to 150px so the buttons stayed visible in a wide
     table; in a card that is just a narrow column in a wide space. */
  .tbl--review .review-form { width: auto; }
  /* The SAME trap as `.tbl--keys .owner-cell` below (ABD-449). The audit column widths
     are percentages of a TABLE; in a card the td is a full-width block, and a 15% cap
     crushes the value into ~50px beside its 7rem label - "abdulhaleem" rendered
     "abdu/lhal/eem" at 390px, the very shredding this ticket removes from the table.
     Repeat the CLASS list rather than writing `table.tbl--audit th, ... td`: that form is
     (0,1,2) and every width rule it means to undo is (0,2,1), so it LOSES the cascade and
     does nothing at all - a review round caught it inert, with a comment claiming an
     effect it never had. Matching (0,2,1) and coming later in the file is what wins. */
  table.tbl--audit .col-when,
  table.tbl--audit .col-src,
  table.tbl--audit .col-actor,
  table.tbl--audit .col-action,
  table.tbl--audit .col-target,
  table.tbl--audit .col-details,
  table.tbl--audit .col-ip { width: auto; }
  /* `table.` is load-bearing, not noise: the rules being undone are written
     `table.tbl--keys .owner-cell` (0,2,1), so a plain `.tbl--keys .owner-cell` (0,2,0)
     loses and the 12rem cap survives - which pinned the cell to 192px and left 20px for
     the owner input and its Set button, crushing the text to one letter per line. */
  table.tbl--keys .owner-cell,
  table.tbl--review .owner-cell { width: auto; max-width: none; }
  /* The owner form is a flex row inside the value column; the button must keep its own
     width so "Set" cannot be squeezed into a vertical stack of letters. */
  table.tbl--keys .owner-cell form { flex-wrap: wrap; }
  table.tbl--keys .owner-cell button { flex: 0 0 auto; white-space: nowrap; }

  /* A flat table keeps its header, and `.tbl--wrap`'s break-anywhere is right for a long
     path in a VALUE cell but wrong for the header above it: at 390px it rendered "HITS"
     as "HIT/S" and "CODE" as "COD/E". Every header on these tables is one short word, so
     keeping them whole just makes the column as wide as its name. */
  table.tbl--flat th { white-space: nowrap; overflow-wrap: normal; }

  /* The 768px rule turns the sidebar into a wrapping row, which relies on the nav having
     room beside the brand. At 390px it has ~70px, so all 11 chips stack into a ~520px
     column and every page opens below the fold. Give the nav a row of its own and the
     chips flow across it. */
  .sidebar-nav { flex: 1 0 100%; }

  /* Touch targets. 44px is the Apple HIG minimum; the nav chips were 32px, which is
     comfortable with a mouse and a miss with a thumb. */
  .navitem { height: 44px; }
  .btn, button, input, select { min-height: 44px; }
  table.tbl .btn--sm, table.tbl button.btn--sm { min-height: 36px; }
}

/* ----- API Explorer (ABD-262) ----- */
.ex-header { margin-bottom: 14px; }
.explorer {
  display: grid;
  grid-template-columns: 300px minmax(0, 1fr);
  gap: 16px;
  align-items: start;
  /* The PAGE is the single scroll container - the panel grows to fit its content (the
     full response, no inner scroll) and the page scrolls if it exceeds the viewport.
     Nested/inner scroll areas are a known anti-pattern (control + a11y problems); the
     only concession is the sticky sidebar below, which scrolls internally ONLY when the
     endpoint list itself is taller than the viewport. */
}

/* Left column: a sticky sidebar (search box over the endpoint list) that stays in view as
   the page scrolls the panel; it scrolls internally only if the list exceeds the viewport. */
.explorer-list {
  display: flex;
  flex-direction: column;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--r);
  overflow: hidden;
  position: sticky;
  top: 20px;
  max-height: calc(100vh - 40px);
}
.explorer-list-search { padding: 10px; border-bottom: 1px solid var(--border); }
.explorer-list-search input {
  width: 100%; background: var(--bg); border: 1px solid var(--border-strong);
  border-radius: var(--r-sm); color: var(--fg); padding: 8px 10px; font-size: 13px;
}
.explorer-list-search input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); }
.explorer-list-scroll { flex: 1; overflow-y: auto; padding: 8px; display: flex; flex-direction: column; gap: 4px; }

.ex-item {
  display: flex; align-items: flex-start; gap: 10px; width: 100%; text-align: left;
  padding: 9px 11px; background: transparent; border: 1px solid transparent;
  border-radius: var(--r-sm); color: var(--fg-secondary); cursor: pointer; line-height: 1.35;
  /* Override the global button height:36px reset - the list item is a <button> and that
     fixed height locked it to one line, so the second line (the path) overflowed below
     the box (obvious on the active item's highlight). Let it grow to both lines. */
  height: auto; flex: 0 0 auto;
}
.ex-item:hover { background: var(--surface-2); color: var(--fg); }
.ex-item.active { background: var(--brand-dim); border-color: var(--brand-ring); color: var(--fg); }
.ex-item:focus-visible { outline: 2px solid var(--brand-strong); outline-offset: -1px; }
.ex-item-txt { display: flex; flex-direction: column; gap: 3px; min-width: 0; flex: 1; }
.ex-item-sum { font-size: 13px; font-weight: 600; line-height: 1.35; }
/* Reset the global `code {}` chip (bg/border/padding): on an inline <code> the vertical
   padding bleeds into adjacent rows and collides them (only visible with the real
   Inconsolata metrics). Plain block text instead - own line, ellipsis, no bleed. */
.ex-item-path { display: block; background: transparent; border: 0; padding: 0; border-radius: 0;
  font-family: Inconsolata, monospace; font-size: 11px; color: var(--fg-muted);
  line-height: 1.35; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ex-method {
  flex: 0 0 auto; margin-top: 1px; font-family: Inconsolata, monospace; font-size: 10px;
  font-weight: 600; letter-spacing: 0.04em; padding: 2px 6px; border-radius: 4px;
  background: var(--surface-3); color: var(--fg-secondary);
}
.ex-method--get { background: var(--ok-bg); color: var(--ok); }
.ex-method--post { background: var(--warn-bg); color: var(--warn); }
.ex-group { font-size: 10px; font-weight: 700; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--fg-faint); padding: 12px 8px 4px; }
.ex-group:first-child { padding-top: 2px; }

/* Right column: a full-height panel with a header bar over a scrolling body. */
.explorer-panel {
  display: flex; flex-direction: column; min-width: 0;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); overflow: hidden;
}
.ex-panel-head {
  display: flex; align-items: center; gap: 10px; padding: 14px 18px;
  border-bottom: 1px solid var(--border);
  background: linear-gradient(180deg, var(--surface-2), var(--surface));
}
.ex-panel-head .ex-path { background: transparent; border: 0; padding: 0;
  font-family: Inconsolata, monospace; font-size: 14px; color: var(--fg);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ex-panel-head .ex-status { margin-left: auto; font-size: 13px; font-weight: 600; font-variant-numeric: tabular-nums; }
.ex-status.ok { color: var(--ok); }
.ex-status.warn { color: var(--warn); }
.ex-status.err { color: var(--err); }

/* Natural page flow: the panel-body and all its sections grow to their content - no inner
   scroll. The whole response is laid out; if the panel is taller than the viewport the
   PAGE scrolls (single scroll container), which is the standard, accessible pattern. */
.ex-panel-body { display: flex; flex-direction: column; gap: 14px; padding: 18px; }
.ex-meta { display: flex; flex-direction: column; gap: 14px; }
.ex-summary { font-size: 16px; font-weight: 600; color: var(--fg); }
.ex-desc { color: var(--fg-secondary); font-size: 13px; line-height: 1.55; margin: -6px 0 0; }

.ex-params-title, .ex-code-title {
  font-size: 11px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; color: var(--fg-muted);
}
.ex-params-title { margin-bottom: 10px; }
.ex-form { display: flex; flex-direction: column; gap: 12px; }
.ex-field { display: flex; flex-direction: column; gap: 5px; }
.ex-field label { font-size: 12px; color: var(--fg-secondary); font-weight: 500; }
.ex-input {
  width: 100%; background: var(--bg); border: 1px solid var(--border-strong); border-radius: var(--r-sm);
  color: var(--fg); padding: 9px 11px; font-family: Inconsolata, monospace; font-size: 13px;
}
.ex-input:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); }
/* Named-example picker (ABD-297). Wraps so a long summary never pushes the select out of
   the field; the note is allowed to shrink and ellipsize rather than widen the panel. */
.ex-examples { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.ex-examples-label {
  font-size: 11px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase;
  color: var(--fg-muted);
}
/* background-COLOR only: the global `select` rule paints the chevron with
   background-image, and the `background` shorthand would erase it. Keep the right
   padding clear of that chevron too. */
.ex-example-pick {
  background-color: var(--bg); border: 1px solid var(--border-strong);
  border-radius: var(--r-sm); color: var(--fg); font-size: 12px;
  padding: 4px 28px 4px 8px; max-width: 100%; height: auto;
}
.ex-example-pick:focus { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); }
.ex-example-note {
  font-size: 11px; color: var(--fg-muted); flex: 1 1 120px; min-width: 0;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.ex-hint { font-size: 11px; color: var(--fg-muted); }
.ex-run-row { display: flex; align-items: center; gap: 12px; }
.ex-run { min-width: 92px; }
.ex-run-hint { font-size: 12px; }

/* Code viewer blocks (Response + cURL): header bar over a scrollable body. */
.ex-code {
  display: flex; flex-direction: column; background: var(--bg);
  border: 1px solid var(--border); border-radius: var(--r-sm); overflow: hidden;
}
.ex-code--grow .ex-code-body { min-height: 96px; }   /* the empty "Run to see..." state has a body */
.ex-code-head {
  display: flex; align-items: center; gap: 10px; padding: 7px 12px;
  background: var(--surface-2); border-bottom: 1px solid var(--border);
}
.ex-code-meta { margin-left: auto; font-size: 11px; color: var(--fg-muted); font-variant-numeric: tabular-nums; }
.ex-copy {
  background: transparent; border: 1px solid var(--border-strong); border-radius: 4px;
  color: var(--fg-secondary); font-size: 11px; padding: 2px 9px; cursor: pointer;
}
.ex-copy:hover { background: var(--surface-3); color: var(--fg); }
.ex-code-body {
  margin: 0; padding: 12px; font-family: Inconsolata, monospace; font-size: 12.5px;
  line-height: 1.55; color: var(--fg-secondary);
  /* Expand fully (no inner scroll) and WRAP long lines - the page scrolls, and wrapping
     keeps a wide response from forcing a horizontal scroll. */
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.ex-code-body[data-empty] {
  display: flex; align-items: center; justify-content: center; white-space: normal;
  color: var(--fg-faint); font-family: Inter, system-ui, sans-serif; font-size: 13px;
}
.ex-curl { color: var(--brand); }

@media (max-width: 900px) {
  .explorer { grid-template-columns: minmax(0, 1fr); }
  /* Stacked: the list sits above the panel, so it is a normal block (not sticky),
     capped so it doesn't fill the screen before the panel. */
  .explorer-list { position: static; max-height: 320px; }
}

/* ----- Keys: pinned Favourites card (ABD-273) ----- */
.card--fav { border-color: var(--brand-ring); background:
  linear-gradient(180deg, var(--brand-dim), transparent 120px), var(--surface); }
.card--fav .card-head h2 { display: inline-flex; align-items: center; gap: 6px; }
.fav-star { color: var(--star); font-size: 14px; line-height: 1; }

/* ----- Drug navigator: the Home page and its NDC / GCN pages (ABD-755) ----- */
.brand-icon { display: block; object-fit: contain; }
/* ABD-775: the supplied icon's lines are a near-black, which vanishes on a dark surface.
   So the icon is two files, the supplied one and a copy in Dark's own --fg and --brand,
   and the shell renders an <img> for each. These three rules display one of them. Light
   is the default, so a browser that never ran theme.js gets the supplied icon. */
.brand-icon--dark { display: none; }
:root[data-theme="dark"] .brand-icon--light { display: none; }
:root[data-theme="dark"] .brand-icon--dark { display: block; }
.mono { font-family: 'Inconsolata', ui-monospace, monospace; font-variant-numeric: tabular-nums; }
.pill.warn { background: var(--warn-bg); color: var(--warn); border-color: var(--warn-bd); }
.kpi .value.value--sm { font-size: 16px; font-weight: 500; }
.nav-hero { padding: 12px 0 28px; }
.nav-hero h1 { font-size: 44px; margin-bottom: 8px; }
.nav-hero .eyebrow { color: var(--brand); }
.nav-search { display: flex; gap: 10px; align-items: flex-start; margin: 0 0 20px; max-width: 760px; }
.nav-search-box { position: relative; flex: 1; }
.nav-search-box input { width: 100%; }
.nav-search.hero input { height: 48px; font-size: 16px; padding: 0 16px; }
.nav-search.hero .btn { height: 48px; padding: 0 22px; }
.nav-suggest {
  position: absolute; z-index: 20; top: calc(100% + 4px); left: 0; right: 0;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--r);
  box-shadow: var(--shadow); overflow: hidden;
}
.nav-suggest a {
  display: grid; grid-template-columns: 1fr auto; gap: 2px 12px;
  padding: 9px 14px; color: var(--fg); border-bottom: 1px solid var(--border);
}
.nav-suggest a:last-child { border-bottom: 0; }
.nav-suggest a:hover, .nav-suggest a[aria-selected="true"] { background: var(--lavender); text-decoration: none; }
.nav-suggest .sg-kind { font-size: 11px; color: var(--fg-muted); text-transform: uppercase; letter-spacing: 0.06em; }
.nav-suggest .sg-detail { grid-column: 1 / -1; font-size: 12px; color: var(--fg-muted); }
.nav-suggest .sg-empty { padding: 10px 14px; font-size: 13px; color: var(--fg-muted); }
.nav-examples { align-items: center; }
dl.kv { display: grid; grid-template-columns: minmax(120px, max-content) 1fr; gap: 8px 20px; margin: 0; }
dl.kv dt { color: var(--fg-muted); font-size: 13px; }
dl.kv dd { margin: 0; overflow-wrap: anywhere; }
.chips { display: flex; flex-wrap: wrap; gap: 6px; }
.chip {
  display: inline-flex; align-items: center; gap: 6px; min-height: 28px;
  padding: 3px 10px; border-radius: 999px; font-size: 13px;
  background: var(--surface); border: 1px solid var(--border); color: var(--fg);
}
.chip b { font-weight: 600; color: var(--fg-muted); font-variant-numeric: tabular-nums; }
.chip:hover { border-color: var(--lilac); background: var(--lavender); text-decoration: none; }
.chip.on { background: var(--brand); border-color: var(--brand); color: var(--on-brand); }
.chip.on b { color: var(--on-brand-muted); }
.chip-group + .chip-group { margin-top: 14px; }
.chip-label { font-size: 12px; font-weight: 600; color: var(--fg-secondary); margin-bottom: 6px; }
.filter-bar { margin-bottom: 10px; align-items: center; }
.pager { display: flex; gap: 14px; align-items: center; margin: 10px 0; }
@media (max-width: 720px) {
  .nav-hero h1 { font-size: 34px; }
  .nav-search { flex-direction: column; align-items: stretch; }
  dl.kv { grid-template-columns: 1fr; gap: 2px; }
  dl.kv dd { margin-bottom: 8px; }
  .chip { min-height: 44px; }
}
